Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: cycloneprotocol/cyclone-contracts
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 2.1
Choose a base ref
...
head repository: cycloneprotocol/cyclone-contracts
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
  • 7 commits
  • 7 files changed
  • 1 contributor

Commits on Apr 7, 2021

  1. Update Aeolus.sol

    bf945 authored Apr 7, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    aca4e60 View commit details
  2. Update AeolusV2.sol

    bf945 authored Apr 7, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    b4e7f8a View commit details

Commits on May 8, 2021

  1. Create AeolusV2dot2.sol

    bf945 authored May 8, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    c95f85c View commit details
  2. Create AeolusV2dot1.sol

    bf945 authored May 8, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    ab01279 View commit details
  3. Create CycloneV2dot2.sol

    bf945 authored May 8, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    3e6332c View commit details
  4. Create ICycloneV2dot2.sol

    bf945 authored May 8, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    6b15d04 View commit details
  5. Create IDelegator.sol

    bf945 authored May 8, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    1c75636 View commit details
Showing with 672 additions and 3 deletions.
  1. +2 −2 contracts/Aeolus.sol
  2. +1 −1 contracts/AeolusV2.sol
  3. +203 −0 contracts/AeolusV2dot1.sol
  4. +191 −0 contracts/AeolusV2dot2.sol
  5. +242 −0 contracts/CycloneV2dot2.sol
  6. +15 −0 contracts/ICycloneV2dot2.sol
  7. +18 −0 contracts/cream/IDelegator.sol
4 changes: 2 additions & 2 deletions contracts/Aeolus.sol
Original file line number Diff line number Diff line change
@@ -160,9 +160,9 @@ contract Aeolus is Whitelist {
function safeCYCTransfer(address _to, uint256 _amount) internal {
uint256 cycBalance = cycToken.balanceOf(address(this));
if (_amount > cycBalance) {
cycToken.transfer(_to, cycBalance);
require(cycToken.transfer(_to, cycBalance), "failed to transfer cyc token");
} else {
cycToken.transfer(_to, _amount);
require(cycToken.transfer(_to, _amount), "failed to transfer cyc token");
}
}
}
2 changes: 1 addition & 1 deletion contracts/AeolusV2.sol
Original file line number Diff line number Diff line change
@@ -193,6 +193,6 @@ contract AeolusV2 is Ownable {
_amount = cycBalance;
}
rewardToDistribute -= _amount;
cycToken.transfer(_to, _amount);
require(cycToken.transfer(_to, _amount), "failed to transfer cyc token");
}
}
203 changes: 203 additions & 0 deletions contracts/AeolusV2dot1.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
pragma solidity <0.6 >=0.4.24;

import "./math/SafeMath.sol";
import "./ownership/Ownable.sol";
import "./token/IERC20.sol";
import "./token/IMintableToken.sol";
import "./token/SafeERC20.sol";
import "./uniswapv2/IRouter.sol";

