Address Contract Verified
Address
0x660aF4Df78bD29bAa56De92F2E8e4f440b0805c3
Balance
0.335447 ETH
Nonce
1
Code Size
22738 bytes
Creator
0x66290821...11C1 at tx 0x98f5023c...4b5405
Indexed Transactions
0
Contract Bytecode
22738 bytes
0x61016080604052600436101561001e575b50361561001c57600080fd5b005b60003560e01c908163024ece8914613c64575080630306c19a1461364257806307c9f46614613622578063099e4133146135f9578063130894e01461358b5780631411b4381461356d5780631f2a8dd61461354a5780631fb05b331461352c5780632179e5fe14612fd357806321ff997014612f825780633d40a54f14612dda5780634655471914612da257806346d1cdc014612cdf5780634c9f166d14612cbc57806351cff8d914612b46578063615b2c7814612793578063715018a61461273657806375b1510c146115bf57806378a29c09146126c45780637946b7811461262457806379ba5097146125615780638288c80e146124e25780638770912f1461235b5780638bbed8ef1461233d5780638c2bd69214611cef5780638da5cb5b14611cc65780638edfe78714611caa5780639474aef614611c8c57806395e737c014611c1e578063a8037e0e14611b34578063a8f3f408146115e9578063ab04663b146115c4578063ab535f8f146115bf578063b9f433a614610bbe578063bf26931f14610ba0578063c03fb87c14610acc578063ca44bb5314610ab0578063d0339fee14610922578063d89b6dc014610906578063def08d26146107ed578063e30c3978146107c4578063eaecfca714610727578063ef2104a2146102aa578063f030f0131461028a5763f2fde38b1461021a5738610010565b3461028557602036600319011261028557610233613d79565b61023b613f9b565b60018060a01b0380911690816001600160601b0360a01b6001541617600155600054167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e22700600080a3005b600080fd5b3461028557600036600319011261028557602060405164174876e8008152f35b346102855760403660031901126102855760243560ff81168103610285576004546001600160a01b031633148015610713575b156106e9576102ea614825565b6102f56004356156f9565b610300600435615787565b6004356000526012602052604060002060018060a01b03600054163314610691575b600501600460ff198254161790557ff0fbc916c90170d09d494b840e0bb03ab2e82065a8e1990d3673211b45182a8e60206040516004358152a1600435600052601260205260406000209060018060a01b03825416906001830154916002840154916103966103918486614032565b614ff6565b926103a46103918287614032565b946103b26103918383614032565b916103c06103918284614032565b976000926000958a89898c6000935b86851061063e5750505050506000925b84841061049d575060008051602061587d8339815191528a8a6104938b8f6104608d60008051602061583d83398151915260008051602061585d833981519152938f6104346040519283928960043585615117565b0390a16040805160043581526001600160a01b0386166020820152908101919091529081906060820190565b0390a160405161046f81613e26565b600081526040519061048082613e26565b60008252604051958695600435876151d8565b0390a16001600255005b9091929394966105bb6105b5886105c1938f8f8f8f918f8b908e958e956104d46104cb8f60028b91016149c3565b98905499614032565b956104ed8760018060a01b038b8b60031b1c16926148cc565b526001600160a01b03600388811b8a901c82166000908152601360209081526040808320600480358552925290912080546002820154928201549190930154909316969193909160ff166001036105cc575061057a9796959492610558610574959361055e93614032565b90614032565b936105748360009d6000975b6105748b8b6148cc565b526148cc565b519160018060a01b039160031b1c1660005260146020528d6040600020906000526020526105ae6040600020918254614032565b9055614032565b98613ff3565b94613ff3565b9291909493946103df565b85829d610574969497956105749361057a9c9b9a9560018060a01b0360005416600052601490816020526040600020836000526020526106126040600020918254614032565b90558660005260205260406000209060005260205260406000206106378a8254614032565b905561056a565b9a6105bb936105b593868d948d9f99829d9e8d9b610665839e9f9c60016106819e016149c3565b969054976104ed8760018060a01b038b8b60031b1c16926148cc565b89898c8e989798969594966103cf565b60078101546202a30081018091116106d35742116103225760405162461bcd60e51b8152602060048201526002602482015261062760f31b6044820152606490fd5b634e487b7160e01b600052601160045260246000fd5b60405162461bcd60e51b81526020600482015260026024820152611a1960f11b6044820152606490fd5b506000546001600160a01b031633146102dd565b3461028557602036600319011261028557610740613d79565b610748613f9b565b6001600160a01b0316801561079b57600480546001600160a01b03191682179055604080519182523360208301527fea8ca364cc9c0e44ae5ceab245cececcc710724873a9ecfc89a0559983e4473991a1005b60405162461bcd60e51b81526020600482015260016024820152603160f81b6044820152606490fd5b34610285576000366003190112610285576001546040516001600160a01b039091168152602090f35b604036600319011261028557600435610804613d8f565b61080c613f9b565b60018060a01b03169081600052602090601082526108306040600020541515614002565b61083b811515614924565b826108eb578034036108c1575b3360005260148252604060002083600052825261086b6040600020918254614032565b9055336000818152601483526040808220948252939092529082902054825190815260208101919091527fc6eb93583634faca0ac6216e0725f08457b08e39adbbf4b9484973740f23327791819081015b0390a1005b60405162461bcd60e51b8152600481018390526002602482015261323960f01b6044820152606490fd5b6108f53415614d74565b61090181303386614da5565b610848565b3461028557600036600319011261028557602060405160058152f35b61018036600319011261028557610937613d79565b604435602435610945613d69565b6084359060028210156102855760a4359360c4359160e4359160ff831683036102855761012435946001600160a01b03808716870361028557610164356001600160401b0381116102855761099e903690600401613e79565b986109aa600954613ff3565b96876009554282111580610aa7575b15610a7e5760077fd2f7931a802085b3d0234d4c320ce7ee0041da96678ce2bf5c93e8d3d7e65f529460809461001c9e1693610a0a610a05866000526010602052604060002054151590565b614002565b8a6000526012602052604060002091856001600160601b0360a01b8454161783556002600584019161ff0083549160081b169061ffff19161717905560068201550155610a57848a614032565b6040519188835260208301523360408301526060820152a1610144359561010435946140a5565b60405162461bcd60e51b81526020600482015260016024820152601960f91b6044820152606490fd5b504281116109b9565b3461028557600036600319011261028557602060405160088152f35b346102855760403660031901126102855760a0610ae7613d8f565b60006080604051610af781613dd5565b8281528260208201528260408201528260608201520152600180831b038091166000526013602052604060002060043560005260205260406000209060405190610b4082613dd5565b82549283835260ff6001820154166020840190815260ff6002830154916040860192835260806004866003870154169560608901968752015496019586526040519687525116602086015251604085015251166060830152516080820152f35b34610285576000366003190112610285576020600954604051908152f35b34610285576040366003190112610285576001600160401b0360043581811161028557610bef903690600401613e79565b6024359182116102855736602383011215610285578160040135610c1281613e62565b92610c206040519485613e41565b8184526024602085019260051b8201019036821161028557602401915b8183106115a557505050610c4f6157e2565b610c57614825565b805190600a8211158061159b575b610c729093929193614879565b6000925b818410610c84576001600255005b610c8e84826148cc565b5193846000526012602052600560406000200180549060ff916008928082851c166002811015611527576115715780610cc7868a6148cc565b51169384151580611567575b1561153d57610ce18a6156f9565b610cea8a615742565b60048501908282116106d357828216101561152757169060ff19161790557fb6171b34500ba874839b220ee75cbfbeb25bba762c6a57c158e19a3c27af89ae60408051888152836020820152a1600381036111aa57509091929380600052601260205260406000209060018060a01b03825416600183015492600281015490610d766103918387614032565b94610d846103918483614032565b92610d926103918284614032565b90610da06103918285614032565b9260009160009560005b83811061101857506000925b828410610e7b5750505050509260008051602061585d833981519152610e3b610e6b9460008051602061583d833981519152610e739b9a989560008051602061587d8339815191529a98610e116040519283928a8d85615117565b0390a1604080518881526001600160a01b0387166020820152908101919091529081906060820190565b0390a1604051610e4a81613e26565b6000815260405191610e5b83613e26565b60008352604051968796876151d8565b0390a1613ff3565b929190610c76565b90919293968a8c8b876002870190610e92916149c3565b905460039190911b1c6001600160a01b03169182610eb0878b614032565b9182610ebb916148cc565b52826000526013602052604060002084600052602052604060002054938360005260136020526040600020816000526020528d8d6040600020600201549686600052601360205260406000208460005260205260406000206004015493876000526013602052604060002090600052602052600160a01b600190036040600020600301541692600160a01b60019003600054166000526014968760205260406000208160005260205260406000208a815490610f7691614032565b9055846000528760205260406000209060005260205285604060002086815490610f9f91614032565b9055610faa916148cc565b52610fb5908d6148cc565b52610fc08d8d6148cc565b52610fcb908d6148cc565b519160005260205260406000208c600052602052604060002090815490610ff191614032565b9055610ffc91614032565b9661100690613ff3565b9361101090613ff3565b929190610db6565b9396919290918b8561102d81600187016149c3565b905460039190911b1c6001600160a01b031691829161104b916148cc565b5280600052601360205260406000208c6000526020526040600020549080600052601360205260406000208d6000526020528b8d8c6040600020600201549484600052601360205260406000208360005260205260406000206004015492856000526013602052604060002090600052602052600160a01b600190036040600020600301541691600160a01b60019003600054166000526014948560205260406000208160005260205260406000208881549061110791614032565b905583600052856020526040600020906000526020528b60406000208581549061113091614032565b905561113b916148cc565b526111468d8c6148cc565b526111518c8c6148cc565b5261115c888d6148cc565b519160005260205260406000208c60005260205260406000209081549061118291614032565b905561118d91614032565b9661119790613ff3565b936111a190613ff3565b92919092610daa565b85600052601260205260406000209560018060a01b038754166000600189019360028a019182600460038d01549c0154926002849114611519575b5050855483549b92908c906111f981614ff6565b9461121461039161120d6103918686614032565b9484614032565b936000926000925b81841061141857505050506112388e9798999a9b9c9d9e614ff6565b966000905b80821061135757505050936113266113429460008051602061585d8339815191526112f98b999660008051602061583d833981519152610e739f9e9c60008051602061587d8339815191529e6112cf6113349a839e60018060a01b0360005416600052601460205260406000208560005260205260406000206112c1888254614032565b905560405194859485615117565b0390a1604080518c81526001600160a01b038c166020820152908101919091529081906060820190565b0390a1611318604051998a998a5260e060208b015260e08a019061514f565b9088820360408a0152613ef3565b90868203606088015261514f565b908482036080860152613ef3565b90600060a084015260c08301520390a1613ff3565b90919461140c6114066114129260028c8f8f8c918f8b611376916149c3565b60018060a01b0391549060031b1c1660005260136020526040600020906000526020526040600020926113ab8b8554926148cc565b5260038301546001600160a01b0316806113c5848e6148cc565b528c6113d760048601549485926148cc565b5260005260146020526040600020906000526020526113fc6040600020918254614032565b9055015490614032565b96613ff3565b92613ff3565b9061123d565b90919293966105bb6105b5611511928f8f8f8b908f8f8f948b958f938f61148761148f916114488860029d6149c3565b60018060a01b0391549060031b1c169586600052601360205260406000209060005260205261055860406000209b6114828d548094614911565b615070565b9586926148cc565b5260038701956114a98460018060a01b03895416926148cc565b526114ba60048801938454926148cc565b52600052601491826020526040600020846000526020526114e16040600020918254614032565b9055549260018060a01b039054166000526020526040600020906000526020526113fc6040600020918254614032565b92919061121c565b9b9096935091508b806111e5565b634e487b7160e01b600052602160045260246000fd5b60405162461bcd60e51b8152602060048201526002602482015261313160f01b6044820152606490fd5b5060048510610cd3565b60405162461bcd60e51b8152602060048201526002602482015261031360f41b6044820152606490fd5b5082518214610c65565b823560ff8116810361028557815260209283019201610c3d565b613ed7565b3461028557600036600319011261028557602060405169152d02c7e14af68000008152f35b34610285576060366003190112610285576001600160401b036004358181116102855761161a903690600401613f7d565b9060243581811161028557611633903690600401613f7d565b906044359081116102855761164c903690600401613e79565b90611655613f9b565b825161166e825184519083149081611b2a575b50614879565b60005b81811061174c57505060005b83518110156116b1576116ac906116a66001600160a01b0361169f83886148cc565b5116614a73565b50613ff3565b61167d565b509060005b83518110156116e4576116df906116a66001600160a01b036116d883886148cc565b5116614ad7565b6116b6565b7fee34c4fff4314ef30863a798e2773a9aa5251ff332d9de421fdddcabfc423778611725856117418561173388604051958695608087526080870190613ced565b908582036020870152613ced565b908382036040850152613ef3565b3360608301520390a1005b6117766001600160a01b0361176183886148cc565b51166000526010602052604060002054151590565b611b00576001600160a01b0361178c82856148cc565b511661181f5761179c81856148cc565b51156117f557806117b06117f092866148cc565b516001600160a01b036117c383896148cc565b51166000908152601660205260409020556116a66001600160a01b036117e983896148cc565b51166149db565b611671565b60405162461bcd60e51b8152602060048201526002602482015261032360f41b6044820152606490fd5b6001600160a01b0361183182856148cc565b511660405190633fabe5a360e21b9081835260a083600481845afa8015611a5357600093600091611ad8575b506000841315611aae57611875620151809142614904565b11611a845760405191825260a082600481845afa918215611a5357600092611a5f575b506000908190815b60058110611987575b50506118b59250615420565b90606491826118c38261540a565b059060008282039212818312811690828413901516176106d35780846118eb6118f29361540a565b0590615820565b90821215918261197c575b50501561195557506117f0906001600160a01b0361191b82866148cc565b51166001600160a01b0361192f83896148cc565b511660005260116020526040600020906001600160601b0360a01b825416179055613ff3565b60405162461bcd60e51b8152602060048201526002602482015261323760f01b6044820152fd5b1315905087806118fd565b909160405190639a6fc8f560e01b82526001600160501b038616600483015260a082602481875afa908115611a53576119c892600092611a1d575b50615820565b92600181018091116106d357936001600160501b03811615611a14576001600160501b036000199116016001600160501b0381116106d357611a0a9091613ff3565b93929190936118a0565b849392506118a9565b611a4091925060a03d60a011611a4c575b611a388183613e41565b8101906153ca565b5050509050908d6119c2565b503d611a2e565b6040513d6000823e3d90fd5b611a7991925060a03d60a011611a4c57611a388183613e41565b505050509088611898565b60405162461bcd60e51b8152602060048201526002602482015261323560f01b6044820152606490fd5b60405162461bcd60e51b8152602060048201526002602482015261313960f01b6044820152606490fd5b9050611af491935060a03d60a011611a4c57611a388183613e41565b5094925050928961185d565b60405162461bcd60e51b81526020600482015260026024820152611a1b60f11b6044820152606490fd5b9050821486611668565b346102855760208060031936011261028557611b4e613d79565b90600b8054611b5c81614ff6565b93611b6682614ff6565b926001600160a01b039182169060005b848110611bab57611b9a88611ba78989604051948594604086526040860190613ced565b9184830390850152613ef3565b0390f35b611c19908260005284817f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9015416611be3828b6148cc565b528360005260148852604060002085611bfc838c6148cc565b51166000528852604060002054611c1382896148cc565b52613ff3565b611b76565b34610285576020366003190112610285577f39d65726005dded9e32c1c37c38c433a478814ed5df9a080f9788a8861cc383a6108bc611c5b613d5a565b611c63613f9b565b600a805460ff191660ff9215159283161790556040805191825233602083015290918291820190565b34610285576000366003190112610285576020600754604051908152f35b3461028557600036600319011261028557602060405160148152f35b34610285576000366003190112610285576000546040516001600160a01b039091168152602090f35b3461028557606036600319011261028557611d08613d79565b6044359060ff8216820361028557611d1e6157e2565b611d26614825565b611d316024356156f9565b611d3c602435615787565b60018060a01b0381166000526013602052604060002060243560005260205260ff600160406000200154161561231357602435600052601260205260406000209060018060a01b0382541691600181015491600282015460405193611da085613e0b565b600185526020368187013760405191611db883613e0b565b600183526020368185013760405194611dd086613e0b565b600186526020368188013760405192611de884613e0b565b60018452602036818601376001600160a01b038681166000908152601360209081526040808320602435845290915290208054600280830154600484015460039094015460058801549499951697909693909160089190911c60ff169081101561152757801590816122de575b81156122d3575b50156121395750600160ff611e7b8b611e7684870161403f565b615219565b9e16036120db57611e90906105588887614032565b9a5b60018060a01b03891660005260146020526040600020906000526020526040600020611ebf8c8254614032565b905560038101611ed08c8254614904565b9055826000198101116106d3577f679f73fac18389f339b511a489b811473ff4d0f3d4937002ed9951c7636cce3a6120236120c49960008051602061585d8339815191529860008051602061587d8339815191529f986113189f8f8f9091611fb88f611fbe946120709f611f9660008051602061583d8339815191529f8e926001611f64611f7993600019018287016149c3565b828060a01b0391549060031b1c1694016149c3565b819391549060031b9160018060a01b03809116831b921b19161790565b9055611fa460018d01615195565b6001600160a01b03891690611fb8906148a9565b526148a9565b52611fc8866148a9565b526001600160a01b031660008181526013602090815260408083206024358085529083528184208481556001810185905560028101859055600381018590556004019390935580519384529083019190915290918291820190565b0390a160018060a01b03905416976120446040519283928b60243585615117565b0390a16040805160243581526001600160a01b0388166020820152908101919091529081906060820190565b0390a161133460405161208281613e26565b600081526120b66040519361209685613e26565b600085526040519889986024358a5260e060208b015260e08a0190613ced565b908682036060880152613ced565b90600360a084015260c08301520390a16001600255005b9a60018060a01b03600054166000526014602052604060002081600052602052604060002061210b868254614032565b90558560005260146020526040600020816000526020526040600020612132888254614032565b9055611e92565b9350919a93600160ff6121548b611e766002879c970161403f565b9e160361226f57612169929161055891614032565b986000946000935b60018060a01b0389166000526014602052604060002090600052602052604060002061219e8c8254614032565b9055600481016121af8c8254614904565b9055826000198101116106d3577f679f73fac18389f339b511a489b811473ff4d0f3d4937002ed9951c7636cce3a6120236120c49960008051602061585d8339815191529860008051602061587d8339815191529f986113189f8f8f9091611fb88f611fbe946120709f61225c60008051602061583d8339815191529f8e926002612243611f7993600019018287016149c3565b905460039190911b1c6001600160a01b031694016149c3565b905561226a60028d01615195565b611fa4565b9360018060a09d949d9893981b0360005416600052601460205260406000208160005260205260406000206122a5868254614032565b905585600052601460205260406000208160005260205260406000206122cc888254614032565b9055612171565b60019150148f611e5c565b905060018060a01b038b1660005260136020526040600020602435600052602052600160ff8160406000200154161490611e55565b60405162461bcd60e51b8152602060048201526002602482015261189960f11b6044820152606490fd5b34610285576000366003190112610285576020600654604051908152f35b3461028557602080600319360112610285576004356001600160401b0381116102855761238c903690600401613f7d565b90612395613f9b565b815160005b81811061244b57505060005b82518110156123d4576123cf906116a66001600160a01b036123c883876148cc565b5116614b42565b6123a6565b5060005b825181101561240657612401906116a66001600160a01b036123fa83876148cc565b5116614c2c565b6123d8565b7fe315bdee05329e2e69a5253f25814537fb86d11c4c808142d2ea502822516e7f6124408484604051928392604084526040840190613ced565b9033908301520390a1005b6001600160a01b036124618161176184886148cc565b156124b85790816124b39261247683886148cc565b51166000526011855260406000206001600160601b0360a01b815416905561249e82876148cc565b51166000526016845260006040812055613ff3565b61239a565b60405162461bcd60e51b8152600481018590526002602482015261343760f01b6044820152606490fd5b34610285576020366003190112610285577f1952e249d74df2e2b0c9a7de47fdd506d63de48981dfbffad2b925a1152928f96108bc600435612522613f9b565b60075481101580612552575b61253790614924565b60078190556040805191825233602083015290918291820190565b506402540be40081111561252e565b34610285576000366003190112610285576001546001600160a01b0333818316036125cd576001600160601b0360a01b8092166001556000549133908316176000553391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b60405162461bcd60e51b815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f7420746865206044820152683732bb9037bbb732b960b91b6064820152608490fd5b34610285576020366003190112610285576001600160a01b03612645613d79565b1660005260156020526126aa604060002060ff6002820154166126b860016126966040519461267f866126788184614d37565b0387613e41565b61268f6040518094819301614d37565b0382613e41565b604051948594606086526060860190613ef3565b908482036020860152613ef3565b90151560408301520390f35b610120366003190112610285576126d9613d69565b6084359060ff821682036102855760c435916001600160a01b03831683036102855761010435926001600160401b0384116102855761271f61001c943690600401613e79565b9260e4359260a435916044356024356004356140a5565b346102855760003660031901126102855761274f613f9b565b606460405162461bcd60e51b815260206004820152602060248201527f52656e6f756e63696e67206f776e6572736869702069732064697361626c65646044820152fd5b34610285576080366003190112610285576004356001600160401b038111610285576127c3903690600401613e79565b6024356001600160401b038111610285576127e2903690600401613e79565b906044356001600160a01b0381168103610285576064351515606435036102855761280b613f9b565b6001600160a01b0381166000908152600c602052604090205461282f901515614cd5565b81519183519183151580612b3d575b80612b32575b61284d90614879565b64174876e8009260005b856000198101116106d35760001986018110156128fc5761287881856148cc565b51600182018083116106d35761288e90866148cc565b51106128d2576064356128c0575b806128b6866128ae6128bb948b6148cc565b511115614d06565b613ff3565b612857565b69152d02c7e14af6800000945061289c565b60405162461bcd60e51b8152602060048201526002602482015261323160f01b6044820152606490fd5b5091859284606435612b21575b60001982019182116106d3576128ae61292292866148cc565b60405190606082018281106001600160401b03821117612ace5760405281526020810192835260408101906064351515825260018060a01b03831660005260156020526040600020905180516001600160401b038111612ace57600160401b91828211612ace578354828555808310612af8575b5060200183600052602060002060005b838110612ae4575050505060019485830190518051926001600160401b038411612ace578311612ace578154838355808410612aa3575b5060200190600052602060002060005b838110612a90577fbf48b0820ca81715d6b1acb25649f7ba1d18bd574712fcc4788a22a3f9e49229612a7a89898960028a019051151560ff8019835416911617905560018060a01b03166000526015602052604060002060ff60026040519485946040865260606040870152612a6660a0870186614d37565b868103603f19016060880152908501614d37565b92015416151560808301523360208301520390a1005b87906020845194019381840155016129ed565b826000528784602060002092830192015b828110612ac25750506129dd565b60008155018890612ab4565b634e487b7160e01b600052604160045260246000fd5b6001906020845194019381840155016129a6565b8460005282602060002091820191015b818110612b155750612996565b60008155600101612b08565b5069152d02c7e14af6800000612909565b506014841115612844565b5082841461283e565b346102855760208060031936011261028557612b60613d79565b612b68614825565b3360009081526014835260408082206001600160a01b039093168083529284529020548015612c935733600052601483526040600020826000528352600060408120558115600014612c3357600080808084335af1612bc5614ee3565b5015612c0957906060917f30f0f2f1c6b25d25ce05404ece9a5feb949e2e6f459f939cc1f01c6512428066935b604051928352820152336040820152a16001600255005b60405162461bcd60e51b8152600481018490526002602482015261333760f01b6044820152606490fd5b60405163a9059cbb60e01b84820152336024820152604480820183905281527f30f0f2f1c6b25d25ce05404ece9a5feb949e2e6f459f939cc1f01c6512428066936060939291612c8e90612c88606482613e41565b84614de7565b612bf2565b60405162461bcd60e51b81526004810184905260016024820152600760fb1b6044820152606490fd5b3461028557600036600319011261028557602060ff600a54166040519015158152f35b3461028557604036600319011261028557600435602435612cfe613f9b565b80151580612d97575b80612d8e575b80612d83575b15612d59577f42a3e0b2ac4ef58d5c467d7199e859b2b40ee8a57507885e2d4539a9ae0a05da9181606092600655816005556040519182526020820152336040820152a1005b60405162461bcd60e51b8152602060048201526002602482015261323360f01b6044820152606490fd5b506032821115612d13565b50811515612d0d565b506032811115612d07565b346102855760203660031901126102855760206004358015159081612dcd575b506040519015158152f35b9050600954101582612dc2565b34610285576020806003193601126102855760043590604051612dfc81613db9565b60008152606082820152606060408201526000606082015260006080820152600060a0820152600060c0820152600060e0820152600061010080920152612e42836156f9565b826000526012825260406000209160405193612e5d85613db9565b83546001600160a01b03908116865292612e796001860161403f565b93838701948552612e8c6002870161403f565b93604088019485526003870154956060890196875260048801549060808a0191825260058901549260ff60a08c0194612ec782821687614099565b60081c169460c08c0195600281101561152757865260068b01549a60e08d019b8c526007015497878d01988952612efd9061565c565b612f079086614099565b6040519b828d525116908b01525195610120968760408c01526101408b01612f2e91613ced565b90518a8203601f190160608c0152612f469190613ced565b965160808a01525160a08901525160088110156115275760c08801525194600286101561152757869560e0870152519085015251908301520390f35b34610285576020366003190112610285577fe2c8b1f4bcb8086dc9805a9868ee616914948de41a09786213a2284a8ffba27b6040600435612fc1613f9b565b806003558151908152336020820152a1005b61010036600319011261028557612fe8613d69565b60a435906001600160a01b03821682036102855760e4356001600160401b0381116102855761301b903690600401613e79565b916130276004356156f9565b3360005260136020526040600020600435600052602052604060002060ff6001820154161561350357600435600052601260205260406000209260018060a01b038454169460ff600a5416156134d95761308260043561565c565b6008811015611527576002036134af57856134a0573460243503613476575b6130af604435602435614032565b906130ba8288615260565b9460009360ff6008541661330e575b505050838111156132e4576130fe6130e9826130e48961543b565b614911565b876132d3576130f86012615090565b90615070565b6000878152600e6020526040902054156132cd576131289060166020526040600020541115614924565b60443561321d575b9061317360a095949392877f0b17a6125c8c06bcd40d6a58de2c6f4e8815d9f52c8f8b6999ca7493c0fea31a98613206575b5061316d8386614032565b90614904565b9261317f848454614032565b835561319060028401918254614032565b90556131a160048301918254614032565b905560018181015460ff16036131f057600383016131c0838254614032565b90555b5491600180851b0390541690604051926004358452602084015260408301523360608301526080820152a1005b600483016131ff838254614032565b90556131c3565b613217906024359030903390614da5565b88613162565b93929190336000526014602052604060002086600052602052604435604060002054106132a35761317360a0957f0b17a6125c8c06bcd40d6a58de2c6f4e8815d9f52c8f8b6999ca7493c0fea31a9733600052601460205260406000208160005260205260406000206132936044358254614904565b9055975091929394955050613130565b60405162461bcd60e51b8152602060048201526002602482015261199b60f11b6044820152606490fd5b50613128565b6130f86132df896150ba565b615090565b60405162461bcd60e51b8152602060048201526002602482015261333560f01b6044820152606490fd5b60ff6040999693999895929794985191336020840152166040820152608435606082015260018060a01b038716608082015260c43560a082015260a0815261335581613df0565b60208151910120604051602081019182526020815261337381613e0b565b5190209660035497966000975b8a518910156133cb57613393898c6148cc565b5190818110156133b7576000526020526133b1604060002098613ff3565b97613380565b906000526020526133b160406000206105b5565b9194975092959850969093960361344c576b1d6329f1c35ca4bfabb9f56160281b946084358603908682116106d357869161340591614911565b049485916001600160a01b031661341d575b806130c9565b909491925060c4358103918183116106d3576134449261343c91614911565b048094614904565b908680613417565b60405162461bcd60e51b8152602060048201526002602482015261343560f01b6044820152606490fd5b60405162461bcd60e51b8152602060048201526002602482015261333360f01b6044820152606490fd5b6134aa3415614d74565b6130a1565b60405162461bcd60e51b8152602060048201526002602482015261199960f11b6044820152606490fd5b60405162461bcd60e51b8152602060048201526002602482015261333160f01b6044820152606490fd5b60405162461bcd60e51b81526020600482015260016024820152603760f81b6044820152606490fd5b346102855760003660031901126102855760206040516202a3008152f35b3461028557600036600319011261028557602060ff600854166040519015158152f35b34610285576000366003190112610285576020600554604051908152f35b34610285576020366003190112610285577f46eb189395b90ab0cfd42a309b25f46e015815a01e7e676f6f804ef62466127d6108bc6135c8613d5a565b6135d0613f9b565b6008805460ff191660ff9215159283161790556040805191825233602083015290918291820190565b34610285576000366003190112610285576004546040516001600160a01b039091168152602090f35b346102855760003660031901126102855760206040516402540be4008152f35b34610285576060366003190112610285576001600160401b0360243581811161028557613673903690600401613d2a565b90916044359081116102855761368d903690600401613d2a565b6136956157e2565b61369d614825565b6136a86004356156f9565b6136b3600435615742565b60043560005260126020526005604060002001805460ff8160081c16600281101561152757600103613c3a5782851480613c2f575b15613c05576000805b868210613b0b5768056bc75e2d63100000915003613ae15760059060ff19161790557fb6171b34500ba874839b220ee75cbfbeb25bba762c6a57c158e19a3c27af89ae60408051600435815260056020820152a1600435600052601260205260406000209160018060a01b0383541660c05260018301549161377285614ff6565b916137806103918786614904565b948761378f6103918988614904565b9561379981614ff6565b936137a382614ff6565b608052600095600096898c6000965b8688106139175750505050505050509160008051602061583d8339815191526138396138679360008051602061585d8339815191529560018060a01b0360005416600052601460205260406000209060018060a01b039054166000526020526040600020613821858254614032565b90556040519182916080519060c05160043585615117565b0390a16040805160c05160043582526001600160a01b03166020820152908101919091529081906060820190565b0390a1604051938060e08601600435875260e06020880152526101008501959060005b8181106138f15760008051602061587d83398151915287806138d8896138ca8a6138bc8f8c8782036040890152613ef3565b908582036060870152613ced565b908382036080850152613ef3565b600160a083015260c05160c08301520390a16001600255005b909196602080600192838060a01b036139098c613da5565b16815201980192910161388a565b879a969791899161394b61392e85600186016149c3565b905460039190911b1c6001600160a01b0316611e76368585613f27565b9061395985600186016149c3565b60018060a01b0391549060031b1c1660005260136020526040600020600435600052602052604060002060a052828214600014613a5b57505050926139e6926139c7826139af6139d7956001613a5199016149c3565b905460039190911b1c6001600160a01b0316926148cc565b528d611c138260a05154926148cc565b945b600260a051015490614032565b9760a05160048101906003825491019060018060a01b038254166000526014602052604060002060018060a01b038b5416600052602052613a2d6040600020918254614032565b9055546001600160a01b0316613a43838b6148cc565b5254611c13826080516148cc565b9392898c8e6137b2565b6139e69550613a5196999450613aac613aa78368056bc75e2d63100000613a978d613a90848f6003613adb9c9d0154936148e0565b3590614911565b04966001600160a01b03946148e0565b6148f0565b166000526014602052604060002060c0516000526020526040600020613ad3848254614032565b90558d6148cc565b526139d9565b60405162461bcd60e51b8152602060048201526002602482015261313760f01b6044820152606490fd5b613b2190613b1a8387896148e0565b3590614032565b9080613b9f575b6001600160a01b03613b3e613aa7838a8c6148e0565b166000526013602052604060002060043560005260205260ff6001604060002001541615613b7557613b6f90613ff3565b906136f1565b60405162461bcd60e51b8152602060048201526002602482015261313560f01b6044820152606490fd5b613bad613aa782898b6148e0565b60001982018281116106d3576001600160a01b03908190613bd390613aa7908c8e6148e0565b16911611613b285760405162461bcd60e51b8152602060048201526002602482015261189b60f11b6044820152606490fd5b60405162461bcd60e51b81526020600482015260026024820152610c4d60f21b6044820152606490fd5b50600a8511156136e8565b60405162461bcd60e51b8152602060048201526002602482015261313360f01b6044820152606490fd5b3461028557600036600319011261028557600f5480825260209082828101600f6000527f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac8029260005b85828210613cd757505050613cc392500383613e41565b611ba7604051928284938452830190613ced565b8554845260019586019588955093019201613cac565b90815180825260208080930193019160005b828110613d0d575050505090565b83516001600160a01b031685529381019392810192600101613cff565b9181601f84011215610285578235916001600160401b038311610285576020808501948460051b01011161028557565b60043590811515820361028557565b6064359060ff8216820361028557565b600435906001600160a01b038216820361028557565b602435906001600160a01b038216820361028557565b35906001600160a01b038216820361028557565b61012081019081106001600160401b03821117612ace57604052565b60a081019081106001600160401b03821117612ace57604052565b60c081019081106001600160401b03821117612ace57604052565b604081019081106001600160401b03821117612ace57604052565b602081019081106001600160401b03821117612ace57604052565b90601f801991011681019081106001600160401b03821117612ace57604052565b6001600160401b038111612ace5760051b60200190565b81601f8201121561028557803591613e9083613e62565b92613e9e6040519485613e41565b808452602092838086019260051b820101928311610285578301905b828210613ec8575050505090565b81358152908301908301613eba565b34610285576000366003190112610285576020604051600a8152f35b90815180825260208080930193019160005b828110613f13575050505090565b835185529381019392810192600101613f05565b9291613f3282613e62565b91613f406040519384613e41565b829481845260208094019160051b810192831161028557905b828210613f665750505050565b838091613f7284613da5565b815201910190613f59565b9080601f8301121561028557816020613f9893359101613f27565b90565b6000546001600160a01b03163303613faf57565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b60001981146106d35760010190565b1561400957565b60405162461bcd60e51b81526020600482015260016024820152603360f81b6044820152606490fd5b919082018092116106d357565b9060405191828154918282526020928383019160005283600020936000905b8282106140765750505061407492500383613e41565b565b85546001600160a01b03168452600195860195889550938101939091019061405e565b60088210156115275752565b959492909693919760e05285600052601260205260406000206040516140ca81613db9565b81546001600160a01b031681526140e36001830161403f565b60208201526140f46002830161403f565b6040820152600382015460608201526004820154608082015260058201549161412360ff841660a08401614099565b600260ff8460081c1610156115275760076101009160ff60c085019560081c168552600681015460e0850152015491015261415d876156f9565b516002811015611527576001036147dc57600160ff8216036147b3575b33600052601360205260406000208660005260205260ff6001604060002001541661478a576141a7614825565b80928660005260126020526040600020806101205260018060a01b039054169060ff600a5416156134d9576141db8861565c565b6008811015611527576002036134af578161477b57348903613476575b6142028a8a614032565b9061420d8284615260565b9660009560ff600854166145d6575b505050858111156132e457614246614237826130e48561543b565b836145ca576130f86012615090565b6000838152600e6020526040902054156145b757614274908360005260166020526040600020541115614924565b8961455b575b61429660ff91838b600195614547575b505061316d8689614032565b9216148015614527575b156144fc57600161012051016142b63382615028565b54600361012051016142c9838254614032565b90555b60ff600561012051015460081c169060028210156115275760018203614474576006541061444a577f010fe3fa2b3358b8306447cc0a08515aa6c99e255d3d63b181fee6b377b6682398614437967ff6d42021a4549bed2c5e686f3a0e138917db52e05ad24e28d9239f7caef635ca9560a09560016143f1955b14614441575b6040519260049261435c85613dd5565b87855260ff602086019116815260408501908b825260608601926001808c1b03168352608086019384523360005260136020528d60406000209060005260205260406000209551865560ff6001870191511660ff1982541617905551600285015560038401906001808a1b039051166001600160601b03891b82541617905551910155600180851b0361012051541698614032565b6040519187835260208301523360408301528760608301526080820152a1604080519384526001600160a01b039094166020840152928201929092529081906060820190565b0390a16001600255565b6001915061434c565b60405162461bcd60e51b8152602060048201526002602482015261033360f41b6044820152606490fd5b600554106144d2577f010fe3fa2b3358b8306447cc0a08515aa6c99e255d3d63b181fee6b377b6682398614437967ff6d42021a4549bed2c5e686f3a0e138917db52e05ad24e28d9239f7caef635ca9560a09560016143f195614346565b60405162461bcd60e51b81526020600482015260026024820152610d0d60f21b6044820152606490fd5b6002610120510161450d3382615028565b5460046101205101614520838254614032565b90556142cc565b5060ff600561012051015460081c166002811015611527576001146142a0565b6145549130903390614da5565b388b61428a565b33600052601460205260406000208260005260205289604060002054106132a35761429660ff916001933360005260146020526040600020816000526020528c6145ab6040600020918254614904565b9055935091505061427a565b6145c5906007541115614924565b614274565b6130f86132df856150ba565b60409c9a989c9b99979593919694929b5160ff60208201923384521660408201528c606082015260018060a01b03891660808201528760a082015260a0815261461e81613df0565b519020604051602081019182526020815261463881613e0b565b5190209b6003549c610100526000610140525b60e05180519061014051918210156146af5790614667916148cc565b51610100518181101561469e57506101005160005260205260406000205b6101005261469561014051613ff3565b6101405261464b565b906000526020526040600020614685565b505091939597999b9a90929496989a610100510361344c576b1d6329f1c35ca4bfabb9f56160281b03906b1d6329f1c35ca4bfabb9f56160281b82116106d3576b1d6329f1c35ca4bfabb9f56160281b9161470991614911565b049586906001600160a01b038916614722575b8061421c565b90969194506b1d6329f1c35ca4bfabb9f56160281b036b1d6329f1c35ca4bfabb9f56160281b81116106d35761476b6b1d6329f1c35ca4bfabb9f56160281b9161477393614911565b048096614904565b92388061471c565b6147853415614d74565b6141f8565b60405162461bcd60e51b81526020600482015260016024820152601b60f91b6044820152606490fd5b60405162461bcd60e51b81526020600482015260016024820152600d60fa1b6044820152606490fd5b60ff81166001811490811561481a575b5061417a5760405162461bcd60e51b81526020600482015260016024820152603560f81b6044820152606490fd5b6002915014386147ec565b60028054146148345760028055565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b1561488057565b60405162461bcd60e51b81526020600482015260016024820152603960f81b6044820152606490fd5b8051156148b65760200190565b634e487b7160e01b600052603260045260246000fd5b80518210156148b65760209160051b010190565b91908110156148b65760051b0190565b356001600160a01b03811681036102855790565b919082039182116106d357565b818102929181159184041417156106d357565b1561492b57565b60405162461bcd60e51b8152602060048201526002602482015261064760f31b6044820152606490fd5b600f548110156148b657600f6000527f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac8020190600090565b600d548110156148b657600d6000527fd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb50190600090565b80548210156148b65760005260206000200190600090565b6000818152600e6020526040812054614a6e57600d54600160401b811015614a5a576001810180600d55811015614a465790826040927fd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb50155600d54928152600e6020522055600190565b634e487b7160e01b82526032600452602482fd5b634e487b7160e01b82526041600452602482fd5b905090565b600081815260106020526040812054614a6e57600f54600160401b811015614a5a579082614ac3614aac84600160409601600f55614955565b819391549060031b91821b91600019901b19161790565b9055600f5492815260106020522055600190565b6000818152600c6020526040812054614a6e57600b54600160401b811015614a5a576001810180600b55811015614a465790826040927f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db90155600b54928152600c6020522055600190565b6000818152601060205260408120549091908015614c275760001990808201818111614c1357600f5490838201918211614bff57808203614bcb575b505050600f548015614bb757810190614b9682614955565b909182549160031b1b19169055600f55815260106020526040812055600190565b634e487b7160e01b84526031600452602484fd5b614be9614bda614aac93614955565b90549060031b1c928392614955565b9055845260106020526040842055388080614b7e565b634e487b7160e01b86526011600452602486fd5b634e487b7160e01b85526011600452602485fd5b505090565b6000818152600e60205260408120549091908015614c275760001990808201818111614c1357600d5490838201918211614bff57808203614ca1575b505050600d548015614bb757810190614c808261498c565b909182549160031b1b19169055600d558152600e6020526040812055600190565b614cbf614cb0614aac9361498c565b90549060031b1c92839261498c565b90558452600e6020526040842055388080614c68565b15614cdc57565b60405162461bcd60e51b8152602060048201526002602482015261068760f31b6044820152606490fd5b15614d0d57565b60405162461bcd60e51b8152602060048201526002602482015261191960f11b6044820152606490fd5b90815480825260208092019260005281600020916000905b828210614d5d575050505090565b835485529384019360019384019390910190614d4f565b15614d7b57565b60405162461bcd60e51b81526020600482015260026024820152610ccd60f21b6044820152606490fd5b6040516323b872dd60e01b60208201526001600160a01b03928316602482015292909116604483015260648083019390935291815261407491614de782613dd5565b60018060a01b031690614e46604051614dff81613e0b565b6020938482527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564858301526000808587829751910182855af1614e40614ee3565b91614f22565b805191821591848315614eb8575b505050905015614e615750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b919381809450010312614edf57820151908115158203614edc575080388084614e54565b80fd5b5080fd5b3d15614f1d573d906001600160401b038211612ace5760405191614f11601f8201601f191660200184613e41565b82523d6000602084013e565b606090565b91929015614f845750815115614f36575090565b3b15614f3f5790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b825190915015614f975750805190602001fd5b6040519062461bcd60e51b82528160208060048301528251908160248401526000935b828510614fdd575050604492506000838284010152601f80199101168101030190fd5b8481018201518686016044015293810193859350614fba565b9061500082613e62565b61500d6040519182613e41565b828152809261501e601f1991613e62565b0190602036910137565b805490600160401b821015612ace578161504a91600161506c940181556149c3565b815491936001600160a01b0360039290921b82811b19909316911690911b1790565b9055565b811561507a570490565b634e487b7160e01b600052601260045260246000fd5b60ff16604d81116106d357600a0a90565b90816020910312610285575160ff811681036102855790565b60405163313ce56760e01b815290602090829060049082906001600160a01b03165afa908115611a53576000916150ef575090565b613f98915060203d8111615110575b6151088183613e41565b8101906150a1565b503d6150fe565b92613f98949261514192855260018060a01b03166020850152608060408501526080840190613ced565b916060818403910152613ef3565b90815480825260208092019260005281600020916000905b828210615175575050505090565b83546001600160a01b031685529384019360019384019390910190615167565b805480156151c25760001901906151ac82826149c3565b81549060018060a01b039060031b1b1916905555565b634e487b7160e01b600052603160045260246000fd5b9491936120b66152029461131860c097611334959b9a9b8a5260e060208b015260e08a0190613ced565b600260a08401526001600160a01b03909416910152565b9081519060005b82811061522e575050905090565b6001600160a01b038061524183876148cc565b5116908316146152595761525490613ff3565b615220565b9250505090565b60018060a01b03811691615290600091848352600c60205261528760408420541515614cd5565b6130e48461543b565b906152a9841592836000146153aa576130f86012615090565b9381526015602052604081209384548290915b82821061534f57505080156153475760001981019081116153335760026152e860ff92600188016149c3565b90549060031b1c9501541661532d57613f98936130f89261531b9250600014615321576153156012615090565b90614911565b9161543b565b6153156132df856150ba565b50505090565b634e487b7160e01b82526011600452602482fd5b509250505090565b9091600190615364818518831c828616614032565b9183615370848b6149c3565b90549060031b1c11600014615389575050915b906152bc565b90935081018091111561538357634e487b7160e01b84526011600452602484fd5b6130f86132df866150ba565b51906001600160501b038216820361028557565b908160a0910312610285576153de816153b6565b91602082015191604081015191613f986080606084015193016153b6565b604d81116106d357600a0a90565b906005820291808305600514901517156106d357565b811561507a57600160ff1b81146000198314166106d3570590565b6001600160a01b039081166000818152600e602090815260408083205491949293911561546f5750505050506305f5e10090565b60a09460119384825280838720541693835197888096633fabe5a360e21b825260049788915afa978815615652578798615629575b5086976000881390816155fd575b50156155d557918091859360005286825284600020541684519384809263313ce56760e01b82525afa9283156155cb57509060ff92916000926155ae575b5050169260088411156155315750600719830192831161551f57505090615519613f98926153fc565b90615420565b634e487b7160e01b6000525260246000fd5b9260088110615542575b5050505090565b90919293506008036008811161559b5761555b906153fc565b838102939060008212600160ff1b82141661558757818505149015171561551f5750503880808061553b565b5050634e487b7160e01b6000525260246000fd5b50634e487b7160e01b6000525260246000fd5b6155c49250803d10615110576151088183613e41565b38806154f0565b513d6000823e3d90fd5b835162461bcd60e51b81528086018490526002602482015261343360f01b6044820152606490fd5b426201517f19810192508211615615571015386154b2565b8787634e487b7160e01b6000525260246000fd5b90975061564591965060a03d8111611a4c57611a388183613e41565b50979250509596386154a4565b84513d89823e3d90fd5b6000908152601260205260408120600760ff60058301541691015491600882101590816156c757600483149182156156ec575b82156156db575b82156156b4575b5050614a6e575042116156af57600290565b600390565b9091506156c7575060078114388061569d565b634e487b7160e01b81526021600452602490fd5b8092506156c7576006831491615696565b506005831491508061568f565b8015159081615735575b501561570b57565b60405162461bcd60e51b8152602060048201526002602482015261066760f31b6044820152606490fd5b9050600954101538615703565b61574b9061565c565b60088110156115275760030361575d57565b60405162461bcd60e51b8152602060048201526002602482015261333960f01b6044820152606490fd5b6157909061565c565b600881101561152757600381149081156157d7575b50156157ad57565b60405162461bcd60e51b8152602060048201526002602482015261034360f41b6044820152606490fd5b6002915014386157a5565b6004546001600160a01b031633036157f657565b60405162461bcd60e51b8152602060048201526002602482015261343160f01b6044820152606490fd5b919091600083820193841291129080158216911516176106d35756fe7620380c554abbc2f2c8551155ba9b12b3fcd40700f9a718a43678a914753048a933c5f2ff3ab65b8004e747a48707dab64e66636a6ff5247f0431bc16881774eb0cc7968044d5a32d1b2d53427cbc63a081ddf73f5afb1637a4dfbdea2f7db9a26469706673582212200bffa7d031bdce80d5b0d67611d95d4f1055cd3612541aae63c4d02a51ef818d64736f6c63430008130033
Verified Source Code Full Match
Compiler: v0.8.19+commit.7dd6d404
EVM: paris
Optimization: Yes (200 runs)
P2PSports.sol 1664 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import "@openzeppelin/contracts/access/Ownable2Step.sol";
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import "@solarity/solidity-lib/libs/arrays/SetHelper.sol";
import "@solarity/solidity-lib/libs/arrays/ArrayHelper.sol";
import "@solarity/solidity-lib/libs/utils/TypeCaster.sol";
import "@solarity/solidity-lib/libs/decimals/DecimalsConverter.sol";
import "./interfaces/IP2PSports.sol";
import "@solarity/solidity-lib/utils/Globals.sol";
/// @title P2PSports: A Peer-to-Peer Sports Betting Smart Contract
/** @notice This contract allows users to create and join sports betting challenges, bet on outcomes,
* and withdraw winnings in a decentralized manner. It supports betting with STMX token and other ERC20 tokens, along with ETH
* and uses Chainlink for price feeds to calculate admin shares.
* @dev The contract uses OpenZeppelin's Ownable and ReentrancyGuard for access control and reentrancy protection,
* and utilizes libraries from solidity-lib for array and decimal manipulations.
*
* ERROR CODES: In order to reduce the size of the Smart Contract, we have defined the short codes instead of the complete
* error messages in the revert/require statements. The messages corresponding to the error codes can be seen in the following document.
* https://duelnow.notion.site/Smart-Contract-Error-Codes-ca7427520ce04ca293d3e21fb1e21583
*/
contract P2PSports is IP2PSports, Ownable2Step, ReentrancyGuard {
using SafeERC20 for IERC20;
using EnumerableSet for EnumerableSet.AddressSet;
using EnumerableSet for EnumerableSet.UintSet;
using SetHelper for EnumerableSet.AddressSet;
using DecimalsConverter for *;
using ArrayHelper for uint256[];
using TypeCaster for *;
/// @notice merkle tree root node
bytes32 private merkleRoot;
/// @notice Backend server address to resolve, cancel challenges or some additional control.
address public backend;
/// @notice Token address for the STMX token, used as one of the betting currencies
// Configuration / Validation parameters for different betting logics
uint256 public maxChallengersEachSide;
uint256 public maxChallengersForPickem;
uint256 public minUSDBetAmount;
bool public applyMembershipValues;
/// @notice ChallengeId of the last challenge created
uint256 public latestChallengeId;
/// @notice Flag to allow or restrict creations or joining challenges
bool public bettingAllowed;
uint256 public constant maxAdminShareInUsd = 1000 * 10 ** 8;
uint256 public constant maxAdminShareSTMX = 100000 * 10 ** 18;
uint256 public constant maxForMinUSDBetAmount = 100 * 10 ** 8;
uint256 public constant maxChallengesToResolve = 10;
uint256 public constant maxWinnersGroupChallenge = 10;
uint256 public constant awaitingTimeForPublicCancel = 172800; //48 hours
uint256 public constant defaultOracleDecimals = 8;
uint256 public constant maxAdminShareThresholds = 20;
uint256 public constant priceFeedErrorMargin = 5;
// Internal storage for tokens, price feeds, challenges, and bets
EnumerableSet.AddressSet internal _allTokens;
EnumerableSet.AddressSet internal _oraclessTokens;
EnumerableSet.AddressSet internal _allowedTokens;
mapping(address => AggregatorV3Interface) internal _priceFeeds;
mapping(uint256 => Challenge) internal _challenges;
mapping(address => mapping(uint256 => UserBet)) internal _userChallengeBets;
mapping(address => mapping(address => uint256)) internal _withdrawables;
mapping(address => AdminShareRule) internal _adminShareRules;
mapping(address => uint256) internal _oraclessTokensMinBetAmount;
/// @dev Ensures the function is called only by the backend address
modifier onlyBackend() {
_onlyBackend();
_;
}
/// @dev Ensures the function is called only by the backend address or owner address
modifier onlyBackendOrOwner() {
_onlyBackendOrOwner();
_;
}
/// @notice Initializes the contract with provided addresses and tokens
/** @dev Sets initial configuration for the contract and allows specified tokens.
* @param backend_ Address of the backend server for challenge resolution and control
*/
constructor(address backend_) {
require(backend_ != address(0), "1");
// Contract setup and initial token allowance
backend = backend_;
maxChallengersEachSide = 50;
maxChallengersForPickem = 50;
bettingAllowed = true;
minUSDBetAmount = 10 * 10 ** 8;
}
receive() external payable {}
/// @notice Creates a new challenge for betting
/** @dev Emits a `ChallengeCreated` event and calls `joinChallenge` for the challenge creator.
* @param token Address of the token used for betting (zero address for native currency)
* @param amountFromWallet Amount to be bet from the creator's wallet
* @param amountFromWithdrawables Amount to be bet from the creator's withdrawable balance
* @param decision The side of the bet the creator is taking
* @param challengeType The type of challenge (Individual or Group)
* @param startTime Start time of the challenge
* @param endTime End time of the challenge
* @param membershipLevel user membership level
* @param feePercentage percentage amount reduced from admin share
* @param referrer referrer address
* @param referralCommision referral will get the comission from admin share
* @param proof leaf nood proof
*/
function createChallenge(
address token,
uint256 amountFromWallet,
uint256 amountFromWithdrawables,
uint8 decision,
ChallengeType challengeType,
uint256 startTime,
uint256 endTime,
uint8 membershipLevel,
uint256 feePercentage,
address referrer,
uint256 referralCommision,
bytes32[] memory proof
) external payable {
uint256 challengeId = ++latestChallengeId;
require(startTime <= block.timestamp && endTime > block.timestamp, "2");
require(_allowedTokens.contains(token), "3");
Challenge storage _challenge = _challenges[challengeId];
_challenge.token = token;
_challenge.status = ChallengeStatus.Betting;
_challenge.challengeType = challengeType;
_challenge.startTime = startTime;
_challenge.endTime = endTime;
emit ChallengeCreated(
challengeId,
token,
msg.sender,
amountFromWallet + amountFromWithdrawables
);
joinChallenge(
challengeId,
amountFromWallet,
amountFromWithdrawables,
decision,
membershipLevel,
feePercentage,
referrer,
referralCommision,
proof
);
}
/// @notice Allows users to join an existing challenge with their bet
/** @dev Emits a `ChallengeJoined` event if the join is successful.
* @param challengeId ID of the challenge to join
* @param amountFromWallet Amount to be bet from the user's wallet
* @param amountFromWithdrawables Amount to be bet from the user's withdrawable balance
* @param decision The side of the bet the user is taking
* @param membershipLevel user membership level
* @param feePercentage percentage amount reduced from admin share
* @param referrer referrer address
* @param referralCommision referral will get the comission from admin share
* @param proof leaf nood proof
*/
function joinChallenge(
uint256 challengeId,
uint256 amountFromWallet,
uint256 amountFromWithdrawables,
uint8 decision,
uint8 membershipLevel,
uint256 feePercentage,
address referrer,
uint256 referralCommision,
bytes32[] memory proof
) public payable {
Challenge memory challengeDetails = _challenges[challengeId];
_assertChallengeExistence(challengeId);
if (challengeDetails.challengeType == ChallengeType.Group) {
require(decision == 1, "4");
} else {
require(decision == 1 || decision == 2, "5");
}
require(_userChallengeBets[msg.sender][challengeId].decision == 0, "6");
_joinChallenge(
challengeId,
amountFromWallet,
amountFromWithdrawables,
decision,
membershipLevel,
feePercentage,
referrer,
referralCommision,
proof
);
}
/// @notice Allows users to increase the bet amount
/** @dev Emits a `BetAmountIncreased` event if the join is successful.
* @param challengeId ID of the challenge for which user wants to increase the bet amount
* @param amountFromWallet Amount to be bet from the user's wallet
* @param amountFromWithdrawables Amount to be bet from the user's withdrawable balance
* @param membershipLevel user membership level
* @param feePercentage percentage amount reduced from admin share
* @param referrer referrer address
* @param referralCommision referral will get the comission from admin share
* @param proof leaf nood proof
*/
function increaseBetAmount(
uint256 challengeId,
uint256 amountFromWallet,
uint256 amountFromWithdrawables,
uint8 membershipLevel,
uint256 feePercentage,
address referrer,
uint256 referralCommision,
bytes32[] memory proof
) public payable {
_assertChallengeExistence(challengeId);
UserBet storage betDetails = _userChallengeBets[msg.sender][challengeId];
require(betDetails.decision != 0, "7");
Challenge storage challengeDetails = _challenges[challengeId];
(
uint256 amount,
uint256 adminShare,
uint256 referralCommisionAmount
) = _calculateChallengeAmounts(
challengeDetails.token,
amountFromWallet,
amountFromWithdrawables,
challengeId,
membershipLevel,
feePercentage,
referrer,
referralCommision,
proof,
false
);
betDetails.amount += amount;
betDetails.adminShare += adminShare;
betDetails.referralCommision += referralCommisionAmount;
if (betDetails.decision == 1) {
challengeDetails.amountFor += amount;
} else {
challengeDetails.amountAgainst += amount;
}
emit BetAmountIncreased(
challengeId,
betDetails.amount,
amount,
msg.sender,
challengeDetails.token
);
}
/// @notice Checks if a challenge with the given ID exists
/** @dev A challenge is considered to exist if its ID is greater than 0 and less than or equal to the latest challenge ID.
* @param challengeId The ID of the challenge to check.
* @return bool Returns true if the challenge exists, false otherwise.
*/
function challengeExists(uint256 challengeId) public view returns (bool) {
return challengeId > 0 && challengeId <= latestChallengeId;
}
/// @notice Owner can update the root node of merkle
/** @dev This function will allow the owner to update the root node of merkle tree
* @param _root root node of merkle tree
*/
function updateRoot(bytes32 _root) public onlyOwner {
merkleRoot = _root;
emit MerkleRootUpdated(_root, msg.sender);
}
/// @notice Withdraws available tokens for the sender
/** @dev This function allows users to withdraw their available tokens from the contract. It uses the
* nonReentrant modifier from OpenZeppelin to prevent reentrancy attacks. A `UserWithdrawn` event is
* emitted upon a successful withdrawal.
* @param token The address of the token to be withdrawn. Use the zero address for the native currency.
*
* Requirements:
* - The sender must have a positive withdrawable balance for the specified token.
* Emits a {UserWithdrawn} event indicating the token, amount, and the user who performed the withdrawal.
*/
function withdraw(address token) external nonReentrant {
uint256 amount = _withdrawables[msg.sender][token];
require(amount > 0, "8");
delete _withdrawables[msg.sender][token];
_withdraw(token, msg.sender, amount);
emit UserWithdrawn(token, amount, msg.sender);
}
/// @notice Resolves multiple challenges with their final outcomes
/** @dev This function is called by the backend to resolve challenges that have reached their end time
* and are in the awaiting status. It updates the status of each challenge based on its final outcome.
* Only challenges of type `Individual` can be resolved using this function. A `ChallengeResolved` event is
* emitted for each challenge that is resolved. This function uses the `onlyBackend` modifier to ensure
* that only authorized backend addresses can call it, and `nonReentrant` to prevent reentrancy attacks.
* @param challengeIds Array of IDs of the challenges to be resolved.
* @param finalOutcomes Array of final outcomes for each challenge, where outcomes are defined as follows:
* - 1: Side A wins,
* - 2: Side B wins,
* - 3: Draw.
*
* Requirements:
* - The lengths of `challengeIds` and `finalOutcomes` must be the same and not exceed `maxChallengesToResolve`.
* - Each challenge must exist, be in the `Awaiting` status, and be of type `Individual`.
* - Each `finalOutcome` must be within the range [1,3].
*/
function resolveChallenge(
uint256[] memory challengeIds,
uint8[] memory finalOutcomes
) external onlyBackend nonReentrant {
uint256 challengeIdsLength = challengeIds.length;
require(
challengeIdsLength <= maxChallengesToResolve &&
challengeIdsLength == finalOutcomes.length,
"9"
);
for (uint256 i = 0; i < challengeIdsLength; ++i) {
uint256 challengeId = challengeIds[i];
Challenge storage challengeDetails = _challenges[challengeId];
require(challengeDetails.challengeType == ChallengeType.Individual, "10");
uint8 finalOutcome = finalOutcomes[i];
require(finalOutcome > 0 && finalOutcome < 4, "11");
_assertChallengeExistence(challengeId);
_assertResolveableStatus(challengeId);
challengeDetails.status = ChallengeStatus(finalOutcome + 4);
emit ChallengeResolved(challengeId, finalOutcome);
if (finalOutcome == 3) {
_cancelBets(challengeId, 0);
} else {
_calculateChallenge(challengeId, finalOutcome);
}
}
}
/// @notice Cancels a user's participation in a challenge
/** @dev This function allows the backend to cancel a user's participation in a challenge, refunding their bet.
* It can only be called by the backend and is protected against reentrancy attacks. The function checks if the
* challenge exists and ensures that the challenge is either in the `Awaiting` or `Betting` status, implying that
* it has not been resolved yet. Additionally, it verifies that the user has indeed placed a bet on the challenge.
* After these checks, it calls an internal function `_cancelParticipation` to handle the logic for cancelling the
* user's participation and processing the refund.
* @param user The address of the user whose participation is to be cancelled.
* @param challengeId The ID of the challenge from which the user's participation is to be cancelled.
*
* Requirements:
* - The challenge must exist and be in a state where participation can be cancelled (`Awaiting` or `Betting`).
* - The user must have participated in the challenge.
* Uses the `onlyBackend` modifier to ensure only the backend can invoke this function, and `nonReentrant` for security.
*/
function cancelParticipation(
address user,
uint256 challengeId,
uint8 cancelType
) external onlyBackend nonReentrant {
_assertChallengeExistence(challengeId);
_assertCancelableStatus(challengeId);
require(_userChallengeBets[user][challengeId].decision != 0, "12");
_cancelParticipation(user, challengeId, cancelType);
}
/// @notice Resolves a group challenge by determining winners and distributing profits
/** @dev This function is used for resolving group challenges specifically, where multiple participants can win.
* It can only be executed by the backend and is protected against reentrancy. The function ensures that the
* challenge exists, is currently awaiting resolution, and is of the `Group` challenge type. It then validates
* that the lengths of the winners and profits arrays match and do not exceed the maximum number of winners allowed.
* Each winner's address must have participated in the challenge, and winners must be unique. The total of the profits
* percentages must equal 100. Once validated, the challenge status is updated, and profits are calculated and
* distributed to the winners based on the provided profits percentages.
* @param challengeId The ID of the group challenge to resolve.
* @param winners An array of addresses of the winners of the challenge.
* @param profits An array of profit percentages corresponding to each winner, summing to 100.
*
* Requirements:
* - The challenge must exist, be in the `Awaiting` status, and be of the `Group` type.
* - The `winners` and `profits` arrays must have the same length and comply with the maximum winners limit.
* - The sum of the `profits` percentages must equal 100.
* Emits a {ChallengeResolved} event with the challenge ID and a hardcoded outcome of `5`, indicating group resolution.
*/
function resolveGroupChallenge(
uint256 challengeId,
address[] calldata winners,
uint256[] calldata profits
) external onlyBackend nonReentrant {
_assertChallengeExistence(challengeId);
_assertResolveableStatus(challengeId);
Challenge storage challengeDetails = _challenges[challengeId];
require(challengeDetails.challengeType == ChallengeType.Group, "13");
uint256 winnersLength = winners.length;
require(
winnersLength == profits.length && winnersLength <= maxWinnersGroupChallenge,
"14"
);
uint256 totalProfit = 0;
for (uint256 i = 0; i < winnersLength; ++i) {
totalProfit += profits[i];
if (i > 0) {
require(winners[i] > winners[i - 1], "16");
}
require(_userChallengeBets[winners[i]][challengeId].decision != 0, "15");
}
require(totalProfit == (100 * DECIMAL), "17");
challengeDetails.status = ChallengeStatus.ResolvedFor;
emit ChallengeResolved(challengeId, 5);
_calculateGroupChallenge(challengeId, winners, profits);
}
/// @notice Cancels a challenge and refunds all participants
/** @dev This function allows the backend to cancel a challenge if it's either awaiting resolution or still open for betting.
* It ensures that the challenge exists and is in a cancelable state (either `Awaiting` or `Betting`). Upon cancellation,
* the challenge's status is updated to `Canceled`, and all bets placed on the challenge are refunded to the participants.
* This function is protected by the `onlyBackend` modifier to restrict access to the backend address, and `nonReentrant`
* to prevent reentrancy attacks.
* @param challengeId The ID of the challenge to be cancelled.
* @param cancelType 0-Return bet amount without admin shares 1-Return bet amount with admin shares.
*
* Requirements:
* - The challenge must exist and be in a state that allows cancellation (`Awaiting` or `Betting`).
* Emits a {ChallengeCanceled} event upon successful cancellation, indicating which challenge was cancelled.
*/
function cancelChallenge(
uint256 challengeId,
uint8 cancelType
) external onlyBackendOrOwner nonReentrant {
_assertChallengeExistence(challengeId);
_assertCancelableStatus(challengeId);
Challenge storage challengeDetails = _challenges[challengeId];
if (msg.sender == owner()) {
require(
(challengeDetails.endTime + awaitingTimeForPublicCancel) < block.timestamp,
"18"
);
}
challengeDetails.status = ChallengeStatus.Canceled;
emit ChallengeCanceled(challengeId);
_cancelBets(challengeId, cancelType);
}
/// @notice Toggles the ability for users to place bets on challenges
/** @dev This function allows the contract owner to enable or disable betting across the platform.
* It's a straightforward toggle that sets the `bettingAllowed` state variable based on the input.
* Access to this function is restricted to the contract owner through the `onlyOwner` modifier from
* OpenZeppelin's Ownable contract, ensuring that only the owner can change the betting policy.
* @param value_ A boolean indicating whether betting should be allowed (`true`) or not (`false`).
*/
function allowBetting(bool value_) external onlyOwner {
bettingAllowed = value_;
emit BettingAllowed(value_, msg.sender);
}
/// @notice Toggles the ability for membership discount and referral comisions
/** @dev This function will allow the owner to toggle the apply membership values
* @param value_ true to apply membership values and false for disable membership values
*/
function updateApplyMembershipValues(bool value_) external onlyOwner {
applyMembershipValues = value_;
emit MembershipApplied(value_, msg.sender);
}
/// @notice Updates the minimum USD betting amount.
/// @dev Can only be called by the contract owner.
/// @param value_ The new minimum betting amount in USD.
function changeMinUSDBettingAmount(uint256 value_) external onlyOwner {
require(value_ >= minUSDBetAmount && value_ <= maxForMinUSDBetAmount, "28");
minUSDBetAmount = value_;
emit MinUSDBettingAmountUpdated(value_, msg.sender);
}
/// @notice Updates the address of the backend responsible for challenge resolutions and administrative actions
/** @dev This function allows the contract owner to change the backend address to a new one.
* Ensures the new backend address is not the zero address to prevent rendering the contract unusable.
* The function is protected by the `onlyOwner` modifier, ensuring that only the contract owner has the authority
* to update the backend address. This is crucial for maintaining the integrity and security of the contract's
* administrative functions.
* @param backend_ The new address to be set as the backend. It must be a non-zero address.
*
* Requirements:
* - The new backend address cannot be the zero address, ensuring that the function call has meaningful intent.
*/
function changeBackend(address backend_) external onlyOwner {
require(backend_ != address(0), "1");
backend = backend_;
emit BackendChanged(backend_, msg.sender);
}
/// @notice Allows a batch of tokens to be used for betting, with optional price feeds for valuation
/** @dev This function permits the contract owner to add tokens to the list of those allowed for betting.
* It also associates Chainlink price feeds with tokens, enabling the conversion of bets to a common value basis for calculations.
* Tokens without a specified price feed (address(0)) are considered to have fixed or known values and are added to a separate list.
* The function ensures that each token in the input array has a corresponding price feed address (which can be the zero address).
* The `onlyOwner` modifier restricts this function's execution to the contract's owner, safeguarding against unauthorized token addition.
* @param tokens An array of token addresses to be allowed for betting.
* @param priceFeeds An array of Chainlink price feed addresses corresponding to the tokens. Use address(0) for tokens without a need for price feeds.
* @param minBetAmounts An array of amount corresponding to every token being allowed, the value for oracless tokens will be considers only in this method.
* Requirements:
* - The lengths of the `tokens` and `priceFeeds` arrays must match to ensure each token has a corresponding price feed address.
*/
function allowTokens(
address[] memory tokens,
address[] memory priceFeeds,
uint256[] memory minBetAmounts
) public onlyOwner {
uint256 tokensLength = tokens.length;
uint256 priceFeedLength = priceFeeds.length;
uint256 minBetAmountsLength = minBetAmounts.length;
require(tokensLength == priceFeedLength && tokensLength == minBetAmountsLength, "9");
for (uint256 i = 0; i < tokensLength; ++i) {
require(!_allowedTokens.contains(tokens[i]), "46");
if (priceFeeds[i] == address(0)) {
require(minBetAmounts[i] > 0, "20");
_oraclessTokensMinBetAmount[tokens[i]] = minBetAmounts[i];
_oraclessTokens.add(tokens[i]);
} else {
isValidPriceFeed(priceFeeds[i], priceFeedErrorMargin);
_priceFeeds[tokens[i]] = AggregatorV3Interface(priceFeeds[i]);
}
}
_allowedTokens.add(tokens);
_allTokens.add(tokens);
emit TokenAllowed(tokens, priceFeeds, minBetAmounts, msg.sender);
}
/// @notice Removes a batch of tokens from being allowed for betting and deletes associated price feeds
/** @dev This function enables the contract owner to restrict certain tokens from being used in betting activities.
* It involves removing tokens from the list of allowed tokens, potentially removing them from the list of tokens
* without a Chainlink price feed (oracless tokens), and deleting their associated price feeds if any were set.
* This is a crucial administrative function for managing the tokens that can be used on the platform, allowing
* for adjustments based on compliance, liquidity, or other operational considerations.
* Execution is restricted to the contract's owner through the `onlyOwner` modifier, ensuring that token restrictions
* can only be imposed by authorized parties.
* @param tokens An array of token addresses that are to be restricted from use in betting.
*/
function restrictTokens(address[] memory tokens) external onlyOwner {
uint256 tokensLength = tokens.length;
for (uint256 i = 0; i < tokensLength; ++i) {
require(_allowedTokens.contains(tokens[i]), "47");
delete _priceFeeds[tokens[i]];
delete _oraclessTokensMinBetAmount[tokens[i]];
}
_allowedTokens.remove(tokens);
_oraclessTokens.remove(tokens);
emit TokenRestricted(tokens, msg.sender);
}
/// @notice Sets the rules for administrative shares on betting winnings based on thresholds
/** @dev Allows the contract owner to define how administrative shares (a portion of betting winnings) are calculated.
* This can be configured differently for the STMX token versus other tokens, as indicated by the `isSTMX` flag.
* Each entry in the `thresholds` and `sharesInUSD` arrays defines a tier: if the winnings fall into a certain threshold,
* the corresponding percentage is applied as the administrative share. The function enforces ascending order for thresholds
* and ensures that the share in USD do not exceed a maximum limit. This setup allows for flexible configuration
* of administrative fees based on the amount won.
* Access is restricted to the contract owner through the `onlyOwner` modifier, ensuring that only they can set these rules.
* @param thresholds An array of threshold values, each representing the lower bound of a winnings bracket.
* @param sharesInUSD An array of sharesInUSD corresponding to each threshold, defining the admin share for that bracket.
* @param token Token address.
* @param isSTMX A boolean flag indicating whether these rules apply to the STMX token (`true`) or other tokens (`false`).
*
* Requirements:
* - The `thresholds` and `sharesInUSD` arrays must be of equal length and not empty, ensuring each threshold has a corresponding percentage.
* - Thresholds must be in ascending order, and all sharesInUSD must not exceed the predefined maximum admin share percentage.
*/
function setAdminShareRules(
uint256[] memory thresholds,
uint256[] memory sharesInUSD,
address token,
bool isSTMX
) external onlyOwner {
require(_allTokens.contains(token), "48");
uint256 thresholdsLength = thresholds.length;
uint256 sharesInUSDLength = sharesInUSD.length;
require(
thresholdsLength > 0 &&
thresholdsLength == sharesInUSDLength &&
thresholdsLength <= maxAdminShareThresholds,
"9"
);
uint256 maxAdminShare = maxAdminShareInUsd;
for (uint256 i = 0; i < thresholdsLength - 1; ++i) {
require(thresholds[i] <= thresholds[i + 1], "21");
if (isSTMX) {
maxAdminShare = maxAdminShareSTMX;
}
require(sharesInUSD[i] <= maxAdminShare, "22");
}
if (isSTMX) {
maxAdminShare = maxAdminShareSTMX;
}
require(sharesInUSD[sharesInUSDLength - 1] <= maxAdminShare, "22");
_adminShareRules[token] = AdminShareRule({
sharesInUSD: sharesInUSD,
thresholds: thresholds,
isSTMX: isSTMX
});
emit AdminShareRulesUpdated(_adminShareRules[token], msg.sender);
}
/// @notice Update the maximum challenger limits
/**
* Access is restricted to the contract owner through the `onlyOwner` modifier, ensuring that only they can set these rules.
* @param _maxChallengersEachSide maximun limit of challengers can join in each side.
* @param _maxChallengersForPickem maximun limit of challengers can join for pickem.
*/
function updateMaxChallengers(
uint256 _maxChallengersEachSide,
uint256 _maxChallengersForPickem
) external onlyOwner {
require(
_maxChallengersForPickem > 0 &&
_maxChallengersForPickem <= 50 &&
_maxChallengersEachSide > 0 &&
_maxChallengersEachSide <= 50,
"23"
);
maxChallengersForPickem = _maxChallengersForPickem;
maxChallengersEachSide = _maxChallengersEachSide;
emit MaxChallengersUpdated(_maxChallengersEachSide, _maxChallengersForPickem, msg.sender);
}
/// @notice Owner is able to deposit tokens in SC under the owner's withdrawbales, to use the owner withdrawables in user's bets
/**
* Access is restricted to the contract owner through the `onlyOwner` modifier, ensuring that only owner can deposit amount to SC.
* @param _amount amount of tokens.
* @param _token token address.
*/
function debitInSC(uint256 _amount, address _token) external payable onlyOwner {
require(_allowedTokens.contains(_token), "3");
require(_amount > 0, "28");
if (_token == address(0)) {
require(msg.value == _amount, "29");
} else {
require(msg.value == 0, "34");
IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);
}
_withdrawables[msg.sender][_token] += _amount;
emit DebitedInSC(_withdrawables[msg.sender][_token], msg.sender);
}
/// @notice Retrieves the administrative share rules for either the STMX token or other tokens
/** @dev This function provides external access to the administrative share rules that have been set up for either
* the STMX token (if `isSTMX` is true) or for other tokens (if `isSTMX` is false). These rules define the thresholds
* and corresponding percentages that determine how administrative shares are calculated from betting winnings.
* The function returns two arrays: one for the thresholds and one for the percentages, which together outline the
* structure of admin shares based on the amount of winnings.
* @param token A boolean flag indicating whether to retrieve the rules for the STMX token (`true`) or other tokens (`false`).
* @return thresholds An array of uint256 representing the winnings thresholds for admin shares calculation.
* @return sharesInUSD An array of uint256 representing the admin share in USD for each corresponding threshold.
*/
function getAdminShareRules(
address token
)
external
view
returns (uint256[] memory thresholds, uint256[] memory sharesInUSD, bool isSTMX)
{
AdminShareRule storage rule = _adminShareRules[token];
return (rule.thresholds, rule.sharesInUSD, rule.isSTMX);
}
/// @notice Retrieves the list of tokens currently allowed for betting
/** @dev This function provides external visibility into which tokens are currently permitted for use in betting within the platform.
* It leverages the EnumerableSet library from OpenZeppelin to handle the dynamic array of addresses representing the allowed tokens.
* This is particularly useful for interfaces or external contracts that need to verify or display the tokens users can bet with.
* @return An array of addresses, each representing a token that is allowed for betting.
*/
function getAllowedTokens() external view returns (address[] memory) {
return _allowedTokens.values();
}
/// @notice Fetches detailed information about a specific challenge by its ID
/** @dev This function provides access to the details of a given challenge, including its current status, which is
* dynamically determined based on the challenge's timing and resolution state. It's essential for external callers
* to be able to retrieve comprehensive data on a challenge, such as its participants, status, and betting amounts,
* to properly interact with or display information about the challenge. The function checks that the requested
* challenge exists before attempting to access its details.
* @param challengeId The unique identifier of the challenge for which details are requested.
* @return challengeDetails A `Challenge` struct containing all relevant data about the challenge, including an updated status.
*
* Requirements:
* - The challenge must exist, as indicated by its ID being within the range of created challenges.
*/
function getChallengeDetails(
uint256 challengeId
) external view returns (Challenge memory challengeDetails) {
_assertChallengeExistence(challengeId);
challengeDetails = _challenges[challengeId];
challengeDetails.status = _challengeStatus(challengeId);
}
/// @notice Retrieves the bet details placed by a specific user on a particular challenge
/** @dev This function allows anyone to view the details of a bet made by a user on a specific challenge,
* including the amount bet and the side the user has chosen. It's crucial for enabling users or interfaces
* to confirm the details of participation in challenges and to understand the stakes involved. This function
* directly accesses the mapping of user bets based on the user address and challenge ID, returning the
* corresponding `UserBet` struct.
* @param challengeId The ID of the challenge for which the bet details are being queried.
* @param user The address of the user whose bet details are requested.
* @return A `UserBet` struct containing the amount of the bet and the decision (side chosen) by the user for the specified challenge.
*/
function getUserBet(uint256 challengeId, address user) external view returns (UserBet memory) {
return _userChallengeBets[user][challengeId];
}
/// @notice Provides a list of tokens and corresponding amounts available for withdrawal by a specific user
/** @dev This function compiles a comprehensive view of all tokens that a user has available to withdraw,
* including winnings, refunds, or other credits due to the user. It iterates over the entire list of tokens
* recognized by the contract (not just those currently allowed for betting) to ensure that users can access
* any funds owed to them, regardless of whether a token's betting status has changed. This is essential for
* maintaining transparency and access to funds for users within the platform.
* @param user The address of the user for whom withdrawable balances are being queried.
* @return tokens An array of token addresses, representing each token that the user has a balance of.
* @return amounts An array of uint256 values, each corresponding to the balance of the token at the same index in the `tokens` array.
*/
function getUserWithdrawables(
address user
) external view returns (address[] memory tokens, uint256[] memory amounts) {
uint256 allTokensLength = _allTokens.length();
tokens = new address[](allTokensLength);
amounts = new uint256[](allTokensLength);
for (uint256 i = 0; i < allTokensLength; ++i) {
tokens[i] = _allTokens.at(i);
amounts[i] = _withdrawables[user][tokens[i]];
}
}
/// @notice verify the membership proof from merkle tree
/** @dev If merkle proof got verified it will return true otherwise false
* @param proof leaf nood proof
* @param membershipLevel user membership level
* @param feePercentage percentage amount reduced from admin share
* @param referrer referrer address
* @param referralCommision referral will get the comission from admin share
* @return bool Returns true if proof got verified, false otherwise.
*/
function verifyMembership(
bytes32[] memory proof,
uint256 membershipLevel,
uint256 feePercentage,
address referrer,
uint256 referralCommision
) internal view returns (bool) {
bytes32 leaf = keccak256(
bytes.concat(
keccak256(
abi.encode(
msg.sender,
membershipLevel,
feePercentage,
referrer,
referralCommision
)
)
)
);
require(MerkleProof.verify(proof, merkleRoot, leaf), "45");
return true;
}
/**
* @dev Allows a user to join a challenge, handling the financial transactions involved, including admin fees.
* This internal function processes a user's bet on a challenge, taking into account amounts from the user's wallet and
* withdrawable balance. It calculates and deducts an admin share based on the total bet amount and updates the challenge
* and user's records accordingly.
*
* @param challengeId The unique identifier of the challenge the user wishes to join.
* @param amountFromWallet The portion of the user's bet that will be taken from their wallet.
* @param amountFromWithdrawables The portion of the user's bet that will be taken from their withdrawable balance.
* @param decision Indicates whether the user is betting for (1) or against (2) in the challenge; for group challenges, this is ignored.
* @param membershipLevel user membership level
* @param feePercentage percentage amount reduced from admin share
* @param referrer referrer address
* @param referralCommision referral will get the comission from admin share
* @param proof leaf nood proof
*
* The function enforces several checks and conditions:
* - The total bet amount must exceed the admin share calculated for the transaction.
* - The user must have sufficient withdrawable balance if opting to use it.
* - Transfers the required amount from the user's wallet if applicable.
* - Updates the admin's withdrawable balance with the admin share.
* - Adds the user to the challenge participants and updates the challenge's total amount for or against based on the user's decision.
* - Ensures the number of participants does not exceed the maximum allowed.
* - Records the user's bet details.
*
* Emits a `ChallengeJoined` event upon successful joining of the challenge.
* Emits an `AdminShareCalculated` event to indicate the admin share calculated from the user's bet.
*
* Requirements:
* - The sum of `amountFromWallet` and `amountFromWithdrawables` must be greater than the admin share.
* - If using withdrawables, the user must have enough balance.
* - The challenge token must be transferred successfully from the user's wallet if necessary.
* - The challenge's participants count for either side must not exceed `maxChallengersEachSide`.
*
* Notes:
* - This function uses the nonReentrant modifier to prevent reentry attacks.
* - It supports participation in both individual and group challenges.
* - Admin shares are calculated and deducted from the user's total bet amount to ensure fair administration fees.
*/
function _joinChallenge(
uint256 challengeId,
uint256 amountFromWallet,
uint256 amountFromWithdrawables,
uint8 decision,
uint8 membershipLevel,
uint256 feePercentage,
address referrer,
uint256 referralCommision,
bytes32[] memory proof
) internal nonReentrant {
Challenge storage _challenge = _challenges[challengeId];
(
uint256 amount,
uint256 adminShare,
uint256 referralCommisionAmount
) = _calculateChallengeAmounts(
_challenge.token,
amountFromWallet,
amountFromWithdrawables,
challengeId,
membershipLevel,
feePercentage,
referrer,
referralCommision,
proof,
true
);
uint256 participants;
// Depending on the decision, update challenge state and user bet details
if (decision == 1 || _challenge.challengeType == ChallengeType.Group) {
_challenge.usersFor.push(msg.sender);
participants = _challenge.usersFor.length;
_challenge.amountFor += amount;
} else {
_challenge.usersAgainst.push(msg.sender);
participants = _challenge.usersAgainst.length;
_challenge.amountAgainst += amount;
}
// Ensure the number of participants does not exceed the maximum allowed per side
if (_challenge.challengeType == ChallengeType.Group) {
require(participants <= maxChallengersForPickem, "30");
} else {
require(participants <= maxChallengersEachSide, "44");
}
// Record user's bet details for the challenge
if (_challenge.challengeType == ChallengeType.Group) {
decision = 1;
}
_userChallengeBets[msg.sender][challengeId] = UserBet({
amount: amount,
decision: decision,
adminShare: adminShare,
referrer: referrer,
referralCommision: referralCommisionAmount
});
// Emit events for challenge joined and admin received shares
emit ChallengeJoined(
challengeId,
amount,
msg.sender,
_challenge.token,
amountFromWallet + amountFromWithdrawables
);
emit AdminShareCalculated(challengeId, _challenge.token, adminShare);
}
/// @notice calculate the challenge bet amount
/**
* @param challengeToken token in which user tried to bet
* @param amountFromWallet amount which will be deducted from the users wallet
* @param amountFromWithdrawables amount which will be deducted from the users withdrawables
* @param membershipLevel user membership level
* @param feePercentage percentage amount reduced from admin share
* @param referrer referrer address
* @param referralCommision referral will get the comission from admin share
* @param proof leaf nood proof
* @return amount of the bet
* @return admin share on bet amount
*/
function _calculateChallengeAmounts(
address challengeToken,
uint256 amountFromWallet,
uint256 amountFromWithdrawables,
uint256 challengeId,
uint8 membershipLevel,
uint256 feePercentage,
address referrer,
uint256 referralCommision,
bytes32[] memory proof,
bool checkMinUsdAmounts
) internal returns (uint256, uint256, uint256) {
require(bettingAllowed, "31");
require(_challengeStatus(challengeId) == ChallengeStatus.Betting, "32");
if (challengeToken == address(0)) {
require(amountFromWallet == msg.value, "33");
} else {
require(msg.value == 0, "34");
}
uint256 amount = amountFromWallet + amountFromWithdrawables;
uint256 adminShare = _calculateAdminShare(challengeToken, amount);
uint256 referralCommisionAmount;
if (applyMembershipValues) {
verifyMembership(proof, membershipLevel, feePercentage, referrer, referralCommision);
adminShare = (adminShare * (100 ** 20 - feePercentage)) / 100 ** 20;
if (referrer != address(0)) {
referralCommisionAmount = adminShare; // admin share amount with refferral commision amount
// deduct the referral commission from admin share
adminShare = (adminShare * (100 ** 20 - referralCommision)) / 100 ** 20;
referralCommisionAmount -= adminShare;
}
}
// Ensure that the total amount is greater than the admin share per challenge
require(amount > adminShare, "35");
uint256 valueAmount = (_getValue(challengeToken) * amount) /
10 ** (challengeToken == address(0) ? 18 : challengeToken.decimals());
if (_oraclessTokens.contains(challengeToken)) {
require(valueAmount >= _oraclessTokensMinBetAmount[challengeToken], "28");
} else {
require(!checkMinUsdAmounts || valueAmount >= minUSDBetAmount, "28");
}
// Deduct the amount from the withdrawables if bet amount is from withdrawables
if (amountFromWithdrawables > 0) {
require(_withdrawables[msg.sender][challengeToken] >= amountFromWithdrawables, "36");
_withdrawables[msg.sender][challengeToken] -= amountFromWithdrawables;
}
// Transfer the amount from the user's wallet to the contract
if (challengeToken != address(0)) {
IERC20(challengeToken).safeTransferFrom(msg.sender, address(this), amountFromWallet);
}
amount -= (adminShare + referralCommisionAmount);
return (amount, adminShare, referralCommisionAmount);
}
/**
* @dev Calculates the results of a challenge based on the final outcome and updates the participants' balances accordingly.
* This internal function takes the final outcome of a challenge and determines the winners and losers, redistributing the
* pooled amounts between participants based on their initial bets. It ensures that the winnings are proportionally distributed
* to the winners from the total amount bet by the losers.
*
* @param challengeId The unique identifier of the challenge to calculate results for.
* @param finalOutcome The final outcome of the challenge represented as a uint8 value. A value of `1` indicates
* that the original "for" side wins, while `2` indicates that the "against" side wins.
*
* The function performs the following steps:
* - Identifies the winning and losing sides based on `finalOutcome`.
* - Calculates the total winning amount for each winning participant based on their bet proportion.
* - Updates the `_withdrawables` mapping to reflect the winnings for each winning participant.
* - Prepares data for the losing participants but does not adjust their balances as their amounts are considered lost.
*
* Emits a `ChallengeFundsMoved` event indicating the redistribution of funds following the challenge's conclusion.
* This event provides detailed arrays of winning and losing users, alongside the amounts won or lost.
*
* Requirements:
* - The challenge identified by `challengeId` must exist within the `_challenges` mapping.
* - The `finalOutcome` must correctly reflect the challenge's outcome, with `1` for a win by the or...
// [truncated — 81741 bytes total]
IP2PSports.sol 601 lines
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/**
* @title IP2PSports
* @dev Interface for a peer-to-peer sports betting platform.
* This interface defines the basic events, enums, and structs required for creating, joining, resolving,
* and canceling challenges, as well as managing and withdrawing bets and admin shares in a decentralized sports betting platform.
*/
interface IP2PSports {
/**
* @dev Emitted when the backend address changes.
* @param backend The new backend address.
* @param by The address of the user who changed the backend address.
*/
event BackendChanged(address backend, address by);
/**
* @dev Emitted when the merkle root is updated.
* @param root The new merkle root.
* @param by The address of the user who updated the merkle root.
*/
event MerkleRootUpdated(bytes32 root, address by);
/**
* @dev Emitted when the maximum number of challengers is updated.
* @param maxChallengersEachSide The maximum number of challengers on each side.
* @param maxChallengersForPickem The maximum number of challengers for pickem.
* @param by The address of the user who updated the maximum number of challengers.
*/
event MaxChallengersUpdated(
uint256 maxChallengersEachSide,
uint256 maxChallengersForPickem,
address by
);
/**
* @dev Emitted when the minimum USD bet amount is updated.
* @param amount The new minimum USD bet amount.
* @param by The address of the user who updated the minimum USD bet amount.
*/
event MinUSDBettingAmountUpdated(uint256 amount, address by);
/**
* @dev Emitted when the membership application status is updated.
* @param value The new membership application status (true or false).
* @param by The address of the user who updated the membership application status.
*/
event MembershipApplied(bool value, address by);
/**
* @dev Emitted when betting is enabled or disabled.
* @param value The new betting status (true or false).
* @param by The address of the user who updated the betting status.
*/
event BettingAllowed(bool value, address by);
/**
* @dev Emitted when the admin share rules are updated.
* @param adminShareRules The new admin share rules.
* @param by The address of the user who updated the admin share rules.
*/
event AdminShareRulesUpdated(AdminShareRule adminShareRules, address by);
/**
* @dev Emitted when an amount is debited in SC.
* @param amount The amount to be debited.
* @param by The address of the user who initiated the debit.
*/
event DebitedInSC(uint256 amount, address by);
/**
* @dev Emitted when a token is allowed.
* @param tokens The addresses of the allowed tokens.
* @param priceFeeds The addresses of the price feeds for the tokens.
* @param minBetAmounts The minimum bet amounts for the tokens.
* @param by The address of the user who allowed the tokens.
*/
event TokenAllowed(
address[] tokens,
address[] priceFeeds,
uint256[] minBetAmounts,
address by
);
/**
* @dev Emitted when tokens are restricted.
* @param tokens The addresses of the restricted tokens.
* @param by The address of the user who restricted the tokens.
*/
event TokenRestricted(address[] tokens, address by);
/**
* @dev Emitted when a new challenge is created.
* @param challengeId Unique identifier for the challenge.
* @param token Address of the token used for betting.
* @param by Address of the user who created the challenge.
* @param inputStakedQty The original amount input by user, without any deductions
*/
event ChallengeCreated(uint256 challengeId, address token, address by, uint256 inputStakedQty);
/**
* @dev Emitted when a user joins an existing challenge.
* @param challengeId Unique identifier for the challenge.
* @param amount Amount of the token bet by the user.
* @param by Address of the user who joined the challenge.
* @param inputStakedQty The original amount input by user, without any deductions
* @param token Address of the token used for betting.
*/
event ChallengeJoined(
uint256 challengeId,
uint256 amount,
address by,
address token,
uint256 inputStakedQty
);
/**
* @dev Emitted when a user increases amount for an already joined challenge.
* @param challengeId Unique identifier for the challenge.
* @param increasedAmount Amount that is added to the previous amount of the user for the specified bet.
* @param newTotalAmount The new total amount of the user's participation in the bet.
* @param by Address of the user who increased the challenge amount.
* @param token Address of the token used for betting.
*/
event BetAmountIncreased(
uint256 challengeId,
uint256 increasedAmount,
uint256 newTotalAmount,
address by,
address token
);
/**
* @dev Emitted when a challenge is resolved.
* @param challengeId Unique identifier for the challenge.
* @param finalOutcome Final outcome of the challenge (1 for win, 2 for loss, etc.).
*/
event ChallengeResolved(uint256 challengeId, uint8 finalOutcome);
/**
* @dev Emitted when a challenge is canceled.
* @param challengeId Unique identifier for the canceled challenge.
*/
event ChallengeCanceled(uint256 challengeId);
/**
* @dev Emitted when a user cancels their participation in a challenge.
* @param user Address of the user canceling their participation.
* @param challengeId Unique identifier for the challenge.
*/
event CancelParticipation(address user, uint256 challengeId);
/**
* @dev Emitted after the resolution of a challenge, detailing the redistribution of funds.
* @param challengeId Unique identifier for the challenge.
* @param winners Array of addresses of the winning users.
* @param winnersProfit Array of profits earned by each winning user.
* @param losers Array of addresses of the losing users.
* @param losersLoss Array of amounts lost by each losing user.
*/
event ChallengeFundsMoved(
uint256 challengeId,
address[] winners,
uint256[] winnersProfit,
address[] losers,
uint256[] losersLoss,
MethodType mothodType,
address token
);
/**
* @dev Emitted when a user withdraws their winnings or funds.
* @param token Address of the token being withdrawn.
* @param amount Amount of the token being withdrawn.
* @param by Address of the user performing the withdrawal.
*/
event UserWithdrawn(address token, uint256 amount, address by);
/**
* @dev Emitted when the admin shares is calculated from challenge participation fees.
* @param challengeId Unique identifier for the challenge from which the fees were taken.
* @param token Address of the token in which the fees were paid.
* @param amount Amount of the fees received.
*/
event AdminShareCalculated(uint256 challengeId, address token, uint256 amount);
/**
* @dev Emitted when the admin receives a share from challenge participation fees.
* @param challengeId Unique identifier for the challenge from which the fees were taken.
* @param token Address of the token in which the fees were paid.
* @param amount Amount of the fees received.
*/
event AdminReceived(uint256 challengeId, address token, uint256 amount);
/**
* @dev Emitted when the referrel commission is earned by the referrer from challenge participation fees.
* @param challengeId Unique identifier for the challenge from which the fees were taken.
* @param token Address of the token in which the fees were paid.
* @param referrers addresses of the referrers.
* @param referrelCommissions Amount of the fees received.
*/
event ReferralsEarned(
uint256 challengeId,
address token,
address[] referrers,
uint256[] referrelCommissions
);
/**
* @dev Emitted when the admin withdraws their accumulated shares.
* @param token Address of the token being withdrawn.
* @param amount Amount of the token being withdrawn.
*/
event AdminWithdrawn(address token, uint256 amount);
/**
* @dev Enum for tracking the status of a challenge.
*/
enum ChallengeStatus {
None,
CanBeCreated,
Betting,
Awaiting,
Canceled,
ResolvedFor,
ResolvedAgainst,
ResolvedDraw
}
/**
* @dev Enum for distinguishing between individual and group challenges.
*/
enum ChallengeType {
Individual,
Group
}
/**
* @dev Enum for the functions in which the fund are being distributed.
*/
enum MethodType {
ResolveChallenge,
ResolveGroupChallenge,
CancelChallenge,
CancelParticipation
}
/**
* @dev Struct for storing details about a challenge.
*/
struct Challenge {
address token; // Token used for betting.
address[] usersFor; // Users betting for the outcome.
address[] usersAgainst; // Users betting against the outcome.
uint256 amountFor; // Total amount bet for the outcome.
uint256 amountAgainst; // Total amount bet against the outcome.
ChallengeStatus status; // Current status of the challenge.
ChallengeType challengeType; // Type of challenge (individual or group).
uint256 startTime; // Start time of the challenge.
uint256 endTime; // End time of the challenge.
}
/**
* @dev Struct for storing a user's bet on a challenge.
*/
struct UserBet {
uint256 amount; // Amount of the bet.
uint8 decision; // User's decision (for or against).
uint256 adminShare; //Admin's share calculated for this bet amount
address referrer;
uint256 referralCommision;
}
/**
* @dev Struct for defining admin share rules based on bet thresholds.
*/
struct AdminShareRule {
uint256[] thresholds; // Bet amount thresholds for different share percentages.
uint256[] sharesInUSD; // Admin share in USD for corresponding thresholds.
bool isSTMX; //To define if this is an STMX or some other
}
/**
* External Methods
*/
/** @dev Emits a `ChallengeCreated` event and calls `joinChallenge` for the challenge creator.
* @param token Address of the token used for betting (zero address for native currency)
* @param amountFromWallet Amount to be bet from the creator's wallet
* @param amountFromWithdrawables Amount to be bet from the creator's withdrawable balance
* @param decision The side of the bet the creator is taking
* @param challengeType The type of challenge (Individual or Group)
* @param startTime Start time of the challenge
* @param endTime End time of the challenge
* @param membershipLevel user membership level
* @param feePercentage percentage amount reduced from admin share
* @param referrer referrer address
* @param referralCommision referral will get the comission from admin share
* @param proof leaf nood proof
*/
function createChallenge(
address token,
uint256 amountFromWallet,
uint256 amountFromWithdrawables,
uint8 decision,
ChallengeType challengeType,
uint256 startTime,
uint256 endTime,
uint8 membershipLevel,
uint256 feePercentage,
address referrer,
uint256 referralCommision,
bytes32[] memory proof
) external payable;
/** @dev This function allows users to withdraw their available tokens from the contract. It uses the
* nonReentrant modifier from OpenZeppelin to prevent reentrancy attacks. A `UserWithdrawn` event is
* emitted upon a successful withdrawal.
* @param token The address of the token to be withdrawn. Use the zero address for the native currency.
*/
function withdraw(address token) external;
/** @dev This function is called by the backend to resolve challenges that have reached their end time
* and are in the awaiting status. It updates the status of each challenge based on its final outcome.
* Only challenges of type `Individual` can be resolved using this function. A `ChallengeResolved` event is
* emitted for each challenge that is resolved. This function uses the `onlyBackend` modifier to ensure
* that only authorized backend addresses can call it, and `nonReentrant` to prevent reentrancy attacks.
* @param challengeIds Array of IDs of the challenges to be resolved.
* @param finalOutcomes Array of final outcomes for each challenge, where outcomes are defined as follows:
* - 1: Side A wins,
* - 2: Side B wins,
* - 3: Draw.
*/
function resolveChallenge(
uint256[] memory challengeIds,
uint8[] memory finalOutcomes
) external;
/** @dev This function allows the backend to cancel a user's participation in a challenge, refunding their bet.
* It can only be called by the backend and is protected against reentrancy attacks. The function checks if the
* challenge exists and ensures that the challenge is either in the `Awaiting` or `Betting` status, implying that
* it has not been resolved yet. Additionally, it verifies that the user has indeed placed a bet on the challenge.
* After these checks, it calls an internal function `_cancelParticipation` to handle the logic for cancelling the
* user's participation and processing the refund.
* @param user The address of the user whose participation is to be cancelled.
* @param challengeId The ID of the challenge from which the user's participation is to be cancelled.
*/
function cancelParticipation(address user, uint256 challengeId, uint8 cancelType) external;
/** @dev This function is used for resolving group challenges specifically, where multiple participants can win.
* It can only be executed by the backend and is protected against reentrancy. The function ensures that the
* challenge exists, is currently awaiting resolution, and is of the `Group` challenge type. It then validates
* that the lengths of the winners and profits arrays match and do not exceed the maximum number of winners allowed.
* Each winner's address must have participated in the challenge, and winners must be unique. The total of the profits
* percentages must equal 100. Once validated, the challenge status is updated, and profits are calculated and
* distributed to the winners based on the provided profits percentages.
* @param challengeId The ID of the group challenge to resolve.
* @param winners An array of addresses of the winners of the challenge.
* @param profits An array of profit percentages corresponding to each winner, summing to 100.
*/
function resolveGroupChallenge(
uint256 challengeId,
address[] calldata winners,
uint256[] calldata profits
) external;
/** @dev This function allows the backend to cancel a challenge if it's either awaiting resolution or still open for betting.
* It ensures that the challenge exists and is in a cancelable state (either `Awaiting` or `Betting`). Upon cancellation,
* the challenge's status is updated to `Canceled`, and all bets placed on the challenge are refunded to the participants.
* This function is protected by the `onlyBackend` modifier to restrict access to the backend address, and `nonReentrant`
* to prevent reentrancy attacks.
* @param challengeId The ID of the challenge to be cancelled.
* @param cancelType 0-Return bet amount without admin shares 1-Return bet amount with admin shares.
*/
function cancelChallenge(uint256 challengeId, uint8 cancelType) external;
/** @dev This function allows the contract owner to enable or disable betting across the platform.
* It's a straightforward toggle that sets the `bettingAllowed` state variable based on the input.
* Access to this function is restricted to the contract owner through the `onlyOwner` modifier from
* OpenZeppelin's Ownable contract, ensuring that only the owner can change the betting policy.
* @param value_ A boolean indicating whether betting should be allowed (`true`) or not (`false`).
*/
function allowBetting(bool value_) external;
/** @dev This function will allow the owner to toggle the apply membership values
* @param value_ true to apply membership values and false for disable membership values
*/
function updateApplyMembershipValues(bool value_) external;
/** @dev Can only be called by the contract owner.
* @param value_ The new minimum betting amount in USD.
*/
function changeMinUSDBettingAmount(uint256 value_) external;
/** @dev This function allows the contract owner to change the backend address to a new one.
* Ensures the new backend address is not the zero address to prevent rendering the contract unusable.
* The function is protected by the `onlyOwner` modifier, ensuring that only the contract owner has the authority
* to update the backend address. This is crucial for maintaining the integrity and security of the contract's
* administrative functions.
* @param backend_ The new address to be set as the backend. It must be a non-zero address.
*/
function changeBackend(address backend_) external;
/** @dev This function is designed to adjust the timing of a challenge, allowing the backend to
* modify the start and end times as necessary. It's particularly useful for correcting mistakes
* or accommodating changes in event schedules. The function checks for the existence of the challenge
* and validates that the new end time is indeed after the new start time to maintain logical consistency.
* Access is restricted to the backend through the `onlyBackend` modifier to ensure that only authorized
* personnel can make such adjustments.
* @param challengeId The ID of the challenge whose timings are to be changed.
* @param startTime The new start time for the challenge.
* @param endTime The new end time for the challenge.
*/
// function changeChallengeTime(
// uint256 challengeId,
// uint256 startTime,
// uint256 endTime
// ) external;
/** @dev This function enables the contract owner to restrict certain tokens from being used in betting activities.
* It involves removing tokens from the list of allowed tokens, potentially removing them from the list of tokens
* without a Chainlink price feed (oracless tokens), and deleting their associated price feeds if any were set.
* This is a crucial administrative function for managing the tokens that can be used on the platform, allowing
* for adjustments based on compliance, liquidity, or other operational considerations.
* Execution is restricted to the contract's owner through the `onlyOwner` modifier, ensuring that token restrictions
* can only be imposed by authorized parties.
* @param tokens An array of token addresses that are to be restricted from use in betting.
*/
function restrictTokens(address[] memory tokens) external;
/** @dev Allows the contract owner to define how administrative shares (a portion of betting winnings) are calculated.
* This can be configured differently for the STMX token versus other tokens, as indicated by the `isSTMX` flag.
* Each entry in the `thresholds` and `percentages` arrays defines a tier: if the winnings fall into a certain threshold,
* the corresponding percentage is applied as the administrative share. The function enforces ascending order for thresholds
* and ensures that the share percentages do not exceed a maximum limit. This setup allows for flexible configuration
* of administrative fees based on the amount won.
* Access is restricted to the contract owner through the `onlyOwner` modifier, ensuring that only they can set these rules.
* @param thresholds An array of threshold values, each representing the lower bound of a winnings bracket.
* @param percentages An array of percentages corresponding to each threshold, defining the admin share for that bracket.
* @param token Token address.
* @param isSTMX A boolean flag indicating whether these rules apply to the STMX token (`true`) or other tokens (`false`).
*/
function setAdminShareRules(
uint256[] memory thresholds,
uint256[] memory percentages,
address token,
bool isSTMX
) external;
/**
* Access is restricted to the contract owner through the `onlyOwner` modifier, ensuring that only they can set these rules.
* @param _maxChallengersEachSide maximun limit of challengers can join in each side.
* @param _maxChallengersForPickem maximun limit of challengers can join for pickem.
*/
function updateMaxChallengers(
uint256 _maxChallengersEachSide,
uint256 _maxChallengersForPickem
) external;
/**
* Access is restricted to the contract owner through the `onlyOwner` modifier, ensuring that only owner can deposit amount to SC.
* @param _amount amount of tokens.
* @param _token token address.
*/
function debitInSC(uint256 _amount, address _token) external payable;
/** @dev This function provides external access to the administrative share rules that have been set up for either
* the STMX token (if `isSTMX` is true) or for other tokens (if `isSTMX` is false). These rules define the thresholds
* and corresponding percentages that determine how administrative shares are calculated from betting winnings.
* The function returns two arrays: one for the thresholds and one for the percentages, which together outline the
* structure of admin shares based on the amount of winnings.
* @param token A boolean flag indicating whether to retrieve the rules for the STMX token (`true`) or other tokens (`false`).
* @return thresholds An array of uint256 representing the winnings thresholds for admin shares calculation.
* @return percentages An array of uint256 representing the admin share percentages for each corresponding threshold.
*/
function getAdminShareRules(
address token
)
external
view
returns (uint256[] memory thresholds, uint256[] memory percentages, bool isSTMX);
/** @dev This function provides external visibility into which tokens are currently permitted for use in betting within the platform.
* It leverages the EnumerableSet library from OpenZeppelin to handle the dynamic array of addresses representing the allowed tokens.
* This is particularly useful for interfaces or external contracts that need to verify or display the tokens users can bet with.
* @return An array of addresses, each representing a token that is allowed for betting.
*/
function getAllowedTokens() external view returns (address[] memory);
/** @dev This function provides access to the details of a given challenge, including its current status, which is
* dynamically determined based on the challenge's timing and resolution state. It's essential for external callers
* to be able to retrieve comprehensive data on a challenge, such as its participants, status, and betting amounts,
* to properly interact with or display information about the challenge. The function checks that the requested
* challenge exists before attempting to access its details.
* @param challengeId The unique identifier of the challenge for which details are requested.
* @return challengeDetails A `Challenge` struct containing all relevant data about the challenge, including an updated status.
*
* Requirements:
* - The challenge must exist, as indicated by its ID being within the range of created challenges.
*/
function getChallengeDetails(
uint256 challengeId
) external view returns (Challenge memory challengeDetails);
/** @dev This function allows anyone to view the details of a bet made by a user on a specific challenge,
* including the amount bet and the side the user has chosen. It's crucial for enabling users or interfaces
* to confirm the details of participation in challenges and to understand the stakes involved. This function
* directly accesses the mapping of user bets based on the user address and challenge ID, returning the
* corresponding `UserBet` struct.
* @param challengeId The ID of the challenge for which the bet details are being queried.
* @param user The address of the user whose bet details are requested.
* @return A `UserBet` struct containing the amount of the bet and the decision (side chosen) by the user for the specified challenge.
*/
function getUserBet(uint256 challengeId, address user) external view returns (UserBet memory);
/** @dev This function compiles a comprehensive view of all tokens that a user has available to withdraw,
* including winnings, refunds, or other credits due to the user. It iterates over the entire list of tokens
* recognized by the contract (not just those currently allowed for betting) to ensure that users can access
* any funds owed to them, regardless of whether a token's betting status has changed. This is essential for
* maintaining transparency and access to funds for users within the platform.
* @param user The address of the user for whom withdrawable balances are being queried.
* @return tokens An array of token addresses, representing each token that the user has a balance of.
* @return amounts An array of uint256 values, each corresponding to the balance of the token at the same index in the `tokens` array.
*/
function getUserWithdrawables(
address user
) external view returns (address[] memory tokens, uint256[] memory amounts);
/** @dev Emits a `ChallengeJoined` event if the join is successful.
* @param challengeId ID of the challenge to join
* @param amountFromWallet Amount to be bet from the user's wallet
* @param amountFromWithdrawables Amount to be bet from the user's withdrawable balance
* @param decision The side of the bet the user is taking
* @param membershipLevel user membership level
* @param feePercentage percentage amount reduced from admin share
* @param referrer referrer address
* @param referralCommision referral will get the comission from admin share
* @param proof leaf nood proof
*/
function joinChallenge(
uint256 challengeId,
uint256 amountFromWallet,
uint256 amountFromWithdrawables,
uint8 decision,
uint8 membershipLevel,
uint256 feePercentage,
address referrer,
uint256 referralCommision,
bytes32[] memory proof
) external payable;
/** @dev Emits a `BetAmountIncreased` event if the join is successful.
* @param challengeId ID of the challenge for which user wants to increase the bet amount
* @param amountFromWallet Amount to be bet from the user's wallet
* @param amountFromWithdrawables Amount to be bet from the user's withdrawable balance
* @param membershipLevel user membership level
* @param feePercentage percentage amount reduced from admin share
* @param referrer referrer address
* @param referralCommision referral will get the comission from admin share
* @param proof leaf nood proof
*/
function increaseBetAmount(
uint256 challengeId,
uint256 amountFromWallet,
uint256 amountFromWithdrawables,
uint8 membershipLevel,
uint256 feePercentage,
address referrer,
uint256 referralCommision,
bytes32[] memory proof
) external payable;
/** @dev A challenge is considered to exist if its ID is greater than 0 and less than or equal to the latest challenge ID.
* @param challengeId The ID of the challenge to check.
* @return bool Returns true if the challenge exists, false otherwise.
*/
function challengeExists(uint256 challengeId) external view returns (bool);
/** @dev This function will allow the owner to update the root node of merkle tree
* @param _root root node of merkle tree
*/
function updateRoot(bytes32 _root) external;
/** @dev This function permits the contract owner to add tokens to the list of those allowed for betting.
* It also associates Chainlink price feeds with tokens, enabling the conversion of bets to a common value basis for calculations.
* Tokens without a specified price feed (address(0)) are considered to have fixed or known values and are added to a separate list.
* The function ensures that each token in the input array has a corresponding price feed address (which can be the zero address).
* The `onlyOwner` modifier restricts this function's execution to the contract's owner, safeguarding against unauthorized token addition.
* @param tokens An array of token addresses to be allowed for betting.
* @param priceFeeds An array of Chainlink price feed addresses corresponding to the tokens. Use address(0) for tokens without a need for price feeds.
* @param minBetAmounts An array of amount corresponding to every token being allowed, the value for oracless tokens will be considers only in this method.
* Requirements:
* - The lengths of the `tokens` and `priceFeeds` arrays must match to ensure each token has a corresponding price feed address.
*/
function allowTokens(
address[] memory tokens,
address[] memory priceFeeds,
uint256[] memory minBetAmounts
) external;
}
Globals.sol 6 lines
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; uint256 constant PRECISION = 10 ** 25; uint256 constant DECIMAL = 10 ** 18; uint256 constant PERCENTAGE_100 = 10 ** 27;
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);
}
}
}
Context.sol 24 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
Ownable.sol 83 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
Math.sol 339 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @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.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}
ERC20.sol 365 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(address from, address to, uint256 amount) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
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);
}
Ownable2Step.sol 57 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.0;
import "./Ownable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() public virtual {
address sender = _msgSender();
require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
_transferOwnership(sender);
}
}
SetHelper.sol 61 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {StringSet} from "../data-structures/StringSet.sol";
/**
* @notice A simple library to work with sets
*/
library SetHelper {
using EnumerableSet for EnumerableSet.UintSet;
using EnumerableSet for EnumerableSet.AddressSet;
using StringSet for StringSet.Set;
/**
* @notice The function to insert an array of elements into the set
* @param set the set to insert the elements into
* @param array_ the elements to be inserted
*/
function add(EnumerableSet.AddressSet storage set, address[] memory array_) internal {
for (uint256 i = 0; i < array_.length; i++) {
set.add(array_[i]);
}
}
function add(EnumerableSet.UintSet storage set, uint256[] memory array_) internal {
for (uint256 i = 0; i < array_.length; i++) {
set.add(array_[i]);
}
}
function add(StringSet.Set storage set, string[] memory array_) internal {
for (uint256 i = 0; i < array_.length; i++) {
set.add(array_[i]);
}
}
/**
* @notice The function to remove an array of elements from the set
* @param set the set to remove the elements from
* @param array_ the elements to be removed
*/
function remove(EnumerableSet.AddressSet storage set, address[] memory array_) internal {
for (uint256 i = 0; i < array_.length; i++) {
set.remove(array_[i]);
}
}
function remove(EnumerableSet.UintSet storage set, uint256[] memory array_) internal {
for (uint256 i = 0; i < array_.length; i++) {
set.remove(array_[i]);
}
}
function remove(StringSet.Set storage set, string[] memory array_) internal {
for (uint256 i = 0; i < array_.length; i++) {
set.remove(array_[i]);
}
}
}
TypeCaster.sol 451 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/**
* @notice This library simplifies non-obvious type castings
*/
library TypeCaster {
/**
* @notice The function that casts the list of `X`-type elements to the list of uint256
* @param from_ the list of `X`-type elements
* @return array_ the list of uint256
*/
function asUint256Array(
bytes32[] memory from_
) internal pure returns (uint256[] memory array_) {
assembly {
array_ := from_
}
}
function asUint256Array(
address[] memory from_
) internal pure returns (uint256[] memory array_) {
assembly {
array_ := from_
}
}
/**
* @notice The function that casts the list of `X`-type elements to the list of addresses
* @param from_ the list of `X`-type elements
* @return array_ the list of addresses
*/
function asAddressArray(
bytes32[] memory from_
) internal pure returns (address[] memory array_) {
assembly {
array_ := from_
}
}
function asAddressArray(
uint256[] memory from_
) internal pure returns (address[] memory array_) {
assembly {
array_ := from_
}
}
/**
* @notice The function that casts the list of `X`-type elements to the list of bytes32
* @param from_ the list of `X`-type elements
* @return array_ the list of bytes32
*/
function asBytes32Array(
uint256[] memory from_
) internal pure returns (bytes32[] memory array_) {
assembly {
array_ := from_
}
}
function asBytes32Array(
address[] memory from_
) internal pure returns (bytes32[] memory array_) {
assembly {
array_ := from_
}
}
/**
* @notice The function to transform an element into an array
* @param from_ the element
* @return array_ the element as an array
*/
function asSingletonArray(uint256 from_) internal pure returns (uint256[] memory array_) {
array_ = new uint256[](1);
array_[0] = from_;
}
function asSingletonArray(address from_) internal pure returns (address[] memory array_) {
array_ = new address[](1);
array_[0] = from_;
}
function asSingletonArray(bool from_) internal pure returns (bool[] memory array_) {
array_ = new bool[](1);
array_[0] = from_;
}
function asSingletonArray(string memory from_) internal pure returns (string[] memory array_) {
array_ = new string[](1);
array_[0] = from_;
}
function asSingletonArray(bytes32 from_) internal pure returns (bytes32[] memory array_) {
array_ = new bytes32[](1);
array_[0] = from_;
}
/**
* @notice The function to convert static array to dynamic
* @param static_ the static array to convert
* @return dynamic_ the converted dynamic array
*/
function asDynamic(
uint256[1] memory static_
) internal pure returns (uint256[] memory dynamic_) {
return asSingletonArray(static_[0]);
}
function asDynamic(
uint256[2] memory static_
) internal pure returns (uint256[] memory dynamic_) {
dynamic_ = new uint256[](2);
uint256 pointerS_;
uint256 pointerD_;
assembly {
pointerS_ := static_
pointerD_ := dynamic_
}
_copy(pointerS_, pointerD_, 2);
}
function asDynamic(
uint256[3] memory static_
) internal pure returns (uint256[] memory dynamic_) {
dynamic_ = new uint256[](3);
uint256 pointerS_;
uint256 pointerD_;
assembly {
pointerS_ := static_
pointerD_ := dynamic_
}
_copy(pointerS_, pointerD_, 3);
}
function asDynamic(
uint256[4] memory static_
) internal pure returns (uint256[] memory dynamic_) {
dynamic_ = new uint256[](4);
uint256 pointerS_;
uint256 pointerD_;
assembly {
pointerS_ := static_
pointerD_ := dynamic_
}
_copy(pointerS_, pointerD_, 4);
}
function asDynamic(
uint256[5] memory static_
) internal pure returns (uint256[] memory dynamic_) {
dynamic_ = new uint256[](5);
uint256 pointerS_;
uint256 pointerD_;
assembly {
pointerS_ := static_
pointerD_ := dynamic_
}
_copy(pointerS_, pointerD_, 5);
}
function asDynamic(
address[1] memory static_
) internal pure returns (address[] memory dynamic_) {
return asSingletonArray(static_[0]);
}
function asDynamic(
address[2] memory static_
) internal pure returns (address[] memory dynamic_) {
dynamic_ = new address[](2);
uint256 pointerS_;
uint256 pointerD_;
assembly {
pointerS_ := static_
pointerD_ := dynamic_
}
_copy(pointerS_, pointerD_, 2);
}
function asDynamic(
address[3] memory static_
) internal pure returns (address[] memory dynamic_) {
dynamic_ = new address[](3);
uint256 pointerS_;
uint256 pointerD_;
assembly {
pointerS_ := static_
pointerD_ := dynamic_
}
_copy(pointerS_, pointerD_, 3);
}
function asDynamic(
address[4] memory static_
) internal pure returns (address[] memory dynamic_) {
dynamic_ = new address[](4);
uint256 pointerS_;
uint256 pointerD_;
assembly {
pointerS_ := static_
pointerD_ := dynamic_
}
_copy(pointerS_, pointerD_, 4);
}
function asDynamic(
address[5] memory static_
) internal pure returns (address[] memory dynamic_) {
dynamic_ = new address[](5);
uint256 pointerS_;
uint256 pointerD_;
assembly {
pointerS_ := static_
pointerD_ := dynamic_
}
_copy(pointerS_, pointerD_, 5);
}
function asDynamic(bool[1] memory static_) internal pure returns (bool[] memory dynamic_) {
return asSingletonArray(static_[0]);
}
function asDynamic(bool[2] memory static_) internal pure returns (bool[] memory dynamic_) {
dynamic_ = new bool[](2);
uint256 pointerS_;
uint256 pointerD_;
assembly {
pointerS_ := static_
pointerD_ := dynamic_
}
_copy(pointerS_, pointerD_, 2);
}
function asDynamic(bool[3] memory static_) internal pure returns (bool[] memory dynamic_) {
dynamic_ = new bool[](3);
uint256 pointerS_;
uint256 pointerD_;
assembly {
pointerS_ := static_
pointerD_ := dynamic_
}
_copy(pointerS_, pointerD_, 3);
}
function asDynamic(bool[4] memory static_) internal pure returns (bool[] memory dynamic_) {
dynamic_ = new bool[](4);
uint256 pointerS_;
uint256 pointerD_;
assembly {
pointerS_ := static_
pointerD_ := dynamic_
}
_copy(pointerS_, pointerD_, 4);
}
function asDynamic(bool[5] memory static_) internal pure returns (bool[] memory dynamic_) {
dynamic_ = new bool[](5);
uint256 pointerS_;
uint256 pointerD_;
assembly {
pointerS_ := static_
pointerD_ := dynamic_
}
_copy(pointerS_, pointerD_, 5);
}
function asDynamic(string[1] memory static_) internal pure returns (string[] memory dynamic_) {
return asSingletonArray(static_[0]);
}
function asDynamic(string[2] memory static_) internal pure returns (string[] memory dynamic_) {
dynamic_ = new string[](2);
uint256 pointerS_;
uint256 pointerD_;
assembly {
pointerS_ := static_
pointerD_ := dynamic_
}
_copy(pointerS_, pointerD_, 2);
}
function asDynamic(string[3] memory static_) internal pure returns (string[] memory dynamic_) {
dynamic_ = new string[](3);
uint256 pointerS_;
uint256 pointerD_;
assembly {
pointerS_ := static_
pointerD_ := dynamic_
}
_copy(pointerS_, pointerD_, 3);
}
function asDynamic(string[4] memory static_) internal pure returns (string[] memory dynamic_) {
dynamic_ = new string[](4);
uint256 pointerS_;
uint256 pointerD_;
assembly {
pointerS_ := static_
pointerD_ := dynamic_
}
_copy(pointerS_, pointerD_, 4);
}
function asDynamic(string[5] memory static_) internal pure returns (string[] memory dynamic_) {
dynamic_ = new string[](5);
uint256 pointerS_;
uint256 pointerD_;
assembly {
pointerS_ := static_
pointerD_ := dynamic_
}
_copy(pointerS_, pointerD_, 5);
}
function asDynamic(
bytes32[1] memory static_
) internal pure returns (bytes32[] memory dynamic_) {
return asSingletonArray(static_[0]);
}
function asDynamic(
bytes32[2] memory static_
) internal pure returns (bytes32[] memory dynamic_) {
dynamic_ = new bytes32[](2);
uint256 pointerS_;
uint256 pointerD_;
assembly {
pointerS_ := static_
pointerD_ := dynamic_
}
_copy(pointerS_, pointerD_, 2);
}
function asDynamic(
bytes32[3] memory static_
) internal pure returns (bytes32[] memory dynamic_) {
dynamic_ = new bytes32[](3);
uint256 pointerS_;
uint256 pointerD_;
assembly {
pointerS_ := static_
pointerD_ := dynamic_
}
_copy(pointerS_, pointerD_, 3);
}
function asDynamic(
bytes32[4] memory static_
) internal pure returns (bytes32[] memory dynamic_) {
dynamic_ = new bytes32[](4);
uint256 pointerS_;
uint256 pointerD_;
assembly {
pointerS_ := static_
pointerD_ := dynamic_
}
_copy(pointerS_, pointerD_, 4);
}
function asDynamic(
bytes32[5] memory static_
) internal pure returns (bytes32[] memory dynamic_) {
dynamic_ = new bytes32[](5);
uint256 pointerS_;
uint256 pointerD_;
assembly {
pointerS_ := static_
pointerD_ := dynamic_
}
_copy(pointerS_, pointerD_, 5);
}
function _copy(uint256 locationS_, uint256 locationD_, uint256 length_) private pure {
assembly {
for {
let i := 0
} lt(i, length_) {
i := add(i, 1)
} {
locationD_ := add(locationD_, 0x20)
mstore(locationD_, mload(locationS_))
locationS_ := add(locationS_, 0x20)
}
}
}
}
ArrayHelper.sol 270 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
/**
* @notice A simple library to work with arrays
*/
library ArrayHelper {
/**
* @notice The function that searches for the index of the first occurring element, which is
* greater than or equal to the `element_`. The time complexity is O(log n)
* @param array the array to search in
* @param element_ the element
* @return index_ the index of the found element or the length of the `array` if no such element
*/
function lowerBound(
uint256[] storage array,
uint256 element_
) internal view returns (uint256 index_) {
(uint256 low_, uint256 high_) = (0, array.length);
while (low_ < high_) {
uint256 mid_ = Math.average(low_, high_);
if (array[mid_] >= element_) {
high_ = mid_;
} else {
low_ = mid_ + 1;
}
}
return high_;
}
/**
* @notice The function that searches for the index of the first occurring element, which is
* greater than the `element_`. The time complexity is O(log n)
* @param array the array to search in
* @param element_ the element
* @return index_ the index of the found element or the length of the `array` if no such element
*/
function upperBound(
uint256[] storage array,
uint256 element_
) internal view returns (uint256 index_) {
(uint256 low_, uint256 high_) = (0, array.length);
while (low_ < high_) {
uint256 mid_ = Math.average(low_, high_);
if (array[mid_] > element_) {
high_ = mid_;
} else {
low_ = mid_ + 1;
}
}
return high_;
}
/**
* @notice The function that calculates the sum of all array elements from `beginIndex_` to
* `endIndex_` inclusive using its prefix sum array
* @param beginIndex_ the index of the first range element
* @param endIndex_ the index of the last range element
* @return the sum of all elements of the range
*/
function getRangeSum(
uint256[] storage prefixes,
uint256 beginIndex_,
uint256 endIndex_
) internal view returns (uint256) {
require(beginIndex_ <= endIndex_, "ArrayHelper: wrong range");
if (beginIndex_ == 0) {
return prefixes[endIndex_];
}
return prefixes[endIndex_] - prefixes[beginIndex_ - 1];
}
/**
* @notice The function to compute the prefix sum array
* @param arr_ the initial array to be turned into the prefix sum array
* @return prefixes_ the prefix sum array
*/
function countPrefixes(
uint256[] memory arr_
) internal pure returns (uint256[] memory prefixes_) {
if (arr_.length == 0) {
return prefixes_;
}
prefixes_ = new uint256[](arr_.length);
prefixes_[0] = arr_[0];
for (uint256 i = 1; i < prefixes_.length; i++) {
prefixes_[i] = prefixes_[i - 1] + arr_[i];
}
}
/**
* @notice The function to reverse an array
* @param arr_ the array to reverse
* @return reversed_ the reversed array
*/
function reverse(uint256[] memory arr_) internal pure returns (uint256[] memory reversed_) {
reversed_ = new uint256[](arr_.length);
uint256 i = arr_.length;
while (i > 0) {
i--;
reversed_[arr_.length - 1 - i] = arr_[i];
}
}
function reverse(address[] memory arr_) internal pure returns (address[] memory reversed_) {
reversed_ = new address[](arr_.length);
uint256 i = arr_.length;
while (i > 0) {
i--;
reversed_[arr_.length - 1 - i] = arr_[i];
}
}
function reverse(string[] memory arr_) internal pure returns (string[] memory reversed_) {
reversed_ = new string[](arr_.length);
uint256 i = arr_.length;
while (i > 0) {
i--;
reversed_[arr_.length - 1 - i] = arr_[i];
}
}
function reverse(bytes32[] memory arr_) internal pure returns (bytes32[] memory reversed_) {
reversed_ = new bytes32[](arr_.length);
uint256 i = arr_.length;
while (i > 0) {
i--;
reversed_[arr_.length - 1 - i] = arr_[i];
}
}
/**
* @notice The function to insert an array into the other array
* @param to_ the array to insert into
* @param index_ the insertion starting index
* @param what_ the array to be inserted
* @return the index to start the next insertion from
*/
function insert(
uint256[] memory to_,
uint256 index_,
uint256[] memory what_
) internal pure returns (uint256) {
for (uint256 i = 0; i < what_.length; i++) {
to_[index_ + i] = what_[i];
}
return index_ + what_.length;
}
function insert(
address[] memory to_,
uint256 index_,
address[] memory what_
) internal pure returns (uint256) {
for (uint256 i = 0; i < what_.length; i++) {
to_[index_ + i] = what_[i];
}
return index_ + what_.length;
}
function insert(
string[] memory to_,
uint256 index_,
string[] memory what_
) internal pure returns (uint256) {
for (uint256 i = 0; i < what_.length; i++) {
to_[index_ + i] = what_[i];
}
return index_ + what_.length;
}
function insert(
bytes32[] memory to_,
uint256 index_,
bytes32[] memory what_
) internal pure returns (uint256) {
for (uint256 i = 0; i < what_.length; i++) {
to_[index_ + i] = what_[i];
}
return index_ + what_.length;
}
/**
* @notice The function that free memory that was allocated for array
* @param array_ the array to crop
* @param newLength_ the new length of the array
* @return ref to cropped array
*/
function crop(
uint256[] memory array_,
uint256 newLength_
) internal pure returns (uint256[] memory) {
if (newLength_ < array_.length) {
assembly {
mstore(array_, newLength_)
}
}
return array_;
}
function crop(
address[] memory array_,
uint256 newLength_
) internal pure returns (address[] memory) {
if (newLength_ < array_.length) {
assembly {
mstore(array_, newLength_)
}
}
return array_;
}
function crop(bool[] memory array_, uint256 newLength_) internal pure returns (bool[] memory) {
if (newLength_ < array_.length) {
assembly {
mstore(array_, newLength_)
}
}
return array_;
}
function crop(
string[] memory array_,
uint256 newLength_
) internal pure returns (string[] memory) {
if (newLength_ < array_.length) {
assembly {
mstore(array_, newLength_)
}
}
return array_;
}
function crop(
bytes32[] memory array_,
uint256 newLength_
) internal pure returns (bytes32[] memory) {
if (newLength_ < array_.length) {
assembly {
mstore(array_, newLength_)
}
}
return array_;
}
}
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;
}
}
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));
}
}
EnumerableSet.sol 378 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}
StringSet.sol 101 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/**
* @notice ## Usage example:
*
* ```
* using StringSet for StringSet.Set;
*
* StringSet.Set internal set;
* ```
*/
library StringSet {
struct Set {
string[] _values;
mapping(string => uint256) _indexes;
}
/**
* @notice The function add value to set
* @param set the set object
* @param value_ the value to add
*/
function add(Set storage set, string memory value_) internal returns (bool) {
if (!contains(set, value_)) {
set._values.push(value_);
set._indexes[value_] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @notice The function remove value to set
* @param set the set object
* @param value_ the value to remove
*/
function remove(Set storage set, string memory value_) internal returns (bool) {
uint256 valueIndex_ = set._indexes[value_];
if (valueIndex_ != 0) {
uint256 toDeleteIndex_ = valueIndex_ - 1;
uint256 lastIndex_ = set._values.length - 1;
if (lastIndex_ != toDeleteIndex_) {
string memory lastValue_ = set._values[lastIndex_];
set._values[toDeleteIndex_] = lastValue_;
set._indexes[lastValue_] = valueIndex_;
}
set._values.pop();
delete set._indexes[value_];
return true;
} else {
return false;
}
}
/**
* @notice The function returns true if value in the set
* @param set the set object
* @param value_ the value to search in set
* @return true if value is in the set, false otherwise
*/
function contains(Set storage set, string memory value_) internal view returns (bool) {
return set._indexes[value_] != 0;
}
/**
* @notice The function returns length of set
* @param set the set object
* @return the the number of elements in the set
*/
function length(Set storage set) internal view returns (uint256) {
return set._values.length;
}
/**
* @notice The function returns value from set by index
* @param set the set object
* @param index_ the index of slot in set
* @return the value at index
*/
function at(Set storage set, uint256 index_) internal view returns (string memory) {
return set._values[index_];
}
/**
* @notice The function that returns values the set stores, can be very expensive to call
* @param set the set object
* @return the memory array of values
*/
function values(Set storage set) internal view returns (string[] memory) {
return set._values;
}
}
MerkleProof.sol 227 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.2) (utils/cryptography/MerkleProof.sol)
pragma solidity ^0.8.0;
/**
* @dev These functions deal with verification of Merkle Tree proofs.
*
* The tree and the proofs can be generated using our
* https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
* You will find a quickstart guide in the readme.
*
* WARNING: You should avoid using leaf values that are 64 bytes long prior to
* hashing, or use a hash function other than keccak256 for hashing leaves.
* This is because the concatenation of a sorted pair of internal nodes in
* the merkle tree could be reinterpreted as a leaf value.
* OpenZeppelin's JavaScript library generates merkle trees that are safe
* against this attack out of the box.
*/
library MerkleProof {
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*/
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
/**
* @dev Calldata version of {verify}
*
* _Available since v4.7._
*/
function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProofCalldata(proof, leaf) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leafs & pre-images are assumed to be sorted.
*
* _Available since v4.4._
*/
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Calldata version of {processProof}
*
* _Available since v4.7._
*/
function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Returns true if the `leaves` can be simultaneously proven to be a part of a merkle tree defined by
* `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
*
* CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
*
* _Available since v4.7._
*/
function multiProofVerify(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProof(proof, proofFlags, leaves) == root;
}
/**
* @dev Calldata version of {multiProofVerify}
*
* CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
*
* _Available since v4.7._
*/
function multiProofVerifyCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProofCalldata(proof, proofFlags, leaves) == root;
}
/**
* @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
* proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
* leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
* respectively.
*
* CAUTION: Not all merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
* is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
* tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
*
* _Available since v4.7._
*/
function processMultiProof(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
require(leavesLen + proofLen - 1 == totalHashes, "MerkleProof: invalid multiproof");
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
require(proofPos == proofLen, "MerkleProof: invalid multiproof");
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Calldata version of {processMultiProof}.
*
* CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
*
* _Available since v4.7._
*/
function processMultiProofCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
require(leavesLen + proofLen - 1 == totalHashes, "MerkleProof: invalid multiproof");
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
require(proofPos == proofLen, "MerkleProof: invalid multiproof");
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
}
function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}
DecimalsConverter.sol 269 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {ERC20, IERC20, IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
/**
* @notice This library is used to convert numbers that use token's N decimals to M decimals.
* Comes extremely handy with standardizing the business logic that is intended to work with many different ERC20 tokens
* that have different precision (decimals). One can perform calculations with 18 decimals only and resort to convertion
* only when the payouts (or interactions) with the actual tokes have to be made.
*
* The best usage scenario involves accepting and calculating values with 18 decimals throughout the project, despite the tokens decimals.
*
* Also it is recommended to call `round18()` function on the first execution line in order to get rid of the
* trailing numbers if the destination decimals are less than 18
*
* ## Usage example:
*
* ```
* contract Taker {
* ERC20 public USDC;
* uint256 public paid;
*
* . . .
*
* function pay(uint256 amount) external {
* uint256 decimals = USDC.decimals();
* amount = amount.round18(decimals);
*
* paid += amount;
* USDC.transferFrom(msg.sender, address(this), amount.from18(decimals));
* }
* }
* ```
*/
library DecimalsConverter {
/**
* @notice The function to get the decimals of ERC20 token. Needed for bytecode optimization
* @param token_ the ERC20 token
* @return the decimals of provided token
*/
function decimals(address token_) internal view returns (uint8) {
return ERC20(token_).decimals();
}
/**
* @notice The function to bring the number to 18 decimals of precision
* @param amount_ the number to convert
* @param token_ the token, whose decimals will be precised to 18
* @return the number brought to 18 decimals of precision
*/
function to18(uint256 amount_, address token_) internal view returns (uint256) {
return to18(amount_, decimals(token_));
}
/**
* @notice The function to bring the number to 18 decimals of precision
* @param amount_ the number to convert
* @param baseDecimals_ the current precision of the number
* @return the number brought to 18 decimals of precision
*/
function to18(uint256 amount_, uint256 baseDecimals_) internal pure returns (uint256) {
return convert(amount_, baseDecimals_, 18);
}
/**
* @notice The function to bring the number to 18 decimals of precision. Reverts if output is zero
* @param amount_ the number to convert
* @param token_ the token, whose decimals will be precised to 18
* @return the number brought to 18 decimals of precision
*/
function to18Safe(uint256 amount_, address token_) internal view returns (uint256) {
return to18Safe(amount_, decimals(token_));
}
/**
* @notice The function to bring the number to 18 decimals of precision. Reverts if output is zero
* @param amount_ the number to convert
* @param baseDecimals_ the current precision of the number
* @return the number brought to 18 decimals of precision
*/
function to18Safe(uint256 amount_, uint256 baseDecimals_) internal pure returns (uint256) {
return _convertSafe(amount_, baseDecimals_, _to18);
}
/**
* @notice The function to bring the number from 18 decimals to the desired decimals of precision
* @param amount_ the number to covert
* @param token_ the token, whose decimals will be used as desired decimals of precision
* @return the number brought from 18 to desired decimals of precision
*/
function from18(uint256 amount_, address token_) internal view returns (uint256) {
return from18(amount_, decimals(token_));
}
/**
* @notice The function to bring the number from 18 decimals to the desired decimals of precision
* @param amount_ the number to covert
* @param destDecimals_ the desired precision decimals
* @return the number brought from 18 to desired decimals of precision
*/
function from18(uint256 amount_, uint256 destDecimals_) internal pure returns (uint256) {
return convert(amount_, 18, destDecimals_);
}
/**
* @notice The function to bring the number from 18 decimals to the desired decimals of precision.
* Reverts if output is zero
* @param amount_ the number to covert
* @param token_ the token, whose decimals will be used as desired decimals of precision
* @return the number brought from 18 to desired decimals of precision
*/
function from18Safe(uint256 amount_, address token_) internal view returns (uint256) {
return from18Safe(amount_, decimals(token_));
}
/**
* @notice The function to bring the number from 18 decimals to the desired decimals of precision.
* Reverts if output is zero
* @param amount_ the number to covert
* @param destDecimals_ the desired precision decimals
* @return the number brought from 18 to desired decimals of precision
*/
function from18Safe(uint256 amount_, uint256 destDecimals_) internal pure returns (uint256) {
return _convertSafe(amount_, destDecimals_, _from18);
}
/**
* @notice The function to substitute the trailing digits of a number with zeros
* @param amount_ the number to round. Should be with 18 precision decimals
* @param decimals_ the required number precision
* @return the rounded number. Comes with 18 precision decimals
*/
function round18(uint256 amount_, uint256 decimals_) internal pure returns (uint256) {
return to18(from18(amount_, decimals_), decimals_);
}
/**
* @notice The function to substitute the trailing digits of a number with zeros. Reverts if output is zero
* @param amount_ the number to round. Should be with 18 precision decimals
* @param decimals_ the required number precision
* @return the rounded number. Comes with 18 precision decimals
*/
function round18Safe(uint256 amount_, uint256 decimals_) internal pure returns (uint256) {
return _convertSafe(amount_, decimals_, round18);
}
/**
* @notice The function to do the token precision convertion
* @param amount_ the amount to convert
* @param baseToken_ current token
* @param destToken_ desired token
* @return the converted number
*/
function convert(
uint256 amount_,
address baseToken_,
address destToken_
) internal view returns (uint256) {
return convert(amount_, uint256(decimals(baseToken_)), uint256(decimals(destToken_)));
}
/**
* @notice The function to do the precision convertion
* @param amount_ the amount to covert
* @param baseDecimals_ current number precision
* @param destDecimals_ desired number precision
* @return the converted number
*/
function convert(
uint256 amount_,
uint256 baseDecimals_,
uint256 destDecimals_
) internal pure returns (uint256) {
if (baseDecimals_ > destDecimals_) {
amount_ = amount_ / 10 ** (baseDecimals_ - destDecimals_);
} else if (baseDecimals_ < destDecimals_) {
amount_ = amount_ * 10 ** (destDecimals_ - baseDecimals_);
}
return amount_;
}
/**
* @notice The function to do the token precision convertion. Reverts if output is zero
* @param amount_ the amount to convert
* @param baseToken_ current token
* @param destToken_ desired token
* @return the converted number
*/
function convertTokensSafe(
uint256 amount_,
address baseToken_,
address destToken_
) internal view returns (uint256) {
return _convertTokensSafe(amount_, baseToken_, destToken_, _convertTokens);
}
/**
* @notice The function to bring the number to 18 decimals of precision
* @param amount_ the number to convert
* @param baseDecimals_ the current precision of the number
* @return the number brought to 18 decimals of precision
*/
function _to18(uint256 amount_, uint256 baseDecimals_) private pure returns (uint256) {
return convert(amount_, baseDecimals_, 18);
}
/**
* @notice The function to bring the number from 18 decimals to the desired decimals of precision
* @param amount_ the number to covert
* @param destDecimals_ the desired precision decimals
* @return the number brought from 18 to desired decimals of precision
*/
function _from18(uint256 amount_, uint256 destDecimals_) private pure returns (uint256) {
return convert(amount_, 18, destDecimals_);
}
/**
* @notice The function to do the token precision convertion
* @param amount_ the amount to convert
* @param baseToken_ current token
* @param destToken_ desired token
* @return the converted number
*/
function _convertTokens(
uint256 amount_,
address baseToken_,
address destToken_
) private view returns (uint256) {
return convert(amount_, uint256(decimals(baseToken_)), uint256(decimals(destToken_)));
}
/**
* @notice The function wrapper to do the safe precision convertion. Reverts if output is zero
* @param amount_ the amount to covert
* @param decimals_ the precision decimals
* @param _convertFunc the internal function pointer to "from", "to", or "round" functions
* @return conversionResult_ the convertion result
*/
function _convertSafe(
uint256 amount_,
uint256 decimals_,
function(uint256, uint256) internal pure returns (uint256) _convertFunc
) private pure returns (uint256 conversionResult_) {
conversionResult_ = _convertFunc(amount_, decimals_);
require(conversionResult_ > 0, "DecimalsConverter: conversion failed");
}
/**
* @notice The function wrapper to do the safe precision convertion for ERC20 tokens. Reverts if output is zero
* @param amount_ the amount to covert
* @param baseToken_ current token
* @param destToken_ desired token
* @param _convertFunc the internal function pointer to "from", "to", or "round" functions
* @return conversionResult_ the convertion result
*/
function _convertTokensSafe(
uint256 amount_,
address baseToken_,
address destToken_,
function(uint256, address, address) internal view returns (uint256) _convertFunc
) private view returns (uint256 conversionResult_) {
conversionResult_ = _convertFunc(amount_, baseToken_, destToken_);
require(conversionResult_ > 0, "DecimalsConverter: conversion failed");
}
}
IERC20Permit.sol 60 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (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.
*/
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].
*/
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);
}
IERC20Metadata.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
AggregatorV3Interface.sol 19 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(
uint80 _roundId
) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
function latestRoundData()
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}
Read Contract
applyMembershipValues 0x1f2a8dd6 → bool
awaitingTimeForPublicCancel 0x1fb05b33 → uint256
backend 0x099e4133 → address
bettingAllowed 0x4c9f166d → bool
challengeExists 0x46554719 → bool
defaultOracleDecimals 0xca44bb53 → uint256
getAdminShareRules 0x7946b781 → uint256[], uint256[], bool
getAllowedTokens 0x024ece89 → address[]
getChallengeDetails 0x3d40a54f → tuple
getUserBet 0xc03fb87c → tuple
getUserWithdrawables 0xa8037e0e → address[], uint256[]
latestChallengeId 0xbf26931f → uint256
maxAdminShareInUsd 0xf030f013 → uint256
maxAdminShareSTMX 0xab04663b → uint256
maxAdminShareThresholds 0x8edfe787 → uint256
maxChallengersEachSide 0x1411b438 → uint256
maxChallengersForPickem 0x8bbed8ef → uint256
maxChallengesToResolve 0xab535f8f → uint256
maxForMinUSDBetAmount 0x07c9f466 → uint256
maxWinnersGroupChallenge 0x75b1510c → uint256
minUSDBetAmount 0x9474aef6 → uint256
owner 0x8da5cb5b → address
pendingOwner 0xe30c3978 → address
priceFeedErrorMargin 0xd89b6dc0 → uint256
renounceOwnership 0x715018a6
Write Contract 20 functions
These functions modify contract state and require a wallet transaction to execute.
acceptOwnership 0x79ba5097
No parameters
allowBetting 0x95e737c0
bool value_
allowTokens 0xa8f3f408
address[] tokens
address[] priceFeeds
uint256[] minBetAmounts
cancelChallenge 0xef2104a2
uint256 challengeId
uint8 cancelType
cancelParticipation 0x8c2bd692
address user
uint256 challengeId
uint8 cancelType
changeBackend 0xeaecfca7
address backend_
changeMinUSDBettingAmount 0x8288c80e
uint256 value_
createChallenge 0xd0339fee
address token
uint256 amountFromWallet
uint256 amountFromWithdrawables
uint8 decision
uint8 challengeType
uint256 startTime
uint256 endTime
uint8 membershipLevel
uint256 feePercentage
address referrer
uint256 referralCommision
bytes32[] proof
debitInSC 0xdef08d26
uint256 _amount
address _token
increaseBetAmount 0x2179e5fe
uint256 challengeId
uint256 amountFromWallet
uint256 amountFromWithdrawables
uint8 membershipLevel
uint256 feePercentage
address referrer
uint256 referralCommision
bytes32[] proof
joinChallenge 0x78a29c09
uint256 challengeId
uint256 amountFromWallet
uint256 amountFromWithdrawables
uint8 decision
uint8 membershipLevel
uint256 feePercentage
address referrer
uint256 referralCommision
bytes32[] proof
resolveChallenge 0xb9f433a6
uint256[] challengeIds
uint8[] finalOutcomes
resolveGroupChallenge 0x0306c19a
uint256 challengeId
address[] winners
uint256[] profits
restrictTokens 0x8770912f
address[] tokens
setAdminShareRules 0x615b2c78
uint256[] thresholds
uint256[] sharesInUSD
address token
bool isSTMX
transferOwnership 0xf2fde38b
address newOwner
updateApplyMembershipValues 0x130894e0
bool value_
updateMaxChallengers 0x46d1cdc0
uint256 _maxChallengersEachSide
uint256 _maxChallengersForPickem
updateRoot 0x21ff9970
bytes32 _root
withdraw 0x51cff8d9
address token
Token Balances (2)
View Transfers →Recent Transactions
No transactions found for this address