Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x7d470C1a647719eE3e1fD275bBc62ad7b98293fe
Balance 0 ETH
Nonce 1
Code Size 13981 bytes
Indexed Transactions 0
External Etherscan · Sourcify

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