Address Contract Verified
Address
0xC1397AAe5Fdcaf254F19289387B5d8eE78a7bCb7
Balance
0 ETH
Nonce
1
Code Size
11874 bytes
Creator
0x788CE923...A6a4 at tx 0x6e97170b...5a6e00
Indexed Transactions
0
Contract Bytecode
11874 bytes
0x608080604052600436101561001357600080fd5b600090813560e01c90816314bb01d114611b265750806314ce783d14611a33578063198e223e14611976578063251b6cf714611912578063277fc17d146118f357806327ed7188146118d05780632925c7dc146118b05780632d18c1c9146117bd5780632f380b351461153e5780634a7371af146110a6578063564b791814610fe357806361a52a3614610fc557806361fb220c14610f125780636e7fea7b14610ee85780637b0472f014610c865780637d1034ad14610c4e578063841d9ac2146109e557806385dc0aaf146109135780638b4db06b146108ea5780638bea2827146107d657806390d297e6146106fa5780639d6ffa56146106c85780639e3079cb146106aa578063b357ffd01461065e578063bd393aa414610550578063c1d5ec6714610519578063c706b31614610457578063ca7bc84014610384578063d2aeb1f81461027a578063fc8a3682146102505763fd16e1b81461017657600080fd5b3461024d5761018436611ee4565b600154604051630935e01b60e21b815233600482015290602090829060249082906001600160a01b03165afa908115610242578491610214575b50156102025760207f181a28ff5d3c03f2a0647b31da36579f4eda89fdbfb1c46fbda6a4be056d109f9183855260038252806006604087200155604051908152a280f35b60405163036c8cf960e11b8152600490fd5b610235915060203d811161023b575b61022d8183611ea8565b810190611efa565b386101be565b503d610223565b6040513d86823e3d90fd5b80fd5b503461024d57602036600319011261024d5760406020916004358152600783522054604051908152f35b503461024d576020806003193601126103805790600435815260048252604081208054916102a78361269b565b926102b56040519485611ea8565b8084528484018093835285832083915b83831061032a5750505050604051928484019085855251809152604084019460408260051b8601019392955b8287106102fe5785850386f35b90919293828061031a600193603f198a82030186528851611dec565b96019201960195929190926102f1565b60048860019260409a99979a5161034081611e8d565b855481528486015483820152610358600287016126b2565b6040820152610369600387016126b2565b6060820152815201920192019190969395966102c5565b5080fd5b503461024d57604036600319011261024d576004356103a1611ca1565b600154604051630935e01b60e21b815233600482015290602090829060249082906001600160a01b03165afa908115610242578491610439575b5015610202578183526003602090815260408420600701805460ff60a81b191683151560a81b60ff60a81b161790557fe2e05c80c9983e4d6cef73e7297d849909c22cfaddec3d69dd53c0fd00257ded915b6040519015158152a280f35b610451915060203d811161023b5761022d8183611ea8565b386103db565b503461024d57604036600319011261024d57600435610474611ca1565b600154604051630935e01b60e21b815233600482015290602090829060249082906001600160a01b03165afa9081156102425784916104fb575b50156102025760207fdce31f9aa052b12055c018b60847649ac80692ec49c211d2a5377d947f0615c3918385526003825261042d81600560408820019060ff801983541691151516179055565b610513915060203d811161023b5761022d8183611ea8565b386104ae565b503461024d57608036600319011261024d576020610548610538611cb0565b6064359060443590600435612715565b604051908152f35b503461024d5761055f36611d06565b600154604051630935e01b60e21b8152336004820152919492939190602090829060249082906001600160a01b03165afa908115610653578791610635575b50156102025760025485101561061c5783810361060a577fa050bf61fbe14879ef5c1589dded0de06f91a19bffa5d0a0c2cc4b31c31d5778936020936105ff9287895260038652600460408a2001946105f78654611f12565b8655886128af565b54604051908152a280f35b60405163512509d360e11b8152600490fd5b604051631e657b1d60e01b815260048101869052602490fd5b61064d915060203d811161023b5761022d8183611ea8565b3861059e565b6040513d89823e3d90fd5b503461024d57604036600319011261024d57604061067a611cb0565b9160043581526009602052209060018060a01b0316600052602052602060ff604060002054166040519015158152f35b503461024d578060031936011261024d576020600254604051908152f35b503461024d57606036600319011261024d576106e2612052565b6106f360443560243560043561235a565b6001815580f35b503461024d57602036600319011261024d5760043590610718612052565b81815260056020526040812033600052602052604060002091819082915b84548310156107875761076a610781916107646107538689611fe6565b5061075e42916122b9565b86612b2b565b90612045565b926107758187611fe6565b50600242910155611f12565b91610736565b90915080610798575b826001815580f35b6107a38133846125bc565b6040519081527fdacbdde355ba930696a362ea6738feb9f8bd52dfb3d81947558fd3217e23e32560203392a38180610790565b503461024d57606036600319011261024d5760043560243560028110156108e6576107ff611cc6565b600154604051630935e01b60e21b81523360048201526001600160a01b0393916020908290602490829088165afa9081156108db5786916108bd575b5015610202577f34d9f41816f2b9995d9432df78d36a250ead035e6739b34f0243bd403ea9481a9260409285875260036020526108a7816007868a20016108828682611f94565b8054610100600160a81b03191660089290921b610100600160a81b0316919091179055565b6108b384518094611d95565b166020820152a280f35b6108d5915060203d811161023b5761022d8183611ea8565b3861083b565b6040513d88823e3d90fd5b8280fd5b503461024d578060031936011261024d576001546040516001600160a01b039091168152602090f35b503461024d57604036600319011261024d57600435610930611cb0565b91809180825260209360058552604083209060018060a01b0316600052845260406000209182546109608161269b565b9361096e6040519586611ea8565b81855282528582208290878087015b8484106109c6575050505050925b82518410156109bc576109b06109b691610764426109a98888612701565b5186612b2b565b93611f12565b9261098b565b8490604051908152f35b6001916003916109d5856122b9565b815201920192019190889061097d565b503461024d576020806003193601126103805760043591610a04612052565b828152600682526040812033825282526040812092818283925b86549283851015610af6576002610a35868a611fe6565b5001544210610ae957610a57610a6d91610a4f878b611fe6565b505490612045565b926001610a64878b611fe6565b50015490612045565b9260001990818101908111610ad557610a89610a9a918a611fe6565b50610a94878b611fe6565b9061230c565b87548015610ac15701610ab6610ab0828a611fe6565b90612336565b87555b919091610a1e565b634e487b7160e01b87526031600452602487fd5b634e487b7160e01b87526011600452602487fd5b9192509260010192610ab9565b8592878215610c3c5783855260038152604085206007810160ff8154166002811015610c2857879190600103610be3576001808060a01b0380925460081c1693015416823b1561038057604051630c1e8bf760e01b81526001600160a01b0391909116600482015233602482015260448101869052918290606490829084905af180156108db57610bd0575b505b81610bc0575b6040519283528201527f75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a2160403392a36001815580f35b610bcb8233866125bc565b610b8a565b610bdc90959195611e7a565b9385610b82565b50506001015460405163a9059cbb60e01b8382015233602482015260448082018690528152610c23916001600160a01b0316610c1e82611e8d565b612102565b610b84565b634e487b7160e01b88526021600452602488fd5b604051631aae9b2b60e11b8152600490fd5b503461024d57602036600319011261024d57610c82610c6e60043561278f565b604051918291602083526020830190611dec565b0390f35b503461024d57610c9536611ee4565b90610c9e612052565b808352602090600382526040842060ff600582015416610ecf576006810154808510610eb157506007810160ff815460a81c1680610e92575b610e735782865260058452604086203387528452604086205415610e57575b60ff8154166002811015610e4357859190600103610e20576001610d2e93818060a01b03918291015416915460081c169033906120a8565b808452600582526040842033855282526040842060405190610d4f82611e5f565b8482524284830152426040830152805490600160401b821015610e0c5781610d80916001610d869594018155611fe6565b90612002565b80845260058252604080852033865283528420546000198101908111610df85790806007939260405190815285848201527fb4caaf29adda3eefee3ad552a8e85058589bf834c7466cae4ee58787f70589ed60403392a3845252610def60408320918254612045565b90556001815580f35b634e487b7160e01b85526011600452602485fd5b634e487b7160e01b87526041600452602487fd5b50600190910154610e3e9190309033906001600160a01b03166120a8565b610d2e565b634e487b7160e01b87526021600452602487fd5b8286526008845260408620610e6c8154611f12565b9055610cf6565b604051632746515360e01b815260048101849052336024820152604490fd5b508286526009845260408620338752845260ff60408720541615610cd7565b8460449160405191634df4ec0560e11b835260048301526024820152fd5b60405163f18e8c2160e01b815260048101839052602490fd5b503461024d57602036600319011261024d5760406020916004358152600883522054604051908152f35b503461024d57610f2136611ee4565b600154604051630935e01b60e21b815233600482015290602090829060249082906001600160a01b03165afa908115610242578491610fa7575b5015610202576020610f8d7f22e9eec82d3d5c1da9298c0a81b435237f7230731f5081157d03ab901aad9a80926122e1565b83855260038252806003604087200155604051908152a280f35b610fbf915060203d811161023b5761022d8183611ea8565b38610f5b565b503461024d578060031936011261024d576020604051620151808152f35b503461024d57610ff236611cdc565b9092918281815260209460058652604082209060018060a01b031682528552604081209081546110218161269b565b9261102f6040519485611ea8565b81845282528682208290888086015b848410611087575050505050935b815185101561107d57611071611077916107648661106a8987612701565b5187612b2b565b94611f12565b9361104c565b8590604051908152f35b600191600391611096856122b9565b815201920192019190899061103e565b503461024d5761014036600319011261024d576004356001600160401b038111610380573660238201121561038057818160040135916110e583611ec9565b926110f36040519485611ea8565b80845236602482840101116108e657806024602093018386013783010152611119611cb0565b90611122611cc6565b916084356001600160401b03811161153a57611142903690600401611c6c565b60a4929192356001600160401b03811161153657611164903690600401611c6c565b939092600260c43510156115325760e4356001600160a01b038116810361152e5761010435801515810361152a576101243591821515830361152657600154604051630935e01b60e21b815233600482015290602090829060249082906001600160a01b03165afa90811561151b578c916114fc575b50156102025787860361060a57600254996111f48b611f12565b6002556112026064356122e1565b906040519561121087611e2d565b8b87526001600160a01b03908116602088015216604086015260608501526001608085015260a084018b9052670de0b6b3a764000060c085015261125960c43560e08601611f37565b6001600160a01b03166101008401521515610120830152151561014082015286885260036020526040882081518051906001600160401b0382116114e8576112a18354611f43565b601f81116114ad575b50602090601f83116001146114435760079392918c9183611438575b50508160011b916000199060031b1c19161781555b6001810160018060a01b03602085015116906bffffffffffffffffffffffff60a01b9182825416179055600282019060018060a01b0360408601511690825416179055606083015160038201556080830151600482015561135160a08401511515600583019060ff801983541691151516179055565b60c08301516006820155019060e0810151600281101561142457928261014061140a96936113a661141e9a977ff01906dc0ac306fa816e50166a7ed46af1f05721cb90cea30beebdd8dede3a6d9c9a97611f94565b6101008101518354610100600160a81b03191660089190911b610100600160a81b0316178355610120810151835460ff60a81b191690151560a81b60ff60a81b161783550151815460ff60b01b191690151560b01b60ff60b01b16179055886128af565b604051918291602083526020830190611d55565b0390a280f35b634e487b7160e01b8a52602160045260248afd5b0151905038806112c6565b90838c5260208c20918c5b601f1985168110611495575091839160019360079695601f1981161061147c575b505050811b0181556112db565b015160001960f88460031b161c1916905538808061146f565b9192602060018192868501518155019401920161144e565b6114d890848d5260208d20601f850160051c810191602086106114de575b601f0160051c0190611f7d565b386112aa565b90915081906114cb565b634e487b7160e01b8b52604160045260248bfd5b611515915060203d60201161023b5761022d8183611ea8565b386111da565b6040513d8e823e3d90fd5b8a80fd5b8980fd5b8880fd5b8780fd5b8680fd5b8480fd5b503461024d57602036600319011261024d578061014060405161156081611e2d565b606081528260208201528260408201528260608201528260808201528260a08201528260c08201528260e0820152826101008201528261012082015201526115a6612769565b506004358152600360205260408120604051916115c283611e2d565b604051818354926115d284611f43565b908184526001948581169081600014611796575060011461175c575b8660ff6007888888611602818a0382611ea8565b85528101546001600160a01b0390811660208601526002820154166040850152600381015460608501526004810154608085015260058101548316151560a0850152600681015460c0850152015461165f81831660e08501611f37565b600881901c6001600160a01b031661010084015260a881901c8216151561012084015260b01c16151561014082015261169960043561278f565b610c82604051928392604084526101406116c1835161016060408801526101a0870190611d55565b9260018060a01b03602082015116606087015260018060a01b036040820151166080870152606081015160a0870152608081015160c087015260a0810151151560e087015260c081015161010087015261172460e0820151610120880190611d95565b6101008101516001600160a01b0316868301526101208101511515610160870152015115156101808501528382036020850152611dec565b9150848252602082205b81831061177d5750508101602001816116026115ee565b8481602092949394548385880101520191019190611766565b60ff191660208087019190915292151560051b8501909201925083915061160290506115ee565b503461024d57604036600319011261024d576117d7611cb0565b60043582526006602090815260408084206001600160a01b03909316845291815290822080546118068161269b565b906118146040519283611ea8565b8082528382018093865284862086915b83831061188957505050506040519280840191818552518092526040840192945b8281106118525784840385f35b9091928260608261187a6001948a516040809180518452602081015160208501520151910152565b01960191019492919094611845565b60038760019261189d859b9a98999b6122b9565b8152019201920191909694939596611824565b503461024d578060031936011261024d57602060405164496cebb8008152f35b503461024d578060031936011261024d576020604051670de0b6b3a76400008152f35b503461024d57602036600319011261024d576020610548600435612897565b503461024d5761192136611d06565b909161192e949394612052565b81810361060a57855b81811061194657866001815580f35b8061196c61195861197193858a611fac565b35611964838789611fac565b35908861235a565b611f12565b611937565b503461024d5761198536611cdc565b9282819392935260209260058452604082209060018060a01b031682528352604081208054906119b48261269b565b926119c26040519485611ea8565b828452908152848120858085015b848410611a1457505050505080518410156119fb5761054892936119f391612701565b514291612b2b565b60405163112517f360e21b815260048101859052602490fd5b600191600391611a23856122b9565b81520192019201919086906119d0565b503461024d57604036600319011261024d57611a4d611cb0565b60043582526005602090815260408084206001600160a01b0390931684529181529082208054611a7c8161269b565b90611a8a6040519283611ea8565b8082528382018093865284862086915b838310611aff57505050506040519280840191818552518092526040840192945b828110611ac85784840385f35b90919282606082611af06001948a516040809180518452602081015160208501520151910152565b01960191019492919094611abb565b600387600192611b13859b9a98999b6122b9565b8152019201920191909694939596611a9a565b905034610380576060366003190112610380576004356024356001600160401b038111611c6857611b5b903690600401611c6c565b919092604435938415159384860361153657600154630935e01b60e21b84523360048501526020936001600160a01b03939185908290602490829088165afa908115611c5d578991611c40575b501561020257875b828110611bbb578880f35b611c3b90868a526009865260408a2085611bde611bd9848888611fac565b611fd2565b168b528652611bfc8960408c209060ff801983541691151516179055565b84611c0b611bd9838787611fac565b16877f6b96ca3bea63999fca0466ba18af37111522a3a3f46d8933ae55e8da0b3339de886040518c8152a3611f12565b611bb0565b611c579150853d871161023b5761022d8183611ea8565b38611ba8565b6040513d8b823e3d90fd5b8380fd5b9181601f84011215611c9c578235916001600160401b038311611c9c576020808501948460051b010111611c9c57565b600080fd5b602435908115158203611c9c57565b602435906001600160a01b0382168203611c9c57565b604435906001600160a01b0382168203611c9c57565b6060906003190112611c9c57600435906024356001600160a01b0381168103611c9c579060443590565b906060600319830112611c9c57600435916001600160401b0391602435838111611c9c5782611d3791600401611c6c565b93909392604435918211611c9c57611d5191600401611c6c565b9091565b919082519283825260005b848110611d81575050826000602080949584010152601f8019910116010190565b602081830181015184830182015201611d60565b906002821015611da25752565b634e487b7160e01b600052602160045260246000fd5b90815180825260208080930193019160005b828110611dd8575050505090565b835185529381019392810192600101611dca565b611e2a9181518152602082015160208201526060611e196040840151608060408501526080840190611db8565b920151906060818403910152611db8565b90565b61016081019081106001600160401b03821117611e4957604052565b634e487b7160e01b600052604160045260246000fd5b606081019081106001600160401b03821117611e4957604052565b6001600160401b038111611e4957604052565b608081019081106001600160401b03821117611e4957604052565b90601f801991011681019081106001600160401b03821117611e4957604052565b6001600160401b038111611e4957601f01601f191660200190565b6040906003190112611c9c576004359060243590565b90816020910312611c9c57518015158103611c9c5790565b6000198114611f215760010190565b634e487b7160e01b600052601160045260246000fd5b6002821015611da25752565b90600182811c92168015611f73575b6020831014611f5d57565b634e487b7160e01b600052602260045260246000fd5b91607f1691611f52565b818110611f88575050565b60008155600101611f7d565b906002811015611da25760ff80198354169116179055565b9190811015611fbc5760051b0190565b634e487b7160e01b600052603260045260246000fd5b356001600160a01b0381168103611c9c5790565b8054821015611fbc576000526003602060002091020190600090565b919061202257604081600292518455602081015160018501550151910155565b634e487b7160e01b600052600060045260246000fd5b91908203918211611f2157565b91908201809211611f2157565b600260005414612063576002600055565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b6040516323b872dd60e01b60208201526001600160a01b03928316602482015292909116604483015260648083019390935291815260a08101918183106001600160401b03841117611e495761210092604052612102565b565b60408051908101916001600160a01b03166001600160401b03831182841017611e4957612190926040526000806020958685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656487860152868151910182855af13d15612218573d9161217483611ec9565b926121826040519485611ea8565b83523d60008785013e61221c565b805190828215928315612200575b505050156121a95750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b6122109350820181019101611efa565b38828161219e565b6060915b9192901561227e5750815115612230575090565b3b156122395790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b8251909150156122915750805190602001fd5b60405162461bcd60e51b8152602060048201529081906122b5906024830190611d55565b0390fd5b906040516122c681611e5f565b60406002829480548452600181015460208501520154910152565b906201518091828102928184041490151715611f2157565b81810292918115918404141715611f2157565b91906120225780820361231d575050565b6002818192548455600181015460018501550154910155565b906120225760028160008093558260018201550155565b8015611f21576000190190565b6000908082526020906003825260409384842060058452858520338652845285852090815490818410156125a4576123928484611fe6565b50928354936001810154948b81106125815760ff600785015460b01c161580612577575b612566576123d68c6123d1426123cb866122b9565b8c612b2b565b6122f9565b81156125525790808d9204958183106000146124c857506123f8929350612038565b81556002429101555b848752600686528787203388528652612421600389892092015442612045565b9088519161242e83611e5f565b8a8352838884015289830152805490600160401b8210156124b4579261246b6124b09a99989693610d80848a989597600160079c99018155611fe6565b88519283528a868401528883015260608201527f202eeecd97a5a8aaf6d06740fa058250cd2858b0527d2bc1cf5118709360ed5060803392a383525220918254612038565b9055565b634e487b7160e01b89526041600452602489fd5b600019935083810192508211905061253e576124e76124f29184611fe6565b50610a948885611fe6565b8154801561252a57018161250a610ab0838095611fe6565b556124015784875260088652878720612523815461234d565b9055612401565b634e487b7160e01b8a52603160045260248afd5b634e487b7160e01b8a52601160045260248afd5b634e487b7160e01b8b52601260045260248bfd5b8a516347e2ef1d60e01b8152600490fd5b50808c14156123b6565b866064918d8d5192632aa4603f60e11b8452600484015260248301526044820152fd5b875163112517f360e21b815260048101859052602490fd5b91906000928352600360205260408320926007840190815460ff811660028110156126875761260657505060026121009460018060a01b03918291015416915460081c16906120a8565b600290950154909460081c6001600160a01b0390811693925016823b1561153a57604051631226256760e11b81526001600160a01b03918216600482015291166024820152604481019290925290919081908390606490829084905af190811561267b57506126725750565b61210090611e7a565b604051903d90823e3d90fd5b634e487b7160e01b83526021600452602483fd5b6001600160401b038111611e495760051b60200190565b9060405191828154918282526020928383019160005283600020936000905b8282106126e75750505061210092500383611ea8565b8554845260019586019588955093810193909101906126d1565b8051821015611fbc5760209160051b010190565b92916127469061274c9285600052600560205260406000209060018060a01b03166000526020526040600020611fe6565b506122b9565b604081015182111561276157611e2a92612b2b565b505050600090565b6040519061277682611e8d565b6060808360008152600060208201528160408201520152565b90612798612769565b50600091825260049160208381526040908183209182546127b88161269b565b936127c583519586611ea8565b818552855282852085908486015b898685851061284e5750505050505082511561281d575050805160001981019290831161280a5750612806929350612701565b5190565b634e487b7160e01b815260118552602490fd5b5162461bcd60e51b815280860191909152600b60248201526a139bc81d1a595c881cd95d60aa1b6044820152606490fd5b600192875161285c81611e8d565b855481528486015483820152612874600287016126b2565b89820152612884600387016126b2565b60608201528152019201920191906127d3565b6201518090818102918183041490151715611f215790565b92939190916128bd8261269b565b936040936128cd85519687611ea8565b838652601f196128dc8561269b565b0193602094368689013760005b818110612afe575050506000526004808352836000209560038452818560002001549085519161291883611e8d565b82528482019342855286830197885261293c6129338261269b565b97519788611ea8565b8087528587019060051b820191368311611c9c578690915b838310612aee575050505060608101948552865492600160401b9384811015612ad957600198898201808255821015612ac457600052856000209060021b01915182555187820155600281019551958651906001600160401b0397888311612aaf57858311612aaf5786908254848455808510612a92575b500190600052856000208960005b848110612a805750505050506003019351918251958611612a6b578511612a56575081908354858555808610612a39575b500191600052806000209060005b848110612a2857505050505050565b835183820155928101928501612a19565b612a50908560005286846000209182019101611f7d565b38612a0b565b604190634e487b7160e01b6000525260246000fd5b604182634e487b7160e01b6000525260246000fd5b88845194019381840155018a906129da565b612aa9908460005285846000209182019101611f7d565b386129cc565b604185634e487b7160e01b6000525260246000fd5b603285634e487b7160e01b6000525260246000fd5b604184634e487b7160e01b6000525260246000fd5b8235815291810191879101612954565b80612b16612b10612b26938587611fac565b35612897565b612b20828b612701565b52611f12565b6128e9565b90929160009360009283526004602092818452604080862092835490612b508261269b565b94612b5d84519687611ea8565b82865288528688208890888088015b858410612c2c57505050505050830151945b8251811015612bfb578585612b938386612701565b5101511180612be5575b612bb0575b612bab90611f12565b612b7e565b9496612bdc612bab9161076487612bc78a88612701565b5101518b88612bd7839e8a612c79565b612cee565b97959050612ba2565b508185612bf28386612701565b51015110612b9d565b50939250838310612c0d575b50505050565b91612c2294959391612bd78261076495612c79565b9038808080612c07565b60019185918851612c3c81611e8d565b855481528486015483820152612c54600287016126b2565b8a820152612c64600387016126b2565b60608201528152019201920191908990612b6c565b90612c82612769565b508151805b612c9b575050805115611fbc576020015190565b6000198101818111612cd957826020612cb48387612701565b5101511115612ccd5750612cc79061234d565b80612c87565b91505061280691612701565b60246000634e487b7160e01b81526011600452fd5b602082018051600096958795929492612d1292612d0a91612038565b945190612038565b60408301805151946000198087018781119390895b898110612d3b575050505050505050505050565b80612df9578a865b612de55782821015612dde57612d5a828551612701565b51905b80871115612dd8575085905b80891015612dd35750875b818111612d8c575b5050612d8790611f12565b612d27565b9d64496cebb800839f8b6123d18e612dbd612d87986060612db3612dcb9a612dc498612038565b9551930151612701565b51906122f9565b0490612045565b9c9038612d7c565b612d74565b90612d69565b8490612d5d565b634e487b7160e01b8c52601160045260248cfd5b825184820190828211612e18578791612e1191612701565b5190612d43565b634e487b7160e01b8d52601160045260248dfdfea26469706673582212207abdbd9668042eef0c4c2fe3985e9067b8fcca78d14d11e0f8ca8019fc37b04564736f6c63430008130033
Verified Source Code Full Match
Compiler: v0.8.19+commit.7dd6d404
EVM: paris
Optimization: Yes (200 runs)
ReentrancyGuard.sol 77 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @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 making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}
IERC20Permit.sol 90 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
IERC20.sol 78 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @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);
/**
* @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 `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, 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.
*
* IMPORTANT: 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 `from` to `to` 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 from, address to, uint256 amount) external returns (bool);
}
SafeERC20.sol 143 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
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'
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));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @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. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @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).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}
Address.sol 244 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
PlatformAccessController.sol 41 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import "../admin_panel/PlatformAdminPanel.sol";
/**
* @title Abstract contract from which platform contracts with admin function are inherited
* @dev Contains the platform admin panel
* Contains modifier that checks whether sender is platform admin, use platform admin panel
*/
abstract contract PlatformAccessController {
address public _panel;
error CallerNotAdmin();
error AlreadyInitialized();
function _initiatePlatformAccessController(address adminPanel) internal {
if(address(_panel) != address(0))
revert AlreadyInitialized();
_panel = adminPanel;
}
/**
* @dev Modifier that makes function available for platform admins only
*/
modifier onlyPlatformAdmin() {
if(!PlatformAdminPanel(_panel).isAdmin(msgSender()))
revert CallerNotAdmin();
_;
}
function _isAdmin() internal view returns (bool) {
return PlatformAdminPanel(_panel).isAdmin(msgSender());
}
function msgSender() internal view virtual returns (address) {
return msg.sender;
}
}
IPlatformAdminPanel.sol 7 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
interface IPlatformAdminPanel {
function isAdmin(address wallet) external view returns (bool);
}
PlatformAdminPanel.sol 105 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import "./Iplatform_admin_panel/IPlatformAdminPanel.sol";
/**
* @title Platform admins holder contract
* @notice Used to check accessibility of senders to admin functions in platform contracts
*/
contract PlatformAdminPanel is IPlatformAdminPanel {
/**
* @notice Emit during root admin set and reset
*/
event SetRootAdmin(address indexed wallet);
event InsertAdminList(address[] adminList);
event RemoveAdminList(address[] adminList);
mapping(address => bool) private _adminMap;
address private _rootAdmin;
modifier onlyRootAdmin() {
require(_rootAdmin == msg.sender, "sender is not root admin");
_;
}
/**
* @notice Specify the root admin, only he has the rights to add and remove admins
*/
constructor(address rootAdminWallet) {
_setRootAdmin(rootAdminWallet);
}
/**
* @notice Needed to determine if the user has admin rights for platform contracts
*/
function isAdmin(address wallet)
external
view
virtual
override
returns (bool)
{
return wallet == _rootAdmin || _adminMap[wallet];
}
function rootAdmin() external view returns (address) {
return _rootAdmin;
}
/**
* @notice Only root admin can call
*/
function insertAdminList(address[] calldata adminList)
external
onlyRootAdmin
{
require(0 < adminList.length, "empty admin list");
require(20 >= adminList.length, "list too large");
uint256 index = adminList.length;
while (0 < index) {
--index;
_adminMap[adminList[index]] = true;
}
emit InsertAdminList(adminList);
}
/**
* @notice Only root admin can call
*/
function removeAdminList(address[] calldata adminList)
external
onlyRootAdmin
{
require(0 < adminList.length, "empty admin list");
require(20 >= adminList.length, "list too large");
uint256 index = adminList.length;
while (0 < index) {
--index;
_adminMap[adminList[index]] = false;
}
emit RemoveAdminList(adminList);
}
/**
* @notice Only root admin can call
*/
function setRootAdmin(address rootAdminWallet) external onlyRootAdmin {
_setRootAdmin(rootAdminWallet);
}
function _setRootAdmin(address wallet) private {
require(wallet != address(0), "wallet is zero address");
_rootAdmin = wallet;
emit SetRootAdmin(wallet);
}
}
WELFStaking.sol 714 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
/**
* @author
* Creator: Yash Gupta (GitHub: @theyashgupta)
* Contributor: Ajibade Sokunbi (GitHub: @AjibadeSokunbi)
*/
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@openzeppelin/contracts/security/ReentrancyGuard.sol';
import '../access_controller/PlatformAccessController.sol';
/// @notice Interface for treasury funding strategy.
interface ITreasury {
function transferReward(IERC20 token, address to, uint256 amount) external;
function withdrawStake(IERC20 token, address to, uint256 amount) external;
}
/**
* @title WelfStaking
* @dev A staking contract that allows users to stake tokens and earn rewards based on predefined tiers and APYs.
* The contract supports multiple staking pools, each with its own configuration and reward strategy.
* It includes functionalities for creating and managing pools, staking, initiating unstake, claiming rewards, and withdrawing tokens.
* The contract also supports restricted pools where only whitelisted addresses can stake.
*/
contract WelfStaking is ReentrancyGuard, PlatformAccessController {
using SafeERC20 for IERC20;
/// @dev Custom errors for gas-efficient reverts.
error PoolPaused(uint256 poolId);
error BelowMinStake(uint256 amount, uint256 minStake);
error ArrayLengthMismatch();
error InsufficientStakeAmount(uint256 index, uint256 requested, uint256 available);
error IndexOutOfBounds(uint256 index);
error NoCooldownReady();
error PartialUnstakeNotAllowed();
error NotAllowedToStake(uint256 poolId, address staker);
error InvalidAddress();
error PoolDoesNotExist(uint256 poolId);
/// @notice Reward funding strategy options.
enum RewardStrategy {
RewardWallet,
Treasury
}
// ========================================================
// Constants
// ========================================================
uint256 public constant MIN_STAKE_AMOUNT = 1 ether;
uint256 public constant SECONDS_IN_DAY = 1 days;
uint256 public constant YEAR_DIVISOR = 365 days * 10000;
// ========================================================
// Data Structures
// ========================================================
/// @notice Configuration for each staking pool.
struct PoolConfig {
string name;
IERC20 stakingToken;
IERC20 rewardToken;
uint256 cooldownPeriod; // in seconds
uint256 currentTierVersion;
bool isPaused;
uint256 minStake;
RewardStrategy rewardStrategy;
address rewardAddress; // For RewardWallet: wallet (with approved allowance); for Treasury: treasury contract.
bool restricted; // If true, only allowed addresses may stake.
bool allowPartialUnstake; // If false, only full unstaking is allowed.
}
/// @notice A TierSet defines milestone durations and corresponding APYs.
/// Each update records its effective time.
struct TierSet {
uint256 version;
uint256 effectiveTime;
uint256[] durations; // Cumulative tier boundaries (in seconds) relative to stake start.
uint256[] apys; // APYs in basis points (e.g. 500 = 5%).
}
/// @notice A Stake holds a user's staked amount and timing data.
struct Stake {
uint256 amount;
uint256 startTime;
uint256 lastClaimTime;
}
/// @notice A Cooldown entry represents unstaked tokens pending withdrawal.
struct Cooldown {
uint256 amount;
uint256 reward;
uint256 endTime;
}
// ========================================================
// Storage Variables
// ========================================================
uint256 public poolCounter;
mapping(uint256 => PoolConfig) private pools; // poolId => PoolConfig
mapping(uint256 => TierSet[]) private poolTiers; // poolId => array of TierSet (in order of effectiveTime)
mapping(uint256 => mapping(address => Stake[])) private userStakes; // poolId => user => Stakes
mapping(uint256 => mapping(address => Cooldown[])) private userCooldowns; // poolId => user => Cooldown entries
mapping(uint256 => uint256) private poolTotalStaked; // poolId => total staked amount
mapping(uint256 => uint256) private poolActiveUserCount; // poolId => active user count
// For restricted pools: mapping of allowed staker addresses.
mapping(uint256 => mapping(address => bool)) private poolAllowedStakers;
// ========================================================
// Events
// ========================================================
event PoolCreated(uint256 indexed poolId, string name);
event TiersUpdated(uint256 indexed poolId, uint256 newVersion);
event Staked(address indexed user, uint256 indexed poolId, uint256 index, uint256 amount);
event UnstakeInitiated(
address indexed user,
uint256 indexed poolId,
uint256 index,
uint256 amount,
uint256 pendingReward,
uint256 stakeTimestamp
);
event Withdrawn(address indexed user, uint256 indexed poolId, uint256 amount, uint256 reward);
event RewardsClaimed(address indexed user, uint256 indexed poolId, uint256 amount);
event PoolRestrictionSet(uint256 indexed poolId, bool restricted);
event PoolAllowedStakerSet(uint256 indexed poolId, address indexed staker, bool allowed);
event PoolPauseSet(uint256 indexed poolId, bool isPaused);
event PoolCooldownSet(uint256 indexed poolId, uint256 cooldownSeconds);
event PoolMinStakeSet(uint256 indexed poolId, uint256 newMinStake);
event PoolRewardConfigSet(uint256 indexed poolId, RewardStrategy newStrategy, address rewardAddress);
/**
* @param adminPanel platform admin panel address
*/
constructor(address adminPanel) {
if (adminPanel == address(0)) revert InvalidAddress();
_initiatePlatformAccessController(adminPanel);
}
// ========================================================
// Pool Management Functions
// ========================================================
/**
* @notice Creates a new pool.
* @param stakingToken Token to be staked.
* @param rewardToken Reward token.
* @param cooldownDays Cooldown period (in days) before withdrawal.
* @param initialDurationsDays Tier boundaries (in days).
* @param initialAPYs APYs for each tier (in basis points).
* @param rewardStrategy Funding strategy (RewardWallet or Treasury).
* @param rewardAddress For RewardWallet: wallet address; for Treasury: treasury contract.
* @param restricted If true, only whitelisted addresses may stake.
* @param allowPartialUnstake If false, only full unstaking is permitted.
*/
function createPool(
string memory name,
IERC20 stakingToken,
IERC20 rewardToken,
uint256 cooldownDays,
uint256[] calldata initialDurationsDays,
uint256[] calldata initialAPYs,
RewardStrategy rewardStrategy,
address rewardAddress,
bool restricted,
bool allowPartialUnstake
) external onlyPlatformAdmin {
if (initialDurationsDays.length != initialAPYs.length) revert ArrayLengthMismatch();
uint256 poolId = poolCounter++;
uint256 cooldownSeconds = daysToSeconds(cooldownDays);
pools[poolId] = PoolConfig({
name: name,
stakingToken: stakingToken,
rewardToken: rewardToken,
cooldownPeriod: cooldownSeconds,
currentTierVersion: 1,
isPaused: false,
minStake: MIN_STAKE_AMOUNT,
rewardStrategy: rewardStrategy,
rewardAddress: rewardAddress,
restricted: restricted,
allowPartialUnstake: allowPartialUnstake
});
_addTierSet(poolId, initialDurationsDays, initialAPYs);
emit PoolCreated(poolId, name);
}
/**
* @notice Updates the TierSet for a pool.
* @param poolId The pool identifier.
* @param newDurationsDays New tier boundaries (in days).
* @param newAPYs New APYs for each tier (in basis points).
*/
function updateTiers(
uint256 poolId,
uint256[] calldata newDurationsDays,
uint256[] calldata newAPYs
) external onlyPlatformAdmin {
if (poolId >= poolCounter) revert PoolDoesNotExist(poolId);
if (newDurationsDays.length != newAPYs.length) revert ArrayLengthMismatch();
PoolConfig storage pool = pools[poolId];
pool.currentTierVersion++;
_addTierSet(poolId, newDurationsDays, newAPYs);
emit TiersUpdated(poolId, pool.currentTierVersion);
}
/**
* @notice Sets the restriction flag for a pool.
* @param poolId The pool identifier.
* @param restricted True to restrict staking.
*/
function setPoolRestriction(uint256 poolId, bool restricted) external onlyPlatformAdmin {
pools[poolId].restricted = restricted;
emit PoolRestrictionSet(poolId, restricted);
}
/**
* @notice Batch update allowed stakers for a restricted pool.
* @param poolId The pool identifier.
* @param stakers Array of addresses.
* @param allowed True to allow staking, false to remove.
*/
function setPoolAllowedStakers(
uint256 poolId,
address[] calldata stakers,
bool allowed
) external onlyPlatformAdmin {
for (uint256 i = 0; i < stakers.length; i++) {
poolAllowedStakers[poolId][stakers[i]] = allowed;
emit PoolAllowedStakerSet(poolId, stakers[i], allowed);
}
}
/**
* @notice Pauses or unpauses a pool.
* @param poolId The pool identifier.
* @param pause True to pause; false to unpause.
*/
function setPoolPause(uint256 poolId, bool pause) external onlyPlatformAdmin {
pools[poolId].isPaused = pause;
emit PoolPauseSet(poolId, pause);
}
/**
* @notice Updates the cooldown period for a pool.
* @param poolId The pool identifier.
* @param cooldownDays New cooldown period (in days).
*/
function setPoolCooldown(uint256 poolId, uint256 cooldownDays) external onlyPlatformAdmin {
uint256 cooldownSeconds = daysToSeconds(cooldownDays);
pools[poolId].cooldownPeriod = cooldownSeconds;
emit PoolCooldownSet(poolId, cooldownSeconds);
}
/**
* @notice Updates the minimum stake for a pool.
* @param poolId The pool identifier.
* @param newMinStake New minimum stake amount.
*/
function setPoolMinStake(uint256 poolId, uint256 newMinStake) external onlyPlatformAdmin {
pools[poolId].minStake = newMinStake;
emit PoolMinStakeSet(poolId, newMinStake);
}
/**
* @notice Updates the reward configuration for a pool.
* This updates both the reward strategy and the reward address.
* @param poolId The pool identifier.
* @param newStrategy New reward strategy.
* @param newRewardAddress New reward address.
*/
function setPoolRewardConfig(
uint256 poolId,
RewardStrategy newStrategy,
address newRewardAddress
) external onlyPlatformAdmin {
PoolConfig storage pool = pools[poolId];
pool.rewardStrategy = newStrategy;
pool.rewardAddress = newRewardAddress;
emit PoolRewardConfigSet(poolId, newStrategy, newRewardAddress);
}
// ========================================================
// Staking Functions
// ========================================================
/**
* @notice Stakes a specified amount into a pool.
* @param poolId The pool identifier.
* @param amount Amount to stake.
*/
function stake(uint256 poolId, uint256 amount) external nonReentrant {
PoolConfig storage pool = pools[poolId];
if (pool.isPaused) revert PoolPaused(poolId);
if (amount < pool.minStake) revert BelowMinStake(amount, pool.minStake);
if (pool.restricted && !poolAllowedStakers[poolId][msg.sender]) revert NotAllowedToStake(poolId, msg.sender);
if (userStakes[poolId][msg.sender].length == 0) {
poolActiveUserCount[poolId]++;
}
if (pool.rewardStrategy == RewardStrategy.Treasury) {
pool.stakingToken.safeTransferFrom(msg.sender, pool.rewardAddress, amount);
} else {
pool.stakingToken.safeTransferFrom(msg.sender, address(this), amount);
}
userStakes[poolId][msg.sender].push(
Stake({ amount: amount, startTime: block.timestamp, lastClaimTime: block.timestamp })
);
emit Staked(msg.sender, poolId, userStakes[poolId][msg.sender].length - 1, amount);
poolTotalStaked[poolId] += amount;
}
/**
* @notice Internal function to initiate unstaking for a given stake index.
* If partial unstaking is disabled, the unstake amount must equal the full stake.
* @param poolId The pool identifier.
* @param index The stake index.
* @param amount The amount to unstake.
*/
function _initiateUnstake(uint256 poolId, uint256 index, uint256 amount) internal {
PoolConfig storage pool = pools[poolId];
Stake[] storage stakes = userStakes[poolId][msg.sender];
if (index >= stakes.length) revert IndexOutOfBounds(index);
Stake storage userStake = stakes[index];
uint256 currentStakeAmount = userStake.amount;
uint256 stakeStartTime = userStake.startTime;
if (currentStakeAmount < amount) revert InsufficientStakeAmount(index, amount, currentStakeAmount);
if (!pool.allowPartialUnstake && amount != currentStakeAmount) revert PartialUnstakeNotAllowed();
uint256 totalAccrued = _calculateRewards(poolId, userStake, block.timestamp);
uint256 rewardForUnstake = (totalAccrued * amount) / currentStakeAmount;
if (amount < currentStakeAmount) {
userStake.amount = currentStakeAmount - amount;
userStake.lastClaimTime = block.timestamp;
} else {
stakes[index] = stakes[stakes.length - 1];
stakes.pop();
if (stakes.length == 0) {
poolActiveUserCount[poolId]--;
}
}
userCooldowns[poolId][msg.sender].push(
Cooldown({ amount: amount, reward: rewardForUnstake, endTime: block.timestamp + pool.cooldownPeriod })
);
emit UnstakeInitiated(msg.sender, poolId, index, amount, rewardForUnstake, stakeStartTime);
poolTotalStaked[poolId] -= amount;
}
/**
* @notice Initiates unstaking for a given stake index.
* @param poolId The pool identifier.
* @param index The stake index.
* @param amount The amount to unstake.
*/
function initiateUnstake(uint256 poolId, uint256 index, uint256 amount) external nonReentrant {
_initiateUnstake(poolId, index, amount);
}
/**
* @notice Batch initiates unstake operations.
* @param poolId The pool identifier.
* @param indices Array of stake indices.
* @param amounts Array of unstake amounts.
*/
function batchInitiateUnstake(
uint256 poolId,
uint256[] calldata indices,
uint256[] calldata amounts
) external nonReentrant {
if (indices.length != amounts.length) revert ArrayLengthMismatch();
for (uint256 i = 0; i < indices.length; i++) {
_initiateUnstake(poolId, indices[i], amounts[i]);
}
}
/**
* @notice Claims rewards for all active stakes in a pool.
* @param poolId The pool identifier.
*/
function batchClaimRewards(uint256 poolId) external nonReentrant {
Stake[] storage stakes = userStakes[poolId][msg.sender];
uint256 totalRewards;
for (uint256 i = 0; i < stakes.length; i++) {
totalRewards += _calculateRewards(poolId, stakes[i], block.timestamp);
stakes[i].lastClaimTime = block.timestamp;
}
if (totalRewards > 0) {
_transferRewards(poolId, msg.sender, totalRewards);
emit RewardsClaimed(msg.sender, poolId, totalRewards);
}
}
/**
* @notice Withdraws tokens and rewards from expired cooldown entries.
* @param poolId The pool identifier.
*/
function withdrawCooldown(uint256 poolId) external nonReentrant {
Cooldown[] storage cooldowns = userCooldowns[poolId][msg.sender];
uint256 totalAmount;
uint256 totalReward;
uint256 i = 0;
while (i < cooldowns.length) {
if (block.timestamp >= cooldowns[i].endTime) {
totalAmount += cooldowns[i].amount;
totalReward += cooldowns[i].reward;
cooldowns[i] = cooldowns[cooldowns.length - 1];
cooldowns.pop();
} else {
unchecked {
i++;
}
}
}
if (totalAmount == 0) revert NoCooldownReady();
PoolConfig storage pool = pools[poolId];
if (pool.rewardStrategy == RewardStrategy.Treasury) {
ITreasury(pool.rewardAddress).withdrawStake(pool.stakingToken, msg.sender, totalAmount);
} else {
pool.stakingToken.safeTransfer(msg.sender, totalAmount);
}
if (totalReward > 0) {
_transferRewards(poolId, msg.sender, totalReward);
}
emit Withdrawn(msg.sender, poolId, totalAmount, totalReward);
}
// ========================================================
// Reward Funding Management
// ========================================================
/**
* @notice Internal function to transfer rewards based on pool configuration.
*/
function _transferRewards(uint256 poolId, address to, uint256 amount) internal {
PoolConfig storage pool = pools[poolId];
if (pool.rewardStrategy == RewardStrategy.RewardWallet) {
pool.rewardToken.safeTransferFrom(pool.rewardAddress, to, amount);
} else {
ITreasury(pool.rewardAddress).transferReward(pool.rewardToken, to, amount);
}
}
// ========================================================
// View Functions
// ========================================================
/**
* @notice Checks if a given address is allowed to stake in a restricted pool.
* @param poolId The pool identifier.
* @param staker The address to check.
* @return True if allowed; otherwise false.
*/
function isAddressAllowed(uint256 poolId, address staker) external view returns (bool) {
return poolAllowedStakers[poolId][staker];
}
/**
* @notice Returns the full tier history for a pool.
* @param poolId The pool identifier.
* @return Array of TierSet.
*/
function getPoolTierHistory(uint256 poolId) external view returns (TierSet[] memory) {
return poolTiers[poolId];
}
/**
* @notice Returns all stakes for a user in a pool.
* @param poolId The pool identifier.
* @param user The user's address.
* @return Array of Stake.
*/
function getUserStakes(uint256 poolId, address user) external view returns (Stake[] memory) {
return userStakes[poolId][user];
}
/**
* @notice Returns the total pending rewards for a user in a pool.
* @param poolId The pool identifier.
* @param user The user's address.
* @return totalRewards Sum of pending rewards across all stakes.
*/
function getPendingRewards(uint256 poolId, address user) external view returns (uint256 totalRewards) {
Stake[] memory stakes = userStakes[poolId][user];
for (uint256 i = 0; i < stakes.length; i++) {
totalRewards += _calculateRewards(poolId, stakes[i], block.timestamp);
}
}
/**
* @notice Returns the pending reward for a specific stake (by index) using the current time.
* @param poolId The pool identifier.
* @param user The user's address.
* @param stakeIndex The stake index.
* @return reward The pending reward.
*/
function getPendingRewardsByStakeId(
uint256 poolId,
address user,
uint256 stakeIndex
) external view returns (uint256 reward) {
Stake[] memory stakes = userStakes[poolId][user];
if (stakeIndex >= stakes.length) revert IndexOutOfBounds(stakeIndex);
reward = _calculateRewards(poolId, stakes[stakeIndex], block.timestamp);
}
/**
* @notice Previews the reward for a specific stake up to a future timestamp.
* @param poolId The pool identifier.
* @param user The user's address.
* @param stakeIndex The stake index.
* @param futureTimestamp Future timestamp for preview.
* @return reward The previewed reward.
*/
function previewRewardForTimestampByStakeId(
uint256 poolId,
address user,
uint256 stakeIndex,
uint256 futureTimestamp
) external view returns (uint256 reward) {
Stake memory stakeData = userStakes[poolId][user][stakeIndex];
if (futureTimestamp <= stakeData.lastClaimTime) return 0;
reward = _calculateRewards(poolId, stakeData, futureTimestamp);
}
/**
* @notice Previews the total future rewards for all stakes of a user up to a given timestamp.
* @param poolId The pool identifier.
* @param user The user's address.
* @param futureTimestamp Future timestamp for preview.
* @return totalReward The sum of previewed rewards.
*/
function previewRewardsForUser(
uint256 poolId,
address user,
uint256 futureTimestamp
) external view returns (uint256 totalReward) {
Stake[] memory stakes = userStakes[poolId][user];
for (uint256 i = 0; i < stakes.length; i++) {
totalReward += _calculateRewards(poolId, stakes[i], futureTimestamp);
}
}
/**
* @notice Returns the current active TierSet for a pool.
* @dev The active tier is assumed to be the last TierSet in the array.
* @param poolId The pool identifier.
* @return activeTier The active TierSet.
*/
function getCurrentActiveTier(uint256 poolId) public view returns (TierSet memory activeTier) {
TierSet[] memory tiers = poolTiers[poolId];
require(tiers.length > 0, 'No tier set');
activeTier = tiers[tiers.length - 1];
}
/**
* @notice Returns the pool configuration along with its current active TierSet.
* @param poolId The pool identifier.
* @return config The PoolConfig.
* @return activeTier The active TierSet.
*/
function getPoolInfo(uint256 poolId) external view returns (PoolConfig memory config, TierSet memory activeTier) {
config = pools[poolId];
activeTier = getCurrentActiveTier(poolId);
}
/**
* @notice Returns all cooldown entries for a user in a pool.
* @param poolId The pool identifier.
* @param user The user's address.
* @return Array of Cooldown entries.
*/
function getUserCooldowns(uint256 poolId, address user) external view returns (Cooldown[] memory) {
return userCooldowns[poolId][user];
}
/**
* @notice Returns the total amount staked in a pool.
* @param poolId The pool identifier.
* @return Total amount staked in the pool.
*/
function getPoolTotalStaked(uint256 poolId) external view returns (uint256) {
return poolTotalStaked[poolId];
}
/**
* @notice Returns the total number of users currently staking in a pool.
* @param poolId The pool identifier.
* @return The active staker count.
*/
function getActiveUserCount(uint256 poolId) external view returns (uint256) {
return poolActiveUserCount[poolId];
}
// ========================================================
// Internal Helper Functions
// ========================================================
/**
* @notice Converts days to seconds.
* @param days_ Number of days.
* @return Seconds equivalent.
*/
function daysToSeconds(uint256 days_) public pure returns (uint256) {
return days_ * SECONDS_IN_DAY;
}
/**
* @dev Adds a new TierSet for a pool using the current block timestamp.
*/
function _addTierSet(uint256 poolId, uint256[] calldata durationsDays, uint256[] calldata apys) internal {
uint256 len = durationsDays.length;
uint256[] memory durationsSeconds = new uint256[](len);
for (uint256 i = 0; i < len; i++) {
durationsSeconds[i] = daysToSeconds(durationsDays[i]);
}
poolTiers[poolId].push(
TierSet({
version: pools[poolId].currentTierVersion,
effectiveTime: block.timestamp,
durations: durationsSeconds,
apys: apys
})
);
}
/**
* @dev Calculates rewards for a stake from its last claim time until a specified end time,
* prorating across TierSet updates.
*/
function _calculateRewards(
uint256 poolId,
Stake memory stakeData,
uint256 endTime
) internal view returns (uint256 totalReward) {
TierSet[] memory tierHistory = poolTiers[poolId];
uint256 L = stakeData.lastClaimTime;
uint256 N = endTime;
uint256 segmentStart = L;
for (uint256 i = 0; i < tierHistory.length; i++) {
if (tierHistory[i].effectiveTime > segmentStart && tierHistory[i].effectiveTime < N) {
uint256 segmentEnd = tierHistory[i].effectiveTime;
TierSet memory activeTier = _findTierSet(tierHistory, segmentStart);
totalReward += _calculateSegmentReward(activeTier, stakeData, segmentStart, segmentEnd);
segmentStart = segmentEnd;
}
}
if (segmentStart < N) {
TierSet memory activeTier = _findTierSet(tierHistory, segmentStart);
totalReward += _calculateSegmentReward(activeTier, stakeData, segmentStart, N);
}
}
/**
* @dev Finds the TierSet in effect at a given timestamp.
* Assumes tierHistory is sorted in ascending order by effectiveTime.
*/
function _findTierSet(
TierSet[] memory tierHistory,
uint256 timestamp
) internal pure returns (TierSet memory activeTier) {
for (uint256 i = tierHistory.length; i > 0; i--) {
if (tierHistory[i - 1].effectiveTime <= timestamp) {
return tierHistory[i - 1];
}
}
return tierHistory[0];
}
/**
* @dev Calculates the reward for a time segment [segStart, segEnd] using a given TierSet.
*/
function _calculateSegmentReward(
TierSet memory tier,
Stake memory stakeData,
uint256 segStart,
uint256 segEnd
) internal pure returns (uint256 reward) {
uint256 startOffset = segStart - stakeData.startTime;
uint256 endOffset = segEnd - stakeData.startTime;
uint256 tiersLength = tier.durations.length;
for (uint256 i = 0; i < tiersLength; i++) {
uint256 tierStart = i == 0 ? 0 : tier.durations[i - 1];
uint256 tierEnd = i < tiersLength - 1 ? tier.durations[i] : type(uint256).max;
uint256 overlapStart = startOffset > tierStart ? startOffset : tierStart;
uint256 overlapEnd = endOffset < tierEnd ? endOffset : tierEnd;
if (overlapEnd > overlapStart) {
uint256 durationInTier = overlapEnd - overlapStart;
reward += (stakeData.amount * tier.apys[i] * durationInTier) / YEAR_DIVISOR;
}
}
}
}
Read Contract
MIN_STAKE_AMOUNT 0x27ed7188 → uint256
SECONDS_IN_DAY 0x61a52a36 → uint256
YEAR_DIVISOR 0x2925c7dc → uint256
_panel 0x8b4db06b → address
daysToSeconds 0x277fc17d → uint256
getActiveUserCount 0x6e7fea7b → uint256
getCurrentActiveTier 0x7d1034ad → tuple
getPendingRewards 0x85dc0aaf → uint256
getPendingRewardsByStakeId 0x198e223e → uint256
getPoolInfo 0x2f380b35 → tuple, tuple
getPoolTierHistory 0xd2aeb1f8 → tuple[]
getPoolTotalStaked 0xfc8a3682 → uint256
getUserCooldowns 0x2d18c1c9 → tuple[]
getUserStakes 0x14ce783d → tuple[]
isAddressAllowed 0xb357ffd0 → bool
poolCounter 0x9e3079cb → uint256
previewRewardForTimestampByStakeId 0xc1d5ec67 → uint256
previewRewardsForUser 0x564b7918 → uint256
Write Contract 13 functions
These functions modify contract state and require a wallet transaction to execute.
batchClaimRewards 0x90d297e6
uint256 poolId
batchInitiateUnstake 0x251b6cf7
uint256 poolId
uint256[] indices
uint256[] amounts
createPool 0x4a7371af
string name
address stakingToken
address rewardToken
uint256 cooldownDays
uint256[] initialDurationsDays
uint256[] initialAPYs
uint8 rewardStrategy
address rewardAddress
bool restricted
bool allowPartialUnstake
initiateUnstake 0x9d6ffa56
uint256 poolId
uint256 index
uint256 amount
setPoolAllowedStakers 0x14bb01d1
uint256 poolId
address[] stakers
bool allowed
setPoolCooldown 0x61fb220c
uint256 poolId
uint256 cooldownDays
setPoolMinStake 0xfd16e1b8
uint256 poolId
uint256 newMinStake
setPoolPause 0xc706b316
uint256 poolId
bool pause
setPoolRestriction 0xca7bc840
uint256 poolId
bool restricted
setPoolRewardConfig 0x8bea2827
uint256 poolId
uint8 newStrategy
address newRewardAddress
stake 0x7b0472f0
uint256 poolId
uint256 amount
updateTiers 0xbd393aa4
uint256 poolId
uint256[] newDurationsDays
uint256[] newAPYs
withdrawCooldown 0x841d9ac2
uint256 poolId
Recent Transactions
No transactions found for this address