UNPKG

@gooddollar/goodcontracts

Version:
926 lines (925 loc) 732 kB
{ "contractName": "cERC20", "abi": [ { "constant": false, "inputs": [ { "internalType": "uint256", "name": "mintAmount", "type": "uint256" } ], "name": "mint", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "uint256", "name": "mintAmount", "type": "uint256" } ], "name": "redeemUnderlying", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [], "name": "exchangeRateCurrent", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "exchangeRateStored", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [ { "internalType": "address", "name": "addr", "type": "address" } ], "name": "balanceOf", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "address", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "transfer", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" } ], "metadata": "{\"compiler\":{\"version\":\"0.5.16+commit.9c3226ce\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"exchangeRateCurrent\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"exchangeRateStored\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"mintAmount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"mintAmount\",\"type\":\"uint256\"}],\"name\":\"redeemUnderlying\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"methods\":{}},\"userdoc\":{\"methods\":{}}},\"settings\":{\"compilationTarget\":{\"/Users/hadar/workspace/GoodDollar/GoodBootstrap/packages/contracts/stakingModel/contracts/SimpleDAIStaking.sol\":\"cERC20\"},\"evmVersion\":\"istanbul\",\"libraries\":{},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"/Users/hadar/workspace/GoodDollar/GoodBootstrap/packages/contracts/contracts/DSMath.sol\":{\"keccak256\":\"0x59fb5b1c4c3f68c1694f6ff5698016ac8ee9401b61e87473113dc103b7a6b0ff\",\"urls\":[\"bzz-raw://7410f43989dea44e64bed870958ce9199606dd8a9783e79fe8deca7b544164af\",\"dweb:/ipfs/QmPrrdvp2ErwL6SJSveB3JANo7ivadSMnk9gpWZD8nR7hy\"]},\"/Users/hadar/workspace/GoodDollar/GoodBootstrap/packages/contracts/contracts/dao/schemes/FeelessScheme.sol\":{\"keccak256\":\"0x7c542d44cbd864ee0ddecbc5016d9d7eb9ce65ab463fdd9f21eb8ce3c6ffc8ea\",\"urls\":[\"bzz-raw://4753dd823c0b0cbe7d7d02e1df64ace07d7a163af3aaff12697aee70a1b45f2f\",\"dweb:/ipfs/QmPkKeH88M3f6UbHphayUbjEBNV4rmoogtDmVundzBKbFK\"]},\"/Users/hadar/workspace/GoodDollar/GoodBootstrap/packages/contracts/contracts/dao/schemes/SchemeGuard.sol\":{\"keccak256\":\"0x32e1b2a172e2c22a6aa757bfcb0722cc67f64c5c2e48df3dec2737903516d565\",\"urls\":[\"bzz-raw://bee57a8eba08348e93a0c18f3ab534e67865c0fa27afe27f5cf234bd5f248915\",\"dweb:/ipfs/Qmd8aveHWcUkYWMyJrJRZPyVXj8MAMBfpBFo6g4Z2VJeo3\"]},\"/Users/hadar/workspace/GoodDollar/GoodBootstrap/packages/contracts/contracts/identity/Identity.sol\":{\"keccak256\":\"0x282184478f1d618d3523b28a84a7f7003dfdc99cad120e5e358d2cce1f5fb491\",\"urls\":[\"bzz-raw://67e17ecb57b24c4fb801abc2c52c3148af3b967e28da4fb7e074d18efb7a57c1\",\"dweb:/ipfs/QmfZ8jXhdTHtxr3gfByRj4HnXnGynX7SLZWWGR8k768aGB\"]},\"/Users/hadar/workspace/GoodDollar/GoodBootstrap/packages/contracts/contracts/identity/IdentityAdminRole.sol\":{\"keccak256\":\"0x8e82c45eefe0da77d017144dfad12602c68124297035937901cdd08ce48c57ff\",\"urls\":[\"bzz-raw://e20e606342b3b35f41a081294cf3f716fe7e27e81d3090daa37c5208f4ef4bc3\",\"dweb:/ipfs/QmVEhHpiwVsksnPyHkfNa5LpTBLxoP9kj5NRZUBv456ewK\"]},\"/Users/hadar/workspace/GoodDollar/GoodBootstrap/packages/contracts/contracts/identity/IdentityGuard.sol\":{\"keccak256\":\"0xcc395c0e8f812201b5ed1f45e7272120ee5ffe73bfe13e325dfd872aef235b22\",\"urls\":[\"bzz-raw://58f93e4b9f48ec31ac151208e4f9e6b356cc23dfc9585d846bc09bf35d6ff7be\",\"dweb:/ipfs/QmdxJHUCaFx8N66kkDhYdA4k6d2V91yBRG6sombuPxBq7X\"]},\"/Users/hadar/workspace/GoodDollar/GoodBootstrap/packages/contracts/stakingModel/contracts/SimpleDAIStaking.sol\":{\"keccak256\":\"0x245246632bb2a59a67277107f64975487806db57f8ad49d14e9626d8d72cd186\",\"urls\":[\"bzz-raw://e767763526d6b497514991f02db1a3da9c925ce32e522213f943cd2b8305cf12\",\"dweb:/ipfs/QmfUjN8Z59ZZNb3vkChmjoTGrpxwrNKNxzjML2pmrhWD2T\"]},\"@daostack/arc/contracts/controller/Avatar.sol\":{\"keccak256\":\"0x4e4fb9d3bc21f42fbfc6a01e9f8aa483a59e9f447b0d604f55b587a81ec09ef4\",\"urls\":[\"bzz-raw://9d3c1c7575cf6075ea1d89921f09b9b8e322b208dacaf0446cd4f64506c999f0\",\"dweb:/ipfs/QmVGDDHAR2ByuQpbgV7BBKXGEAcbaWvejpEZYzHgaagBFK\"]},\"@daostack/arc/contracts/controller/ControllerInterface.sol\":{\"keccak256\":\"0x933e92612c90f292c7b59908958b003f1a13d90b97bed0572b566353c20974dc\",\"urls\":[\"bzz-raw://ce1fcf85b744fcce2dbd4205e3943e9b4ec36a5f0330d4bbf61e65f1a3e0e470\",\"dweb:/ipfs/QmUWiLkBDdekhpy93nTzBtxDHL6yNh6sTUM9G8VbsHcBzy\"]},\"@daostack/arc/contracts/controller/DAOToken.sol\":{\"keccak256\":\"0x9b213e4e3ceeffcfc1c962a43b70b8d763d8091a2dd713fc1989b97bb68786e6\",\"urls\":[\"bzz-raw://51d1d6e85fd90f45395a16b7a3c703c4636e836041f2a13f806e4222ef2e59c1\",\"dweb:/ipfs/QmQjeCeFf7TJzsYjT5scyZUDDixBA8Yy8YppgBuxcKPDnP\"]},\"@daostack/arc/contracts/globalConstraints/GlobalConstraintInterface.sol\":{\"keccak256\":\"0xc006c4591ec10b845a56e13f6791db2c428610953034686d52b2a934b6c8f36f\",\"urls\":[\"bzz-raw://19495cd88280e54d7ddcd043ffeff75e6d3777a0c42debcd61cae448e9497ea0\",\"dweb:/ipfs/QmYxWz7CgFmmddrcVXJKfob5Ktwykc2imNVUwLSR5WR8bJ\"]},\"@daostack/arc/contracts/libs/SafeERC20.sol\":{\"keccak256\":\"0xf6727d432f67046145b078f40c1cc4f6be7b0d12a6678ecaf457edf7550d244c\",\"urls\":[\"bzz-raw://5dba7734c4fed446c084a707761cbc66ba22b8b93ef53c8d4a22a76b6e271690\",\"dweb:/ipfs/QmP2VpPKUqLh4ZZs6LaxHVpCN1zCmiorygL9bMub4mbYCw\"]},\"@daostack/infra/contracts/Reputation.sol\":{\"keccak256\":\"0x3d226dac0fe7d758f0287fb28bc25fcec1f69d19888ae3a550fa49856d61c482\",\"urls\":[\"bzz-raw://8856852c8fe0ba1c9aab326f0a9a6e7f31a238de2778fa5188e6fc399f7eb352\",\"dweb:/ipfs/QmVH4ppR9Dk7v8yxaSgeArdMv9gBBVoQxEf4E3UsqQ4rY2\"]},\"openzeppelin-solidity/contracts/access/Roles.sol\":{\"keccak256\":\"0x659ba0f9a3392cd50a8a5fafaf5dfd8c6a0878f6a4613bceff4e90dceddcd865\",\"urls\":[\"bzz-raw://4ca6617349a9b36fd82e9442a8e445ce9021a174e4399d1457ddc894ee3f5df3\",\"dweb:/ipfs/QmcmyB4cNazZ1sw963Afaijt67M9bDgwmGXCgQajGTD4tm\"]},\"openzeppelin-solidity/contracts/access/roles/PauserRole.sol\":{\"keccak256\":\"0x6506133e295a7199df2ac5dfc52853d9d767f68450b6d0193e9d8056b164ce86\",\"urls\":[\"bzz-raw://9c03592a9e6b1377e401b74eccf175a250232b5a341347d3fc5cb50e9bc86ba2\",\"dweb:/ipfs/QmYAr1yXT89hNUMJ37aiBpD9HtYfBDqmueVCzphRuuU7oZ\"]},\"openzeppelin-solidity/contracts/lifecycle/Pausable.sol\":{\"keccak256\":\"0x3b0ed31deeba7e3c6cfbbe8092a6b427084ee390800a06acdea5c17c8185d606\",\"urls\":[\"bzz-raw://bbdbf9d3754fcdcc85aec0432fabcec914317b1c2081b43753442ed53853a34a\",\"dweb:/ipfs/QmVHWQBn5xQkQEHhyGNEeS5gjjALZEk5ZfcjFt7kL6MoLy\"]},\"openzeppelin-solidity/contracts/math/SafeMath.sol\":{\"keccak256\":\"0x965012d27b4262d7a41f5028cbb30c51ebd9ecd4be8fb30380aaa7a3c64fbc8b\",\"urls\":[\"bzz-raw://1eb493553b27efd46c231a836a83ce50d215639feb97b4aac9f301d0fd034ff5\",\"dweb:/ipfs/QmbMdvSXcqGKxeeYvzF3VrWpoHQyVzMXBhzhAokF2Gdymv\"]},\"openzeppelin-solidity/contracts/ownership/Ownable.sol\":{\"keccak256\":\"0x980de387a1a020a498f53d00f89fecebb12c949a17e8f160093c0303ede2b786\",\"urls\":[\"bzz-raw://f07a2c393caa85f62f0aaa461e3edd50aa59371d4209778ba0558d7f8b4b856c\",\"dweb:/ipfs/QmeN3ZiYGSjCnQFeYaJNgoXjF9eDue1g3e9pqManRkeBtV\"]},\"openzeppelin-solidity/contracts/token/ERC20/ERC20.sol\":{\"keccak256\":\"0xba431353cb4fe70777ac026231eb5468e41aa1e81240a2b8136917acc5fff4dc\",\"urls\":[\"bzz-raw://f40c91ca270bdc9b156f339081a0fc2bbcf73cf5f62b8c73ba555df753ff6619\",\"dweb:/ipfs/QmT8y2VVjGaFdGphpcXrgvdPi2EgF9LuCDgJiJ89LvpFRV\"]},\"openzeppelin-solidity/contracts/token/ERC20/ERC20Burnable.sol\":{\"keccak256\":\"0xa3e68fd7f987c8fbdaf4867d428a69a15f3cf2f69709a5b0a0fbef750b0e6fbc\",\"urls\":[\"bzz-raw://50b1ff986d3b2793850a66c8429d9886fdfb134b1509b3e98714e3e2a71e24f5\",\"dweb:/ipfs/QmSQBYPeY9tbR3ciGRNQqvcWKnNiuxtVMq2MsPWpLwuGGm\"]},\"openzeppelin-solidity/contracts/token/ERC20/IERC20.sol\":{\"keccak256\":\"0x079c4e23ee448f529e43bfa3c4e8fb4be52cd0318ee923a276835bedf45b93d8\",\"urls\":[\"bzz-raw://ee762ff5b7746e41d27dcbcea00c7a3034b58adb1b45ddadddb5b721aff450df\",\"dweb:/ipfs/QmdxqtgEFZjHCoiYyz6pUAQ5iBm7As25jcyEbZoTaAgyDa\"]},\"openzeppelin-solidity/contracts/utils/Address.sol\":{\"keccak256\":\"0x5c731061b804fa256fc8c05150eafe5d20b6bb94541a8f187912bf84f7033f34\",\"urls\":[\"bzz-raw://a3c2d9f046beebab7fb41b1b124a6a124a36f510f7c67365861402ac831bdc71\",\"dweb:/ipfs/QmUtASRgitregbKH83anopLtF9vFQe3BKeAjhUa6F7t9YV\"]}},\"version\":1}", "bytecode": "0x", "deployedBytecode": "0x", "sourceMap": "", "deployedSourceMap": "", "source": "pragma solidity >0.5.4;\n\nimport \"openzeppelin-solidity/contracts/math/SafeMath.sol\";\nimport \"openzeppelin-solidity/contracts/lifecycle/Pausable.sol\";\nimport \"openzeppelin-solidity/contracts/token/ERC20/ERC20.sol\";\nimport \"@daostack/arc/contracts/controller/Avatar.sol\";\nimport \"../../contracts/dao/schemes/FeelessScheme.sol\";\nimport \"../../contracts/identity/Identity.sol\";\nimport \"../../contracts/DSMath.sol\";\n\ninterface cERC20 {\n function mint(uint256 mintAmount) external returns (uint256);\n\n function redeemUnderlying(uint256 mintAmount) external returns (uint256);\n\n function exchangeRateCurrent() external returns (uint256);\n\n function exchangeRateStored() external view returns (uint256);\n\n function balanceOf(address addr) external view returns (uint256);\n\n function transfer(address to, uint256 amount) external returns (bool);\n}\n\n/**\n * @title Staking contract that donates earned interest to the DAO\n * allowing stakers to deposit DAI or withdraw their stake in DAI.\n * The contract buys cDAI and can transfer the daily interest to the DAO\n */\ncontract SimpleDAIStaking is DSMath, Pausable, FeelessScheme {\n using SafeMath for uint256;\n\n // Entity that holds a staker info\n struct Staker {\n // The staked DAI amount\n uint256 stakedDAI;\n // The latest block number which the\n // staker has staked tokens\n uint256 lastStake;\n }\n\n // The map which holds the stakers entities\n mapping(address => Staker) public stakers;\n\n // Emits when new DAI tokens have been staked\n event DAIStaked(\n // The staker address\n address indexed staker,\n // How many tokens have been staked\n uint256 daiValue\n );\n\n // Emits when DAI tokens are being withdrawn\n event DAIStakeWithdraw(\n // The staker that initiate the action\n address indexed staker,\n // The initial DAI value that was staked\n uint256 daiValue,\n // The current DAI value that was staked\n uint256 daiActual\n );\n\n // Emits when the interest is collected\n event InterestCollected(\n // Who is receives the interest\n address recipient,\n // How many cDAI tokens have been transferred\n uint256 cdaiValue,\n // The worth of the transferred tokens in DAI\n uint256 daiValue,\n // Lost amount. A result of different precisions\n uint256 daiPrecisionLoss\n );\n\n // DAI token address\n ERC20 public dai;\n\n // cDAI token address\n cERC20 public cDai;\n\n // The block interval defines the number of\n // blocks that shall be passed before the\n // next execution of `collectUBIInterest`\n uint256 public blockInterval;\n\n // The last block number which\n // `collectUBIInterest` has been executed in\n uint256 public lastUBICollection;\n\n // The total staked DAI amount in the contract\n uint256 public totalStaked = 0;\n\n // How much of the generated interest is donated,\n // meaning no GD is expected in compensation, 1 in mil precision.\n // 100% for phase0 POC\n uint32 public avgInterestDonatedRatio = 1e6;\n\n // The address of the fund manager contract\n address public fundManager;\n\n modifier onlyFundManager {\n require(msg.sender == fundManager, \"Only FundManager can call this method\");\n _;\n }\n\n /**\n * @dev Constructor\n * @param _dai The address of DAI\n * @param _cDai The address of cDAI\n * @param _fundManager The address of the fund manager contract\n * @param _blockInterval How many blocks should be passed before the next execution of `collectUBIInterest`\n * @param _avatar The avatar of the DAO\n * @param _identity The identity contract\n */\n constructor(\n address _dai,\n address _cDai,\n address _fundManager,\n uint256 _blockInterval,\n Avatar _avatar,\n Identity _identity\n ) public FeelessScheme(_identity, _avatar) {\n dai = ERC20(_dai);\n cDai = cERC20(_cDai);\n blockInterval = _blockInterval;\n lastUBICollection = block.number.div(blockInterval);\n fundManager = _fundManager;\n\n // Adds the avatar as a pauser of this contract\n addPauser(address(avatar));\n }\n\n /**\n * @dev Allows the DAO to change the fund manager contract address\n * @param _fundManager Address of the new contract\n */\n function setFundManager(address _fundManager) public onlyAvatar {\n fundManager = _fundManager;\n }\n\n /**\n * @dev Allows a staker to deposit DAI tokens. Notice that `approve` is\n * needed to be executed before the execution of this method.\n * Can be executed only when the contract is not paused.\n * @param _amount The amount of DAI to stake\n */\n function stakeDAI(uint256 _amount) public whenNotPaused {\n require(_amount > 0, \"You need to stake a positive token amount\");\n require(\n dai.transferFrom(msg.sender, address(this), _amount) == true,\n \"transferFrom failed, make sure you approved DAI transfer\"\n );\n\n // approve the transfer to cDAI\n dai.approve(address(cDai), _amount);\n\n // mint ctokens\n uint256 res = cDai.mint(_amount);\n\n // cDAI returns >0 if error happened while minting.\n // Makes sure that there are no errors. If an error\n // has occurred, DAI funds shall be returned.\n if (res > 0) {\n require(res == 0, \"Minting cDai failed, funds returned\");\n }\n\n // updated the staker entity\n Staker storage staker = stakers[msg.sender];\n staker.stakedDAI = staker.stakedDAI.add(_amount);\n staker.lastStake = block.number;\n\n // adds the staked amount to the total staked\n totalStaked = totalStaked.add(_amount);\n\n emit DAIStaked(msg.sender, _amount);\n }\n\n /**\n * @dev Withdraws the sender staked DAI.\n */\n function withdrawStake() public {\n Staker storage staker = stakers[msg.sender];\n require(staker.stakedDAI > 0, \"No DAI staked\");\n require(cDai.redeemUnderlying(staker.stakedDAI) == 0, \"Failed to redeem cDai\");\n uint256 daiWithdraw = staker.stakedDAI;\n\n // updates balance before transfer to prevent re-entry\n staker.stakedDAI = 0;\n\n totalStaked = totalStaked.sub(daiWithdraw);\n\n //redeeming in compound may result in a tiny fraction of precission error\n //so if we redeem 100 DAI we might get something like 99.9999999999\n uint256 daiActual = dai.balanceOf(address(this));\n if (daiActual < daiWithdraw) {\n daiWithdraw = daiActual;\n }\n require(dai.transfer(msg.sender, daiWithdraw), \"withdraw transfer failed\");\n emit DAIStakeWithdraw(msg.sender, daiWithdraw, daiActual);\n }\n\n /**\n * @dev Calculates the worth of the staked cDAI tokens in DAI.\n * @return (uint256) The worth in DAI\n */\n function currentDAIWorth() public view returns (uint256) {\n uint256 er = cDai.exchangeRateStored();\n\n //TODO: why 1e10? cDai is e8 so we should convert it to e28 like exchange rate\n uint256 daiBalance = rmul(cDai.balanceOf(address(this)).mul(1e10), er).div(10);\n return daiBalance;\n }\n\n /**\n * @dev Calculates the current interest that was gained.\n * @return (uint256, uint256, uint256) The interest in cDAI, the interest in DAI,\n * the amount which is not covered by precision of DAI\n */\n function currentUBIInterest()\n public\n view\n returns (\n uint256,\n uint256,\n uint256\n )\n {\n uint256 er = cDai.exchangeRateStored();\n uint256 daiWorth = currentDAIWorth();\n if (daiWorth <= totalStaked) {\n return (0, 0, 0);\n }\n uint256 daiGains = daiWorth.sub(totalStaked);\n // mul by 1e10 to equalize precision otherwise since exchangerate\n // is very big, dividing by it would result in 0.\n uint256 cdaiGains = rdiv(daiGains.mul(1e10), er);\n // gets right most bits not covered by precision of cdai which is\n // only 8 decimals while RAY is 27\n uint256 precisionLossCDaiRay = cdaiGains.mod(1e19);\n // lower back to 8 decimals\n cdaiGains = cdaiGains.div(1e19);\n //div by 1e10 to get results in dai precision 1e18\n uint256 precisionLossDai = rmul(precisionLossCDaiRay, er).div(1e10);\n return (cdaiGains, daiGains, precisionLossDai);\n }\n\n /**\n * @dev Collects gained interest by fundmanager. Can be collected only once\n * in an interval which is defined above.\n * @param _recipient The recipient of cDAI gains\n * @return (uint256, uint256, uint256, uint32) The interest in cDAI, the interest in DAI,\n * the amount which is not covered by precision of DAI, how much of the generated interest is donated\n */\n function collectUBIInterest(address _recipient)\n public\n onlyFundManager\n returns (\n uint256,\n uint256,\n uint256,\n uint32\n )\n {\n // otherwise fund manager has to wait for the next interval\n require(_recipient != address(this), \"Recipient cannot be the staking contract\");\n\n require(canCollect(), \"Need to wait for the next interval\");\n\n (\n uint256 cdaiGains,\n uint256 daiGains,\n uint256 precisionLossDai\n ) = currentUBIInterest();\n lastUBICollection = block.number.div(blockInterval);\n if (cdaiGains > 0)\n require(cDai.transfer(_recipient, cdaiGains), \"collect transfer failed\");\n emit InterestCollected(_recipient, cdaiGains, daiGains, precisionLossDai);\n return (cdaiGains, daiGains, precisionLossDai, avgInterestDonatedRatio);\n }\n\n /**\n * @dev Checks if enough blocks have passed so it would be possible to\n * execute `collectUBIInterest` according to the length of `blockInterval`\n * @return (bool) True if enough blocks have passed\n */\n function canCollect() public view returns (bool) {\n return block.number.div(blockInterval) > lastUBICollection;\n }\n\n /**\n * @dev Start function. Adds this contract to identity as a feeless scheme.\n * Can only be called if scheme is registered\n */\n function start() public onlyRegistered {\n addRights();\n }\n\n /**\n * @dev making the contract inactive\n * NOTICE: this could theoretically result in future interest earned in cdai to remain locked\n * but we dont expect any other stakers but us in SimpleDAIStaking\n */\n function end() public onlyAvatar {\n pause();\n removeRights();\n }\n\n /**\n * @dev method to recover any stuck erc20 tokens (ie compound COMP)\n * @param _token the ERC20 token to recover\n */\n function recover(ERC20 _token) public onlyAvatar {\n uint256 toWithdraw = _token.balanceOf(address(this));\n\n // recover left cDai(stakers token) only when all stakes have been withdrawn\n if (address(_token) == address(cDai)) {\n require(\n totalStaked == 0 && paused(),\n \"can recover cDai only when stakes have been withdrawn\"\n );\n }\n require(_token.transfer(address(avatar), toWithdraw), \"recover transfer failed\");\n }\n}\n", "sourcePath": "/Users/hadar/workspace/GoodDollar/GoodBootstrap/packages/contracts/stakingModel/contracts/SimpleDAIStaking.sol", "ast": { "absolutePath": "/Users/hadar/workspace/GoodDollar/GoodBootstrap/packages/contracts/stakingModel/contracts/SimpleDAIStaking.sol", "exportedSymbols": { "SimpleDAIStaking": [ 9740 ], "cERC20": [ 9160 ] }, "id": 9741, "nodeType": "SourceUnit", "nodes": [ { "id": 9112, "literals": [ "solidity", ">", "0.5", ".4" ], "nodeType": "PragmaDirective", "src": "0:23:29" }, { "absolutePath": "openzeppelin-solidity/contracts/math/SafeMath.sol", "file": "openzeppelin-solidity/contracts/math/SafeMath.sol", "id": 9113, "nodeType": "ImportDirective", "scope": 9741, "sourceUnit": 21309, "src": "25:59:29", "symbolAliases": [], "unitAlias": "" }, { "absolutePath": "openzeppelin-solidity/contracts/lifecycle/Pausable.sol", "file": "openzeppelin-solidity/contracts/lifecycle/Pausable.sol", "id": 9114, "nodeType": "ImportDirective", "scope": 9741, "sourceUnit": 21112, "src": "85:64:29", "symbolAliases": [], "unitAlias": "" }, { "absolutePath": "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol", "file": "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol", "id": 9115, "nodeType": "ImportDirective", "scope": 9741, "sourceUnit": 21850, "src": "150:63:29", "symbolAliases": [], "unitAlias": "" }, { "absolutePath": "@daostack/arc/contracts/controller/Avatar.sol", "file": "@daostack/arc/contracts/controller/Avatar.sol", "id": 9116, "nodeType": "ImportDirective", "scope": 9741, "sourceUnit": 12706, "src": "214:55:29", "symbolAliases": [], "unitAlias": "" }, { "absolutePath": "/Users/hadar/workspace/GoodDollar/GoodBootstrap/packages/contracts/contracts/dao/schemes/FeelessScheme.sol", "file": "../../contracts/dao/schemes/FeelessScheme.sol", "id": 9117, "nodeType": "ImportDirective", "scope": 9741, "sourceUnit": 1228, "src": "270:55:29", "symbolAliases": [], "unitAlias": "" }, { "absolutePath": "/Users/hadar/workspace/GoodDollar/GoodBootstrap/packages/contracts/contracts/identity/Identity.sol", "file": "../../contracts/identity/Identity.sol", "id": 9118, "nodeType": "ImportDirective", "scope": 9741, "sourceUnit": 2381, "src": "326:47:29", "symbolAliases": [], "unitAlias": "" }, { "absolutePath": "/Users/hadar/workspace/GoodDollar/GoodBootstrap/packages/contracts/contracts/DSMath.sol", "file": "../../contracts/DSMath.sol", "id": 9119, "nodeType": "ImportDirective", "scope": 9741, "sourceUnit": 304, "src": "374:36:29", "symbolAliases": [], "unitAlias": "" }, { "baseContracts": [], "contractDependencies": [], "contractKind": "interface", "documentation": null, "fullyImplemented": false, "id": 9160, "linearizedBaseContracts": [ 9160 ], "name": "cERC20", "nodeType": "ContractDefinition", "nodes": [ { "body": null, "documentation": null, "id": 9126, "implemented": false, "kind": "function", "modifiers": [], "name": "mint", "nodeType": "FunctionDefinition", "parameters": { "id": 9122, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 9121, "name": "mintAmount", "nodeType": "VariableDeclaration", "scope": 9126, "src": "449:18:29", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 9120, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "449:7:29", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "value": null, "visibility": "internal" } ], "src": "448:20:29" }, "returnParameters": { "id": 9125, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 9124, "name": "", "nodeType": "VariableDeclaration", "scope": 9126, "src": "487:7:29", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 9123, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "487:7:29", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "value": null, "visibility": "internal" } ], "src": "486:9:29" }, "scope": 9160, "src": "435:61:29", "stateMutability": "nonpayable", "superFunction": null, "visibility": "external" }, { "body": null, "documentation": null, "id": 9133, "implemented": false, "kind": "function", "modifiers": [], "name": "redeemUnderlying", "nodeType": "FunctionDefinition", "parameters": { "id": 9129, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 9128, "name": "mintAmount", "nodeType": "VariableDeclaration", "scope": 9133, "src": "528:18:29", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 9127, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "528:7:29", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "value": null, "visibility": "internal" } ], "src": "527:20:29" }, "returnParameters": { "id": 9132, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 9131, "name": "", "nodeType": "VariableDeclaration", "scope": 9133, "src": "566:7:29", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 9130, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "566:7:29", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "value": null, "visibility": "internal" } ], "src": "565:9:29" }, "scope": 9160, "src": "502:73:29", "stateMutability": "nonpayable", "superFunction": null, "visibility": "external" }, { "body": null, "documentation": null, "id": 9138, "implemented": false, "kind": "function", "modifiers": [], "name": "exchangeRateCurrent", "nodeType": "FunctionDefinition", "parameters": { "id": 9134, "nodeType": "ParameterList", "parameters": [], "src": "609:2:29" }, "returnParameters": { "id": 9137, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 9136, "name": "", "nodeType": "VariableDeclaration", "scope": 9138, "src": "630:7:29", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 9135, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "630:7:29", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "value": null, "visibility": "internal" } ], "src": "629:9:29" }, "scope": 9160, "src": "581:58:29", "stateMutability": "nonpayable", "superFunction": null, "visibility": "external" }, { "body": null, "documentation": null, "id": 9143, "implemented": false, "kind": "function", "modifiers": [], "name": "exchangeRateStored", "nodeType": "FunctionDefinition", "parameters": { "id": 9139, "nodeType": "ParameterList", "parameters": [], "src": "672:2:29" }, "returnParameters": { "id": 9142, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 9141, "name": "", "nodeType": "VariableDeclaration", "scope": 9143, "src": "698:7:29", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 9140, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "698:7:29", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "value": null, "visibility": "internal" } ], "src": "697:9:29" }, "scope": 9160, "src": "645:62:29", "stateMutability": "view", "superFunction": null, "visibility": "external" }, { "body": null, "documentation": null, "id": 9150, "implemented": false, "kind": "function", "modifiers": [], "name": "balanceOf", "nodeType": "FunctionDefinition", "parameters": { "id": 9146, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 9145, "name": "addr", "nodeType": "VariableDeclaration", "scope": 9150, "src": "732:12:29", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" }, "typeName": { "id": 9144, "name": "address", "nodeType": "ElementaryTypeName", "src": "732:7:29", "stateMutability": "nonpayable", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "value": null, "visibility": "internal" } ], "src": "731:14:29" }, "returnParameters": { "id": 9149, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 9148, "name": "", "nodeType": "VariableDeclaration", "scope": 9150, "src": "769:7:29", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 9147, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "769:7:29", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "value": null, "visibility": "internal" } ], "src": "768:9:29" }, "scope": 9160, "src": "713:65:29", "stateMutability": "view", "superFunction": null, "visibility": "external" }, { "body": null, "documentation": null, "id": 9159, "implemented": false, "kind": "function", "modifiers": [], "name": "transfer", "nodeType": "FunctionDefinition", "parameters": { "id": 9155, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 9152, "name": "to", "nodeType": "VariableDeclaration", "scope": 9159, "src": "802:10:29", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" }, "typeName": { "id": 9151, "name": "address", "nodeType": "ElementaryTypeName", "src": "802:7:29", "stateMutability": "nonpayable", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "value": null, "visibility": "internal" }, { "constant": false, "id": 9154, "name": "amount", "nodeType": "VariableDeclaration", "scope": 9159, "src": "814:14:29", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 9153, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "814:7:29", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "value": null, "visibility": "internal" } ], "src": "801:28:29" }, "returnParameters": { "id": 9158, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 9157, "name": "", "nodeType": "VariableDeclaration", "scope": 9159, "src": "848:4:29", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_bool", "typeString": "bool" }, "typeName": { "id": 9156, "name": "bool", "nodeType": "ElementaryTypeName", "src": "848:4:29", "typeDescriptions": { "typeIdentifier": "t_bool", "typeString": "bool" } }, "value": null, "visibility": "internal" } ], "src": "847:6:29" }, "scope": 9160, "src": "784:70:29", "stateMutability": "nonpayable", "superFunction": null, "visibility": "external" } ], "scope": 9741, "src": "412:444:29" }, { "baseContracts": [ { "arguments": null, "baseName": { "contractScope": null, "id": 9161, "name": "DSMath", "nodeType": "UserDefinedTypeName", "referencedDeclaration": 303, "src": "1102:6:29", "typeDescriptions": { "typeIdentifier": "t_contract$_DSMath_$303", "typeString": "contract DSMath" } }, "id": 9162, "nodeType": "InheritanceSpecifier", "src": "1102:6:29" }, { "arguments": null, "baseName": { "contractScope": null, "id": 9163, "name": "Pausable", "nodeType": "UserDefinedTypeName", "referencedDeclaration": 21111, "src": "1110:8:29", "typeDescriptions": { "typeIdentifier": "t_contract$_Pausable_$21111", "typeString": "contract Pausable" } }, "id": 9164, "nodeType": "InheritanceSpecifier", "src": "1110:8:29" }, { "arguments": null, "baseName": { "contractScope": null, "id": 9165, "name": "FeelessScheme", "nodeType": "UserDefinedTypeName", "referencedDeclaration": 1227, "src": "1120:13:29", "typeDescriptions": { "typeIdentifier": "t_contract$_FeelessScheme_$1227", "typeString": "contract FeelessScheme" } }, "id": 9166, "nodeType": "InheritanceSpecifier", "src": "1120:13:29" } ], "contractDependencies": [ 303, 1227, 1423, 2667, 20947, 21111, 21417 ], "contractKind": "contract", "documentation": "@title Staking contract that donates earned interest to the DAO\nallowing stakers to deposit DAI or withdraw their stake in DAI.\nThe contract buys cDAI and can transfer the daily interest to the DAO", "fullyImplemented": true, "id": 9740, "linearizedBaseContracts": [ 9740, 1227, 2667, 1423, 21417, 21111, 20947, 303 ], "name": "SimpleDAIStaking", "nodeType": "ContractDefinition", "nodes": [ { "id": 9169, "libraryName": { "contractScope": null, "id": 9167, "name": "SafeMath", "nodeType": "UserDefinedTypeName", "referencedDeclaration": 21308, "src": "1146:8:29", "typeDescriptions": { "typeIdentifier": "t_contract$_SafeMath_$21308", "typeString": "library SafeMath" } }, "nodeType": "UsingForDirective", "src": "1140:27:29", "typeName": { "id": 9168, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "1159:7:29", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } } }, { "canonicalName": "SimpleDAIStaking.Staker", "id": 9174, "members": [ { "constant": false, "id": 9171, "name": "stakedDAI", "nodeType": "VariableDeclaration", "scope": 9174, "src": "1269:17:29", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 9170, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "1269:7:29", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "value": null, "visibility": "internal" }, { "constant": false, "id": 9173, "name": "lastStake", "nodeType": "VariableDeclaration", "scope": 9174, "src": "1377:17:29", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 9172, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "1377:7:29", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "value": null, "visibility": "internal" } ], "name": "Staker", "nodeType": "StructDefinition", "scope": 9740, "src": "1212:189:29", "visibility": "public" }, { "constant": false, "id": 9178, "name": "stakers", "nodeType": "VariableDeclaration", "scope": 9740, "src": "1455:41:29", "stateVariable": true, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_mapping$_t_address_$_t_struct$_Staker_$9174_storage_$", "typeString": "mapping(address => struct SimpleDAIStaking.Staker)" }, "typeName": { "id": 9177, "keyType": { "id": 9175, "name": "address", "nodeType": "ElementaryTypeName", "src": "1463:7:29", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "nodeType": "Mapping", "src": "1455:26:29", "typeDescriptions": { "typeIdentifier": "t_mapping$_t_address_$_t_struct$_Staker_$9174_storage_$", "typeString": "mapping(address => struct SimpleDAIStaking.Staker)" }, "valueType": { "contractScope": null, "id": 9176, "name": "Staker", "nodeType": "UserDefinedTypeName", "referencedDeclaration": 9174, "src": "1474:6:29", "typeDescriptions": { "typeIdentifier": "t_struct$_Staker_$9174_storage_ptr", "typeString": "struct SimpleDAIStaking.Staker" } } },