// Aeolus is the master of Cyclone tokens. He can distribute CYC and he is a fair guy.
//
// Note that it's ownable and the owner wields tremendous power. The ownership
// will be transferred to a governance smart contract once CYC is sufficiently
// distributed and the community can show to govern itself.
//
// Have fun reading it. Hopefully it's bug-free. God bless.
contract AeolusV2dot1 is Ownable {
using SafeMath for uint256;
using SafeERC20 for IERC20;

// Info of each user.
struct UserInfo {
uint256 amount; // How many LP tokens the user has provided.
uint256 rewardDebt; // Reward debt. See explanation below.
//
// We do some fancy math here. Basically, any point in time, the amount of CYCs
// entitled to a user but is pending to be distributed is:
//
// pending reward = (user.amount * accCYCPerShare) - user.rewardDebt
//
// Whenever a user deposits or withdraws LP tokens to a pool. Here's what happens:
// 1. Update accCYCPerShare and lastRewardBlock
// 2. User receives the pending reward sent to his/her address.
// 3. User's `amount` gets updated.
// 4. User's `rewardDebt` gets updated.
}


// Address of LP token contract.
IERC20 public lpToken;
// Accumulated CYCs per share, times 1e12. See below.
uint256 public accCYCPerShare;
// Last block reward block height
uint256 public lastRewardBlock;
// Reward per block
uint256 public rewardPerBlock;
// Reward to distribute
uint256 public rewardToDistribute;
// Entrance Fee Rate
uint256 public entranceFeeRate;

IERC20 public wrappedCoin;
IRouter public router;
// The Cyclone TOKEN
IMintableToken public cycToken;

// Info of each user that stakes LP tokens.
mapping (address => UserInfo) public userInfo;

event RewardAdded(uint256 amount, bool isBlockReward);
event Deposit(address indexed user, uint256 amount, uint256 fee);
event Withdraw(address indexed user, uint256 amount);
event EmergencyWithdraw(address indexed user, uint256 amount);

constructor(IMintableToken _cycToken, IERC20 _lpToken, address _router, IERC20 _wrappedCoin) public {
cycToken = _cycToken;
lastRewardBlock = block.number;
lpToken = _lpToken;
router = IRouter(_router);
wrappedCoin = _wrappedCoin;
require(_lpToken.approve(_router, uint256(-1)), "failed to approve router");
require(_wrappedCoin.approve(_router, uint256(-1)), "failed to approve router");
}

function setEntranceFeeRate(uint256 _entranceFeeRate) public onlyOwner {
require(_entranceFeeRate < 10000, "invalid entrance fee rate");
entranceFeeRate = _entranceFeeRate;
}

function setRewardPerBlock(uint256 _rewardPerBlock) public onlyOwner {
updateBlockReward();
rewardPerBlock = _rewardPerBlock;
}

function rewardPending() internal view returns (uint256) {
uint256 reward = block.number.sub(lastRewardBlock).mul(rewardPerBlock);
uint256 cycBalance = cycToken.balanceOf(address(this)).sub(rewardToDistribute);
if (cycBalance < reward) {
return cycBalance;
}
return reward;
}

// View function to see pending reward on frontend.
function pendingReward(address _user) external view returns (uint256) {
UserInfo storage user = userInfo[_user];
uint256 acps = accCYCPerShare;
if (rewardPerBlock > 0) {
uint256 lpSupply = lpToken.balanceOf(address(this));
if (block.number > lastRewardBlock && lpSupply > 0) {
acps = acps.add(rewardPending().mul(1e12).div(lpSupply));
}
}

return user.amount.mul(acps).div(1e12).sub(user.rewardDebt);
}

// Update reward variables to be up-to-date.
function updateBlockReward() public {
if (block.number <= lastRewardBlock || rewardPerBlock == 0) {
return;
}
uint256 lpSupply = lpToken.balanceOf(address(this));
uint256 reward = rewardPending();
if (lpSupply == 0 || reward == 0) {
lastRewardBlock = block.number;
return;
}
rewardToDistribute = rewardToDistribute.add(reward);
emit RewardAdded(reward, true);
lastRewardBlock = block.number;
accCYCPerShare = accCYCPerShare.add(reward.mul(1e12).div(lpSupply));
}

// Deposit LP tokens to Aeolus for CYC allocation.
function deposit(uint256 _amount) public {
updateBlockReward();
UserInfo storage user = userInfo[msg.sender];
uint256 originAmount = user.amount;
uint256 acps = accCYCPerShare;
if (originAmount > 0) {
uint256 pending = originAmount.mul(acps).div(1e12).sub(user.rewardDebt);
if (pending > 0) {
safeCYCTransfer(msg.sender, pending);
}
}
uint256 feeInCYC = 0;
if (_amount > 0) {
lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);
uint256 entranceFee = _amount.mul(entranceFeeRate).div(10000);
if (entranceFee > 0) {
IERC20 wct = wrappedCoin;
(uint256 wcAmount, uint256 cycAmount) = router.removeLiquidity(address(wct), address(cycToken), entranceFee, 0, 0, address(this), block.timestamp.mul(2));
if (wcAmount > 0) {
address[] memory path = new address[](2);
path[0] = address(wct);
path[1] = address(cycToken);
uint256[] memory amounts = router.swapExactTokensForTokens(wcAmount, 0, path, address(this), block.timestamp.mul(2));
feeInCYC = cycAmount.add(amounts[1]);
} else {
feeInCYC = cycAmount;
}
if (feeInCYC > 0) {
require(cycToken.burn(feeInCYC), "failed to burn cyc token");
}
_amount = _amount.sub(entranceFee);
}
user.amount = originAmount.add(_amount);
}
user.rewardDebt = user.amount.mul(acps).div(1e12);
emit Deposit(msg.sender, _amount, feeInCYC);
}

