Address Contract Verified
Address
0x6c644cf9B3ca2AFB11f2218ec4a9F2a561C99626
Balance
0 ETH
Nonce
1
Code Size
16243 bytes
Creator
0xE7DCFE1B...585e at tx 0xaa50062a...74342a
Indexed Transactions
0 (1 on-chain, 0% indexed)
Contract Bytecode
16243 bytes
0x6080604052600436106102675760003560e01c80635c975abb11610144578063b4e7c8d5116100b6578063d547741f1161007a578063d547741f146107a1578063e4f376f0146107c1578063e63ab1e9146107fc578063fb6162b014610830578063fbfa77cf14610863578063ffa1ad741461088357600080fd5b8063b4e7c8d5146106e0578063bae43a1714610714578063bb9db1d314610734578063c39cc7b114610754578063cdecf0451461077457600080fd5b80638cdd3711116101085780638cdd37111461060d57806391d148541461062d578063941909e71461064d578063a217fddf1461066d578063ad3cb1cc14610682578063b3e3f288146106c057600080fd5b80635c975abb1461057d5780635de366c0146105a257806378d67f61146105c25780637a5f6d7b146105e25780638456cb59146105f857600080fd5b806336568abe116101dd5780634c4d30fc116101a15780634c4d30fc146104d75780634de5383b146104f75780634f1ef2861461052957806352d1902d1461053c57806355ae3f2214610551578063593bd11e1461056757600080fd5b806336568abe14610456578063382f7579146104765780633f4ba83a1461048c578063423160cc146104a1578063464d77d3146104b757600080fd5b80631d0ec8041161022f5780631d0ec8041461036e5780631e4e0091146103a25780632290bf58146103c2578063248a9ca3146103e257806324ea54f4146104025780632f2ff15d1461043657600080fd5b806301ffc9a71461026c57806307e2da96146102a157806308e93ea5146102d9578063116191b6146103145780631181c1801461034c575b600080fd5b34801561027857600080fd5b5061028c610287366004613366565b6108b1565b60405190151581526020015b60405180910390f35b3480156102ad57600080fd5b506004546102c1906001600160401b031681565b6040516001600160401b039091168152602001610298565b3480156102e557600080fd5b506103066102f43660046133a7565b60066020526000908152604090205481565b604051908152602001610298565b34801561032057600080fd5b50600154610334906001600160a01b031681565b6040516001600160a01b039091168152602001610298565b34801561035857600080fd5b5061036c6103673660046133d7565b6108e8565b005b34801561037a57600080fd5b506103067f2b664c6bd75f93c938153ebeda1a9c2b041092a0c9966749a0b54a17d501dd0d81565b3480156103ae57600080fd5b5061036c6103bd3660046133f4565b610916565b3480156103ce57600080fd5b5061036c6103dd366004613612565b610930565b3480156103ee57600080fd5b506103066103fd3660046136d5565b610bf6565b34801561040e57600080fd5b506103067f55435dd261a4b9b3364963f7738a7a662ad9c84396d64be3365284bb7f0a504181565b34801561044257600080fd5b5061036c6104513660046136ee565b610c18565b34801561046257600080fd5b5061036c6104713660046136ee565b610c3a565b34801561048257600080fd5b506102c161100081565b34801561049857600080fd5b5061036c610c6d565b3480156104ad57600080fd5b5061030660035481565b3480156104c357600080fd5b5061036c6104d236600461371e565b610c83565b3480156104e357600080fd5b5061036c6104f2366004613765565b610e4e565b34801561050357600080fd5b50600a546105149063ffffffff1681565b60405163ffffffff9091168152602001610298565b61036c61053736600461378f565b610e92565b34801561054857600080fd5b50610306610eb1565b34801561055d57600080fd5b5061030660055481565b34801561057357600080fd5b5061030660095481565b34801561058957600080fd5b50600080516020613f1e8339815191525460ff1661028c565b3480156105ae57600080fd5b5061036c6105bd366004613826565b610ece565b3480156105ce57600080fd5b5061036c6105dd3660046133d7565b610efa565b3480156105ee57600080fd5b5061030660085481565b34801561060457600080fd5b5061036c610f4e565b34801561061957600080fd5b5061036c610628366004613867565b610f80565b34801561063957600080fd5b5061028c6106483660046136ee565b611225565b34801561065957600080fd5b5061036c6106683660046136d5565b61125d565b34801561067957600080fd5b50610306600081565b34801561068e57600080fd5b506106b3604051806040016040528060058152602001640352e302e360dc1b81525081565b60405161029891906138f1565b3480156106cc57600080fd5b50600c54610334906001600160a01b031681565b3480156106ec57600080fd5b506103067f7f38b5d46e112fa3e1881849ae78427c56ffc703e62199b9477dbe78db2e94b381565b34801561072057600080fd5b5061036c61072f3660046136d5565b611297565b34801561074057600080fd5b5061036c61074f366004613904565b6112d6565b34801561076057600080fd5b5061036c61076f366004613937565b6112fe565b34801561078057600080fd5b5061030661078f3660046136d5565b60076020526000908152604090205481565b3480156107ad57600080fd5b5061036c6107bc3660046136ee565b61170d565b3480156107cd57600080fd5b5061028c6107dc3660046139e9565b805160208183018101805160008252928201919093012091525460ff1681565b34801561080857600080fd5b506103067f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b34801561083c57600080fd5b5061051461084b3660046136d5565b600b6020526000908152604090205463ffffffff1681565b34801561086f57600080fd5b50600254610334906001600160a01b031681565b34801561088f57600080fd5b506040805180820190915260058152640302e312e360dc1b60208201526106b3565b60006001600160e01b03198216637965db0b60e01b14806108e257506301ffc9a760e01b6001600160e01b03198316145b92915050565b60006108f381611729565b50600c80546001600160a01b0319166001600160a01b0392909216919091179055565b600061092181611729565b61092b8383611733565b505050565b610938611796565b600085815260076020908152604080832054600a54818552600b90935292205463ffffffff9182169161096c911642613a4f565b101561098b576040516304004b9b60e11b815260040160405180910390fd5b806109a957604051635e18297560e11b815260040160405180910390fd5b60006109e78286886040516020016109d39190815181526020918201519181019190915260400190565b6040516020818303038152906040526117c9565b50905080610a085760405163207a2e5560e21b815260040160405180910390fd5b6000610a24876020015185876040516020016109d39190613a62565b50905080610a4557604051635dd5590360e01b815260040160405180910390fd5b6000610a526000876118ca565b805190915063ffffffff1615610a7b57604051631b40be7b60e01b815260040160405180910390fd5b6020810151604051632ba7822f60e01b81526000917333d3b4b66ae89e640d048e10f857ef0e20ed52cb91632ba7822f91610abb91859190600401613a7e565b600060405180830381865af4158015610ad8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610b009190810190613b53565b90506000816020015160000151604051610b1a9190613a62565b9081526040519081900360200190205460ff1615610b4a57604051623f613760e71b815260040160405180910390fd5b60016000826020015160000151604051610b649190613a62565b908152602001604051809103902060006101000a81548160ff0219169083151502179055506000610b9c826020015160200151611a0d565b9050610ba781611ae7565b602082015151610bb690611b52565b6040518281527f1a95b33fabfa977d8127ccbe17c99ebaae863489b31ea9549270d1599ae730b79060200160405180910390a25050505050505050505050565b6000908152600080516020613efe833981519152602052604090206001015490565b610c2182610bf6565b610c2a81611729565b610c348383611be7565b50505050565b6001600160a01b0381163314610c635760405163334bd91960e11b815260040160405180910390fd5b61092b8282611c93565b6000610c7881611729565b610c80611d0f565b50565b7f55435dd261a4b9b3364963f7738a7a662ad9c84396d64be3365284bb7f0a5041610cad81611729565b610cb5611796565b6004546001600160401b031660008181526006602052604090205480610cee57604051630f49c3c160e21b815260040160405180910390fd5b816001600160401b0316866001600160401b0316111580610d225750611000610d178388613be3565b6001600160401b0316115b15610d405760405163f277783560e01b815260040160405180910390fd5b6001600160401b0386166000818152600660209081526040808320899055600554835260078252808320889055878352600b825291829020805463ffffffff19164263ffffffff16179055815192835282018790527f292f5abc3167175400fca463fa99530cda826ec53ec5eb1f3a2776006dacd75d910160405180910390a183866001600160401b0316836001600160401b03167fcd3f6b17554d9b1ffdca3e8d4fd49a7f2219dc00eaf2e69507df8f3c598c0548600554604051610e0891815260200190565b60405180910390a460058054906000610e2083613c03565b90915550506004805467ffffffffffffffff19166001600160401b0397909716969096179095555050505050565b6000610e5981611729565b610e61611796565b5063ffffffff9091166000818152600660205260409020919091556004805467ffffffffffffffff19169091179055565b610e9a611d6f565b610ea382611e14565b610ead8282611e1f565b5050565b6000610ebb611edc565b50600080516020613ede83398151915290565b6000610ed981611729565b8282604051610ee9929190613c1c565b604051908190039020600355505050565b7f7f38b5d46e112fa3e1881849ae78427c56ffc703e62199b9477dbe78db2e94b3610f2481611729565b610ead7f2b664c6bd75f93c938153ebeda1a9c2b041092a0c9966749a0b54a17d501dd0d83610c18565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a610f7881611729565b610c80611f25565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b0316600081158015610fc55750825b90506000826001600160401b03166001148015610fe15750303b155b905081158015610fef575080155b1561100d5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561103757845460ff60401b1916600160401b1785555b611042600033611be7565b5061106d7f55435dd261a4b9b3364963f7738a7a662ad9c84396d64be3365284bb7f0a504133611be7565b506110987f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a33611be7565b506110c37f7f38b5d46e112fa3e1881849ae78427c56ffc703e62199b9477dbe78db2e94b333611be7565b506110cc611f6e565b6110dc60408701602088016133d7565b600180546001600160a01b0319166001600160a01b039290921691909117905561110960208701876133d7565b600280546001600160a01b0319166001600160a01b0392909216919091179055606086018035906006906000906111439060408b016133a7565b6001600160401b03166001600160401b031681526020019081526020016000208190555085604001602081019061117a91906133a7565b6004805467ffffffffffffffff19166001600160401b0392909216919091179055608086013560095560a08601356008556111b860c0870187613c2c565b6040516111c6929190613c1c565b6040519081900390206003556001600555831561121d57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050565b6000918252600080516020613efe833981519152602090815260408084206001600160a01b0393909316845291905290205460ff1690565b600061126881611729565b50600160005260066020527f3e5fec24aa4dc4e5aee2e025e51e1392c72a2500577559fae9665c6d52bd6a3155565b60006112a281611729565b600882905560405182907f43dd5643e53de1f9208e3fbc1960b756ab8f488fa4936750f5ac7e35fd71cfae90600090a25050565b60006112e181611729565b50600a805463ffffffff191663ffffffff92909216919091179055565b611306611796565b600086815260076020908152604080832054600a54818552600b90935292205463ffffffff9182169161133a911642613a4f565b1015611359576040516304004b9b60e11b815260040160405180910390fd5b8061137757604051635e18297560e11b815260040160405180910390fd5b60006113a78261138688613c72565b604080518b356020808301919091528c0135918101919091526060016109d3565b509050806113c85760405163207a2e5560e21b815260040160405180910390fd5b60006113ee60208901356113db86613c72565b88886040516020016109d3929190613c1c565b5090508061140f57604051635dd5590360e01b815260040160405180910390fd5b6000611452600088888080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506118ca92505050565b805190915063ffffffff161561147b57604051631b40be7b60e01b815260040160405180910390fd5b6020810151604051633888dfcd60e01b81526000917333d3b4b66ae89e640d048e10f857ef0e20ed52cb91633888dfcd916114bb91859190600401613a7e565b600060405180830381865af41580156114d8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115009190810190613c7e565b9050600081602001516000015160405161151a9190613a62565b9081526040519081900360200190205460ff161561154a57604051623f613760e71b815260040160405180910390fd5b600354602080830151606001515180519101201461157b5760405163c1ab6dc160e01b815260040160405180910390fd5b600160008260200151600001516040516115959190613a62565b908152602001604051809103902060006101000a81548160ff02191690831515021790555060008082602001516020015151602a03611678576115df836020015160200151611f7e565b600c5460405163511865e960e01b81526001600160a01b03808416600483015292945091169063511865e990602401602060405180830381865afa15801561162b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061164f9190613da8565b90506001600160a01b03811661167357611670836020015160400151611f7e565b90505b61168c565b611689836020015160400151611f7e565b90505b60006116a384602001516060015160200151611b52565b90506116af8282612095565b816001600160a01b03166116ca856020015160000151611b52565b6040518381527f1366555870c37fa6d0a14bba77d9b420b10593604c84c9ea15a17f8a940c1b509060200160405180910390a35050505050505050505050505050565b61171682610bf6565b61171f81611729565b610c348383611c93565b610c8081336120f7565b600080516020613efe833981519152600061174d84610bf6565b600085815260208490526040808220600101869055519192508491839187917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a450505050565b600080516020613f1e8339815191525460ff16156117c75760405163d93c066560e01b815260040160405180910390fd5b565b60008060018460400151116117f157835151156117ec57506000905060016118c2565b611817565b61180384602001518560400151612130565b8451511461181757506000905060016118c2565b836040015184602001511061183257506000905060026118c2565b600061183d846121c6565b8551519091506000036118705784604001516001036118635785149150600090506118c2565b60008092509250506118c2565b60008061188b87602001518860400151858a6000015161223b565b909250905060008160058111156118a4576118a4613dc5565b146118b7576000945092506118c2915050565b508614925060009150505b935093915050565b6040805160808082018352600080835260606020808501829052848601839052818501839052855193840186528284528301819052938201819052928101839052909161191785856123c5565b905060005b8151811015611a035781818151811061193757611937613ddb565b6020026020010151606001516001600160401b031660010361197d5781818151811061196557611965613ddb565b602090810291909101015160e0015163ffffffff1683525b81818151811061198f5761198f613ddb565b6020026020010151606001516001600160401b03166002036119d3578181815181106119bd576119bd613ddb565b6020026020010151610100015183602001819052505b8181815181106119e5576119e5613ddb565b50508181815181106119f9576119f9613ddb565b505060010161191c565b5090949350505050565b6000818180805b8351831015611ac5576000848481518110611a3157611a31613ddb565b016020015160f81c905060308110801590611a4d575060398111155b15611a7957611a5d603082613a4f565b611a6884600a613df1565b611a729190613e08565b9250611ab9565b83158015611a87575080602d145b15611a955760019150611ab9565b86604051637594c35160e11b8152600401611ab091906138f1565b60405180910390fd5b50600190920191611a14565b8015611ade57611ad482613e1b565b9695505050505050565b50949350505050565b80600003611af25750565b600254604051622e09b760e51b8152600481018390526001600160a01b03909116906305c136e090602401600060405180830381600087803b158015611b3757600080fd5b505af1158015611b4b573d6000803e3d6000fd5b5050505050565b600081815b8151811015611be0576000828281518110611b7457611b74613ddb565b016020015160f81c905060308110801590611b90575060398111155b15611bbc57611ba0603082613a4f565b611bab85600a613df1565b611bb59190613e08565b9350611bd7565b84604051636d344d9f60e11b8152600401611ab091906138f1565b50600101611b57565b5050919050565b6000600080516020613efe833981519152611c028484611225565b611c82576000848152602082815260408083206001600160a01b03871684529091529020805460ff19166001179055611c383390565b6001600160a01b0316836001600160a01b0316857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a460019150506108e2565b60009150506108e2565b5092915050565b6000600080516020613efe833981519152611cae8484611225565b15611c82576000848152602082815260408083206001600160a01b0387168085529252808320805460ff1916905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a460019150506108e2565b611d17612524565b600080516020613f1e833981519152805460ff191681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a150565b306001600160a01b037f0000000000000000000000006c644cf9b3ca2afb11f2218ec4a9f2a561c99626161480611df657507f0000000000000000000000006c644cf9b3ca2afb11f2218ec4a9f2a561c996266001600160a01b0316611dea600080516020613ede833981519152546001600160a01b031690565b6001600160a01b031614155b156117c75760405163703e46dd60e11b815260040160405180910390fd5b6000610ead81611729565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611e79575060408051601f3d908101601f19168201909252611e7691810190613e37565b60015b611ea157604051634c9c8ce360e01b81526001600160a01b0383166004820152602401611ab0565b600080516020613ede8339815191528114611ed257604051632a87526960e21b815260048101829052602401611ab0565b61092b8383612554565b306001600160a01b037f0000000000000000000000006c644cf9b3ca2afb11f2218ec4a9f2a561c9962616146117c75760405163703e46dd60e11b815260040160405180910390fd5b611f2d611796565b600080516020613f1e833981519152805460ff191660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833611d51565b611f766125aa565b6117c76125f3565b600080602a835114611fa357604051638dc6ac0160e01b815260040160405180910390fd5b82600081518110611fb657611fb6613ddb565b60209101015160f81c603014611fdf576040516303bc10f160e11b815260040160405180910390fd5b82600181518110611ff257611ff2613ddb565b60209101015160f81c60781461201b576040516303bc10f160e11b815260040160405180910390fd5b60025b602a811015611c8c57600061204e85836001018151811061204157612041613ddb565b016020015160f81c612614565b600461206587858151811061204157612041613ddb565b60ff90811690911b91909101166001600160a01b036004600119850102609803161b92909201915060020161201e565b600254604051630357371d60e01b81526001600160a01b0384811660048301526024820184905290911690630357371d90604401600060405180830381600087803b1580156120e357600080fd5b505af115801561121d573d6000803e3d6000fd5b6121018282611225565b610ead5760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401611ab0565b600060018211612142575060006108e2565b61214b82612683565b61215790610100613a4f565b90506000612166600183613a4f565b6001901b90506001816121799190613a4f565b841161218557506108e2565b806001036121975760019150506108e2565b6121b36121a48286613a4f565b6121ae8386613a4f565b612130565b6121be906001613e08565b9150506108e2565b60006002600060f81b836040516020016121e1929190613e50565b60408051601f19818403018152908290526121fb91613a62565b602060405180830381855afa158015612218573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906108e29190613e37565b60008084600003612251575082905060036123bc565b846001036122765782511561226b575082905060046123bc565b5082905060006123bc565b825160000361228a575082905060056123bc565b6000612295866126b0565b905060006122b2856000600188516122ad9190613a4f565b6126f2565b90506000828910156123365760006122cc8a858a8661223b565b9250905060008260058111156122e4576122e4613dc5565b146122f7575086945092506123bc915050565b612327818860018a5161230a9190613a4f565b8151811061231a5761231a613ddb565b602002602001015161286e565b600095509550505050506123bc565b6000612356612345858c613a4f565b61234f868c613a4f565b8a8661223b565b92509050600082600581111561236e5761236e613dc5565b14612381575086945092506123bc915050565b6123b187600189516123939190613a4f565b815181106123a3576123a3613ddb565b60200260200101518261286e565b600095509550505050505b94509492505050565b604080516001808252818301909252606091816020015b6123e4613316565b8152602001906001900390816123dc5790505090506000835b8351816001600160401b0316101561251c57600061241b82866128ec565b90508060a0015191508451826001600160401b031610156124e8576000612443846002613e81565b60ff166001600160401b0381111561245d5761245d613416565b60405190808252806020026020018201604052801561249657816020015b612483613316565b81526020019060019003908161247b5790505b50905060005b85518110156124e4578581815181106124b7576124b7613ddb565b60200260200101518282815181106124d1576124d1613ddb565b602090810291909101015260010161249c565b5093505b80848460ff16815181106124fe576124fe613ddb565b6020908102919091010152612514600184613e81565b9250506123fd565b505092915050565b600080516020613f1e8339815191525460ff166117c757604051638dfc202b60e01b815260040160405180910390fd5b61255d82612cbf565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a28051156125a25761092b8282612d24565b610ead612d9a565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff166117c757604051631afcd79f60e31b815260040160405180910390fd5b6125fb6125aa565b600080516020613f1e833981519152805460ff19169055565b600060308260ff161015801561262e575060398260ff1611155b1561263b5750602f190190565b60618260ff1610158015612653575060668260ff1611155b1561266057506056190190565b604051635554647b60e01b815260ff83166004820152602401611ab0565b919050565b60005b81816001901b10156126a45761269d600182613e08565b9050612686565b6108e281610100613a4f565b600060018210156126c057600080fd5b60006126cb83612db9565b905060006126da600183613a4f565b6001901b90508381036126eb5760011c5b9392505050565b6060818311156127575760405162461bcd60e51b815260206004820152602a60248201527f496e76616c69642072616e67653a205f626567696e2069732067726561746572604482015269081d1a185b8817d95b9960b21b6064820152608401611ab0565b83518311806127665750835182115b156127cb5760405162461bcd60e51b815260206004820152602f60248201527f496e76616c69642072616e67653a205f626567696e206f72205f656e6420617260448201526e65206f7574206f6620626f756e647360881b6064820152608401611ab0565b60006127d78484613a4f565b6001600160401b038111156127ee576127ee613416565b604051908082528060200260200182016040528015612817578160200160208202803683370190505b509050835b83811015611ade5785818151811061283657612836613ddb565b602002602001015182868361284b9190613a4f565b8151811061285b5761285b613ddb565b602090810291909101015260010161281c565b604051600160f81b6020820152602181018390526041810182905260009060029060610160408051601f19818403018152908290526128ac91613a62565b602060405180830381855afa1580156128c9573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906126eb9190613e37565b6128f4613316565b6000806000806129048787612ddc565b93509350935093508551876001600160401b03161061295d5760405162461bcd60e51b81526020600482015260156024820152747374617274206c656e677468206f766572666c6f7760581b6044820152606401611ab0565b836129995760405162461bcd60e51b815260206004820152600c60248201526b6b6579206465636f64696e6760a01b6044820152606401611ab0565b6129a1613316565b6001600160401b03808516602083015283166060820152808260068111156129cb576129cb613dc5565b908160068111156129de576129de613dc5565b90525060008260068111156129f5576129f5613dc5565b03612a8b576000806000612a09878b612ece565b92509250925082612a4e5760405162461bcd60e51b815260206004820152600f60248201526e766172696e74206465636f64696e6760881b6044820152606401611ab0565b6001600160401b038083166040860152811660c0850152612a6f8783613be3565b6001600160401b0390811660808601529190911660a084015250505b6001826006811115612a9f57612a9f613dc5565b03612b34576000806000612ab3878b61306e565b92509250925082612af85760405162461bcd60e51b815260206004820152600f60248201526e626974733634206465636f64696e6760881b6044820152606401611ab0565b6001600160401b038083166040860152811660c0850152600860808501819052612b229088613e9a565b6001600160401b031660a08501525050505b6005826006811115612b4857612b48613dc5565b03612be1576000806000612b5c878b6130ac565b92509250925082612ba15760405162461bcd60e51b815260206004820152600f60248201526e626974733332206465636f64696e6760881b6044820152606401611ab0565b6001600160401b038216604085015263ffffffff811660e0850152600460808501819052612bcf9088613e9a565b6001600160401b031660a08501525050505b6002826006811115612bf557612bf5613dc5565b03612cb4576000806000612c09878b6130fc565b92509250925082612c5c5760405162461bcd60e51b815260206004820152601960248201527f6c656e6774682064656c696d69746564206465636f64696e67000000000000006044820152606401611ab0565b612c798a836001600160401b0316836001600160401b0316613184565b6101008501526001600160401b038083166040860152811660808501819052612ca29083613e9a565b6001600160401b031660a08501525050505b979650505050505050565b806001600160a01b03163b600003612cf557604051634c9c8ce360e01b81526001600160a01b0382166004820152602401611ab0565b600080516020613ede83398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080846001600160a01b031684604051612d419190613a62565b600060405180830381855af49150503d8060008114612d7c576040519150601f19603f3d011682016040523d82523d6000602084013e612d81565b606091505b5091509150612d91858383613291565b95945050505050565b34156117c75760405163b398979f60e01b815260040160405180910390fd5b6000805b82156108e25780612dcd81613c03565b915050600183901c9250612dbd565b6000806000806000806000612df18989612ece565b92509250925082612e11575060009550935084925060069150612ec59050565b600381901c671fffffffffffffff166007821660068110612e45576000846000600698509850985098505050505050612ec5565b6000816001600160401b03166006811115612e6257612e62613dc5565b90506003816006811115612e7857612e78613dc5565b1480612e9557506004816006811115612e9357612e93613dc5565b145b15612eb457600085600060069950995099509950505050505050612ec5565b600199509397509095509193505050505b92959194509250565b60008060008060005b600a6001600160401b0382161015612fb9578551612ef58883613e9a565b6001600160401b031610612f155760008760009450945094505050613067565b600086612f22838a613e9a565b6001600160401b031681518110612f3b57612f3b613ddb565b016020015160f81c9050607f8116612f54836007613eba565b60ff82166001600160401b03919091161b939093179260808216600003612faf576000836001600160401b0316118015612f8f575060ff8116155b15612fa857600089600096509650965050505050613067565b5050612fb9565b5050600101612ed7565b600a6001600160401b03821610612fdc5760008760009450945094505050613067565b612fe86001600a613be3565b6001600160401b0316816001600160401b0316036130465760018661300d838a613e9a565b6001600160401b03168151811061302657613026613ddb565b016020015160f81c11156130465760008760009450945094505050613067565b60016130528289613e9a565b61305d906001613e9a565b8394509450945050505b9250925092565b6000806000806000806130818888612ece565b9250925092508261309d57506000945092508391506130679050565b60019891975095509350505050565b6000806000806000806130bf8888612ece565b925092509250826130db57506000945092508391506130679050565b67ffffffff0000000081161561309d57506000945092508391506130679050565b60008060008060008061310f8888612ece565b9250925092508261312b57506000945092508391506130679050565b816001600160401b03168183016001600160401b0316101561315857506000945092508391506130679050565b86516131648383613e9a565b6001600160401b0316111561309d57506000945092508391506130679050565b60608161319281601f613e08565b10156131d15760405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b6044820152606401611ab0565b6131db8284613e08565b8451101561321f5760405162461bcd60e51b8152602060048201526011602482015270736c6963655f6f75744f66426f756e647360781b6044820152606401611ab0565b60608215801561323e5760405191506000825260208201604052611ade565b6040519150601f8416801560200281840101858101878315602002848b0101015b8183101561327757805183526020928301920161325f565b5050858452601f01601f1916604052505090509392505050565b6060826132a6576132a1826132ed565b6126eb565b81511580156132bd57506001600160a01b0384163b155b156132e657604051639996b31560e01b81526001600160a01b0385166004820152602401611ab0565b50806126eb565b8051156132fd5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b60408051610120810190915280600081526000602082018190526040820181905260608083018290526080830182905260a0830182905260c0830182905260e08301919091526101009091015290565b60006020828403121561337857600080fd5b81356001600160e01b0319811681146126eb57600080fd5b80356001600160401b038116811461267e57600080fd5b6000602082840312156133b957600080fd5b6126eb82613390565b6001600160a01b0381168114610c8057600080fd5b6000602082840312156133e957600080fd5b81356126eb816133c2565b6000806040838503121561340757600080fd5b50508035926020909101359150565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b038111828210171561344e5761344e613416565b60405290565b604080519081016001600160401b038111828210171561344e5761344e613416565b604051608081016001600160401b038111828210171561344e5761344e613416565b604051601f8201601f191681016001600160401b03811182821017156134c0576134c0613416565b604052919050565b6000606082840312156134da57600080fd5b6134e261342c565b905081356001600160401b03808211156134fb57600080fd5b818401915084601f83011261350f57600080fd5b813560208282111561352357613523613416565b8160051b9250613534818401613498565b828152928401810192818101908885111561354e57600080fd5b948201945b8486101561356c57853582529482019490820190613553565b80875250508086013581860152505050506040820135604082015292915050565b60006001600160401b038211156135a6576135a6613416565b50601f01601f191660200190565b60006135c76135c28461358d565b613498565b90508281528383830111156135db57600080fd5b828260208301376000602084830101529392505050565b600082601f83011261360357600080fd5b6126eb838335602085016135b4565b600080600080600085870360c081121561362b57600080fd5b863595506040601f198201121561364157600080fd5b5061364a613454565b6020878101358252604088013590820152935060608601356001600160401b038082111561367757600080fd5b61368389838a016134c8565b9450608088013591508082111561369957600080fd5b6136a589838a016135f2565b935060a08801359150808211156136bb57600080fd5b506136c8888289016134c8565b9150509295509295909350565b6000602082840312156136e757600080fd5b5035919050565b6000806040838503121561370157600080fd5b823591506020830135613713816133c2565b809150509250929050565b60008060006060848603121561373357600080fd5b61373c84613390565b95602085013595506040909401359392505050565b803563ffffffff8116811461267e57600080fd5b6000806040838503121561377857600080fd5b61378183613751565b946020939093013593505050565b600080604083850312156137a257600080fd5b82356137ad816133c2565b915060208301356001600160401b038111156137c857600080fd5b6137d4858286016135f2565b9150509250929050565b60008083601f8401126137f057600080fd5b5081356001600160401b0381111561380757600080fd5b60208301915083602082850101111561381f57600080fd5b9250929050565b6000806020838503121561383957600080fd5b82356001600160401b0381111561384f57600080fd5b61385b858286016137de565b90969095509350505050565b60006020828403121561387957600080fd5b81356001600160401b0381111561388f57600080fd5b820160e081850312156126eb57600080fd5b60005b838110156138bc5781810151838201526020016138a4565b50506000910152565b600081518084526138dd8160208601602086016138a1565b601f01601f19169290920160200192915050565b6020815260006126eb60208301846138c5565b60006020828403121561391657600080fd5b6126eb82613751565b60006060828403121561393157600080fd5b50919050565b60008060008060008086880360c081121561395157600080fd5b873596506040601f198201121561396757600080fd5b5060208701945060608701356001600160401b038082111561398857600080fd5b6139948a838b0161391f565b955060808901359150808211156139aa57600080fd5b6139b68a838b016137de565b909550935060a08901359150808211156139cf57600080fd5b506139dc89828a0161391f565b9150509295509295509295565b6000602082840312156139fb57600080fd5b81356001600160401b03811115613a1157600080fd5b8201601f81018413613a2257600080fd5b613a31848235602084016135b4565b949350505050565b634e487b7160e01b600052601160045260246000fd5b818103818111156108e2576108e2613a39565b60008251613a748184602087016138a1565b9190910192915050565b6001600160401b0383168152604060208201526000613a3160408301846138c5565b600082601f830112613ab157600080fd5b8151613abf6135c28261358d565b818152846020838601011115613ad457600080fd5b613a318260208301602087016138a1565b600060408284031215613af757600080fd5b613aff613454565b905081516001600160401b0380821115613b1857600080fd5b613b2485838601613aa0565b83526020840151915080821115613b3a57600080fd5b50613b4784828501613aa0565b60208301525092915050565b600060208284031215613b6557600080fd5b81516001600160401b0380821115613b7c57600080fd5b9083019060408286031215613b9057600080fd5b613b98613454565b825182811115613ba757600080fd5b613bb387828601613aa0565b825250602083015182811115613bc857600080fd5b613bd487828601613ae5565b60208301525095945050505050565b6001600160401b03828116828216039080821115611c8c57611c8c613a39565b600060018201613c1557613c15613a39565b5060010190565b8183823760009101908152919050565b6000808335601e19843603018112613c4357600080fd5b8301803591506001600160401b03821115613c5d57600080fd5b60200191503681900382131561381f57600080fd5b60006108e236836134c8565b600060208284031215613c9057600080fd5b81516001600160401b0380821115613ca757600080fd5b9083019060408286031215613cbb57600080fd5b613cc3613454565b825182811115613cd257600080fd5b613cde87828601613aa0565b825250602083015182811115613cf357600080fd5b929092019160808387031215613d0857600080fd5b613d10613476565b835183811115613d1f57600080fd5b613d2b88828701613aa0565b825250602084015183811115613d4057600080fd5b613d4c88828701613aa0565b602083015250604084015183811115613d6457600080fd5b613d7088828701613aa0565b604083015250606084015183811115613d8857600080fd5b613d9488828701613ae5565b606083015250602082015295945050505050565b600060208284031215613dba57600080fd5b81516126eb816133c2565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b80820281158282048414176108e2576108e2613a39565b808201808211156108e2576108e2613a39565b6000600160ff1b8201613e3057613e30613a39565b5060000390565b600060208284031215613e4957600080fd5b5051919050565b6001600160f81b0319831681528151600090613e738160018501602087016138a1565b919091016001019392505050565b60ff81811683821601908111156108e2576108e2613a39565b6001600160401b03818116838216019080821115611c8c57611c8c613a39565b6001600160401b0381811683821602808216919082811461251c5761251c613a3956fe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300a2646970667358221220f6be014c15b69c3874efdcf520fe8fe634bb9a23952902f773ba191e11f45c6864736f6c63430008180033
Verified Source Code Full Match
Compiler: v0.8.24+commit.e11b9ed9
EVM: paris
Optimization: Yes (200 runs)
AccessControlUpgradeable.sol 233 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
struct AccessControlStorage {
mapping(bytes32 role => RoleData) _roles;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;
function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {
assembly {
$.slot := AccessControlStorageLocation
}
}
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
function __AccessControl_init() internal onlyInitializing {
}
function __AccessControl_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
return $._roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
AccessControlStorage storage $ = _getAccessControlStorage();
return $._roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
AccessControlStorage storage $ = _getAccessControlStorage();
bytes32 previousAdminRole = getRoleAdmin(role);
$._roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
if (!hasRole(role, account)) {
$._roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
if (hasRole(role, account)) {
$._roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}
Initializable.sol 228 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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 reininitialization) 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 Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}
UUPSUpgradeable.sol 153 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.20;
import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Initializable} from "./Initializable.sol";
/**
* @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
* {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
*
* A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
* reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
* `UUPSUpgradeable` with a custom implementation of upgrades.
*
* The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
*/
abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable __self = address(this);
/**
* @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
* and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
* while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
* If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
* be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
* during an upgrade.
*/
string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
/**
* @dev The call is from an unauthorized context.
*/
error UUPSUnauthorizedCallContext();
/**
* @dev The storage `slot` is unsupported as a UUID.
*/
error UUPSUnsupportedProxiableUUID(bytes32 slot);
/**
* @dev Check that the execution is being performed through a delegatecall call and that the execution context is
* a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
* for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
* function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
* fail.
*/
modifier onlyProxy() {
_checkProxy();
_;
}
/**
* @dev Check that the execution is not being performed through a delegate call. This allows a function to be
* callable on the implementing contract but not through proxies.
*/
modifier notDelegated() {
_checkNotDelegated();
_;
}
function __UUPSUpgradeable_init() internal onlyInitializing {
}
function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
}
/**
* @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
* implementation. It is used to validate the implementation's compatibility when performing an upgrade.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
*/
function proxiableUUID() external view virtual notDelegated returns (bytes32) {
return ERC1967Utils.IMPLEMENTATION_SLOT;
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
* encoded in `data`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, data);
}
/**
* @dev Reverts if the execution is not performed via delegatecall or the execution
* context is not of a proxy with an ERC1967-compliant implementation pointing to self.
* See {_onlyProxy}.
*/
function _checkProxy() internal view virtual {
if (
address(this) == __self || // Must be called through delegatecall
ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
) {
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Reverts if the execution is performed via delegatecall.
* See {notDelegated}.
*/
function _checkNotDelegated() internal view virtual {
if (address(this) != __self) {
// Must not be called through delegatecall
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
* {upgradeToAndCall}.
*
* Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
*
* ```solidity
* function _authorizeUpgrade(address) internal onlyOwner {}
* ```
*/
function _authorizeUpgrade(address newImplementation) internal virtual;
/**
* @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
*
* As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
* is expected to be the implementation slot in ERC1967.
*
* Emits an {IERC1967-Upgraded} event.
*/
function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
revert UUPSUnsupportedProxiableUUID(slot);
}
ERC1967Utils.upgradeToAndCall(newImplementation, data);
} catch {
// The implementation is not UUPS
revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
}
}
}
ContextUpgradeable.sol 34 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @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 ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
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;
}
}
PausableUpgradeable.sol 140 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Pausable
struct PausableStorage {
bool _paused;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Pausable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300;
function _getPausableStorage() private pure returns (PausableStorage storage $) {
assembly {
$.slot := PausableStorageLocation
}
}
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Initializes the contract in unpaused state.
*/
function __Pausable_init() internal onlyInitializing {
__Pausable_init_unchained();
}
function __Pausable_init_unchained() internal onlyInitializing {
PausableStorage storage $ = _getPausableStorage();
$._paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
PausableStorage storage $ = _getPausableStorage();
return $._paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
PausableStorage storage $ = _getPausableStorage();
$._paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
PausableStorage storage $ = _getPausableStorage();
$._paused = false;
emit Unpaused(_msgSender());
}
}
ERC165Upgradeable.sol 33 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 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 ERC165Upgradeable is Initializable, IERC165 {
function __ERC165_init() internal onlyInitializing {
}
function __ERC165_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
IAccessControl.sol 98 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)
pragma solidity ^0.8.20;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev The `account` is missing a role.
*/
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
/**
* @dev The caller of a function is not the expected one.
*
* NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
*/
error AccessControlBadConfirmation();
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*/
function renounceRole(bytes32 role, address callerConfirmation) external;
}
draft-IERC1822.sol 20 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC1822.sol)
pragma solidity ^0.8.20;
/**
* @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
* proxy whose upgrades are fully controlled by the current implementation.
*/
interface IERC1822Proxiable {
/**
* @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
* address.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy.
*/
function proxiableUUID() external view returns (bytes32);
}
ERC1967Utils.sol 193 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)
pragma solidity ^0.8.20;
import {IBeacon} from "../beacon/IBeacon.sol";
import {Address} from "../../utils/Address.sol";
import {StorageSlot} from "../../utils/StorageSlot.sol";
/**
* @dev This abstract contract provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
*/
library ERC1967Utils {
// We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
// This will be fixed in Solidity 0.8.21. At that point we should remove these events.
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Emitted when the beacon is changed.
*/
event BeaconUpgraded(address indexed beacon);
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev The `implementation` of the proxy is invalid.
*/
error ERC1967InvalidImplementation(address implementation);
/**
* @dev The `admin` of the proxy is invalid.
*/
error ERC1967InvalidAdmin(address admin);
/**
* @dev The `beacon` of the proxy is invalid.
*/
error ERC1967InvalidBeacon(address beacon);
/**
* @dev An upgrade function sees `msg.value > 0` that may be lost.
*/
error ERC1967NonPayable();
/**
* @dev Returns the current implementation address.
*/
function getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
if (newImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(newImplementation);
}
StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Performs implementation upgrade with additional setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-Upgraded} event.
*/
function upgradeToAndCall(address newImplementation, bytes memory data) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
if (data.length > 0) {
Address.functionDelegateCall(newImplementation, data);
} else {
_checkNonPayable();
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Returns the current admin.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
* the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
*/
function getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
if (newAdmin == address(0)) {
revert ERC1967InvalidAdmin(address(0));
}
StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {IERC1967-AdminChanged} event.
*/
function changeAdmin(address newAdmin) internal {
emit AdminChanged(getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Returns the current beacon.
*/
function getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the EIP1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
if (newBeacon.code.length == 0) {
revert ERC1967InvalidBeacon(newBeacon);
}
StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
address beaconImplementation = IBeacon(newBeacon).implementation();
if (beaconImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(beaconImplementation);
}
}
/**
* @dev Change the beacon and trigger a setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-BeaconUpgraded} event.
*
* CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
* it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
* efficiency.
*/
function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length > 0) {
Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
} else {
_checkNonPayable();
}
}
/**
* @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
* if an upgrade doesn't perform an initialization call.
*/
function _checkNonPayable() private {
if (msg.value > 0) {
revert ERC1967NonPayable();
}
}
}
IBeacon.sol 16 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.20;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {UpgradeableBeacon} will check that this address is a contract.
*/
function implementation() external view returns (address);
}
Address.sol 159 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}
StorageSlot.sol 135 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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 ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* 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;
* }
* }
* ```
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 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) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
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) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
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) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* 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[EIP 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);
}
FuelStreamX.sol 390 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import { Pausable } from "../utils/Pausable.sol";
import { ExecTxResultLib } from "./lib/ExecTxResultLib.sol";
import { ProtobufAnyLib, WithdrawalAny, SupplyDeltaAny } from "./lib/ProtobufAnyLib.sol";
import { StringToUint } from "./lib/StringToUint.sol";
import { StringToInt } from "./lib/StringToInt.sol";
import { StringToAddress } from "./lib/StringToAddress.sol";
import { IFuelStreamX } from "../interfaces/IFuelStreamX.sol";
import { IVault } from "../interfaces/IVault.sol";
import { ISequencerMessageHandler, BridgeCommitmentLeaf } from "../interfaces/ISequencerMessageHandler.sol";
import { IRecoveryRegistry } from "../interfaces/IRecoveryRegistry.sol";
import "../vendor/blobstream/lib/tree/binary/BinaryMerkleTree.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
contract FuelStreamX is
IFuelStreamX,
ISequencerMessageHandler,
AccessControlUpgradeable,
Pausable,
UUPSUpgradeable
{
bytes32 public constant GUARDIAN_ROLE = keccak256("GUARDIAN_ROLE");
bytes32 public constant L2_ADMIN_ROLE = keccak256("L2_ADMIN_ROLE");
bytes32 public constant L2_ACCOUNT_ROLE = keccak256("L2_ACCOUNT_ROLE");
mapping(string => bool) public usedNonces;
/// @notice The address of the gateway contract.
address public gateway;
/// @notice Contract that contains tokens to be released or its supply delta updated
address public vault;
bytes32 public tokenCosmosDenominationHash;
/// @notice The block is the first one in the next bridge commitment.
uint64 public latestBlock;
/// @notice The maximum number of blocks that can be skipped in a single request. Should be
/// large enough to skip forward at least 4 hours.
uint64 public constant BRIDGE_COMMITMENT_MAX = 4096;
/// @notice Nonce for proof events. Must be incremented sequentially.
uint256 public state_proofNonce;
/// @notice Maps block heights to their header hashes.
mapping(uint64 => bytes32) public blockHeightToHeaderHash;
/// @notice Mapping of bridge commitment nonces to bridge commitments.
mapping(uint256 => bytes32) public state_bridgeCommitments;
/// @notice Header range function id.
bytes32 public headerRangeFunctionId;
/// @notice Next header function id.
bytes32 public nextHeaderFunctionId;
/// @notice Time after which a commit becomes finalized
uint32 public timeToFinalize;
/// @notice Timestamp when the commitment is made or updated
mapping(bytes32 => uint32) public commitmentTimestamp;
/// @notice Address of the recovery registry contract
address public recoveryRegistry;
struct InitParameters {
address vault;
address gateway;
uint64 height;
bytes32 header;
bytes32 nextHeaderFunctionId;
bytes32 headerRangeFunctionId;
string tokenCosmosDenomination;
}
function VERSION() external pure returns (string memory) {
return "0.1.0";
}
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize(InitParameters calldata _params) external initializer {
_grantRole(DEFAULT_ADMIN_ROLE, _msgSender());
_grantRole(GUARDIAN_ROLE, _msgSender());
_grantRole(PAUSER_ROLE, _msgSender());
_grantRole(L2_ADMIN_ROLE, _msgSender());
__Pausable_init();
gateway = _params.gateway;
vault = _params.vault;
blockHeightToHeaderHash[_params.height] = _params.header;
latestBlock = _params.height;
nextHeaderFunctionId = _params.nextHeaderFunctionId;
headerRangeFunctionId = _params.headerRangeFunctionId;
tokenCosmosDenominationHash = keccak256(
bytes(_params.tokenCosmosDenomination)
);
state_proofNonce = 1;
}
function setCosmosTokenDenomination(
string calldata denomination
) external onlyRole(DEFAULT_ADMIN_ROLE) {
tokenCosmosDenominationHash = keccak256(bytes(denomination));
}
function setRecoveryRegistry(
address newRecoveryRegistry
) external onlyRole(DEFAULT_ADMIN_ROLE) {
recoveryRegistry = newRecoveryRegistry;
}
function initializeTrustedHeader(
bytes32 header
) external onlyRole(DEFAULT_ADMIN_ROLE) {
blockHeightToHeaderHash[1] = header;
}
function setTimeToFinalize(
uint32 newTimeToFinalize
) external onlyRole(DEFAULT_ADMIN_ROLE) {
timeToFinalize = newTimeToFinalize;
}
/// @notice Only admin can update the genesis state of the light client.
function updateGenesisState(
uint32 _height,
bytes32 _header
) external onlyRole(DEFAULT_ADMIN_ROLE) whenNotPaused {
blockHeightToHeaderHash[_height] = _header;
latestBlock = _height;
}
/// @notice Only the guardian can update commit header range in case of succinct not working.
function updateCommitHeaderRange(
uint64 _targetBlock,
bytes32 _targetHeader,
bytes32 _bridgeCommitment // check 0 bytes
) external onlyRole(GUARDIAN_ROLE) whenNotPaused {
uint64 _latestBlock = latestBlock;
bytes32 trustedHeader = blockHeightToHeaderHash[_latestBlock];
if (trustedHeader == bytes32(0)) {
revert TrustedHeaderNotFound();
}
if (
_targetBlock <= _latestBlock ||
_targetBlock - _latestBlock > BRIDGE_COMMITMENT_MAX
) {
revert TargetBlockNotInRange();
}
blockHeightToHeaderHash[_targetBlock] = _targetHeader;
state_bridgeCommitments[state_proofNonce] = _bridgeCommitment;
commitmentTimestamp[_bridgeCommitment] = uint32(block.timestamp);
emit HeadUpdate(_targetBlock, _targetHeader);
emit BridgeCommitmentStored(
state_proofNonce,
_latestBlock,
_targetBlock,
_bridgeCommitment
);
state_proofNonce++;
latestBlock = _targetBlock;
}
function processSequencerWithdrawalMessage(
uint256 _proofNonce,
BridgeCommitmentLeaf calldata bridgeCommitmentLeaf,
BinaryMerkleProof calldata bridgeCommitmentLeafProof,
bytes calldata txResultMarshalled,
BinaryMerkleProof calldata txResultProof
) external virtual whenNotPaused {
// Load the tuple root at the given index from storage.
bytes32 root = state_bridgeCommitments[_proofNonce];
if (block.timestamp - commitmentTimestamp[root] < timeToFinalize) {
revert CommitmentNotFinalized();
}
if (root == bytes32(0)) {
revert InvalidProofNonce();
}
// ------- Merkle verification
// Verify the proof, be sure the `abi.encode(BridgeCommitmentLeaf)` is giving the same encoded bytes array that was used
// in the inclusion query
(bool isProofValid, ) = BinaryMerkleTree.verify(
root,
bridgeCommitmentLeafProof,
abi.encode(bridgeCommitmentLeaf)
);
if (!isProofValid) revert InvalidCommitmentProof();
// Verify that that transaction was in the ResultsHash
(bool isTxResultProofValid, ) = BinaryMerkleTree.verify(
bridgeCommitmentLeaf.resultsHash,
txResultProof,
abi.encodePacked(txResultMarshalled)
);
if (!isTxResultProofValid) revert InvalidTxResultProof();
// ------- Transaction verification
// Decode the transaction given
ExecTxResultLib.ExecTxResult
memory execTxResultDecoded = ExecTxResultLib.decode(
0,
txResultMarshalled
);
if (execTxResultDecoded.code != 0) revert InvalidExecTxResultCode();
// Decode the data section
WithdrawalAny memory withdrawal = ProtobufAnyLib.decodeWithdrawal(
0,
execTxResultDecoded.data
);
if (usedNonces[withdrawal.value.nonce]) revert NonceAlreadyUsed();
if (
keccak256(bytes(withdrawal.value.amount.denom)) !=
tokenCosmosDenominationHash
) revert InvalidToken();
// ------ Message action
usedNonces[withdrawal.value.nonce] = true;
address from;
address recipient;
if (bytes(withdrawal.value.from).length == 42) { // eth address
from = StringToAddress.stringToAddress(
bytes(withdrawal.value.from)
);
recipient = IRecoveryRegistry(recoveryRegistry)
.getRecoveryAddress(from);
if (recipient == address(0)) {
recipient = StringToAddress.stringToAddress(
bytes(withdrawal.value.to)
);
}
} else {
recipient = StringToAddress.stringToAddress(
bytes(withdrawal.value.to)
);
}
uint256 amount = StringToUint.stringToUint(
withdrawal.value.amount.amount
);
_callVaultRelease(recipient, amount);
emit WithdrawalProcessed(
StringToUint.stringToUint(withdrawal.value.nonce),
recipient,
amount
);
}
/**
* @dev Validates proof and processes a supply update message.
*/
function processSequencerSupplyUpdate(
uint256 _proofNonce,
BridgeCommitmentLeaf memory bridgeCommitmentLeaf,
BinaryMerkleProof memory bridgeCommitmentLeafProof,
bytes memory txResultMarshalled,
BinaryMerkleProof memory txResultProof
) external virtual whenNotPaused {
// Load the tuple root at the given index from storage.
bytes32 root = state_bridgeCommitments[_proofNonce];
if (block.timestamp - commitmentTimestamp[root] < timeToFinalize) {
revert CommitmentNotFinalized();
}
if (root == bytes32(0)) {
revert InvalidProofNonce();
}
// ------- Merkle verification
// Verify the proof, be sure the `abi.encode(BridgeCommitmentLeaf)` is giving the same encoded bytes array that was used
// in the inclusion query
(bool isProofValid, ) = BinaryMerkleTree.verify(
root,
bridgeCommitmentLeafProof,
abi.encode(bridgeCommitmentLeaf)
);
if (!isProofValid) revert InvalidCommitmentProof();
// Verify that that transaction was in the ResultsHash
(bool isTxResultProofValid, ) = BinaryMerkleTree.verify(
bridgeCommitmentLeaf.resultsHash,
txResultProof,
abi.encodePacked(txResultMarshalled)
);
if (!isTxResultProofValid) revert InvalidTxResultProof();
// ------- Transaction verification
// Decode the transaction given
ExecTxResultLib.ExecTxResult
memory execTxResultDecoded = ExecTxResultLib.decode(
0,
txResultMarshalled
);
if (execTxResultDecoded.code != 0) revert InvalidExecTxResultCode();
// Decode the data section
SupplyDeltaAny memory supplyDelta = ProtobufAnyLib.decodeSupplyDelta(
0,
execTxResultDecoded.data
);
if (usedNonces[supplyDelta.value.nonce]) {
revert NonceAlreadyUsed();
}
usedNonces[supplyDelta.value.nonce] = true;
int256 delta = StringToInt.stringToInt(supplyDelta.value.delta);
_callVaultAdjustSurplus(delta);
emit SupplyDelta(
StringToUint.stringToUint(supplyDelta.value.nonce),
delta
);
}
/// @dev This is required to set the {L2_ADMIN_ROLE} the admin role of {L2_ACCOUNT_ROLE}
function setRoleAdmin(
bytes32 role,
bytes32 roleAdmin
) external onlyRole(DEFAULT_ADMIN_ROLE) {
_setRoleAdmin(role, roleAdmin);
}
/// @dev This is required to keep track of the validators on sequencer chain.
function grantL2AccountRole(
address l2Account
) external onlyRole(L2_ADMIN_ROLE) {
grantRole(L2_ACCOUNT_ROLE, l2Account);
}
/// @dev This is required to update the headerRangeFunctionId
function updateHeaderRangeFunctionId(
bytes32 _headerRangeFunctionId
) external onlyRole(DEFAULT_ADMIN_ROLE) {
headerRangeFunctionId = _headerRangeFunctionId;
emit HeaderRangeFunctionIdUpdated(_headerRangeFunctionId);
}
function _callVaultRelease(address recipient, uint256 amount) internal {
IVault(vault).release(recipient, amount);
}
function _callVaultAdjustSurplus(int256 adjustment) internal {
if (adjustment == 0) {
return;
}
IVault(vault).adjustSurplus(adjustment);
}
function _authorizeUpgrade(
address newImplementation
) internal virtual override onlyRole(DEFAULT_ADMIN_ROLE) {}
}
BytesLib.sol 548 lines
// SPDX-License-Identifier: Apache 2.0
// Modified from Goncalo Sa - BytesLib.
pragma solidity >=0.8.0 <0.9.0;
library BytesLib {
function concat(
bytes memory _preBytes,
bytes memory _postBytes
) internal pure returns (bytes memory) {
bytes memory tempBytes;
assembly {
// Get a location of some free memory and store it in tempBytes as
// Solidity does for memory variables.
tempBytes := mload(0x40)
// Store the length of the first bytes array at the beginning of
// the memory for tempBytes.
let length := mload(_preBytes)
mstore(tempBytes, length)
// Maintain a memory counter for the current write location in the
// temp bytes array by adding the 32 bytes for the array length to
// the starting location.
let mc := add(tempBytes, 0x20)
// Stop copying when the memory counter reaches the length of the
// first bytes array.
let end := add(mc, length)
for {
// Initialize a copy counter to the start of the _preBytes data,
// 32 bytes into its memory.
let cc := add(_preBytes, 0x20)
} lt(mc, end) {
// Increase both counters by 32 bytes each iteration.
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
// Write the _preBytes data into the tempBytes memory 32 bytes
// at a time.
mstore(mc, mload(cc))
}
// Add the length of _postBytes to the current length of tempBytes
// and store it as the new length in the first 32 bytes of the
// tempBytes memory.
length := mload(_postBytes)
mstore(tempBytes, add(length, mload(tempBytes)))
// Move the memory counter back from a multiple of 0x20 to the
// actual end of the _preBytes data.
mc := end
// Stop copying when the memory counter reaches the new combined
// length of the arrays.
end := add(mc, length)
for {
let cc := add(_postBytes, 0x20)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
// Update the free-memory pointer by padding our last write location
// to 32 bytes: add 31 bytes to the end of tempBytes to move to the
// next 32 byte block, then round down to the nearest multiple of
// 32. If the sum of the length of the two arrays is zero then add
// one before rounding down to leave a blank 32 bytes (the length block with 0).
mstore(
0x40,
and(
add(add(end, iszero(add(length, mload(_preBytes)))), 31),
not(31) // Round down to the nearest 32 bytes.
)
)
}
return tempBytes;
}
function concatStorage(
bytes storage _preBytes,
bytes memory _postBytes
) internal {
assembly {
// Read the first 32 bytes of _preBytes storage, which is the length
// of the array. (We don't need to use the offset into the slot
// because arrays use the entire slot.)
let fslot := sload(_preBytes.slot)
// Arrays of 31 bytes or less have an even value in their slot,
// while longer arrays have an odd value. The actual length is
// the slot divided by two for odd values, and the lowest order
// byte divided by two for even values.
// If the slot is even, bitwise and the slot with 255 and divide by
// two to get the length. If the slot is odd, bitwise and the slot
// with -1 and divide by two.
let slength := div(
and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)),
2
)
let mlength := mload(_postBytes)
let newlength := add(slength, mlength)
// slength can contain both the length and contents of the array
// if length < 32 bytes so let's prepare for that
// v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
switch add(lt(slength, 32), lt(newlength, 32))
case 2 {
// Since the new array still fits in the slot, we just need to
// update the contents of the slot.
// uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
sstore(
_preBytes.slot,
// all the modifications to the slot are inside this
// next block
add(
// we can just add to the slot contents because the
// bytes we want to change are the LSBs
fslot,
add(
mul(
div(
// load the bytes from memory
mload(add(_postBytes, 0x20)),
// zero all bytes to the right
exp(0x100, sub(32, mlength))
),
// and now shift left the number of bytes to
// leave space for the length in the slot
exp(0x100, sub(32, newlength))
),
// increase length by the double of the memory
// bytes length
mul(mlength, 2)
)
)
)
}
case 1 {
// The stored value fits in the slot, but the combined value
// will exceed it.
// get the keccak hash to get the contents of the array
mstore(0x0, _preBytes.slot)
let sc := add(keccak256(0x0, 0x20), div(slength, 32))
// save new length
sstore(_preBytes.slot, add(mul(newlength, 2), 1))
// The contents of the _postBytes array start 32 bytes into
// the structure. Our first read should obtain the `submod`
// bytes that can fit into the unused space in the last word
// of the stored array. To get this, we read 32 bytes starting
// from `submod`, so the data we read overlaps with the array
// contents by `submod` bytes. Masking the lowest-order
// `submod` bytes allows us to add that value directly to the
// stored value.
let submod := sub(32, slength)
let mc := add(_postBytes, submod)
let end := add(_postBytes, mlength)
let mask := sub(exp(0x100, submod), 1)
sstore(
sc,
add(
and(
fslot,
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
),
and(mload(mc), mask)
)
)
for {
mc := add(mc, 0x20)
sc := add(sc, 1)
} lt(mc, end) {
sc := add(sc, 1)
mc := add(mc, 0x20)
} {
sstore(sc, mload(mc))
}
mask := exp(0x100, sub(mc, end))
sstore(sc, mul(div(mload(mc), mask), mask))
}
default {
// get the keccak hash to get the contents of the array
mstore(0x0, _preBytes.slot)
// Start copying to the last used word of the stored array.
let sc := add(keccak256(0x0, 0x20), div(slength, 32))
// save new length
sstore(_preBytes.slot, add(mul(newlength, 2), 1))
// Copy over the first `submod` bytes of the new data as in
// case 1 above.
let slengthmod := mod(slength, 32)
let mlengthmod := mod(mlength, 32)
let submod := sub(32, slengthmod)
let mc := add(_postBytes, submod)
let end := add(_postBytes, mlength)
let mask := sub(exp(0x100, submod), 1)
sstore(sc, add(sload(sc), and(mload(mc), mask)))
for {
sc := add(sc, 1)
mc := add(mc, 0x20)
} lt(mc, end) {
sc := add(sc, 1)
mc := add(mc, 0x20)
} {
sstore(sc, mload(mc))
}
mask := exp(0x100, sub(mc, end))
sstore(sc, mul(div(mload(mc), mask), mask))
}
}
}
function slice(
bytes memory _bytes,
uint256 _start,
uint256 _length
) internal pure returns (bytes memory) {
require(_length + 31 >= _length, "slice_overflow");
require(_bytes.length >= _start + _length, "slice_outOfBounds");
bytes memory tempBytes;
assembly {
switch iszero(_length)
case 0 {
// Get a location of some free memory and store it in tempBytes as
// Solidity does for memory variables.
tempBytes := mload(0x40)
// The first word of the slice result is potentially a partial
// word read from the original array. To read it, we calculate
// the length of that partial word and start copying that many
// bytes into the array. The first word we copy will start with
// data we don't care about, but the last `lengthmod` bytes will
// land at the beginning of the contents of the new array. When
// we're done copying, we overwrite the full first word with
// the actual length of the slice.
let lengthmod := and(_length, 31)
// The multiplication in the next line is necessary
// because when slicing multiples of 32 bytes (lengthmod == 0)
// the following copy loop was copying the origin's length
// and then ending prematurely not copying everything it should.
let mc := add(
add(tempBytes, lengthmod),
mul(0x20, iszero(lengthmod))
)
let end := add(mc, _length)
for {
// The multiplication in the next line has the same exact purpose
// as the one above.
let cc := add(
add(
add(_bytes, lengthmod),
mul(0x20, iszero(lengthmod))
),
_start
)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
mstore(tempBytes, _length)
//update free-memory pointer
//allocating the array padded to 32 bytes like the compiler does now
mstore(0x40, and(add(mc, 31), not(31)))
}
//if we want a zero-length slice let's just return a zero-length array
default {
tempBytes := mload(0x40)
//zero out the 32 bytes slice we are about to return
//we need to do it because Solidity does not garbage collect
mstore(tempBytes, 0)
mstore(0x40, add(tempBytes, 0x20))
}
}
return tempBytes;
}
function toAddress(
bytes memory _bytes,
uint256 _start
) internal pure returns (address) {
require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
address tempAddress;
assembly {
tempAddress := div(
mload(add(add(_bytes, 0x20), _start)),
0x1000000000000000000000000
)
}
return tempAddress;
}
function toUint8(
bytes memory _bytes,
uint256 _start
) internal pure returns (uint8) {
require(_bytes.length >= _start + 1, "toUint8_outOfBounds");
uint8 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x1), _start))
}
return tempUint;
}
function toUint16(
bytes memory _bytes,
uint256 _start
) internal pure returns (uint16) {
require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
uint16 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x2), _start))
}
return tempUint;
}
function toUint32(
bytes memory _bytes,
uint256 _start
) internal pure returns (uint32) {
require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
uint32 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x4), _start))
}
return tempUint;
}
function toUint64(
bytes memory _bytes,
uint256 _start
) internal pure returns (uint64) {
require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
uint64 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x8), _start))
}
return tempUint;
}
function toUint96(
bytes memory _bytes,
uint256 _start
) internal pure returns (uint96) {
require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
uint96 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0xc), _start))
}
return tempUint;
}
function toUint128(
bytes memory _bytes,
uint256 _start
) internal pure returns (uint128) {
require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
uint128 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x10), _start))
}
return tempUint;
}
function toUint256(
bytes memory _bytes,
uint256 _start
) internal pure returns (uint256) {
require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
uint256 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x20), _start))
}
return tempUint;
}
function toBytes32(
bytes memory _bytes,
uint256 _start
) internal pure returns (bytes32) {
require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
bytes32 tempBytes32;
assembly {
tempBytes32 := mload(add(add(_bytes, 0x20), _start))
}
return tempBytes32;
}
function equal(
bytes memory _preBytes,
bytes memory _postBytes
) internal pure returns (bool) {
bool success = true;
assembly {
let length := mload(_preBytes)
// if lengths don't match the arrays are not equal
switch eq(length, mload(_postBytes))
case 1 {
// cb is a circuit breaker in the for loop since there's
// no said feature for inline assembly loops
// cb = 1 - don't breaker
// cb = 0 - break
let cb := 1
let mc := add(_preBytes, 0x20)
let end := add(mc, length)
for {
let cc := add(_postBytes, 0x20)
// the next line is the loop condition:
// while(uint256(mc < end) + cb == 2)
} eq(add(lt(mc, end), cb), 2) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
// if any of these checks fails then arrays are not equal
if iszero(eq(mload(mc), mload(cc))) {
// unsuccess:
success := 0
cb := 0
}
}
}
default {
// unsuccess:
success := 0
}
}
return success;
}
function equalStorage(
bytes storage _preBytes,
bytes memory _postBytes
) internal view returns (bool) {
bool success = true;
assembly {
// we know _preBytes_offset is 0
let fslot := sload(_preBytes.slot)
// Decode the length of the stored array like in concatStorage().
let slength := div(
and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)),
2
)
let mlength := mload(_postBytes)
// if lengths don't match the arrays are not equal
switch eq(slength, mlength)
case 1 {
// slength can contain both the length and contents of the array
// if length < 32 bytes so let's prepare for that
// v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
if iszero(iszero(slength)) {
switch lt(slength, 32)
case 1 {
// blank the last byte which is the length
fslot := mul(div(fslot, 0x100), 0x100)
if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
// unsuccess:
success := 0
}
}
default {
// cb is a circuit breaker in the for loop since there's
// no said feature for inline assembly loops
// cb = 1 - don't breaker
// cb = 0 - break
let cb := 1
// get the keccak hash to get the contents of the array
mstore(0x0, _preBytes.slot)
let sc := keccak256(0x0, 0x20)
let mc := add(_postBytes, 0x20)
let end := add(mc, mlength)
// the next line is the loop condition:
// while(uint256(mc < end) + cb == 2)
for {
} eq(add(lt(mc, end), cb), 2) {
sc := add(sc, 1)
mc := add(mc, 0x20)
} {
if iszero(eq(sload(sc), mload(mc))) {
// unsuccess:
success := 0
cb := 0
}
}
}
}
}
default {
// unsuccess:
success := 0
}
}
return success;
}
}
ExecTxResultLib.sol 43 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.4 <0.9.0;
import { BytesLib } from "./BytesLib.sol";
import { ProtobufLib } from "./ProtobufLib.sol";
library ExecTxResultLib {
struct ExecTxResult {
uint32 code;
bytes data;
int64 gasWanted;
int64 gasUsed;
}
function decode(
uint64 start,
bytes memory buffer
) internal pure returns (ExecTxResult memory) {
ExecTxResult memory execTxResult;
ProtobufLib.Value[] memory values = ProtobufLib.decodeFields(
start,
buffer
);
for (uint i = 0; i < values.length; i++) {
if (values[i].number == 1) {
execTxResult.code = values[i].data32;
}
if (values[i].number == 2) {
execTxResult.data = values[i].dataBytes;
}
// Skip, gasWanted will not be used
if (values[i].number == 3) {}
// Skip, gasUsed will not be used
if (values[i].number == 4) {}
}
return execTxResult;
}
}
ProtobufAnyLib.sol 116 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.4 <0.9.0;
import { BytesLib } from "./BytesLib.sol";
import { ProtobufLib } from "./ProtobufLib.sol";
import { WithdrawalLib } from "./WithdrawalLib.sol";
import { SupplyDeltaLib, SupplyDeltaData } from "./SupplyDeltaLib.sol";
struct WithdrawalAny {
string typeUrl;
WithdrawalLib.Data value;
}
struct SupplyDeltaAny {
string typeUrl;
SupplyDeltaData value;
}
library ProtobufAnyLib {
error InvalidWithdrawalTypeUrl(string);
error InvalidSupplyDeltaTypeUrl(string);
bytes32 internal constant WITHDRAWAL_TYPE_URL =
keccak256("/fuelsequencer.bridge.v1.MsgWithdrawToEthereumResponse");
bytes32 internal constant SUPPLYDELTA_TYPE_URL =
keccak256("/fuelsequencer.bridge.v1.MsgSupplyDeltaResponse");
function decodeWithdrawal(
uint64 start,
bytes memory buffer
) public pure returns (WithdrawalAny memory) {
WithdrawalAny memory withdrawalAny;
ProtobufLib.Value[] memory values = ProtobufLib.decodeFields(
start,
buffer
);
for (uint i = 0; i < values.length; i++) {
// Skip
if (values[i].number == 1) {}
if (values[i].number == 2) {
ProtobufLib.Value[] memory values2 = ProtobufLib.decodeFields(
0,
values[i].dataBytes
);
for (uint j = 0; j < values2.length; j++) {
// Type Url
if (values2[j].number == 1) {
withdrawalAny.typeUrl = ProtobufLib.decode_string(
values2[j].dataBytes
);
}
// Value
if (values2[j].number == 2) {
withdrawalAny.value = WithdrawalLib.decode(
0,
values2[j].dataBytes
);
}
}
}
}
if (keccak256(bytes(withdrawalAny.typeUrl)) != WITHDRAWAL_TYPE_URL) {
revert InvalidWithdrawalTypeUrl(withdrawalAny.typeUrl);
}
return withdrawalAny;
}
function decodeSupplyDelta(
uint64 start,
bytes memory buffer
) public pure returns (SupplyDeltaAny memory supplyDeltaAny) {
ProtobufLib.Value[] memory values = ProtobufLib.decodeFields(
start,
buffer
);
for (uint i = 0; i < values.length; i++) {
// Skip
if (values[i].number == 1) {}
if (values[i].number == 2) {
ProtobufLib.Value[] memory values2 = ProtobufLib.decodeFields(
0,
values[i].dataBytes
);
for (uint j = 0; j < values2.length; j++) {
// Type Url
if (values2[j].number == 1) {
supplyDeltaAny.typeUrl = ProtobufLib.decode_string(
values2[j].dataBytes
);
}
// Value
if (values2[j].number == 2) {
supplyDeltaAny.value = SupplyDeltaLib.decode(
0,
values2[j].dataBytes
);
}
}
}
}
if (keccak256(bytes(supplyDeltaAny.typeUrl)) != SUPPLYDELTA_TYPE_URL) {
revert InvalidSupplyDeltaTypeUrl(supplyDeltaAny.typeUrl);
}
return supplyDeltaAny;
}
}
ProtobufLib.sol 902 lines
// SPDX-License-Identifier: Apache-2.0
// Modified from Celestia Labs; 2020 - ProtobufLib [under Apache 2.0].
pragma solidity >=0.8.4 <0.9.0;
import { BytesLib } from "./BytesLib.sol";
library ProtobufLib {
/// @notice Protobuf wire types.
enum WireType {
Varint,
Bits64,
LengthDelimited,
StartGroup,
EndGroup,
Bits32,
WIRE_TYPE_MAX
}
/// @notice Union of all possible values as single struct.
struct Value {
WireType kind;
uint64 position;
uint64 positionData;
uint64 number;
uint64 size;
uint64 offset;
uint64 data64;
uint32 data32;
bytes dataBytes;
}
/// @dev Maximum number of bytes for a varint.
/// @dev 64 bits, in groups of base-128 (7 bits).
uint64 internal constant MAX_VARINT_BYTES = 10;
////////////////////////////////////
// Decoding
////////////////////////////////////
/// @notice Decode fields.
function decodeFields(
uint64 start,
bytes memory buffer
) internal pure returns (Value[] memory fields) {
fields = new Value[](1);
uint8 count = 0;
uint64 offset = start;
while (offset < buffer.length) {
Value memory val = decode(offset, buffer);
offset = val.offset;
if (offset < buffer.length) {
Value[] memory fieldsNew = new Value[](count + 2);
for (uint i = 0; i < fields.length; i++) {
fieldsNew[i] = fields[i];
}
fields = fieldsNew;
}
fields[count] = val;
count += 1;
}
}
/// @notice Decode any.
function decode(
uint64 start,
bytes memory buffer
) internal pure returns (Value memory) {
(
bool success,
uint64 position,
uint64 number,
WireType kind
) = decode_key(start, buffer);
require(start < buffer.length, "start length overflow");
require(success, "key decoding");
Value memory value;
value.position = position;
value.number = number;
value.kind = kind;
if (kind == WireType.Varint) {
(
bool itemSuccess,
uint64 itemPosition,
uint64 itemValue
) = decode_varint(position, buffer);
require(itemSuccess, "varint decoding");
value.positionData = itemPosition;
value.data64 = itemValue;
value.size = itemPosition - position;
value.offset = itemPosition;
}
if (kind == WireType.Bits64) {
(
bool itemSuccess,
uint64 itemPosition,
uint64 itemValue
) = ProtobufLib.decode_uint64(position, buffer);
require(itemSuccess, "bits64 decoding");
value.positionData = itemPosition;
value.data64 = itemValue;
value.size = 8;
value.offset = position + value.size;
}
if (kind == WireType.Bits32) {
(
bool itemSuccess,
uint64 itemPosition,
uint32 itemValue
) = ProtobufLib.decode_uint32(position, buffer);
require(itemSuccess, "bits32 decoding");
value.positionData = itemPosition;
value.data32 = itemValue;
value.size = 4;
value.offset = position + value.size;
}
if (kind == WireType.LengthDelimited) {
(
bool itemSuccess,
uint64 itemPosition,
uint64 itemSize
) = ProtobufLib.decode_length_delimited(position, buffer);
require(itemSuccess, "length delimited decoding");
value.dataBytes = BytesLib.slice(buffer, itemPosition, itemSize);
value.positionData = itemPosition;
value.size = itemSize;
value.offset = itemPosition + value.size;
}
return value;
}
/// @notice Decode key.
/// @dev https://developers.google.com/protocol-buffers/docs/encoding#structure
/// @param p Position
/// @param buf Buffer
/// @return Success
/// @return New position
/// @return Field number
/// @return Wire type
function decode_key(
uint64 p,
bytes memory buf
) internal pure returns (bool, uint64, uint64, WireType) {
// The key is a varint with encoding
// (field_number << 3) | wire_type
(bool success, uint64 pos, uint64 key) = decode_varint(p, buf);
if (!success) {
return (false, pos, 0, WireType.WIRE_TYPE_MAX);
}
uint64 field_number = key >> 3;
uint64 wire_type_val = key & 0x07;
// Check that wire type is bounded
if (wire_type_val >= uint64(WireType.WIRE_TYPE_MAX)) {
return (false, pos, 0, WireType.WIRE_TYPE_MAX);
}
WireType wire_type = WireType(wire_type_val);
// Start and end group types are deprecated, so forbid them
if (
wire_type == WireType.StartGroup || wire_type == WireType.EndGroup
) {
return (false, pos, 0, WireType.WIRE_TYPE_MAX);
}
return (true, pos, field_number, wire_type);
}
/// @notice Decode varint.
/// @dev https://developers.google.com/protocol-buffers/docs/encoding#varints
/// @param p Position
/// @param buf Buffer
/// @return Success
/// @return New position
/// @return Decoded int
function decode_varint(
uint64 p,
bytes memory buf
) internal pure returns (bool, uint64, uint64) {
uint64 val;
uint64 i;
for (i = 0; i < MAX_VARINT_BYTES; i++) {
// Check that index is within bounds
if (i + p >= buf.length) {
return (false, p, 0);
}
// Get byte at offset
uint8 b = uint8(buf[p + i]);
// Highest bit is used to indicate if there are more bytes to come
// Mask to get 7-bit value: 0111 1111
uint8 v = b & 0x7F;
// Groups of 7 bits are ordered least significant first
val |= uint64(v) << uint64(i * 7);
// Mask to get keep going bit: 1000 0000
if (b & 0x80 == 0) {
// [STRICT]
// Check for trailing zeroes if more than one byte is used
// (the value 0 still uses one byte)
if (i > 0 && v == 0) {
return (false, p, 0);
}
break;
}
}
// Check that at most MAX_VARINT_BYTES are used
if (i >= MAX_VARINT_BYTES) {
return (false, p, 0);
}
// [STRICT]
// If all 10 bytes are used, the last byte (most significant 7 bits)
// must be at most 0000 0001, since 7*9 = 63
if (i == MAX_VARINT_BYTES - 1) {
if (uint8(buf[p + i]) > 1) {
return (false, p, 0);
}
}
return (true, p + i + 1, val);
}
/// @notice Decode varint int32.
/// @param p Position
/// @param buf Buffer
/// @return Success
/// @return New position
/// @return Decoded int
function decode_int32(
uint64 p,
bytes memory buf
) internal pure returns (bool, uint64, int32) {
(bool success, uint64 pos, uint64 val) = decode_varint(p, buf);
if (!success) {
return (false, pos, 0);
}
// [STRICT]
// Highest 4 bytes must be 0 if positive
if (val >> 63 == 0) {
if (val & 0xFFFFFFFF00000000 != 0) {
return (false, pos, 0);
}
}
return (true, pos, int32(uint32(val)));
}
/// @notice Decode varint int64.
/// @param p Position
/// @param buf Buffer
/// @return Success
/// @return New position
/// @return Decoded int
function decode_int64(
uint64 p,
bytes memory buf
) internal pure returns (bool, uint64, int64) {
(bool success, uint64 pos, uint64 val) = decode_varint(p, buf);
if (!success) {
return (false, pos, 0);
}
return (true, pos, int64(val));
}
/// @notice Decode varint uint32.
/// @param p Position
/// @param buf Buffer
/// @return Success
/// @return New position
/// @return Decoded int
function decode_uint32(
uint64 p,
bytes memory buf
) internal pure returns (bool, uint64, uint32) {
(bool success, uint64 pos, uint64 val) = decode_varint(p, buf);
if (!success) {
return (false, pos, 0);
}
// [STRICT]
// Highest 4 bytes must be 0
if (val & 0xFFFFFFFF00000000 != 0) {
return (false, pos, 0);
}
return (true, pos, uint32(val));
}
/// @notice Decode varint uint64.
/// @param p Position
/// @param buf Buffer
/// @return Success
/// @return New position
/// @return Decoded int
function decode_uint64(
uint64 p,
bytes memory buf
) internal pure returns (bool, uint64, uint64) {
(bool success, uint64 pos, uint64 val) = decode_varint(p, buf);
if (!success) {
return (false, pos, 0);
}
return (true, pos, val);
}
/// @notice Decode varint sint32.
/// @param p Position
/// @param buf Buffer
/// @return Success
/// @return New position
/// @return Decoded int
function decode_sint32(
uint64 p,
bytes memory buf
) internal pure returns (bool, uint64, int32) {
(bool success, uint64 pos, uint64 val) = decode_varint(p, buf);
if (!success) {
return (false, pos, 0);
}
// [STRICT]
// Highest 4 bytes must be 0
if (val & 0xFFFFFFFF00000000 != 0) {
return (false, pos, 0);
}
// https://stackoverflow.com/questions/2210923/zig-zag-decoding/2211086#2211086
uint64 zigzag_val;
unchecked {
zigzag_val = (val >> 1) - (~(val & 1) + 1);
}
return (true, pos, int32(uint32(zigzag_val)));
}
/// @notice Decode varint sint64.
/// @param p Position
/// @param buf Buffer
/// @return Success
/// @return New position
/// @return Decoded int
function decode_sint64(
uint64 p,
bytes memory buf
) internal pure returns (bool, uint64, int64) {
(bool success, uint64 pos, uint64 val) = decode_varint(p, buf);
if (!success) {
return (false, pos, 0);
}
// https://stackoverflow.com/questions/2210923/zig-zag-decoding/2211086#2211086
uint64 zigzag_val;
unchecked {
zigzag_val = (val >> 1) - (~(val & 1) + 1);
}
return (true, pos, int64(zigzag_val));
}
/// @notice Decode Boolean.
/// @param p Position
/// @param buf Buffer
/// @return Success
/// @return New position
/// @return Decoded bool
function decode_bool(
uint64 p,
bytes memory buf
) internal pure returns (bool, uint64, bool) {
(bool success, uint64 pos, uint64 val) = decode_varint(p, buf);
if (!success) {
return (false, pos, false);
}
// [STRICT]
// Value must be 0 or 1
if (val > 1) {
return (false, pos, false);
}
if (val == 0) {
return (true, pos, false);
}
return (true, pos, true);
}
/// @notice Decode enumeration.
/// @param p Position
/// @param buf Buffer
/// @return Success
/// @return New position
/// @return Decoded enum as raw int
function decode_enum(
uint64 p,
bytes memory buf
) internal pure returns (bool, uint64, int32) {
return decode_int32(p, buf);
}
/// @notice Decode fixed 64-bit int.
/// @param p Position
/// @param buf Buffer
/// @return Success
/// @return New position
/// @return Decoded int
function decode_bits64(
uint64 p,
bytes memory buf
) internal pure returns (bool, uint64, uint64) {
uint64 val;
// Check that index is within bounds
if (8 + p > buf.length) {
return (false, p, 0);
}
for (uint64 i = 0; i < 8; i++) {
uint8 b = uint8(buf[p + i]);
// Little endian
val |= uint64(b) << uint64(i * 8);
}
return (true, p + 8, val);
}
/// @notice Decode fixed uint64.
/// @param p Position
/// @param buf Buffer
/// @return Success
/// @return New position
/// @return Decoded int
function decode_fixed64(
uint64 p,
bytes memory buf
) internal pure returns (bool, uint64, uint64) {
(bool success, uint64 pos, uint64 val) = decode_bits64(p, buf);
if (!success) {
return (false, pos, 0);
}
return (true, pos, val);
}
/// @notice Decode fixed int64.
/// @param p Position
/// @param buf Buffer
/// @return Success
/// @return New position
/// @return Decoded int
function decode_sfixed64(
uint64 p,
bytes memory buf
) internal pure returns (bool, uint64, int64) {
(bool success, uint64 pos, uint64 val) = decode_bits64(p, buf);
if (!success) {
return (false, pos, 0);
}
return (true, pos, int64(val));
}
/// @notice Decode fixed 32-bit int.
/// @param p Position
/// @param buf Buffer
/// @return Success
/// @return New position
/// @return Decoded int
function decode_bits32(
uint64 p,
bytes memory buf
) internal pure returns (bool, uint64, uint32) {
uint32 val;
// Check that index is within bounds
if (4 + p > buf.length) {
return (false, p, 0);
}
for (uint64 i = 0; i < 4; i++) {
uint8 b = uint8(buf[p + i]);
// Little endian
val |= uint32(b) << uint32(i * 8);
}
return (true, p + 4, val);
}
/// @notice Decode fixed uint32.
/// @param p Position
/// @param buf Buffer
/// @return Success
/// @return New position
/// @return Decoded int
function decode_fixed32(
uint64 p,
bytes memory buf
) internal pure returns (bool, uint64, uint32) {
(bool success, uint64 pos, uint32 val) = decode_bits32(p, buf);
if (!success) {
return (false, pos, 0);
}
return (true, pos, val);
}
/// @notice Decode fixed int32.
/// @param p Position
/// @param buf Buffer
/// @return Success
/// @return New position
/// @return Decoded int
function decode_sfixed32(
uint64 p,
bytes memory buf
) internal pure returns (bool, uint64, int32) {
(bool success, uint64 pos, uint32 val) = decode_bits32(p, buf);
if (!success) {
return (false, pos, 0);
}
return (true, pos, int32(val));
}
/// @notice Decode length-delimited field.
/// @param p Position
/// @param buf Buffer
/// @return Success
/// @return New position (after size)
/// @return Size in bytes
function decode_length_delimited(
uint64 p,
bytes memory buf
) internal pure returns (bool, uint64, uint64) {
// Length-delimited fields begin with a varint of the number of bytes that follow
(bool success, uint64 pos, uint64 size) = decode_varint(p, buf);
if (!success) {
return (false, pos, 0);
}
// Check for overflow
unchecked {
if (pos + size < pos) {
return (false, pos, 0);
}
}
// Check that index is within bounds
if (size + pos > buf.length) {
return (false, pos, 0);
}
return (true, pos, size);
}
/// @notice Decode string with bytes slice.
/// @param buf Buffer
/// @return StringData
function decode_string(
bytes memory buf
) internal pure returns (string memory) {
bytes memory field = new bytes(buf.length);
for (uint64 i = 0; i < buf.length; i++) {
field[i] = buf[i];
}
return string(field);
}
/// @notice Decode string.
/// @param p Position
/// @param buf Buffer
/// @return Success
/// @return New position
/// @return Size in bytes
function decode_string(
uint64 p,
bytes memory buf
) internal pure returns (bool, uint64, string memory) {
(bool success, uint64 pos, uint64 size) = decode_length_delimited(
p,
buf
);
if (!success) {
return (false, pos, "");
}
bytes memory field = new bytes(size);
for (uint64 i = 0; i < size; i++) {
field[i] = buf[pos + i];
}
return (true, pos + size, string(field));
}
/// @notice Decode bytes array.
/// @param p Position
/// @param buf Buffer
/// @return Success
/// @return New position (after size)
/// @return Size in bytes
function decode_bytes(
uint64 p,
bytes memory buf
) internal pure returns (bool, uint64, uint64) {
return decode_length_delimited(p, buf);
}
/// @notice Decode embedded message.
/// @param p Position
/// @param buf Buffer
/// @return Success
/// @return New position (after size)
/// @return Size in bytes
function decode_embedded_message(
uint64 p,
bytes memory buf
) internal pure returns (bool, uint64, uint64) {
return decode_length_delimited(p, buf);
}
/// @notice Decode packed repeated field.
/// @param p Position
/// @param buf Buffer
/// @return Success
/// @return New position (after size)
/// @return Size in bytes
function decode_packed_repeated(
uint64 p,
bytes memory buf
) internal pure returns (bool, uint64, uint64) {
return decode_length_delimited(p, buf);
}
////////////////////////////////////
// Encoding
////////////////////////////////////
/// @notice Encode key.
/// @dev https://developers.google.com/protocol-buffers/docs/encoding#structure
/// @param field_number Field number
/// @param wire_type Wire type
/// @return Marshaled bytes
function encode_key(
uint64 field_number,
uint64 wire_type
) internal pure returns (bytes memory) {
uint64 key = (field_number << 3) | wire_type;
bytes memory buf = encode_varint(key);
return buf;
}
/// @notice Encode varint.
/// @dev https://developers.google.com/protocol-buffers/docs/encoding#varints
/// @param n Number
/// @return Marshaled bytes
function encode_varint(uint64 n) internal pure returns (bytes memory) {
// Count the number of groups of 7 bits
// We need this pre-processing step since Solidity doesn't allow dynamic memory resizing
uint64 tmp = n;
uint64 num_bytes = 1;
while (tmp > 0x7F) {
tmp = tmp >> 7;
num_bytes += 1;
}
bytes memory buf = new bytes(num_bytes);
tmp = n;
for (uint64 i = 0; i < num_bytes; i++) {
// Set the first bit in the byte for each group of 7 bits
buf[i] = bytes1(0x80 | uint8(tmp & 0x7F));
tmp = tmp >> 7;
}
// Unset the first bit of the last byte
buf[num_bytes - 1] &= 0x7F;
return buf;
}
/// @notice Encode varint int32.
/// @param n Number
/// @return Marshaled bytes
function encode_int32(int32 n) internal pure returns (bytes memory) {
return encode_varint(uint64(int64(n)));
}
/// @notice Decode varint int64.
/// @param n Number
/// @return Marshaled bytes
function encode_int64(int64 n) internal pure returns (bytes memory) {
return encode_varint(uint64(n));
}
/// @notice Encode varint uint32.
/// @param n Number
/// @return Marshaled bytes
function encode_uint32(uint32 n) internal pure returns (bytes memory) {
return encode_varint(n);
}
/// @notice Encode varint uint64.
/// @param n Number
/// @return Marshaled bytes
function encode_uint64(uint64 n) internal pure returns (bytes memory) {
return encode_varint(n);
}
/// @notice Encode varint sint32.
/// @param n Number
/// @return Marshaled bytes
function encode_sint32(int32 n) internal pure returns (bytes memory) {
// https://developers.google.com/protocol-buffers/docs/encoding#signed_integers
uint32 mask = 0;
if (n < 0) {
unchecked {
mask -= 1;
}
}
uint32 zigzag_val = (uint32(n) << 1) ^ mask;
return encode_varint(zigzag_val);
}
/// @notice Encode varint sint64.
/// @param n Number
/// @return Marshaled bytes
function encode_sint64(int64 n) internal pure returns (bytes memory) {
// https://developers.google.com/protocol-buffers/docs/encoding#signed_integers
uint64 mask = 0;
if (n < 0) {
unchecked {
mask -= 1;
}
}
uint64 zigzag_val = (uint64(n) << 1) ^ mask;
return encode_varint(zigzag_val);
}
/// @notice Encode Boolean.
/// @param b Boolean
/// @return Marshaled bytes
function encode_bool(bool b) internal pure returns (bytes memory) {
uint64 n = b ? 1 : 0;
return encode_varint(n);
}
/// @notice Encode enumeration.
/// @param n Number
/// @return Marshaled bytes
function encode_enum(int32 n) internal pure returns (bytes memory) {
return encode_int32(n);
}
/// @notice Encode fixed 64-bit int.
/// @param n Number
/// @return Marshaled bytes
function encode_bits64(uint64 n) internal pure returns (bytes memory) {
bytes memory buf = new bytes(8);
uint64 tmp = n;
for (uint64 i = 0; i < 8; i++) {
// Little endian
buf[i] = bytes1(uint8(tmp & 0xFF));
tmp = tmp >> 8;
}
return buf;
}
/// @notice Encode fixed uint64.
/// @param n Number
/// @return Marshaled bytes
function encode_fixed64(uint64 n) internal pure returns (bytes memory) {
return encode_bits64(n);
}
/// @notice Encode fixed int64.
/// @param n Number
/// @return Marshaled bytes
function encode_sfixed64(int64 n) internal pure returns (bytes memory) {
return encode_bits64(uint64(n));
}
/// @notice Decode fixed 32-bit int.
/// @param n Number
/// @return Marshaled bytes
function encode_bits32(uint32 n) internal pure returns (bytes memory) {
bytes memory buf = new bytes(4);
uint64 tmp = n;
for (uint64 i = 0; i < 4; i++) {
// Little endian
buf[i] = bytes1(uint8(tmp & 0xFF));
tmp = tmp >> 8;
}
return buf;
}
/// @notice Encode fixed uint32.
/// @param n Number
/// @return Marshaled bytes
function encode_fixed32(uint32 n) internal pure returns (bytes memory) {
return encode_bits32(n);
}
/// @notice Encode fixed int32.
/// @param n Number
/// @return Marshaled bytes
function encode_sfixed32(int32 n) internal pure returns (bytes memory) {
return encode_bits32(uint32(n));
}
/// @notice Encode length-delimited field.
/// @param b Bytes
/// @return Marshaled bytes
function encode_length_delimited(
bytes memory b
) internal pure returns (bytes memory) {
// Length-delimited fields begin with a varint of the number of bytes that follow
bytes memory length_buf = encode_uint64(uint64(b.length));
bytes memory buf = new bytes(b.length + length_buf.length);
for (uint64 i = 0; i < length_buf.length; i++) {
buf[i] = length_buf[i];
}
for (uint64 i = 0; i < b.length; i++) {
buf[i + length_buf.length] = b[i];
}
return buf;
}
/// @notice Encode string.
/// @param s String
/// @return Marshaled bytes
function encode_string(
string memory s
) internal pure returns (bytes memory) {
return encode_length_delimited(bytes(s));
}
/// @notice Encode bytes array.
/// @param b Bytes
/// @return Marshaled bytes
function encode_bytes(bytes memory b) internal pure returns (bytes memory) {
return encode_length_delimited(b);
}
/// @notice Encode embedded message.
/// @param m Message
/// @return Marshaled bytes
function encode_embedded_message(
bytes memory m
) internal pure returns (bytes memory) {
return encode_length_delimited(m);
}
/// @notice Encode packed repeated field.
/// @param b Bytes
/// @return Marshaled bytes
function encode_packed_repeated(
bytes memory b
) internal pure returns (bytes memory) {
return encode_length_delimited(b);
}
}
StringToAddress.sol 42 lines
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.22;
library StringToAddress {
error Missing0xPrefix();
error InvalidStringLength();
error InvalidCharacter(uint8 char);
uint256 constant ADDRESS_STR_LENGTH = 42;
function stringToAddress(bytes memory str) internal pure returns (address) {
uint160 _address;
if (str.length != ADDRESS_STR_LENGTH) revert InvalidStringLength();
if (uint8(str[0]) != 48) revert Missing0xPrefix(); // Check presence of '0'
if (uint8(str[1]) != 120) revert Missing0xPrefix(); // Check presence of 'x'
unchecked {
for (uint256 i = 2; i < ADDRESS_STR_LENGTH; i += 2) {
uint160 b = ((parseAsciiChar(uint8(str[i])) << 4) +
((parseAsciiChar(uint8(str[i + 1])))));
_address += b << (uint160(152) - uint160(i - 2) * 4);
}
}
return address(_address);
}
/// @notice does not parse upper-case characters
function parseAsciiChar(uint8 char) internal pure returns (uint8) {
unchecked {
if (char >= 48 && char <= 57) {
return char - 48;
} else if (char >= 97 && char <= 102) {
return char - 87;
} else {
revert InvalidCharacter(char);
}
}
}
}
StringToInt.sol 30 lines
// SPDX-License-Identifier: Apache 2.0
pragma solidity ^0.8.22;
library StringToInt {
error InvalidIntString(string s);
function stringToInt(string memory s) internal pure returns (int256) {
bytes memory b = bytes(s);
uint256 i;
uint256 number = 0;
bool isNegative = false;
for (i = 0; i < b.length; i++) {
uint256 c = uint256(uint8(b[i]));
if (c >= 48 && c <= 57) {
number = number * 10 + (c - 48);
} else if (i == 0 && c == 45) {
isNegative = true;
} else revert InvalidIntString(s);
}
if (number > type(uint128).max) {}
if (isNegative) {
return -int256(number);
} else {
return int256(number);
}
}
}
StringToUint.sol 18 lines
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.22;
library StringToUint {
error InvalidUintString(string s);
function stringToUint(string memory s) internal pure returns (uint result) {
bytes memory b = bytes(s);
uint256 i;
result = 0;
for (i = 0; i < b.length; i++) {
uint256 c = uint256(uint8(b[i]));
if (c >= 48 && c <= 57) {
result = result * 10 + (c - 48);
} else revert InvalidUintString(s);
}
}
}
SupplyDeltaLib.sol 37 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.4 <0.9.0;
import { BytesLib } from "./BytesLib.sol";
import { ProtobufLib } from "./ProtobufLib.sol";
struct SupplyDeltaData {
string nonce;
string delta;
}
library SupplyDeltaLib {
function decode(
uint64 start,
bytes memory buffer
) internal pure returns (SupplyDeltaData memory) {
SupplyDeltaData memory data;
ProtobufLib.Value[] memory values = ProtobufLib.decodeFields(
start,
buffer
);
for (uint i = 0; i < values.length; i++) {
// nonce
if (values[i].number == 1) {
data.nonce = ProtobufLib.decode_string(values[i].dataBytes);
}
// delta
if (values[i].number == 2) {
data.delta = ProtobufLib.decode_string(values[i].dataBytes);
}
}
return data;
}
}
WithdrawalLib.sol 71 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.4 <0.9.0;
import { BytesLib } from "./BytesLib.sol";
import { ProtobufLib } from "./ProtobufLib.sol";
library WithdrawalLib {
struct Coin {
string denom;
string amount;
}
struct Data {
string nonce;
string from;
string to;
Coin amount;
}
function decode(
uint64 start,
bytes memory buffer
) internal pure returns (Data memory) {
Data memory data;
ProtobufLib.Value[] memory values = ProtobufLib.decodeFields(
start,
buffer
);
for (uint i = 0; i < values.length; i++) {
// nonce
if (values[i].number == 1) {
data.nonce = ProtobufLib.decode_string(values[i].dataBytes);
}
// from
if (values[i].number == 2) {
data.from = ProtobufLib.decode_string(values[i].dataBytes);
}
// to
if (values[i].number == 3) {
data.to = ProtobufLib.decode_string(values[i].dataBytes);
}
// coin
if (values[i].number == 4) {
ProtobufLib.Value[] memory coin = ProtobufLib.decodeFields(
0,
values[i].dataBytes
);
for (uint c = 0; c < coin.length; c++) {
if (coin[c].number == 1) {
data.amount.denom = ProtobufLib.decode_string(
coin[c].dataBytes
);
}
if (coin[c].number == 2) {
data.amount.amount = ProtobufLib.decode_string(
coin[c].dataBytes
);
}
}
}
}
return data;
}
}
IFuelStreamX.sol 67 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
import "../vendor/blobstream/DataRootTuple.sol";
import "../vendor/blobstream/lib/tree/binary/BinaryMerkleTree.sol";
interface IFuelStreamX {
/// @notice Emits event with the new head update.
event HeadUpdate(uint64 blockNumber, bytes32 headerHash);
/// @notice Trusted header not found.
error TrustedHeaderNotFound();
/// @notice Latest header not found.
error LatestHeaderNotFound();
/// @notice Target block for proof must be greater than latest block and less than the
/// latest block plus the maximum number of skipped blocks.
error TargetBlockNotInRange();
error InvalidCommitmentProof();
error InvalidTxResultProof();
error InvalidExecTxResultCode();
error NonceAlreadyUsed();
error InvalidToken();
error CommitmentNotFinalized();
error InvalidProofNonce();
/// @notice Bridge commitment stored for the block range [startBlock, endBlock) with proof nonce.
/// @param proofNonce The nonce of the proof.
/// @param startBlock The start block of the block range.
/// @param endBlock The end block of the block range.
/// @param bridgeCommitment The bridge commitment for the block range.
event BridgeCommitmentStored(
uint256 proofNonce,
uint64 indexed startBlock,
uint64 indexed endBlock,
bytes32 indexed bridgeCommitment
);
/// @notice Emits event with the inputs of a next header request.
/// @param trustedBlock The trusted block for the next header request.
/// @param trustedHeader The header hash of the trusted block.
event NextHeaderRequested(
uint64 indexed trustedBlock,
bytes32 indexed trustedHeader
);
/// @notice Emits event with the inputs of a header range request.
/// @param trustedBlock The trusted block for the header range request.
/// @param trustedHeader The header hash of the trusted block.
/// @param targetBlock The target block of the header range request.
event HeaderRangeRequested(
uint64 indexed trustedBlock,
bytes32 indexed trustedHeader,
uint64 indexed targetBlock
);
/// @notice Bridge commitment for specified block range does not exist.
error BridgeCommitmentNotFound();
/// @notice Emitted with headerRangeFunctionId is updated.
/// @param newHeaderRangeFunctionId The new header range function id
event HeaderRangeFunctionIdUpdated(
bytes32 indexed newHeaderRangeFunctionId
);
}
IRecoveryRegistry.sol 9 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IRecoveryRegistry {
function setRecoveryAddress(address compromised, address recovery) external;
function getRecoveryAddress(
address account
) external view returns (address);
}
ISequencerMessageHandler.sol 40 lines
// SPDX-License-Identifier: Apache 2.0
pragma solidity >=0.5.0 <0.9.0;
import "../vendor/blobstream/lib/tree/binary/BinaryMerkleTree.sol";
struct BridgeCommitmentLeaf {
uint256 height;
bytes32 resultsHash;
}
interface ISequencerMessageHandler {
event WithdrawalProcessed(
uint256 indexed nonce,
address indexed recipient,
uint256 amount
);
event SupplyDelta(uint256 indexed nonce, int256 delta);
/**
* @dev Validates proof and processes token withdrawal.
*/
function processSequencerWithdrawalMessage(
uint256 _proofNonce,
BridgeCommitmentLeaf memory bridgeCommitmentLeaf,
BinaryMerkleProof memory bridgeCommitmentLeafProof,
bytes memory txResultMarshalled,
BinaryMerkleProof memory txResultProof
) external;
/**
* @dev Validates proof and processes a supply update message.
*/
function processSequencerSupplyUpdate(
uint256 _proofNonce,
BridgeCommitmentLeaf memory bridgeCommitmentLeaf,
BinaryMerkleProof memory bridgeCommitmentLeafProof,
bytes memory txResultMarshalled,
BinaryMerkleProof memory txResultProof
) external;
}
IVault.sol 43 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0 <0.9.0;
interface IVault {
/// @notice Emitted when tokens are released from the vault
/// @param recipient The address receiving the released tokens
/// @param amount The amount of tokens released
event Released(address indexed recipient, uint256 amount);
/// @notice Emitted when the balances are adjusted
/// @param totalDeposits The updated total deposits value
/// @param surplus The updated surplus value
event BalancesAdjusted(uint256 totalDeposits, int256 surplus);
/// @notice Release tokens from the vault to a recipient
/// @dev This function can only be called by an authorized agent
/// @param recipient The address to receive the tokens
/// @param amount The amount of tokens to release
/// @dev Reverts if the surplus is negative
function release(address recipient, uint256 amount) external;
/// @notice Synchronize the stored balances with the actual token balance
/// @dev This function is called to adjust the stored balances based on the actual token balance
function sync() external returns (uint256 newDeposits);
/// @notice Increases the surplus and decreases the totalDeposits by a given value
/// @dev This function can only be called by an authorized agent
/// @param adjustment The amount by which to adjust the surplus & deposits values
function adjustSurplus(int256 adjustment) external;
/// @notice Increase the surplus value
/// @dev This function can only be called by an authorized agent
/// @param amount The amount by which to increase the surplus value
function increaseSurplus(uint256 amount) external;
/// @notice Get the balances stored in the vault
/// @return totalDeposits The total deposits owned by users on L2
/// @return surplus The surplus allocated towards rewards
function balances()
external
view
returns (uint256 totalDeposits, int256 surplus);
}
Pausable.sol 17 lines
// SPDX-License-Identifier: Apache 2.0
pragma solidity ^0.8.20;
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
abstract contract Pausable is AccessControlUpgradeable, PausableUpgradeable {
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
function pause() external onlyRole(PAUSER_ROLE) {
_pause();
}
function unpause() external onlyRole(DEFAULT_ADMIN_ROLE) {
_unpause();
}
}
DataRootTuple.sol 15 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.22;
/// @notice A tuple of data root with metadata. Each data root is associated
/// with a Celestia block height.
/// @dev `availableDataRoot` in
/// https://github.com/celestiaorg/celestia-specs/blob/master/src/specs/data_structures.md#header
struct DataRootTuple {
// Celestia block height the data root was included in.
// Genesis block is height = 0.
// First queryable block is height = 1.
uint256 height;
// Data root.
bytes32 dataRoot;
}
Constants.sol 27 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.22;
import "./Types.sol";
library Constants {
///////////////
// Constants //
///////////////
/// @dev Maximum tree height
uint256 internal constant MAX_HEIGHT = 256;
/// @dev The prefixes of leaves and nodes
bytes1 internal constant LEAF_PREFIX = 0x00;
bytes1 internal constant NODE_PREFIX = 0x01;
}
/// @dev Parity share namespace.
/// utility function to provide the parity share namespace as a Namespace struct.
function PARITY_SHARE_NAMESPACE() pure returns (Namespace memory) {
return
Namespace(
0xFF,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
);
}
Types.sol 40 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.22;
/// @notice A representation of the Celestia-app namespace ID and its version.
/// See: https://celestiaorg.github.io/celestia-app/specs/namespace.html
struct Namespace {
// The namespace version.
bytes1 version;
// The namespace ID.
bytes28 id;
}
using { equalTo, lessThan, greaterThan, toBytes } for Namespace global;
function equalTo(Namespace memory l, Namespace memory r) pure returns (bool) {
return l.toBytes() == r.toBytes();
}
function lessThan(Namespace memory l, Namespace memory r) pure returns (bool) {
return l.toBytes() < r.toBytes();
}
function greaterThan(
Namespace memory l,
Namespace memory r
) pure returns (bool) {
return l.toBytes() > r.toBytes();
}
function toBytes(Namespace memory n) pure returns (bytes29) {
return bytes29(abi.encodePacked(n.version, n.id));
}
function toNamespace(bytes29 n) pure returns (Namespace memory) {
bytes memory id = new bytes(28);
for (uint256 i = 1; i < 29; i++) {
id[i - 1] = n[i];
}
return Namespace(n[0], bytes28(id));
}
Utils.sol 86 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.22;
import "./Constants.sol";
/// @notice Calculate the starting bit of the path to a leaf
/// @param numLeaves : The total number of leaves in the tree
/// @return startingBit : The starting bit of the path
// solhint-disable-next-line func-visibility
function getStartingBit(uint256 numLeaves) pure returns (uint256 startingBit) {
// Determine height of the left subtree. This is the maximum path length, so all paths start at this offset from the right-most bit
startingBit = 0;
while ((1 << startingBit) < numLeaves) {
startingBit += 1;
}
return Constants.MAX_HEIGHT - startingBit;
}
/// @notice Calculate the length of the path to a leaf
/// @param key: The key of the leaf
/// @param numLeaves: The total number of leaves in the tree
/// @return pathLength : The length of the path to the leaf
// solhint-disable-next-line func-visibility
function pathLengthFromKey(
uint256 key,
uint256 numLeaves
) pure returns (uint256 pathLength) {
if (numLeaves <= 1) {
// if the number of leaves of the tree is 1 or 0, the path always is 0.
return 0;
}
// Get the height of the left subtree. This is equal to the offset of the starting bit of the path
pathLength = Constants.MAX_HEIGHT - getStartingBit(numLeaves);
// Determine the number of leaves in the left subtree
uint256 numLeavesLeftSubTree = (1 << (pathLength - 1));
// If leaf is in left subtree, path length is full height of left subtree
if (key <= numLeavesLeftSubTree - 1) {
return pathLength;
}
// If left sub tree has only one leaf but key is not there, path has one additional step
else if (numLeavesLeftSubTree == 1) {
return 1;
}
// Otherwise, add 1 to height and recurse into right subtree
else {
return
1 +
pathLengthFromKey(
key - numLeavesLeftSubTree,
numLeaves - numLeavesLeftSubTree
);
}
}
/// @notice Returns the minimum number of bits required to represent `x`; the
/// result is 0 for `x` == 0.
/// @param x Number.
function _bitsLen(uint256 x) pure returns (uint256) {
uint256 count = 0;
while (x != 0) {
count++;
x >>= 1;
}
return count;
}
/// @notice Returns the largest power of 2 less than `x`.
/// @param x Number.
function _getSplitPoint(uint256 x) pure returns (uint256) {
// Note: since `x` is always an unsigned int * 2, the only way for this
// to be violated is if the input == 0. Since the input is the end
// index exclusive, an input of 0 is guaranteed to be invalid (it would
// be a proof of inclusion of nothing, which is vacuous).
require(x >= 1);
uint256 bitLen = _bitsLen(x);
uint256 k = 1 << (bitLen - 1);
if (k == x) {
k >>= 1;
}
return k;
}
BinaryMerkleProof.sol 12 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.22;
/// @notice Merkle Tree Proof structure.
struct BinaryMerkleProof {
// List of side nodes to verify and calculate tree.
bytes32[] sideNodes;
// The key of the leaf to verify.
uint256 key;
// The number of leaves in the tree
uint256 numLeaves;
}
BinaryMerkleTree.sol 172 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.22;
import "../Constants.sol";
import "../Utils.sol";
import "./TreeHasher.sol";
import "./BinaryMerkleProof.sol";
/// @title Binary Merkle Tree.
library BinaryMerkleTree {
/////////////////
// Error codes //
/////////////////
enum ErrorCodes {
NoError,
/// @notice The provided side nodes count is invalid for the proof.
InvalidNumberOfSideNodes,
/// @notice The provided proof key is not part of the tree.
KeyNotInTree,
/// @notice Invalid number of leaves in proof.
InvalidNumberOfLeavesInProof,
/// @notice The proof contains unexpected side nodes.
UnexpectedInnerHashes,
/// @notice The proof verification expected at least one inner hash.
ExpectedAtLeastOneInnerHash
}
///////////////
// Functions //
///////////////
/// @notice Verify if element exists in Merkle tree, given data, proof, and root.
/// @param root The root of the tree in which verify the given leaf.
/// @param proof Binary Merkle proof for the leaf.
/// @param data The data of the leaf to verify.
/// @return `true` is proof is valid, `false` otherwise.
/// @dev proof.numLeaves is necessary to determine height of subtree containing the data to prove.
function verify(
bytes32 root,
BinaryMerkleProof memory proof,
bytes memory data
) internal pure returns (bool, ErrorCodes) {
// Check proof is correct length for the key it is proving
if (proof.numLeaves <= 1) {
if (proof.sideNodes.length != 0) {
return (false, ErrorCodes.InvalidNumberOfSideNodes);
}
} else if (
proof.sideNodes.length !=
pathLengthFromKey(proof.key, proof.numLeaves)
) {
return (false, ErrorCodes.InvalidNumberOfSideNodes);
}
// Check key is in tree
if (proof.key >= proof.numLeaves) {
return (false, ErrorCodes.KeyNotInTree);
}
// A sibling at height 1 is created by getting the hash of the data to prove.
bytes32 digest = leafDigest(data);
// Null proof is only valid if numLeaves = 1
// If so, just verify hash(data) is root
if (proof.sideNodes.length == 0) {
if (proof.numLeaves == 1) {
return (root == digest, ErrorCodes.NoError);
} else {
return (false, ErrorCodes.NoError);
}
}
(bytes32 computedHash, ErrorCodes error) = computeRootHash(
proof.key,
proof.numLeaves,
digest,
proof.sideNodes
);
if (error != ErrorCodes.NoError) {
return (false, error);
}
return (computedHash == root, ErrorCodes.NoError);
}
/// @notice Use the leafHash and innerHashes to get the root merkle hash.
/// If the length of the innerHashes slice isn't exactly correct, the result is nil.
/// Recursive impl.
function computeRootHash(
uint256 key,
uint256 numLeaves,
bytes32 leafHash,
bytes32[] memory sideNodes
) private pure returns (bytes32, ErrorCodes) {
if (numLeaves == 0) {
return (leafHash, ErrorCodes.InvalidNumberOfLeavesInProof);
}
if (numLeaves == 1) {
if (sideNodes.length != 0) {
return (leafHash, ErrorCodes.UnexpectedInnerHashes);
}
return (leafHash, ErrorCodes.NoError);
}
if (sideNodes.length == 0) {
return (leafHash, ErrorCodes.ExpectedAtLeastOneInnerHash);
}
uint256 numLeft = _getSplitPoint(numLeaves);
bytes32[] memory sideNodesLeft = slice(
sideNodes,
0,
sideNodes.length - 1
);
ErrorCodes error;
if (key < numLeft) {
bytes32 leftHash;
(leftHash, error) = computeRootHash(
key,
numLeft,
leafHash,
sideNodesLeft
);
if (error != ErrorCodes.NoError) {
return (leafHash, error);
}
return (
nodeDigest(leftHash, sideNodes[sideNodes.length - 1]),
ErrorCodes.NoError
);
}
bytes32 rightHash;
(rightHash, error) = computeRootHash(
key - numLeft,
numLeaves - numLeft,
leafHash,
sideNodesLeft
);
if (error != ErrorCodes.NoError) {
return (leafHash, error);
}
return (
nodeDigest(sideNodes[sideNodes.length - 1], rightHash),
ErrorCodes.NoError
);
}
/// @notice creates a slice of bytes32 from the data slice of bytes32 containing the elements
/// that correspond to the provided range.
/// It selects a half-open range which includes the begin element, but excludes the end one.
/// @param _data The slice that we want to select data from.
/// @param _begin The beginning of the range (inclusive).
/// @param _end The ending of the range (exclusive).
/// @return _ the sliced data.
function slice(
bytes32[] memory _data,
uint256 _begin,
uint256 _end
) internal pure returns (bytes32[] memory) {
if (_begin > _end) {
revert("Invalid range: _begin is greater than _end");
}
if (_begin > _data.length || _end > _data.length) {
revert("Invalid range: _begin or _end are out of bounds");
}
bytes32[] memory out = new bytes32[](_end - _begin);
for (uint256 i = _begin; i < _end; i++) {
out[i - _begin] = _data[i];
}
return out;
}
}
TreeHasher.sol 23 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.22;
import "../Constants.sol";
/// @notice Calculate the digest of a node.
/// @param left The left child.
/// @param right The right child.
/// @return digest The node digest.
/// @dev More details in https://github.com/celestiaorg/celestia-specs/blob/master/src/specs/data_structures.md#binary-merkle-tree
// solhint-disable-next-line func-visibility
function nodeDigest(bytes32 left, bytes32 right) pure returns (bytes32 digest) {
digest = sha256(abi.encodePacked(Constants.NODE_PREFIX, left, right));
}
/// @notice Calculate the digest of a leaf.
/// @param data The data of the leaf.
/// @return digest The leaf digest.
/// @dev More details in https://github.com/celestiaorg/celestia-specs/blob/master/src/specs/data_structures.md#binary-merkle-tree
// solhint-disable-next-line func-visibility
function leafDigest(bytes memory data) pure returns (bytes32 digest) {
digest = sha256(abi.encodePacked(Constants.LEAF_PREFIX, data));
}
Read Contract
BRIDGE_COMMITMENT_MAX 0x382f7579 → uint64
DEFAULT_ADMIN_ROLE 0xa217fddf → bytes32
GUARDIAN_ROLE 0x24ea54f4 → bytes32
L2_ACCOUNT_ROLE 0x1d0ec804 → bytes32
L2_ADMIN_ROLE 0xb4e7c8d5 → bytes32
PAUSER_ROLE 0xe63ab1e9 → bytes32
UPGRADE_INTERFACE_VERSION 0xad3cb1cc → string
VERSION 0xffa1ad74 → string
blockHeightToHeaderHash 0x08e93ea5 → bytes32
commitmentTimestamp 0xfb6162b0 → uint32
gateway 0x116191b6 → address
getRoleAdmin 0x248a9ca3 → bytes32
hasRole 0x91d14854 → bool
headerRangeFunctionId 0x7a5f6d7b → bytes32
latestBlock 0x07e2da96 → uint64
nextHeaderFunctionId 0x593bd11e → bytes32
paused 0x5c975abb → bool
proxiableUUID 0x52d1902d → bytes32
recoveryRegistry 0xb3e3f288 → address
state_bridgeCommitments 0xcdecf045 → bytes32
state_proofNonce 0x55ae3f22 → uint256
supportsInterface 0x01ffc9a7 → bool
timeToFinalize 0x4de5383b → uint32
tokenCosmosDenominationHash 0x423160cc → bytes32
usedNonces 0xe4f376f0 → bool
vault 0xfbfa77cf → address
Write Contract 18 functions
These functions modify contract state and require a wallet transaction to execute.
grantL2AccountRole 0x78d67f61
address l2Account
grantRole 0x2f2ff15d
bytes32 role
address account
initialize 0x588570a5
tuple _params
initializeTrustedHeader 0x941909e7
bytes32 header
pause 0x8456cb59
No parameters
processSequencerSupplyUpdate 0xe80c583e
uint256 _proofNonce
tuple bridgeCommitmentLeaf
tuple bridgeCommitmentLeafProof
bytes txResultMarshalled
tuple txResultProof
processSequencerWithdrawalMessage 0xfba26e00
uint256 _proofNonce
tuple bridgeCommitmentLeaf
tuple bridgeCommitmentLeafProof
bytes txResultMarshalled
tuple txResultProof
renounceRole 0x36568abe
bytes32 role
address callerConfirmation
revokeRole 0xd547741f
bytes32 role
address account
setCosmosTokenDenomination 0x5de366c0
string denomination
setRecoveryRegistry 0x1181c180
address newRecoveryRegistry
setRoleAdmin 0x1e4e0091
bytes32 role
bytes32 roleAdmin
setTimeToFinalize 0xbb9db1d3
uint32 newTimeToFinalize
unpause 0x3f4ba83a
No parameters
updateCommitHeaderRange 0x464d77d3
uint64 _targetBlock
bytes32 _targetHeader
bytes32 _bridgeCommitment
updateGenesisState 0x4c4d30fc
uint32 _height
bytes32 _header
updateHeaderRangeFunctionId 0xbae43a17
bytes32 _headerRangeFunctionId
upgradeToAndCall 0x4f1ef286
address newImplementation
bytes data
Recent Transactions
This address has 1 on-chain transactions, but only 0% of the chain is indexed. Transactions will appear as indexing progresses. View on Etherscan →