Address Contract Verified
Address
0x7d470C1a647719eE3e1fD275bBc62ad7b98293fe
Balance
0 ETH
Nonce
1
Code Size
13981 bytes
Creator
0x67d09B87...0707 at tx 0x9c480421...196c68
Indexed Transactions
0
Contract Bytecode
13981 bytes
0x608060405234801561001057600080fd5b50600436106102485760003560e01c806379ba50971161013b578063b66503cf116100b8578063e9fad8ee1161007c578063e9fad8ee14610775578063ee6c8f531461077d578063f08c3e90146107b9578063f122977714610866578063f28e5a711461088c57610248565b8063b66503cf14610691578063bcd11014146106bd578063d0ed26ae146106e3578063d1ab58f414610719578063e70b9e271461074757610248565b80638da5cb5b116100ff5780638da5cb5b146105e957806391b4ded9146105f1578063a694fc3a146105f9578063ac46888114610616578063b5d5b5fa1461064c57610248565b806379ba50971461053c5780637bb7bed11461054457806384f963f41461056157806388450f901461058f5780638980f11f146105bd57610248565b80633f695b45116101c9578063638634ee1161018d578063638634ee146104845780636920b9d6146104aa5780637035ab98146104e057806370a082311461050e57806372f702f31461053457610248565b80633f695b4514610381578063454f06ec146103af57806348e5d9f8146103e157806353a47bb7146104445780635c975abb1461046857610248565b8063211dc32d11610210578063211dc32d146102dc5780632378bea61461030a5780632e1a7d4d146103365780633d18b912146103535780633d9c055b1461035b57610248565b8063045544431461024d5780631627540c1461026757806316c38b3c1461028f57806318160ddd146102ae57806318864a36146102b6575b600080fd5b6102556108b2565b60408051918252519081900360200190f35b61028d6004803603602081101561027d57600080fd5b50356001600160a01b03166108b8565b005b61028d600480360360208110156102a557600080fd5b50351515610914565b61025561098e565b610255600480360360208110156102cc57600080fd5b50356001600160a01b0316610994565b610255600480360360408110156102f257600080fd5b506001600160a01b03813581169160200135166109cc565b61028d6004803603604081101561032057600080fd5b506001600160a01b038135169060200135610a7f565b61028d6004803603602081101561034c57600080fd5b5035610c16565b61028d611105565b6102556004803603602081101561037157600080fd5b50356001600160a01b0316611616565b61028d6004803603604081101561039757600080fd5b506001600160a01b03813581169160200135166116d9565b610255600480360360608110156103c557600080fd5b506001600160a01b0381351690602081013590604001356117a4565b610407600480360360208110156103f757600080fd5b50356001600160a01b03166118b1565b604080516001600160a01b0390971687526020870195909552858501939093526060850191909152608084015260a0830152519081900360c00190f35b61044c6118f0565b604080516001600160a01b039092168252519081900360200190f35b6104706118ff565b604080519115158252519081900360200190f35b6102556004803603602081101561049a57600080fd5b50356001600160a01b0316611908565b610255600480360360608110156104c057600080fd5b506001600160a01b0381358116916020810135909116906040013561192f565b610255600480360360408110156104f657600080fd5b506001600160a01b0381358116916020013516611a00565b6102556004803603602081101561052457600080fd5b50356001600160a01b0316611a1d565b61044c611a38565b61028d611a4c565b61044c6004803603602081101561055a57600080fd5b5035611b08565b6102556004803603604081101561057757600080fd5b506001600160a01b0381358116916020013516611b2f565b610255600480360360408110156105a557600080fd5b506001600160a01b0381358116916020013516611b76565b61028d600480360360408110156105d357600080fd5b506001600160a01b038135169060200135611c6c565b61044c611db2565b610255611dc1565b61028d6004803603602081101561060f57600080fd5b5035611dc7565b6102556004803603606081101561062c57600080fd5b506001600160a01b03813581169160208101359091169060400135612340565b6106786004803603604081101561066257600080fd5b506001600160a01b03813516906020013561237b565b6040805192835260208301919091528051918290030190f35b61028d600480360360408110156106a757600080fd5b506001600160a01b0381351690602001356123b4565b610255600480360360208110156106d357600080fd5b50356001600160a01b03166127a8565b61028d600480360360608110156106f957600080fd5b506001600160a01b038135811691602081013590911690604001356127da565b6102556004803603604081101561072f57600080fd5b506001600160a01b03813581169160200135166129d5565b6102556004803603604081101561075d57600080fd5b506001600160a01b03813581169160200135166129f2565b61028d612a0f565b6102556004803603608081101561079357600080fd5b506001600160a01b03813581169160208101359091169060408101359060600135612a32565b6107f5600480360360808110156107cf57600080fd5b506001600160a01b03813581169160208101359091169060408101359060600135612ad5565b60405180806020018281038252838181518152602001915080516000925b8184101561085557602080850284010151606080838360005b8381101561084457818101518382015260200161082c565b505050509050019260010192610813565b925050509250505060405180910390f35b6102556004803603602081101561087c57600080fd5b50356001600160a01b0316612caf565b61028d600480360360208110156108a257600080fd5b50356001600160a01b0316612d56565b600b5481565b6108c0613058565b600280546001600160a01b0383166001600160a01b0319909116811790915560408051918252517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce229181900360200190a150565b61091c613058565b60045460ff16151581151514156109325761098b565b6004805460ff1916821515179081905560ff161561094f57426003555b6004546040805160ff90921615158252517f8fb6c181ee25a520cf3dd6565006ef91229fcfe5a989566c2a3b8c115570cec59181900360200190a15b50565b60095490565b6001600160a01b038082166000818152600560209081526040808320549094168252600881528382209282529190915220545b919050565b6001600160a01b038083166000818152600860209081526040808320948616808452948252808320549383526007825280832094835293905291822054610a769190610a6a90670de0b6b3a764000090610a5e90610a3990610a2d89612caf565b9063ffffffff6130a116565b6001600160a01b0389166000908152600a60205260409020549063ffffffff6130fe16565b9063ffffffff61315716565b9063ffffffff6131c116565b90505b92915050565b6001600160a01b038216610ad5576040805162461bcd60e51b815260206004820152601860248201527716995c9bc81859191c995cdcc81b9bdd08185b1b1bddd95960421b604482015290519081900360640190fd5b6001600160a01b0382166000908152600560205260409020600201544211610b44576040805162461bcd60e51b815260206004820152601a60248201527f52657761726420706572696f64207374696c6c20616374697665000000000000604482015290519081900360640190fd5b6001600160a01b03828116600090815260056020526040902054163314610b6a57600080fd5b60008111610bbf576040805162461bcd60e51b815260206004820181905260248201527f526577617264206475726174696f6e206d757374206265206e6f6e2d7a65726f604482015290519081900360640190fd5b6001600160a01b038216600081815260056020908152604091829020600101849055815184815291517fad2f86b01ed93b4b3a150d448c61a4f5d8d38075d3c0c64cc0a26fd6e1f495459281900390910190a25050565b600080546001018082559033905b600654811015610e2f57600060068281548110610c3d57fe5b6000918252602090912001546009546001600160a01b039091169150610cf3576001600160a01b0380821660008181526005602081815260408084208054909616845260088252808420948452938152929091205491526003820154600490920154610cc092610a6a91610cb490610a2d87611908565b9063ffffffff6130fe16565b6001600160a01b038083166000818152600560209081526040808320549094168252600881528382209282529190915220555b610cfc81612caf565b6001600160a01b03821660009081526005602081905260409091200155610d2281611908565b6001600160a01b03808316600090815260056020526040902060040191909155831615610e2657610d5383826109cc565b6001600160a01b0380851660009081526008602090815260408083209386168352929052908120919091555b6001600160a01b0384166000908152600c6020526040902054811015610def57610daa84838361192f565b6001600160a01b038086166000908152600d60209081526040808320938716835292905220805483908110610ddb57fe5b600091825260209091200155600101610d7f565b506001600160a01b038082166000818152600560208181526040808420909201549488168352600781528183209383529290925220555b50600101610c24565b5060008311610e79576040805162461bcd60e51b8152602060048201526011602482015270043616e6e6f74207769746864726177203607c1b604482015290519081900360640190fd5b336000908152600a6020526040812054610e9490859061321b565b905060008090506000610ec86006600081548110610eae57fe5b6000918252602090912001546001600160a01b0316611908565b905060005b336000908152600c6020526040902054811015610fb0576000610ef13384846117a4565b90508015610f9c57610f03818661321b565b336000908152600c602052604090208054919250610f489183919085908110610f2857fe5b9060005260206000209060020201600101546130a190919063ffffffff16565b336000908152600c60205260409020805484908110610f6357fe5b6000918252602090912060016002909202010155610f87858263ffffffff6130a116565b9450610f99848263ffffffff6131c116565b93505b84610fa75750610fb0565b50600101610ecd565b50600082118015610fc15750858211155b8015610fdc5750336000908152600a60205260409020548211155b6110175760405162461bcd60e51b815260040180806020018281038252602d815260200180613586602d913960400191505060405180910390fd5b60095461102a908363ffffffff6130a116565b600955336000908152600a602052604090205461104d908363ffffffff6130a116565b336000818152600a6020526040902091909155600454611083916101009091046001600160a01b0316908463ffffffff61323116565b60408051838152905133917f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d5919081900360200190a2505050506000548114611101576040805162461bcd60e51b815260206004820152601f60248201526000805160206134c5833981519152604482015290519081900360640190fd5b5050565b600080546001018082559033905b6006548110156113125760006006828154811061112c57fe5b6000918252602090912001546009546001600160a01b0390911691506111d6576001600160a01b03808216600081815260056020818152604080842080549096168452600882528084209484529381529290912054915260038201546004909201546111a392610a6a91610cb490610a2d87611908565b6001600160a01b038083166000818152600560209081526040808320549094168252600881528382209282529190915220555b6111df81612caf565b6001600160a01b0382166000908152600560208190526040909120015561120581611908565b6001600160a01b038083166000908152600560205260409020600401919091558316156113095761123683826109cc565b6001600160a01b0380851660009081526008602090815260408083209386168352929052908120919091555b6001600160a01b0384166000908152600c60205260409020548110156112d25761128d84838361192f565b6001600160a01b038086166000908152600d602090815260408083209387168352929052208054839081106112be57fe5b600091825260209091200155600101611262565b506001600160a01b038082166000818152600560208181526040808420909201549488168352600781528183209383529290925220555b50600101611113565b5060005b6006548110156115d05760006006828154811061132f57fe5b60009182526020808320909101543383526008825260408084206001600160a01b03909216808552919092529082205460068054929450909283929091829161137b91908390610eae57fe5b905060005b336000908152600c602052604090205481101561147d5760006113a533888585612a32565b90508015611469576113b7818661321b565b336000908152600d602090815260408083206001600160a01b038c1684529091529020805491925061140991839190859081106113f057fe5b90600052602060002001546130a190919063ffffffff16565b336000908152600d602090815260408083206001600160a01b038c168452909152902080548490811061143857fe5b600091825260209091200155611454858263ffffffff6130a116565b9450611466848263ffffffff6131c116565b93505b84611474575061147d565b50600101611380565b50838211156114bd5760405162461bcd60e51b815260040180806020018281038252602481526020018061351a6024913960400191505060405180910390fd5b81156115bf573360009081526008602090815260408083206001600160a01b03891684529091529020546114f7908363ffffffff6130a116565b3360008181526008602090815260408083206001600160a01b038b1680855290835281842095909555928252600e81528282209382529290925290205461154590839063ffffffff6131c116565b336000818152600e602090815260408083206001600160a01b038b1680855292529091209290925561157e91908463ffffffff61323116565b6040805183815290516001600160a01b0387169133917f540798df468d7b23d11f156fdb954cb19ad414d150722a7b6d55ba369dea792e9181900360200190a35b505060019093019250611316915050565b5050600054811461098b576040805162461bcd60e51b815260206004820152601f60248201526000805160206134c5833981519152604482015290519081900360640190fd5b6000806000905060006116306006600081548110610eae57fe5b905060005b6001600160a01b0385166000908152600c602052604090205481101561167a576116706116638684846117a4565b849063ffffffff6131c116565b9250600101611635565b506001600160a01b0384166000908152600a60205260409020548211156116d25760405162461bcd60e51b81526004018080602001828103825260258152602001806135616025913960400191505060405180910390fd5b5092915050565b6116e1613058565b6001600160a01b0382161580159061170157506001600160a01b03811615155b61174d576040805162461bcd60e51b815260206004820152601860248201527716995c9bc81859191c995cdcc81b9bdd08185b1b1bddd95960421b604482015290519081900360640190fd5b6001600160a01b0382811660008181526005602052604080822080546001600160a01b0319169486169485179055517f55bf2bef00411dcc98731a296d0f25718b09b68c42f85f0346efc0ba0b4009e49190a35050565b6001600160a01b0383166000908152600c602052604081208054829190849081106117cb57fe5b90600052602060002090600202016001015411801561186457506005600060066000815481106117f757fe5b60009182526020808320909101546001600160a01b031683528201929092526040019020600201544210158061186457506001600160a01b0384166000908152600c6020526040902080548491908490811061184f57fe5b90600052602060002090600202016000015411155b156118a6576001600160a01b0384166000908152600c6020526040902080548390811061188d57fe5b90600052602060002090600202016001015490506118aa565b5060005b9392505050565b60056020819052600091825260409091208054600182015460028301546003840154600485015494909501546001600160a01b03909316949193909286565b6002546001600160a01b031681565b60045460ff1681565b6001600160a01b038116600090815260056020526040812060020154610a7990429061321b565b6001600160a01b038084166000908152600d60209081526040808320938616835292905290812080546119f891908490811061196757fe5b60009182526020808320909101546001600160a01b03808916845260078352604080852091891685529252912054610a6a90670de0b6b3a764000090610a5e906119b490610a2d8a612caf565b6001600160a01b038a166000908152600c602052604090208054899081106119d857fe5b9060005260206000209060020201600101546130fe90919063ffffffff16565b949350505050565b600760209081526000928352604080842090915290825290205481565b6001600160a01b03166000908152600a602052604090205490565b60045461010090046001600160a01b031681565b6002546001600160a01b03163314611a955760405162461bcd60e51b81526004018080602001828103825260358152602001806134e56035913960400191505060405180910390fd5b600154600254604080516001600160a01b03938416815292909116602083015280517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c9281900390910190a160028054600180546001600160a01b03199081166001600160a01b03841617909155169055565b60068181548110611b1557fe5b6000918252602090912001546001600160a01b0316905081565b600080611b3c84846109cc565b6001600160a01b038086166000908152600e60209081526040808320938816835292905220549091506119f890829063ffffffff6131c116565b600080611b8384846109cc565b9050600081905060008090506000611ba26006600081548110610eae57fe5b905060005b6001600160a01b0388166000908152600c6020526040902054811015611c22576000611bd589898585612a32565b90508015611c0e57611be7818661321b565b9050611bf9858263ffffffff6130a116565b9450611c0b848263ffffffff6131c116565b93505b84611c195750611c22565b50600101611ba7565b5083821115611c625760405162461bcd60e51b815260040180806020018281038252602481526020018061351a6024913960400191505060405180910390fd5b5095945050505050565b611c74613058565b6004546001600160a01b03838116610100909204161415611cdc576040805162461bcd60e51b815260206004820152601d60248201527f43616e6e6f74207769746864726177207374616b696e6720746f6b656e000000604482015290519081900360640190fd5b6001600160a01b03821660009081526005602052604090206004015415611d4a576040805162461bcd60e51b815260206004820152601c60248201527f43616e6e6f742077697468647261772072657761726420746f6b656e00000000604482015290519081900360640190fd5b600154611d6a906001600160a01b0384811691168363ffffffff61323116565b604080516001600160a01b03841681526020810183905281517f8c1256b8896378cd5044f80c202f9772b9d77dc85c8a6eb51967210b09bfaa28929181900390910190a15050565b6001546001600160a01b031681565b60035481565b600080546001019081905560045460ff1615611e145760405162461bcd60e51b815260040180806020018281038252603c815260200180613603603c913960400191505060405180910390fd5b3360005b60065481101561201757600060068281548110611e3157fe5b6000918252602090912001546009546001600160a01b039091169150611edb576001600160a01b0380821660008181526005602081815260408084208054909616845260088252808420948452938152929091205491526003820154600490920154611ea892610a6a91610cb490610a2d87611908565b6001600160a01b038083166000818152600560209081526040808320549094168252600881528382209282529190915220555b611ee481612caf565b6001600160a01b03821660009081526005602081905260409091200155611f0a81611908565b6001600160a01b0380831660009081526005602052604090206004019190915583161561200e57611f3b83826109cc565b6001600160a01b0380851660009081526008602090815260408083209386168352929052908120919091555b6001600160a01b0384166000908152600c6020526040902054811015611fd757611f9284838361192f565b6001600160a01b038086166000908152600d60209081526040808320938716835292905220805483908110611fc357fe5b600091825260209091200155600101611f67565b506001600160a01b038082166000818152600560208181526040808420909201549488168352600781528183209383529290925220555b50600101611e18565b506000831161205e576040805162461bcd60e51b815260206004820152600e60248201526d043616e6e6f74207374616b6520360941b604482015290519081900360640190fd5b60048054604080516370a0823160e01b81523093810193909352516000926101009092046001600160a01b0316916370a08231916024808301926020929190829003018186803b1580156120b157600080fd5b505afa1580156120c5573d6000803e3d6000fd5b505050506040513d60208110156120db57600080fd5b50516004549091506120fd9061010090046001600160a01b0316333087613288565b60006121a682600460019054906101000a90046001600160a01b03166001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561216e57600080fd5b505afa158015612182573d6000803e3d6000fd5b505050506040513d602081101561219857600080fd5b50519063ffffffff6130a116565b90506121b2818661321b565b6009549091506121c8908263ffffffff6131c116565b600955336000908152600a60205260409020546121eb908263ffffffff6131c116565b336000908152600a6020908152604080832093909355600c90528190208151808301909252600b54909190819061222990429063ffffffff6131c116565b815260209081018490528254600181810185556000948552828520845160029093020191825592909101519101555b6006548110156122c257336000908152600d60205260408120600680549192918490811061228257fe5b60009182526020808320909101546001600160a01b0316835282810193909352604090910181208054600181810183559183529282209092015501612258565b5060408051828152905133917f9e71bc8eea02a63969f509818f2dafb9254532904319f9dbda79b67bd34a5f3d919081900360200190a25050506000548114611101576040805162461bcd60e51b815260206004820152601f60248201526000805160206134c5833981519152604482015290519081900360640190fd5b600d602052826000526040600020602052816000526040600020818154811061236557fe5b9060005260206000200160009250925050505481565b600c602052816000526040600020818154811061239457fe5b600091825260209091206002909102018054600190910154909250905082565b6000805b6006548110156125b7576000600682815481106123d157fe5b6000918252602090912001546009546001600160a01b03909116915061247b576001600160a01b038082166000818152600560208181526040808420805490961684526008825280842094845293815292909120549152600382015460049092015461244892610a6a91610cb490610a2d87611908565b6001600160a01b038083166000818152600560209081526040808320549094168252600881528382209282529190915220555b61248481612caf565b6001600160a01b038216600090815260056020819052604090912001556124aa81611908565b6001600160a01b038083166000908152600560205260409020600401919091558316156125ae576124db83826109cc565b6001600160a01b0380851660009081526008602090815260408083209386168352929052908120919091555b6001600160a01b0384166000908152600c60205260409020548110156125775761253284838361192f565b6001600160a01b038086166000908152600d6020908152604080832093871683529290522080548390811061256357fe5b600091825260209091200155600101612507565b506001600160a01b038082166000818152600560208181526040808420909201549488168352600781528183209383529290925220555b506001016123b8565b506001600160a01b038381166000908152600560205260409020541633146125de57600080fd5b6125f96001600160a01b03841633308563ffffffff61328816565b6001600160a01b0383166000908152600560205260409020600201544210612669576001600160a01b03831660009081526005602052604090206001015461264890839063ffffffff61315716565b6001600160a01b038416600090815260056020526040902060030155612718565b6001600160a01b038316600090815260056020526040812060020154612695904263ffffffff6130a116565b6001600160a01b038516600090815260056020526040812060030154919250906126c690839063ffffffff6130fe16565b6001600160a01b0386166000908152600560205260409020600101549091506126f990610a5e868463ffffffff6131c116565b6001600160a01b03861660009081526005602052604090206003015550505b6001600160a01b0383166000908152600560205260409020426004820181905560019091015461274e919063ffffffff6131c116565b6001600160a01b03841660009081526005602090815260409182902060020192909255805184815290517fde88a922e0d3b88b24e9623efeb464919c6bf9f66857a65e2bfcf2ce87a9433d929181900390910190a1505050565b6001600160a01b03811660009081526005602052604081206001810154600390910154610a799163ffffffff6130fe16565b6127e2613058565b6001600160a01b0383166000908152600560205260409020600101541561283a5760405162461bcd60e51b815260040180806020018281038252602381526020018061353e6023913960400191505060405180910390fd5b6001600160a01b0383161580159061285a57506001600160a01b03821615155b6128a6576040805162461bcd60e51b815260206004820152601860248201527716995c9bc81859191c995cdcc81b9bdd08185b1b1bddd95960421b604482015290519081900360640190fd5b600081116128fb576040805162461bcd60e51b815260206004820181905260248201527f526577617264206475726174696f6e206d757374206265206e6f6e2d7a65726f604482015290519081900360640190fd5b6006805460018181019092557ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0180546001600160a01b038087166001600160a01b031992831681179093556000838152600560205260408082208054938916939094168317845592909401859055905190927f55bf2bef00411dcc98731a296d0f25718b09b68c42f85f0346efc0ba0b4009e491a36040805182815290516001600160a01b038516917fad2f86b01ed93b4b3a150d448c61a4f5d8d38075d3c0c64cc0a26fd6e1f49545919081900360200190a2505050565b600e60209081526000928352604080842090915290825290205481565b600860209081526000928352604080842090915290825290205481565b336000908152600a6020526040902054612a2890610c16565b612a30611105565b565b6000600560006006600081548110612a4657fe5b60009182526020808320909101546001600160a01b0316835282019290925260400190206002015442101580612ab357506001600160a01b0385166000908152600c60205260409020805484919084908110612a9e57fe5b90600052602060002090600202016000015411155b15612aca57612ac385858461192f565b90506119f8565b506000949350505050565b6001600160a01b0384166000908152600c602052604090205460609080831115612afd578092505b83831015612b52576040805162461bcd60e51b815260206004820181905260248201527f73746172742063616e6e6f7420626520686967686572207468616e2073746f70604482015290519081900360640190fd5b604080518585038082526020808202830101909252606090828015612b9157816020015b612b7e6134a6565b815260200190600190039081612b765790505b50905060005b82811015612ca3576001600160a01b0389166000908152600c602052604090208054888301908110612bc557fe5b906000526020600020906002020160000154828281518110612be357fe5b6020026020010151600060038110612bf757fe5b602002018181525050600c60008a6001600160a01b03166001600160a01b0316815260200190815260200160002081880181548110612c3257fe5b906000526020600020906002020160010154828281518110612c5057fe5b6020026020010151600160038110612c6457fe5b6020020152612c76898989840161192f565b828281518110612c8257fe5b6020026020010151600260038110612c9657fe5b6020020152600101612b97565b50979650505050505050565b600060095460001415612cdf57506001600160a01b038116600090815260056020819052604090912001546109c7565b6009546001600160a01b03831660009081526005602052604090206003810154600490910154610a7992612d2d929091610a5e91670de0b6b3a764000091610cb491908290610a2d8b611908565b6001600160a01b038416600090815260056020819052604090912001549063ffffffff6131c116565b6000805460010180825590805b600654811015612f6257600060068281548110612d7c57fe5b6000918252602090912001546009546001600160a01b039091169150612e26576001600160a01b0380821660008181526005602081815260408084208054909616845260088252808420948452938152929091205491526003820154600490920154612df392610a6a91610cb490610a2d87611908565b6001600160a01b038083166000818152600560209081526040808320549094168252600881528382209282529190915220555b612e2f81612caf565b6001600160a01b03821660009081526005602081905260409091200155612e5581611908565b6001600160a01b03808316600090815260056020526040902060040191909155831615612f5957612e8683826109cc565b6001600160a01b0380851660009081526008602090815260408083209386168352929052908120919091555b6001600160a01b0384166000908152600c6020526040902054811015612f2257612edd84838361192f565b6001600160a01b038086166000908152600d60209081526040808320938716835292905220805483908110612f0e57fe5b600091825260209091200155600101612eb2565b506001600160a01b038082166000818152600560208181526040808420909201549488168352600781528183209383529290925220555b50600101612d63565b506001600160a01b03838116600090815260056020526040902054163314612f8957600080fd5b6001600160a01b038316600090815260056020526040902060020154421015612fb157600080fd5b3360009081526008602090815260408083206001600160a01b03871684529091529020548015613012573360008181526008602090815260408083206001600160a01b0389168085529252822091909155613012918363ffffffff61323116565b50506000548114611101576040805162461bcd60e51b815260206004820152601f60248201526000805160206134c5833981519152604482015290519081900360640190fd5b6001546001600160a01b03163314612a305760405162461bcd60e51b815260040180806020018281038252602f8152602001806135b3602f913960400191505060405180910390fd5b6000828211156130f8576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b60008261310d57506000610a79565b8282028284828161311a57fe5b0414610a765760405162461bcd60e51b81526004018080602001828103825260218152602001806135e26021913960400191505060405180910390fd5b60008082116131ad576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b60008284816131b857fe5b04949350505050565b600082820183811015610a76576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b600081831061322a5781610a76565b5090919050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526132839084906132e8565b505050565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526132e29085906132e8565b50505050565b6132fa826001600160a01b03166134a0565b61334b576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b602083106133895780518252601f19909201916020918201910161336a565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146133eb576040519150601f19603f3d011682016040523d82523d6000602084013e6133f0565b606091505b509150915081613447576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b8051156132e25780806020019051602081101561346357600080fd5b50516132e25760405162461bcd60e51b815260040180806020018281038252602a81526020018061363f602a913960400191505060405180910390fd5b3b151590565b6040518060600160405280600390602082028038833950919291505056fe5265656e7472616e637947756172643a207265656e7472616e742063616c6c00596f75206d757374206265206e6f6d696e61746564206265666f726520796f752063616e20616363657074206f776e657273686970496e7465726e616c206572726f72206f6e207265776172642063616c63756c6174696f6e7265776172642064617461206f6620746f6b656e20686173206265656e206164646564696e7465726e616c20636f6e73697374656e6379206f6e207374616b656420616d6f756e744e6f20756e6c6f636b6564207374616b6520617661696c61626c65206f7220696e7465726e616c206572726f724f6e6c792074686520636f6e7472616374206f776e6572206d617920706572666f726d207468697320616374696f6e536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775468697320616374696f6e2063616e6e6f7420626520706572666f726d6564207768696c652074686520636f6e7472616374206973207061757365645361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a265627a7a72315820e97d3894c7249d70362fb6d943234617b12aab0fe4f00b4e1e9732de651e295364736f6c63430005110032
Verified Source Code Full Match
Compiler: v0.5.17+commit.d19bba13
EVM: istanbul
Optimization: Yes (200 runs)
MultiRewards.sol 971 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.5.17;
library Address {
/**
* _ _ _ _
* | | | |_ _ _ __ ___ __ _ _ __ (_) |_ _ _
* | |_| | | | | '_ ` _ \ / _` | '_ \| | __| | | |
* | _ | |_| | | | | | | (_| | | | | | |_| |_| |
* |_| |_|\__,_|_| |_| |_|\__,_|_| |_|_|\__|\__, |
* https://humanity.org |___/
* @dev Returns true if `account` is a contract.
*
* This test is non-exhaustive, and there may be false-negatives: during the
* execution of a contract's constructor, its address will be reported as
* not containing a contract.
*
* > It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*/
function isContract(address account) internal view returns (bool) {
// This method relies in extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly {
size := extcodesize(account)
}
return size > 0;
}
}
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a `Transfer` event.
*/
function transfer(
address recipient,
uint256 amount
) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through `transferFrom`. This is
* zero by default.
*
* This value changes when `approve` or `transferFrom` are called.
*/
function allowance(
address owner,
address spender
) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* > Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an `Approval` event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a `Transfer` event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to `approve`. `value` is the new allowance.
*/
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
}
library Math {
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow, so we distribute
return (a / 2) + (b / 2) + (((a % 2) + (b % 2)) / 2);
}
}
contract Owned {
address public owner;
address public nominatedOwner;
constructor(address _owner) public {
require(_owner != address(0), "Owner address cannot be 0");
owner = _owner;
emit OwnerChanged(address(0), _owner);
}
function nominateNewOwner(address _owner) external onlyOwner {
nominatedOwner = _owner;
emit OwnerNominated(_owner);
}
function acceptOwnership() external {
require(
msg.sender == nominatedOwner,
"You must be nominated before you can accept ownership"
);
emit OwnerChanged(owner, nominatedOwner);
owner = nominatedOwner;
nominatedOwner = address(0);
}
modifier onlyOwner() {
_onlyOwner();
_;
}
function _onlyOwner() private view {
require(
msg.sender == owner,
"Only the contract owner may perform this action"
);
}
event OwnerNominated(address newOwner);
event OwnerChanged(address oldOwner, address newOwner);
}
contract Pausable is Owned {
uint public lastPauseTime;
bool public paused;
constructor() internal {
// This contract is abstract, and thus cannot be instantiated directly
require(owner != address(0), "Owner must be set");
// Paused will be false, and lastPauseTime will be 0 upon initialisation
}
/**
* @notice Change the paused state of the contract
* @dev Only the contract owner may call this.
*/
function setPaused(bool _paused) external onlyOwner {
// Ensure we're actually changing the state before we do anything
if (_paused == paused) {
return;
}
// Set our paused state.
paused = _paused;
// If applicable, set the last pause time.
if (paused) {
lastPauseTime = now;
}
// Let everyone know that our pause state has changed.
emit PauseChanged(paused);
}
event PauseChanged(bool isPaused);
modifier notPaused() {
require(
!paused,
"This action cannot be performed while the contract is paused"
);
_;
}
}
contract ReentrancyGuard {
/// @dev counter to allow mutex lock with only one SSTORE operation
uint256 private _guardCounter;
constructor() internal {
// The counter starts at one to prevent changing it from zero to a non-zero
// value, which is a more expensive operation.
_guardCounter = 1;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_guardCounter += 1;
uint256 localCounter = _guardCounter;
_;
require(
localCounter == _guardCounter,
"ReentrancyGuard: reentrant call"
);
}
}
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
callOptionalReturn(
token,
abi.encodeWithSelector(token.transfer.selector, to, value)
);
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
callOptionalReturn(
token,
abi.encodeWithSelector(token.transferFrom.selector, from, to, value)
);
}
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
// solhint-disable-next-line max-line-length
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
callOptionalReturn(
token,
abi.encodeWithSelector(token.approve.selector, spender, value)
);
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(
value
);
callOptionalReturn(
token,
abi.encodeWithSelector(
token.approve.selector,
spender,
newAllowance
)
);
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(
value
);
callOptionalReturn(
token,
abi.encodeWithSelector(
token.approve.selector,
spender,
newAllowance
)
);
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves.
// A Solidity high level call has three parts:
// 1. The target address is checked to verify it contains contract code
// 2. The call itself is made, and success asserted
// 3. The return value is decoded, which in turn checks the size of the returned data.
// solhint-disable-next-line max-line-length
require(address(token).isContract(), "SafeERC20: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = address(token).call(data);
require(success, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
// solhint-disable-next-line max-line-length
require(
abi.decode(returndata, (bool)),
"SafeERC20: ERC20 operation did not succeed"
);
}
}
}
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, "SafeMath: division by zero");
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}
/**
* _ _ _ _
* | | | |_ _ _ __ ___ __ _ _ __ (_) |_ _ _
* | |_| | | | | '_ ` _ \ / _` | '_ \| | __| | | |
* | _ | |_| | | | | | | (_| | | | | | |_| |_| |
* |_| |_|\__,_|_| |_| |_|\__,_|_| |_|_|\__|\__, |
* https://humanity.org |___/
* @title MultiRewards
* @notice Multi-reward staking contract
* @dev EXACT implementation of audited MultiRewards contract - DO NOT MODIFY
*/
contract MultiRewards is ReentrancyGuard, Pausable {
using SafeMath for uint256;
using SafeERC20 for IERC20;
/* ========== STATE VARIABLES ========== */
struct Reward {
address rewardsDistributor;
uint256 rewardsDuration;
uint256 periodFinish;
uint256 rewardRate;
uint256 lastUpdateTime;
uint256 rewardPerTokenStored;
}
IERC20 public stakingToken;
mapping(address => Reward) public rewardData;
address[] public rewardTokens;
// user -> reward token -> amount
mapping(address => mapping(address => uint256))
public userRewardPerTokenPaid;
mapping(address => mapping(address => uint256)) public rewards;
uint256 private _totalSupply;
mapping(address => uint256) private _balances;
// handling lockup for individual stake
uint256 public lockDuration;
struct Stake {
uint256 stakingMaturity;
uint256 remainingBalance;
}
// user -> stakes [stake index]
mapping(address => Stake[]) public userStakes;
// bookkeeping the reward generated by each stake
// user -> reward token -> amount [stake index]
mapping(address => mapping(address => uint256[])) public rewardsForStake;
// user -> reward token -> amount
mapping(address => mapping(address => uint256)) public claimedRewards;
/* ========== CONSTRUCTOR ========== */
constructor(
address _owner,
address _stakingToken,
uint256 _lockDuration
) public Owned(_owner) {
stakingToken = IERC20(_stakingToken);
lockDuration = _lockDuration;
}
function addReward(
address _rewardsToken,
address _rewardsDistributor,
uint256 _rewardsDuration
) public onlyOwner {
require(
rewardData[_rewardsToken].rewardsDuration == 0,
"reward data of token has been added"
);
require(
_rewardsToken != address(0) && _rewardsDistributor != address(0),
"Zero address not allowed"
);
require(_rewardsDuration > 0, "Reward duration must be non-zero");
rewardTokens.push(_rewardsToken);
rewardData[_rewardsToken].rewardsDistributor = _rewardsDistributor;
rewardData[_rewardsToken].rewardsDuration = _rewardsDuration;
emit RewardsDistributorUpdated(_rewardsToken, _rewardsDistributor);
emit RewardsDurationUpdated(_rewardsToken, _rewardsDuration);
}
/* ========== VIEWS ========== */
function totalSupply() external view returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) external view returns (uint256) {
return _balances[account];
}
function lastTimeRewardApplicable(
address _rewardsToken
) public view returns (uint256) {
return
Math.min(block.timestamp, rewardData[_rewardsToken].periodFinish);
}
function rewardPerToken(
address _rewardsToken
) public view returns (uint256) {
if (_totalSupply == 0) {
return rewardData[_rewardsToken].rewardPerTokenStored;
}
return
rewardData[_rewardsToken].rewardPerTokenStored.add(
lastTimeRewardApplicable(_rewardsToken)
.sub(rewardData[_rewardsToken].lastUpdateTime)
.mul(rewardData[_rewardsToken].rewardRate)
.mul(1e18)
.div(_totalSupply)
);
}
function earned(
address account,
address _rewardsToken
) public view returns (uint256) {
return
_balances[account]
.mul(
rewardPerToken(_rewardsToken).sub(
userRewardPerTokenPaid[account][_rewardsToken]
)
)
.div(1e18)
.add(rewards[account][_rewardsToken]);
}
function earnedLifetime(
address account,
address _rewardsToken
) public view returns (uint256) {
uint256 notClaimed = earned(account, _rewardsToken);
return notClaimed.add(claimedRewards[account][_rewardsToken]);
}
function earned_for_stake(
address account,
address _rewardsToken,
uint256 stakeI
) public view returns (uint256) {
return
userStakes[account][stakeI]
.remainingBalance
.mul(
rewardPerToken(_rewardsToken).sub(
userRewardPerTokenPaid[account][_rewardsToken]
)
)
.div(1e18)
.add(rewardsForStake[account][_rewardsToken][stakeI]);
}
function getRewardForDuration(
address _rewardsToken
) external view returns (uint256) {
return
rewardData[_rewardsToken].rewardRate.mul(
rewardData[_rewardsToken].rewardsDuration
);
}
function unlockedStakeAtTime(
address account,
uint256 thisTime,
uint256 stakeI
) public view returns (uint256) {
if (
userStakes[account][stakeI].remainingBalance > 0 &&
(block.timestamp >= rewardData[rewardTokens[0]].periodFinish ||
userStakes[account][stakeI].stakingMaturity <= thisTime)
) return userStakes[account][stakeI].remainingBalance;
else return 0;
}
function unlockedStake(address account) public view returns (uint256) {
uint256 actualAmount = 0;
uint256 thisTest = lastTimeRewardApplicable(rewardTokens[0]);
for (uint i; i < userStakes[account].length; i++) {
actualAmount = actualAmount.add(
unlockedStakeAtTime(account, thisTest, i)
);
}
require(
actualAmount <= _balances[account],
"internal consistency on staked amount"
);
return actualAmount;
}
function unlockedRewardAtTime(
address account,
address _rewardsToken,
uint256 thisTime,
uint256 stakeI
) public view returns (uint256) {
if (
block.timestamp >= rewardData[rewardTokens[0]].periodFinish ||
userStakes[account][stakeI].stakingMaturity <= thisTime
) return earned_for_stake(account, _rewardsToken, stakeI);
else return 0;
}
function unlockedReward(
address account,
address _rewardsToken
) public view returns (uint256) {
uint256 reward = earned(account, _rewardsToken);
uint256 askedAmount = reward;
uint256 actualAmount = 0;
uint256 thisTest = lastTimeRewardApplicable(rewardTokens[0]);
for (uint j; j < userStakes[account].length; j++) {
uint256 outAmount = unlockedRewardAtTime(
account,
_rewardsToken,
thisTest,
j
);
if (outAmount > 0) {
outAmount = Math.min(outAmount, askedAmount);
askedAmount = askedAmount.sub(outAmount);
actualAmount = actualAmount.add(outAmount);
}
if (askedAmount == 0) break;
}
require(actualAmount <= reward, "Internal error on reward calculation");
return actualAmount;
}
function distributorRemainingReward(
address _rewardsToken
) public view returns (uint256) {
return
rewards[rewardData[_rewardsToken].rewardsDistributor][
_rewardsToken
];
}
function userInfoByIndexRange(
address account,
address _rewardsToken,
uint256 _start,
uint256 _stop
) external view returns (uint256[3][] memory) {
uint256 _allStakeLength = userStakes[account].length;
if (_stop > _allStakeLength) {
_stop = _allStakeLength;
}
require(_stop >= _start, "start cannot be higher than stop");
uint256 _qty = _stop - _start;
uint256[3][] memory result = new uint256[3][](_qty);
for (uint i = 0; i < _qty; i++) {
result[i][0] = userStakes[account][_start + i].stakingMaturity;
result[i][1] = userStakes[account][_start + i].remainingBalance;
result[i][2] = earned_for_stake(account, _rewardsToken, _start + i);
}
return result;
}
/* ========== MUTATIVE FUNCTIONS ========== */
function setRewardsDistributor(
address _rewardsToken,
address _rewardsDistributor
) external onlyOwner {
require(
_rewardsToken != address(0) && _rewardsDistributor != address(0),
"Zero address not allowed"
);
rewardData[_rewardsToken].rewardsDistributor = _rewardsDistributor;
emit RewardsDistributorUpdated(_rewardsToken, _rewardsDistributor);
}
function stake(
uint256 amount
) external nonReentrant notPaused updateReward(msg.sender) {
require(amount > 0, "Cannot stake 0");
uint256 previousBalance = IERC20(stakingToken).balanceOf(address(this));
stakingToken.safeTransferFrom(msg.sender, address(this), amount);
uint256 actualAmount = IERC20(stakingToken)
.balanceOf(address(this))
.sub(previousBalance);
actualAmount = Math.min(actualAmount, amount);
_totalSupply = _totalSupply.add(actualAmount);
_balances[msg.sender] = _balances[msg.sender].add(actualAmount);
userStakes[msg.sender].push(
Stake(block.timestamp.add(lockDuration), actualAmount)
);
for (uint i; i < rewardTokens.length; i++)
rewardsForStake[msg.sender][rewardTokens[i]].push(0);
emit Staked(msg.sender, actualAmount);
}
function withdraw(
uint256 amount
) public nonReentrant updateReward(msg.sender) {
require(amount > 0, "Cannot withdraw 0");
uint256 askedAmount = Math.min(amount, _balances[msg.sender]);
uint256 actualAmount = 0;
uint256 thisTest = lastTimeRewardApplicable(rewardTokens[0]);
for (uint i; i < userStakes[msg.sender].length; i++) {
uint256 outAmount = unlockedStakeAtTime(msg.sender, thisTest, i);
if (outAmount > 0) {
outAmount = Math.min(outAmount, askedAmount);
userStakes[msg.sender][i].remainingBalance = userStakes[
msg.sender
][i].remainingBalance.sub(outAmount);
askedAmount = askedAmount.sub(outAmount);
actualAmount = actualAmount.add(outAmount);
}
if (askedAmount == 0) break;
}
require(
actualAmount > 0 &&
actualAmount <= amount &&
actualAmount <= _balances[msg.sender],
"No unlocked stake available or internal error"
);
_totalSupply = _totalSupply.sub(actualAmount);
_balances[msg.sender] = _balances[msg.sender].sub(actualAmount);
stakingToken.safeTransfer(msg.sender, actualAmount);
emit Withdrawn(msg.sender, actualAmount);
}
function getReward() public nonReentrant updateReward(msg.sender) {
for (uint i; i < rewardTokens.length; i++) {
address _rewardsToken = rewardTokens[i];
uint256 reward = rewards[msg.sender][_rewardsToken];
uint256 askedAmount = reward;
uint256 actualAmount = 0;
uint256 thisTest = lastTimeRewardApplicable(rewardTokens[0]);
for (uint j; j < userStakes[msg.sender].length; j++) {
uint256 outAmount = unlockedRewardAtTime(
msg.sender,
_rewardsToken,
thisTest,
j
);
if (outAmount > 0) {
outAmount = Math.min(outAmount, askedAmount);
rewardsForStake[msg.sender][_rewardsToken][
j
] = rewardsForStake[msg.sender][_rewardsToken][j].sub(
outAmount
);
askedAmount = askedAmount.sub(outAmount);
actualAmount = actualAmount.add(outAmount);
}
if (askedAmount == 0) break;
}
require(
actualAmount <= reward,
"Internal error on reward calculation"
);
if (actualAmount > 0) {
// let 0 case pass so that exit will work
rewards[msg.sender][_rewardsToken] = rewards[msg.sender][
_rewardsToken
].sub(actualAmount);
claimedRewards[msg.sender][_rewardsToken] = actualAmount.add(
claimedRewards[msg.sender][_rewardsToken]
);
IERC20(_rewardsToken).safeTransfer(msg.sender, actualAmount);
emit RewardPaid(msg.sender, _rewardsToken, actualAmount);
}
}
}
function exit() external {
withdraw(_balances[msg.sender]);
getReward();
}
/* ========== RESTRICTED FUNCTIONS ========== */
function notifyRewardAmount(
address _rewardsToken,
uint256 reward
) external updateReward(address(0)) {
require(rewardData[_rewardsToken].rewardsDistributor == msg.sender);
// handle the transfer of reward tokens via `transferFrom` to reduce the number
// of transactions required and ensure correctness of the reward amount
IERC20(_rewardsToken).safeTransferFrom(
msg.sender,
address(this),
reward
);
if (block.timestamp >= rewardData[_rewardsToken].periodFinish) {
rewardData[_rewardsToken].rewardRate = reward.div(
rewardData[_rewardsToken].rewardsDuration
);
} else {
uint256 remaining = rewardData[_rewardsToken].periodFinish.sub(
block.timestamp
);
uint256 leftover = remaining.mul(
rewardData[_rewardsToken].rewardRate
);
rewardData[_rewardsToken].rewardRate = reward.add(leftover).div(
rewardData[_rewardsToken].rewardsDuration
);
}
rewardData[_rewardsToken].lastUpdateTime = block.timestamp;
rewardData[_rewardsToken].periodFinish = block.timestamp.add(
rewardData[_rewardsToken].rewardsDuration
);
emit RewardAdded(reward);
}
function collectRemainingReward(
address _rewardsToken
) external nonReentrant updateReward(address(0)) {
require(rewardData[_rewardsToken].rewardsDistributor == msg.sender);
require(block.timestamp >= rewardData[_rewardsToken].periodFinish);
uint256 amount = rewards[msg.sender][_rewardsToken];
if (amount > 0) {
rewards[msg.sender][_rewardsToken] = 0;
IERC20(_rewardsToken).safeTransfer(msg.sender, amount);
}
}
// Added to support recovering LP Rewards from other systems such as BAL to be distributed to holders
function recoverERC20(
address tokenAddress,
uint256 tokenAmount
) external onlyOwner {
require(
tokenAddress != address(stakingToken),
"Cannot withdraw staking token"
);
require(
rewardData[tokenAddress].lastUpdateTime == 0,
"Cannot withdraw reward token"
);
IERC20(tokenAddress).safeTransfer(owner, tokenAmount);
emit Recovered(tokenAddress, tokenAmount);
}
function setRewardsDuration(
address _rewardsToken,
uint256 _rewardsDuration
) external {
require(_rewardsToken != address(0), "Zero address not allowed");
require(
block.timestamp > rewardData[_rewardsToken].periodFinish,
"Reward period still active"
);
require(rewardData[_rewardsToken].rewardsDistributor == msg.sender);
require(_rewardsDuration > 0, "Reward duration must be non-zero");
rewardData[_rewardsToken].rewardsDuration = _rewardsDuration;
emit RewardsDurationUpdated(
_rewardsToken,
rewardData[_rewardsToken].rewardsDuration
);
}
/* ========== MODIFIERS ========== */
modifier updateReward(address account) {
for (uint i; i < rewardTokens.length; i++) {
address token = rewardTokens[i];
if (_totalSupply == 0)
rewards[rewardData[token].rewardsDistributor][
token
] = lastTimeRewardApplicable(token)
.sub(rewardData[token].lastUpdateTime)
.mul(rewardData[token].rewardRate)
.add(rewards[rewardData[token].rewardsDistributor][token]);
rewardData[token].rewardPerTokenStored = rewardPerToken(token);
rewardData[token].lastUpdateTime = lastTimeRewardApplicable(token);
if (account != address(0)) {
rewards[account][token] = earned(account, token);
for (uint j; j < userStakes[account].length; j++)
rewardsForStake[account][token][j] = earned_for_stake(
account,
token,
j
);
userRewardPerTokenPaid[account][token] = rewardData[token]
.rewardPerTokenStored;
}
}
_;
}
/* ========== EVENTS ========== */
event RewardAdded(uint256 reward);
event Staked(address indexed user, uint256 amount);
event Withdrawn(address indexed user, uint256 amount);
event RewardPaid(
address indexed user,
address indexed rewardsToken,
uint256 reward
);
event RewardsDistributorUpdated(
address indexed token,
address indexed newDistributor
);
event RewardsDurationUpdated(address indexed token, uint256 newDuration);
event Recovered(address token, uint256 amount);
}
Read Contract
balanceOf 0x70a08231 → uint256
claimedRewards 0xd1ab58f4 → uint256
distributorRemainingReward 0x18864a36 → uint256
earned 0x211dc32d → uint256
earnedLifetime 0x84f963f4 → uint256
earned_for_stake 0x6920b9d6 → uint256
getRewardForDuration 0xbcd11014 → uint256
lastPauseTime 0x91b4ded9 → uint256
lastTimeRewardApplicable 0x638634ee → uint256
lockDuration 0x04554443 → uint256
nominatedOwner 0x53a47bb7 → address
owner 0x8da5cb5b → address
paused 0x5c975abb → bool
rewardData 0x48e5d9f8 → address, uint256, uint256, uint256, uint256, uint256
rewardPerToken 0xf1229777 → uint256
rewardTokens 0x7bb7bed1 → address
rewards 0xe70b9e27 → uint256
rewardsForStake 0xac468881 → uint256
stakingToken 0x72f702f3 → address
totalSupply 0x18160ddd → uint256
unlockedReward 0x88450f90 → uint256
unlockedRewardAtTime 0xee6c8f53 → uint256
unlockedStake 0x3d9c055b → uint256
unlockedStakeAtTime 0x454f06ec → uint256
userInfoByIndexRange 0xf08c3e90 → uint256[3][]
userRewardPerTokenPaid 0x7035ab98 → uint256
userStakes 0xb5d5b5fa → uint256, uint256
Write Contract 13 functions
These functions modify contract state and require a wallet transaction to execute.
acceptOwnership 0x79ba5097
No parameters
addReward 0xd0ed26ae
address _rewardsToken
address _rewardsDistributor
uint256 _rewardsDuration
collectRemainingReward 0xf28e5a71
address _rewardsToken
exit 0xe9fad8ee
No parameters
getReward 0x3d18b912
No parameters
nominateNewOwner 0x1627540c
address _owner
notifyRewardAmount 0xb66503cf
address _rewardsToken
uint256 reward
recoverERC20 0x8980f11f
address tokenAddress
uint256 tokenAmount
setPaused 0x16c38b3c
bool _paused
setRewardsDistributor 0x3f695b45
address _rewardsToken
address _rewardsDistributor
setRewardsDuration 0x2378bea6
address _rewardsToken
uint256 _rewardsDuration
stake 0xa694fc3a
uint256 amount
withdraw 0x2e1a7d4d
uint256 amount
Recent Transactions
No transactions found for this address