// Withdraw LP tokens from Aeolus.
function withdraw(uint256 _amount) public {
UserInfo storage user = userInfo[msg.sender];
uint256 originAmount = user.amount;
require(originAmount >= _amount, "withdraw: not good");
updateBlockReward();
uint256 acps = accCYCPerShare;
uint256 pending = originAmount.mul(acps).div(1e12).sub(user.rewardDebt);
if (pending > 0) {
safeCYCTransfer(msg.sender, pending);
}
if (_amount > 0) {
user.amount = originAmount.sub(_amount);
lpToken.safeTransfer(address(msg.sender), _amount);
}
user.rewardDebt = user.amount.mul(acps).div(1e12);
emit Withdraw(msg.sender, _amount);
}

// Withdraw without caring about rewards. EMERGENCY ONLY.
function emergencyWithdraw() public {
UserInfo storage user = userInfo[msg.sender];
uint256 amount = user.amount;
user.amount = 0;
user.rewardDebt = 0;
lpToken.safeTransfer(address(msg.sender), amount);
emit EmergencyWithdraw(msg.sender, amount);
}

// Safe CYC transfer function, just in case if rounding error causes pool to not have enough CYCs.
function safeCYCTransfer(address _to, uint256 _amount) internal {
IMintableToken token = cycToken;
uint256 cycBalance = token.balanceOf(address(this));
if (_amount > cycBalance) {
_amount = cycBalance;
}
rewardToDistribute = rewardToDistribute.sub(_amount);
require(token.transfer(_to, _amount), "failed to transfer cyc token");
}
}
191 changes: 191 additions & 0 deletions contracts/AeolusV2dot2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
pragma solidity <0.6 >=0.4.24;

import "./math/SafeMath.sol";
import "./ownership/Ownable.sol";
import "./token/IERC20.sol";
import "./token/IMintableToken.sol";
import "./token/SafeERC20.sol";
import "./uniswapv1/IExchange.sol";

