Address Contract Partially Verified
Address
0xcafe5De18756817D98F4603F6828397406D4CaFE
Balance
0 ETH
Nonce
1
Code Size
21972 bytes
Creator
0x61ff36e4...227e at tx 0xf0ab42f5...12872d
Indexed Transactions
0
Contract Bytecode
21972 bytes
0x6080604052600436106102d9575f3560e01c806371d41eb311610189578063b3f120a1116100d8578063e66f53b711610092578063f399e22e1161006d578063f399e22e14610944578063f69a00b614610963578063f7b6604e14610996578063fec53a14146109ac575f5ffd5b8063e66f53b7146108fd578063e78cea9214610911578063ea9e96a614610925575f5ffd5b8063b3f120a114610829578063c65a20f01461083d578063d505accf1461085c578063d6f29e811461087b578063dd62ed3e1461089a578063ddb2bf5c146108de575f5ffd5b806395d89b4111610143578063a53cb8511161011e578063a53cb8511461079e578063a9059cbb146107ca578063aa22dc33146107e9578063acb734bb14610815575f5ffd5b806395d89b411461074c5780639ecb381a14610760578063a41942a41461077f575f5ffd5b806371d41eb31461064657806377cc7a3a146106675780637ecebe001461068657806384b0196e146106a5578063873234cf146106cc5780638a510f271461072d575f5ffd5b8063313ce567116102455780635c975abb116101ff5780636aaa54cf116101da5780636aaa54cf146105c05780636d0d6a7e146105d45780636df0627e146105f357806370a0823114610612575f5ffd5b80635c975abb146105665780635f029ebe14610599578063692ea04b146105ac575f5ffd5b8063313ce567146104a45780633243bc60146104bf5780633644e515146104d357806347a10e56146104e7578063520a54951461053357806357a6d45a14610547575f5ffd5b806318160ddd1161029657806318160ddd146103d257806318bf5077146103e657806323b872dd1461040757806327ae6883146104265780632b8c49e31461045957806330315dc414610478575f5ffd5b806301ffc9a7146102dd57806306fdde0314610311578063095ea7b3146103325780630ed4c438146103515780631014d37514610373578063147040de146103be575b5f5ffd5b3480156102e8575f5ffd5b506102fc6102f7366004614267565b6109c0565b60405190151581526020015b60405180910390f35b34801561031c575f5ffd5b506103256109f6565b60405161030891906142bc565b34801561033d575f5ffd5b506102fc61034c3660046142e2565b610a86565b34801561035c575f5ffd5b50610365610a9d565b604051908152602001610308565b34801561037e575f5ffd5b506103a67f00000000000000000000000077703ae126b971c9946d562f41dd47071da0077781565b6040516001600160a01b039091168152602001610308565b3480156103c9575f5ffd5b50610325610abf565b3480156103dd575f5ffd5b50600254610365565b3480156103f1575f5ffd5b506104056104003660046142e2565b610aec565b005b348015610412575f5ffd5b506102fc61042136600461430c565b610b44565b348015610431575f5ffd5b506103a67f0000000000000000000000008147664fbd1571e3105e942527096f8bf5bf626481565b348015610464575f5ffd5b506104056104733660046142e2565b610b67565b348015610483575f5ffd5b5061048c610bb7565b6040516001600160401b039091168152602001610308565b3480156104af575f5ffd5b5060405160098152602001610308565b3480156104ca575f5ffd5b50610365610bd2565b3480156104de575f5ffd5b50610365610bf4565b3480156104f2575f5ffd5b506102fc61050136600461434a565b7f00000000000000000000000077703ae126b971c9946d562f41dd47071da007776001600160a01b0390811691161490565b34801561053e575f5ffd5b50610365610bfd565b348015610552575f5ffd5b50610405610561366004614374565b610c1b565b348015610571575f5ffd5b5061057a610d2b565b6040805193151584529115156020840152151590820152606001610308565b6103656105a73660046143b4565b610d7b565b3480156105b7575f5ffd5b50610365610ed9565b3480156105cb575f5ffd5b50610365610f00565b3480156105df575f5ffd5b506104056105ee36600461440f565b610f1e565b3480156105fe575f5ffd5b5061040561060d36600461447a565b611208565b34801561061d575f5ffd5b5061036561062c36600461434a565b6001600160a01b03165f9081526020819052604090205490565b348015610651575f5ffd5b5061065a6112da565b60405161030891906144e1565b348015610672575f5ffd5b506103656106813660046143b4565b6113b7565b348015610691575f5ffd5b506103656106a036600461434a565b6113d3565b3480156106b0575f5ffd5b506106b96113f0565b60405161030897969594939291906144f3565b3480156106d7575f5ffd5b506106e0611432565b60405161030891905f60808201905061ffff835116825261ffff60208401511660208301526001600160401b03604084015116604083015262ffffff606084015116606083015292915050565b348015610738575f5ffd5b506103656107473660046145a8565b6114b0565b348015610757575f5ffd5b50610325611803565b34801561076b575f5ffd5b5061040561077a36600461434a565b611812565b34801561078a575f5ffd5b5061040561079936600461434a565b6118d5565b3480156107a9575f5ffd5b506107bd6107b83660046143b4565b611990565b60405161030891906145ed565b3480156107d5575f5ffd5b506102fc6107e43660046142e2565b611a84565b3480156107f4575f5ffd5b506108086108033660046145fb565b611a91565b604051610308919061466a565b348015610820575f5ffd5b50610325611b52565b348015610834575f5ffd5b50610325611c4d565b348015610848575f5ffd5b506104056108573660046146b4565b611c86565b348015610867575f5ffd5b50610405610876366004614700565b611cd3565b348015610886575f5ffd5b5061040561089536600461476c565b611e0e565b3480156108a5575f5ffd5b506103656108b436600461479a565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b3480156108e9575f5ffd5b506103656108f83660046143b4565b61201f565b348015610908575f5ffd5b506103a661204a565b34801561091c575f5ffd5b506103a6612065565b348015610930575f5ffd5b5061040561093f36600461487b565b61207d565b34801561094f575f5ffd5b5061040561095e36600461498f565b612134565b34801561096e575f5ffd5b506103a67f0000000000000000000000005c9584c0b5fe561fdc041b29878113d446d87e8081565b3480156109a1575f5ffd5b50633b9aca00610365565b3480156109b7575f5ffd5b50610365612492565b5f6001600160e01b03198216630cccc66560e21b14806109f057506301ffc9a760e01b6001600160e01b03198316145b92915050565b606060038054610a05906149ac565b80601f0160208091040260200160405190810160405280929190818152602001828054610a31906149ac565b8015610a7c5780601f10610a5357610100808354040283529160200191610a7c565b820191905f5260205f20905b815481529060010190602001808311610a5f57829003601f168201915b5050505050905090565b5f33610a938185856124a4565b5060019392505050565b5f610aa66124b1565b60020154600160401b90046001600160401b0316919050565b6060610ae760014614610ad06124b1565b6003015460601b6001600160601b031916906124d5565b905090565b610af533612526565b610aff82826125b8565b60405181815233906001600160a01b038416907fde22baff038e3a3e08407cbdf617deed74e869a7ba517df611e33131c6e6ea04906020015b60405180910390a35050565b5f33610b518582856125ec565b610b5c858585612668565b506001949350505050565b610b7033612526565b610b7a82826126c5565b60405181815233906001600160a01b038416907fb90795a66650155983e242cac3e1ac1a4dc26f8ed2987f3ce416a34e00111fd490602001610b38565b5f610bc06124b1565b600201546001600160401b0316919050565b5f610bdb6124b1565b60030154600160a01b90046001600160401b0316919050565b5f610ae76126f9565b5f610c066124b1565b60030154600160e01b900462ffffff16919050565b610c68610c266124b1565b600101546001600160a01b0316336001600160a01b0316146040518060400160405280600c81526020016b1d5b985d5d1a1bdc9a5e995960a21b815250612825565b82610c716124b1565b8054911515600160a01b0260ff60a01b1990921691909117905581610c946124b1565b6003018054911515600160f81b026001600160f81b0390921691909117905580610cbc6124b1565b60020180546001600160f81b0316600160f81b9215159290920291909117905560408051338152841515602082015283151581830152821515606082015290517f66b162f85d23ed59903ee9c03df5a009eb949fb229d6f4ffb235806f6663bbd69181900360800190a1505050565b5f5f5f610d366124b1565b54600160a01b900460ff16610d496124b1565b60030154600160f81b900460ff16610d5f6124b1565b600201601f9054906101000a900460ff16925092509250909192565b5f610d84612833565b610dcb610d8f6124b1565b600201601f9054906101000a900460ff16156040518060400160405280600c81526020016b706175736564206d696e747360a01b815250612825565b604051633752120b60e01b81526001600160a01b037f00000000000000000000000077703ae126b971c9946d562f41dd47071da00777811660048301527f0000000000000000000000005c9584c0b5fe561fdc041b29878113d446d87e801660248201526044810183905273eccc198b53afecb007234d5344e9b965d180c63c90633752120b90606401602060405180830381865af4158015610e70573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e9491906149de565b9050610eca5f198214156040518060400160405280600e81526020016d185b1c9958591e481b5a5b9d195960921b815250612825565b610ed46001600855565b919050565b5f610ee26124b1565b60020154600160801b90046bffffffffffffffffffffffff16919050565b5f610f096124b1565b60020154600160e01b900462ffffff16919050565b610f26612833565b610f83610f316124b1565b6004015461ffff16610f496080860160608701614a04565b61ffff16101560405180604001604052806016815260200175696e73756666696369656e74207769746e657373657360501b815250612825565b610fce610f9e610f916124b1565b6006015460208601351490565b604051806040016040528060128152602001710d2dcecc2d8d2c840e4c2c8dedc40d0c2e6d60731b815250612825565b604051633686b53f60e11b81525f906001600160a01b037f00000000000000000000000077703ae126b971c9946d562f41dd47071da007771690636d0d6a7e9061102090879087908790600401614a88565b5f604051808303815f875af115801561103b573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526110629190810190614c98565b6040516367c4440f60e01b815290915073eccc198b53afecb007234d5344e9b965d180c63c906367c4440f9061109c908490600401614dee565b602060405180830381865af49250505080156110d5575060408051601f3d908101601f191682019092526110d291810190614e70565b60015b611148576110e1614e8b565b806308c379a00361110f57506110f5614ea3565b806111005750611111565b6111098161285d565b506111f8565b505b3d80801561113a576040519150601f19603f3d011682016040523d82523d5f602084013e61113f565b606091505b50611109612894565b604080830151608084015182516001600160401b038086168252909116602082015290917f8a01b18bdca3d556adc5e6a85f562b83d2c1933f166e93421ff507cfccb09a07910160405180910390a2806111a06124b1565b60020160086101000a8154816001600160401b0302191690836001600160401b0316021790555081608001516111d46124b1565b600201805467ffffffffffffffff19166001600160401b0392909216919091179055505b506112036001600855565b505050565b611213610c266124b1565b6112c460036112256020840184614a04565b61ffff161015801561124b575060326112446040840160208501614a04565b61ffff1611155b80156112735750630bebc2006112676060840160408501614f25565b6001600160401b031610155b801561129657506203345061128e6080840160608501614f50565b62ffffff1610155b6040518060400160405280601081526020016f696e76616c69642073657474696e677360801b815250612825565b806112cd6124b1565b6004016112038282614f6b565b60606112e46124b1565b600501805480602002602001604051908101604052809291908181526020015f905b828210156113ae578382905f5260205f20018054611323906149ac565b80601f016020809104026020016040519081016040528092919081815260200182805461134f906149ac565b801561139a5780601f106113715761010080835404028352916020019161139a565b820191905f5260205f20905b81548152906001019060200180831161137d57829003601f168201915b505050505081526020019060010190611306565b50505050905090565b5f6113c06124b1565b5f92835260070160205250604090205490565b6001600160a01b0381165f908152600760205260408120546109f0565b5f6060805f5f5f60606114016128ca565b6114096128f7565b604080515f80825260208201909252600f60f81b9b939a50919850469750309650945092509050565b604080516080810182525f80825260208201819052918101829052606081019190915261145d6124b1565b604080516080810182526004929092015461ffff808216845262010000820416602084015264010000000081046001600160401b031691830191909152600160601b900462ffffff166060820152919050565b5f6114f86114bc6124b1565b600301601f9054906101000a900460ff16156040518060400160405280600c81526020016b706175736564206275726e7360a01b815250612825565b6115406001600160401b03851661150e3361062c565b1015604051806040016040528060128152602001716e6f7420656e6f7567682062616c616e636560701b815250612825565b5f6115496124b1565b6002015460408051808201909152601781527f63616e6e6f7420756e777261702074686174206d75636800000000000000000060208201526001600160401b03600160401b909204821692506115a491871683101590612825565b6115f3633b9aca00866001600160401b031610156040518060400160405280601981526020017f63616e6e6f7420756e777261702074686174206c6974746c6500000000000000815250612825565b5f61163785858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250504660011491506129249050565b90506116cb7f70fa10e5bf7a5778ff7a34ae40e9929bb09930b30000000000000000000000006001600160601b03199081169083161415801561169c575061169a6116806124b1565b6003015460601b6001600160601b03199081169084161490565b155b604051806040016040528060118152602001701a5b9d985b1a59081c9958da5c1a595b9d607a1b815250612825565b6116d58683615030565b6116dd6124b1565b60020180546fffffffffffffffff00000000000000001916600160401b6001600160401b039384160217905561171690339088166126c5565b61171e6124b1565b6003018054601c9061173b90600160e01b900462ffffff1661504f565b91906101000a81548162ffffff021916908362ffffff160217905562ffffff169250856117666124b1565b6003018054601490611789908490600160a01b90046001600160401b0316615071565b92506101000a8154816001600160401b0302191690836001600160401b031602179055506117b43390565b6001600160a01b03167f0ac1547df8510f30b54430e0e4e1e93ce326490aec9cebf0f78b8faa55dc1ded868689876040516117f29493929190615090565b60405180910390a250509392505050565b606060048054610a05906149ac565b61181d610c266124b1565b60408051808201909152600b81526a7a65726f2062726964676560a81b6020820152611855906001600160a01b038316151590612825565b7f9d20a3013144fe0ad1f61c921e224f9c4788418e3af6670991d30c5b6804c4843361187f6124b1565b54604080516001600160a01b03938416815291831660208301529184168183015290519081900360600190a1806118b46124b1565b80546001600160a01b0319166001600160a01b039290921691909117905550565b6118e0610c266124b1565b60408051808201909152600c81526b3d32b9379031bab930ba37b960a11b6020820152611919906001600160a01b038316151590612825565b806001600160a01b031661192b6124b1565b600101546040516001600160a01b03909116907fc63757acc12b39558fe5b88c5393e4b0353ffddaae3a2d07e121a8fc62d39c37905f90a38061196c6124b1565b60010180546001600160a01b0319166001600160a01b039290921691909117905550565b5f5f61199a6124b1565b5f848152600791909101602052604081205491508190036119bd57505f92915050565b5f1981036119ce5750600392915050565b6001604051631bc1eaf360e21b8152600481018390527f00000000000000000000000077703ae126b971c9946d562f41dd47071da007776001600160a01b031690636f07abcc90602401602060405180830381865afa158015611a33573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a5791906150bf565b6006811115611a6857611a686145c5565b14611a74576002611a77565b60015b9392505050565b50919050565b5f33610a93818585612668565b6060816001600160401b03811115611aab57611aab6147d1565b604051908082528060200260200182016040528015611ad4578160200160208202803683370190505b5090505f5b82811015611b4b57611b02848483818110611af657611af66150dd565b90506020020135611990565b828281518110611b1457611b146150dd565b60200260200101906003811115611b2d57611b2d6145c5565b90816003811115611b4057611b406145c5565b905250600101611ad9565b5092915050565b60607f00000000000000000000000077703ae126b971c9946d562f41dd47071da007776001600160a01b0316637b1039996040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bb0573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611bd491906150f1565b6001600160a01b0316638a227764611bea6124b1565b600601546040518263ffffffff1660e01b8152600401611c0c91815260200190565b5f60405180830381865afa158015611c26573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610ae7919081019061510c565b6060610ae76001600160601b03197f70fa10e5bf7a5778ff7a34ae40e9929bb09930b300000000000000000000000016466001146124d5565b611c91610c266124b1565b611ccf82828080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506129d592505050565b5050565b83421115611cfc5760405163313c898160e11b8152600481018590526024015b60405180910390fd5b5f7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9888888611d478c6001600160a01b03165f90815260076020526040902080546001810190915590565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040528051906020012090505f611da182612b7f565b90505f611db082878787612bab565b9050896001600160a01b0316816001600160a01b031614611df7576040516325c0072360e11b81526001600160a01b0380831660048301528b166024820152604401611cf3565b611e028a8a8a6124a4565b50505050505050505050565b611e16612833565b611e717f00000000000000000000000077703ae126b971c9946d562f41dd47071da007776001600160a01b031633146040518060400160405280600e81526020016d696e76616c6964206f7261636c6560901b815250612825565b604051635d933eb760e01b815273eccc198b53afecb007234d5344e9b965d180c63c90635d933eb790611eac9086908690869060040161513d565b5f60405180830381865af4925050508015611ee857506040513d5f823e601f3d908101601f19168201604052611ee59190810190615156565b60015b611f5b57611ef4614e8b565b806308c379a003611f225750611f08614ea3565b80611f135750611f24565b611f1c8161285d565b50612015565b505b3d808015611f4d576040519150601f19603f3d011682016040523d82523d5f602084013e611f52565b606091505b50611f1c612894565b611fb77f162f79432c9f1c2ad59cffb77a1a8c184062442a0ca347b39c4e65fb114d47758580519060200120146040518060400160405280601181526020017034b73b30b634b21031bab9ba37b234b0b760791b815250612825565b611fca82826001600160401b03166125b8565b816001600160a01b03167fbae099c209765c02c309f0fac06aec3ac516a3cc60d09f1a3da4b52d673d28a3848388604051612007939291906151ea565b60405180910390a250505050505b6112036001600855565b5f6109f07f00000000000000000000000077703ae126b971c9946d562f41dd47071da0077783612bd7565b5f6120536124b1565b600101546001600160a01b0316919050565b5f61206e6124b1565b546001600160a01b0316919050565b612088610c266124b1565b6120c05f82511160405180604001604052806013815260200172656d707479207270632070726f76696465727360681b815250612825565b806120c96124b1565b60050190805190602001906120df9291906141b1565b506120f8816120f360014614610ad06124b1565b612cdd565b7f3847d0a1ed317f0f886923b234ef1259a67db2f4a0020e85eb0e5b36abf3cae03382604051612129929190615217565b60405180910390a150565b5f61213d612dee565b805490915060ff600160401b82041615906001600160401b03165f811580156121635750825b90505f826001600160401b0316600114801561217e5750303b155b90508115801561218c575080155b156121aa5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156121d457845460ff60401b1916600160401b1785555b876121dd6124b1565b60010180546001600160a01b0319166001600160a01b03928316179055604051908916905f907fc63757acc12b39558fe5b88c5393e4b0353ffddaae3a2d07e121a8fc62d39c37908290a36028602160991b016122386124b1565b80546001600160a01b0319166001600160a01b039290921691909117905560408051608081019091528046600114612271576003612274565b600c5b61ffff16815260056020820152630bebc20060408201526203345060609091015261229d6124b1565b8151600491909101805460208085015160408087015160609097015162ffffff16600160601b0262ffffff60601b196001600160401b0390981664010000000002979097166effffffffffffffffffffff000000001961ffff938416620100000263ffffffff199096169390971692909217939093179490941693909317939093179055815160018082528184019093525f92909182015b60608152602001906001900390816123355790505090506001461461238f576040518060400160405280601d81526020017f68747470733a2f2f7270632d746573746e65742e7769746e65742e696f0000008152506123c6565b6040518060400160405280601881526020017f68747470733a2f2f7270632d30312e7769746e65742e696f00000000000000008152505b815f815181106123d8576123d86150dd565b6020026020010181905250806123ec6124b1565b60050190805190602001906124029291906141b1565b5061244188888080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506129d592505050565b50831561248857845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b5f61249b6124b1565b60060154905090565b6112038383836001612e16565b7f6116473658e87b023e7f215d122c0048f3d7a669d8df94a5565f0c95871c580090565b6060611a778360601c8361250557604051806040016040528060048152602001631d1dda5d60e21b815250612ee8565b604051806040016040528060038152602001621dda5d60ea1b815250612ee8565b6125736125316124b1565b54604080518082019091526013815272756e617574686f72697a65642062726964676560681b60208201526001600160a01b0384811692169190911490612825565b6125b561257e6124b1565b5460408051808201909152600d81526c7061757365642062726964676560981b6020820152600160a01b90910460ff161590612825565b50565b6001600160a01b0382166125e15760405163ec442f0560e01b81525f6004820152602401611cf3565b611ccf5f8383612f1c565b6001600160a01b038381165f908152600160209081526040808320938616835292905220545f19811015612662578181101561265457604051637dc7a0d960e11b81526001600160a01b03841660048201526024810182905260448101839052606401611cf3565b61266284848484035f612e16565b50505050565b6001600160a01b03831661269157604051634b637e8f60e11b81525f6004820152602401611cf3565b6001600160a01b0382166126ba5760405163ec442f0560e01b81525f6004820152602401611cf3565b611203838383612f1c565b6001600160a01b0382166126ee57604051634b637e8f60e11b81525f6004820152602401611cf3565b611ccf825f83612f1c565b5f306001600160a01b037f000000000000000000000000cafe5de18756817d98f4603f6828397406d4cafe1614801561275157507f000000000000000000000000000000000000000000000000000000000000000146145b1561277b57507f08e1e3a649641b7e6ba6228d0c7379f1fa60b7e3eee2c7e0cc309a975103e11c90565b610ae7604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527fa51b04f7b61713ec5f80ea1d85243b3c6ec21caca925fa934cc35abd7d86dab8918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a08201525f9060c00160405160208183030381529060405280519060200120905090565b90565b81611ccf57611ccf8161285d565b60026008540361285657604051633ee5aeb560e01b815260040160405180910390fd5b6002600855565b8060405160200161286e9190615251565b60408051601f198184030181529082905262461bcd60e51b8252611cf3916004016142bc565b6128c8604051806040016040528060138152602001723ab73430b7323632b21032bc31b2b83a34b7b760691b81525061285d565b565b6060610ae77f5769746e657400000000000000000000000000000000000000000000000000066005613042565b6060610ae77f31000000000000000000000000000000000000000000000000000000000000016006613042565b5f8161293157602b612934565b602a5b60ff1683511461297f5760405162461bcd60e51b8152602060048201526016602482015275084cac6d066647440d2dcecc2d8d2c840d8cadccee8d60531b6044820152606401611cf3565b6129cb83836129aa57604051806040016040528060048152602001631d1dda5d60e21b8152506130eb565b604051806040016040528060038152602001621dda5d60ea1b8152506130eb565b60601b9392505050565b612a377f162f79432c9f1c2ad59cffb77a1a8c184062442a0ca347b39c4e65fb114d477582805190602001201415604051806040016040528060168152602001753ab730b1b1b2b83a30b13632903ab73bb930b83832b960511b815250612825565b7f844058b2a3856451d27b99dee44bd7cecf3ce6104d17b7f59ab652d3ae03a5d13382604051612a68929190615272565b60405180910390a1612a7d8160014614612924565b612a856124b1565b60030180546001600160a01b03191660609290921c9190911790556125b5612aab6124b1565b600501805480602002602001604051908101604052809291908181526020015f905b82821015612b75578382905f5260205f20018054612aea906149ac565b80601f0160208091040260200160405190810160405280929190818152602001828054612b16906149ac565b8015612b615780601f10612b3857610100808354040283529160200191612b61565b820191905f5260205f20905b815481529060010190602001808311612b4457829003601f168201915b505050505081526020019060010190612acd565b5050505082612cdd565b5f6109f0612b8b6126f9565b8360405161190160f01b8152600281019290925260228201526042902090565b5f5f5f5f612bbb88888888613169565b925092509250612bcb8282613231565b50909695505050505050565b5f806001600160a01b0384166305e742ef847f6116473658e87b023e7f215d122c0048f3d7a669d8df94a5565f0c95871c580060049081015460405160e085901b6001600160e01b031916815291820192909252600160601b90910462ffffff166024820152604401602060405180830381865afa158015612c5b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c7f91906149de565b905060647f6116473658e87b023e7f215d122c0048f3d7a669d8df94a5565f0c95871c580060040154612cbd9062010000900461ffff166064615295565b612ccb9061ffff16836152af565b612cd591906152c6565b949350505050565b604080516002808252606082019092525f91816020015b6060815260200190600190039081612cf4579050509050612d13611c4d565b815f81518110612d2557612d256150dd565b60200260200101819052508181600181518110612d4457612d446150dd565b6020908102919091010152604051633c389c6f60e21b81526001600160a01b037f0000000000000000000000008147664fbd1571e3105e942527096f8bf5bf6264169063f0e271bc90612d9d90849087906004016152e5565b6020604051808303815f875af1158015612db9573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612ddd91906149de565b612de56124b1565b60060155505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006109f0565b6001600160a01b038416612e3f5760405163e602df0560e01b81525f6004820152602401611cf3565b6001600160a01b038316612e6857604051634a1406b160e11b81525f6004820152602401611cf3565b6001600160a01b038085165f908152600160209081526040808320938716835292905220829055801561266257826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92584604051612eda91815260200190565b60405180910390a350505050565b604051606083811b6001600160601b031916602083015290611a7790603401604051602081830303815290604052836132e9565b6001600160a01b038316612f46578060025f828254612f3b9190615309565b90915550612fb69050565b6001600160a01b0383165f9081526020819052604090205481811015612f985760405163391434e360e21b81526001600160a01b03851660048201526024810182905260448101839052606401611cf3565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b038216612fd257600280548290039055612ff0565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161303591815260200190565b60405180910390a3505050565b606060ff831461305c5761305583613335565b90506109f0565b818054613068906149ac565b80601f0160208091040260200160405190810160405280929190818152602001828054613094906149ac565b80156130df5780601f106130b6576101008083540402835291602001916130df565b820191905f5260205f20905b8154815290600101906020018083116130c257829003601f168201915b505050505090506109f0565b5f5f5f61311885604051602001613102919061531c565b6040516020818303038152906040526001613372565b9150915061314584604051602001613130919061531c565b604051602081830303815290604052836136fc565b5f61315482600560085f613750565b905061315f8161375e565b9695505050505050565b5f80807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08411156131a257505f91506003905082613227565b604080515f808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa1580156131f3573d5f5f3e3d5ffd5b5050604051601f1901519150506001600160a01b03811661321e57505f925060019150829050613227565b92505f91508190505b9450945094915050565b5f826003811115613244576132446145c5565b0361324d575050565b6001826003811115613261576132616145c5565b0361327f5760405163f645eedf60e01b815260040160405180910390fd5b6002826003811115613293576132936145c5565b036132b45760405163fce698f760e01b815260048101829052602401611cf3565b60038260038111156132c8576132c86145c5565b03611ccf576040516335e2f38360e21b815260048101829052602401611cf3565b60605f826040516020016132fd919061531c565b60405160208183030381529060405290505f61331e856008600560016137b8565b905061332c8282600161387e565b95945050505050565b60605f61334183613b00565b6040805160208082528183019092529192505f91906020820181803683375050509182525060208101929092525090565b6060805f605a855111156133c85760405162461bcd60e51b815260206004820152601d60248201527f4265636833323a20696e76616c696420737472696e67206c656e6774680000006044820152606401611cf3565b5f5b85518110156134c3575f8682815181106133e6576133e66150dd565b016020015160f81c9050602181108015906134055750607e8160ff1611155b6134465760405162461bcd60e51b81526020600482015260126024820152712132b1b419991d103bb937b7339031b430b960711b6044820152606401611cf3565b60301960ff8216016134ba5782158015613461575060018210155b8015613471575086518260070111155b6134b65760405162461bcd60e51b81526020600482015260166024820152754265636833323a2077726f6e6720706f73206f66203160501b6044820152606401611cf3565b8192505b506001016133ca565b50806001600160401b038111156134dc576134dc6147d1565b6040519080825280601f01601f191660200182016040528015613506576020820181803683370190505b5092505f5b8181101561356057858181518110613525576135256150dd565b602001015160f81c60f81b848281518110613542576135426150dd565b60200101906001600160f81b03191690815f1a90535060010161350b565b50600181865103036001600160401b0381111561357f5761357f6147d1565b6040519080825280602002602001820160405280156135a8578160200160208202803683370190505b5091505f5b8251811015613699575f604051806101200160405280610100815260200161547f610100913987848401600101815181106135ea576135ea6150dd565b0160200151815160f89190911c908110613606576136066150dd565b01602001516001600160f81b031990811691508190036136685760405162461bcd60e51b815260206004820152601c60248201527f4265636833323a2062797465206e6f7420696e20616c706861626574000000006044820152606401611cf3565b8060f81c84838151811061367e5761367e6150dd565b60ff90921660209283029190910190910152506001016135ad565b506136a5838386613b27565b6136ea5760405162461bcd60e51b81526020600482015260166024820152754265636833323a2077726f6e6720636865636b73756d60501b6044820152606401611cf3565b50805160051901815290939092509050565b8080519060200120828051906020012014611ccf5760405162461bcd60e51b8152602060048201526014602482015273084cac6d066647440d0e4e040dad2e6dac2e8c6d60631b6044820152606401611cf3565b606061332c85858585613c51565b5f81516014146137b05760405162461bcd60e51b815260206004820152601b60248201527f4265636833323a20696e76616c69642064617461206c656e67746800000000006044820152606401611cf3565b506014015190565b60605f85516001600160401b038111156137d4576137d46147d1565b6040519080825280602002602001820160405280156137fd578160200160208202803683370190505b5090505f5b81518163ffffffff16101561387157868163ffffffff1681518110613829576138296150dd565b602001015160f81c60f81b60f81c828263ffffffff168151811061384f5761384f6150dd565b60ff9092166020928302919091019091015261386a81615327565b9050613802565b5061315f81868686613c51565b60605f61388c858585613e1e565b90505f81518551875101016001016001600160401b038111156138b1576138b16147d1565b6040519080825280601f01601f1916602001820160405280156138db576020820181803683370190505b5090505f5b8651811015613936578681815181106138fb576138fb6150dd565b602001015160f81c60f81b828281518110613918576139186150dd565b60200101906001600160f81b03191690815f1a9053506001016138e0565b50603160f81b8187518151811061394f5761394f6150dd565b60200101906001600160f81b03191690815f1a90535085516001015f5b8651811015613a30575f878281518110613988576139886150dd565b01602090810151604080518082019091528281525f51602061557f5f395f51905f529083015260f81c9150811015613a27576040518060400160405280602081526020015f51602061557f5f395f51905f528152508160ff16815181106139f1576139f16150dd565b602001015160f81c60f81b8484840181518110613a1057613a106150dd565b60200101906001600160f81b03191690815f1a9053505b5060010161396c565b508551015f5b8351811015612bcb575f848281518110613a5257613a526150dd565b602002602001015190506040518060400160405280602081526020015f51602061557f5f395f51905f52815250518160ff161015613af7576040518060400160405280602081526020015f51602061557f5f395f51905f528152508160ff1681518110613ac157613ac16150dd565b602001015160f81c60f81b8484840181518110613ae057613ae06150dd565b60200101906001600160f81b03191690815f1a9053505b50600101613a36565b5f60ff8216601f8111156109f057604051632cd44ac360e21b815260040160405180910390fd5b5f5f613b3285613fbd565b90505f84518251016001600160401b03811115613b5157613b516147d1565b604051908082528060200260200182016040528015613b7a578160200160208202803683370190505b5090505f5b8251811015613bd457828181518110613b9a57613b9a6150dd565b602002602001015160ff16828281518110613bb757613bb76150dd565b63ffffffff90921660209283029190910190910152600101613b7f565b505f5b8551811015613c2f57858181518110613bf257613bf26150dd565b602002602001015160ff16828451830181518110613c1257613c126150dd565b63ffffffff90921660209283029190910190910152600101613bd7565b508363ffffffff16613c40826140b7565b63ffffffff16149695505050505050565b60605f8080613c63600180881b615342565b90505f5b8851811015613d57575f898281518110613c8357613c836150dd565b6020908102919091010151905060ff8082168a1c1615613d045760405162461bcd60e51b815260206004820152603660248201527f4265636833323a2076616c7565206d757374206265206e6f6e2d6e6567617469604482015275766520616e642066697420696e2066726f6d6269747360501b6064820152608401611cf3565b93881b60ff85161793928801925b878410613d4e576040519388900393613d3890879087871c861660f81b90602001615355565b6040516020818303038152906040529550613d12565b50600101613c67565b508415613d9f578115613d9a578381613d708489615342565b85901b1660f81b604051602001613d88929190615355565b60405160208183030381529060405293505b613e13565b86821080613db9575080613db38388615342565b84901b16155b613e135760405162461bcd60e51b815260206004820152602560248201527f4265636833323a20696e76616c69642070616464696e67206f722076616c75656044820152642073697a6560d81b6064820152608401611cf3565b505050949350505050565b60605f613e2a85613fbd565b90505f84518251016006016001600160401b03811115613e4c57613e4c6147d1565b604051908082528060200260200182016040528015613e75578160200160208202803683370190505b5090505f5b8551835101811015613f38578251811015613edd57828181518110613ea157613ea16150dd565b602002602001015160ff16828281518110613ebe57613ebe6150dd565b602002602001019063ffffffff16908163ffffffff1681525050613f30565b858351820381518110613ef257613ef26150dd565b602001015160f81c60f81b60f81c60ff16828281518110613f1557613f156150dd565b602002602001019063ffffffff16908163ffffffff16815250505b600101613e7a565b5060408051600680825260e08201909252906020820160c0803683370190505092505f84613f65836140b7565b1890505f5b6006811015613fb257806005036005028263ffffffff16901c601f16858281518110613f9857613f986150dd565b60ff90921660209283029190910190910152600101613f6a565b505050509392505050565b606081518251016001016001600160401b03811115613fde57613fde6147d1565b604051908082528060200260200182016040528015614007578160200160208202803683370190505b5090505f5b8251811015611a7e576005838281518110614029576140296150dd565b602001015160f81c60f81b60f81c60ff16901c82828151811061404e5761404e6150dd565b602002602001019060ff16908160ff1681525050828181518110614074576140746150dd565b602001015160f81c60f81b60f81c601f1682845183016001018151811061409d5761409d6150dd565b60ff9092166020928302919091019091015260010161400c565b6040805160a081018252633b6a57b281526326508e6d6020820152631ea119fa91810191909152633d4233dd6060820152632a1462b360808201525f90600190825b84518163ffffffff1610156141a8575f60198463ffffffff16901c9050858263ffffffff168151811061412e5761412e6150dd565b60200260200101516005856301ffffff1663ffffffff16901b1893505f5f90505b60058163ffffffff16101561419e57600163ffffffff8381169083161c8116900361419657838163ffffffff166005811061418c5761418c6150dd565b6020020151851894505b60010161414f565b50506001016140f9565b50909392505050565b828054828255905f5260205f209081019282156141f5579160200282015b828111156141f557825182906141e590826153c4565b50916020019190600101906141cf565b50614201929150614205565b5090565b80821115614201575f6142188282614221565b50600101614205565b50805461422d906149ac565b5f825580601f1061423c575050565b601f0160209004905f5260205f20908101906125b591905b80821115614201575f8155600101614254565b5f60208284031215614277575f5ffd5b81356001600160e01b031981168114611a77575f5ffd5b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f611a77602083018461428e565b6001600160a01b03811681146125b5575f5ffd5b5f5f604083850312156142f3575f5ffd5b82356142fe816142ce565b946020939093013593505050565b5f5f5f6060848603121561431e575f5ffd5b8335614329816142ce565b92506020840135614339816142ce565b929592945050506040919091013590565b5f6020828403121561435a575f5ffd5b8135611a77816142ce565b80358015158114610ed4575f5ffd5b5f5f5f60608486031215614386575f5ffd5b61438f84614365565b925061439d60208501614365565b91506143ab60408501614365565b90509250925092565b5f602082840312156143c4575f5ffd5b5035919050565b5f5f83601f8401126143db575f5ffd5b5081356001600160401b038111156143f1575f5ffd5b602083019150836020828501011115614408575f5ffd5b9250929050565b5f5f5f60408486031215614421575f5ffd5b83356001600160401b03811115614436575f5ffd5b840160e08187031215614447575f5ffd5b925060208401356001600160401b03811115614461575f5ffd5b61446d868287016143cb565b9497909650939450505050565b5f608082840312801561448b575f5ffd5b509092915050565b5f82825180855260208501945060208160051b830101602085015f5b83811015612bcb57601f198584030188526144cb83835161428e565b60209889019890935091909101906001016144af565b602081525f611a776020830184614493565b60ff60f81b8816815260e060208201525f61451160e083018961428e565b8281036040840152614523818961428e565b606084018890526001600160a01b038716608085015260a0840186905283810360c0850152845180825260208087019350909101905f5b8181101561457857835183526020938401939092019160010161455a565b50909b9a5050505050505050505050565b6001600160401b03811681146125b5575f5ffd5b8035610ed481614589565b5f5f5f604084860312156145ba575f5ffd5b833561444781614589565b634e487b7160e01b5f52602160045260245ffd5b600481106145e9576145e96145c5565b9052565b602081016109f082846145d9565b5f5f6020838503121561460c575f5ffd5b82356001600160401b03811115614621575f5ffd5b8301601f81018513614631575f5ffd5b80356001600160401b03811115614646575f5ffd5b8560208260051b840101111561465a575f5ffd5b6020919091019590945092505050565b602080825282518282018190525f918401906040840190835b818110156146a9576146968385516145d9565b6020938401939290920191600101614683565b509095945050505050565b5f5f602083850312156146c5575f5ffd5b82356001600160401b038111156146da575f5ffd5b6146e6858286016143cb565b90969095509350505050565b60ff811681146125b5575f5ffd5b5f5f5f5f5f5f5f60e0888a031215614716575f5ffd5b8735614721816142ce565b96506020880135614731816142ce565b95506040880135945060608801359350608088013561474f816146f2565b9699959850939692959460a0840135945060c09093013592915050565b5f5f5f6040848603121561477e575f5ffd5b8335925060208401356001600160401b03811115614461575f5ffd5b5f5f604083850312156147ab575f5ffd5b82356147b6816142ce565b915060208301356147c6816142ce565b809150509250929050565b634e487b7160e01b5f52604160045260245ffd5b60c081018181106001600160401b0382111715614804576148046147d1565b60405250565b604081018181106001600160401b0382111715614804576148046147d1565b601f8201601f191681016001600160401b038111828210171561484e5761484e6147d1565b6040525050565b5f6001600160401b0382111561486d5761486d6147d1565b50601f01601f191660200190565b5f6020828403121561488b575f5ffd5b81356001600160401b038111156148a0575f5ffd5b8201601f810184136148b0575f5ffd5b80356001600160401b038111156148c9576148c96147d1565b8060051b6040516148dd6020830182614829565b9182526020818401810192908101878411156148f7575f5ffd5b6020850192505b838310156149845782356001600160401b0381111561491b575f5ffd5b8501603f8101891361492b575f5ffd5b602081013561493981614855565b6040516149468282614829565b8281526040848401018c101561495a575f5ffd5b826040850160208301375f60208483010152808552505050506020810190506020830192506148fe565b509695505050505050565b5f5f5f604084860312156149a1575f5ffd5b8335614447816142ce565b600181811c908216806149c057607f821691505b602082108103611a7e57634e487b7160e01b5f52602260045260245ffd5b5f602082840312156149ee575f5ffd5b5051919050565b61ffff811681146125b5575f5ffd5b5f60208284031215614a14575f5ffd5b8135611a77816149f5565b5f5f8335601e19843603018112614a34575f5ffd5b83016020810192503590506001600160401b03811115614a52575f5ffd5b803603821315614408575f5ffd5b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b6040808252843582820152602085013560608301525f90850135614aab816149f5565b61ffff1660808301526060850135614ac2816149f5565b61ffff1660a08301526080850135614ad981614589565b6001600160401b031660c0830152614af360a0860161459d565b6001600160401b031660e0830152614b0e60c0860186614a1f565b60e0610100850152614b2561012085018284614a60565b915050828103602084015261315f818587614a60565b805160148110610ed4575f5ffd5b8051610ed481614589565b5f82601f830112614b63575f5ffd5b8151602083015f614b7383614855565b604051614b808282614829565b809250848152878585011115614b94575f5ffd5b8484602083015e5f60208683010152809550505050505092915050565b8051610ed4816146f2565b5f60c08284031215614bcc575f5ffd5b604051614bd8816147e5565b80915082516001600160401b03811115614bf0575f5ffd5b830160408186031215614c01575f5ffd5b604051614c0d8161480a565b81516001600160401b03811115614c22575f5ffd5b614c2e87828501614b54565b825250602091820151828201528252614c48908401614bb1565b6020820152614c5960408401614bb1565b6040820152614c6a60608401614bb1565b6060820152614c7b60808401614b49565b6080820152614c8c60a08401614b49565b60a08201525092915050565b5f60208284031215614ca8575f5ffd5b81516001600160401b03811115614cbd575f5ffd5b820160c08185031215614cce575f5ffd5b604051614cda816147e5565b81516101008110614ce9575f5ffd5b8152614cf760208301614b3b565b60208201526040828101519082015260608083015190820152614d1c60808301614b49565b608082015260a08201516001600160401b03811115614d39575f5ffd5b614d4586828501614bbc565b60a083015250949350505050565b5f815160c084528051604060c0860152614d7161010086018261428e565b9050602082015160e086015260ff602085015116602086015260408401519150614da0604086018360ff169052565b60608401519150614db6606086018360ff169052565b60808401519150614dd260808601836001600160401b03169052565b60a08401519150612cd560a08601836001600160401b03169052565b602081525f82516101008110614e0657614e066145c5565b80602084015250602083015160148110614e2257614e226145c5565b8060408401525060408301516060830152606083015160808301526080830151614e5760a08401826001600160401b03169052565b5060a083015160c080840152612cd560e0840182614d53565b5f60208284031215614e80575f5ffd5b8151611a7781614589565b5f60033d11156128225760045f5f3e505f5160e01c90565b5f60443d1015614eb05790565b6040513d600319016004823e80513d60248201116001600160401b0382111715614ed957505090565b80820180516001600160401b03811115614ef4575050505090565b3d8401600319018282016020011115614f0e575050505090565b614f1d60208285010185614829565b509392505050565b5f60208284031215614f35575f5ffd5b8135611a7781614589565b62ffffff811681146125b5575f5ffd5b5f60208284031215614f60575f5ffd5b8135611a7781614f40565b8135614f76816149f5565b61ffff8116905081548161ffff1982161783556020840135614f97816149f5565b63ffff00008160101b169050808363ffffffff198416171784556040850135614fbf81614589565b6bffffffffffffffff000000008160201b16846bffffffffffffffffffffffff198516178317178555505050505f6060830135614ffb81614f40565b825462ffffff60601b191660609190911b62ffffff60601b16179091555050565b634e487b7160e01b5f52601160045260245ffd5b6001600160401b0382811682821603908111156109f0576109f061501c565b5f62ffffff821662ffffff81036150685761506861501c565b60010192915050565b6001600160401b0381811683821601908111156109f0576109f061501c565b606081525f6150a3606083018688614a60565b6001600160401b03949094166020830152506040015292915050565b5f602082840312156150cf575f5ffd5b815160078110611a77575f5ffd5b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215615101575f5ffd5b8151611a77816142ce565b5f6020828403121561511c575f5ffd5b81516001600160401b03811115615131575f5ffd5b612cd584828501614b54565b838152604060208201525f61332c604083018486614a60565b5f5f5f5f5f60a0868803121561516a575f5ffd5b855160208701519095506001600160401b03811115615187575f5ffd5b61519388828901614b54565b94505060408601516001600160401b038111156151ae575f5ffd5b6151ba88828901614b54565b93505060608601516151cb816142ce565b60808701519092506151dc81614589565b809150509295509295909350565b606081525f6151fc606083018661428e565b6001600160401b039490941660208301525060400152919050565b6001600160a01b03831681526040602082018190525f90612cd590830184614493565b5f81518060208401855e5f93019283525090919050565b6c02bb4ba3732ba22a92199181d1609d1b81525f611a77600d83018461523a565b6001600160a01b03831681526040602082018190525f90612cd59083018461428e565b61ffff81811683821601908111156109f0576109f061501c565b80820281158282048414176109f0576109f061501c565b5f826152e057634e487b7160e01b5f52601260045260245ffd5b500490565b604081525f6152f76040830185614493565b828103602084015261332c8185614493565b808201808211156109f0576109f061501c565b5f611a77828461523a565b5f63ffffffff821663ffffffff81036150685761506861501c565b818103818111156109f0576109f061501c565b5f615360828561523a565b6001600160f81b03199390931683525050600101919050565b601f82111561120357805f5260205f20601f840160051c8101602085101561539e5750805b601f840160051c820191505b818110156153bd575f81556001016153aa565b5050505050565b81516001600160401b038111156153dd576153dd6147d1565b6153f1816153eb84546149ac565b84615379565b6020601f821160018114615423575f831561540c5750848201515b5f19600385901b1c1916600184901b1784556153bd565b5f84815260208120601f198516915b828110156154525787850151825560209485019460019092019101615432565b508482101561546f57868401515f19600387901b60f8161c191681555b50505050600190811b0190555056feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0fff0a1115141a1e0705ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1dff180d19090817ff12161f1b13ff010003100b1c0c0e060402ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff71707a7279397838676632747664773073336a6e35346b686365366d7561376ca264697066735822122018feb9d83e3537c88097834461bb31cd06f308bf27e4d9d55af35aaf3d6e204b64736f6c634300081e0033
Verified Source Code Partial Match
Compiler: v0.8.30+commit.73712a01
EVM: prague
Optimization: Yes (200 runs)
Initializable.sol 238 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reinitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Pointer to storage slot. Allows integrators to override it with a custom storage location.
*
* NOTE: Consider following the ERC-7201 formula to derive storage locations.
*/
function _initializableStorageSlot() internal pure virtual returns (bytes32) {
return INITIALIZABLE_STORAGE;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
bytes32 slot = _initializableStorageSlot();
assembly {
$.slot := slot
}
}
}
IERC165.sol 6 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC165.sol)
pragma solidity >=0.4.16;
import {IERC165} from "../utils/introspection/IERC165.sol";
IERC5267.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC5267.sol)
pragma solidity >=0.4.16;
interface IERC5267 {
/**
* @dev MAY be emitted to signal that the domain could have changed.
*/
event EIP712DomainChanged();
/**
* @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
* signature.
*/
function eip712Domain()
external
view
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
);
}
draft-IERC6093.sol 161 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/draft-IERC6093.sol)
pragma solidity >=0.8.4;
/**
* @dev Standard ERC-20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC-721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC-1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}
draft-IERC7802.sol 31 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/draft-IERC7802.sol)
pragma solidity >=0.6.2;
import {IERC165} from "./IERC165.sol";
/// @title IERC7802
/// @notice Defines the interface for crosschain ERC20 transfers.
interface IERC7802 is IERC165 {
/// @notice Emitted when a crosschain transfer mints tokens.
/// @param to Address of the account tokens are being minted for.
/// @param amount Amount of tokens minted.
/// @param sender Address of the caller (msg.sender) who invoked crosschainMint.
event CrosschainMint(address indexed to, uint256 amount, address indexed sender);
/// @notice Emitted when a crosschain transfer burns tokens.
/// @param from Address of the account tokens are being burned from.
/// @param amount Amount of tokens burned.
/// @param sender Address of the caller (msg.sender) who invoked crosschainBurn.
event CrosschainBurn(address indexed from, uint256 amount, address indexed sender);
/// @notice Mint tokens through a crosschain transfer.
/// @param _to Address to mint tokens to.
/// @param _amount Amount of tokens to mint.
function crosschainMint(address _to, uint256 _amount) external;
/// @notice Burn tokens through a crosschain transfer.
/// @param _from Address to burn tokens from.
/// @param _amount Amount of tokens to burn.
function crosschainBurn(address _from, uint256 _amount) external;
}
ERC20.sol 305 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.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}.
*
* 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 ERC-20
* applications.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* Both 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 returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual 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 returns (uint8) {
return 18;
}
/// @inheritdoc IERC20
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/// @inheritdoc IERC20
function balanceOf(address account) public view virtual 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 `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/// @inheritdoc IERC20
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` 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 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Skips emitting an {Approval} event indicating an allowance update. This is not
* required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
*
* 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 `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` 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.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` 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.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
*
* ```solidity
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner`'s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance < type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
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 value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` 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 value) external returns (bool);
}
ERC20Permit.sol 77 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/ERC20Permit.sol)
pragma solidity ^0.8.20;
import {IERC20Permit} from "./IERC20Permit.sol";
import {ERC20} from "../ERC20.sol";
import {ECDSA} from "../../../utils/cryptography/ECDSA.sol";
import {EIP712} from "../../../utils/cryptography/EIP712.sol";
import {Nonces} from "../../../utils/Nonces.sol";
/**
* @dev Implementation of the ERC-20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[ERC-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC-20 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.
*/
abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712, Nonces {
bytes32 private constant PERMIT_TYPEHASH =
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
/**
* @dev Permit deadline has expired.
*/
error ERC2612ExpiredSignature(uint256 deadline);
/**
* @dev Mismatched signature.
*/
error ERC2612InvalidSigner(address signer, address owner);
/**
* @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
*
* It's a good idea to use the same `name` that is defined as the ERC-20 token name.
*/
constructor(string memory name) EIP712(name, "1") {}
/// @inheritdoc IERC20Permit
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
if (block.timestamp > deadline) {
revert ERC2612ExpiredSignature(deadline);
}
bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));
bytes32 hash = _hashTypedDataV4(structHash);
address signer = ECDSA.recover(hash, v, r, s);
if (signer != owner) {
revert ERC2612InvalidSigner(signer, owner);
}
_approve(owner, spender, value);
}
/// @inheritdoc IERC20Permit
function nonces(address owner) public view virtual override(IERC20Permit, Nonces) returns (uint256) {
return super.nonces(owner);
}
/// @inheritdoc IERC20Permit
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view virtual returns (bytes32) {
return _domainSeparatorV4();
}
}
IERC20Metadata.sol 26 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity >=0.6.2;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC-20 standard.
*/
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);
}
IERC20Permit.sol 90 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[ERC-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC-20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
draft-ERC20Bridgeable.sol 51 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/draft-ERC20Bridgeable.sol)
pragma solidity ^0.8.20;
import {ERC20} from "../ERC20.sol";
import {ERC165, IERC165} from "../../../utils/introspection/ERC165.sol";
import {IERC7802} from "../../../interfaces/draft-IERC7802.sol";
/**
* @dev ERC20 extension that implements the standard token interface according to
* https://eips.ethereum.org/EIPS/eip-7802[ERC-7802].
*/
abstract contract ERC20Bridgeable is ERC20, ERC165, IERC7802 {
/// @dev Modifier to restrict access to the token bridge.
modifier onlyTokenBridge() {
// Token bridge should never be impersonated using a relayer/forwarder. Using msg.sender is preferable to
// _msgSender() for security reasons.
_checkTokenBridge(msg.sender);
_;
}
/// @inheritdoc ERC165
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return interfaceId == type(IERC7802).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC7802-crosschainMint}. Emits a {IERC7802-CrosschainMint} event.
*/
function crosschainMint(address to, uint256 value) public virtual override onlyTokenBridge {
_mint(to, value);
emit CrosschainMint(to, value, _msgSender());
}
/**
* @dev See {IERC7802-crosschainBurn}. Emits a {IERC7802-CrosschainBurn} event.
*/
function crosschainBurn(address from, uint256 value) public virtual override onlyTokenBridge {
_burn(from, value);
emit CrosschainBurn(from, value, _msgSender());
}
/**
* @dev Checks if the caller is a trusted token bridge. MUST revert otherwise.
*
* Developers should implement this function using an access control mechanism that allows
* customizing the list of allowed senders. Consider using {AccessControl} or {AccessManaged}.
*/
function _checkTokenBridge(address caller) internal virtual;
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
Nonces.sol 46 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Nonces.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides tracking nonces for addresses. Nonces will only increment.
*/
abstract contract Nonces {
/**
* @dev The nonce used for an `account` is not the expected current nonce.
*/
error InvalidAccountNonce(address account, uint256 currentNonce);
mapping(address account => uint256) private _nonces;
/**
* @dev Returns the next unused nonce for an address.
*/
function nonces(address owner) public view virtual returns (uint256) {
return _nonces[owner];
}
/**
* @dev Consumes a nonce.
*
* Returns the current value and increments nonce.
*/
function _useNonce(address owner) internal virtual returns (uint256) {
// For each account, the nonce has an initial value of 0, can only be incremented by one, and cannot be
// decremented or reset. This guarantees that the nonce never overflows.
unchecked {
// It is important to do x++ and not ++x here.
return _nonces[owner]++;
}
}
/**
* @dev Same as {_useNonce} but checking that `nonce` is the next valid for `owner`.
*/
function _useCheckedNonce(address owner, uint256 nonce) internal virtual {
uint256 current = _useNonce(owner);
if (nonce != current) {
revert InvalidAccountNonce(owner, current);
}
}
}
Panic.sol 57 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)
pragma solidity ^0.8.20;
/**
* @dev Helper library for emitting standardized panic codes.
*
* ```solidity
* contract Example {
* using Panic for uint256;
*
* // Use any of the declared internal constants
* function foo() { Panic.GENERIC.panic(); }
*
* // Alternatively
* function foo() { Panic.panic(Panic.GENERIC); }
* }
* ```
*
* Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
*
* _Available since v5.1._
*/
// slither-disable-next-line unused-state
library Panic {
/// @dev generic / unspecified error
uint256 internal constant GENERIC = 0x00;
/// @dev used by the assert() builtin
uint256 internal constant ASSERT = 0x01;
/// @dev arithmetic underflow or overflow
uint256 internal constant UNDER_OVERFLOW = 0x11;
/// @dev division or modulo by zero
uint256 internal constant DIVISION_BY_ZERO = 0x12;
/// @dev enum conversion error
uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
/// @dev invalid encoding in storage
uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
/// @dev empty array pop
uint256 internal constant EMPTY_ARRAY_POP = 0x31;
/// @dev array out of bounds access
uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
/// @dev resource error (too large allocation or too large array)
uint256 internal constant RESOURCE_ERROR = 0x41;
/// @dev calling invalid internal function
uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;
/// @dev Reverts with a panic code. Recommended to use with
/// the internal constants with predefined codes.
function panic(uint256 code) internal pure {
assembly ("memory-safe") {
mstore(0x00, 0x4e487b71)
mstore(0x20, code)
revert(0x1c, 0x24)
}
}
}
ReentrancyGuard.sol 87 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @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 EIP-1153 (transient storage) is available on the chain you're deploying at,
* consider using {ReentrancyGuardTransient} instead.
*
* 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;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
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
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// 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;
}
}
ShortStrings.sol 122 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/ShortStrings.sol)
pragma solidity ^0.8.20;
import {StorageSlot} from "./StorageSlot.sol";
// | string | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA |
// | length | 0x BB |
type ShortString is bytes32;
/**
* @dev This library provides functions to convert short memory strings
* into a `ShortString` type that can be used as an immutable variable.
*
* Strings of arbitrary length can be optimized using this library if
* they are short enough (up to 31 bytes) by packing them with their
* length (1 byte) in a single EVM word (32 bytes). Additionally, a
* fallback mechanism can be used for every other case.
*
* Usage example:
*
* ```solidity
* contract Named {
* using ShortStrings for *;
*
* ShortString private immutable _name;
* string private _nameFallback;
*
* constructor(string memory contractName) {
* _name = contractName.toShortStringWithFallback(_nameFallback);
* }
*
* function name() external view returns (string memory) {
* return _name.toStringWithFallback(_nameFallback);
* }
* }
* ```
*/
library ShortStrings {
// Used as an identifier for strings longer than 31 bytes.
bytes32 private constant FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;
error StringTooLong(string str);
error InvalidShortString();
/**
* @dev Encode a string of at most 31 chars into a `ShortString`.
*
* This will trigger a `StringTooLong` error is the input string is too long.
*/
function toShortString(string memory str) internal pure returns (ShortString) {
bytes memory bstr = bytes(str);
if (bstr.length > 31) {
revert StringTooLong(str);
}
return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
}
/**
* @dev Decode a `ShortString` back to a "normal" string.
*/
function toString(ShortString sstr) internal pure returns (string memory) {
uint256 len = byteLength(sstr);
// using `new string(len)` would work locally but is not memory safe.
string memory str = new string(32);
assembly ("memory-safe") {
mstore(str, len)
mstore(add(str, 0x20), sstr)
}
return str;
}
/**
* @dev Return the length of a `ShortString`.
*/
function byteLength(ShortString sstr) internal pure returns (uint256) {
uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
if (result > 31) {
revert InvalidShortString();
}
return result;
}
/**
* @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
*/
function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
if (bytes(value).length < 32) {
return toShortString(value);
} else {
StorageSlot.getStringSlot(store).value = value;
return ShortString.wrap(FALLBACK_SENTINEL);
}
}
/**
* @dev Decode a string that was encoded to `ShortString` or written to storage using {toShortStringWithFallback}.
*/
function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
return toString(value);
} else {
return store;
}
}
/**
* @dev Return the length of a string that was encoded to `ShortString` or written to storage using
* {toShortStringWithFallback}.
*
* WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
* actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
*/
function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
return byteLength(value);
} else {
return bytes(store).length;
}
}
}
StorageSlot.sol 143 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC-1967 implementation slot:
* ```solidity
* contract ERC1967 {
* // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* TIP: Consider using this library along with {SlotDerivation}.
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct Int256Slot {
int256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Int256Slot` with member `value` located at `slot`.
*/
function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
assembly ("memory-safe") {
r.slot := store.slot
}
}
/**
* @dev Returns a `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
assembly ("memory-safe") {
r.slot := store.slot
}
}
}
Strings.sol 490 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SafeCast} from "./math/SafeCast.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
using SafeCast for *;
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
uint256 private constant SPECIAL_CHARS_LOOKUP =
(1 << 0x08) | // backspace
(1 << 0x09) | // tab
(1 << 0x0a) | // newline
(1 << 0x0c) | // form feed
(1 << 0x0d) | // carriage return
(1 << 0x22) | // double quote
(1 << 0x5c); // backslash
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev The string being parsed contains characters that are not in scope of the given base.
*/
error StringsInvalidChar();
/**
* @dev The string being parsed is not a properly formatted address.
*/
error StringsInvalidAddressFormat();
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
assembly ("memory-safe") {
ptr := add(add(buffer, 0x20), length)
}
while (true) {
ptr--;
assembly ("memory-safe") {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its checksummed ASCII `string` hexadecimal
* representation, according to EIP-55.
*/
function toChecksumHexString(address addr) internal pure returns (string memory) {
bytes memory buffer = bytes(toHexString(addr));
// hash the hex part of buffer (skip length + 2 bytes, length 40)
uint256 hashValue;
assembly ("memory-safe") {
hashValue := shr(96, keccak256(add(buffer, 0x22), 40))
}
for (uint256 i = 41; i > 1; --i) {
// possible values for buffer[i] are 48 (0) to 57 (9) and 97 (a) to 102 (f)
if (hashValue & 0xf > 7 && uint8(buffer[i]) > 96) {
// case shift by xoring with 0x20
buffer[i] ^= 0x20;
}
hashValue >>= 4;
}
return string(buffer);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
/**
* @dev Parse a decimal string and returns the value as a `uint256`.
*
* Requirements:
* - The string must be formatted as `[0-9]*`
* - The result must fit into an `uint256` type
*/
function parseUint(string memory input) internal pure returns (uint256) {
return parseUint(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseUint-string} that parses a substring of `input` located between position `begin` (included) and
* `end` (excluded).
*
* Requirements:
* - The substring must be formatted as `[0-9]*`
* - The result must fit into an `uint256` type
*/
function parseUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {
(bool success, uint256 value) = tryParseUint(input, begin, end);
if (!success) revert StringsInvalidChar();
return value;
}
/**
* @dev Variant of {parseUint-string} that returns false if the parsing fails because of an invalid character.
*
* NOTE: This function will revert if the result does not fit in a `uint256`.
*/
function tryParseUint(string memory input) internal pure returns (bool success, uint256 value) {
return _tryParseUintUncheckedBounds(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseUint-string-uint256-uint256} that returns false if the parsing fails because of an invalid
* character.
*
* NOTE: This function will revert if the result does not fit in a `uint256`.
*/
function tryParseUint(
string memory input,
uint256 begin,
uint256 end
) internal pure returns (bool success, uint256 value) {
if (end > bytes(input).length || begin > end) return (false, 0);
return _tryParseUintUncheckedBounds(input, begin, end);
}
/**
* @dev Implementation of {tryParseUint-string-uint256-uint256} that does not check bounds. Caller should make sure that
* `begin <= end <= input.length`. Other inputs would result in undefined behavior.
*/
function _tryParseUintUncheckedBounds(
string memory input,
uint256 begin,
uint256 end
) private pure returns (bool success, uint256 value) {
bytes memory buffer = bytes(input);
uint256 result = 0;
for (uint256 i = begin; i < end; ++i) {
uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));
if (chr > 9) return (false, 0);
result *= 10;
result += chr;
}
return (true, result);
}
/**
* @dev Parse a decimal string and returns the value as a `int256`.
*
* Requirements:
* - The string must be formatted as `[-+]?[0-9]*`
* - The result must fit in an `int256` type.
*/
function parseInt(string memory input) internal pure returns (int256) {
return parseInt(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseInt-string} that parses a substring of `input` located between position `begin` (included) and
* `end` (excluded).
*
* Requirements:
* - The substring must be formatted as `[-+]?[0-9]*`
* - The result must fit in an `int256` type.
*/
function parseInt(string memory input, uint256 begin, uint256 end) internal pure returns (int256) {
(bool success, int256 value) = tryParseInt(input, begin, end);
if (!success) revert StringsInvalidChar();
return value;
}
/**
* @dev Variant of {parseInt-string} that returns false if the parsing fails because of an invalid character or if
* the result does not fit in a `int256`.
*
* NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.
*/
function tryParseInt(string memory input) internal pure returns (bool success, int256 value) {
return _tryParseIntUncheckedBounds(input, 0, bytes(input).length);
}
uint256 private constant ABS_MIN_INT256 = 2 ** 255;
/**
* @dev Variant of {parseInt-string-uint256-uint256} that returns false if the parsing fails because of an invalid
* character or if the result does not fit in a `int256`.
*
* NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.
*/
function tryParseInt(
string memory input,
uint256 begin,
uint256 end
) internal pure returns (bool success, int256 value) {
if (end > bytes(input).length || begin > end) return (false, 0);
return _tryParseIntUncheckedBounds(input, begin, end);
}
/**
* @dev Implementation of {tryParseInt-string-uint256-uint256} that does not check bounds. Caller should make sure that
* `begin <= end <= input.length`. Other inputs would result in undefined behavior.
*/
function _tryParseIntUncheckedBounds(
string memory input,
uint256 begin,
uint256 end
) private pure returns (bool success, int256 value) {
bytes memory buffer = bytes(input);
// Check presence of a negative sign.
bytes1 sign = begin == end ? bytes1(0) : bytes1(_unsafeReadBytesOffset(buffer, begin)); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
bool positiveSign = sign == bytes1("+");
bool negativeSign = sign == bytes1("-");
uint256 offset = (positiveSign || negativeSign).toUint();
(bool absSuccess, uint256 absValue) = tryParseUint(input, begin + offset, end);
if (absSuccess && absValue < ABS_MIN_INT256) {
return (true, negativeSign ? -int256(absValue) : int256(absValue));
} else if (absSuccess && negativeSign && absValue == ABS_MIN_INT256) {
return (true, type(int256).min);
} else return (false, 0);
}
/**
* @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as a `uint256`.
*
* Requirements:
* - The string must be formatted as `(0x)?[0-9a-fA-F]*`
* - The result must fit in an `uint256` type.
*/
function parseHexUint(string memory input) internal pure returns (uint256) {
return parseHexUint(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseHexUint-string} that parses a substring of `input` located between position `begin` (included) and
* `end` (excluded).
*
* Requirements:
* - The substring must be formatted as `(0x)?[0-9a-fA-F]*`
* - The result must fit in an `uint256` type.
*/
function parseHexUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {
(bool success, uint256 value) = tryParseHexUint(input, begin, end);
if (!success) revert StringsInvalidChar();
return value;
}
/**
* @dev Variant of {parseHexUint-string} that returns false if the parsing fails because of an invalid character.
*
* NOTE: This function will revert if the result does not fit in a `uint256`.
*/
function tryParseHexUint(string memory input) internal pure returns (bool success, uint256 value) {
return _tryParseHexUintUncheckedBounds(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseHexUint-string-uint256-uint256} that returns false if the parsing fails because of an
* invalid character.
*
* NOTE: This function will revert if the result does not fit in a `uint256`.
*/
function tryParseHexUint(
string memory input,
uint256 begin,
uint256 end
) internal pure returns (bool success, uint256 value) {
if (end > bytes(input).length || begin > end) return (false, 0);
return _tryParseHexUintUncheckedBounds(input, begin, end);
}
/**
* @dev Implementation of {tryParseHexUint-string-uint256-uint256} that does not check bounds. Caller should make sure that
* `begin <= end <= input.length`. Other inputs would result in undefined behavior.
*/
function _tryParseHexUintUncheckedBounds(
string memory input,
uint256 begin,
uint256 end
) private pure returns (bool success, uint256 value) {
bytes memory buffer = bytes(input);
// skip 0x prefix if present
bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(buffer, begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
uint256 offset = hasPrefix.toUint() * 2;
uint256 result = 0;
for (uint256 i = begin + offset; i < end; ++i) {
uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));
if (chr > 15) return (false, 0);
result *= 16;
unchecked {
// Multiplying by 16 is equivalent to a shift of 4 bits (with additional overflow check).
// This guarantees that adding a value < 16 will not cause an overflow, hence the unchecked.
result += chr;
}
}
return (true, result);
}
/**
* @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as an `address`.
*
* Requirements:
* - The string must be formatted as `(0x)?[0-9a-fA-F]{40}`
*/
function parseAddress(string memory input) internal pure returns (address) {
return parseAddress(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseAddress-string} that parses a substring of `input` located between position `begin` (included) and
* `end` (excluded).
*
* Requirements:
* - The substring must be formatted as `(0x)?[0-9a-fA-F]{40}`
*/
function parseAddress(string memory input, uint256 begin, uint256 end) internal pure returns (address) {
(bool success, address value) = tryParseAddress(input, begin, end);
if (!success) revert StringsInvalidAddressFormat();
return value;
}
/**
* @dev Variant of {parseAddress-string} that returns false if the parsing fails because the input is not a properly
* formatted address. See {parseAddress-string} requirements.
*/
function tryParseAddress(string memory input) internal pure returns (bool success, address value) {
return tryParseAddress(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseAddress-string-uint256-uint256} that returns false if the parsing fails because input is not a properly
* formatted address. See {parseAddress-string-uint256-uint256} requirements.
*/
function tryParseAddress(
string memory input,
uint256 begin,
uint256 end
) internal pure returns (bool success, address value) {
if (end > bytes(input).length || begin > end) return (false, address(0));
bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(bytes(input), begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
uint256 expectedLength = 40 + hasPrefix.toUint() * 2;
// check that input is the correct length
if (end - begin == expectedLength) {
// length guarantees that this does not overflow, and value is at most type(uint160).max
(bool s, uint256 v) = _tryParseHexUintUncheckedBounds(input, begin, end);
return (s, address(uint160(v)));
} else {
return (false, address(0));
}
}
function _tryParseChr(bytes1 chr) private pure returns (uint8) {
uint8 value = uint8(chr);
// Try to parse `chr`:
// - Case 1: [0-9]
// - Case 2: [a-f]
// - Case 3: [A-F]
// - otherwise not supported
unchecked {
if (value > 47 && value < 58) value -= 48;
else if (value > 96 && value < 103) value -= 87;
else if (value > 64 && value < 71) value -= 55;
else return type(uint8).max;
}
return value;
}
/**
* @dev Escape special characters in JSON strings. This can be useful to prevent JSON injection in NFT metadata.
*
* WARNING: This function should only be used in double quoted JSON strings. Single quotes are not escaped.
*
* NOTE: This function escapes all unicode characters, and not just the ones in ranges defined in section 2.5 of
* RFC-4627 (U+0000 to U+001F, U+0022 and U+005C). ECMAScript's `JSON.parse` does recover escaped unicode
* characters that are not in this range, but other tooling may provide different results.
*/
function escapeJSON(string memory input) internal pure returns (string memory) {
bytes memory buffer = bytes(input);
bytes memory output = new bytes(2 * buffer.length); // worst case scenario
uint256 outputLength = 0;
for (uint256 i; i < buffer.length; ++i) {
bytes1 char = bytes1(_unsafeReadBytesOffset(buffer, i));
if (((SPECIAL_CHARS_LOOKUP & (1 << uint8(char))) != 0)) {
output[outputLength++] = "\\";
if (char == 0x08) output[outputLength++] = "b";
else if (char == 0x09) output[outputLength++] = "t";
else if (char == 0x0a) output[outputLength++] = "n";
else if (char == 0x0c) output[outputLength++] = "f";
else if (char == 0x0d) output[outputLength++] = "r";
else if (char == 0x5c) output[outputLength++] = "\\";
else if (char == 0x22) {
// solhint-disable-next-line quotes
output[outputLength++] = '"';
}
} else {
output[outputLength++] = char;
}
}
// write the actual length and deallocate unused memory
assembly ("memory-safe") {
mstore(output, outputLength)
mstore(0x40, add(output, shl(5, shr(5, add(outputLength, 63)))))
}
return string(output);
}
/**
* @dev Reads a bytes32 from a bytes array without bounds checking.
*
* NOTE: making this function internal would mean it could be used with memory unsafe offset, and marking the
* assembly block as such would prevent some optimizations.
*/
function _unsafeReadBytesOffset(bytes memory buffer, uint256 offset) private pure returns (bytes32 value) {
// This is not memory safe in the general case, but all calls to this private function are within bounds.
assembly ("memory-safe") {
value := mload(add(add(buffer, 0x20), offset))
}
}
}
ECDSA.sol 180 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.20;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
/**
* @dev The signature derives the `address(0)`.
*/
error ECDSAInvalidSignature();
/**
* @dev The signature has an invalid length.
*/
error ECDSAInvalidSignatureLength(uint256 length);
/**
* @dev The signature has an S value that is in the upper half order.
*/
error ECDSAInvalidSignatureS(bytes32 s);
/**
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
* and a bytes32 providing additional information about the error.
*
* If no error is returned, then the address can be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*/
function tryRecover(
bytes32 hash,
bytes memory signature
) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
assembly ("memory-safe") {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[ERC-2098 short signatures]
*/
function tryRecover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
// We do not check for an overflow here since the shift operation results in 0 or 1.
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
*/
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}
EIP712.sol 160 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/cryptography/EIP712.sol)
pragma solidity ^0.8.20;
import {MessageHashUtils} from "./MessageHashUtils.sol";
import {ShortStrings, ShortString} from "../ShortStrings.sol";
import {IERC5267} from "../../interfaces/IERC5267.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP-712] is a standard for hashing and signing of typed structured data.
*
* The encoding scheme specified in the EIP requires a domain separator and a hash of the typed structured data, whose
* encoding is very generic and therefore its implementation in Solidity is not feasible, thus this contract
* does not implement the encoding itself. Protocols need to implement the type-specific encoding they need in order to
* produce the hash of their typed data using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP-712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
* separator of the implementation contract. This will cause the {_domainSeparatorV4} function to always rebuild the
* separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
*
* @custom:oz-upgrades-unsafe-allow state-variable-immutable
*/
abstract contract EIP712 is IERC5267 {
using ShortStrings for *;
bytes32 private constant TYPE_HASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
// Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
// invalidate the cached domain separator if the chain id changes.
bytes32 private immutable _cachedDomainSeparator;
uint256 private immutable _cachedChainId;
address private immutable _cachedThis;
bytes32 private immutable _hashedName;
bytes32 private immutable _hashedVersion;
ShortString private immutable _name;
ShortString private immutable _version;
// slither-disable-next-line constable-states
string private _nameFallback;
// slither-disable-next-line constable-states
string private _versionFallback;
/**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP-712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
constructor(string memory name, string memory version) {
_name = name.toShortStringWithFallback(_nameFallback);
_version = version.toShortStringWithFallback(_versionFallback);
_hashedName = keccak256(bytes(name));
_hashedVersion = keccak256(bytes(version));
_cachedChainId = block.chainid;
_cachedDomainSeparator = _buildDomainSeparator();
_cachedThis = address(this);
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
return _cachedDomainSeparator;
} else {
return _buildDomainSeparator();
}
}
function _buildDomainSeparator() private view returns (bytes32) {
return keccak256(abi.encode(TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*
* ```solidity
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
* keccak256("Mail(address to,string contents)"),
* mailTo,
* keccak256(bytes(mailContents))
* )));
* address signer = ECDSA.recover(digest, signature);
* ```
*/
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash);
}
/// @inheritdoc IERC5267
function eip712Domain()
public
view
virtual
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
return (
hex"0f", // 01111
_EIP712Name(),
_EIP712Version(),
block.chainid,
address(this),
bytes32(0),
new uint256[](0)
);
}
/**
* @dev The name parameter for the EIP712 domain.
*
* NOTE: By default this function reads _name which is an immutable value.
* It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
*/
// solhint-disable-next-line func-name-mixedcase
function _EIP712Name() internal view returns (string memory) {
return _name.toStringWithFallback(_nameFallback);
}
/**
* @dev The version parameter for the EIP712 domain.
*
* NOTE: By default this function reads _version which is an immutable value.
* It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
*/
// solhint-disable-next-line func-name-mixedcase
function _EIP712Version() internal view returns (string memory) {
return _version.toStringWithFallback(_versionFallback);
}
}
MessageHashUtils.sol 99 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/cryptography/MessageHashUtils.sol)
pragma solidity ^0.8.20;
import {Strings} from "../Strings.sol";
/**
* @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
*
* The library provides methods for generating a hash of a message that conforms to the
* https://eips.ethereum.org/EIPS/eip-191[ERC-191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
* specifications.
*/
library MessageHashUtils {
/**
* @dev Returns the keccak256 digest of an ERC-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing a bytes32 `messageHash` with
* `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
* hash signed when using the https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign[`eth_sign`] JSON-RPC method.
*
* NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
* keccak256, although any bytes32 value can be safely used because the final digest will
* be re-hashed.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
assembly ("memory-safe") {
mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
}
}
/**
* @dev Returns the keccak256 digest of an ERC-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing an arbitrary `message` with
* `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
* hash signed when using the https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign[`eth_sign`] JSON-RPC method.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
return
keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
}
/**
* @dev Returns the keccak256 digest of an ERC-191 signed data with version
* `0x00` (data with intended validator).
*
* The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
* `validator` address. Then hashing the result.
*
* See {ECDSA-recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(hex"19_00", validator, data));
}
/**
* @dev Variant of {toDataWithIntendedValidatorHash-address-bytes} optimized for cases where `data` is a bytes32.
*/
function toDataWithIntendedValidatorHash(
address validator,
bytes32 messageHash
) internal pure returns (bytes32 digest) {
assembly ("memory-safe") {
mstore(0x00, hex"19_00")
mstore(0x02, shl(96, validator))
mstore(0x16, messageHash)
digest := keccak256(0x00, 0x36)
}
}
/**
* @dev Returns the keccak256 digest of an EIP-712 typed data (ERC-191 version `0x01`).
*
* The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
* `\x19\x01` and hashing the result. It corresponds to the hash signed by the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
*
* See {ECDSA-recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(ptr, hex"19_01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
digest := keccak256(ptr, 0x42)
}
}
}
ERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
Math.sol 749 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Return the 512-bit addition of two uint256.
*
* The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.
*/
function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
assembly ("memory-safe") {
low := add(a, b)
high := lt(low, a)
}
}
/**
* @dev Return the 512-bit multiplication of two uint256.
*
* The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.
*/
function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
// 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use
// the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = high * 2²⁵⁶ + low.
assembly ("memory-safe") {
let mm := mulmod(a, b, not(0))
low := mul(a, b)
high := sub(sub(mm, low), lt(mm, low))
}
}
/**
* @dev Returns the addition of two unsigned integers, with a success flag (no overflow).
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a + b;
success = c >= a;
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a - b;
success = c <= a;
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a * b;
assembly ("memory-safe") {
// Only true when the multiplication doesn't overflow
// (c / a == b) || (a == 0)
success := or(eq(div(c, a), b), iszero(a))
}
// equivalent to: success ? c : 0
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
success = b > 0;
assembly ("memory-safe") {
// The `DIV` opcode returns zero when the denominator is 0.
result := div(a, b)
}
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
success = b > 0;
assembly ("memory-safe") {
// The `MOD` opcode returns zero when the denominator is 0.
result := mod(a, b)
}
}
}
/**
* @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.
*/
function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {
(bool success, uint256 result) = tryAdd(a, b);
return ternary(success, result, type(uint256).max);
}
/**
* @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.
*/
function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {
(, uint256 result) = trySub(a, b);
return result;
}
/**
* @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.
*/
function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {
(bool success, uint256 result) = tryMul(a, b);
return ternary(success, result, type(uint256).max);
}
/**
* @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
*
* IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
* However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
* one branch when needed, making this function more expensive.
*/
function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
// branchless ternary works because:
// b ^ (a ^ b) == a
// b ^ 0 == b
return b ^ ((a ^ b) * SafeCast.toUint(condition));
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a > b, a, b);
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(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 towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
Panic.panic(Panic.DIVISION_BY_ZERO);
}
// The following calculation ensures accurate ceiling division without overflow.
// Since a is non-zero, (a - 1) / b will not overflow.
// The largest possible result occurs when (a - 1) / b is type(uint256).max,
// but the largest value we can obtain is type(uint256).max - 1, which happens
// when a = type(uint256).max and b = 1.
unchecked {
return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
}
}
/**
* @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
*
* 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 {
(uint256 high, uint256 low) = mul512(x, y);
// Handle non-overflow cases, 256 by 256 division.
if (high == 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 low / denominator;
}
// Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
if (denominator <= high) {
Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [high low].
uint256 remainder;
assembly ("memory-safe") {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
high := sub(high, gt(remainder, low))
low := sub(low, 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.
uint256 twos = denominator & (0 - denominator);
assembly ("memory-safe") {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [high low] by twos.
low := div(low, twos)
// Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from high into low.
low |= high * twos;
// Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
// that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv ≡ 1 mod 2⁴.
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⁸
inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
inverse *= 2 - denominator * inverse; // inverse mod 2³²
inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶
// 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²⁵⁶. Since the preconditions guarantee that the outcome is
// less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and high
// is no longer required.
result = low * inverse;
return result;
}
}
/**
* @dev 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) {
return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
}
/**
* @dev Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.
*/
function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {
unchecked {
(uint256 high, uint256 low) = mul512(x, y);
if (high >= 1 << n) {
Panic.panic(Panic.UNDER_OVERFLOW);
}
return (high << (256 - n)) | (low >> n);
}
}
/**
* @dev Calculates x * y >> n with full precision, following the selected rounding direction.
*/
function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {
return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 0);
}
/**
* @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
*
* If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
* If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
*
* If the input value is not inversible, 0 is returned.
*
* NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
* inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
*/
function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
unchecked {
if (n == 0) return 0;
// The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
// Used to compute integers x and y such that: ax + ny = gcd(a, n).
// When the gcd is 1, then the inverse of a modulo n exists and it's x.
// ax + ny = 1
// ax = 1 + (-y)n
// ax ≡ 1 (mod n) # x is the inverse of a modulo n
// If the remainder is 0 the gcd is n right away.
uint256 remainder = a % n;
uint256 gcd = n;
// Therefore the initial coefficients are:
// ax + ny = gcd(a, n) = n
// 0a + 1n = n
int256 x = 0;
int256 y = 1;
while (remainder != 0) {
uint256 quotient = gcd / remainder;
(gcd, remainder) = (
// The old remainder is the next gcd to try.
remainder,
// Compute the next remainder.
// Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
// where gcd is at most n (capped to type(uint256).max)
gcd - remainder * quotient
);
(x, y) = (
// Increment the coefficient of a.
y,
// Decrement the coefficient of n.
// Can overflow, but the result is casted to uint256 so that the
// next value of y is "wrapped around" to a value between 0 and n - 1.
x - y * int256(quotient)
);
}
if (gcd != 1) return 0; // No inverse exists.
return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
}
}
/**
* @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
*
* From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
* prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
* `a**(p-2)` is the modular multiplicative inverse of a in Fp.
*
* NOTE: this function does NOT check that `p` is a prime greater than `2`.
*/
function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
unchecked {
return Math.modExp(a, p - 2, p);
}
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
*
* Requirements:
* - modulus can't be zero
* - underlying staticcall to precompile must succeed
*
* IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
* sure the chain you're using it on supports the precompiled contract for modular exponentiation
* at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
* the underlying function will succeed given the lack of a revert, but the result may be incorrectly
* interpreted as 0.
*/
function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
(bool success, uint256 result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
* It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
* to operate modulo 0 or if the underlying precompile reverted.
*
* IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
* you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
* https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
* of a revert, but the result may be incorrectly interpreted as 0.
*/
function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
if (m == 0) return (false, 0);
assembly ("memory-safe") {
let ptr := mload(0x40)
// | Offset | Content | Content (Hex) |
// |-----------|------------|--------------------------------------------------------------------|
// | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x60:0x7f | value of b | 0x<.............................................................b> |
// | 0x80:0x9f | value of e | 0x<.............................................................e> |
// | 0xa0:0xbf | value of m | 0x<.............................................................m> |
mstore(ptr, 0x20)
mstore(add(ptr, 0x20), 0x20)
mstore(add(ptr, 0x40), 0x20)
mstore(add(ptr, 0x60), b)
mstore(add(ptr, 0x80), e)
mstore(add(ptr, 0xa0), m)
// Given the result < m, it's guaranteed to fit in 32 bytes,
// so we can use the memory scratch space located at offset 0.
success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
result := mload(0x00)
}
}
/**
* @dev Variant of {modExp} that supports inputs of arbitrary length.
*/
function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
(bool success, bytes memory result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Variant of {tryModExp} that supports inputs of arbitrary length.
*/
function tryModExp(
bytes memory b,
bytes memory e,
bytes memory m
) internal view returns (bool success, bytes memory result) {
if (_zeroBytes(m)) return (false, new bytes(0));
uint256 mLen = m.length;
// Encode call args in result and move the free memory pointer
result = abi.encodePacked(b.length, e.length, mLen, b, e, m);
assembly ("memory-safe") {
let dataPtr := add(result, 0x20)
// Write result on top of args to avoid allocating extra memory.
success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
// Overwrite the length.
// result.length > returndatasize() is guaranteed because returndatasize() == m.length
mstore(result, mLen)
// Set the memory pointer after the returned data.
mstore(0x40, add(dataPtr, mLen))
}
}
/**
* @dev Returns whether the provided byte array is zero.
*/
function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
for (uint256 i = 0; i < byteArray.length; ++i) {
if (byteArray[i] != 0) {
return false;
}
}
return true;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* This method is based on Newton's method for computing square roots; the algorithm is restricted to only
* using integer operations.
*/
function sqrt(uint256 a) internal pure returns (uint256) {
unchecked {
// Take care of easy edge cases when a == 0 or a == 1
if (a <= 1) {
return a;
}
// In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
// sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
// the current value as `ε_n = | x_n - sqrt(a) |`.
//
// For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
// of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
// bigger than any uint256.
//
// By noticing that
// `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
// we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
// to the msb function.
uint256 aa = a;
uint256 xn = 1;
if (aa >= (1 << 128)) {
aa >>= 128;
xn <<= 64;
}
if (aa >= (1 << 64)) {
aa >>= 64;
xn <<= 32;
}
if (aa >= (1 << 32)) {
aa >>= 32;
xn <<= 16;
}
if (aa >= (1 << 16)) {
aa >>= 16;
xn <<= 8;
}
if (aa >= (1 << 8)) {
aa >>= 8;
xn <<= 4;
}
if (aa >= (1 << 4)) {
aa >>= 4;
xn <<= 2;
}
if (aa >= (1 << 2)) {
xn <<= 1;
}
// We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
//
// We can refine our estimation by noticing that the middle of that interval minimizes the error.
// If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
// This is going to be our x_0 (and ε_0)
xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)
// From here, Newton's method give us:
// x_{n+1} = (x_n + a / x_n) / 2
//
// One should note that:
// x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
// = ((x_n² + a) / (2 * x_n))² - a
// = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
// = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
// = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
// = (x_n² - a)² / (2 * x_n)²
// = ((x_n² - a) / (2 * x_n))²
// ≥ 0
// Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
//
// This gives us the proof of quadratic convergence of the sequence:
// ε_{n+1} = | x_{n+1} - sqrt(a) |
// = | (x_n + a / x_n) / 2 - sqrt(a) |
// = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
// = | (x_n - sqrt(a))² / (2 * x_n) |
// = | ε_n² / (2 * x_n) |
// = ε_n² / | (2 * x_n) |
//
// For the first iteration, we have a special case where x_0 is known:
// ε_1 = ε_0² / | (2 * x_0) |
// ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
// ≤ 2**(2*e-4) / (3 * 2**(e-1))
// ≤ 2**(e-3) / 3
// ≤ 2**(e-3-log2(3))
// ≤ 2**(e-4.5)
//
// For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
// ε_{n+1} = ε_n² / | (2 * x_n) |
// ≤ (2**(e-k))² / (2 * 2**(e-1))
// ≤ 2**(2*e-2*k) / 2**e
// ≤ 2**(e-2*k)
xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above
xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5
xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9
xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18
xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36
xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72
// Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
// ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
// sqrt(a) or sqrt(a) + 1.
return xn - SafeCast.toUint(xn > a / xn);
}
}
/**
* @dev 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 x) internal pure returns (uint256 r) {
// If value has upper 128 bits set, log2 result is at least 128
r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
// If upper 64 bits of 128-bit half set, add 64 to result
r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
// If upper 32 bits of 64-bit half set, add 32 to result
r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
// If upper 16 bits of 32-bit half set, add 16 to result
r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
// If upper 8 bits of 16-bit half set, add 8 to result
r |= SafeCast.toUint((x >> r) > 0xff) << 3;
// If upper 4 bits of 8-bit half set, add 4 to result
r |= SafeCast.toUint((x >> r) > 0xf) << 2;
// Shifts value right by the current result and use it as an index into this lookup table:
//
// | x (4 bits) | index | table[index] = MSB position |
// |------------|---------|-----------------------------|
// | 0000 | 0 | table[0] = 0 |
// | 0001 | 1 | table[1] = 0 |
// | 0010 | 2 | table[2] = 1 |
// | 0011 | 3 | table[3] = 1 |
// | 0100 | 4 | table[4] = 2 |
// | 0101 | 5 | table[5] = 2 |
// | 0110 | 6 | table[6] = 2 |
// | 0111 | 7 | table[7] = 2 |
// | 1000 | 8 | table[8] = 3 |
// | 1001 | 9 | table[9] = 3 |
// | 1010 | 10 | table[10] = 3 |
// | 1011 | 11 | table[11] = 3 |
// | 1100 | 12 | table[12] = 3 |
// | 1101 | 13 | table[13] = 3 |
// | 1110 | 14 | table[14] = 3 |
// | 1111 | 15 | table[15] = 3 |
//
// The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.
assembly ("memory-safe") {
r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))
}
}
/**
* @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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* 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 x) internal pure returns (uint256 r) {
// If value has upper 128 bits set, log2 result is at least 128
r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
// If upper 64 bits of 128-bit half set, add 64 to result
r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
// If upper 32 bits of 64-bit half set, add 32 to result
r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
// If upper 16 bits of 32-bit half set, add 16 to result
r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
// Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8
return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);
}
/**
* @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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}
SafeCast.sol 1162 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeCast {
/**
* @dev Value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
/**
* @dev An int value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedIntToUint(int256 value);
/**
* @dev Value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
/**
* @dev An uint value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedUintToInt(uint256 value);
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toUint248(uint256 value) internal pure returns (uint248) {
if (value > type(uint248).max) {
revert SafeCastOverflowedUintDowncast(248, value);
}
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toUint240(uint256 value) internal pure returns (uint240) {
if (value > type(uint240).max) {
revert SafeCastOverflowedUintDowncast(240, value);
}
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toUint232(uint256 value) internal pure returns (uint232) {
if (value > type(uint232).max) {
revert SafeCastOverflowedUintDowncast(232, value);
}
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
if (value > type(uint224).max) {
revert SafeCastOverflowedUintDowncast(224, value);
}
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toUint216(uint256 value) internal pure returns (uint216) {
if (value > type(uint216).max) {
revert SafeCastOverflowedUintDowncast(216, value);
}
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toUint208(uint256 value) internal pure returns (uint208) {
if (value > type(uint208).max) {
revert SafeCastOverflowedUintDowncast(208, value);
}
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toUint200(uint256 value) internal pure returns (uint200) {
if (value > type(uint200).max) {
revert SafeCastOverflowedUintDowncast(200, value);
}
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toUint192(uint256 value) internal pure returns (uint192) {
if (value > type(uint192).max) {
revert SafeCastOverflowedUintDowncast(192, value);
}
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toUint184(uint256 value) internal pure returns (uint184) {
if (value > type(uint184).max) {
revert SafeCastOverflowedUintDowncast(184, value);
}
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toUint176(uint256 value) internal pure returns (uint176) {
if (value > type(uint176).max) {
revert SafeCastOverflowedUintDowncast(176, value);
}
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toUint168(uint256 value) internal pure returns (uint168) {
if (value > type(uint168).max) {
revert SafeCastOverflowedUintDowncast(168, value);
}
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) {
revert SafeCastOverflowedUintDowncast(160, value);
}
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toUint152(uint256 value) internal pure returns (uint152) {
if (value > type(uint152).max) {
revert SafeCastOverflowedUintDowncast(152, value);
}
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toUint144(uint256 value) internal pure returns (uint144) {
if (value > type(uint144).max) {
revert SafeCastOverflowedUintDowncast(144, value);
}
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toUint136(uint256 value) internal pure returns (uint136) {
if (value > type(uint136).max) {
revert SafeCastOverflowedUintDowncast(136, value);
}
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
if (value > type(uint128).max) {
revert SafeCastOverflowedUintDowncast(128, value);
}
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toUint120(uint256 value) internal pure returns (uint120) {
if (value > type(uint120).max) {
revert SafeCastOverflowedUintDowncast(120, value);
}
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toUint112(uint256 value) internal pure returns (uint112) {
if (value > type(uint112).max) {
revert SafeCastOverflowedUintDowncast(112, value);
}
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toUint104(uint256 value) internal pure returns (uint104) {
if (value > type(uint104).max) {
revert SafeCastOverflowedUintDowncast(104, value);
}
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toUint96(uint256 value) internal pure returns (uint96) {
if (value > type(uint96).max) {
revert SafeCastOverflowedUintDowncast(96, value);
}
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toUint88(uint256 value) internal pure returns (uint88) {
if (value > type(uint88).max) {
revert SafeCastOverflowedUintDowncast(88, value);
}
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toUint80(uint256 value) internal pure returns (uint80) {
if (value > type(uint80).max) {
revert SafeCastOverflowedUintDowncast(80, value);
}
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toUint72(uint256 value) internal pure returns (uint72) {
if (value > type(uint72).max) {
revert SafeCastOverflowedUintDowncast(72, value);
}
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
if (value > type(uint64).max) {
revert SafeCastOverflowedUintDowncast(64, value);
}
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toUint56(uint256 value) internal pure returns (uint56) {
if (value > type(uint56).max) {
revert SafeCastOverflowedUintDowncast(56, value);
}
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toUint48(uint256 value) internal pure returns (uint48) {
if (value > type(uint48).max) {
revert SafeCastOverflowedUintDowncast(48, value);
}
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toUint40(uint256 value) internal pure returns (uint40) {
if (value > type(uint40).max) {
revert SafeCastOverflowedUintDowncast(40, value);
}
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
if (value > type(uint32).max) {
revert SafeCastOverflowedUintDowncast(32, value);
}
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toUint24(uint256 value) internal pure returns (uint24) {
if (value > type(uint24).max) {
revert SafeCastOverflowedUintDowncast(24, value);
}
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
if (value > type(uint16).max) {
revert SafeCastOverflowedUintDowncast(16, value);
}
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toUint8(uint256 value) internal pure returns (uint8) {
if (value > type(uint8).max) {
revert SafeCastOverflowedUintDowncast(8, value);
}
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
if (value < 0) {
revert SafeCastOverflowedIntToUint(value);
}
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(248, value);
}
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(240, value);
}
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(232, value);
}
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(224, value);
}
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(216, value);
}
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(208, value);
}
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(200, value);
}
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(192, value);
}
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(184, value);
}
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(176, value);
}
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(168, value);
}
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(160, value);
}
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(152, value);
}
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(144, value);
}
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(136, value);
}
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(128, value);
}
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(120, value);
}
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(112, value);
}
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(104, value);
}
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(96, value);
}
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(88, value);
}
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(80, value);
}
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(72, value);
}
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(64, value);
}
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(56, value);
}
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(48, value);
}
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(40, value);
}
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(32, value);
}
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(24, value);
}
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(16, value);
}
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(8, value);
}
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
if (value > uint256(type(int256).max)) {
revert SafeCastOverflowedUintToInt(value);
}
return int256(value);
}
/**
* @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
*/
function toUint(bool b) internal pure returns (uint256 u) {
assembly ("memory-safe") {
u := iszero(iszero(b))
}
}
}
SignedMath.sol 68 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
import {SafeCast} from "./SafeCast.sol";
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
*
* IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
* However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
* one branch when needed, making this function more expensive.
*/
function ternary(bool condition, int256 a, int256 b) internal pure returns (int256) {
unchecked {
// branchless ternary works because:
// b ^ (a ^ b) == a
// b ^ 0 == b
return b ^ ((a ^ b) * int256(SafeCast.toUint(condition)));
}
}
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return ternary(a > b, a, b);
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return ternary(a < b, a, b);
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// Formula from the "Bit Twiddling Hacks" by Sean Eron Anderson.
// Since `n` is a signed integer, the generated bytecode will use the SAR opcode to perform the right shift,
// taking advantage of the most significant (or "sign" bit) in two's complement representation.
// This opcode adds new most significant bits set to the value of the previous most significant bit. As a result,
// the mask will either be `bytes32(0)` (if n is positive) or `~bytes32(0)` (if n is negative).
int256 mask = n >> 255;
// A `bytes32(0)` mask leaves the input unchanged, while a `~bytes32(0)` mask complements it.
return uint256((n + mask) ^ mask);
}
}
}
WitOracle.sol 25 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import "./interfaces/IWitOracle.sol";
import "./interfaces/IWitOracleAppliance.sol";
import "./interfaces/IWitOracleQueriable.sol";
import "./interfaces/IWitOracleQueriableEvents.sol";
/// @title Witnet Request Board functionality base contract.
/// @author The Witnet Foundation.
abstract contract WitOracle
is
IWitAppliance,
IWitOracle,
IWitOracleQueriable,
IWitOracleQueriableEvents
{
function specs() virtual override external pure returns (bytes4) {
return (
type(IWitOracle).interfaceId
^ type(IWitOracleQueriable).interfaceId
);
}
}
WitOracleRadonRequestFactory.sol 20 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import "./interfaces/IWitOracleAppliance.sol";
import "./interfaces/IWitOracleRadonRegistryEvents.sol";
import "./interfaces/IWitOracleRadonRequestFactory.sol";
abstract contract WitOracleRadonRequestFactory
is
IWitOracleAppliance,
IWitOracleRadonRegistryEvents,
IWitOracleRadonRequestFactory
{
function specs() virtual override external pure returns (bytes4) {
return (
type(WitOracleRadonRequestFactory).interfaceId
);
}
}
IWitAppliance.sol 29 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
abstract contract IWitAppliance {
/// @notice Returns the name of the actual contract implementing the logic of this Witnet appliance.
function class() virtual public view returns (string memory);
/// @notice Returns the ERC-165 id of the minimal functionality expected for this appliance.
function specs() virtual external view returns (bytes4);
function _require(bool _condition, string memory _message) virtual internal view {
if (!_condition) {
_revert(_message);
}
}
function _revert(string memory _message) virtual internal view {
revert(
string(abi.encodePacked(
class(),
": ",
_message
))
);
}
}
IWitOracle.sol 39 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import "./IWitOracleRadonRegistry.sol";
interface IWitOracle {
error InvalidDataReport();
event WitOracleReport(
address indexed evmOrigin,
address indexed evmConsumer,
address evmReporter,
Witnet.TransactionHash witDrTxHash,
Witnet.RadonHash queryRadHash,
Witnet.QuerySLA queryParams,
Witnet.Timestamp resultTimestamp,
bytes resultCborBytes
);
/// @notice Uniquely identifies the WitOracle instance and the chain on which it's deployed.
function channel() external view returns (bytes4);
/// @notice Verify the data report (as provided by Wit/Kermit API) is well-formed and authentic,
/// returning the parsed Witnet.DataResult if so, or reverting otherwise.
function parseDataReport(Witnet.DataPushReport calldata report, bytes calldata proof) external view returns (Witnet.DataResult memory);
/// @notice Same as `parseDataReport` but on certain implementations it may store roll-up information
/// that will contribute to reduce the cost of verifying and/or rolling-up future data reports.
/// Emits `DataReport` if report is authentic.
function pushDataReport(Witnet.DataPushReport calldata report, bytes calldata proof) external returns (Witnet.DataResult memory);
/// @notice Returns the WitOracleRadonRegistry in which Witnet-compliant Radon requests
/// @notice can be formally verified and forever registered as a away to let smart contracts
/// and users to track actual data sources and offchain computations applied on data updates
/// safely reported from the Wit/Oracle blockchain.
function registry() external view returns (IWitOracleRadonRegistry);
}
IWitOracleAppliance.sol 13 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./IWitAppliance.sol";
abstract contract IWitOracleAppliance
is
IWitAppliance
{
/// @notice Returns the WitOracle address that this appliance is bound to.
function witOracle() virtual external view returns (address);
}
IWitOracleConsumer.sol 14 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import "./IWitOracle.sol";
interface IWitOracleConsumer {
/// @notice Accepts a data report from the Wit/oracle blockchain that ought to be
/// verified by the WitOracle contract pointed out by `witOracle()`.
/// @dev The referred `witOracle()` contract emits a `IWitOracle.DataReport` for
/// every `Witnet.DataPushReport` proven to be authentic.
function pushDataReport(Witnet.DataPushReport calldata report, bytes calldata proof) external;
}
IWitOracleQueriable.sol 102 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import "../libs/Witnet.sol";
interface IWitOracleQueriable {
/// @notice Removes all query data from storage. Pays back reward on expired queries.
/// @dev Fails if the query is not in a final status, or not called from the actual requester.
/// @param queryId The unique query identifier.
function deleteQuery(uint256 queryId) external returns (Witnet.QueryEvmReward);
/// @notice Estimate the minimum reward required for posting a data request.
/// @param evmGasPrice Expected gas price to pay upon posting the data request.
function estimateBaseFee(uint256 evmGasPrice) external view returns (uint256);
/// @notice Estimate the minimum reward required for posting a data request with a callback.
/// @param evmGasPrice Expected gas price to pay upon posting the data request.
/// @param callbackGas Maximum gas to be spent when reporting the data request result.
function estimateBaseFeeWithCallback(uint256 evmGasPrice, uint24 callbackGas) external view returns (uint256);
/// @notice Estimate the extra reward (i.e. over the base fee) to be paid when posting a new
/// @notice data query in order to avoid getting provable "too low incentives" results from
/// @notice the Wit/Oracle blockchain.
/// @dev The extra fee gets calculated in proportion to:
/// @param evmGasPrice Tentative EVM gas price at the moment the query result is ready.
/// @param evmWitPrice Tentative nanoWit price in Wei at the moment the query is solved on the Wit/Oracle blockchain.
/// @param querySLA The query SLA data security parameters as required for the Wit/Oracle blockchain.
function estimateExtraFee(uint256 evmGasPrice, uint256 evmWitPrice, Witnet.QuerySLA calldata querySLA) external view returns (uint256);
/// @notice Returns next query id to be generated by the Witnet Request Board.
function getNextQueryId() external view returns (Witnet.QueryId);
/// @notice Gets the whole Query data contents, if any, no matter its current status.
function getQuery(Witnet.QueryId queryId) external view returns (Witnet.Query memory);
/// @notice Gets the current EVM reward the reporter can claim, if not done yet.
function getQueryEvmReward(uint256) external view returns (Witnet.QueryEvmReward);
/// @notice Retrieves the RAD hash and SLA parameters of the given query.
function getQueryRequest(Witnet.QueryId) external view returns (Witnet.QueryRequest memory);
/// @notice Retrieves the whole `Witnet.QueryResponse` record referred to a previously posted Witnet Data Request.
function getQueryResponse(Witnet.QueryId) external view returns (Witnet.QueryResponse memory);
function getQueryResult(uint256) external view returns (Witnet.DataResult memory);
function getQueryResultStatus(uint256) external view returns (Witnet.ResultStatus);
function getQueryResultStatusDescription(uint256) external view returns (string memory);
/// @notice Gets query's result back tracing trails
function getQueryResultTrails(uint256)
external view returns (
bytes32 queryUUID,
Witnet.TransactionHash resultDrTxHash,
Witnet.Timestamp resultTimestamp,
uint256 resultFinalityBlock
);
/// @notice Gets current status of given query.
function getQueryStatus(uint256) external view returns (Witnet.QueryStatus);
function getQueryStatusString(uint256) external view returns (string memory);
/// @notice Get current status of all given query ids.
function getQueryStatusBatch(uint256[] calldata) external view returns (Witnet.QueryStatus[] memory);
/// @notice Request real world data from the Wit/Oracle sidechain.
/// @notice The paid fee is escrowed as a reward for the reporter that eventually relays back
/// @notice a valid query result from the Wit/Oracle sidechain.
/// @notice Query results are CBOR-encoded, and can contain either some data, or an error.
/// @dev Reasons to revert:
/// @dev - the data request's RAD hash was not previously verified into the WitOracleRadonRegistry contract;
/// @dev - invalid query SLA parameters were provided;
/// @dev - insufficient value is paid as reward.
/// @param radonHash The unique hash of the Radon Request to be solved by Wit/Oracle sidechain.
function queryData(
Witnet.RadonHash radonHash,
Witnet.QuerySLA calldata
)
external payable returns (uint256);
/// @notice Request real world data from the Wit/Oracle sidechain.
/// @notice The paid fee is escrowed as a reward for the reporter that eventually relays back
/// @notice a valid query result from the Wit/Oracle sidechain.
/// @notice The Witnet-provable result will be reported directly to the requesting contract.
/// @notice Query results are CBOR-encoded, and can contain either some data, or an error.
/// @dev Reasons to revert:
/// @dev - the data request's RAD hash was not previously verified into the Radon Registry;
/// @dev - invalid query SLA parameters were provided;
/// @dev - insufficient value is paid as reward.
/// @dev - passed `consumer` is not a contract implementing the IWitOracleQueriableConsumer interface;
/// @param radonHash The unique hash of the Radon Request to be solved by Wit/Oracle sidechain.
function queryDataWithCallback(
Witnet.RadonHash radonHash,
Witnet.QuerySLA calldata,
Witnet.QueryCallback calldata
)
external payable returns (uint256);
/// @notice Increments the reward of a previously posted request by adding the transaction value to it.
function upgradeQueryEvmReward(uint256) external payable;
}
IWitOracleQueriableConsumer.sol 22 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./IWitOracleQueriable.sol";
interface IWitOracleQueriableConsumer {
/// Method to be called from the WitOracle contract as soon as the given Witnet `queryId` gets reported.
/// @dev It should revert if called from an address different to the WitOracle instance being used by consumer.
/// @param queryId The unique identifier of the Witnet query being reported.
/// @param queryResult Abi-encoded Witnet.DataResult containing the CBOR-encoded query's result, and metadata.
function reportWitOracleQueryResult(
uint256 queryId,
bytes calldata queryResult
) external;
/// @notice Determines if Witnet queries can be reported from given address.
/// @dev In practice, must only be true on the WitOracle address that's being used by
/// @dev the WitOracleQueriableConsumer to post queries.
function reportableFrom(address) external view returns (bool);
}
IWitOracleQueriableEvents.sol 56 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import "../libs/Witnet.sol";
interface IWitOracleQueriableEvents {
/// Emitted every time a new query containing some verified data request is posted to the WitOracle.
event WitOracleQuery(
address indexed evmRequester,
uint256 evmGasPrice,
uint256 evmReward,
Witnet.QueryId queryId,
Witnet.RadonHash radonHash,
Witnet.QuerySLA radonParams
);
/// Emitted when the reward of some not-yet reported query gets upgraded.
event WitOracleQueryUpgrade(
Witnet.QueryId queryId,
address evmSender,
uint256 evmGasPrice,
uint256 evmReward
);
/// Emitted when a query with no callback gets reported into the WRB.
event WitOracleQueryReport(
Witnet.QueryId queryId,
uint256 evmGasPrice
);
event WitOracleQueryReportDispute(
Witnet.QueryId queryId,
address evmDisputer
);
/// Emitted when a query with a callback gets successfully reported into the WRB.
event WitOracleQueryReportDelivery(
Witnet.QueryId queryId,
address evmConsumer,
uint256 evmGasPrice,
uint256 evmCallbackGas
);
/// Emitted when a query with a callback cannot get reported into the WRB.
event WitOracleResportDeliveryFailed(
Witnet.QueryId queryId,
address evmConsumer,
uint256 evmGasPrice,
uint256 evmCallbackActualGas,
string evmCallbackRevertReason,
bytes resultCborBytes
);
}
IWitOracleRadonRegistry.sol 184 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import "../libs/Witnet.sol";
interface IWitOracleRadonRegistry {
/// @notice Returns the Witnet-compliant DRO bytecode for some data request object
/// made out of the given Radon Request and Radon SLA security parameters.
function bytecodeOf(
Witnet.RadonHash radonRequestHash,
Witnet.QuerySLA calldata queryParams
) external view returns (bytes memory);
/// @notice Returns the Witnet-compliant DRO bytecode for some data request object
/// made out of the given RAD bytecode and Radon SLA security parameters.
function bytecodeOf(
bytes calldata radonRequestBytecode,
Witnet.QuerySLA calldata queryParams
) external view returns (bytes memory);
/// @notice Returns the hash of the given Witnet-compliant bytecode. Returned value
/// can be used to trace back in the Witnet blockchain all past resolutions
/// of the given data request payload.
function hashOf(bytes calldata) external view returns (Witnet.RadonHash);
/// @notice Tells whether the specified Radon Reducer has been formally verified into the registry.
function isVerifiedRadonReducer(bytes32) external view returns (bool);
/// @notice Tells whether the given Radon Hash has been formally verified into the registry.
function isVerifiedRadonRequest(Witnet.RadonHash) external view returns (bool);
/// @notice Tells whether the specified Radon Retrieval has been formally verified into the registry.
function isVerifiedRadonRetrieval(bytes32) external view returns (bool);
/// @notice Returns the whole Witnet.RadonReducer metadata struct for the given hash.
/// @dev Reverts if unknown.
function lookupRadonReducer(bytes32 hash) external view returns (Witnet.RadonReducer memory);
// /// @notice Returns the whole Witnet.RadonRequest metadata struct for the given RAD hash value.
// /// @dev Reverts if unknown.
// function lookupRadonRequest(Witnet.RadonHash radonRequestHash) external view returns (Witnet.RadonRequest memory);
/// @notice Returns the Witnet-compliant RAD bytecode for some Radon Request
/// identified by its unique RAD hash.
function lookupRadonRequestBytecode(Witnet.RadonHash radonRequestHash) external view returns (bytes memory);
/// @notice Returns the Tally reducer that is applied to aggregated values revealed by the witnessing nodes on the
/// Witnet blockchain.
/// @dev Reverts if unknown.
function lookupRadonRequestCrowdAttestationTally(Witnet.RadonHash radonRequestHash) external view returns (Witnet.RadonReducer memory);
/// @notice Returns the deterministic data type returned by successful resolutions of the given Radon Request.
/// @dev Reverts if unknown.
function lookupRadonRequestResultDataType(Witnet.RadonHash radonRequestHash) external view returns (Witnet.RadonDataTypes);
/// @notice Returns introspective metadata for the index-th data source of some pre-verified Radon Request.
/// @dev Reverts if out of range.
// function lookupRadonRequestRetrievalByIndex(Witnet.RadonHash radonRequestHash, uint256 index) external view returns (Witnet.RadonRetrieval memory);
/// @notice Returns an array (one or more items) containing the introspective metadata of the given Radon Request's
/// data sources (i.e. Radon Retrievals).
/// @dev Reverts if unknown.
function lookupRadonRequestRetrievals(Witnet.RadonHash radonRequestHash) external view returns (Witnet.RadonRetrieval[] memory);
/// @notice Returns the Aggregate reducer that is applied to the data extracted from the data sources
/// (i.e. Radon Retrievals) whenever the given Radon Request gets solved on the Witnet blockchain.
/// @dev Reverts if unknown.
function lookupRadonRequestRetrievalsAggregator(Witnet.RadonHash radonRequestHash) external view returns (Witnet.RadonReducer memory);
function lookupRadonRequestRetrievalsCount(Witnet.RadonHash radonRequestHash) external view returns (uint8);
/// @notice Returns introspective metadata of some previously verified Radon Retrieval (i.e. public data source).
///@dev Reverts if unknown.
function lookupRadonRetrieval(bytes32 hash) external view returns (Witnet.RadonRetrieval memory);
/// @notice Returns the number of indexed parameters required to be fulfilled when
/// eventually using the given Radon Retrieval.
/// @dev Reverts if unknown.
function lookupRadonRetrievalArgsCount(bytes32 hash) external view returns (uint8);
/// @notice Returns the type of the data that would be retrieved by the given Radon Retrieval
/// (i.e. public data source).
/// @dev Reverts if unknown.
function lookupRadonRetrievalResultDataType(bytes32 hash) external view returns (Witnet.RadonDataTypes);
/// @notice Verifies and registers the given sequence of dataset filters and reducing function to be
/// potentially used as either Aggregate or Tally reducers within the resolution workflow
/// of Radon Requests in the Wit/Oracle blockchain. Returns a unique hash that identifies the
/// given Radon Reducer in the registry.
/// @dev Reverts if unsupported reducing or filtering methods are specified.
function verifyRadonReducer(Witnet.RadonReducer calldata reducer) external returns (bytes32 hash);
/// @notice Verifies and registers the specified Radon Request out of the given data sources (i.e. retrievals)
/// and the aggregate and tally Radon Reducers. Returns a unique RAD hash that identifies the
/// verified Radon Request.
/// @dev Reverts if:
/// - unverified retrievals are passed;
/// - retrievals return different data types;
/// - any of passed retrievals is parameterized;
/// - unsupported reducers are passed.
function verifyRadonRequest(
bytes32[] calldata radonRetrieveHashes,
Witnet.RadonReducer calldata dataSourcesAggregator,
Witnet.RadonReducer calldata crowsAttestationTally
) external returns (Witnet.RadonHash radonRequestHash);
/// @notice Verifies and registers the specified Radon Request out of the given data sources (i.e. retrievals),
/// data sources parameters (if required), and some pre-verified aggregate and tally Radon Reducers.
/// Returns a unique RAD hash that identifies the verified Radon Request.
/// @dev Reverts if:
/// - unverified retrievals are passed;
/// - retrievals return different data types;
/// - ranks of passed args don't match with those required by each given retrieval;
/// - unverified reducers are passed.
function verifyRadonRequest(
bytes32[] calldata retrieveHashes,
bytes32 aggregateReducerHash,
bytes32 tallyReducerHash
) external returns (Witnet.RadonHash radonRequestHash);
/// @notice Verifies and registers the specified Radon Request out of the given data sources (i.e. retrievals),
/// data sources parameters (if required), and the aggregate and tally Radon Reducers. Returns a unique
/// RAD hash that identifies the verified Radon Request.
/// @dev Reverts if:
/// - unverified retrievals are passed;
/// - retrievals return different data types;
/// - ranks of passed args don't match with those required by each given retrieval;
/// - unsupported reducers are passed.
function verifyRadonRequest(
bytes32[] calldata radonRetrieveHashes,
string[][] calldata radonRetrieveArgs,
Witnet.RadonReducer calldata dataSourcesAggregator,
Witnet.RadonReducer calldata crowdAttestationTally
) external returns (Witnet.RadonHash radonRequestHash);
/// @notice Verifies and registers the specified Radon Request out of a single modal retrieval where first
/// parameter corresponds to data provider's URL, an array of data providers (i.e. URLs), and an array
/// of parmeter values common to all data providers. Some pre-verified aggregate and tally Radon Reducers
/// must also be provided. Returns a unique RAD hash that identifies the verified Radon Request.
/// @dev Reverts if:
/// - unverified retrieval is passed;
/// - ranks of passed args don't match with those expected by given retrieval, after replacing the data provider URL.
/// - unverified reducers are passed.
function verifyRadonRequest(
bytes32[] calldata radonRetrieveHashes,
string[][] calldata radonRetrieveArgs,
bytes32 dataSourcesAggregatorHash,
bytes32 crowdAttestationTallyHash
) external returns (Witnet.RadonHash radonRequestHash);
function verifyRadonRequest(
bytes32 commonRetrieveHash,
string[] calldata commonRetrieveArgs,
string[] calldata dataProviders,
bytes32 dataSourcesAggregatorHash,
bytes32 crowdAttestationTallyHash
) external returns (Witnet.RadonHash radonRequestHash);
/// @notice Verifies and registers the specified Radon Retrieval (i.e. public data source) into this registry contract.
/// Returns a unique retrieval hash that identifies the verified Radon Retrieval.
/// All parameters but the retrieval method are parameterizable by using embedded wildcard \x\ substrings (with x='0'..'9').
/// @dev Reverts if:
/// - unsupported retrieval method is given;
/// - no URL is provided Http/* requests;
/// - non-empty strings given on RNG reqs.
function verifyRadonRetrieval(
Witnet.RadonRetrievalMethods requestMethod,
string calldata requestURL,
string calldata requestBody,
string[2][] calldata requestHeaders,
bytes calldata requestRadonScript
) external returns (bytes32 hash);
// /// Verifies a new Radon Retrieval by specifying the value to the highest indexed parameter of an already existing one.
// /// Returns the unique hash that identifies the resulting Radon Retrieval.
// /// Reverts if an unverified retrieval hash is passed.
// function verifyRadonRetrieval(
// bytes32 retrieveHash,
// string calldata lastArgValue
// ) external returns (bytes32 hash);
// }
}
IWitOracleRadonRegistryEvents.sol 20 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import "../libs/Witnet.sol";
interface IWitOracleRadonRegistryEvents {
/// Emitted every time a new Radon Reducer gets successfully verified and
/// stored into the WitOracleRadonRegistry.
event NewRadonReducer(bytes32 hash);
/// Emitted every time a new Radon Retrieval gets successfully verified and
/// stored into the WitOracleRadonRegistry.
event NewRadonRetrieval(bytes32 hash);
/// Emitted every time a new Radon Request gets successfully verified and
/// stored into the WitOracleRadonRegistry.
event NewRadonRequest(Witnet.RadonHash radonHash);
}
IWitOracleRadonRequestFactory.sol 43 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import "./IWitOracleRadonRequestModal.sol";
import "./IWitOracleRadonRequestTemplate.sol";
interface IWitOracleRadonRequestFactory {
event NewRadonRequestModal(address witOracleRadonRequestModal);
event NewRadonRequestTemplate(address witOracleRadonRequestTemplate);
struct DataSource {
string url;
DataSourceRequest request;
}
struct DataSourceRequest {
Witnet.RadonRetrievalMethods method;
string body;
string[2][] headers;
bytes script;
}
function buildRadonRequestModal(
DataSourceRequest calldata commonDataRequest,
Witnet.RadonReducer calldata crowdAttestationTally
)
external returns (IWitOracleRadonRequestModal);
function buildRadonRequestTemplate(
bytes32[] calldata dataRetrieveHashes,
Witnet.RadonReducer calldata dataSourcesAggregator,
Witnet.RadonReducer calldata crowdAttestationTally
) external returns (IWitOracleRadonRequestTemplate);
function buildRadonRequestTemplate(
DataSource[] calldata dataSources,
Witnet.RadonReducer calldata dataSourcesAggregator,
Witnet.RadonReducer calldata crowdAttestationTally
)
external returns (IWitOracleRadonRequestTemplate);
}
IWitOracleRadonRequestModal.sol 18 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import "../libs/Witnet.sol";
interface IWitOracleRadonRequestModal {
function getCrowdAttestationTally() external view returns (Witnet.RadonReducer memory);
function getDataResultType() external view returns (Witnet.RadonDataTypes);
function getDataSourceArgsCount(string calldata url) external view returns (uint8);
function getDataSourcesAggregator() external view returns (Witnet.RadonReducer memory);
function getRadonModalRetrieval() external view returns (Witnet.RadonRetrieval memory);
function verifyRadonRequest(string[] calldata commonRetrievalArgs, string[] calldata dataProviders) external returns (Witnet.RadonHash);
function witOracle() external view returns (address);
}
IWitOracleRadonRequestTemplate.sol 31 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import "../libs/Witnet.sol";
interface IWitOracleRadonRequestTemplate {
function getArgsCount() external view returns (uint8[] memory);
function getCrowdAttestationTally() external view returns (Witnet.RadonReducer memory);
function getDataResultType() external view returns (Witnet.RadonDataTypes);
function getDataSources() external view returns (Witnet.RadonRetrieval[] memory);
function getDataSourcesAggregator() external view returns (Witnet.RadonReducer memory);
/// Verifies into the bounded WitOracle's registry the actual bytecode
/// and RAD hash of the Witnet-compliant Radon Request that gets provably
/// made out of the data sources, aggregate and tally Radon Reducers that
/// compose this WitOracleRequestTemplate. While no WitOracleRequest instance is
/// actually constructed, the returned value will be accepted as a valid
/// RAD hash on the witOracle() contract from now on.
/// Reverts if:
/// - the ranks of passed array don't match either the number of this
/// template's data sources, or the number of required parameters by
/// each one of those.
/// @dev This method requires less gas than buildWitOracleRequest(string[][]), and
/// @dev it's usually preferred when data requests built out of this template
/// @dev are intended to be used just once in lifetime.
function verifyRadonRequest(string[][] calldata args) external returns (Witnet.RadonHash);
function witOracle() external view returns (address);
}
Bech32.sol 390 lines
// SPDX-License-Identifier: MIT
// Stratonet Contracts (last updated v1.0.0) (utils/Bech32.sol)
pragma solidity ^0.8.0;
/**
* @dev Collection of functions related to the Bech32 address generation
*/
library Bech32 {
bytes constant ALPHABET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
bytes constant ALPHABET_REV = hex"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0fff0a1115141a1e0705ffffffffffffff1dff180d19090817ff12161f1b13ff010003100b1c0c0e060402ffffffffffff1dff180d19090817ff12161f1b13ff010003100b1c0c0e060402ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
bytes constant ALPHABET_REV_LOWER_ONLY = hex"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0fff0a1115141a1e0705ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1dff180d19090817ff12161f1b13ff010003100b1c0c0e060402ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
// 0f <= 0: 48 | 30
// 0a <= 2: 50 | 32
// 11 <= 3: 51 | 33
// 15 <= 4: 52 | 34
// 14 <= 5: 53 | 35
// 1a <= 6: 54 | 36
// 1e <= 7: 55 | 37
// 07 <= 8: 56 | 38
// 05 <= 9: 57 | 39
// 1d <= a: 97, 65 | 61, 41
// 18 <= c: 99, 67 | 63, 43
// 0d <= d: 100, 68 | 64, 44
// 19 <= e: 101, 69 | 65, 45
// 09 <= f: 102, 70 | 66, 46
// 08 <= g: 103, 71 | 67, 47
// 17 <= h: 104, 72 | 68, 48
// 12 <= j: 106, 74 | 6A, 4A
// 16 <= k: 107, 75 | 6B, 4B
// 1f <= l: 108, 76 | 6C, 4C
// 1b <= m: 109, 77 | 6D, 4D
// 13 <= n: 110, 78 | 6E, 4E
// 01 <= p: 112, 80 | 70, 50
// 00 <= q: 113, 81 | 71, 51
// 03 <= r: 114, 82 | 72, 52
// 10 <= s: 115, 83 | 73, 53
// 0b <= t: 116, 84 | 74, 54
// 1c <= u: 117, 85 | 75, 55
// 0c <= v: 118, 86 | 76, 56
// 0e <= w: 119, 87 | 77, 57
// 06 <= x: 120, 88 | 78, 58
// 04 <= y: 121, 89 | 79, 59
// 02 <= z: 122, 90 | 7A, 5A
uint32 constant ENC_BECH32 = 1;
uint32 constant ENC_BECH32M = 0x2bc830a3;
function toBech32(
address addr,
string memory prefix
) internal pure returns (string memory) {
return toBech32(abi.encodePacked(addr), prefix);
}
function toBech32(
bytes memory data,
string memory prefix
) internal pure returns (string memory) {
bytes memory hrp = abi.encodePacked(prefix);
bytes memory input = convertBits(data, 8, 5, true);
return encode(hrp, input, ENC_BECH32);
}
function toBech32(
address addr,
string memory prefix,
uint8 version
) internal pure returns (string memory) {
return toBech32(abi.encodePacked(addr), prefix, version);
}
function toBech32(
bytes memory data,
string memory prefix,
uint8 version
) internal pure returns (string memory) {
bytes memory hrp = abi.encodePacked(prefix);
bytes memory input = convertBits(data, 8, 5, true);
uint32 enc = ENC_BECH32;
if (version > 0) {
enc = ENC_BECH32M;
}
bytes memory inputWithV = abi.encodePacked(bytes1(version), input);
return encode(hrp, inputWithV, enc);
}
function fromBech32(
string memory bechAddr
) internal pure returns (address) {
(, uint8[] memory data) = decode(
abi.encodePacked(bechAddr),
ENC_BECH32
);
bytes memory input = convertBits(data, 5, 8, false);
return getAddressFromBytes(input);
}
function fromBech32(
string memory bechAddr,
string memory prefix
) internal pure returns (address) {
(bytes memory dHrp, uint8[] memory data) = decode(
abi.encodePacked(bechAddr),
ENC_BECH32
);
_requireHrpMatch(abi.encodePacked(prefix), dHrp);
bytes memory input = convertBits(data, 5, 8, false);
return getAddressFromBytes(input);
}
function fromBech32WithVersion(
string memory bechAddr,
string memory prefix,
uint32 enc
) internal pure returns (uint8, bytes memory) {
(bytes memory dHrp, uint8[] memory data) = decode(
abi.encodePacked(bechAddr),
enc
);
_requireHrpMatch(abi.encodePacked(prefix), dHrp);
require(!(data.length < 1 || data[0] > 16), "Bech32: wrong version");
uint8[] memory dataNoV = new uint8[](data.length - 1);
for (uint8 i = 1; i < data.length; ++i) {
dataNoV[i - 1] = data[i];
}
bytes memory input = convertBits(dataNoV, 5, 8, false);
require(
input.length >= 2 && input.length <= 40,
"Bech32: wrong bits length"
);
require(
!(data[0] == 0 && input.length != 20 && input.length != 32),
"Bech32: wrong bits length for version"
);
return (uint8(data[0]), input);
}
function _requireHrpMatch(
bytes memory hrp1,
bytes memory hrp2
) internal pure {
require(keccak256(hrp1) == keccak256(hrp2), "Bech32: hrp mismatch");
}
function getAddressFromBytes(
bytes memory data
) internal pure returns (address) {
require(data.length == 20, "Bech32: invalid data length");
address addr;
assembly {
addr := mload(add(data, 20))
}
return addr;
}
function encode(
bytes memory hrp,
bytes memory input,
uint32 enc
) internal pure returns (string memory) {
unchecked {
uint8[] memory checksum = createChecksum(hrp, input, enc);
bytes memory result = new bytes(hrp.length + input.length + checksum.length + 1);
for (uint i; i < hrp.length; ++ i) {
result[i] = hrp[i];
}
result[hrp.length] = bytes1("1");
uint offset = hrp.length + 1;
for (uint i; i < input.length; ++ i) {
uint8 _data = uint8(input[i]);
if (_data < ALPHABET.length) {
result[i + offset] = ALPHABET[_data];
}
}
offset += input.length;
for (uint i; i < checksum.length; ++ i) {
uint8 _data = uint8(checksum[i]);
if (_data < ALPHABET.length) {
result[i + offset] = ALPHABET[_data];
}
}
return string(result);
}
}
function decode(bytes memory bechStr, uint32 enc)
internal pure
returns (bytes memory hrp, uint8[] memory data)
{
unchecked {
uint pos;
require(
bechStr.length <= 90,
"Bech32: invalid string length"
);
for (uint p = 0; p < bechStr.length; ++ p) {
uint8 charAt = uint8(bechStr[p]);
require(
charAt >= 33
&& charAt <= 126,
"Bech32: wrong char"
);
if (charAt == uint8(bytes1("1"))) {
require(
pos == 0
&& p >= 1
&& p + 7 <= bechStr.length,
"Bech32: wrong pos of 1"
);
pos = p;
}
}
hrp = new bytes(pos);
for (uint i; i < pos; ++ i) {
hrp[i] = bechStr[i];
}
data = new uint8[](bechStr.length - pos - 1);
for (uint i; i < data.length; ++ i) {
bytes1 charAt = ALPHABET_REV_LOWER_ONLY[uint8(bechStr[i + pos + 1])];
require(charAt != 0xff, "Bech32: byte not in alphabet");
data[i] = uint8(charAt);
}
require(
verifyChecksum(hrp, data, enc),
"Bech32: wrong checksum"
);
uint dataLength = data.length - 6;
assembly {
mstore(data, dataLength)
}
}
}
function hrpExpand(
bytes memory hrp
) internal pure returns (uint8[] memory ret) {
unchecked {
ret = new uint8[](hrp.length + hrp.length + 1);
for (uint p; p < hrp.length; ++ p) {
ret[p] = uint8(hrp[p]) >> 5;
ret[p + hrp.length + 1] = uint8(hrp[p]) & 31;
}
}
}
function polymod(uint32[] memory values) internal pure returns (uint32) {
uint32 chk = 1;
uint32[5] memory GEN = [
0x3b6a57b2,
0x26508e6d,
0x1ea119fa,
0x3d4233dd,
0x2a1462b3
];
unchecked {
for (uint32 i = 0; i < values.length; ++i) {
uint32 top = chk >> 25;
chk = (uint32(chk & 0x1ffffff) << 5) ^ uint32(values[i]);
for (uint32 j = 0; j < 5; ++j) {
if (((top >> j) & 1) == 1) {
chk ^= GEN[j];
}
}
}
}
return chk;
}
function createChecksum(
bytes memory hrp,
bytes memory data,
uint32 enc
) internal pure returns (uint8[] memory res) {
unchecked {
uint8[] memory values = hrpExpand(hrp);
uint32[] memory comb = new uint32[](values.length + data.length + 6);
for (uint i; i < values.length + data.length; ++ i) {
if (i < values.length) {
comb[i] = uint32(values[i]);
} else {
comb[i] = uint32(uint8(data[i - values.length]));
}
}
res = new uint8[](6);
uint32 mod = polymod(comb) ^ enc;
for (uint p = 0; p < 6; ++ p) {
res[p] = uint8((mod >> (5 * (5 - p))) & 31);
}
}
}
function verifyChecksum(
bytes memory hrp,
uint8[] memory data,
uint32 enc
) internal pure returns (bool) {
unchecked {
uint8[] memory ehrp = hrpExpand(hrp);
uint32[] memory cData = new uint32[](ehrp.length + data.length);
for (uint i; i < ehrp.length; ++ i) {
cData[i] = uint32(ehrp[i]);
}
for (uint i; i < data.length; ++ i) {
cData[i + ehrp.length] = uint32(data[i]);
}
return polymod(cData) == enc;
}
}
function convertBits(
bytes memory data,
uint frombits,
uint tobits,
bool pad
) internal pure returns (bytes memory) {
uint8[] memory dataBits = new uint8[](data.length);
for (uint32 p = 0; p < dataBits.length; ++p) {
dataBits[p] = uint8(data[p]);
}
return _convertBits(dataBits, frombits, tobits, pad);
}
function convertBits(
uint8[] memory data,
uint frombits,
uint tobits,
bool pad
) internal pure returns (bytes memory) {
return _convertBits(data, frombits, tobits, pad);
}
function _convertBits(
uint8[] memory dataBits,
uint frombits,
uint tobits,
bool pad
) internal pure returns (bytes memory ret) {
uint acc = 0;
uint bits = 0;
uint maxv = (1 << tobits) - 1;
unchecked {
for (uint p; p < dataBits.length; ++p) {
uint8 value = dataBits[p];
require(
value >= 0 && (value >> frombits) == 0,
"Bech32: value must be non-negative and fit in frombits"
);
acc = (acc << frombits) | value;
bits += frombits;
while (bits >= tobits) {
bits -= tobits;
ret = abi.encodePacked(
ret,
bytes1(uint8((acc >> bits) & maxv))
);
}
}
}
if (pad) {
if (bits > 0) {
ret = abi.encodePacked(
ret,
bytes1(uint8((acc << (tobits - bits)) & maxv))
);
}
} else {
require(
bits < frombits || ((acc << (tobits - bits)) & maxv) == 0,
"Bech32: invalid padding or value size"
);
}
}
}
Secp256k1.sol 302 lines
// SPDX-License-Identifier: MIT
pragma solidity >= 0.8.17;
/**
* @title Secp256k1 public key recovery Library
* @dev Library providing arithmetic operations over signed `secpk256k1` signed message due to recover the signer public key EC point in `Solidity`.
* @author cyphered.eth
*/
library Secp256k1 {
// Elliptic curve Constants
uint256 private constant U255_MAX_PLUS_1 =
57896044618658097711785492504343953926634992332820282019728792003956564819968;
// Curve Constants
uint256 private constant A = 0;
uint256 private constant B = 7;
uint256 private constant GX = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798;
uint256 private constant GY = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8;
uint256 private constant P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F;
uint256 private constant N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
/// @dev recovers signer public key point value.
/// @param digest hashed message
/// @param v recovery
/// @param r first 32 bytes of signature
/// @param v last 32 bytes of signature
/// @return (x, y) EC point
function recover(
uint256 digest,
uint8 v,
uint256 r,
uint256 s
) internal pure returns (uint256, uint256) {
uint256 x = addmod(r, P * (v >> 1), P);
if (x > P || s > N || r > N || s == 0 || r == 0 || v > 1) {
return (0, 0);
}
uint256 rInv = invMod(r, N);
uint256 y2 = addmod(mulmod(x, mulmod(x, x, P), P), addmod(mulmod(x, A, P), B, P), P);
y2 = expMod(y2, (P + 1) / 4);
uint256 y = ((y2 + v + 2) & 1 == 0) ? y2 : P - y2;
(uint256 qx, uint256 qy, uint256 qz) = jacMul(mulmod(rInv, N - digest, N), GX, GY, 1);
(uint256 qx2, uint256 qy2, uint256 qz2) = jacMul(mulmod(rInv, s, N), x, y, 1);
(uint256 qx3, uint256 qy3) = ecAdd(qx, qy, qz, qx2, qy2, qz2);
return (qx3, qy3);
}
/// @dev Modular exponentiation, b^e % P.
/// Source: https://github.com/witnet/elliptic-curve-solidity/blob/master/contracts/EllipticCurve.sol
/// Source: https://github.com/androlo/standard-contracts/blob/master/contracts/src/crypto/ECCMath.sol
/// @param _base base
/// @param _exp exponent
/// @return r such that r = b**e (mod P)
function expMod(uint256 _base, uint256 _exp) internal pure returns (uint256) {
if (_base == 0) return 0;
if (_exp == 0) return 1;
uint256 r = 1;
uint256 bit = U255_MAX_PLUS_1;
assembly {
for {
} gt(bit, 0) {
} {
r := mulmod(mulmod(r, r, P), exp(_base, iszero(iszero(and(_exp, bit)))), P)
r := mulmod(mulmod(r, r, P), exp(_base, iszero(iszero(and(_exp, div(bit, 2))))), P)
r := mulmod(mulmod(r, r, P), exp(_base, iszero(iszero(and(_exp, div(bit, 4))))), P)
r := mulmod(mulmod(r, r, P), exp(_base, iszero(iszero(and(_exp, div(bit, 8))))), P)
bit := div(bit, 16)
}
}
return r;
}
/// @dev Adds two points (x1, y1, z1) and (x2 y2, z2).
/// Source: https://github.com/witnet/elliptic-curve-solidity/blob/master/contracts/EllipticCurve.sol
/// @param _x1 coordinate x of P1
/// @param _y1 coordinate y of P1
/// @param _z1 coordinate z of P1
/// @param _x2 coordinate x of square
/// @param _y2 coordinate y of square
/// @param _z2 coordinate z of square
/// @return (qx, qy, qz) P1+square in Jacobian
function jacAdd(
uint256 _x1,
uint256 _y1,
uint256 _z1,
uint256 _x2,
uint256 _y2,
uint256 _z2
)
internal
pure
returns (
uint256,
uint256,
uint256
)
{
if (_x1 == 0 && _y1 == 0) return (_x2, _y2, _z2);
if (_x2 == 0 && _y2 == 0) return (_x1, _y1, _z1);
// We follow the equations described in https://pdfs.semanticscholar.org/5c64/29952e08025a9649c2b0ba32518e9a7fb5c2.pdf Section 5
uint256[4] memory zs; // z1^2, z1^3, z2^2, z2^3
zs[0] = mulmod(_z1, _z1, P);
zs[1] = mulmod(_z1, zs[0], P);
zs[2] = mulmod(_z2, _z2, P);
zs[3] = mulmod(_z2, zs[2], P);
// u1, s1, u2, s2
zs = [mulmod(_x1, zs[2], P), mulmod(_y1, zs[3], P), mulmod(_x2, zs[0], P), mulmod(_y2, zs[1], P)];
// In case of zs[0] == zs[2] && zs[1] == zs[3], double function should be used
require(zs[0] != zs[2] || zs[1] != zs[3], 'Use jacDouble function instead');
uint256[4] memory hr;
//h
hr[0] = addmod(zs[2], P - zs[0], P);
//r
hr[1] = addmod(zs[3], P - zs[1], P);
//h^2
hr[2] = mulmod(hr[0], hr[0], P);
// h^3
hr[3] = mulmod(hr[2], hr[0], P);
// qx = -h^3 -2u1h^2+r^2
uint256 qx = addmod(mulmod(hr[1], hr[1], P), P - hr[3], P);
qx = addmod(qx, P - mulmod(2, mulmod(zs[0], hr[2], P), P), P);
// qy = -s1*z1*h^3+r(u1*h^2 -x^3)
uint256 qy = mulmod(hr[1], addmod(mulmod(zs[0], hr[2], P), P - qx, P), P);
qy = addmod(qy, P - mulmod(zs[1], hr[3], P), P);
// qz = h*z1*z2
uint256 qz = mulmod(hr[0], mulmod(_z1, _z2, P), P);
return (qx, qy, qz);
}
/// @dev Multiply point (x, y, z) times d.
/// Source: https://github.com/witnet/elliptic-curve-solidity/blob/master/contracts/EllipticCurve.sol
/// @param _d scalar to multiply
/// @param _x coordinate x of P1
/// @param _y coordinate y of P1
/// @param _z coordinate z of P1
/// @return (qx, qy, qz) d*P1 in Jacobian
function jacMul(
uint256 _d,
uint256 _x,
uint256 _y,
uint256 _z
)
internal
pure
returns (
uint256,
uint256,
uint256
)
{
// Early return in case that `_d == 0`
if (_d == 0) {
return (_x, _y, _z);
}
uint256 remaining = _d;
uint256 qx = 0;
uint256 qy = 0;
uint256 qz = 1;
// Double and add algorithm
while (remaining != 0) {
if ((remaining & 1) != 0) {
(qx, qy, qz) = jacAdd(qx, qy, qz, _x, _y, _z);
}
remaining = remaining / 2;
(_x, _y, _z) = jacDouble(_x, _y, _z);
}
return (qx, qy, qz);
}
/// @dev Doubles a points (x, y, z).
/// Source: https://github.com/witnet/elliptic-curve-solidity/blob/master/contracts/EllipticCurve.sol
/// @param _x coordinate x of P1
/// @param _y coordinate y of P1
/// @param _z coordinate z of P1
/// @return (qx, qy, qz) 2P in Jacobian
function jacDouble(
uint256 _x,
uint256 _y,
uint256 _z
)
internal
pure
returns (
uint256,
uint256,
uint256
)
{
if (_z == 0) return (_x, _y, _z);
// We follow the equations described in https://pdfs.semanticscholar.org/5c64/29952e08025a9649c2b0ba32518e9a7fb5c2.pdf Section 5
// Note: there is a bug in the paper regarding the m parameter, M=3*(x1^2)+a*(z1^4)
// x, y, z at this point represent the squares of _x, _y, _z
uint256 x = mulmod(_x, _x, P); //x1^2
uint256 y = mulmod(_y, _y, P); //y1^2
uint256 z = mulmod(_z, _z, P); //z1^2
// s
uint256 s = mulmod(4, mulmod(_x, y, P), P);
// m
uint256 m = addmod(mulmod(3, x, P), mulmod(A, mulmod(z, z, P), P), P);
// x, y, z at this point will be reassigned and rather represent qx, qy, qz from the paper
// This allows to reduce the gas cost and stack footprint of the algorithm
// qx
x = addmod(mulmod(m, m, P), P - addmod(s, s, P), P);
// qy = -8*y1^4 + M(S-T)
y = addmod(mulmod(m, addmod(s, P - x, P), P), P - mulmod(8, mulmod(y, y, P), P), P);
// qz = 2*y1*z1
z = mulmod(2, mulmod(_y, _z, P), P);
return (x, y, z);
}
/// @dev Add two points (x1, y1) and (x2, y2) in affine coordinates.
/// Source: https://github.com/witnet/elliptic-curve-solidity/blob/master/contracts/EllipticCurve.sol
/// @param _x1 coordinate x of P1
/// @param _y1 coordinate y of P1
/// @param _x2 coordinate x of P2
/// @param _y2 coordinate y of P2
/// @return (qx, qy) = P1+P2 in affine coordinates
function ecAdd(
uint256 _x1,
uint256 _y1,
uint256 _z1,
uint256 _x2,
uint256 _y2,
uint256 _z2
) internal pure returns (uint256, uint256) {
uint256 x = 0;
uint256 y = 0;
uint256 z = 0;
// Double if x1==x2 else add
if (_x1 == _x2) {
// y1 = -y2 mod p
if (addmod(_y1, _y2, P) == 0) {
return (0, 0);
} else {
// P1 = P2
(x, y, z) = jacDouble(_x1, _y1, _z1);
}
} else {
(x, y, z) = jacAdd(_x1, _y1, _z1, _x2, _y2, _z2);
}
// Get back to affine
return toAffine(x, y, z);
}
/// @dev Converts a point (x, y, z) expressed in Jacobian coordinates to affine coordinates (x', y', 1).
/// Source: https://github.com/witnet/elliptic-curve-solidity/blob/master/contracts/EllipticCurve.sol
/// @param _x coordinate x
/// @param _y coordinate y
/// @param _z coordinate z
/// @return (x', y') affine coordinates
function toAffine(
uint256 _x,
uint256 _y,
uint256 _z
) internal pure returns (uint256, uint256) {
uint256 zInv = invMod(_z, P);
uint256 zInv2 = mulmod(zInv, zInv, P);
uint256 x2 = mulmod(_x, zInv2, P);
uint256 y2 = mulmod(_y, mulmod(zInv, zInv2, P), P);
return (x2, y2);
}
/// @dev Modular euclidean inverse of a number (mod p).
/// Source: https://github.com/witnet/elliptic-curve-solidity/blob/master/contracts/EllipticCurve.sol
/// @param _x The number
/// @param _pp The modulus
/// @return q such that x*q = 1 (mod _pp)
function invMod(uint256 _x, uint256 _pp) internal pure returns (uint256) {
require(_x != 0 && _x != _pp && _pp != 0, 'Invalid number');
uint256 q = 0;
uint256 newT = 1;
uint256 r = _pp;
uint256 t;
while (_x != 0) {
t = r / _x;
(q, newT) = (newT, addmod(q, (_pp - mulmod(t, newT, _pp)), _pp));
(r, _x) = (_x, r - t * _x);
}
return q;
}
}
Witnet.sol 1328 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import "./Bech32.sol";
import "./Secp256k1.sol";
import "./WitnetCBOR.sol";
library Witnet {
using Bech32 for Witnet.Address;
using WitnetBuffer for WitnetBuffer.Buffer;
using WitnetCBOR for WitnetCBOR.CBOR;
using WitnetCBOR for WitnetCBOR.CBOR[];
type Address is bytes20;
type BlockNumber is uint64;
type QueryEvmReward is uint72;
type QueryUUID is bytes15;
type QueryId is uint64;
type RadonHash is bytes32;
type ServiceProvider is bytes20;
type Timestamp is uint64;
type TransactionHash is bytes32;
uint32 constant internal WIT_1_GENESIS_TIMESTAMP = 0; // TBD
uint32 constant internal WIT_1_SECS_PER_EPOCH = 45;
uint32 constant internal WIT_2_GENESIS_BEACON_INDEX = 0; // TBD
uint32 constant internal WIT_2_GENESIS_BEACON_PREV_INDEX = 0; // TBD
bytes24 constant internal WIT_2_GENESIS_BEACON_PREV_ROOT = 0; // TBD
bytes16 constant internal WIT_2_GENESIS_BEACON_DDR_TALLIES_MERKLE_ROOT = 0; // TBD
bytes16 constant internal WIT_2_GENESIS_BEACON_DRO_TALLIES_MERKLE_ROOT = 0; // TBD
uint256 constant internal WIT_2_GENESIS_BEACON_NEXT_COMMITTEE_AGG_PUBKEY_0 = 0; // TBD
uint256 constant internal WIT_2_GENESIS_BEACON_NEXT_COMMITTEE_AGG_PUBKEY_1 = 0; // TBD
uint256 constant internal WIT_2_GENESIS_BEACON_NEXT_COMMITTEE_AGG_PUBKEY_2 = 0; // TBD
uint256 constant internal WIT_2_GENESIS_BEACON_NEXT_COMMITTEE_AGG_PUBKEY_3 = 0; // TBD
uint32 constant internal WIT_2_GENESIS_EPOCH = 0; // TBD
uint32 constant internal WIT_2_GENESIS_TIMESTAMP = 0; // TBD
uint32 constant internal WIT_2_SECS_PER_EPOCH = 20; // TBD
uint32 constant internal WIT_2_FAST_FORWARD_COMMITTEE_SIZE = 64; // TBD
function channel(address wrb) internal view returns (bytes4) {
return bytes4(keccak256(abi.encode(address(wrb), block.chainid)));
}
struct Beacon {
uint32 index;
uint32 prevIndex;
bytes24 prevRoot;
bytes16 ddrTalliesMerkleRoot;
bytes16 droTalliesMerkleRoot;
uint256[4] nextCommitteeAggPubkey;
}
struct DataPullReport {
QueryId queryId;
QueryUUID queryHash; // KECCAK256(channel | blockhash(block.number - 1) | ...)
bytes witDrRelayerSignature; // ECDSA.signature(queryHash)
BlockNumber witDrResultEpoch;
bytes witDrResultCborBytes;
TransactionHash witDrTxHash;
}
struct DataPushReport {
TransactionHash witDrTxHash;
RadonHash queryRadHash;
QuerySLA queryParams;
Timestamp resultTimestamp;
bytes resultCborBytes;
}
/// Data struct containing the Witnet-provided result to a Data Request.
struct DataResult {
ResultStatus status;
RadonDataTypes dataType;
TransactionHash drTxHash;
uint256 finality;
Timestamp timestamp;
WitnetCBOR.CBOR value;
}
struct FastForward {
Beacon beacon;
uint256[2] committeeAggSignature;
uint256[4][] committeeMissingPubkeys;
}
/// Struct containing both request and response data related to every query posted to the Witnet Request Board
struct Query {
QueryRequest request;
QueryResponse response;
QuerySLA slaParams; // Minimum Service-Level parameters to be committed by the Witnet blockchain.
QueryUUID uuid; // Universal unique identifier determined by the payload, WRB instance, chain id and EVM's previous block hash.
QueryEvmReward reward; // EVM amount in wei eventually to be paid to the legit reporter.
BlockNumber checkpoint;
}
/// Possible status of a Witnet query.
enum QueryStatus {
Unknown,
Posted,
Reported,
Finalized,
Delayed,
Expired,
Disputed
}
struct QueryCallback {
address consumer; // consumer contract address to which the query result will be reported
uint24 gasLimit; // expected max amount of gas required by the callback method in the consumer contract
}
/// Data kept in EVM-storage for every Request posted to the Witnet Request Board.
struct QueryRequest {
address requester; // EVM address from which the request was posted.
uint24 callbackGas; // Max callback gas limit upon response, if a callback is required.
bytes radonBytecode; // Optional: Witnet Data Request bytecode to be solved by the Witnet blockchain.
RadonHash radonHash; // Optional: Previously verified hash of the Witnet Data Request to be solved.
}
/// QueryResponse metadata and result as resolved by the Witnet blockchain.
struct QueryResponse {
address reporter; // EVM address from which the Data Request result was reported.
Timestamp resultTimestamp; // Unix timestamp (seconds) at which the data request was resolved in the Witnet blockchain.
TransactionHash resultDrTxHash; // Unique hash of the commit/reveal act in the Witnet blockchain that resolved the data request.
bytes resultCborBytes; // CBOR-encode result to the request, as resolved in the Witnet blockchain.
address disputer;
}
/// Structure containing all possible SLA security parameters for Wit/2.1 Data Requests
struct QuerySLA {
uint16 witResultMaxSize; // max size permitted to whatever query result may come from the Wit/Oracle blockchain.
uint16 witCommitteeSize; // max number of eligibile witnesses in the Wit/Oracle blockchain for solving some query.
uint64 witUnitaryReward; // min fees in nanowits to be paid for getting the query solved and reported from the Wit/Oracle.
}
enum ResultStatus {
/// 0x00: No errors.
NoErrors,
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Source-specific format error sub-codes ============================================================================
/// 0x01: At least one of the source scripts is not a valid CBOR-encoded value.
SourceScriptNotCBOR,
/// 0x02: The CBOR value decoded from a source script is not an Array.
SourceScriptNotArray,
/// 0x03: The Array value decoded form a source script is not a valid Data Request.
SourceScriptNotRADON,
/// 0x04: The request body of at least one data source was not properly formated.
SourceRequestBody,
/// 0x05: The request headers of at least one data source was not properly formated.
SourceRequestHeaders,
/// 0x06: The request URL of at least one data source was not properly formated.
SourceRequestURL,
/// Unallocated
SourceFormat0x07, SourceFormat0x08, SourceFormat0x09, SourceFormat0x0A, SourceFormat0x0B, SourceFormat0x0C,
SourceFormat0x0D, SourceFormat0x0E, SourceFormat0x0F,
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Complexity error sub-codes ========================================================================================
/// 0x10: The request contains too many sources.
RequestTooManySources,
/// 0x11: The script contains too many calls.
ScriptTooManyCalls,
/// Unallocated
Complexity0x12, Complexity0x13, Complexity0x14, Complexity0x15, Complexity0x16, Complexity0x17, Complexity0x18,
Complexity0x19, Complexity0x1A, Complexity0x1B, Complexity0x1C, Complexity0x1D, Complexity0x1E, Complexity0x1F,
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Lack of support error sub-codes ===================================================================================
/// 0x20: Some Radon operator code was found that is not supported (1+ args).
UnsupportedOperator,
/// 0x21: Some Radon filter opcode is not currently supported (1+ args).
UnsupportedFilter,
/// 0x22: Some Radon request type is not currently supported (1+ args).
UnsupportedHashFunction,
/// 0x23: Some Radon reducer opcode is not currently supported (1+ args)
UnsupportedReducer,
/// 0x24: Some Radon hash function is not currently supported (1+ args).
UnsupportedRequestType,
/// 0x25: Some Radon encoding function is not currently supported (1+ args).
UnsupportedEncodingFunction,
/// Unallocated
Operator0x26, Operator0x27,
/// 0x28: Wrong number (or type) of arguments were passed to some Radon operator.
WrongArguments,
/// Unallocated
Operator0x29, Operator0x2A, Operator0x2B, Operator0x2C, Operator0x2D, Operator0x2E, Operator0x2F,
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Retrieve-specific circumstantial error sub-codes ================================================================================
/// 0x30: A majority of data sources returned an HTTP status code other than 200 (1+ args):
HttpErrors,
/// 0x31: A majority of data sources timed out:
RetrievalsTimeout,
/// Unallocated
RetrieveCircumstance0x32, RetrieveCircumstance0x33, RetrieveCircumstance0x34, RetrieveCircumstance0x35,
RetrieveCircumstance0x36, RetrieveCircumstance0x37, RetrieveCircumstance0x38, RetrieveCircumstance0x39,
RetrieveCircumstance0x3A, RetrieveCircumstance0x3B, RetrieveCircumstance0x3C, RetrieveCircumstance0x3D,
RetrieveCircumstance0x3E, RetrieveCircumstance0x3F,
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Scripting-specific runtime error sub-code =========================================================================
/// 0x40: Math operator caused an underflow.
MathUnderflow,
/// 0x41: Math operator caused an overflow.
MathOverflow,
/// 0x42: Math operator tried to divide by zero.
MathDivisionByZero,
/// 0x43: Wrong input to subscript call.
WrongSubscriptInput,
/// 0x44: Value cannot be extracted from input binary buffer.
BufferIsNotValue,
/// 0x45: Value cannot be decoded from expected type.
Decode,
/// 0x46: Unexpected empty array.
EmptyArray,
/// 0x47: Value cannot be encoded to expected type.
Encode,
/// 0x48: Failed to filter input values (1+ args).
Filter,
/// 0x49: Failed to hash input value.
Hash,
/// 0x4A: Mismatching array ranks.
MismatchingArrays,
/// 0x4B: Failed to process non-homogenous array.
NonHomegeneousArray,
/// 0x4C: Failed to parse syntax of some input value, or argument.
Parse,
/// 0x4D: Parsing logic limits were exceeded.
ParseOverflow,
/// Unallocated
ScriptError0x4E, ScriptError0x4F,
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Actual first-order result error codes =============================================================================
/// 0x50: Not enough reveals were received in due time:
InsufficientReveals,
/// 0x51: No actual reveal majority was reached on tally stage:
InsufficientMajority,
/// 0x52: Not enough commits were received before tally stage:
InsufficientCommits,
/// 0x53: Generic error during tally execution (to be deprecated after WIP #0028)
TallyExecution,
/// 0x54: A majority of data sources could either be temporarily unresponsive or failing to report the requested data:
CircumstantialFailure,
/// 0x55: At least one data source is inconsistent when queried through multiple transports at once:
InconsistentSources,
/// 0x56: Any one of the (multiple) Retrieve, Aggregate or Tally scripts were badly formated:
MalformedDataRequest,
/// 0x57: Values returned from a majority of data sources don't match the expected schema:
MalformedQueryResponses,
/// Unallocated:
OtherError0x58, OtherError0x59, OtherError0x5A, OtherError0x5B, OtherError0x5C, OtherError0x5D, OtherError0x5E,
/// 0x5F: Size of serialized tally result exceeds allowance:
OversizedTallyResult,
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Inter-stage runtime error sub-codes ===============================================================================
/// 0x60: Data aggregation reveals could not get decoded on the tally stage:
MalformedReveals,
/// 0x61: The result to data aggregation could not get encoded:
EncodeReveals,
/// 0x62: A mode tie ocurred when calculating some mode value on the aggregation or the tally stage:
ModeTie,
/// Unallocated:
OtherError0x63, OtherError0x64, OtherError0x65, OtherError0x66, OtherError0x67, OtherError0x68, OtherError0x69,
OtherError0x6A, OtherError0x6B, OtherError0x6C, OtherError0x6D, OtherError0x6E, OtherError0x6F,
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Runtime access error sub-codes ====================================================================================
/// 0x70: Tried to access a value from an array using an index that is out of bounds (1+ args):
ArrayIndexOutOfBounds,
/// 0x71: Tried to access a value from a map using a key that does not exist (1+ args):
MapKeyNotFound,
/// 0X72: Tried to extract value from a map using a JSON Path that returns no values (+1 args):
JsonPathNotFound,
/// Unallocated:
OtherError0x73, OtherError0x74, OtherError0x75, OtherError0x76, OtherError0x77, OtherError0x78,
OtherError0x79, OtherError0x7A, OtherError0x7B, OtherError0x7C, OtherError0x7D, OtherError0x7E, OtherError0x7F,
OtherError0x80, OtherError0x81, OtherError0x82, OtherError0x83, OtherError0x84, OtherError0x85, OtherError0x86,
OtherError0x87, OtherError0x88, OtherError0x89, OtherError0x8A, OtherError0x8B, OtherError0x8C, OtherError0x8D,
OtherError0x8E, OtherError0x8F, OtherError0x90, OtherError0x91, OtherError0x92, OtherError0x93, OtherError0x94,
OtherError0x95, OtherError0x96, OtherError0x97, OtherError0x98, OtherError0x99, OtherError0x9A, OtherError0x9B,
OtherError0x9C, OtherError0x9D, OtherError0x9E, OtherError0x9F, OtherError0xA0, OtherError0xA1, OtherError0xA2,
OtherError0xA3, OtherError0xA4, OtherError0xA5, OtherError0xA6, OtherError0xA7, OtherError0xA8, OtherError0xA9,
OtherError0xAA, OtherError0xAB, OtherError0xAC, OtherError0xAD, OtherError0xAE, OtherError0xAF, OtherError0xB0,
OtherError0xB1, OtherError0xB2, OtherError0xB3, OtherError0xB4, OtherError0xB5, OtherError0xB6, OtherError0xB7,
OtherError0xB8, OtherError0xB9, OtherError0xBA, OtherError0xBB, OtherError0xBC, OtherError0xBD, OtherError0xBE,
OtherError0xBF, OtherError0xC0, OtherError0xC1, OtherError0xC2, OtherError0xC3, OtherError0xC4, OtherError0xC5,
OtherError0xC6, OtherError0xC7, OtherError0xC8, OtherError0xC9, OtherError0xCA, OtherError0xCB, OtherError0xCC,
OtherError0xCD, OtherError0xCE, OtherError0xCF, OtherError0xD0, OtherError0xD1, OtherError0xD2, OtherError0xD3,
OtherError0xD4, OtherError0xD5, OtherError0xD6, OtherError0xD7, OtherError0xD8, OtherError0xD9, OtherError0xDA,
OtherError0xDB, OtherError0xDC, OtherError0xDD, OtherError0xDE, OtherError0xDF,
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Inter-client generic error codes ==================================================================================
/// Data requests that cannot be relayed into the Witnet blockchain should be reported
/// with one of these errors.
/// 0xE0: Requests that cannot be parsed must always get this error as their result.
BridgeMalformedDataRequest,
/// 0xE1: Witnesses exceeds 100
BridgePoorIncentives,
/// 0xE2: The request is rejected on the grounds that it may cause the submitter to spend or stake an
/// amount of value that is unjustifiably high when compared with the reward they will be getting
BridgeOversizedTallyResult,
/// Unallocated:
OtherError0xE3, OtherError0xE4, OtherError0xE5, OtherError0xE6, OtherError0xE7, OtherError0xE8, OtherError0xE9,
OtherError0xEA, OtherError0xEB, OtherError0xEC, OtherError0xED, OtherError0xEE, OtherError0xEF,
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Transient errors as determined by the Request Board contract ======================================================
/// 0xF0:
BoardAwaitingResult,
/// 0xF1:
BoardFinalizingResult,
/// 0xF2:
BoardBeingDisputed,
/// Unallocated
OtherError0xF3, OtherError0xF4, OtherError0xF5, OtherError0xF6, OtherError0xF7,
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Final errors as determined by the Request Board contract ==========================================================
/// 0xF8:
BoardAlreadyDelivered,
/// 0xF9:
BoardResolutionTimeout,
/// Unallocated:
OtherError0xFA, OtherError0xFB, OtherError0xFC, OtherError0xFD, OtherError0xFE,
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// 0xFF: Some tally error is not intercepted but it should (0+ args)
UnhandledIntercept
}
/// Possible types either processed by Witnet Radon Scripts or included within results to Witnet Data Requests.
enum RadonDataTypes {
/* 0x00 */ Any,
/* 0x01 */ Array,
/* 0x02 */ Bool,
/* 0x03 */ Bytes,
/* 0x04 */ Integer,
/* 0x05 */ Float,
/* 0x06 */ Map,
/* 0x07 */ String,
Unused0x08, Unused0x09, Unused0x0A, Unused0x0B,
Unused0x0C, Unused0x0D, Unused0x0E, Unused0x0F,
/* 0x10 */ Same,
/* 0x11 */ Inner,
/* 0x12 */ Match,
/* 0x13 */ Subscript
}
/// Structure defining some data filtering that can be applied at the Aggregation or the Tally stages
/// within a Witnet Data Request resolution workflow.
struct RadonFilter {
RadonFilterOpcodes opcode;
bytes cborArgs;
}
/// Filtering methods currently supported on the Witnet blockchain.
enum RadonFilterOpcodes {
/* 0x00 */ Reserved0x00, //GreaterThan,
/* 0x01 */ Reserved0x01, //LessThan,
/* 0x02 */ Reserved0x02, //Equals,
/* 0x03 */ Reserved0x03, //AbsoluteDeviation,
/* 0x04 */ Reserved0x04, //RelativeDeviation
/* 0x05 */ StandardDeviation,
/* 0x06 */ Reserved0x06, //Top,
/* 0x07 */ Reserved0x07, //Bottom,
/* 0x08 */ Mode,
/* 0x09 */ Reserved0x09 //LessOrEqualThan
}
/// Structure defining the array of filters and reducting function to be applied at either the Aggregation
/// or the Tally stages within a Witnet Data Request resolution workflow.
struct RadonReducer {
RadonReduceOpcodes opcode;
RadonFilter[] filters;
}
/// Reducting functions currently supported on the Witnet blockchain.
enum RadonReduceOpcodes {
/* 0x00 */ Reserved0x00, //Minimum,
/* 0x01 */ Reserved0x01, //Maximum,
/* 0x02 */ Mode,
/* 0x03 */ AverageMean,
/* 0x04 */ Reserved0x04, //AverageMeanWeighted,
/* 0x05 */ AverageMedian,
/* 0x06 */ Reserved0x06, //AverageMedianWeighted,
/* 0x07 */ StandardDeviation,
/* 0x08 */ Reserved0x08, //AverageDeviation,
/* 0x09 */ Reserved0x09, //MedianDeviation,
/* 0x0A */ Reserved0x10, //MaximumDeviation,
/* 0x0B */ ConcatenateAndHash
}
/// Structure containing the Retrieve-Attestation-Delivery parts of a Witnet-compliant Data Request.
struct RadonRequest {
RadonRetrieval[] retrieve;
RadonReducer aggregate;
RadonReducer tally;
}
/// Structure containing all the parameters that fully describe a Witnet Radon Retrieval within a Witnet Data Request.
struct RadonRetrieval {
uint8 argsCount;
RadonRetrievalMethods method;
RadonDataTypes dataType;
string url;
string body;
string[2][] headers;
bytes radonScript;
}
/// Possible Radon retrieval methods that can be used within a Radon Retrieval.
enum RadonRetrievalMethods {
/* 0 */ Unknown,
/* 1 */ HttpGet,
/* 2 */ RNG,
/* 3 */ HttpPost,
/* 4 */ HttpHead
}
/// Structure containing all possible SLA security parameters of a Witnet-compliant Data Request.
struct RadonSLAv1 {
uint8 numWitnesses;
uint8 minConsensusPercentage;
uint64 witnessReward;
uint64 witnessCollateral;
uint64 minerCommitRevealFee;
}
/// =======================================================================
/// --- Witnet.Address helper functions -----------------------------------
function eq(Address a, Address b) internal pure returns (bool) {
return Address.unwrap(a) == Address.unwrap(b);
}
function fromBech32(string memory pkh, bool mainnet) internal pure returns (Address) {
require(bytes(pkh).length == (mainnet ? 42 : 43), "Bech32: invalid length");
return Address.wrap(bytes20(Bech32.fromBech32(pkh, mainnet ? "wit" : "twit")));
}
function toBech32(Address witAddress, bool mainnet) internal pure returns (string memory) {
return Bech32.toBech32(address(Address.unwrap(witAddress)), mainnet ? "wit" : "twit");
}
function isZero(Address a) internal pure returns (bool) {
return Address.unwrap(a) == bytes20(0);
}
/// =======================================================================
/// --- Witnet.Beacon helper functions ------------------------------------
function equals(Beacon storage self, Beacon calldata other)
internal view returns (bool)
{
return (
root(self) == root(other)
);
}
function root(Beacon calldata self) internal pure returns (bytes24) {
return bytes24(keccak256(abi.encode(
self.index,
self.prevIndex,
self.prevRoot,
self.ddrTalliesMerkleRoot,
self.droTalliesMerkleRoot,
self.nextCommitteeAggPubkey
)));
}
function root(Beacon storage self) internal view returns (bytes24) {
return bytes24(keccak256(abi.encode(
self.index,
self.prevIndex,
self.prevRoot,
self.ddrTalliesMerkleRoot,
self.droTalliesMerkleRoot,
self.nextCommitteeAggPubkey
)));
}
/// =======================================================================
/// --- BlockNumber helper functions --------------------------------------
function egt(BlockNumber a, BlockNumber b) internal pure returns (bool) {
return BlockNumber.unwrap(a) >= BlockNumber.unwrap(b);
}
function elt(BlockNumber a, BlockNumber b) internal pure returns (bool) {
return BlockNumber.unwrap(a) <= BlockNumber.unwrap(b);
}
function gt(BlockNumber a, BlockNumber b) internal pure returns (bool) {
return BlockNumber.unwrap(a) > BlockNumber.unwrap(b);
}
function lt(BlockNumber a, BlockNumber b) internal pure returns (bool) {
return BlockNumber.unwrap(a) < BlockNumber.unwrap(b);
}
function isZero(BlockNumber b) internal pure returns (bool) {
return (BlockNumber.unwrap(b) == 0);
}
/// ===============================================================================================================
/// --- Data*Report helper methods --------------------------------------------------------------------------------
function queryRelayer(DataPullReport calldata self) internal pure returns (address) {
return recoverEvmAddr(
self.witDrRelayerSignature,
hashify(self.queryHash)
);
}
function tallyHash(DataPullReport calldata self) internal pure returns (bytes32) {
return keccak256(abi.encode(
self.queryHash,
self.witDrRelayerSignature,
self.witDrTxHash,
self.witDrResultEpoch,
self.witDrResultCborBytes
));
}
function digest(DataPushReport calldata self) internal pure returns (bytes32) {
return keccak256(abi.encode(
self.witDrTxHash,
self.queryRadHash,
self.queryParams.witResultMaxSize,
self.queryParams.witCommitteeSize,
self.queryParams.witUnitaryReward,
self.resultTimestamp,
self.resultCborBytes
));
}
/// ========================================================================================================
/// --- 'DataResult' helper methods ------------------------------------------------------------------------
function noErrors(DataResult memory self) internal pure returns (bool) {
return self.status == ResultStatus.NoErrors;
}
function keepWaiting(DataResult memory self) internal pure returns (bool) {
return keepWaiting(self.status);
}
function hasErrors(DataResult memory self) internal pure returns (bool) {
return hasErrors(self.status);
}
modifier _checkDataType(DataResult memory self, RadonDataTypes expectedDataType) {
require(
!keepWaiting(self)
&& self.dataType == expectedDataType
, "cbor: cannot fetch data"
); _;
self.dataType = peekRadonDataType(self.value);
}
function fetchAddress(DataResult memory self)
internal pure
_checkDataType(self, RadonDataTypes.Bytes)
returns (address _res)
{
return toAddress(self.value.readBytes());
}
function fetchBool(DataResult memory self)
internal pure
_checkDataType(self, RadonDataTypes.Bool)
returns (bool)
{
return self.value.readBool();
}
function fetchBytes(DataResult memory self)
internal pure
_checkDataType(self, RadonDataTypes.Bytes)
returns (bytes memory)
{
return self.value.readBytes();
}
function fetchBytes4(DataResult memory self)
internal pure
_checkDataType(self, RadonDataTypes.Bytes)
returns (bytes4)
{
return toBytes4(self.value.readBytes());
}
function fetchBytes32(DataResult memory self)
internal pure
_checkDataType(self, RadonDataTypes.Bytes)
returns (bytes32)
{
return toBytes32(self.value.readBytes());
}
function fetchCborArray(DataResult memory self)
internal pure
_checkDataType(self, RadonDataTypes.Array)
returns (WitnetCBOR.CBOR[] memory)
{
return self.value.readArray();
}
/// @dev Decode a fixed16 (half-precision) numeric value from the Result's CBOR value.
/// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values.
/// by 5 decimal orders so as to get a fixed precision of 5 decimal positions, which should be OK for most `fixed16`.
/// use cases. In other words, the output of this method is 10,000 times the actual value, encoded into an `int32`.
function fetchFloatFixed16(DataResult memory self)
internal pure
_checkDataType(self, RadonDataTypes.Float)
returns (int32)
{
return self.value.readFloat16();
}
/// @dev Decode an array of fixed16 values from the Result's CBOR value.
function fetchFloatFixed16Array(DataResult memory self)
internal pure
_checkDataType(self, RadonDataTypes.Array)
returns (int32[] memory)
{
return self.value.readFloat16Array();
}
/// @dev Decode a `int64` value from the DataResult's CBOR value.
function fetchInt(DataResult memory self)
internal pure
_checkDataType(self, RadonDataTypes.Integer)
returns (int64)
{
return self.value.readInt();
}
function fetchInt64Array(DataResult memory self)
internal pure
_checkDataType(self, RadonDataTypes.Array)
returns (int64[] memory)
{
return self.value.readIntArray();
}
function fetchString(DataResult memory self)
internal pure
_checkDataType(self, RadonDataTypes.String)
returns (string memory)
{
return self.value.readString();
}
function fetchStringArray(DataResult memory self)
internal pure
_checkDataType(self, RadonDataTypes.Array)
returns (string[] memory)
{
return self.value.readStringArray();
}
/// @dev Decode a `uint64` value from the DataResult's CBOR value.
function fetchUint(DataResult memory self)
internal pure
_checkDataType(self, RadonDataTypes.Integer)
returns (uint64)
{
return self.value.readUint();
}
function fetchUint64Array(DataResult memory self)
internal pure
_checkDataType(self, RadonDataTypes.Array)
returns (uint64[] memory)
{
return self.value.readUintArray();
}
bytes7 private constant _CBOR_MAJOR_TYPE_TO_RADON_DATA_TYPES_MAP = 0x04040307010600;
function peekRadonDataType(WitnetCBOR.CBOR memory cbor) internal pure returns (RadonDataTypes _type) {
_type = RadonDataTypes.Any;
if (!cbor.eof()) {
if (cbor.majorType <= 6) {
return RadonDataTypes(uint8(bytes1(_CBOR_MAJOR_TYPE_TO_RADON_DATA_TYPES_MAP[cbor.majorType])));
} else if (cbor.majorType == 7) {
if (cbor.additionalInformation == 20 || cbor.additionalInformation == 21) {
return RadonDataTypes.Bool;
} else if (cbor.additionalInformation >= 25 && cbor.additionalInformation <= 27) {
return RadonDataTypes.Float;
}
}
}
}
/// =======================================================================
/// --- FastForward helper functions --------------------------------------
function head(FastForward[] calldata rollup)
internal pure returns (Beacon calldata)
{
return rollup[rollup.length - 1].beacon;
}
/// ===============================================================================================================
/// --- Query* helper methods -------------------------------------------------------------------------------------
function equalOrGreaterThan(QuerySLA calldata self, QuerySLA storage stored) internal view returns (bool) {
return (
self.witCommitteeSize >= stored.witCommitteeSize
&& self.witUnitaryReward >= stored.witUnitaryReward
&& self.witResultMaxSize <= stored.witResultMaxSize
);
}
function hashify(QueryUUID hash) internal pure returns (bytes32) {
return keccak256(abi.encode(QueryUUID.unwrap(hash)));
}
function hashify(QueryId _queryId, Witnet.RadonHash _radHash, bytes32 _slaHash) internal view returns (Witnet.QueryUUID) {
return Witnet.QueryUUID.wrap(bytes15(
keccak256(abi.encode(
channel(address(this)),
blockhash(block.number - 1),
_queryId, Witnet.RadonHash.unwrap(_radHash), _slaHash
))
));
}
function hashify(QuerySLA memory querySLA) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(
querySLA.witResultMaxSize,
querySLA.witCommitteeSize,
querySLA.witUnitaryReward
));
}
function isValid(QuerySLA memory self) internal pure returns (bool) {
return (
self.witResultMaxSize >= 0
&& self.witCommitteeSize > 0
&& self.witCommitteeSize <= 127
&& self.witUnitaryReward > 0
);
}
function isZero(QueryId a) internal pure returns (bool) {
return (QueryId.unwrap(a) == 0);
}
function toV1(QuerySLA calldata self) internal pure returns (RadonSLAv1 memory) {
return RadonSLAv1({
numWitnesses: uint8(self.witCommitteeSize),
minConsensusPercentage: 51,
witnessReward: self.witUnitaryReward,
witnessCollateral: self.witUnitaryReward * self.witCommitteeSize,
minerCommitRevealFee: self.witUnitaryReward / self.witCommitteeSize
});
}
/// ===============================================================================================================
/// --- RadonHash helper methods ----------------------------------------------------------------------------------
function eq(RadonHash a, RadonHash b) internal pure returns (bool) {
return RadonHash.unwrap(a) == RadonHash.unwrap(b);
}
function isZero(RadonHash h) internal pure returns (bool) {
return RadonHash.unwrap(h) == bytes32(0);
}
/// ===============================================================================================================
/// --- ResultStatus helper methods -------------------------------------------------------------------------------
function hasErrors(ResultStatus self) internal pure returns (bool) {
return (
self != ResultStatus.NoErrors
&& !keepWaiting(self)
);
}
function isCircumstantial(ResultStatus self) internal pure returns (bool) {
return (self == ResultStatus.CircumstantialFailure);
}
function isRetriable(ResultStatus self) internal pure returns (bool) {
return (
lackOfConsensus(self)
|| isCircumstantial(self)
|| poorIncentives(self)
);
}
function keepWaiting(ResultStatus self) internal pure returns (bool) {
return (
self == ResultStatus.BoardAwaitingResult
|| self == ResultStatus.BoardFinalizingResult
);
}
function lackOfConsensus(ResultStatus self) internal pure returns (bool) {
return (
self == ResultStatus.InsufficientCommits
|| self == ResultStatus.InsufficientMajority
|| self == ResultStatus.InsufficientReveals
);
}
function poorIncentives(ResultStatus self) internal pure returns (bool) {
return (
self == ResultStatus.OversizedTallyResult
|| self == ResultStatus.InsufficientCommits
|| self == ResultStatus.BridgePoorIncentives
|| self == ResultStatus.BridgeOversizedTallyResult
);
}
/// ===============================================================================================================
/// --- Timestamp helper methods ----------------------------------------------------------------------------------
function gt(Timestamp a, Timestamp b) internal pure returns (bool) {
return Timestamp.unwrap(a) > Timestamp.unwrap(b);
}
function egt(Timestamp a, Timestamp b) internal pure returns (bool) {
return Timestamp.unwrap(a) >= Timestamp.unwrap(b);
}
function elt(Timestamp a, Timestamp b) internal pure returns (bool) {
return Timestamp.unwrap(a) <= Timestamp.unwrap(b);
}
function isZero(Timestamp t) internal pure returns (bool) {
return Timestamp.unwrap(t) == 0;
}
/// ===============================================================================================================
/// --- 'bytes*' helper methods -----------------------------------------------------------------------------------
function intoMemArray(bytes32[1] memory _values) internal pure returns (bytes32[] memory) {
return abi.decode(abi.encode(uint256(32), 1, _values), (bytes32[]));
}
function intoMemArray(bytes32[2] memory _values) internal pure returns (bytes32[] memory) {
return abi.decode(abi.encode(uint256(32), 2, _values), (bytes32[]));
}
function intoMemArray(bytes32[3] memory _values) internal pure returns (bytes32[] memory) {
return abi.decode(abi.encode(uint256(32), 3, _values), (bytes32[]));
}
function intoMemArray(bytes32[4] memory _values) internal pure returns (bytes32[] memory) {
return abi.decode(abi.encode(uint256(32), 4, _values), (bytes32[]));
}
function intoMemArray(bytes32[5] memory _values) internal pure returns (bytes32[] memory) {
return abi.decode(abi.encode(uint256(32), 5, _values), (bytes32[]));
}
function intoMemArray(bytes32[6] memory _values) internal pure returns (bytes32[] memory) {
return abi.decode(abi.encode(uint256(32), 6, _values), (bytes32[]));
}
function intoMemArray(bytes32[7] memory _values) internal pure returns (bytes32[] memory) {
return abi.decode(abi.encode(uint256(32), 7, _values), (bytes32[]));
}
function intoMemArray(bytes32[8] memory _values) internal pure returns (bytes32[] memory) {
return abi.decode(abi.encode(uint256(32), 8, _values), (bytes32[]));
}
function merkleHash(bytes32 a, bytes32 b) internal pure returns (bytes32) {
return (a < b
? _merkleHash(a, b)
: _merkleHash(b, a)
);
}
function merkleRoot(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32 _root) {
_root = leaf;
for (uint _ix = 0; _ix < proof.length; _ix ++) {
_root = merkleHash(_root, proof[_ix]);
}
}
function radHash(bytes calldata bytecode) internal pure returns (Witnet.RadonHash) {
return Witnet.RadonHash.wrap(keccak256(bytecode));
}
function recoverEvmAddr(bytes memory signature, bytes32 hash_)
internal pure
returns (address)
{
if (signature.length != 65) {
return (address(0));
}
bytes32 r;
bytes32 s;
uint8 v;
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return address(0);
}
if (v != 27 && v != 28) {
return address(0);
}
return ecrecover(hash_, v, r, s);
}
function verifyWitAddressAuthorization(
address evmAuthorized,
Address witSigner,
bytes memory witSignature
)
internal pure
returns (bool)
{
bytes32 _publicKeyX = recoverWitPublicKey(keccak256(abi.encodePacked(evmAuthorized)), witSignature);
bytes20 _witSigner = Address.unwrap(witSigner);
return (
_witSigner == bytes20(sha256(abi.encodePacked(bytes1(0x00), _publicKeyX)))
|| _witSigner == bytes20(sha256(abi.encodePacked(bytes1(0x01), _publicKeyX)))
|| _witSigner == bytes20(sha256(abi.encodePacked(bytes1(0x02), _publicKeyX)))
|| _witSigner == bytes20(sha256(abi.encodePacked(bytes1(0x03), _publicKeyX)))
);
}
function recoverWitPublicKey(bytes32 evmDigest, bytes memory witSignature)
internal pure
returns (bytes32 _witPublicKey)
{
if (witSignature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
assembly {
r := mload(add(witSignature, 0x20))
s := mload(add(witSignature, 0x40))
v := byte(0, mload(add(witSignature, 0x60)))
}
if (
uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0
&& (v == 27 || v == 28)
) {
(uint256 x,) = Secp256k1.recover(uint256(evmDigest), v - 27, uint256(r), uint256(s));
_witPublicKey = bytes32(x);
}
}
}
function toAddress(bytes memory _value) internal pure returns (address) {
return address(toBytes20(_value));
}
function toBytes4(bytes memory _value) internal pure returns (bytes4) {
return bytes4(toFixedBytes(_value, 4));
}
function toBytes20(bytes memory _value) internal pure returns (bytes20) {
return bytes20(toFixedBytes(_value, 20));
}
function toBytes32(bytes memory _value) internal pure returns (bytes32) {
return toFixedBytes(_value, 32);
}
function toFixedBytes(bytes memory _value, uint8 _numBytes)
internal pure
returns (bytes32 _bytes32)
{
assert(_numBytes <= 32);
unchecked {
uint _len = _value.length > _numBytes ? _numBytes : _value.length;
for (uint _i = 0; _i < _len; _i ++) {
_bytes32 |= bytes32(_value[_i] & 0xff) >> (_i * 8);
}
}
}
/// @notice Converts bytes32 into string.
function asAscii(bytes32 _bytes32)
internal pure
returns (string memory)
{
bytes memory _bytes = new bytes(_toStringLength(_bytes32));
for (uint _i = 0; _i < _bytes.length;) {
_bytes[_i] = _bytes32[_i];
unchecked {
_i ++;
}
}
return string(_bytes);
}
function toHexString(bytes32 _bytes32)
internal pure
returns (string memory)
{
bytes memory _bytes = new bytes(64);
for (uint8 _i; _i < _bytes.length;) {
_bytes[_i ++] = _toHexChar(uint8(_bytes32[_i / 2] >> 4));
_bytes[_i ++] = _toHexChar(uint8(_bytes32[_i / 2] & 0x0f));
}
return string(_bytes);
}
/// ===============================================================================================================
/// --- 'string' helper methods -----------------------------------------------------------------------------------
function toLowerCase(string memory str)
internal pure
returns (string memory)
{
bytes memory lowered = new bytes(bytes(str).length);
unchecked {
for (uint i = 0; i < lowered.length; i ++) {
uint8 char = uint8(bytes(str)[i]);
if (char >= 65 && char <= 90) {
lowered[i] = bytes1(char + 32);
} else {
lowered[i] = bytes1(char);
}
}
}
return string(lowered);
}
// Function to parse a hex string into a byte array
function parseHexString(string memory hexString) internal pure returns (bytes memory result) {
unchecked {
result = new bytes(bytes(hexString).length / 2);
for (uint256 i; i < result.length; i ++) {
uint8 byte1 = _hexCharToByte(uint8(bytes(hexString)[2 * i]));
uint8 byte2 = _hexCharToByte(uint8(bytes(hexString)[2 * i + 1]));
result[i] = bytes1(byte1 * 16 + byte2); // Combining the two hex digits into one byte
}
}
}
function tryUint(string memory str)
internal pure
returns (uint res, bool)
{
unchecked {
for (uint256 i = 0; i < bytes(str).length; i++) {
if (
(uint8(bytes(str)[i]) - 48) < 0
|| (uint8(bytes(str)[i]) - 48) > 9
) {
return (0, false);
}
res += (uint8(bytes(str)[i]) - 48) * 10 ** (bytes(str).length - i - 1);
}
return (res, true);
}
}
/// ===============================================================================================================
/// --- 'uint*' helper methods ------------------------------------------------------------------------------------
function determineBeaconIndexFromEpoch(BlockNumber epoch) internal pure returns (uint64) {
return BlockNumber.unwrap(epoch) / 10;
}
function determineBeaconIndexFromTimestamp(Timestamp timestamp) internal pure returns (uint64) {
return determineBeaconIndexFromEpoch(
determineEpochFromTimestamp(
timestamp
)
);
}
function determineEpochFromTimestamp(Timestamp timestamp) internal pure returns (BlockNumber) {
if (Timestamp.unwrap(timestamp) > WIT_2_GENESIS_TIMESTAMP) {
return BlockNumber.wrap(
WIT_2_GENESIS_EPOCH
+ (Timestamp.unwrap(timestamp) - WIT_2_GENESIS_TIMESTAMP)
/ WIT_2_SECS_PER_EPOCH
);
} else if (Timestamp.unwrap(timestamp) > WIT_1_GENESIS_TIMESTAMP) {
return BlockNumber.wrap(
(Timestamp.unwrap(timestamp) - WIT_1_GENESIS_TIMESTAMP)
/ WIT_1_SECS_PER_EPOCH
);
} else {
return BlockNumber.wrap(0);
}
}
function determineTimestampFromEpoch(BlockNumber epoch) internal pure returns (Timestamp) {
if (BlockNumber.unwrap(epoch) >= WIT_2_GENESIS_EPOCH) {
return Timestamp.wrap(
WIT_2_GENESIS_TIMESTAMP
+ (WIT_2_SECS_PER_EPOCH * (
BlockNumber.unwrap(epoch)
- WIT_2_GENESIS_EPOCH)
)
);
} else return Timestamp.wrap(
WIT_1_GENESIS_TIMESTAMP
+ (WIT_1_SECS_PER_EPOCH * BlockNumber.unwrap(epoch))
);
}
/// Generates a pseudo-random uint32 number uniformly distributed within the range `[0 .. range)`, based on
/// the given `nonce` and `seed` values.
function randomUniformUint64(uint64 range, uint256 nonce, bytes32 seed)
internal pure
returns (uint64)
{
uint256 _number = uint256(
keccak256(
abi.encode(seed, nonce)
)
) & uint256(2 ** 192 - 1);
return uint64((_number * range) >> 192);
}
/// @notice Convert a `uint8` into a 2 characters long `string` representing its two less significant hexadecimal values.
function toHexString(uint8 _u)
internal pure
returns (string memory)
{
bytes memory b2 = new bytes(2);
uint8 d0 = uint8(_u / 16) + 48;
uint8 d1 = uint8(_u % 16) + 48;
if (d0 > 57)
d0 += 7;
if (d1 > 57)
d1 += 7;
b2[0] = bytes1(d0);
b2[1] = bytes1(d1);
return string(b2);
}
/// @notice Convert a `uint8` into a 1, 2 or 3 characters long `string` representing its.
/// three less significant decimal values....
// [truncated — 52859 bytes total]
WitnetBuffer.sol 825 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
/// @title A convenient wrapper around the `bytes memory` type that exposes a buffer-like interface
/// @notice The buffer has an inner cursor that tracks the final offset of every read, i.e. any subsequent read will
/// start with the byte that goes right after the last one in the previous read.
/// @dev `uint32` is used here for `cursor` because `uint16` would only enable seeking up to 8KB, which could in some
/// theoretical use cases be exceeded. Conversely, `uint32` supports up to 512MB, which cannot credibly be exceeded.
/// @author The Witnet Foundation.
library WitnetBuffer {
error EmptyBuffer();
error IndexOutOfBounds(uint index, uint range);
error MissingArgs(uint expected, uint given);
/// Iterable bytes buffer.
struct Buffer {
bytes data;
uint cursor;
}
// Ensures we access an existing index in an array
modifier withinRange(uint index, uint _range) {
if (index > _range) {
revert IndexOutOfBounds(index, _range);
}
_;
}
/// @notice Concatenate undefinite number of bytes chunks.
/// @dev Faster than looping on `abi.encodePacked(output, _buffs[ix])`.
function concat(bytes[] memory _buffs)
internal pure
returns (bytes memory output)
{
unchecked {
uint destinationPointer;
uint destinationLength;
assembly {
// get safe scratch location
output := mload(0x40)
// set starting destination pointer
destinationPointer := add(output, 32)
}
for (uint ix = 1; ix <= _buffs.length; ix ++) {
uint source;
uint sourceLength;
uint sourcePointer;
assembly {
// load source length pointer
source := mload(add(_buffs, mul(ix, 32)))
// load source length
sourceLength := mload(source)
// sets source memory pointer
sourcePointer := add(source, 32)
}
memcpy(
destinationPointer,
sourcePointer,
sourceLength
);
assembly {
// increase total destination length
destinationLength := add(destinationLength, sourceLength)
// sets destination memory pointer
destinationPointer := add(destinationPointer, sourceLength)
}
}
assembly {
// protect output bytes
mstore(output, destinationLength)
// set final output length
mstore(0x40, add(mload(0x40), add(destinationLength, 32)))
}
}
}
function fork(WitnetBuffer.Buffer memory buffer)
internal pure
returns (WitnetBuffer.Buffer memory)
{
return Buffer(
buffer.data,
buffer.cursor
);
}
function mutate(
WitnetBuffer.Buffer memory buffer,
uint length,
bytes memory pokes
)
internal pure
withinRange(length, buffer.data.length - buffer.cursor + 1)
{
bytes[] memory parts = new bytes[](3);
parts[0] = peek(
buffer,
0,
buffer.cursor
);
parts[1] = pokes;
parts[2] = peek(
buffer,
buffer.cursor + length,
buffer.data.length - buffer.cursor - length
);
buffer.data = concat(parts);
}
/// @notice Read and consume the next byte from the buffer.
/// @param buffer An instance of `Buffer`.
/// @return The next byte in the buffer counting from the cursor position.
function next(Buffer memory buffer)
internal pure
withinRange(buffer.cursor, buffer.data.length)
returns (bytes1)
{
// Return the byte at the position marked by the cursor and advance the cursor all at once
return buffer.data[buffer.cursor ++];
}
function peek(
WitnetBuffer.Buffer memory buffer,
uint offset,
uint length
)
internal pure
withinRange(offset + length, buffer.data.length)
returns (bytes memory)
{
bytes memory data = buffer.data;
bytes memory peeks = new bytes(length);
uint destinationPointer;
uint sourcePointer;
assembly {
destinationPointer := add(peeks, 32)
sourcePointer := add(add(data, 32), offset)
}
memcpy(
destinationPointer,
sourcePointer,
length
);
return peeks;
}
// @notice Extract bytes array from buffer starting from current cursor.
/// @param buffer An instance of `Buffer`.
/// @param length How many bytes to peek from the Buffer.
// solium-disable-next-line security/no-assign-params
function peek(
WitnetBuffer.Buffer memory buffer,
uint length
)
internal pure
withinRange(length, buffer.data.length - buffer.cursor)
returns (bytes memory)
{
return peek(
buffer,
buffer.cursor,
length
);
}
/// @notice Read and consume a certain amount of bytes from the buffer.
/// @param buffer An instance of `Buffer`.
/// @param length How many bytes to read and consume from the buffer.
/// @return output A `bytes memory` containing the first `length` bytes from the buffer, counting from the cursor position.
function read(Buffer memory buffer, uint length)
internal pure
withinRange(buffer.cursor + length, buffer.data.length)
returns (bytes memory output)
{
// Create a new `bytes memory destination` value
output = new bytes(length);
// Early return in case that bytes length is 0
if (length > 0) {
bytes memory input = buffer.data;
uint offset = buffer.cursor;
// Get raw pointers for source and destination
uint sourcePointer;
uint destinationPointer;
assembly {
sourcePointer := add(add(input, 32), offset)
destinationPointer := add(output, 32)
}
// Copy `length` bytes from source to destination
memcpy(
destinationPointer,
sourcePointer,
length
);
// Move the cursor forward by `length` bytes
seek(
buffer,
length,
true
);
}
}
/// @notice Read and consume the next 2 bytes from the buffer as an IEEE 754-2008 floating point number enclosed in an
/// `int32`.
/// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values
/// by 5 decimal orders so as to get a fixed precision of 5 decimal positions, which should be OK for most `float16`
/// use cases. In other words, the integer output of this method is 10,000 times the actual value. The input bytes are
/// expected to follow the 16-bit base-2 format (a.k.a. `binary16`) in the IEEE 754-2008 standard.
/// @param buffer An instance of `Buffer`.
/// @return result The `int32` value of the next 4 bytes in the buffer counting from the cursor position.
function readFloat16(Buffer memory buffer)
internal pure
returns (int32 result)
{
uint32 value = readUint16(buffer);
// Get bit at position 0
uint32 sign = value & 0x8000;
// Get bits 1 to 5, then normalize to the [-15, 16] range so as to counterweight the IEEE 754 exponent bias
int32 exponent = (int32(value & 0x7c00) >> 10) - 15;
// Get bits 6 to 15
int32 fraction = int32(value & 0x03ff);
// Add 2^10 to the fraction if exponent is not -15
if (exponent != -15) {
fraction |= 0x400;
} else if (exponent == 16) {
revert(
string(abi.encodePacked(
"WitnetBuffer.readFloat16: ",
sign != 0 ? "negative" : hex"",
" infinity"
))
);
}
// Compute `2 ^ exponent · (1 + fraction / 1024)`
if (exponent >= 0) {
result = int32(int(
int(1 << uint256(int256(exponent)))
* 10000
* fraction
) >> 10);
} else {
result = int32(int(
int(fraction)
* 10000
/ int(1 << uint(int(- exponent)))
) >> 10);
}
// Make the result negative if the sign bit is not 0
if (sign != 0) {
result *= -1;
}
}
/// @notice Consume the next 4 bytes from the buffer as an IEEE 754-2008 floating point number enclosed into an `int`.
/// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values
/// by 9 decimal orders so as to get a fixed precision of 9 decimal positions, which should be OK for most `float32`
/// use cases. In other words, the integer output of this method is 10^9 times the actual value. The input bytes are
/// expected to follow the 64-bit base-2 format (a.k.a. `binary32`) in the IEEE 754-2008 standard.
/// @param buffer An instance of `Buffer`.
/// @return result The `int` value of the next 8 bytes in the buffer counting from the cursor position.
function readFloat32(Buffer memory buffer)
internal pure
returns (int result)
{
uint value = readUint32(buffer);
// Get bit at position 0
uint sign = value & 0x80000000;
// Get bits 1 to 8, then normalize to the [-127, 128] range so as to counterweight the IEEE 754 exponent bias
int exponent = (int(value & 0x7f800000) >> 23) - 127;
// Get bits 9 to 31
int fraction = int(value & 0x007fffff);
// Add 2^23 to the fraction if exponent is not -127
if (exponent != -127) {
fraction |= 0x800000;
} else if (exponent == 128) {
revert(
string(abi.encodePacked(
"WitnetBuffer.readFloat32: ",
sign != 0 ? "negative" : hex"",
" infinity"
))
);
}
// Compute `2 ^ exponent · (1 + fraction / 2^23)`
if (exponent >= 0) {
result = (
int(1 << uint(exponent))
* (10 ** 9)
* fraction
) >> 23;
} else {
result = (
fraction
* (10 ** 9)
/ int(1 << uint(-exponent))
) >> 23;
}
// Make the result negative if the sign bit is not 0
if (sign != 0) {
result *= -1;
}
}
/// @notice Consume the next 8 bytes from the buffer as an IEEE 754-2008 floating point number enclosed into an `int`.
/// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values
/// by 15 decimal orders so as to get a fixed precision of 15 decimal positions, which should be OK for most `float64`
/// use cases. In other words, the integer output of this method is 10^15 times the actual value. The input bytes are
/// expected to follow the 64-bit base-2 format (a.k.a. `binary64`) in the IEEE 754-2008 standard.
/// @param buffer An instance of `Buffer`.
/// @return result The `int` value of the next 8 bytes in the buffer counting from the cursor position.
function readFloat64(Buffer memory buffer)
internal pure
returns (int result)
{
uint value = readUint64(buffer);
// Get bit at position 0
uint sign = value & 0x8000000000000000;
// Get bits 1 to 12, then normalize to the [-1023, 1024] range so as to counterweight the IEEE 754 exponent bias
int exponent = (int(value & 0x7ff0000000000000) >> 52) - 1023;
// Get bits 6 to 15
int fraction = int(value & 0x000fffffffffffff);
// Add 2^52 to the fraction if exponent is not -1023
if (exponent != -1023) {
fraction |= 0x10000000000000;
} else if (exponent == 1024) {
revert(
string(abi.encodePacked(
"WitnetBuffer.readFloat64: ",
sign != 0 ? "negative" : hex"",
" infinity"
))
);
}
// Compute `2 ^ exponent · (1 + fraction / 1024)`
if (exponent >= 0) {
result = (
int(1 << uint(exponent))
* (10 ** 15)
* fraction
) >> 52;
} else {
result = (
fraction
* (10 ** 15)
/ int(1 << uint(-exponent))
) >> 52;
}
// Make the result negative if the sign bit is not 0
if (sign != 0) {
result *= -1;
}
}
// Read a text string of a given length from a buffer. Returns a `bytes memory` value for the sake of genericness,
/// but it can be easily casted into a string with `string(result)`.
// solium-disable-next-line security/no-assign-params
function readText(
WitnetBuffer.Buffer memory buffer,
uint64 length
)
internal pure
returns (bytes memory text)
{
text = new bytes(length);
unchecked {
for (uint64 index = 0; index < length; index ++) {
uint8 char = readUint8(buffer);
if (char & 0x80 != 0) {
if (char < 0xe0) {
char = (char & 0x1f) << 6
| (readUint8(buffer) & 0x3f);
length -= 1;
} else if (char < 0xf0) {
char = (char & 0x0f) << 12
| (readUint8(buffer) & 0x3f) << 6
| (readUint8(buffer) & 0x3f);
length -= 2;
} else {
char = (char & 0x0f) << 18
| (readUint8(buffer) & 0x3f) << 12
| (readUint8(buffer) & 0x3f) << 6
| (readUint8(buffer) & 0x3f);
length -= 3;
}
}
text[index] = bytes1(char);
}
// Adjust text to actual length:
assembly {
mstore(text, length)
}
}
}
/// @notice Read and consume the next byte from the buffer as an `uint8`.
/// @param buffer An instance of `Buffer`.
/// @return value The `uint8` value of the next byte in the buffer counting from the cursor position.
function readUint8(Buffer memory buffer)
internal pure
withinRange(buffer.cursor, buffer.data.length)
returns (uint8 value)
{
bytes memory data = buffer.data;
uint offset = buffer.cursor;
assembly {
value := mload(add(add(data, 1), offset))
}
buffer.cursor ++;
}
/// @notice Read and consume the next 2 bytes from the buffer as an `uint16`.
/// @param buffer An instance of `Buffer`.
/// @return value The `uint16` value of the next 2 bytes in the buffer counting from the cursor position.
function readUint16(Buffer memory buffer)
internal pure
withinRange(buffer.cursor + 2, buffer.data.length)
returns (uint16 value)
{
bytes memory data = buffer.data;
uint offset = buffer.cursor;
assembly {
value := mload(add(add(data, 2), offset))
}
buffer.cursor += 2;
}
/// @notice Read and consume the next 4 bytes from the buffer as an `uint32`.
/// @param buffer An instance of `Buffer`.
/// @return value The `uint32` value of the next 4 bytes in the buffer counting from the cursor position.
function readUint32(Buffer memory buffer)
internal pure
withinRange(buffer.cursor + 4, buffer.data.length)
returns (uint32 value)
{
bytes memory data = buffer.data;
uint offset = buffer.cursor;
assembly {
value := mload(add(add(data, 4), offset))
}
buffer.cursor += 4;
}
/// @notice Read and consume the next 8 bytes from the buffer as an `uint64`.
/// @param buffer An instance of `Buffer`.
/// @return value The `uint64` value of the next 8 bytes in the buffer counting from the cursor position.
function readUint64(Buffer memory buffer)
internal pure
withinRange(buffer.cursor + 8, buffer.data.length)
returns (uint64 value)
{
bytes memory data = buffer.data;
uint offset = buffer.cursor;
assembly {
value := mload(add(add(data, 8), offset))
}
buffer.cursor += 8;
}
/// @notice Read and consume the next 16 bytes from the buffer as an `uint128`.
/// @param buffer An instance of `Buffer`.
/// @return value The `uint128` value of the next 16 bytes in the buffer counting from the cursor position.
function readUint128(Buffer memory buffer)
internal pure
withinRange(buffer.cursor + 16, buffer.data.length)
returns (uint128 value)
{
bytes memory data = buffer.data;
uint offset = buffer.cursor;
assembly {
value := mload(add(add(data, 16), offset))
}
buffer.cursor += 16;
}
/// @notice Read and consume the next 32 bytes from the buffer as an `uint256`.
/// @param buffer An instance of `Buffer`.
/// @return value The `uint256` value of the next 32 bytes in the buffer counting from the cursor position.
function readUint256(Buffer memory buffer)
internal pure
withinRange(buffer.cursor + 32, buffer.data.length)
returns (uint256 value)
{
bytes memory data = buffer.data;
uint offset = buffer.cursor;
assembly {
value := mload(add(add(data, 32), offset))
}
buffer.cursor += 32;
}
/// @notice Count number of required parameters for given bytes arrays
/// @dev Wildcard format: "\#\", with # in ["0".."9"].
/// @param input Bytes array containing strings.
/// @param count Highest wildcard index found, plus 1.
function argsCountOf(bytes memory input)
internal pure
returns (uint8 count)
{
if (input.length < 3) {
return 0;
}
unchecked {
uint ix = 0;
uint length = input.length - 2;
for (; ix < length; ) {
if (
input[ix] == bytes1("\\")
&& input[ix + 2] == bytes1("\\")
&& input[ix + 1] >= bytes1("0")
&& input[ix + 1] <= bytes1("9")
) {
uint8 ax = uint8(uint8(input[ix + 1]) - uint8(bytes1("0")) + 1);
if (ax > count) {
count = ax;
}
ix += 3;
} else {
ix ++;
}
}
}
}
/// @notice Replace indexed bytes-wildcards by correspondent substrings.
/// @dev Wildcard format: "\#\", with # in ["0".."9"].
/// @param input Bytes array containing strings.
/// @param args Array of substring values for replacing indexed wildcards.
/// @return output Resulting bytes array after replacing all wildcards.
/// @return hits Total number of replaced wildcards.
function replace(bytes memory input, string[] memory args)
internal pure
returns (bytes memory output, uint hits)
{
uint ix = 0; uint lix = 0;
uint inputLength;
uint inputPointer;
uint outputLength;
uint outputPointer;
uint source;
uint sourceLength;
uint sourcePointer;
if (input.length < 3) {
return (input, 0);
}
assembly {
// set starting input pointer
inputPointer := add(input, 32)
// get safe output location
output := mload(0x40)
// set starting output pointer
outputPointer := add(output, 32)
}
unchecked {
uint length = input.length - 2;
for (; ix < length; ) {
if (
input[ix] == bytes1("\\")
&& input[ix + 2] == bytes1("\\")
&& input[ix + 1] >= bytes1("0")
&& input[ix + 1] <= bytes1("9")
) {
inputLength = (ix - lix);
if (ix > lix) {
memcpy(
outputPointer,
inputPointer,
inputLength
);
inputPointer += inputLength + 3;
outputPointer += inputLength;
} else {
inputPointer += 3;
}
uint ax = uint(uint8(input[ix + 1]) - uint8(bytes1("0")));
if (ax >= args.length) {
revert MissingArgs(ax + 1, args.length);
}
assembly {
source := mload(add(args, mul(32, add(ax, 1))))
sourceLength := mload(source)
sourcePointer := add(source, 32)
}
memcpy(
outputPointer,
sourcePointer,
sourceLength
);
outputLength += inputLength + sourceLength;
outputPointer += sourceLength;
ix += 3;
lix = ix;
hits ++;
} else {
ix ++;
}
}
ix = input.length;
}
if (outputLength > 0) {
if (ix > lix ) {
memcpy(
outputPointer,
inputPointer,
ix - lix
);
outputLength += (ix - lix);
}
assembly {
// set final output length
mstore(output, outputLength)
// protect output bytes
mstore(0x40, add(mload(0x40), add(outputLength, 32)))
}
}
else {
return (input, 0);
}
}
/// @notice Replace indexed bytes-wildcard by given substring.
/// @dev Wildcard format: "\#\", with # in ["0".."9"].
/// @param input Bytes array containing strings.
/// @param argIndex Index of the wildcard to be replaced.
/// @param argValue Replacing substring to be used.
/// @return output Resulting bytes array after replacing all wildcards.
/// @return hits Total number of replaced wildcards.
function replace(bytes memory input, uint8 argIndex, string memory argValue)
internal pure
returns (bytes memory output, uint hits)
{
uint ix = 0; uint lix = 0;
uint inputLength;
uint inputPointer;
uint outputLength;
uint outputPointer;
uint argValueLength;
uint argValuePointer;
if (input.length < 3) {
return (input, 0);
}
assembly {
// set starting input pointer
inputPointer := add(input, 32)
// get safe output location
output := mload(0x40)
// set starting output pointer
outputPointer := add(output, 32)
// set pointer to arg value substring
argValuePointer := add(argValue, 32)
// set arg value substring length
argValueLength := mload(argValue)
}
unchecked {
uint length = input.length - 2;
for (; ix < length; ) {
if (
input[ix] == bytes1("\\")
&& input[ix + 2] == bytes1("\\")
&& input[ix + 1] >= bytes1("0")
&& input[ix + 1] <= bytes1("9")
&& uint8(input[ix + 1]) - uint8(bytes1("0")) == argIndex
) {
inputLength = (ix - lix);
if (ix > lix) {
memcpy(
outputPointer,
inputPointer,
inputLength
);
inputPointer += inputLength + 3;
outputPointer += inputLength;
} else {
inputPointer += 3;
}
memcpy(
outputPointer,
argValuePointer,
argValueLength
);
outputLength += inputLength + argValueLength;
outputPointer += argValueLength;
ix += 3;
lix = ix;
hits ++;
} else {
ix ++;
}
}
ix = input.length;
}
if (outputLength > 0) {
if (ix > lix ) {
memcpy(
outputPointer,
inputPointer,
ix - lix
);
outputLength += (ix - lix);
}
assembly {
// set final output length
mstore(output, outputLength)
// protect output bytes
mstore(0x40, add(mload(0x40), add(outputLength, 32)))
}
}
else {
return (input, 0);
}
}
/// @notice Replace indexed string wildcards by correspondent substrings.
/// @dev Wildcard format: "\#\", with # in ["0".."9"].
/// @param input String potentially containing wildcards.
/// @param args Array of substring values for replacing indexed wildcards.
/// @return output Resulting string after replacing all wildcards.
function replace(string memory input, string[] memory args)
internal pure
returns (string memory)
{
(bytes memory _outputBytes, ) = replace(bytes(input), args);
return string(_outputBytes);
}
/// @notice Replace last indexed wildcard by given substring.
/// @dev Wildcard format: "\#\", with # in ["0".."9"].
/// @param input String potentially containing wildcards.
/// @param argIndex Index of the wildcard to be replaced.
/// @param argValue Replacing string to be used.
/// @return output Resulting string after replacing all wildcards.
function replace(string memory input, uint8 argIndex, string memory argValue)
internal pure
returns (string memory)
{
(bytes memory _outputBytes, ) = replace(bytes(input), argIndex, argValue);
return string(_outputBytes);
}
/// @notice Move the inner cursor of the buffer to a relative or absolute position.
/// @param buffer An instance of `Buffer`.
/// @param offset How many bytes to move the cursor forward.
/// @param relative Whether to count `offset` from the last position of the cursor (`true`) or the beginning of the
/// buffer (`true`).
/// @return The final position of the cursor (will equal `offset` if `relative` is `false`).
// solium-disable-next-line security/no-assign-params
function seek(
Buffer memory buffer,
uint offset,
bool relative
)
internal pure
withinRange(offset, buffer.data.length)
returns (uint)
{
// Deal with relative offsets
if (relative) {
offset += buffer.cursor;
}
buffer.cursor = offset;
return offset;
}
/// @notice Move the inner cursor a number of bytes forward.
/// @dev This is a simple wrapper around the relative offset case of `seek()`.
/// @param buffer An instance of `Buffer`.
/// @param relativeOffset How many bytes to move the cursor forward.
/// @return The final position of the cursor.
function seek(
Buffer memory buffer,
uint relativeOffset
)
internal pure
returns (uint)
{
return seek(
buffer,
relativeOffset,
true
);
}
/// @notice Copy bytes from one memory address into another.
/// @dev This function was borrowed from Nick Johnson's `solidity-stringutils` lib, and reproduced here under the terms
/// of [Apache License 2.0](https://github.com/Arachnid/solidity-stringutils/blob/master/LICENSE).
/// @param dest Address of the destination memory.
/// @param src Address to the source memory.
/// @param len How many bytes to copy.
// solium-disable-next-line security/no-assign-params
function memcpy(
uint dest,
uint src,
uint len
)
private pure
{
unchecked {
// Copy word-length chunks while possible
for (; len >= 32; len -= 32) {
assembly {
mstore(dest, mload(src))
}
dest += 32;
src += 32;
}
if (len > 0) {
// Copy remaining bytes
uint _mask = 256 ** (32 - len) - 1;
assembly {
let srcpart := and(mload(src), not(_mask))
let destpart := and(mload(dest), _mask)
mstore(dest, or(destpart, srcpart))
}
}
}
}
}
WitnetCBOR.sol 583 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import "./WitnetBuffer.sol";
/// @title A minimalistic implementation of “RFC 7049 Concise Binary Object Representation”
/// @notice This library leverages a buffer-like structure for step-by-step decoding of bytes so as to minimize
/// the gas cost of decoding them into a useful native type.
/// @dev Most of the logic has been borrowed from Patrick Gansterer’s cbor.js library: https://github.com/paroga/cbor-js
/// @author The Witnet Foundation.
library WitnetCBOR {
using WitnetBuffer for WitnetBuffer.Buffer;
using WitnetCBOR for WitnetCBOR.CBOR;
/// Data struct following the RFC-7049 standard: Concise Binary Object Representation.
struct CBOR {
WitnetBuffer.Buffer buffer;
uint8 initialByte;
uint8 majorType;
uint8 additionalInformation;
uint64 len;
uint64 tag;
}
uint8 internal constant MAJOR_TYPE_INT = 0;
uint8 internal constant MAJOR_TYPE_NEGATIVE_INT = 1;
uint8 internal constant MAJOR_TYPE_BYTES = 2;
uint8 internal constant MAJOR_TYPE_STRING = 3;
uint8 internal constant MAJOR_TYPE_ARRAY = 4;
uint8 internal constant MAJOR_TYPE_MAP = 5;
uint8 internal constant MAJOR_TYPE_TAG = 6;
uint8 internal constant MAJOR_TYPE_CONTENT_FREE = 7;
uint32 internal constant UINT32_MAX = type(uint32).max;
uint64 internal constant UINT64_MAX = type(uint64).max;
error EmptyArray();
error InvalidLengthEncoding(uint length);
error UnexpectedMajorType(uint read, uint expected);
error UnsupportedPrimitive(uint primitive);
error UnsupportedMajorType(uint unexpected);
modifier isMajorType(
WitnetCBOR.CBOR memory cbor,
uint8 expected
) {
if (cbor.majorType != expected) {
revert UnexpectedMajorType(cbor.majorType, expected);
}
_;
}
modifier notEmpty(WitnetBuffer.Buffer memory buffer) {
if (buffer.data.length == 0) {
revert WitnetBuffer.EmptyBuffer();
}
_;
}
function eof(CBOR memory cbor)
internal pure
returns (bool)
{
return cbor.buffer.cursor >= cbor.buffer.data.length;
}
/// @notice Decode a CBOR structure from raw bytes.
/// @dev This is the main factory for CBOR instances, which can be later decoded into native EVM types.
/// @param bytecode Raw bytes representing a CBOR-encoded value.
/// @return A `CBOR` instance containing a partially decoded value.
function fromBytes(bytes memory bytecode)
internal pure
returns (CBOR memory)
{
WitnetBuffer.Buffer memory buffer = WitnetBuffer.Buffer(bytecode, 0);
return fromBuffer(buffer);
}
/// @notice Decode a CBOR structure from raw bytes.
/// @dev This is an alternate factory for CBOR instances, which can be later decoded into native EVM types.
/// @param buffer A Buffer structure representing a CBOR-encoded value.
/// @return A `CBOR` instance containing a partially decoded value.
function fromBuffer(WitnetBuffer.Buffer memory buffer)
internal pure
notEmpty(buffer)
returns (CBOR memory)
{
uint8 initialByte;
uint8 majorType = 255;
uint8 additionalInformation;
uint64 tag = UINT64_MAX;
uint256 len;
bool isTagged = true;
while (isTagged) {
// Extract basic CBOR properties from input bytes
initialByte = buffer.readUint8();
len ++;
majorType = initialByte >> 5;
additionalInformation = initialByte & 0x1f;
// Early CBOR tag parsing.
if (majorType == MAJOR_TYPE_TAG) {
uint _cursor = buffer.cursor;
tag = readLength(buffer, additionalInformation);
len += buffer.cursor - _cursor;
} else {
isTagged = false;
}
}
if (majorType > MAJOR_TYPE_CONTENT_FREE) {
revert UnsupportedMajorType(majorType);
}
return CBOR(
buffer,
initialByte,
majorType,
additionalInformation,
uint64(len),
tag
);
}
function fork(WitnetCBOR.CBOR memory self)
internal pure
returns (WitnetCBOR.CBOR memory)
{
return CBOR({
buffer: self.buffer.fork(),
initialByte: self.initialByte,
majorType: self.majorType,
additionalInformation: self.additionalInformation,
len: self.len,
tag: self.tag
});
}
function settle(CBOR memory self)
internal pure
returns (WitnetCBOR.CBOR memory)
{
if (!self.eof()) {
return fromBuffer(self.buffer);
} else {
return self;
}
}
function skip(CBOR memory self)
internal pure
returns (WitnetCBOR.CBOR memory)
{
if (
self.majorType == MAJOR_TYPE_INT
|| self.majorType == MAJOR_TYPE_NEGATIVE_INT
|| (
self.majorType == MAJOR_TYPE_CONTENT_FREE
&& self.additionalInformation >= 25
&& self.additionalInformation <= 27
)
) {
self.buffer.cursor += self.peekLength();
} else if (
self.majorType == MAJOR_TYPE_STRING
|| self.majorType == MAJOR_TYPE_BYTES
) {
uint64 len = readLength(self.buffer, self.additionalInformation);
self.buffer.cursor += len;
} else if (
self.majorType == MAJOR_TYPE_ARRAY
|| self.majorType == MAJOR_TYPE_MAP
) {
self.len = readLength(self.buffer, self.additionalInformation);
} else if (
self.majorType != MAJOR_TYPE_CONTENT_FREE
|| (
self.additionalInformation != 20
&& self.additionalInformation != 21
)
) {
revert("WitnetCBOR.skip: unsupported major type");
}
return self;
}
function peekLength(CBOR memory self)
internal pure
returns (uint64)
{
if (self.additionalInformation < 24) {
return 0;
} else if (self.additionalInformation < 28) {
return uint64(1 << (self.additionalInformation - 24));
} else {
revert InvalidLengthEncoding(self.additionalInformation);
}
}
function readArray(CBOR memory self)
internal pure
isMajorType(self, MAJOR_TYPE_ARRAY)
returns (CBOR[] memory items)
{
// read array's length and move self cursor forward to the first array element:
uint64 len = readLength(self.buffer, self.additionalInformation);
items = new CBOR[](len + 1);
for (uint ix = 0; ix < len; ix ++) {
// settle next element in the array:
self = self.settle();
// fork it and added to the list of items to be returned:
items[ix] = self.fork();
if (self.majorType == MAJOR_TYPE_ARRAY) {
CBOR[] memory _subitems = self.readArray();
// move forward to the first element after inner array:
self = _subitems[_subitems.length - 1];
} else if (self.majorType == MAJOR_TYPE_MAP) {
CBOR[] memory _subitems = self.readMap();
// move forward to the first element after inner map:
self = _subitems[_subitems.length - 1];
} else {
// move forward to the next element:
self.skip();
}
}
// return self cursor as extra item at the end of the list,
// as to optimize recursion when jumping over nested arrays:
items[len] = self;
}
function readMap(CBOR memory self)
internal pure
isMajorType(self, MAJOR_TYPE_MAP)
returns (CBOR[] memory items)
{
// read number of items within the map and move self cursor forward to the first inner element:
uint64 len = readLength(self.buffer, self.additionalInformation) * 2;
items = new CBOR[](len + 1);
for (uint ix = 0; ix < len; ix ++) {
// settle next element in the array:
self = self.settle();
// fork it and added to the list of items to be returned:
items[ix] = self.fork();
if (ix % 2 == 0 && self.majorType != MAJOR_TYPE_STRING) {
revert UnexpectedMajorType(self.majorType, MAJOR_TYPE_STRING);
} else if (self.majorType == MAJOR_TYPE_ARRAY || self.majorType == MAJOR_TYPE_MAP) {
CBOR[] memory _subitems = (self.majorType == MAJOR_TYPE_ARRAY
? self.readArray()
: self.readMap()
);
// move forward to the first element after inner array or map:
self = _subitems[_subitems.length - 1];
} else {
// move forward to the next element:
self.skip();
}
}
// return self cursor as extra item at the end of the list,
// as to optimize recursion when jumping over nested arrays:
items[len] = self;
}
/// Reads the length of the settle CBOR item from a buffer, consuming a different number of bytes depending on the
/// value of the `additionalInformation` argument.
function readLength(
WitnetBuffer.Buffer memory buffer,
uint8 additionalInformation
)
internal pure
returns (uint64)
{
if (additionalInformation < 24) {
return additionalInformation;
}
if (additionalInformation == 24) {
return buffer.readUint8();
}
if (additionalInformation == 25) {
return buffer.readUint16();
}
if (additionalInformation == 26) {
return buffer.readUint32();
}
if (additionalInformation == 27) {
return buffer.readUint64();
}
if (additionalInformation == 31) {
return UINT64_MAX;
}
revert InvalidLengthEncoding(additionalInformation);
}
/// @notice Read a `CBOR` structure into a native `bool` value.
/// @param cbor An instance of `CBOR`.
/// @return The value represented by the input, as a `bool` value.
function readBool(CBOR memory cbor)
internal pure
isMajorType(cbor, MAJOR_TYPE_CONTENT_FREE)
returns (bool)
{
if (cbor.additionalInformation == 20) {
return false;
} else if (cbor.additionalInformation == 21) {
return true;
} else {
revert UnsupportedPrimitive(cbor.additionalInformation);
}
}
/// @notice Decode a `CBOR` structure into a native `bytes` value.
/// @param cbor An instance of `CBOR`.
/// @return output The value represented by the input, as a `bytes` value.
function readBytes(CBOR memory cbor)
internal pure
isMajorType(cbor, MAJOR_TYPE_BYTES)
returns (bytes memory output)
{
cbor.len = readLength(
cbor.buffer,
cbor.additionalInformation
);
if (cbor.len == UINT32_MAX) {
// These checks look repetitive but the equivalent loop would be more expensive.
uint32 length = uint32(_readIndefiniteStringLength(
cbor.buffer,
cbor.majorType
));
if (length < UINT32_MAX) {
output = abi.encodePacked(cbor.buffer.read(length));
length = uint32(_readIndefiniteStringLength(
cbor.buffer,
cbor.majorType
));
if (length < UINT32_MAX) {
output = abi.encodePacked(
output,
cbor.buffer.read(length)
);
}
}
} else {
return cbor.buffer.read(uint32(cbor.len));
}
}
/// @notice Decode a `CBOR` structure into a `fixed16` value.
/// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values
/// by 5 decimal orders so as to get a fixed precision of 5 decimal positions, which should be OK for most `fixed16`
/// use cases. In other words, the output of this method is 10,000 times the actual value, encoded into an `int32`.
/// @param cbor An instance of `CBOR`.
/// @return The value represented by the input, as an `int128` value.
function readFloat16(CBOR memory cbor)
internal pure
isMajorType(cbor, MAJOR_TYPE_CONTENT_FREE)
returns (int32)
{
if (cbor.additionalInformation == 25) {
return cbor.buffer.readFloat16();
} else {
revert UnsupportedPrimitive(cbor.additionalInformation);
}
}
/// @notice Decode a `CBOR` structure into a `fixed32` value.
/// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values
/// by 9 decimal orders so as to get a fixed precision of 9 decimal positions, which should be OK for most `fixed64`
/// use cases. In other words, the output of this method is 10^9 times the actual value, encoded into an `int`.
/// @param cbor An instance of `CBOR`.
/// @return The value represented by the input, as an `int` value.
function readFloat32(CBOR memory cbor)
internal pure
isMajorType(cbor, MAJOR_TYPE_CONTENT_FREE)
returns (int)
{
if (cbor.additionalInformation == 26) {
return cbor.buffer.readFloat32();
} else {
revert UnsupportedPrimitive(cbor.additionalInformation);
}
}
/// @notice Decode a `CBOR` structure into a `fixed64` value.
/// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values
/// by 15 decimal orders so as to get a fixed precision of 15 decimal positions, which should be OK for most `fixed64`
/// use cases. In other words, the output of this method is 10^15 times the actual value, encoded into an `int`.
/// @param cbor An instance of `CBOR`.
/// @return The value represented by the input, as an `int` value.
function readFloat64(CBOR memory cbor)
internal pure
isMajorType(cbor, MAJOR_TYPE_CONTENT_FREE)
returns (int)
{
if (cbor.additionalInformation == 27) {
return cbor.buffer.readFloat64();
} else {
revert UnsupportedPrimitive(cbor.additionalInformation);
}
}
/// @notice Decode a `CBOR` structure into a native `int128[]` value whose inner values follow the same convention
/// @notice as explained in `decodeFixed16`.
/// @param cbor An instance of `CBOR`.
function readFloat16Array(CBOR memory cbor)
internal pure
isMajorType(cbor, MAJOR_TYPE_ARRAY)
returns (int32[] memory values)
{
uint64 length = readLength(cbor.buffer, cbor.additionalInformation);
if (length < UINT64_MAX) {
values = new int32[](length);
for (uint64 i = 0; i < length; ) {
CBOR memory item = fromBuffer(cbor.buffer);
values[i] = readFloat16(item);
unchecked {
i ++;
}
}
} else {
revert InvalidLengthEncoding(length);
}
}
/// @notice Decode a `CBOR` structure into a native `int128` value.
/// @param cbor An instance of `CBOR`.
/// @return The value represented by the input, as an `int128` value.
function readInt(CBOR memory cbor)
internal pure
returns (int64)
{
if (cbor.majorType == 1) {
uint64 _value = readLength(
cbor.buffer,
cbor.additionalInformation
);
return int64(-1) - int64(uint64(_value));
} else if (cbor.majorType == 0) {
// Any `uint64` can be safely casted to `int128`, so this method supports majorType 1 as well so as to have offer
// a uniform API for positive and negative numbers
return int64(readUint(cbor));
}
else {
revert UnexpectedMajorType(cbor.majorType, 1);
}
}
/// @notice Decode a `CBOR` structure into a native `int[]` value.
/// @param cbor instance of `CBOR`.
/// @return array The value represented by the input, as an `int[]` value.
function readIntArray(CBOR memory cbor)
internal pure
isMajorType(cbor, MAJOR_TYPE_ARRAY)
returns (int64[] memory array)
{
uint64 length = readLength(cbor.buffer, cbor.additionalInformation);
if (length < UINT64_MAX) {
array = new int64[](length);
for (uint i = 0; i < length; ) {
CBOR memory item = fromBuffer(cbor.buffer);
array[i] = readInt(item);
unchecked {
i ++;
}
}
} else {
revert InvalidLengthEncoding(length);
}
}
/// @notice Decode a `CBOR` structure into a native `string` value.
/// @param cbor An instance of `CBOR`.
/// @return text The value represented by the input, as a `string` value.
function readString(CBOR memory cbor)
internal pure
isMajorType(cbor, MAJOR_TYPE_STRING)
returns (string memory text)
{
cbor.len = readLength(cbor.buffer, cbor.additionalInformation);
if (cbor.len == UINT64_MAX) {
bool _done;
while (!_done) {
uint64 length = _readIndefiniteStringLength(
cbor.buffer,
cbor.majorType
);
if (length < UINT64_MAX) {
text = string(abi.encodePacked(
text,
cbor.buffer.readText(length / 4)
));
} else {
_done = true;
}
}
} else {
return string(cbor.buffer.readText(cbor.len));
}
}
/// @notice Decode a `CBOR` structure into a native `string[]` value.
/// @param cbor An instance of `CBOR`.
/// @return strings The value represented by the input, as an `string[]` value.
function readStringArray(CBOR memory cbor)
internal pure
isMajorType(cbor, MAJOR_TYPE_ARRAY)
returns (string[] memory strings)
{
uint length = readLength(cbor.buffer, cbor.additionalInformation);
if (length < UINT64_MAX) {
strings = new string[](length);
for (uint i = 0; i < length; ) {
CBOR memory item = fromBuffer(cbor.buffer);
strings[i] = readString(item);
unchecked {
i ++;
}
}
} else {
revert InvalidLengthEncoding(length);
}
}
/// @notice Decode a `CBOR` structure into a native `uint64` value.
/// @param cbor An instance of `CBOR`.
/// @return The value represented by the input, as an `uint64` value.
function readUint(CBOR memory cbor)
internal pure
isMajorType(cbor, MAJOR_TYPE_INT)
returns (uint64)
{
return readLength(
cbor.buffer,
cbor.additionalInformation
);
}
/// @notice Decode a `CBOR` structure into a native `uint64[]` value.
/// @param cbor An instance of `CBOR`.
/// @return values The value represented by the input, as an `uint64[]` value.
function readUintArray(CBOR memory cbor)
internal pure
isMajorType(cbor, MAJOR_TYPE_ARRAY)
returns (uint64[] memory values)
{
uint64 length = readLength(cbor.buffer, cbor.additionalInformation);
if (length < UINT64_MAX) {
values = new uint64[](length);
for (uint ix = 0; ix < length; ) {
CBOR memory item = fromBuffer(cbor.buffer);
values[ix] = readUint(item);
unchecked {
ix ++;
}
}
} else {
revert InvalidLengthEncoding(length);
}
}
/// Read the length of a CBOR indifinite-length item (arrays, maps, byte strings and text) from a buffer, consuming
/// as many bytes as specified by the first byte.
function _readIndefiniteStringLength(
WitnetBuffer.Buffer memory buffer,
uint8 majorType
)
private pure
returns (uint64 len)
{
uint8 initialByte = buffer.readUint8();
if (initialByte == 0xff) {
return UINT64_MAX;
}
len = readLength(
buffer,
initialByte & 0x1f
);
if (len >= UINT64_MAX) {
revert InvalidLengthEncoding(len);
} else if (majorType != (initialByte >> 5)) {
revert UnexpectedMajorType((initialByte >> 5), majorType);
}
}
}
WitnetERC20.sol 614 lines
// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity >=0.8.20 <0.9.0;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC20Bridgeable} from "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Bridgeable.sol";
import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "witnet-solidity-bridge/contracts/WitOracle.sol";
import {
IWitOracleRadonRequestModal,
IWitOracleRadonRequestFactory
} from "witnet-solidity-bridge/contracts/WitOracleRadonRequestFactory.sol";
import {IWitOracleConsumer} from "witnet-solidity-bridge/contracts/interfaces/IWitOracleConsumer.sol";
import {IWitOracleQueriableConsumer} from "witnet-solidity-bridge/contracts/interfaces/IWitOracleQueriableConsumer.sol";
import {IWitnetMintableERC20, WitnetERC20Lib} from "./libs/WitnetERC20Lib.sol";
/// @custom:security-contact [email protected]
contract WitnetERC20
is
ERC20,
ERC20Bridgeable,
ERC20Permit,
Initializable,
IWitnetMintableERC20,
IWitOracleConsumer,
IWitOracleQueriableConsumer,
ReentrancyGuard
{
using Witnet for Witnet.Address;
using Witnet for Witnet.RadonHash;
uint256 internal constant _CANONICAL_CHAIN_ID = 1; // Ethereum Mainnet
uint8 internal constant _DECIMALS = 9;
uint256 internal constant _MIN_UNWRAPPABLE_NANOWITS = 1_000_000_000; // 1.0 $WIT
address internal constant _SUPERCHAIN_TOKEN_BRIDGE = 0x4200000000000000000000000000000000000028; // Superchain default bridge
uint16 internal constant _WIT_ORACLE_REPORTS_MIN_MIN_WITNESSES = 3;
uint16 internal constant _WIT_ORACLE_QUERIABLE_CONSUMER_MAX_EXTRA_FEE_PERCENTAGE = 50;
uint64 internal constant _WIT_ORACLE_QUERIABLE_CONSUMER_MIN_UNITARY_REWARD = 200_000_000; // 0.2 $WIT
uint24 internal constant _WIT_ORACLE_QUERIABLE_CONSUMER_MIN_RESPONSE_CALLBACK_GAS = 210_000;
WitOracle public immutable witOracle;
IWitOracleRadonRequestModal public immutable witOracleCrossChainProofOfReserveTemplate;
IWitOracleRadonRequestModal public immutable witOracleCrossChainProofOfInclusionTemplate;
Witnet.Address internal immutable __witCustodianWrapper;
bytes32 internal immutable __witCustodianWrapperBech32Hash;
modifier onlyCurator {
_require(
_msgSender() == __storage().curator,
"unauthorized"
); _;
}
modifier whenWitnetBurnsNotPaused {
_require(
!__storage().pausedWitnetBurns,
"paused burns"
); _;
}
modifier whenWitnetMintsNotPaused {
_require(
!__storage().pausedWitnetMints,
"paused mints"
); _;
}
constructor(
IWitOracleRadonRequestFactory _witOracleRadonRequestFactory,
string memory _witCustodianBech32
)
ERC20("Witnet", "WIT")
ERC20Permit("Witnet")
{
// Settle immutable parameters --------------------------------------------------------------------------------
__witCustodianWrapper = Witnet.fromBech32(_witCustodianBech32, block.chainid == _CANONICAL_CHAIN_ID);
__witCustodianWrapperBech32Hash = keccak256(bytes(_witCustodianBech32));
witOracle = WitOracle(IWitOracleAppliance(address(_witOracleRadonRequestFactory)).witOracle());
string[2][] memory _httpRequestHeaders = new string[2][](1);
_httpRequestHeaders[0] = [ "Content-Type", "application/json;charset=UTF-8" ];
witOracleCrossChainProofOfReserveTemplate = _witOracleRadonRequestFactory.buildRadonRequestModal(
IWitOracleRadonRequestFactory.DataSourceRequest({
method: Witnet.RadonRetrievalMethods.HttpPost,
body: '{"jsonrpc":"2.0","method":"getBalance2","params":{"pkh":"\\0\\;\\1\\"},"id":1}',
headers: _httpRequestHeaders,
script: // [RadonString] parseJSONMap()
// [RadonMap] getMap("result")
// [RadonArray] values()
hex"83187782186666726573756c741869"
}),
Witnet.RadonReducer({
opcode: Witnet.RadonReduceOpcodes.Mode,
filters: new Witnet.RadonFilter[](0)
})
);
witOracleCrossChainProofOfInclusionTemplate = _witOracleRadonRequestFactory.buildRadonRequestModal(
IWitOracleRadonRequestFactory.DataSourceRequest({
method: Witnet.RadonRetrievalMethods.HttpPost,
body: '{"jsonrpc":"2.0","method":"getValueTransfer","params":{"hash":"\\0\\","mode":"ethereal","force":true},"id":1}',
headers: _httpRequestHeaders,
script: // [RadonString] parseJSONMap()
// [RadonMap] getMap("result")
// [RadonArray] values()
hex"83187782186666726573756c741869"
}),
Witnet.RadonReducer({
opcode: Witnet.RadonReduceOpcodes.Mode,
filters: new Witnet.RadonFilter[](0)
})
);
}
function initialize(
address _curator,
string calldata _witCustodianUnwrapperBech32
)
external
initializer
{
// Initialize curatorship:
__storage().curator = _curator;
emit CuratorshipTransferred(address(0), _curator);
// Initialize default parameters:
__storage().bridge = _SUPERCHAIN_TOKEN_BRIDGE;
__storage().witOracleQuerySettings = WitOracleSettings({
minWitnesses: block.chainid == _CANONICAL_CHAIN_ID ? 12 : _WIT_ORACLE_REPORTS_MIN_MIN_WITNESSES,
extraFeePercentage: 5, // 5 %
unitaryRewardNanowits: _WIT_ORACLE_QUERIABLE_CONSUMER_MIN_UNITARY_REWARD,
responseCallbackGasLimit: _WIT_ORACLE_QUERIABLE_CONSUMER_MIN_RESPONSE_CALLBACK_GAS
});
string[] memory _witOracleRpcProviders = new string[](1);
_witOracleRpcProviders[0] = (
block.chainid == _CANONICAL_CHAIN_ID
? "https://rpc-01.witnet.io"
: "https://rpc-testnet.witnet.io"
);
__storage().witOracleCrossChainRpcProviders = _witOracleRpcProviders;
// Settle Wit/ Unwrapper address and formally verify parameterized Radon assets:
__settleWitCustodianUnwrapper(_witCustodianUnwrapperBech32);
}
/// ===============================================================================================================
/// --- ERC20 -----------------------------------------------------------------------------------------------------
function decimals() override public pure returns (uint8) {
return _DECIMALS;
}
/// ===============================================================================================================
/// --- ERC20Bridgeable -------------------------------------------------------------------------------------------
function _checkTokenBridge(address caller) override internal view {
_require(
caller == __storage().bridge,
"unauthorized bridge"
);
_require(
!__storage().pausedBridge,
"paused bridge"
);
}
/// ===============================================================================================================
/// --- Wrapped/WIT read-only methods -----------------------------------------------------------------------------
function bridge() override external view returns (address) {
return __storage().bridge;
}
function curator() override external view returns (address) {
return __storage().curator;
}
function getWrapTransactionLastQueryId(Witnet.TransactionHash _witnetValueTransferTransactionHash)
override external view
returns (uint256)
{
return __storage().witOracleWrappingTransactionLastQueryId[_witnetValueTransferTransactionHash];
}
function getWrapTransactionStatus(Witnet.TransactionHash _witnetValueTransferTransactionHash)
override public view
returns (WrappingStatus)
{
uint256 _witOracleLastQueryId = __storage().witOracleWrappingTransactionLastQueryId[
_witnetValueTransferTransactionHash
];
if (_witOracleLastQueryId == 0) {
return WrappingStatus.Unknown;
} else if (_witOracleLastQueryId == WitnetERC20Lib._WIT_ORACLE_QUERIABLE_CONSUMER_CALLBACK_PROCESSED) {
return WrappingStatus.Done;
} else {
return (
witOracle.getQueryStatus(_witOracleLastQueryId) == Witnet.QueryStatus.Posted
? WrappingStatus.Awaiting
: WrappingStatus.Retry
);
}
}
function getWrapTransactionStatuses(Witnet.TransactionHash[] calldata _hashes)
override external view
returns (WrappingStatus[] memory _statuses)
{
_statuses = new WrappingStatus[](_hashes.length);
for (uint256 _ix = 0; _ix < _hashes.length; ++ _ix) {
_statuses[_ix] = getWrapTransactionStatus(_hashes[_ix]);
}
}
function minUnwrappableNanowits()
override external pure
returns (uint256)
{
return _MIN_UNWRAPPABLE_NANOWITS;
}
function paused()
override external view
returns (bool, bool, bool)
{
return (
__storage().pausedBridge,
__storage().pausedWitnetBurns,
__storage().pausedWitnetMints
);
}
function totalReserveNanowits() override external view returns (uint256) {
return __storage().evmLastReserveNanowits;
}
function totalUnwrappings() override external view returns (uint256) {
return __storage().evmUnwrappings;
}
function totalUnwraps() override external view returns (uint256) {
return __storage().evmUnwraps;
}
function totalWrappings() override external view returns (uint256) {
return __storage().evmWrappings;
}
function totalWraps() override external view returns (uint256) {
return __storage().evmWraps;
}
function witCustodianWrapper() override public view returns (string memory) {
return __witCustodianWrapper.toBech32(block.chainid == _CANONICAL_CHAIN_ID);
}
function witCustodianUnwrapper() override public view returns (string memory) {
return __storage().witCustodianUnwrapper.toBech32(block.chainid == _CANONICAL_CHAIN_ID);
}
function witOracleCrossChainRpcProviders() override external view returns (string[] memory) {
return __storage().witOracleCrossChainRpcProviders;
}
function witOracleEstimateWrappingFee(uint256 evmGasPrice) override external view returns (uint256) {
return WitnetERC20Lib.witOracleEstimateWrappingFee(
witOracle,
evmGasPrice
);
}
function witOracleProofOfReserveLastUpdate() override external view returns (Witnet.Timestamp) {
return __storage().evmLastReserveTimestamp;
}
function witOracleProofOfReserveRadonBytecode() override external view returns (bytes memory) {
return witOracle
.registry()
.lookupRadonRequestBytecode(
__storage().witOracleProofOfReserveRadonHash
);
}
function witOracleProofOfReserveRadonHash() override external view returns (Witnet.RadonHash) {
return __storage().witOracleProofOfReserveRadonHash;
}
function witOracleQuerySettings() override external view returns (WitOracleSettings memory) {
return __storage().witOracleQuerySettings;
}
/// ===============================================================================================================
/// --- Wrapped/WIT authoritative methods -------------------------------------------------------------------------
function crosschainPause(
bool _erc7802,
bool _witnetBurns,
bool _witnetMints
)
external
onlyCurator
{
__storage().pausedBridge = _erc7802;
__storage().pausedWitnetBurns = _witnetBurns;
__storage().pausedWitnetMints = _witnetMints;
emit PauseFlags(msg.sender, _erc7802, _witnetBurns, _witnetMints);
}
function settleBridge(address _newBridge)
external
onlyCurator
{
_require(_newBridge != address(0), "zero bridge");
emit SettledBridge(msg.sender, __storage().bridge, _newBridge);
__storage().bridge = _newBridge;
}
function settleWitOracleCrossChainRpcProviders(string[] memory _witRpcProviders)
external
onlyCurator
{
_require(_witRpcProviders.length > 0, "empty rpc providers");
__storage().witOracleCrossChainRpcProviders = _witRpcProviders;
__formallyVerifyRadonAssets(
_witRpcProviders,
__storage().witCustodianUnwrapper.toBech32(block.chainid == _CANONICAL_CHAIN_ID)
);
emit WitRpcProvidersChanged(msg.sender, _witRpcProviders);
}
function settleWitOracleSettings(WitOracleSettings calldata _settings)
external
onlyCurator
{
_require(
_settings.minWitnesses >= _WIT_ORACLE_REPORTS_MIN_MIN_WITNESSES
&& _settings.extraFeePercentage <= _WIT_ORACLE_QUERIABLE_CONSUMER_MAX_EXTRA_FEE_PERCENTAGE
&& _settings.unitaryRewardNanowits >= _WIT_ORACLE_QUERIABLE_CONSUMER_MIN_UNITARY_REWARD
&& _settings.responseCallbackGasLimit >= _WIT_ORACLE_QUERIABLE_CONSUMER_MIN_RESPONSE_CALLBACK_GAS,
"invalid settings"
);
__storage().witOracleQuerySettings = _settings;
}
function settleWitCustodianUnwrapper(string calldata _witCustodianUnwrapperBech32)
external
onlyCurator
{
__settleWitCustodianUnwrapper(_witCustodianUnwrapperBech32);
}
function transferCuratorship(address _newCurator)
external
onlyCurator
{
_require(_newCurator != address(0), "zero curator");
emit CuratorshipTransferred(__storage().curator, _newCurator);
__storage().curator = _newCurator;
}
/// ===============================================================================================================
/// --- Wrapped/WIT permissionless wrap/unwrap operations ---------------------------------------------------------
function wrap(Witnet.TransactionHash _witnetValueTransferTransactionHash)
override public payable
nonReentrant
whenWitnetMintsNotPaused
returns (uint256 _witOracleQueryId)
{
// Fee sanity checks are perfomed by the underlying WitOracle contract, so providing
// a `msg.value` smaller than by `witOracleEstimateWrappingFee(tx.gasprice)`,
// or 10x greater than, would make this transaction to revert.
_witOracleQueryId = WitnetERC20Lib.witOracleQueryWitnetValueTransferProofOfInclusion(
witOracle,
witOracleCrossChainProofOfInclusionTemplate,
_witnetValueTransferTransactionHash
);
_require(
_witOracleQueryId != WitnetERC20Lib._WIT_ORACLE_QUERIABLE_CONSUMER_CALLBACK_PROCESSED,
"already minted"
);
}
function unwrap(uint64 value, string calldata witRecipientBech32)
override external
whenWitnetBurnsNotPaused
returns (uint256 evmUnwrapId)
{
_require(
balanceOf(_msgSender()) >= value,
"not enough balance"
);
uint64 _evmLastReserveNanowits = __storage().evmLastReserveNanowits;
_require(
value <= _evmLastReserveNanowits,
"cannot unwrap that much"
);
_require(
value >= _MIN_UNWRAPPABLE_NANOWITS,
"cannot unwrap that little"
);
Witnet.Address _recipient = Witnet.fromBech32(
witRecipientBech32,
block.chainid == _CANONICAL_CHAIN_ID
);
_require(
!_recipient.eq(__witCustodianWrapper)
&& !_recipient.eq(__storage().witCustodianUnwrapper),
"invalid recipient"
);
// immediate reduction of reserve supply:
__storage().evmLastReserveNanowits = _evmLastReserveNanowits - value;
// immediate burning of wrapped wit tokens:
_burn(_msgSender(), value);
// increase unwrapping meters:
evmUnwrapId = ++ __storage().evmUnwraps;
__storage().evmUnwrappings += value;
// emit events
emit Unwrapped(
_msgSender(),
witRecipientBech32,
value,
evmUnwrapId
);
}
/// ===============================================================================================================
/// --- Implementation of IWitOracleQueriableConsumer -------------------------------------------------------------
/// @notice Determines if Wit/Oracle query results can be reported from the given address.
function reportableFrom(address _from) override public view returns (bool) {
return address(_from) == address(witOracle);
}
/// @notice Method called from the WitOracle as soon as the specified `queryId` gets reported from the Witnet blockchain.
/// @param queryId The unique identifier of the Witnet query being reported.
/// @param queryResult Abi-encoded Witnet.DataResult containing the CBOR-encoded query's result, and metadata.
function reportWitOracleQueryResult(
uint256 queryId,
bytes calldata queryResult
)
override external
nonReentrant
{
_require(reportableFrom(msg.sender), "invalid oracle");
try WitnetERC20Lib.processWitOracleQueryResult(
queryId,
queryResult
) returns (
Witnet.TransactionHash _witValueTransferTransactionHash,
string memory _witRecipientBech32,
string memory _witSenderBech32,
address _evmRecipient,
uint64 _value
) {
_require(
keccak256(bytes(_witRecipientBech32)) == __witCustodianWrapperBech32Hash,
"invalid custodian"
);
// mint newly wrapped tokens:
_mint(_evmRecipient, _value);
// emit events:
emit Wrapped(
_witSenderBech32,
_evmRecipient,
_value,
_witValueTransferTransactionHash
);
} catch Error(string memory _reason) {
_revert(_reason);
} catch (bytes memory) {
_revertUnhandled();
}
}
/// ===============================================================================================================
/// --- Implementation of IWitOracleConsumer ----------------------------------------------------------------------
/// @notice Process canonical Proof-of-Reserve reports from the Wit/Oracle blockchain, permissionlessly.
function pushDataReport(
Witnet.DataPushReport calldata report,
bytes calldata proof
)
override external
nonReentrant
{
_require(
report.queryParams.witCommitteeSize >= __storage().witOracleQuerySettings.minWitnesses,
"insufficient witnesses"
);
_require(
report.queryRadHash.eq(__storage().witOracleProofOfReserveRadonHash),
"invalid radon hash"
);
// Ask the Wit/Oracle to validate and parse the posted query's result:
Witnet.DataResult memory _witOracleProofOfReserve = witOracle
.pushDataReport(
report,
proof
);
// Parse expected integer from the posted query's result:
try WitnetERC20Lib.parseWitOracleProofOfReserve(
_witOracleProofOfReserve
) returns (
uint64 _totalReserve
) {
emit ReserveUpdate(
_totalReserve,
_witOracleProofOfReserve.timestamp,
_witOracleProofOfReserve.drTxHash
);
__storage().evmLastReserveNanowits = _totalReserve;
__storage().evmLastReserveTimestamp = _witOracleProofOfReserve.timestamp;
} catch Error(string memory _reason) {
_revert(_reason);
} catch (bytes memory) {
_revertUnhandled();
}
}
/// ===============================================================================================================
/// --- Internal methods ------------------------------------------------------------------------------------------
/// @dev Formally verify cross-chain Radon request for notarizing custodian's proofs of reserve.
function __formallyVerifyRadonAssets(
string[] memory _witRpcProviders,
string memory _witCustodianUnwrapperBech32
)
internal
{
string[] memory _commonArgs = new string[](2);
_commonArgs[0] = witCustodianWrapper();
_commonArgs[1] = _witCustodianUnwrapperBech32;
__storage().witOracleProofOfReserveRadonHash = witOracleCrossChainProofOfReserveTemplate
.verifyRadonRequest(
_commonArgs,
_witRpcProviders
);
}
function _require(bool condition, string memory reason) internal pure {
if (!condition) {
_revert(reason);
}
}
function _revert(string memory reason) internal pure {
revert(
string(abi.encodePacked(
"WitnetERC20: ",
reason
))
);
}
function _revertUnhandled() internal pure {
_revert("unhandled exception");
}
function __settleWitCustodianUnwrapper(string memory _witCustodianUnwrapperBech32)
internal
{
_require(
keccak256(bytes(_witCustodianUnwrapperBech32)) != __witCustodianWrapperBech32Hash,
"unacceptable unwrapper"
);
emit NewCustodianUnwrapper(msg.sender, _witCustodianUnwrapperBech32);
__storage().witCustodianUnwrapper = Witnet.fromBech32(_witCustodianUnwrapperBech32, block.chainid == _CANONICAL_CHAIN_ID);
__formallyVerifyRadonAssets(
__storage().witOracleCrossChainRpcProviders,
_witCustodianUnwrapperBech32
);
}
function __storage() internal pure returns (WitnetERC20Lib.Storage storage) {
return WitnetERC20Lib.data();
}
}
IWitnetMintableERC20.sol 77 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.20 <0.9.0;
import "witnet-solidity-bridge/contracts/WitOracle.sol";
interface IWitnetMintableERC20 {
event CuratorshipTransferred(address indexed evmPrevCurator, address indexed evmNewCurator);
event NewCustodianUnwrapper(address curator, string witCustodianUnwrapper);
event PauseFlags(address curator, bool erc7802, bool witnetBurns, bool witnetMints);
event ReserveUpdate(uint256 value, Witnet.Timestamp timestamp, Witnet.TransactionHash indexed witDrTxHash);
event SettledBridge(address curator, address from, address into);
event WitRpcProvidersChanged(address curator, string[] witRpcProviders);
event Wrapped(string witSender, address indexed evmRecipient, uint256 value, Witnet.TransactionHash witVttHash);
event Unwrapped(address indexed evmSender, string witRecipient, uint256 value, uint256 nonce);
struct WitOracleSettings {
uint16 minWitnesses;
uint16 extraFeePercentage;
uint64 unitaryRewardNanowits;
uint24 responseCallbackGasLimit;
}
enum WrappingStatus {
Unknown,
Awaiting,
Retry,
Done
}
// ====================================================================================================================
/// --- Read-only methods ---------------------------------------------------------------------------------------------
function bridge() external view returns (address);
function curator() external view returns (address);
function getWrapTransactionLastQueryId(Witnet.TransactionHash) external view returns (uint256);
function getWrapTransactionStatus(Witnet.TransactionHash) external view returns (WrappingStatus);
function getWrapTransactionStatuses(Witnet.TransactionHash[] calldata) external view returns (WrappingStatus[] memory);
function minUnwrappableNanowits() external view returns (uint256);
function paused() external view returns (bool bridge, bool witnetBurns, bool witnetMints);
function totalReserveNanowits() external view returns (uint256);
function totalUnwrappings() external view returns (uint256);
function totalUnwraps() external view returns (uint256);
function totalWrappings() external view returns (uint256);
function totalWraps() external view returns (uint256);
function witCustodianWrapper() external view returns (string memory);
function witCustodianUnwrapper() external view returns (string memory);
function witOracleCrossChainRpcProviders() external view returns (string[] memory);
function witOracleEstimateWrappingFee(uint256) external view returns (uint256);
function witOracleProofOfReserveLastUpdate() external view returns (Witnet.Timestamp);
function witOracleProofOfReserveRadonBytecode() external view returns (bytes memory);
function witOracleProofOfReserveRadonHash() external view returns (Witnet.RadonHash);
function witOracleQuerySettings() external view returns (WitOracleSettings memory);
/// ===================================================================================================================
/// --- Authoritative methods -----------------------------------------------------------------------------------------
function crosschainPause(bool erc7802, bool witnetBurns, bool witnetMints) external;
function settleBridge(address) external;
function settleWitCustodianUnwrapper(string calldata) external;
function settleWitOracleCrossChainRpcProviders(string[] memory) external;
function settleWitOracleSettings(WitOracleSettings calldata) external;
function transferCuratorship(address) external;
/// ====================================================================================================================
// --- Permissionless state-modifying methods --------------------------------------------------------------------------
function wrap(Witnet.TransactionHash witTxHash) external payable returns (uint256 witOracleQueryId);
function unwrap(uint64, string calldata) external returns (uint256 evmUnwrapId);
}
WitnetERC20Lib.sol 213 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.20 <0.9.0;
import "../interfaces/IWitnetMintableERC20.sol";
import {IWitOracleRadonRequestModal} from "witnet-solidity-bridge/contracts/WitOracleRadonRequestFactory.sol";
/// @title Witnet Request Board base data model library
/// @author The Witnet Foundation.
library WitnetERC20Lib {
using Witnet for Witnet.DataResult;
using Witnet for Witnet.Timestamp;
using WitnetCBOR for WitnetCBOR.CBOR;
bytes32 internal constant _WRAPPED_WIT_STORAGE_SLOT =
/* keccak256("io.witnet.tokens.WIT") & ~bytes32(uint256(0xff) */
0x6116473658e87b023e7f215d122c0048f3d7a669d8df94a5565f0c95871c5800;
uint256 internal constant _WIT_ORACLE_QUERIABLE_CONSUMER_CALLBACK_PROCESSED = type(uint256).max;
uint16 internal constant _WIT_ORACLE_QUERIABLE_CONSUMER_MAX_RESULT_SIZE = 256;
struct Storage {
address bridge; bool pausedBridge; uint88 _0;
address curator; uint96 _1;
Witnet.Timestamp evmLastReserveTimestamp;
uint64 evmLastReserveNanowits;
uint96 evmWrappings;
uint24 evmWraps;
bool pausedWitnetMints;
Witnet.Address witCustodianUnwrapper;
uint64 evmUnwrappings;
uint24 evmUnwraps;
bool pausedWitnetBurns;
IWitnetMintableERC20.WitOracleSettings witOracleQuerySettings;
string[] witOracleCrossChainRpcProviders;
Witnet.RadonHash witOracleProofOfReserveRadonHash;
mapping (Witnet.TransactionHash => uint256) witOracleWrappingTransactionLastQueryId;
mapping (uint256 => Witnet.TransactionHash) witOracleWrappingQueryTransactionHash;
}
// ================================================================================================================
// --- Public functions -------------------------------------------------------------------------------------------
function parseWitOracleProofOfReserve(Witnet.DataResult memory witOracleProofOfReserve)
external view
returns (uint64)
{
uint64[] memory _witBalance;
if (witOracleProofOfReserve.status == Witnet.ResultStatus.NoErrors) {
_witBalance = witOracleProofOfReserve.fetchUint64Array();
}
require(
witOracleProofOfReserve.status == Witnet.ResultStatus.NoErrors
&& witOracleProofOfReserve.timestamp.gt(data().evmLastReserveTimestamp)
&& _witBalance.length >= 3,
"invalid report"
);
return (
_witBalance[0]
+ _witBalance[1]
+ _witBalance[2]
);
}
function processWitOracleQueryResult(
uint256 witOracleQueryId,
bytes calldata witOracleQueryResult
)
external
returns (
Witnet.TransactionHash _witValueTransferTransactionHash,
string memory _witRecipientBech32,
string memory _witSenderBech32,
address _evmRecipient,
uint64 _value
)
{
_witValueTransferTransactionHash = data().witOracleWrappingQueryTransactionHash[witOracleQueryId];
// Check that the query id actually refers to a wit/wrap tx that's being validated:
require(
Witnet.TransactionHash.unwrap(_witValueTransferTransactionHash) != bytes32(0),
"invalid query id"
);
// Check that the Wit/wrap tx being reported has not yet been validated:
require(
data().witOracleWrappingTransactionLastQueryId[_witValueTransferTransactionHash]
!= _WIT_ORACLE_QUERIABLE_CONSUMER_CALLBACK_PROCESSED,
"wit/wrap tx already minted"
);
// Deserialize the query's result data:
Witnet.DataResult memory _witOracleQueryResult = abi.decode(witOracleQueryResult, (Witnet.DataResult));
// Check that the query was successfully solved:
require(
_witOracleQueryResult.status == Witnet.ResultStatus.NoErrors,
"query solved with errors"
);
// Check that the query result contains an heterogenous array of values:
require(
_witOracleQueryResult.dataType == Witnet.RadonDataTypes.Array,
"invalid query result"
);
// Try to parse Witnet Value Transfer metadata being reported:
WitnetCBOR.CBOR[] memory _metadata = _witOracleQueryResult.fetchCborArray();
require(_metadata.length >= 6, "invalid query result shape");
/**
* [
* 0 -> finalized: uint8,
* 1 -> metadata: string,
* 2 -> recipient: string,
* 3 -> sender: string,
* 4 -> timestamp: uint64,
* 5 -> value: uint64,
* ]
**/
// Revert if the referred Witnet transaction is reported to not be finalized just yet:
require(_metadata[0].readUint() == 1, "unfinalized query result");
// Parse data result:
_witRecipientBech32 = _metadata[2].readString();
_witSenderBech32 = _metadata[3].readString();
_evmRecipient = Witnet.toAddress(
Witnet.parseHexString(
_metadata[1].readString()
)
);
Witnet.Timestamp _valueTimestamp = Witnet.Timestamp.wrap(_metadata[4].readUint());
_value = _metadata[5].readUint();
// Increase wrapping meters:
data().evmWraps ++;
data().evmWrappings += uint96(_value);
// Also increase the burnable supply, only if the VTT's inclusion timestamp is fresher than last PoR's timestamp:
if (_valueTimestamp.gt(data().evmLastReserveTimestamp)) {
data().evmLastReserveNanowits += _value;
}
// Mark as processed only after all validations succeeded
data().witOracleWrappingTransactionLastQueryId[_witValueTransferTransactionHash] = _WIT_ORACLE_QUERIABLE_CONSUMER_CALLBACK_PROCESSED;
}
function witOracleQueryWitnetValueTransferProofOfInclusion(
WitOracle witOracle,
IWitOracleRadonRequestModal witOracleCrossChainProofOfInclusionTemplate,
Witnet.TransactionHash witValueTransferTransactionHash
)
external
returns (uint256 _witQueryId)
{
_witQueryId = data().witOracleWrappingTransactionLastQueryId[witValueTransferTransactionHash];
if (
_witQueryId != _WIT_ORACLE_QUERIABLE_CONSUMER_CALLBACK_PROCESSED
) {
// Redundant re-queries when a query is already “Posted” are allowed on purpose,
// as to overcome past queries that could eventually take longer than usual to resolve.
string[] memory _commonArgs = new string[](1);
_commonArgs[0] = Witnet.toHexString(Witnet.TransactionHash.unwrap(witValueTransferTransactionHash));
Witnet.RadonHash _radonHash = witOracleCrossChainProofOfInclusionTemplate
.verifyRadonRequest(
_commonArgs,
data().witOracleCrossChainRpcProviders
);
_witQueryId = IWitOracleQueriable(witOracle).queryDataWithCallback{
value: msg.value
}(
_radonHash,
Witnet.QuerySLA({
witCommitteeSize: data().witOracleQuerySettings.minWitnesses,
witUnitaryReward: data().witOracleQuerySettings.unitaryRewardNanowits,
witResultMaxSize: _WIT_ORACLE_QUERIABLE_CONSUMER_MAX_RESULT_SIZE
}),
Witnet.QueryCallback({
consumer: address(this),
gasLimit: data().witOracleQuerySettings.responseCallbackGasLimit
})
);
data().witOracleWrappingTransactionLastQueryId[witValueTransferTransactionHash] = _witQueryId;
data().witOracleWrappingQueryTransactionHash[_witQueryId] = witValueTransferTransactionHash;
}
}
// ================================================================================================================
// --- Internal functions -----------------------------------------------------------------------------------------
function data() internal pure returns (Storage storage _ptr) {
assembly {
_ptr.slot := _WRAPPED_WIT_STORAGE_SLOT
}
}
function witOracleEstimateWrappingFee(WitOracle witOracle, uint256 evmGasPrice) internal view returns (uint256) {
uint256 _baseFee = witOracle.estimateBaseFeeWithCallback(
evmGasPrice,
data().witOracleQuerySettings.responseCallbackGasLimit
);
return _baseFee * (100 + data().witOracleQuerySettings.extraFeePercentage) / 100;
}
}
Read Contract
DOMAIN_SEPARATOR 0x3644e515 → bytes32
allowance 0xdd62ed3e → uint256
balanceOf 0x70a08231 → uint256
bridge 0xe78cea92 → address
curator 0xe66f53b7 → address
decimals 0x313ce567 → uint8
eip712Domain 0x84b0196e → bytes1, string, string, uint256, address, bytes32, uint256[]
getWrapTransactionLastQueryId 0x77cc7a3a → uint256
getWrapTransactionStatus 0xa53cb851 → uint8
getWrapTransactionStatuses 0xaa22dc33 → uint8[]
minUnwrappableNanowits 0xf7b6604e → uint256
name 0x06fdde03 → string
nonces 0x7ecebe00 → uint256
paused 0x5c975abb → bool, bool, bool
reportableFrom 0x47a10e56 → bool
supportsInterface 0x01ffc9a7 → bool
symbol 0x95d89b41 → string
totalReserveNanowits 0x0ed4c438 → uint256
totalSupply 0x18160ddd → uint256
totalUnwrappings 0x3243bc60 → uint256
totalUnwraps 0x520a5495 → uint256
totalWrappings 0x692ea04b → uint256
totalWraps 0x6aaa54cf → uint256
witCustodianUnwrapper 0x147040de → string
witCustodianWrapper 0xb3f120a1 → string
witOracle 0x1014d375 → address
witOracleCrossChainProofOfInclusionTemplate 0xf69a00b6 → address
witOracleCrossChainProofOfReserveTemplate 0x27ae6883 → address
witOracleCrossChainRpcProviders 0x71d41eb3 → string[]
witOracleEstimateWrappingFee 0xddb2bf5c → uint256
witOracleProofOfReserveLastUpdate 0x30315dc4 → uint64
witOracleProofOfReserveRadonBytecode 0xacb734bb → bytes
witOracleProofOfReserveRadonHash 0xfec53a14 → bytes32
witOracleQuerySettings 0x873234cf → tuple
Write Contract 17 functions
These functions modify contract state and require a wallet transaction to execute.
approve 0x095ea7b3
address spender
uint256 value
returns: bool
crosschainBurn 0x2b8c49e3
address from
uint256 value
crosschainMint 0x18bf5077
address to
uint256 value
crosschainPause 0x57a6d45a
bool _erc7802
bool _witnetBurns
bool _witnetMints
initialize 0xf399e22e
address _curator
string _witCustodianUnwrapperBech32
permit 0xd505accf
address owner
address spender
uint256 value
uint256 deadline
uint8 v
bytes32 r
bytes32 s
pushDataReport 0x3d4e3cc7
tuple report
bytes proof
reportWitOracleQueryResult 0xd6f29e81
uint256 queryId
bytes queryResult
settleBridge 0x9ecb381a
address _newBridge
settleWitCustodianUnwrapper 0xc65a20f0
string _witCustodianUnwrapperBech32
settleWitOracleCrossChainRpcProviders 0xea9e96a6
string[] _witRpcProviders
settleWitOracleSettings 0xd9bb7258
tuple _settings
transfer 0xa9059cbb
address to
uint256 value
returns: bool
transferCuratorship 0xa41942a4
address _newCurator
transferFrom 0x23b872dd
address from
address to
uint256 value
returns: bool
unwrap 0x8a510f27
uint64 value
string witRecipientBech32
returns: uint256
wrap 0x5f029ebe
bytes32 _witnetValueTransferTransactionHash
returns: uint256
Recent Transactions
No transactions found for this address