// Aeolus is the master of Cyclone tokens. He can distribute CYC and he is a fair guy.
//
// Note that it's ownable and the owner wields tremendous power. The ownership
// will be transferred to a governance smart contract once CYC is sufficiently
// distributed and the community can show to govern itself.
//
// Have fun reading it. Hopefully it's bug-free. God bless.
contract AeolusV2dot2 is Ownable {
using SafeMath for uint256;
using SafeERC20 for IERC20;

// Info of each user.
struct UserInfo {
uint256 amount; // How many LP tokens the user has provided.
uint256 rewardDebt; // Reward debt. See explanation below.
//
// We do some fancy math here. Basically, any point in time, the amount of CYCs
// entitled to a user but is pending to be distributed is:
//
// pending reward = (user.amount * accCYCPerShare) - user.rewardDebt
//
// Whenever a user deposits or withdraws LP tokens to a pool. Here's what happens:
// 1. Update accCYCPerShare and lastRewardBlock
// 2. User receives the pending reward sent to his/her address.
// 3. User's `amount` gets updated.
// 4. User's `rewardDebt` gets updated.
}


// Address of LP token contract.
IERC20 public lpToken;
// Accumulated CYCs per share, times 1e12. See below.
uint256 public accCYCPerShare;
// Last block reward block height
uint256 public lastRewardBlock;
// Reward per block
uint256 public rewardPerBlock;
// Reward to distribute
uint256 public rewardToDistribute;
// Entrance Fee Rate
uint256 public entranceFeeRate;

IExchange public exchange;
// The Cyclone TOKEN
IMintableToken public cycToken;

// Info of each user that stakes LP tokens.
mapping (address => UserInfo) public userInfo;

event RewardAdded(uint256 amount, bool isBlockReward);
event Deposit(address indexed user, uint256 amount, uint256 fee);
event Withdraw(address indexed user, uint256 amount);
event EmergencyWithdraw(address indexed user, uint256 amount);

constructor(IMintableToken _cycToken, address payable _exchange) public {
cycToken = _cycToken;
lastRewardBlock = block.number;
lpToken = IERC20(_exchange);
exchange = IExchange(_exchange);
}

function() external payable {
}

function setEntranceFeeRate(uint256 _entranceFeeRate) public onlyOwner {
require(_entranceFeeRate < 10000, "invalid entrance fee rate");
entranceFeeRate = _entranceFeeRate;
}

function setRewardPerBlock(uint256 _rewardPerBlock) public onlyOwner {
updateBlockReward();
rewardPerBlock = _rewardPerBlock;
}

function rewardPending() internal view returns (uint256) {
uint256 reward = block.number.sub(lastRewardBlock).mul(rewardPerBlock);
uint256 cycBalance = cycToken.balanceOf(address(this)).sub(rewardToDistribute);
if (cycBalance < reward) {
return cycBalance;
}
return reward;
}

// View function to see pending reward on frontend.
function pendingReward(address _user) external view returns (uint256) {
UserInfo storage user = userInfo[_user];
uint256 acps = accCYCPerShare;
if (rewardPerBlock > 0) {
uint256 lpSupply = lpToken.balanceOf(address(this));
if (block.number > lastRewardBlock && lpSupply > 0) {
acps = acps.add(rewardPending().mul(1e12).div(lpSupply));
}
}

return user.amount.mul(acps).div(1e12).sub(user.rewardDebt);
}

// Update reward variables to be up-to-date.
function updateBlockReward() public {
if (block.number <= lastRewardBlock || rewardPerBlock == 0) {
return;
}
uint256 lpSupply = lpToken.balanceOf(address(this));
uint256 reward = rewardPending();
if (lpSupply == 0 || reward == 0) {
lastRewardBlock = block.number;
return;
}
rewardToDistribute = rewardToDistribute.add(reward);
emit RewardAdded(reward, true);
lastRewardBlock = block.number;
accCYCPerShare = accCYCPerShare.add(reward.mul(1e12).div(lpSupply));
}

// Deposit LP tokens to Aeolus for CYC allocation.
function deposit(uint256 _amount) public {
updateBlockReward();
UserInfo storage user = userInfo[msg.sender];
uint256 originAmount = user.amount;
uint256 acps = accCYCPerShare;
if (originAmount > 0) {
uint256 pending = originAmount.mul(acps).div(1e12).sub(user.rewardDebt);
if (pending > 0) {
safeCYCTransfer(msg.sender, pending);
}
}
uint256 feeInCYC = 0;
if (_amount > 0) {
lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);
uint256 entranceFee = _amount.mul(entranceFeeRate).div(10000);
if (entranceFee > 0) {
(uint256 iotxAmount, uint256 cycAmount) = exchange.removeLiquidity(entranceFee, 1, 1, block.timestamp.mul(2));
feeInCYC = cycAmount.add(exchange.iotxToTokenSwapInput.value(iotxAmount)(1, block.timestamp.mul(2)));
require(cycToken.burn(feeInCYC), "failed to burn cyc token");
_amount = _amount.sub(entranceFee);
}
user.amount = originAmount.add(_amount);
}
user.rewardDebt = user.amount.mul(acps).div(1e12);
emit Deposit(msg.sender, _amount, feeInCYC);
}

// Withdraw LP tokens from Aeolus.
function withdraw(uint256 _amount) public {
UserInfo storage user = userInfo[msg.sender];
uint256 originAmount = user.amount;
require(originAmount >= _amount, "withdraw: not good");
updateBlockReward();
uint256 acps = accCYCPerShare;
uint256 pending = originAmount.mul(acps).div(1e12).sub(user.rewardDebt);
if (pending > 0) {
safeCYCTransfer(msg.sender, pending);
}
if (_amount > 0) {
user.amount = originAmount.sub(_amount);
lpToken.safeTransfer(address(msg.sender), _amount);
}
user.rewardDebt = user.amount.mul(acps).div(1e12);
emit Withdraw(msg.sender, _amount);
}

// Withdraw without caring about rewards. EMERGENCY ONLY.
function emergencyWithdraw() public {
UserInfo storage user = userInfo[msg.sender];
uint256 amount = user.amount;
user.amount = 0;
user.rewardDebt = 0;
lpToken.safeTransfer(address(msg.sender), amount);
emit EmergencyWithdraw(msg.sender, amount);
}

// Safe CYC transfer function, just in case if rounding error causes pool to not have enough CYCs.
function safeCYCTransfer(address _to, uint256 _amount) internal {
IMintableToken token = cycToken;
uint256 cycBalance = token.balanceOf(address(this));
if (_amount > cycBalance) {
_amount = cycBalance;
}
rewardToDistribute = rewardToDistribute.sub(_amount);
require(token.transfer(_to, _amount), "failed to transfer cyc token");
}
}
242 changes: 242 additions & 0 deletions contracts/CycloneV2dot2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
pragma solidity <0.6 >=0.4.24;

import "./math/SafeMath.sol";
import "./token/IMintableToken.sol";
import "./utils/Address.sol";
import "./zksnarklib/MerkleTreeWithHistory.sol";
import "./zksnarklib/IVerifier.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

contract CycloneV2dot2 is MerkleTreeWithHistory, ReentrancyGuard {

using SafeMath for uint256;
uint256 public tokenDenomination; // (10K or 100k or 1M) * 10^18
uint256 public coinDenomination;
uint256 public initCYCDenomination;
mapping(bytes32 => bool) public nullifierHashes;
mapping(bytes32 => bool) public commitments; // we store all commitments just to prevent accidental deposits with the same commitment
IVerifier public verifier;
IERC20 public token;
IMintableToken public cycToken;
address public treasury;
address public govDAO;
uint256 public numOfShares;
uint256 public lastRewardBlock;
uint256 public rewardPerBlock;
uint256 public accumulateCYC;
uint256 public anonymityFee;

modifier onlyGovDAO {
// Start with an governance DAO address and will transfer to a governance DAO, e.g., Timelock + GovernorAlpha, after launch
require(msg.sender == govDAO, "Only Governance DAO can call this function.");
_;
}

event Deposit(bytes32 indexed commitment, uint32 leafIndex, uint256 timestamp, uint256 cycDenomination, uint256 anonymityFee);
event Withdrawal(address to, bytes32 nullifierHash, address indexed relayer, uint256 reward, uint256 relayerFee);
event RewardPerBlockUpdated(uint256 oldValue, uint256 newValue);
event AnonymityFeeUpdated(uint256 oldValue, uint256 newValue);

/**
@dev The constructor
@param _verifier the address of SNARK verifier for this contract
@param _merkleTreeHeight the height of deposits' Merkle Tree
@param _govDAO governance DAO address
*/
constructor(
address _govDAO,
IERC20 _token,
IMintableToken _cycToken,
address _treasury,
uint256 _initCYCDenomination,
uint256 _coinDenomination,
uint256 _tokenDenomination,
uint256 _startBlock,
IVerifier _verifier,
uint32 _merkleTreeHeight
) MerkleTreeWithHistory(_merkleTreeHeight) public {
require(address(_token) != address(_cycToken), "token cannot be identical to CYC token");
verifier = _verifier;
treasury = _treasury;
cycToken = _cycToken;
token = _token;
govDAO = _govDAO;
if (_startBlock < block.number) {
lastRewardBlock = block.number;
} else {
lastRewardBlock = _startBlock;
}
initCYCDenomination = _initCYCDenomination;
coinDenomination = _coinDenomination;
tokenDenomination = _tokenDenomination;
numOfShares = 0;
}

function calcAccumulateCYC() internal view returns (uint256) {
uint256 reward = block.number.sub(lastRewardBlock).mul(rewardPerBlock);
uint256 remaining = cycToken.balanceOf(address(this)).sub(accumulateCYC);
if (remaining < reward) {
reward = remaining;
}
return accumulateCYC.add(reward);
}

function updateBlockReward() public {
uint256 blockNumber = block.number;
if (blockNumber <= lastRewardBlock) {
return;
}
if (rewardPerBlock != 0) {
accumulateCYC = calcAccumulateCYC();
}
// always update lastRewardBlock no matter there is sufficient reward or not
lastRewardBlock = blockNumber;
}

function cycDenomination() public view returns (uint256) {
if (numOfShares == 0) {
return initCYCDenomination;
}
uint256 blockNumber = block.number;
uint256 accCYC = accumulateCYC;
if (blockNumber > lastRewardBlock && rewardPerBlock > 0) {
accCYC = calcAccumulateCYC();
}
return accCYC.add(numOfShares - 1).div(numOfShares);
}

/**
@dev Deposit funds into the contract. The caller must send (for Coin) or approve (for ERC20) value equal to or `denomination` of this instance.
@param _commitment the note commitment, which is PedersenHash(nullifier + secret)
*/
function deposit(bytes32 _commitment) external payable nonReentrant {
require(!commitments[_commitment], "The commitment has been submitted");
require(msg.value >= coinDenomination, "insufficient coin amount");
uint256 refund = msg.value - coinDenomination;
uint32 insertedIndex = _insert(_commitment);
commitments[_commitment] = true;
updateBlockReward();
uint256 cycDeno = cycDenomination();
uint256 fee = anonymityFee;
if (cycDeno.add(fee) > 0) {
require(cycToken.transferFrom(msg.sender, address(this), cycDeno.add(fee)), "insufficient CYC allowance");
}
if (fee > 0) {
address t = treasury;
if (t == address(0)) {
require(cycToken.burn(fee), "failed to burn anonymity fee");
} else {
require(safeTransfer(cycToken, t, fee), "failed to transfer anonymity fee");
}
}
uint256 td = tokenDenomination;
if (td > 0) {
require(token.transferFrom(msg.sender, address(this), td), "insufficient allowance");
}
accumulateCYC += cycDeno;
numOfShares += 1;
if (refund > 0) {
(bool success, ) = msg.sender.call.value(refund)("");
require(success, "failed to refund");
}
emit Deposit(_commitment, insertedIndex, block.timestamp, cycDeno, fee);
}

/**
@dev Withdraw a deposit from the contract. `proof` is a zkSNARK proof data, and input is an array of circuit public inputs
`input` array consists of:
- merkle root of all deposits in the contract
- hash of unique deposit nullifier to prevent double spends
- the recipient of funds
- optional fee that goes to the transaction sender (usually a relay)
*/
function withdraw(bytes calldata _proof, bytes32 _root, bytes32 _nullifierHash, address payable _recipient, address payable _relayer, uint256 _relayerFee, uint256 _refund) external payable nonReentrant {
require(_refund == 0, "refund is not zero");
require(!Address.isContract(_recipient), "recipient of cannot be contract");
require(!nullifierHashes[_nullifierHash], "The note has been already spent");
require(isKnownRoot(_root), "Cannot find your merkle root"); // Make sure to use a recent one
require(verifier.verifyProof(_proof, [uint256(_root), uint256(_nullifierHash), uint256(_recipient), uint256(_relayer), _relayerFee, _refund]), "Invalid withdraw proof");

nullifierHashes[_nullifierHash] = true;
uint256 td = tokenDenomination;
if (td > 0) {
require(safeTransfer(token, _recipient, td), "failed to withdraw token");
}
updateBlockReward();
uint256 relayerFee = 0;
// numOfShares should be larger than 0
uint256 cycDeno = accumulateCYC.div(numOfShares);
if (cycDeno > 0) {
accumulateCYC -= cycDeno;
require(safeTransfer(cycToken, _recipient, cycDeno), "failed to reward CYC");
}
uint256 cd = coinDenomination;
if (_relayerFee > cd) {
_relayerFee = cd;
}
if (_relayerFee > 0) {
(bool success,) = _relayer.call.value(_relayerFee)("");
require(success, "failed to send relayer fee");
cd -= _relayerFee;
}
if (cd > 0) {
(bool success,) = _recipient.call.value(cd)("");
require(success, "failed to withdraw coin");
}
numOfShares -= 1;
emit Withdrawal(_recipient, _nullifierHash, _relayer, cycDeno, relayerFee);
}

/** @dev whether a note is already spent */
function isSpent(bytes32 _nullifierHash) public view returns(bool) {
return nullifierHashes[_nullifierHash];
}

/** @dev whether an array of notes is already spent */
function isSpentArray(bytes32[] calldata _nullifierHashes) external view returns(bool[] memory spent) {
spent = new bool[](_nullifierHashes.length);
for(uint i = 0; i < _nullifierHashes.length; i++) {
if (isSpent(_nullifierHashes[i])) {
spent[i] = true;
}
}
}

/**
@dev allow governance DAO to update SNARK verification keys. This is needed to
update keys if tornado.cash update their keys in production.
*/
function updateVerifier(address _newVerifier) external onlyGovDAO {
verifier = IVerifier(_newVerifier);
}

/** @dev governance DAO can change his address */
function changeGovDAO(address _newGovDAO) external onlyGovDAO {
govDAO = _newGovDAO;
}

function setRewardPerBlock(uint256 _rewardPerBlock) public onlyGovDAO {
updateBlockReward();
emit RewardPerBlockUpdated(rewardPerBlock, _rewardPerBlock);
rewardPerBlock = _rewardPerBlock;
}

function setAnonymityFee(uint256 _fee) public onlyGovDAO {
emit AnonymityFeeUpdated(anonymityFee, _fee);
anonymityFee = _fee;
}

// Safe transfer function, just in case if rounding error causes pool to not have enough CYCs.
function safeTransfer(IERC20 _token, address _to, uint256 _amount) internal returns (bool) {
uint256 balance = _token.balanceOf(address(this));
if (_amount > balance) {
return _token.transfer(_to, balance);
}
return _token.transfer(_to, _amount);
}

function version() public pure returns(string memory) {
return "2.2";
}

}
15 changes: 15 additions & 0 deletions contracts/ICycloneV2dot2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
pragma solidity <0.6 >=0.4.24;

import "./token/IERC20.sol";

interface ICycloneV2dot2 {

function coinDenomination() external view returns (uint256);
function tokenDenomination() external view returns (uint256);
function cycDenomination() external view returns (uint256);
function token() external view returns (IERC20);
function cycToken() external view returns (IERC20);
function deposit(bytes32 _commitment) external payable;
function withdraw(bytes calldata _proof, bytes32 _root, bytes32 _nullifierHash, address payable _recipient, address payable _relayer, uint256 _fee, uint256 _refund) external payable;
function anonymityFee() external view returns (uint256);
}
18 changes: 18 additions & 0 deletions contracts/cream/IDelegator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
pragma solidity ^0.5.16;

interface IDelegator {
function expScale() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function balanceOfUnderlying(address owner) external returns (uint);
function getAccountSnapshot(address account) external view returns (uint, uint, uint, uint);
function borrowRatePerBlock() external view returns (uint);
function supplyRatePerBlock() external view returns (uint);
function exchangeRateCurrent() external returns (uint);
function exchangeRateStored() external view returns (uint);
function getCash() external view returns (uint);
function accrueInterest() external returns (uint);
function underlying() external view returns (address);
function mint(uint mintAmount) external returns (uint);
function redeem(uint redeemTokens) external returns (uint);
function redeemUnderlying(uint redeemAmount) external returns (uint);
}