Address: 0x00000000000084fA81809Dd337311297C5594d62
Balance (XRP): 0 XRP
Bytecode: 0x6080604052600436101561001e575b361561001c5761001c611e05565b005b60003560e01c806223de29146101ad578063025b22bc146101a857806313792a4a146101a3578063150b7a021461019e5780631626ba7e1461019957806319822f7c146101945780631a9b23371461018f5780631f6a1eb91461018a578063257671f51461018557806329561426146101805780632dd310001461017b5780634fcf3eca146101765780636ea44577146101715780638943ec021461016c5780638c3f55631461016757806392dcb3fc146101625780639c145aed1461015d5780639f69ef5414610158578063a65d69d414610153578063aaf10f421461014e578063ad55366b14610149578063b93ea7ad14610144578063bc197c811461013f578063f23a6e611461013a5763f727ef1c0361000e57611632565b6115a5565b6114d3565b611374565b611328565b6112d7565b611268565b6111f9565b61106a565b61100c565b610fd0565b610f4c565b610f1d565b610dc3565b610d54565b610c74565b610c1b565b610aff565b610a9c565b6109e7565b61095f565b6108d2565b6107d5565b6102fb565b61026f565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101d557565b600080fd5b6024359073ffffffffffffffffffffffffffffffffffffffff821682036101d557565b6044359073ffffffffffffffffffffffffffffffffffffffff821682036101d557565b359073ffffffffffffffffffffffffffffffffffffffff821682036101d557565b9181601f840112156101d55782359167ffffffffffffffff83116101d557602083818601950101116101d557565b346101d55760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d5576102a66101b2565b506102af6101da565b506102b86101fd565b5060843567ffffffffffffffff81116101d5576102d9903690600401610241565b505060a43567ffffffffffffffff81116101d55761001c903690600401610241565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d55761032d6101b2565b30330361033d5761001c90611ece565b7fa19dbf00000000000000000000000000000000000000000000000000000000006000523360045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60e0810190811067ffffffffffffffff8211176103b657604052565b61036b565b6040810190811067ffffffffffffffff8211176103b657604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176103b657604052565b6040519061042760e0836103d7565b565b60405190610427610120836103d7565b359060ff821682036101d557565b359081151582036101d557565b67ffffffffffffffff81116103b65760051b60200190565b67ffffffffffffffff81116103b657601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b9291926104b28261046c565b916104c060405193846103d7565b8294818452818301116101d5578281602093846000960137010152565b9080601f830112156101d5578160206104f8933591016104a6565b90565b81601f820112156101d55780359061051282610454565b9261052060405194856103d7565b82845260208085019360051b830101918183116101d55760208101935b83851061054c57505050505090565b843567ffffffffffffffff81116101d557820160e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082860301126101d557610593610418565b916105a060208301610220565b83526040820135602084015260608201359267ffffffffffffffff84116101d55760e0836105d58860208098819801016104dd565b6040840152608081013560608401526105f060a08201610447565b608084015261060160c08201610447565b60a0840152013560c082015281520194019361053d565b9080601f830112156101d557813561062f81610454565b9261063d60405194856103d7565b81845260208085019260051b8201019283116101d557602001905b8282106106655750505090565b6020809161067284610220565b815201910190610658565b9060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126101d55760043567ffffffffffffffff81116101d5576101207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82850301126101d5576106ef610429565b906106fc81600401610439565b825261070a60248201610447565b6020830152604481013567ffffffffffffffff81116101d557846004610732928401016104fb565b6040830152606481013560608301526084810135608083015260a481013567ffffffffffffffff81116101d55784600461076e928401016104dd565b60a083015260c481013560c083015260e481013560e083015261010481013567ffffffffffffffff81116101d557600485916107ab930101610618565b610100820152916024359067ffffffffffffffff82116101d5576107d191600401610241565b9091565b346101d5576107e33661067d565b90916101008101926107fe6107f985515161173a565b61175a565b9160005b85518051821015610865579061085f61083a610820836001956117d8565b5173ffffffffffffffffffffffffffffffffffffffff1690565b61084483886117d8565b9073ffffffffffffffffffffffffffffffffffffffff169052565b01610802565b505083838661087a33610844835151856117d8565b52610886818484611f1d565b50156108985760405160018152602090f35b6108ce906040519384937ff58cc8b500000000000000000000000000000000000000000000000000000000855260048501611a33565b0390fd5b346101d55760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d5576109096101b2565b506109126101da565b5060643567ffffffffffffffff81116101d557610933903690600401610241565b505060206040517f150b7a02000000000000000000000000000000000000000000000000000000008152f35b346101d55760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d55760043560243567ffffffffffffffff81116101d5576020916109b76109bd923690600401610241565b91611a58565b7fffffffff0000000000000000000000000000000000000000000000000000000060405191168152f35b346101d55760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d55760043567ffffffffffffffff81116101d5576101207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82360301126101d557610a6a60209160243560443591600401611b2d565b604051908152f35b7fffffffff000000000000000000000000000000000000000000000000000000008116036101d557565b346101d55760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d5576020610ae1600435610adc81610a72565b612102565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d55760043567ffffffffffffffff81116101d557610b49903690600401610241565b60243567ffffffffffffffff81116101d557610b69903690600401610241565b9160027ffc6e07e3992c7c3694a921dc9e412b6cfe475380556756a19805a9e3ddfe2fde5414610be657610bc09360027ffc6e07e3992c7c3694a921dc9e412b6cfe475380556756a19805a9e3ddfe2fde55611cce565b60017ffc6e07e3992c7c3694a921dc9e412b6cfe475380556756a19805a9e3ddfe2fde55005b7f37ed32e80000000000000000000000000000000000000000000000000000000060005260046000fd5b60009103126101d557565b346101d55760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d55760206040517f573fd524dc86b426cf228cb23f874693e22c686ed86eee5bc6720a772e6606088152f35b346101d55760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d55760043530330361033d578015610d2a576020817f307ed6bd941ee9fc80f369c94af5fa11e25bab5102a6140191756c5474a30bfa927fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf855604051908152a161001c7f0000000000000000000000007438718f9e4b9b834e305a620eeecf2b9e6ebe79611ece565b7f4294d1270000000000000000000000000000000000000000000000000000000060005260046000fd5b346101d55760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d557602060405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000018a77519fccca060c2537c9d6d3f168152f35b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d557600435610df981610a72565b30330361033d5773ffffffffffffffffffffffffffffffffffffffff610e1e82612102565b1615610e845760407fffffffff000000000000000000000000000000000000000000000000000000007f0d7fc113eaf016db4681a1ba86d083ce3e0961f321062a75ac2b0aeb33deeed19216610e75600082612e27565b815190815260006020820152a1005b7fffffffff00000000000000000000000000000000000000000000000000000000907f1c3812cc000000000000000000000000000000000000000000000000000000006000521660045260246000fd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126101d5576004359067ffffffffffffffff82116101d5576107d191600401610241565b610f2636610ed4565b9030330361033d57610f3c61001c925a92612237565b90610f46826128b4565b90612590565b346101d55760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d557610f836101b2565b5060443567ffffffffffffffff81116101d557610fa4903690600401610241565b505060206040517f8943ec02000000000000000000000000000000000000000000000000000000008152f35b346101d55760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d5576020610a6a600435612948565b346101d55760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d557604061104860043561298e565b73ffffffffffffffffffffffffffffffffffffffff8351921682526020820152f35b346101d55761107836610ed4565b9060027ffc6e07e3992c7c3694a921dc9e412b6cfe475380556756a19805a9e3ddfe2fde5414610be65760027ffc6e07e3992c7c3694a921dc9e412b6cfe475380556756a19805a9e3ddfe2fde5573ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001680156111cf5733036111a157303b156101d55761114b9160009160405193849283927f6ea4457700000000000000000000000000000000000000000000000000000000845260048401611dc4565b038183305af1801561119c576111815760017ffc6e07e3992c7c3694a921dc9e412b6cfe475380556756a19805a9e3ddfe2fde55005b806111906000611196936103d7565b80610c10565b38610bc0565b611aa4565b7f1d6ddbf4000000000000000000000000000000000000000000000000000000006000523360045260246000fd5b7fd13d78350000000000000000000000000000000000000000000000000000000060005260046000fd5b346101d55760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d557602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000007438718f9e4b9b834e305a620eeecf2b9e6ebe79168152f35b346101d55760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d557602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101d55760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d5576020305473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b346101d55760c061134460008061133e3661067d565b91612a56565b92611350839293613bde565b906040519586526020860152151560408501526060840152608083015260a0820152f35b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d5576004356113aa81610a72565b6113b26101da565b9030330361033d5773ffffffffffffffffffffffffffffffffffffffff6113d882612102565b16611452577f0d7fc113eaf016db4681a1ba86d083ce3e0961f321062a75ac2b0aeb33deeed19173ffffffffffffffffffffffffffffffffffffffff7fffffffff000000000000000000000000000000000000000000000000000000006040931691166114458183612e27565b82519182526020820152a1005b7fffffffff00000000000000000000000000000000000000000000000000000000907f5b4d6d6a000000000000000000000000000000000000000000000000000000006000521660045260246000fd5b9181601f840112156101d55782359167ffffffffffffffff83116101d5576020808501948460051b0101116101d557565b346101d55760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d55761150a6101b2565b506115136101da565b5060443567ffffffffffffffff81116101d5576115349036906004016114a2565b505060643567ffffffffffffffff81116101d5576115569036906004016114a2565b505060843567ffffffffffffffff81116101d557611578903690600401610241565b50506040517fbc197c81000000000000000000000000000000000000000000000000000000008152602090f35b346101d55760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d5576115dc6101b2565b506115e56101da565b5060843567ffffffffffffffff81116101d557611606903690600401610241565b505060206040517ff23a6e61000000000000000000000000000000000000000000000000000000008152f35b346101d55760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d55760043561166c6101da565b604435916bffffffffffffffffffffffff83168093036101d55730330361033d578273ffffffffffffffffffffffffffffffffffffffff836116f77febf265acfac1c01de588ed7ef49743b9c3ce8d6d1edeaf510a1f5453228515b1967fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606097881b161785612da3565b6040519384521660208301526040820152a1005b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b906001820180921161174857565b61170b565b9190820180921161174857565b9061176482610454565b61177160405191826103d7565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061179f8294610454565b0190602036910137565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b80518210156117ec5760209160051b010190565b6117a9565b919082519283825260005b84811061183b5750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b806020809284010151828286010152016117fc565b9080602083519182815201916020808360051b8301019401926000915b83831061187c57505050505090565b9091929394602080827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0856001950301865288519073ffffffffffffffffffffffffffffffffffffffff8251168152828201518382015260c0806118ef604085015160e0604086015260e08501906117f1565b936060810151606085015260808101511515608085015260a0810151151560a085015201519101529701930193019193929061186d565b906020808351928381520192019060005b8181106119445750505090565b825173ffffffffffffffffffffffffffffffffffffffff16845260209384019390920191600101611937565b805160ff1682526104f8916020828101511515908201526101006119ce6119a860408501516101206040860152610120850190611850565b606085015160608501526080850151608085015260a085015184820360a08601526117f1565b9260c081015160c084015260e081015160e0840152015190610100818403910152611926565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b91611a4a6104f89492604085526040850190611970565b9260208185039101526119f4565b90611a759291611a666120a7565b906003825260e0820152611f1d565b5015611a9f577f1626ba7e0000000000000000000000000000000000000000000000000000000090565b600090565b6040513d6000823e3d90fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156101d5570180359067ffffffffffffffff82116101d5576020019181360383136101d557565b908160209103126101d557516104f881610a72565b6040906104f89492815281602082015201916119f4565b91909173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169182156111cf578233036111a15780611c5f575b506020915080610100611b8f920190611ab0565b92611bc760405194859384937f1626ba7e00000000000000000000000000000000000000000000000000000000855260048501611b16565b0381305afa90811561119c577f1626ba7e00000000000000000000000000000000000000000000000000000000917fffffffff0000000000000000000000000000000000000000000000000000000091600091611c30575b501603611c2b57600090565b600190565b611c52915060203d602011611c58575b611c4a81836103d7565b810190611b01565b38611c1f565b503d611c40565b823b156101d5576040517fb760faf900000000000000000000000000000000000000000000000000000000815230600482015292600091849160249183915af190811561119c57602092611b8f92611cb9575b5090611b7b565b806111906000611cc8936103d7565b38611cb2565b91939290611cdd905a93612237565b9160608301516080840151611cf182612948565b818103611d9057509060017f1f180c27086c7a39ea2a7b25239d1ab92348f07ca7bb59d1438fcf527568f881920190611d2a8282612de8565b604080519182526020820192909252a1611d45828685611f1d565b929015611d585750610427939450612590565b836108ce87926040519384937fa2b6d61b00000000000000000000000000000000000000000000000000000000855260048501611a33565b917f9b6514f40000000000000000000000000000000000000000000000000000000060005260045260245260445260646000fd5b9160206104f89381815201916119f4565b3d15611e00573d90611de68261046c565b91611df460405193846103d7565b82523d6000602084013e565b606090565b600436108015611e125750565b611e48906000357fffffffff00000000000000000000000000000000000000000000000000000000811691611e9a575b50612102565b73ffffffffffffffffffffffffffffffffffffffff8116611e665750565b60008091604051368382378036810184815203915af4611e84611dd5565b9015611e9257602081519101f35b602081519101fd5b7fffffffff000000000000000000000000000000000000000000000000000000008092503660040360031b1b161638611e42565b60207f310ba5f1d2ed074b51e2eccd052a47ae9ab7c6b800d1fca3db3999d6a592ca039180305573ffffffffffffffffffffffffffffffffffffffff60405191168152a1565b90156117ec5790565b91907f800000000000000000000000000000000000000000000000000000000000000080611f74611f4e8585611f14565b357fff000000000000000000000000000000000000000000000000000000000000001690565b1614611fd3576000918291611f8894612a56565b905091909192808210611fa3575050611fa090613bde565b91565b7ffd41fcba0000000000000000000000000000000000000000000000000000000060005260045260245260446000fd5b5050611fde906128b4565b90611fe88261298e565b42811115612075575073ffffffffffffffffffffffffffffffffffffffff8116801515908161206a575b5061201e575060019190565b7f8945c3130000000000000000000000000000000000000000000000000000000060005260048390523360245273ffffffffffffffffffffffffffffffffffffffff1660445260646000fd5b905033141538612012565b7ff95b6ab700000000000000000000000000000000000000000000000000000000600052600484905260245260446000fd5b60405190610120820182811067ffffffffffffffff8211176103b6576040526060610100836000815260006020820152826040820152600083820152600060808201528260a0820152600060c0820152600060e08201520152565b73ffffffffffffffffffffffffffffffffffffffff906040517fffffffff0000000000000000000000000000000000000000000000000000000060208201927fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1208452166040820152604081526121796060826103d7565b519020541690565b9061218b82610454565b61219860405191826103d7565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06121c68294610454565b019060005b8281106121d757505050565b6020906040516121e68161039a565b60008152600083820152606060408201526000606082015260006080820152600060a0820152600060c0820152828285010152016121cb565b909392938483116101d55784116101d5578101920390565b9190916122426120a7565b6000815292600190823560f81c827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff818316016124e45750600060608701525b600761229160ff831660011c90565b1680612493575b5060108181160361245e57506001925b6122b184612181565b92604087019384526000905b8582106122cc57505050505050565b8281013560f81c9060010190918160018085160361243c5750612310306122f48389516117d8565b519073ffffffffffffffffffffffffffffffffffffffff169052565b60028084161461241c575b6004808416146123ce575b60088084161461239b575b61238461237e60c08561235d8a60806123548860108060019c9d161493516117d8565b51019015159052565b6123748a60a06123548860208087161493516117d8565b1660061c60031690565b60ff1690565b60c06123918389516117d8565b51015201906122bd565b6001916123849061237e9060c0908781013590602001969060606123c0878d516117d8565b510152959450505050612331565b90612416908481013560e81c9060030161240f6123f66123ee848461174d565b838a8a61221f565b91906040612405888d516117d8565b51019236916104a6565b905261174d565b90612326565b908381013590602001919060206124348389516117d8565b51015261231b565b61245992508481013560601c9060140192906122f48389516117d8565b612310565b6020908116036124805761ffff918381013560f01c906002015b9216926122a8565b60ff918381013560f81c90600101612478565b6124d7919385929190928160031b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001821b019185013590610100031c16920190565b9290608087015238612298565b80850135606090811c908801526014019250612282565b61251360409295949395606083526060830190611970565b9460208201520152565b926104f896959260c09592855260208501526040840152606083015260808201528160a082015201906117f1565b6104f893926060928252602082015281604082015201906117f1565b61257d6104f89492606083526060830190611970565b92602082015260408184039101526117f1565b916000604082019384515190825b8281106125af575b50505050505050565b6125ba8188516117d8565b51936125c960a0860151151590565b806128ac575b61286c575060009360608101518015801580612863575b61282b5784906125f96080850151151590565b156127e5576126a492612620855173ffffffffffffffffffffffffffffffffffffffff1690565b91156127df57505a905b61269f8b61267360608d01516040890151908c8b604051998a967f4c4e814c0000000000000000000000000000000000000000000000000000000060208901526024880161251d565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018552846103d7565b612d76565b156126e7575b506040805187815260208101839052600192917f5a589b1d8062f33451d29cae3dabd9b2e36c62aee644178c600977ca8dda661a91a15b0161259e565b60c001805115612796576001815114612757575160021461270857386126aa565b9493505050507fc2c704302430fe0dc8d95f272e2f4e54bbbc51a3327fd5d75ab41f9fc8fd129b925061274861273c612d88565b6040519384938461254b565b0390a1388080808080806125a6565b50846108ce612764612d88565b6040519384937f7f6b0bb100000000000000000000000000000000000000000000000000000000855260048501612567565b509250600180937f115f347c00e69f252cd6b63c4f81022a9564c6befe8aa719cb74640a4a306f0d6127d76127c9612d88565b604051918291858c8461254b565b0390a16126e1565b9061262a565b835161282093925073ffffffffffffffffffffffffffffffffffffffff169160208501519160001461282557505a905b604085015192612d64565b6126a4565b90612815565b83886108ce5a6040519384937f21395274000000000000000000000000000000000000000000000000000000008552600485016124fb565b50815a106125e6565b6040805188815260208101849052919550600192917f9ae934bf8a986157c889a24c3b3fa85e74b7e4ee4b1f8fc6e7362cb4c1d19d8b91819081016127d7565b5080156125cf565b6129166129426128d46128ce602085015115153090612e66565b93612f61565b60405192839160208301958690916042927f19010000000000000000000000000000000000000000000000000000000000008352600283015260228201520190565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826103d7565b51902090565b60405160208101917f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e83526040820152604081526129876060826103d7565b5190205490565b60405160208101917fc852adf5e97c2fc3b38f405671e91b7af1697ef0287577f227ef10494c2a8e8683526040820152604081526129cd6060826103d7565b51902054906bffffffffffffffffffffffff8260601c921690565b604051906129f5826103bb565b60006020838281520152565b908160409103126101d557602060405191612a1b836103bb565b805183520151602082015290565b60409073ffffffffffffffffffffffffffffffffffffffff6104f8949316815281602082015201906117f1565b909491939291853560f81c60019093819087612a706129e8565b9360408089161480612d46575b612c49575b505050600180861614612c235760028581161460208501526007600286901c1688820135600382901b6101008190039190911c600190911b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01169101908096918196602016612af29060051c90565b612afb9061173a565b8a820135600382901b6101008190039190911c600190911b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01169101959098899a81612b47846128b4565b988993612b539361221f565b91612b5d93613378565b9098612b7191600052602052604060002090565b90612b8491600052602052604060002090565b9073ffffffffffffffffffffffffffffffffffffffff16612bad91600052602052604060002090565b94815190868215159283612c18575b505081612c09575b50612bcc5750565b6040517fccbb534f000000000000000000000000000000000000000000000000000000008152815160048201526020909101516024820152604490fd5b90506020820151101538612bc4565b141591508638612bbc565b86612c3e9792949550612c36939861221f565b939092613156565b919394909293929190565b8a81013560601c985060140192509087908a90849015612c6d575b50829150612a82565b60038101955073ffffffffffffffffffffffffffffffffffffffff945081013560e81c92604092612cf0929091612cbb91612cb4918990612cae898361174d565b9261221f565b36916104a6565b83519586809481937fccce3bc80000000000000000000000000000000000000000000000000000000083523060048401612a29565b0392165afa801561119c57612d0e92600091612d17575b509261174d565b86388981612c64565b612d39915060403d604011612d3f575b612d3181836103d7565b810190612a01565b38612d07565b503d612d27565b5073ffffffffffffffffffffffffffffffffffffffff891615612a7d565b9160009391849360208451940192f190565b9160009291839260208351930191f490565b3d90604051916020818401016040528083526000602084013e565b60405160208101917fc852adf5e97c2fc3b38f405671e91b7af1697ef0287577f227ef10494c2a8e868352604082015260408152612de26060826103d7565b51902055565b60405160208101917f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e8352604082015260408152612de26060826103d7565b60405160208101917fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1208352604082015260408152612de26060826103d7565b15612f16576000905b73ffffffffffffffffffffffffffffffffffffffff6040519160208301937f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f85527f4aa45ca7ad825ceb1bf35643f0a58c295239df563b1b565c2485f96477c5631860408501527f2a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4de606085015260808401521660a082015260a0815261294260c0826103d7565b4690612e6f565b805160209091019060005b818110612f355750505090565b825173ffffffffffffffffffffffffffffffffffffffff16845260209384019390920191600101612f28565b610100810151604051612f7c81612916602082018095612f1d565b51902090612f8b815160ff1690565b60ff81168061300457505090612942612fa76040840151613cdb565b9261291660806060830151920151936040519485936020850197889094939260809260a08301967f11e1e4079a79a66e4ade50033cfe2678cdd5341d2dfe5ef9513edb1a0be147a284526020840152604083015260608201520152565b6001810361306257505060a001518051602091820120604080517fe19a3b94fc3c7ece3f890d98a99bc422615537a08dea0603fa8425867d87d466938101938452908101919091526060810192909252906129428160808101612916565b600281036130b857505060c00151604080517f11fdeb7e8373a1aa96bfac8d0ea91526b2c5d15e5cee20e0543e780258f3e8e4602082019081529181019290925260608201929092526129428160808101612916565b60030361310c575060e00151604080517fe19a3b94fc3c7ece3f890d98a99bc422615537a08dea0603fa8425867d87d466602082019081529181019290925260608201929092526129428160808101612916565b7f048183200000000000000000000000000000000000000000000000000000000060005260ff1660045260246000fd5b9061251390604093969594966060845260608401916119f4565b91949290926000956000956000956000956000956131726120a7565b60028152937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9460005b8281106131c9575050505050505080511515806131bb575b612bcc5750565b5060208101518411156131b4565b600381019d50959b50939950919750929091906131ed908b9085013560e81c61174d565b958a6000848903613331575089915b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8c0361330157509060016132368961323d94878961221f565b908b612a56565b9c939c9b929b9a919a99909a9b9d8e9d9e8f905b106132bb5750928b8851146132b2575b808b101561328057508a60c0850152899295929594919493909361319c565b7f37daf62b0000000000000000000000000000000000000000000000000000000060005260048b905260245260446000fd5b60008852613261565b8d8f6108ce6132cc85858c8e61221f565b9390926040519485947fb006aba00000000000000000000000000000000000000000000000000000000086526004860161313c565b9798999a9160016133178b61331e94888a61221f565b9086612a56565b50929d919c909b929a9092918e8e613251565b916131fc565b73ffffffffffffffffffffffffffffffffffffffff6104f89593606093835216602082015281604082015201916119f4565b908160209103126101d5575190565b9391909360009460009460005b818110613393575050505050565b8481013560f881901c9860019092019788979692909160fc1c988915613b77575060018914613b37576002891461397a576003891461394b57600489146138ca576006891461382a57600589146137dc576007891461371557600889146136bf576009891461359657600a8914613433577fb2505f7c00000000000000000000000000000000000000000000000000000000600052600489905260246000fd5b90919293949596975060038916978815613585575b8381013560601c90601401909960021c60031660ff1684820135600382901b6101008190039190911c600190911b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0116910190810190816134ab91878761221f565b6040517f898bd921000000000000000000000000000000000000000000000000000000008152939184916134e3918a60048501611b16565b038373ffffffffffffffffffffffffffffffffffffffff8d1691815a93602094fa91821561119c5761352693600093613552575b5060ff909a168091019a613fa7565b90801561354c579061354091600052602052604060002090565b955b9392919093613385565b50613540565b60ff9193506135779060203d811161357e575b61356f81836103d7565b810190613369565b9290613517565b503d613565565b8084013560f81c9850600101613448565b909192939495969750600389169788156136ae575b8381013560601c90601401909960021c60031660ff1684820135600382901b6101008190039190911c600190911b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01169101908101908161360e91878761221f565b6040517f13792a4a00000000000000000000000000000000000000000000000000000000815293918491613646918b60048501611a33565b038373ffffffffffffffffffffffffffffffffffffffff8d1691815a93602094fa91821561119c5761368893600093613552575060ff909a168091019a613fa7565b9080156136a857906136a291600052602052604060002090565b95613542565b506136a2565b8084013560f81c98506001016135ab565b9850602087019750949593949293919290918201356136dd86613f4e565b81146136ed575b61368890613f68565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff98506136e4565b975090919293949597600f169687156137ca575b6020600061373b6137a89a9b86613e16565b9c9092918a60405161377e816129168a82019485603c917f19457468657265756d205369676e6564204d6573736167653a0a3332000000008252601c8201520190565b51902092604051948594859094939260ff6060936080840197845216602083015260408201520152565b838052039060015afa1561119c576136889060ff6000519a1680910199613e59565b600189019883013560f81c9750613729565b985060208701975094959394929391929091820135808514613802575b61368890613f0f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff98506137f9565b989091929394959662ffffff985061384c61237e600c8416603f9060021c1690565b9182156138b6575b60031680156138a5575b90819061388990613881908781013560e81c906003019c168c01809c898961221f565b90898b613378565b91111561389c575b906136889291613ec4565b99820199613891565b50600281019084013560f01c61385e565b8482013560f81c9250600190910190613854565b97509761392061392d929394959697600f61393593169085929190928160031b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001821b019185013590610100031c16920190565b929083018093868661221f565b908688613378565b906136a292980198600052602052604060002090565b9850965093949293919290919080820135906020019680156136a857906136a291600052602052604060002090565b90919293949596975060038916978815613b26575b8084013560601c996139ee91601401906139ae9060021c60031661237e565b9085929190928160031b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001821b019185013590610100031c16920190565b90810190613a5260208c613a0485858b8b61221f565b919073ffffffffffffffffffffffffffffffffffffffff8c604051968795869485937f1626ba7e00000000000000000000000000000000000000000000000000000000855260048501611b16565b0392165afa90811561119c577f1626ba7e00000000000000000000000000000000000000000000000000000000917fffffffff0000000000000000000000000000000000000000000000000000000091600091613b08575b501603613ac457509060ff61368892991680910199613e59565b6108ce613ad58c938993898961221f565b906040519485947fb2fed7ae00000000000000000000000000000000000000000000000000000000865260048601613337565b613b20915060203d8111611c5857611c4a81836103d7565b38613aaa565b8381013560f81c985060010161398f565b98600f91929394959697985016968715613b66575b60148101976136889160ff9091169084013560601c613e59565b8281013560f81c9750600101613b4c565b98509091929394959698600f16978815613bc9575b5060206000613b9f6137a89a9b86613e16565b9c90916040519384938c859094939260ff6060936080840197845216602083015260408201520152565b60018101995083013560f81c97506020613b8c565b73ffffffffffffffffffffffffffffffffffffffff9060405160208101917fff0000000000000000000000000000000000000000000000000000000000000083527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000007f00000000000000000000000000000000000018a77519fccca060c2537c9d6d3f60601b16602183015260358201527f573fd524dc86b426cf228cb23f874693e22c686ed86eee5bc6720a772e660608605582015260558152613ca46075826103d7565b51902016301490565b805160209091019060005b818110613cc55750505090565b8251845260209384019390920191600101613cb8565b9081517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0613d21613d0b83610454565b92613d1960405194856103d7565b808452610454565b0136602083013760005b8351811015613dfd5780613d41600192866117d8565b5173ffffffffffffffffffffffffffffffffffffffff81511690602081015190604081015160208151910120906060810151608082015115159060c060a08401511515930151936040519560208701977f0603985259a953da1f65a522f589c17bd1d0117ec1d3abb7c0788aef251ef437895260408801526060870152608086015260a085015260c084015260e08301526101008201526101008152613de9610120826103d7565b519020613df682856117d8565b5201613d2b565b5090915060405161294281612916602082018095613cad565b8101916040602084359401359201601b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84169360ff1c0160ff81116117485791565b90604051907fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060208301937f53657175656e6365207369676e65723a0a000000000000000000000000000000855260601b1660318301526045820152604581526129426065826103d7565b916040519160208301937f53657175656e6365206e657374656420636f6e6669673a0a00000000000000008552603884015260588301526078820152607881526129426098826103d7565b60405160208101917f53657175656e636520737461746963206469676573743a0a000000000000000083526038820152603881526129426058826103d7565b6129166129426128d46128ce600060208601511515612e66565b60405160208101917f53657175656e636520616e792061646472657373207375626469676573743a0a83526040820152604081526129426060826103d7565b91604051917fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060208401947f53657175656e63652073617069656e7420636f6e6669673a0a00000000000000865260601b166039840152604d830152606d820152606d8152612942608d826103d756fea264697066735822122087a48abc01376affb53cab3dcf4b976377072726f6053a6646463ec2e59c180964736f6c634300081c0033
IAuth.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.27; /// @title IAuth /// @author Agustin Aguilar, Michael Standen, William Hua /// @notice Internal interface for the auth modules abstract contract IAuth { function _isValidImage( bytes32 imageHash ) internal view virtual returns (bool isValid); function _updateImageHash( bytes32 imageHash ) internal virtual; }
Stage1Module.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.27; import { Stage2Module } from "./Stage2Module.sol"; import { Calls } from "./modules/Calls.sol"; import { ERC4337v07 } from "./modules/ERC4337v07.sol"; import { Hooks } from "./modules/Hooks.sol"; import { Stage1Auth } from "./modules/auth/Stage1Auth.sol"; import { IAuth } from "./modules/interfaces/IAuth.sol"; /// @title Stage1Module /// @author Agustin Aguilar /// @notice The initial stage of the wallet contract Stage1Module is Calls, Stage1Auth, Hooks, ERC4337v07 { constructor( address _factory, address _entryPoint ) Stage1Auth(_factory, address(new Stage2Module(_entryPoint))) ERC4337v07(_entryPoint) { } /// @inheritdoc IAuth function _isValidImage( bytes32 _imageHash ) internal view virtual override(IAuth, Stage1Auth) returns (bool) { return super._isValidImage(_imageHash); } }
Stage2Module.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.27; import { Calls } from "./modules/Calls.sol"; import { ERC4337v07 } from "./modules/ERC4337v07.sol"; import { Hooks } from "./modules/Hooks.sol"; import { Stage2Auth } from "./modules/auth/Stage2Auth.sol"; import { IAuth } from "./modules/interfaces/IAuth.sol"; /// @title Stage2Module /// @author Agustin Aguilar /// @notice The second stage of the wallet contract Stage2Module is Calls, Stage2Auth, Hooks, ERC4337v07 { constructor( address _entryPoint ) ERC4337v07(_entryPoint) { } /// @inheritdoc IAuth function _isValidImage( bytes32 _imageHash ) internal view virtual override(IAuth, Stage2Auth) returns (bool) { return super._isValidImage(_imageHash); } }
Wallet.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /* // Delegate Proxy in Huff // @title Delegate Proxy // @notice Implements a proxy using the contract's own address to store the delegate target. // Calls with calldata (with or without ETH value) are forwarded to the stored target. // Calls sending only ETH without calldata do nothing and return immediately without forwarding. // @author Agusx1211 #define macro CONSTRUCTOR() = takes (0) returns (0) { 0x41 // [code + arg size] (code_size + 32) __codeoffset(MAIN) // [code_start, code + arg size] returndatasize // [0, code_start, code + arg size] codecopy // [] __codesize(MAIN) // [code_size] dup1 // [code_size, code_size] mload // [arg1, code_size] address // [address, arg1, code_size] sstore // [code_size] returndatasize // [0, code_size] return } #define macro MAIN() = takes(0) returns(0) { returndatasize // [0] returndatasize // [0, 0] calldatasize // [cs, 0, 0] iszero // [cs == 0, 0, 0] callvalue // [cv, cs == 0, 0, 0] mul // [cv * cs == 0, 0, 0] success // [nr, cv * cs == 0, 0, 0] jumpi calldatasize // [cds, 0, 0] returndatasize // [0, cds, 0, 0] returndatasize // [0, 0, cds, 0, 0] calldatacopy // [0, 0] returndatasize // [0, 0, 0] calldatasize // [cds, 0, 0, 0] returndatasize // [0, cds, 0, 0, 0] address // [addr, 0, cds, 0, 0, 0] sload // [imp, 0, cds, 0, 0, 0] gas // [gas, imp, 0, cds, 0, 0, 0] delegatecall // [suc, 0] returndatasize // [rds, suc, 0] dup3 // [0, rds, suc, 0] dup1 // [0, 0, rds, suc, 0] returndatacopy // [suc, 0] swap1 // [0, suc] returndatasize // [rds, 0, suc] swap2 // [suc, 0, rds] success // [nr, suc, 0, rds] jumpi revert success: return } */ library Wallet { bytes internal constant creationCode = hex"6041600e3d396021805130553df33d3d36153402601f57363d3d373d363d30545af43d82803e903d91601f57fd5bf3"; }
Calls.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.27; import { LibOptim } from "../utils/LibOptim.sol"; import { Nonce } from "./Nonce.sol"; import { Payload } from "./Payload.sol"; import { ReentrancyGuard } from "./ReentrancyGuard.sol"; import { BaseAuth } from "./auth/BaseAuth.sol"; import { IDelegatedExtension } from "./interfaces/IDelegatedExtension.sol"; /// @title Calls /// @author Agustin Aguilar, Michael Standen, William Hua /// @notice Contract for executing calls abstract contract Calls is ReentrancyGuard, BaseAuth, Nonce { /// @notice Emitted when a call succeeds event CallSucceeded(bytes32 _opHash, uint256 _index); /// @notice Emitted when a call fails event CallFailed(bytes32 _opHash, uint256 _index, bytes _returnData); /// @notice Emitted when a call is aborted event CallAborted(bytes32 _opHash, uint256 _index, bytes _returnData); /// @notice Emitted when a call is skipped event CallSkipped(bytes32 _opHash, uint256 _index); /// @notice Error thrown when a call reverts error Reverted(Payload.Decoded _payload, uint256 _index, bytes _returnData); /// @notice Error thrown when a signature is invalid error InvalidSignature(Payload.Decoded _payload, bytes _signature); /// @notice Error thrown when there is not enough gas error NotEnoughGas(Payload.Decoded _payload, uint256 _index, uint256 _gasLeft); /// @notice Execute a call /// @param _payload The payload /// @param _signature The signature function execute(bytes calldata _payload, bytes calldata _signature) external payable virtual nonReentrant { uint256 startingGas = gasleft(); Payload.Decoded memory decoded = Payload.fromPackedCalls(_payload); _consumeNonce(decoded.space, decoded.nonce); (bool isValid, bytes32 opHash) = signatureValidation(decoded, _signature); if (!isValid) { revert InvalidSignature(decoded, _signature); } _execute(startingGas, opHash, decoded); } /// @notice Execute a call /// @dev Callable only by the contract itself /// @param _payload The payload function selfExecute( bytes calldata _payload ) external payable virtual onlySelf { uint256 startingGas = gasleft(); Payload.Decoded memory decoded = Payload.fromPackedCalls(_payload); bytes32 opHash = Payload.hash(decoded); _execute(startingGas, opHash, decoded); } function _execute(uint256 _startingGas, bytes32 _opHash, Payload.Decoded memory _decoded) private { bool errorFlag = false; uint256 numCalls = _decoded.calls.length; for (uint256 i = 0; i < numCalls; i++) { Payload.Call memory call = _decoded.calls[i]; // Skip onlyFallback calls if no error occurred if (call.onlyFallback && !errorFlag) { emit CallSkipped(_opHash, i); continue; } // Reset the error flag // onlyFallback calls only apply when the immediately preceding transaction fails errorFlag = false; uint256 gasLimit = call.gasLimit; if (gasLimit != 0 && gasleft() < gasLimit) { revert NotEnoughGas(_decoded, i, gasleft()); } bool success; if (call.delegateCall) { (success) = LibOptim.delegatecall( call.to, gasLimit == 0 ? gasleft() : gasLimit, abi.encodeWithSelector( IDelegatedExtension.handleSequenceDelegateCall.selector, _opHash, _startingGas, i, numCalls, _decoded.space, call.data ) ); } else { (success) = LibOptim.call(call.to, call.value, gasLimit == 0 ? gasleft() : gasLimit, call.data); } if (!success) { if (call.behaviorOnError == Payload.BEHAVIOR_IGNORE_ERROR) { errorFlag = true; emit CallFailed(_opHash, i, LibOptim.returnData()); continue; } if (call.behaviorOnError == Payload.BEHAVIOR_REVERT_ON_ERROR) { revert Reverted(_decoded, i, LibOptim.returnData()); } if (call.behaviorOnError == Payload.BEHAVIOR_ABORT_ON_ERROR) { emit CallAborted(_opHash, i, LibOptim.returnData()); break; } } emit CallSucceeded(_opHash, i); } } }
ERC4337v07.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; import { Calls } from "./Calls.sol"; import { ReentrancyGuard } from "./ReentrancyGuard.sol"; import { IAccount, PackedUserOperation } from "./interfaces/IAccount.sol"; import { IERC1271_MAGIC_VALUE_HASH } from "./interfaces/IERC1271.sol"; import { IEntryPoint } from "./interfaces/IEntryPoint.sol"; /// @title ERC4337v07 /// @author Agustin Aguilar, Michael Standen /// @notice ERC4337 v7 support abstract contract ERC4337v07 is ReentrancyGuard, IAccount, Calls { uint256 internal constant SIG_VALIDATION_FAILED = 1; address public immutable entrypoint; error InvalidEntryPoint(address _entrypoint); error ERC4337Disabled(); constructor( address _entrypoint ) { entrypoint = _entrypoint; } /// @inheritdoc IAccount function validateUserOp( PackedUserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds ) external returns (uint256 validationData) { if (entrypoint == address(0)) { revert ERC4337Disabled(); } if (msg.sender != entrypoint) { revert InvalidEntryPoint(msg.sender); } // userOp.nonce is validated by the entrypoint if (missingAccountFunds != 0) { IEntryPoint(entrypoint).depositTo{ value: missingAccountFunds }(address(this)); } if (this.isValidSignature(userOpHash, userOp.signature) != IERC1271_MAGIC_VALUE_HASH) { return SIG_VALIDATION_FAILED; } return 0; } /// @notice Execute a user operation /// @param _payload The packed payload /// @dev This is the execute function for the EntryPoint to call. function executeUserOp( bytes calldata _payload ) external nonReentrant { if (entrypoint == address(0)) { revert ERC4337Disabled(); } if (msg.sender != entrypoint) { revert InvalidEntryPoint(msg.sender); } this.selfExecute(_payload); } }
Hooks.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.27; import { Storage } from "./Storage.sol"; import { SelfAuth } from "./auth/SelfAuth.sol"; import { IERC1155Receiver } from "./interfaces/IERC1155Receiver.sol"; import { IERC223Receiver } from "./interfaces/IERC223Receiver.sol"; import { IERC721Receiver } from "./interfaces/IERC721Receiver.sol"; import { IERC777Receiver } from "./interfaces/IERC777Receiver.sol"; /// @title Hooks /// @author Agustin Aguilar, Michael Standen /// @notice Enables extension of the wallet by adding hooks contract Hooks is SelfAuth, IERC1155Receiver, IERC777Receiver, IERC721Receiver, IERC223Receiver { /// @dev keccak256("org.arcadeum.module.hooks.hooks") bytes32 private constant HOOKS_KEY = bytes32(0xbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a120); /// @notice Emitted when a hook is defined event DefinedHook(bytes4 selector, address implementation); /// @notice Error thrown when a hook already exists error HookAlreadyExists(bytes4 selector); /// @notice Error thrown when a hook does not exist error HookDoesNotExist(bytes4 selector); /// @notice Read a hook /// @param selector The selector of the hook /// @return implementation The implementation address of the hook function readHook( bytes4 selector ) external view returns (address) { return _readHook(selector); } /// @notice Add a hook /// @param selector The selector of the hook /// @param implementation The implementation address of the hook /// @dev Callable only by the contract itself function addHook(bytes4 selector, address implementation) external payable onlySelf { if (_readHook(selector) != address(0)) { revert HookAlreadyExists(selector); } _writeHook(selector, implementation); } /// @notice Remove a hook /// @param selector The selector of the hook /// @dev Callable only by the contract itself function removeHook( bytes4 selector ) external payable onlySelf { if (_readHook(selector) == address(0)) { revert HookDoesNotExist(selector); } _writeHook(selector, address(0)); } function _readHook( bytes4 selector ) private view returns (address) { return address(uint160(uint256(Storage.readBytes32Map(HOOKS_KEY, bytes32(selector))))); } function _writeHook(bytes4 selector, address implementation) private { Storage.writeBytes32Map(HOOKS_KEY, bytes32(selector), bytes32(uint256(uint160(implementation)))); emit DefinedHook(selector, implementation); } /// @inheritdoc IERC1155Receiver function onERC1155Received(address, address, uint256, uint256, bytes calldata) external pure returns (bytes4) { return Hooks.onERC1155Received.selector; } /// @inheritdoc IERC1155Receiver function onERC1155BatchReceived( address, address, uint256[] calldata, uint256[] calldata, bytes calldata ) external pure returns (bytes4) { return Hooks.onERC1155BatchReceived.selector; } /// @inheritdoc IERC777Receiver function tokensReceived( address operator, address from, address to, uint256 amount, bytes calldata data, bytes calldata operatorData ) external { } /// @inheritdoc IERC721Receiver function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) { return Hooks.onERC721Received.selector; } /// @inheritdoc IERC223Receiver function tokenReceived(address, uint256, bytes calldata) external pure returns (bytes4) { return Hooks.tokenReceived.selector; } /// @notice Fallback function /// @dev Handles delegate calls to hooks fallback() external payable { if (msg.data.length >= 4) { address target = _readHook(bytes4(msg.data)); if (target != address(0)) { (bool success, bytes memory result) = target.delegatecall(msg.data); assembly { if iszero(success) { revert(add(result, 32), mload(result)) } return(add(result, 32), mload(result)) } } } } /// @notice Receive native tokens receive() external payable { } }
Implementation.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.27; import { SelfAuth } from "./auth/SelfAuth.sol"; /// @title Implementation /// @author Agustin Aguilar /// @notice Manages the implementation address of the proxy contract contract Implementation is SelfAuth { /// @notice Emitted when the implementation is updated event ImplementationUpdated(address newImplementation); /// @notice Update the implementation /// @param _implementation The new implementation /// @dev Callable only by the contract itself function updateImplementation( address _implementation ) external payable virtual onlySelf { _updateImplementation(_implementation); } /// @notice Get the implementation /// @return implementation The implementation function getImplementation() external view virtual returns (address) { return _getImplementation(); } function _updateImplementation( address _implementation ) internal virtual { _setImplementation(_implementation); emit ImplementationUpdated(_implementation); } function _setImplementation( address _imp ) internal { assembly { sstore(address(), _imp) } } function _getImplementation() internal view returns (address _imp) { assembly { _imp := sload(address()) } } }
Nonce.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.27; import { Storage } from "./Storage.sol"; /// @title Nonce /// @author Agustin Aguilar /// @notice Manages the nonce of the wallet contract Nonce { /// @notice Emitted when the nonce is changed event NonceChange(uint256 _space, uint256 _newNonce); /// @notice Error thrown when the nonce is bad error BadNonce(uint256 _space, uint256 _provided, uint256 _current); /// @dev keccak256("org.arcadeum.module.calls.nonce") bytes32 private constant NONCE_KEY = bytes32(0x8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e); /// @notice Read the nonce /// @param _space The space /// @return nonce The nonce function readNonce( uint256 _space ) public view virtual returns (uint256) { return uint256(Storage.readBytes32Map(NONCE_KEY, bytes32(_space))); } function _writeNonce(uint256 _space, uint256 _nonce) internal { Storage.writeBytes32Map(NONCE_KEY, bytes32(_space), bytes32(_nonce)); } function _consumeNonce(uint256 _space, uint256 _nonce) internal { uint256 currentNonce = readNonce(_space); if (currentNonce != _nonce) { revert BadNonce(_space, _nonce, currentNonce); } unchecked { uint256 newNonce = _nonce + 1; _writeNonce(_space, newNonce); emit NonceChange(_space, newNonce); return; } } }
Payload.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.27; import { LibBytes } from "../utils/LibBytes.sol"; using LibBytes for bytes; /// @title Payload /// @author Agustin Aguilar, Michael Standen, William Hua /// @notice Library for encoding and decoding payloads library Payload { /// @notice Error thrown when the kind is invalid error InvalidKind(uint8 kind); /// @dev keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)") bytes32 private constant EIP712_DOMAIN_TYPEHASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f; /// @dev keccak256("Sequence Wallet") bytes32 private constant EIP712_DOMAIN_NAME_SEQUENCE = 0x4aa45ca7ad825ceb1bf35643f0a58c295239df563b1b565c2485f96477c56318; /// @dev keccak256("3") bytes32 private constant EIP712_DOMAIN_VERSION_SEQUENCE = 0x2a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4de; function domainSeparator(bool _noChainId, address _wallet) internal view returns (bytes32 _domainSeparator) { return keccak256( abi.encode( EIP712_DOMAIN_TYPEHASH, EIP712_DOMAIN_NAME_SEQUENCE, EIP712_DOMAIN_VERSION_SEQUENCE, _noChainId ? uint256(0) : uint256(block.chainid), _wallet ) ); } /// @dev keccak256("Call(address to,uint256 value,bytes data,uint256 gasLimit,bool delegateCall,bool onlyFallback,uint256 behaviorOnError)") bytes32 private constant CALL_TYPEHASH = 0x0603985259a953da1f65a522f589c17bd1d0117ec1d3abb7c0788aef251ef437; /// @dev keccak256("Calls(Call[] calls,uint256 space,uint256 nonce,address[] wallets)Call(address to,uint256 value,bytes data,uint256 gasLimit,bool delegateCall,bool onlyFallback,uint256 behaviorOnError)") bytes32 private constant CALLS_TYPEHASH = 0x11e1e4079a79a66e4ade50033cfe2678cdd5341d2dfe5ef9513edb1a0be147a2; /// @dev keccak256("Message(bytes message,address[] wallets)") bytes32 private constant MESSAGE_TYPEHASH = 0xe19a3b94fc3c7ece3f890d98a99bc422615537a08dea0603fa8425867d87d466; /// @dev keccak256("ConfigUpdate(bytes32 imageHash,address[] wallets)") bytes32 private constant CONFIG_UPDATE_TYPEHASH = 0x11fdeb7e8373a1aa96bfac8d0ea91526b2c5d15e5cee20e0543e780258f3e8e4; /// @notice Kind of transaction uint8 public constant KIND_TRANSACTIONS = 0x00; /// @notice Kind of digest uint8 public constant KIND_MESSAGE = 0x01; /// @notice Kind of config update uint8 public constant KIND_CONFIG_UPDATE = 0x02; /// @notice Kind of message uint8 public constant KIND_DIGEST = 0x03; /// @notice Behavior on error: ignore error uint8 public constant BEHAVIOR_IGNORE_ERROR = 0x00; /// @notice Behavior on error: revert on error uint8 public constant BEHAVIOR_REVERT_ON_ERROR = 0x01; /// @notice Behavior on error: abort on error uint8 public constant BEHAVIOR_ABORT_ON_ERROR = 0x02; /// @notice Payload call information /// @param to Address of the target contract /// @param value Value to send with the call /// @param data Data to send with the call /// @param gasLimit Gas limit for the call /// @param delegateCall If the call is a delegate call /// @param onlyFallback If the call should only be executed in an error scenario /// @param behaviorOnError Behavior on error struct Call { address to; uint256 value; bytes data; uint256 gasLimit; bool delegateCall; bool onlyFallback; uint256 behaviorOnError; } /// @notice Decoded payload /// @param kind Kind of payload /// @param noChainId If the chain ID should be omitted /// @param calls Array of calls (transaction kind) /// @param space Nonce space for the calls (transaction kind) /// @param nonce Nonce value for the calls (transaction kind) /// @param message Message to validate (message kind) /// @param imageHash Image hash to update to (config update kind) /// @param digest Digest to validate (digest kind) /// @param parentWallets Parent wallets struct Decoded { uint8 kind; bool noChainId; // Transaction kind Call[] calls; uint256 space; uint256 nonce; // Message kind bytes message; // Config update kind bytes32 imageHash; // Digest kind for 1271 bytes32 digest; // Parent wallets address[] parentWallets; } function fromMessage( bytes memory message ) internal pure returns (Decoded memory _decoded) { _decoded.kind = KIND_MESSAGE; _decoded.message = message; } function fromConfigUpdate( bytes32 imageHash ) internal pure returns (Decoded memory _decoded) { _decoded.kind = KIND_CONFIG_UPDATE; _decoded.imageHash = imageHash; } function fromDigest( bytes32 digest ) internal pure returns (Decoded memory _decoded) { _decoded.kind = KIND_DIGEST; _decoded.digest = digest; } function fromPackedCalls( bytes calldata packed ) internal view returns (Decoded memory _decoded) { _decoded.kind = KIND_TRANSACTIONS; // Read the global flag (uint256 globalFlag, uint256 pointer) = packed.readFirstUint8(); // First bit determines if space is zero or not if (globalFlag & 0x01 == 0x01) { _decoded.space = 0; } else { (_decoded.space, pointer) = packed.readUint160(pointer); } // Next 3 bits determine the size of the nonce uint256 nonceSize = (globalFlag >> 1) & 0x07; if (nonceSize > 0) { // Read the nonce (_decoded.nonce, pointer) = packed.readUintX(pointer, nonceSize); } uint256 numCalls; // Bit 5 determines if the batch contains a single call if (globalFlag & 0x10 == 0x10) { numCalls = 1; } else { // Bit 6 determines if the number of calls uses 1 byte or 2 bytes if (globalFlag & 0x20 == 0x20) { (numCalls, pointer) = packed.readUint16(pointer); } else { (numCalls, pointer) = packed.readUint8(pointer); } } // Read the calls _decoded.calls = new Call[](numCalls); for (uint256 i = 0; i < numCalls; i++) { uint8 flags; (flags, pointer) = packed.readUint8(pointer); // First bit determines if this is a call to self // or a call to another address if (flags & 0x01 == 0x01) { // Call to self _decoded.calls[i].to = address(this); } else { // Call to another address (_decoded.calls[i].to, pointer) = packed.readAddress(pointer); } // Second bit determines if the call has value or not if (flags & 0x02 == 0x02) { (_decoded.calls[i].value, pointer) = packed.readUint256(pointer); } // Third bit determines if the call has data or not if (flags & 0x04 == 0x04) { // 3 bytes determine the size of the calldata uint256 calldataSize; (calldataSize, pointer) = packed.readUint24(pointer); _decoded.calls[i].data = packed[pointer:pointer + calldataSize]; pointer += calldataSize; } // Fourth bit determines if the call has a gas limit or not if (flags & 0x08 == 0x08) { (_decoded.calls[i].gasLimit, pointer) = packed.readUint256(pointer); } // Fifth bit determines if the call is a delegate call or not _decoded.calls[i].delegateCall = (flags & 0x10 == 0x10); // Sixth bit determines if the call is fallback only _decoded.calls[i].onlyFallback = (flags & 0x20 == 0x20); // Last 2 bits are directly mapped to the behavior on error _decoded.calls[i].behaviorOnError = (flags & 0xC0) >> 6; } } function hashCall( Call memory c ) internal pure returns (bytes32) { return keccak256( abi.encode( CALL_TYPEHASH, c.to, c.value, keccak256(c.data), c.gasLimit, c.delegateCall, c.onlyFallback, c.behaviorOnError ) ); } function hashCalls( Call[] memory calls ) internal pure returns (bytes32) { // In EIP712, an array is often hashed as the keccak256 of the concatenated // hashes of each item. So we hash each Call, pack them, and hash again. bytes32[] memory callHashes = new bytes32[](calls.length); for (uint256 i = 0; i < calls.length; i++) { callHashes[i] = hashCall(calls[i]); } return keccak256(abi.encodePacked(callHashes)); } function toEIP712( Decoded memory _decoded ) internal pure returns (bytes32) { bytes32 walletsHash = keccak256(abi.encodePacked(_decoded.parentWallets)); if (_decoded.kind == KIND_TRANSACTIONS) { bytes32 callsHash = hashCalls(_decoded.calls); // The top-level struct for Calls might be something like: // Calls(bytes32 callsHash,uint256 space,uint256 nonce,bytes32 walletsHash) return keccak256(abi.encode(CALLS_TYPEHASH, callsHash, _decoded.space, _decoded.nonce, walletsHash)); } else if (_decoded.kind == KIND_MESSAGE) { // If you define your top-level as: Message(bytes32 messageHash,bytes32 walletsHash) return keccak256(abi.encode(MESSAGE_TYPEHASH, keccak256(_decoded.message), walletsHash)); } else if (_decoded.kind == KIND_CONFIG_UPDATE) { // Top-level: ConfigUpdate(bytes32 imageHash,bytes32 walletsHash) return keccak256(abi.encode(CONFIG_UPDATE_TYPEHASH, _decoded.imageHash, walletsHash)); } else if (_decoded.kind == KIND_DIGEST) { // Top-level: Use MESSAGE_TYPEHASH but assume the digest is already the hashed message return keccak256(abi.encode(MESSAGE_TYPEHASH, _decoded.digest, walletsHash)); } else { // Unknown kind revert InvalidKind(_decoded.kind); } } function hash( Decoded memory _decoded ) internal view returns (bytes32) { bytes32 domain = domainSeparator(_decoded.noChainId, address(this)); bytes32 structHash = toEIP712(_decoded); return keccak256(abi.encodePacked("\x19\x01", domain, structHash)); } function hashFor(Decoded memory _decoded, address _wallet) internal view returns (bytes32) { bytes32 domain = domainSeparator(_decoded.noChainId, _wallet); bytes32 structHash = toEIP712(_decoded); return keccak256(abi.encodePacked("\x19\x01", domain, structHash)); } }
ReentrancyGuard.sol
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { Storage } from "./Storage.sol"; abstract contract ReentrancyGuard { bytes32 private constant _INITIAL_VALUE = bytes32(0); bytes32 private constant _NOT_ENTERED = bytes32(uint256(1)); bytes32 private constant _ENTERED = bytes32(uint256(2)); /// @dev keccak256("org.sequence.module.reentrancyguard.status") bytes32 private constant STATUS_KEY = bytes32(0xfc6e07e3992c7c3694a921dc9e412b6cfe475380556756a19805a9e3ddfe2fde); /// @notice Error thrown when a reentrant call is detected error ReentrantCall(); /// @notice Prevents a contract from calling itself, directly or indirectly modifier nonReentrant() { // On the first call to nonReentrant // _status will be _NOT_ENTERED or _INITIAL_VALUE if (Storage.readBytes32(STATUS_KEY) == _ENTERED) { revert ReentrantCall(); } // Any calls to nonReentrant after this point will fail Storage.writeBytes32(STATUS_KEY, _ENTERED); _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) // Notice that because constructors are not available // we always start with _INITIAL_VALUE, not _NOT_ENTERED Storage.writeBytes32(STATUS_KEY, _NOT_ENTERED); } }
Storage.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.27; /// @title Storage /// @author Agustin Aguilar /// @notice Library for storing data at certain storage slots library Storage { function writeBytes32(bytes32 _key, bytes32 _val) internal { assembly { sstore(_key, _val) } } function readBytes32( bytes32 _key ) internal view returns (bytes32 val) { assembly { val := sload(_key) } } function writeBytes32Map(bytes32 _key, bytes32 _subKey, bytes32 _val) internal { bytes32 key = keccak256(abi.encode(_key, _subKey)); assembly { sstore(key, _val) } } function readBytes32Map(bytes32 _key, bytes32 _subKey) internal view returns (bytes32 val) { bytes32 key = keccak256(abi.encode(_key, _subKey)); assembly { val := sload(key) } } }
BaseAuth.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.27; import { Payload } from "../Payload.sol"; import { Storage } from "../Storage.sol"; import { IAuth } from "../interfaces/IAuth.sol"; import { IERC1271, IERC1271_MAGIC_VALUE_HASH } from "../interfaces/IERC1271.sol"; import { IPartialAuth } from "../interfaces/IPartialAuth.sol"; import { ISapient } from "../interfaces/ISapient.sol"; import { BaseSig } from "./BaseSig.sol"; import { SelfAuth } from "./SelfAuth.sol"; using Payload for Payload.Decoded; /// @title BaseAuth /// @author Agustin Aguilar, Michael Standen /// @notice Base contract for the auth module abstract contract BaseAuth is IAuth, IPartialAuth, ISapient, IERC1271, SelfAuth { /// @dev keccak256("org.sequence.module.auth.static") bytes32 private constant STATIC_SIGNATURE_KEY = bytes32(0xc852adf5e97c2fc3b38f405671e91b7af1697ef0287577f227ef10494c2a8e86); /// @notice Error thrown when the sapient signature is invalid error InvalidSapientSignature(Payload.Decoded _payload, bytes _signature); /// @notice Error thrown when the signature weight is invalid error InvalidSignatureWeight(uint256 _threshold, uint256 _weight); /// @notice Error thrown when the static signature has expired error InvalidStaticSignatureExpired(bytes32 _opHash, uint256 _expires); /// @notice Error thrown when the static signature has the wrong caller error InvalidStaticSignatureWrongCaller(bytes32 _opHash, address _caller, address _expectedCaller); /// @notice Event emitted when a static signature is set event StaticSignatureSet(bytes32 _hash, address _address, uint96 _timestamp); function _getStaticSignature( bytes32 _hash ) internal view returns (address, uint256) { uint256 word = uint256(Storage.readBytes32Map(STATIC_SIGNATURE_KEY, _hash)); return (address(uint160(word >> 96)), uint256(uint96(word))); } function _setStaticSignature(bytes32 _hash, address _address, uint256 _timestamp) internal { Storage.writeBytes32Map( STATIC_SIGNATURE_KEY, _hash, bytes32(uint256(uint160(_address)) << 96 | (_timestamp & 0xffffffffffffffffffffffff)) ); } /// @notice Get the static signature for a specific hash /// @param _hash The hash to get the static signature for /// @return address The address associated with the static signature /// @return timestamp The timestamp of the static signature function getStaticSignature( bytes32 _hash ) external view returns (address, uint256) { return _getStaticSignature(_hash); } /// @notice Set the static signature for a specific hash /// @param _hash The hash to set the static signature for /// @param _address The address to associate with the static signature /// @param _timestamp The timestamp of the static signature /// @dev Only callable by the wallet itself function setStaticSignature(bytes32 _hash, address _address, uint96 _timestamp) external onlySelf { _setStaticSignature(_hash, _address, _timestamp); emit StaticSignatureSet(_hash, _address, _timestamp); } /// @notice Update the image hash /// @param _imageHash The new image hash /// @dev Only callable by the wallet itself function updateImageHash( bytes32 _imageHash ) external virtual onlySelf { _updateImageHash(_imageHash); } function signatureValidation( Payload.Decoded memory _payload, bytes calldata _signature ) internal view virtual returns (bool isValid, bytes32 opHash) { // Read first bit to determine if static signature is used bytes1 signatureFlag = _signature[0]; if (signatureFlag & 0x80 == 0x80) { opHash = _payload.hash(); (address addr, uint256 timestamp) = _getStaticSignature(opHash); if (timestamp <= block.timestamp) { revert InvalidStaticSignatureExpired(opHash, timestamp); } if (addr != address(0) && addr != msg.sender) { revert InvalidStaticSignatureWrongCaller(opHash, msg.sender, addr); } return (true, opHash); } // Static signature is not used, recover and validate imageHash uint256 threshold; uint256 weight; bytes32 imageHash; (threshold, weight, imageHash,, opHash) = BaseSig.recover(_payload, _signature, false, address(0)); // Validate the weight if (weight < threshold) { revert InvalidSignatureWeight(threshold, weight); } isValid = _isValidImage(imageHash); } /// @inheritdoc ISapient function recoverSapientSignature( Payload.Decoded memory _payload, bytes calldata _signature ) external view returns (bytes32) { // Copy parent wallets + add caller at the end address[] memory parentWallets = new address[](_payload.parentWallets.length + 1); for (uint256 i = 0; i < _payload.parentWallets.length; i++) { parentWallets[i] = _payload.parentWallets[i]; } parentWallets[_payload.parentWallets.length] = msg.sender; _payload.parentWallets = parentWallets; (bool isValid,) = signatureValidation(_payload, _signature); if (!isValid) { revert InvalidSapientSignature(_payload, _signature); } return bytes32(uint256(1)); } /// @inheritdoc IERC1271 function isValidSignature(bytes32 _hash, bytes calldata _signature) external view returns (bytes4) { Payload.Decoded memory payload = Payload.fromDigest(_hash); (bool isValid,) = signatureValidation(payload, _signature); if (!isValid) { return bytes4(0); } return IERC1271_MAGIC_VALUE_HASH; } /// @inheritdoc IPartialAuth function recoverPartialSignature( Payload.Decoded memory _payload, bytes calldata _signature ) external view returns ( uint256 threshold, uint256 weight, bool isValidImage, bytes32 imageHash, uint256 checkpoint, bytes32 opHash ) { (threshold, weight, imageHash, checkpoint, opHash) = BaseSig.recover(_payload, _signature, false, address(0)); isValidImage = _isValidImage(imageHash); } }
BaseSig.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.27; import { LibBytes } from "../../utils/LibBytes.sol"; import { LibOptim } from "../../utils/LibOptim.sol"; import { Payload } from "../Payload.sol"; import { ICheckpointer, Snapshot } from "../interfaces/ICheckpointer.sol"; import { IERC1271, IERC1271_MAGIC_VALUE_HASH } from "../interfaces/IERC1271.sol"; import { ISapient, ISapientCompact } from "../interfaces/ISapient.sol"; using LibBytes for bytes; using Payload for Payload.Decoded; /// @title BaseSig /// @author Agustin Aguilar, Michael Standen, William Hua, Shun Kakinoki /// @notice Library for recovering signatures from the base-auth payload library BaseSig { uint256 internal constant FLAG_SIGNATURE_HASH = 0; uint256 internal constant FLAG_ADDRESS = 1; uint256 internal constant FLAG_SIGNATURE_ERC1271 = 2; uint256 internal constant FLAG_NODE = 3; uint256 internal constant FLAG_BRANCH = 4; uint256 internal constant FLAG_SUBDIGEST = 5; uint256 internal constant FLAG_NESTED = 6; uint256 internal constant FLAG_SIGNATURE_ETH_SIGN = 7; uint256 internal constant FLAG_SIGNATURE_ANY_ADDRESS_SUBDIGEST = 8; uint256 internal constant FLAG_SIGNATURE_SAPIENT = 9; uint256 internal constant FLAG_SIGNATURE_SAPIENT_COMPACT = 10; /// @notice Error thrown when the weight is too low for a chained signature error LowWeightChainedSignature(bytes _signature, uint256 _threshold, uint256 _weight); /// @notice Error thrown when the ERC1271 signature is invalid error InvalidERC1271Signature(bytes32 _opHash, address _signer, bytes _signature); /// @notice Error thrown when the checkpoint order is wrong error WrongChainedCheckpointOrder(uint256 _nextCheckpoint, uint256 _checkpoint); /// @notice Error thrown when the snapshot is unused error UnusedSnapshot(Snapshot _snapshot); /// @notice Error thrown when the signature flag is invalid error InvalidSignatureFlag(uint256 _flag); function _leafForAddressAndWeight(address _addr, uint256 _weight) internal pure returns (bytes32) { return keccak256(abi.encodePacked("Sequence signer:\n", _addr, _weight)); } function _leafForNested(bytes32 _node, uint256 _threshold, uint256 _weight) internal pure returns (bytes32) { return keccak256(abi.encodePacked("Sequence nested config:\n", _node, _threshold, _weight)); } function _leafForSapient(address _addr, uint256 _weight, bytes32 _imageHash) internal pure returns (bytes32) { return keccak256(abi.encodePacked("Sequence sapient config:\n", _addr, _weight, _imageHash)); } function _leafForHardcodedSubdigest( bytes32 _subdigest ) internal pure returns (bytes32) { return keccak256(abi.encodePacked("Sequence static digest:\n", _subdigest)); } function _leafForAnyAddressSubdigest( bytes32 _anyAddressSubdigest ) internal pure returns (bytes32) { return keccak256(abi.encodePacked("Sequence any address subdigest:\n", _anyAddressSubdigest)); } function recover( Payload.Decoded memory _payload, bytes calldata _signature, bool _ignoreCheckpointer, address _checkpointer ) internal view returns (uint256 threshold, uint256 weight, bytes32 imageHash, uint256 checkpoint, bytes32 opHash) { // First byte is the signature flag (uint256 signatureFlag, uint256 rindex) = _signature.readFirstUint8(); // The possible flags are: // - 0000 00XX (bits [1..0]): signature type (00 = normal, 01/11 = chained, 10 = no chain id) // - 000X XX00 (bits [4..2]): checkpoint size (00 = 0 bytes, 001 = 1 byte, 010 = 2 bytes...) // - 00X0 0000 (bit [5]): threshold size (0 = 1 byte, 1 = 2 bytes) // - 0X00 0000 (bit [6]): set if imageHash checkpointer is used // - X000 0000 (bit [7]): reserved by base-auth Snapshot memory snapshot; // Recover the imageHash checkpointer if any // but checkpointer passed as argument takes precedence // since it can be defined by the chained signatures if (signatureFlag & 0x40 == 0x40 && _checkpointer == address(0)) { // Override the checkpointer // not ideal, but we don't have much room in the stack (_checkpointer, rindex) = _signature.readAddress(rindex); if (!_ignoreCheckpointer) { // Next 3 bytes determine the checkpointer data size uint256 checkpointerDataSize; (checkpointerDataSize, rindex) = _signature.readUint24(rindex); // Read the checkpointer data bytes memory checkpointerData = _signature[rindex:rindex + checkpointerDataSize]; // Call the middleware snapshot = ICheckpointer(_checkpointer).snapshotFor(address(this), checkpointerData); rindex += checkpointerDataSize; } } // If signature type is 01 or 11 we do a chained signature if (signatureFlag & 0x01 == 0x01) { return recoverChained(_payload, _checkpointer, snapshot, _signature[rindex:]); } // If the signature type is 10 we do a no chain id signature _payload.noChainId = signatureFlag & 0x02 == 0x02; { // Recover the checkpoint using the size defined by the flag uint256 checkpointSize = (signatureFlag & 0x1c) >> 2; (checkpoint, rindex) = _signature.readUintX(rindex, checkpointSize); } // Recover the threshold, using the flag for the size { uint256 thresholdSize = ((signatureFlag & 0x20) >> 5) + 1; (threshold, rindex) = _signature.readUintX(rindex, thresholdSize); } // Recover the tree opHash = _payload.hash(); (weight, imageHash) = recoverBranch(_payload, opHash, _signature[rindex:]); imageHash = LibOptim.fkeccak256(imageHash, bytes32(threshold)); imageHash = LibOptim.fkeccak256(imageHash, bytes32(checkpoint)); imageHash = LibOptim.fkeccak256(imageHash, bytes32(uint256(uint160(_checkpointer)))); // If the snapshot is used, either the imageHash must match // or the checkpoint must be greater than the snapshot checkpoint if (snapshot.imageHash != bytes32(0) && snapshot.imageHash != imageHash && checkpoint <= snapshot.checkpoint) { revert UnusedSnapshot(snapshot); } } function recoverChained( Payload.Decoded memory _payload, address _checkpointer, Snapshot memory _snapshot, bytes calldata _signature ) internal view returns (uint256 threshold, uint256 weight, bytes32 imageHash, uint256 checkpoint, bytes32 opHash) { Payload.Decoded memory linkedPayload; linkedPayload.kind = Payload.KIND_CONFIG_UPDATE; uint256 rindex; uint256 prevCheckpoint = type(uint256).max; while (rindex < _signature.length) { uint256 nrindex; { uint256 sigSize; (sigSize, rindex) = _signature.readUint24(rindex); nrindex = sigSize + rindex; } address checkpointer = nrindex == _signature.length ? _checkpointer : address(0); if (prevCheckpoint == type(uint256).max) { (threshold, weight, imageHash, checkpoint, opHash) = recover(_payload, _signature[rindex:nrindex], true, checkpointer); } else { (threshold, weight, imageHash, checkpoint,) = recover(linkedPayload, _signature[rindex:nrindex], true, checkpointer); } if (weight < threshold) { revert LowWeightChainedSignature(_signature[rindex:nrindex], threshold, weight); } rindex = nrindex; if (_snapshot.imageHash == imageHash) { _snapshot.imageHash = bytes32(0); } if (checkpoint >= prevCheckpoint) { revert WrongChainedCheckpointOrder(checkpoint, prevCheckpoint); } linkedPayload.imageHash = imageHash; prevCheckpoint = checkpoint; } if (_snapshot.imageHash != bytes32(0) && checkpoint <= _snapshot.checkpoint) { revert UnusedSnapshot(_snapshot); } } function recoverBranch( Payload.Decoded memory _payload, bytes32 _opHash, bytes calldata _signature ) internal view returns (uint256 weight, bytes32 root) { unchecked { uint256 rindex; // Iterate until the image is completed while (rindex < _signature.length) { // The first byte is half flag (the top nibble) // and the second set of 4 bits can freely be used by the part // Read next item type uint256 firstByte; (firstByte, rindex) = _signature.readUint8(rindex); // The top 4 bits are the flag uint256 flag = (firstByte & 0xf0) >> 4; // Signature hash (0x00) if (flag == FLAG_SIGNATURE_HASH) { // Free bits layout: // - bits [3..0]: Weight (0000 = dynamic, 0001 = 1, ..., 1111 = 15) // We read 64 bytes for an ERC-2098 compact signature (r, yParityAndS). // The top bit of yParityAndS is yParity, the remaining 255 bits are s. uint8 addrWeight = uint8(firstByte & 0x0f); if (addrWeight == 0) { (addrWeight, rindex) = _signature.readUint8(rindex); } bytes32 r; bytes32 s; uint8 v; (r, s, v, rindex) = _signature.readRSVCompact(rindex); address addr = ecrecover(_opHash, v, r, s); weight += addrWeight; bytes32 node = _leafForAddressAndWeight(addr, addrWeight); root = root != bytes32(0) ? LibOptim.fkeccak256(root, node) : node; continue; } // Address (0x01) (without signature) if (flag == FLAG_ADDRESS) { // Free bits layout: // - bits [3..0]: Weight (0000 = dynamic, 0001 = 1, 0010 = 2, ...) // Read weight uint8 addrWeight = uint8(firstByte & 0x0f); if (addrWeight == 0) { (addrWeight, rindex) = _signature.readUint8(rindex); } // Read address address addr; (addr, rindex) = _signature.readAddress(rindex); // Compute the merkle root WITHOUT adding the weight bytes32 node = _leafForAddressAndWeight(addr, addrWeight); root = root != bytes32(0) ? LibOptim.fkeccak256(root, node) : node; continue; } // Signature ERC1271 (0x02) if (flag == FLAG_SIGNATURE_ERC1271) { // Free bits layout: // - XX00 : Signature size size (00 = 0 byte, 01 = 1 byte, 10 = 2 bytes, 11 = 3 bytes) // - 00XX : Weight (00 = dynamic, 01 = 1, 10 = 2, 11 = 3) // Read weight uint8 addrWeight = uint8(firstByte & 0x03); if (addrWeight == 0) { (addrWeight, rindex) = _signature.readUint8(rindex); } // Read signer address addr; (addr, rindex) = _signature.readAddress(rindex); // Read signature size uint256 sizeSize = uint8(firstByte & 0x0c) >> 2; uint256 size; (size, rindex) = _signature.readUintX(rindex, sizeSize); // Read dynamic size signature uint256 nrindex = rindex + size; // Call the ERC1271 contract to check if the signature is valid if (IERC1271(addr).isValidSignature(_opHash, _signature[rindex:nrindex]) != IERC1271_MAGIC_VALUE_HASH) { revert InvalidERC1271Signature(_opHash, addr, _signature[rindex:nrindex]); } rindex = nrindex; // Add the weight and compute the merkle root weight += addrWeight; bytes32 node = _leafForAddressAndWeight(addr, addrWeight); root = root != bytes32(0) ? LibOptim.fkeccak256(root, node) : node; continue; } // Node (0x03) if (flag == FLAG_NODE) { // Free bits left unused // Read node hash bytes32 node; (node, rindex) = _signature.readBytes32(rindex); root = root != bytes32(0) ? LibOptim.fkeccak256(root, node) : node; continue; } // Branch (0x04) if (flag == FLAG_BRANCH) { // Free bits layout: // - XXXX : Size size (0000 = 0 byte, 0001 = 1 byte, 0010 = 2 bytes, ...) // Read size uint256 sizeSize = uint8(firstByte & 0x0f); uint256 size; (size, rindex) = _signature.readUintX(rindex, sizeSize); // Enter a branch of the signature merkle tree uint256 nrindex = rindex + size; (uint256 nweight, bytes32 node) = recoverBranch(_payload, _opHash, _signature[rindex:nrindex]); rindex = nrindex; weight += nweight; root = LibOptim.fkeccak256(root, node); continue; } // Nested (0x06) if (flag == FLAG_NESTED) { // Unused free bits: // - XX00 : Weight (00 = dynamic, 01 = 1, 10 = 2, 11 = 3) // - 00XX : Threshold (00 = dynamic, 01 = 1, 10 = 2, 11 = 3) // Enter a branch of the signature merkle tree // but with an internal threshold and an external fixed weight uint256 externalWeight = uint8(firstByte & 0x0c) >> 2; if (externalWeight == 0) { (externalWeight, rindex) = _signature.readUint8(rindex); } uint256 internalThreshold = uint8(firstByte & 0x03); if (internalThreshold == 0) { (internalThreshold, rindex) = _signature.readUint16(rindex); } uint256 size; (size, rindex) = _signature.readUint24(rindex); uint256 nrindex = rindex + size; (uint256 internalWeight, bytes32 internalRoot) = recoverBranch(_payload, _opHash, _signature[rindex:nrindex]); rindex = nrindex; if (internalWeight >= internalThreshold) { weight += externalWeight; } bytes32 node = _leafForNested(internalRoot, internalThreshold, externalWeight); root = root != bytes32(0) ? LibOptim.fkeccak256(root, node) : node; continue; } // Subdigest (0x05) if (flag == FLAG_SUBDIGEST) { // Free bits left unused // A hardcoded always accepted digest // it pushes the weight to the maximum bytes32 hardcoded; (hardcoded, rindex) = _signature.readBytes32(rindex); if (hardcoded == _opHash) { weight = type(uint256).max; } bytes32 node = _leafForHardcodedSubdigest(hardcoded); root = root != bytes32(0) ? LibOptim.fkeccak256(root, node) : node; continue; } // Signature ETH Sign (0x07) if (flag == FLAG_SIGNATURE_ETH_SIGN) { // Free bits layout: // - bits [3..0]: Weight (0000 = dynamic, 0001 = 1, ..., 1111 = 15) // We read 64 bytes for an ERC-2098 compact signature (r, yParityAndS). // The top bit of yParityAndS is yParity, the remaining 255 bits are s. uint8 addrWeight = uint8(firstByte & 0x0f); if (addrWeight == 0) { (addrWeight, rindex) = _signature.readUint8(rindex); } bytes32 r; bytes32 s; uint8 v; (r, s, v, rindex) = _signature.readRSVCompact(rindex); address addr = ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _opHash)), v, r, s); weight += addrWeight; bytes32 node = _leafForAddressAndWeight(addr, addrWeight); root = root != bytes32(0) ? LibOptim.fkeccak256(root, node) : node; continue; } // Signature Any address subdigest (0x08) // similar to subdigest, but allows for counter-factual payloads if (flag == FLAG_SIGNATURE_ANY_ADDRESS_SUBDIGEST) { // Free bits left unused // A hardcoded always accepted digest // it pushes the weight to the maximum bytes32 hardcoded; (hardcoded, rindex) = _signature.readBytes32(rindex); bytes32 anyAddressOpHash = _payload.hashFor(address(0)); if (hardcoded == anyAddressOpHash) { weight = type(uint256).max; } bytes32 node = _leafForAnyAddressSubdigest(hardcoded); root = root != bytes32(0) ? LibOptim.fkeccak256(root, node) : node; continue; } // Signature Sapient (0x09) if (flag == FLAG_SIGNATURE_SAPIENT) { // Free bits layout: // - XX00 : Signature size size (00 = 0 byte, 01 = 1 byte, 10 = 2 bytes, 11 = 3 bytes) // - 00XX : Weight (00 = dynamic, 01 = 1, 10 = 2, 11 = 3) // Read signer and weight uint8 addrWeight = uint8(firstByte & 0x03); if (addrWeight == 0) { (addrWeight, rindex) = _signature.readUint8(rindex); } address addr; (addr, rindex) = _signature.readAddress(rindex); // Read signature size uint256 size; { uint256 sizeSize = uint8(firstByte & 0x0c) >> 2; (size, rindex) = _signature.readUintX(rindex, sizeSize); } // Read dynamic size signature uint256 nrindex = rindex + size; // Call the ERC1271 contract to check if the signature is valid bytes32 sapientImageHash = ISapient(addr).recoverSapientSignature(_payload, _signature[rindex:nrindex]); rindex = nrindex; // Add the weight and compute the merkle root weight += addrWeight; bytes32 node = _leafForSapient(addr, addrWeight, sapientImageHash); root = root != bytes32(0) ? LibOptim.fkeccak256(root, node) : node; continue; } // Signature Sapient Compact (0x0A) if (flag == FLAG_SIGNATURE_SAPIENT_COMPACT) { // Free bits layout: // - XX00 : Signature size size (00 = 0 byte, 01 = 1 byte, 10 = 2 bytes, 11 = 3 bytes) // - 00XX : Weight (00 = dynamic, 01 = 1, 10 = 2, 11 = 3) // Read signer and weight uint8 addrWeight = uint8(firstByte & 0x03); if (addrWeight == 0) { (addrWeight, rindex) = _signature.readUint8(rindex); } address addr; (addr, rindex) = _signature.readAddress(rindex); // Read signature size uint256 sizeSize = uint8(firstByte & 0x0c) >> 2; uint256 size; (size, rindex) = _signature.readUintX(rindex, sizeSize); // Read dynamic size signature uint256 nrindex = rindex + size; // Call the Sapient contract to check if the signature is valid bytes32 sapientImageHash = ISapientCompact(addr).recoverSapientSignatureCompact(_opHash, _signature[rindex:nrindex]); rindex = nrindex; // Add the weight and compute the merkle root weight += addrWeight; bytes32 node = _leafForSapient(addr, addrWeight, sapientImageHash); root = root != bytes32(0) ? LibOptim.fkeccak256(root, node) : node; continue; } revert InvalidSignatureFlag(flag); } } } }
SelfAuth.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.27; /// @title SelfAuth /// @author Agustin Aguilar, Michael Standen /// @notice Modifier for checking if the caller is the same as the contract abstract contract SelfAuth { /// @notice Error thrown when the caller is not the same as the contract error OnlySelf(address _sender); modifier onlySelf() { if (msg.sender != address(this)) { revert OnlySelf(msg.sender); } _; } }
Stage1Auth.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.27; import { Wallet } from "../../Wallet.sol"; import { Implementation } from "../Implementation.sol"; import { Storage } from "../Storage.sol"; import { BaseAuth } from "./BaseAuth.sol"; /// @title Stage1Auth /// @author Agustin Aguilar /// @notice Stage 1 auth contract contract Stage1Auth is BaseAuth, Implementation { /// @notice Error thrown when the image hash is zero error ImageHashIsZero(); /// @notice Error thrown when the signature type is invalid error InvalidSignatureType(bytes1 _type); /// @notice Initialization code hash bytes32 public immutable INIT_CODE_HASH; /// @notice Factory address address public immutable FACTORY; /// @notice Stage 2 implementation address address public immutable STAGE_2_IMPLEMENTATION; /// @dev keccak256("org.arcadeum.module.auth.upgradable.image.hash") bytes32 internal constant IMAGE_HASH_KEY = bytes32(0xea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8); /// @notice Emitted when the image hash is updated event ImageHashUpdated(bytes32 newImageHash); constructor(address _factory, address _stage2) { // Build init code hash of the deployed wallets using that module bytes32 initCodeHash = keccak256(abi.encodePacked(Wallet.creationCode, uint256(uint160(address(this))))); INIT_CODE_HASH = initCodeHash; FACTORY = _factory; STAGE_2_IMPLEMENTATION = _stage2; } function _updateImageHash( bytes32 _imageHash ) internal virtual override { // Update imageHash in storage if (_imageHash == bytes32(0)) { revert ImageHashIsZero(); } Storage.writeBytes32(IMAGE_HASH_KEY, _imageHash); emit ImageHashUpdated(_imageHash); // Update wallet implementation to stage2 version _updateImplementation(STAGE_2_IMPLEMENTATION); } function _isValidImage( bytes32 _imageHash ) internal view virtual override returns (bool) { return address(uint160(uint256(keccak256(abi.encodePacked(hex"ff", FACTORY, _imageHash, INIT_CODE_HASH))))) == address(this); } }
Stage2Auth.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.27; import { Wallet } from "../../Wallet.sol"; import { Implementation } from "../Implementation.sol"; import { Storage } from "../Storage.sol"; import { BaseAuth } from "./BaseAuth.sol"; /// @title Stage2Auth /// @author Agustin Aguilar /// @notice Stage 2 auth contract contract Stage2Auth is BaseAuth, Implementation { /// @dev keccak256("org.arcadeum.module.auth.upgradable.image.hash") bytes32 internal constant IMAGE_HASH_KEY = bytes32(0xea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8); /// @notice Emitted when the image hash is updated event ImageHashUpdated(bytes32 newImageHash); /// @notice Error thrown when the image hash is zero error ImageHashIsZero(); /// @notice Get the image hash /// @return imageHash The image hash function imageHash() external view virtual returns (bytes32) { return Storage.readBytes32(IMAGE_HASH_KEY); } function _isValidImage( bytes32 _imageHash ) internal view virtual override returns (bool) { return _imageHash != bytes32(0) && _imageHash == Storage.readBytes32(IMAGE_HASH_KEY); } function _updateImageHash( bytes32 _imageHash ) internal virtual override { if (_imageHash == bytes32(0)) { revert ImageHashIsZero(); } Storage.writeBytes32(IMAGE_HASH_KEY, _imageHash); emit ImageHashUpdated(_imageHash); } }
IAccount.sol
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; /** * User Operation struct * @param sender - The sender account of this request. * @param nonce - Unique value the sender uses to verify it is not a replay. * @param initCode - If set, the account contract will be created by this constructor * @param callData - The method call to execute on this account. * @param accountGasLimits - Packed gas limits for validateUserOp and gas limit passed to the callData method call. * @param preVerificationGas - Gas not calculated by the handleOps method, but added to the gas paid. * Covers batch overhead. * @param gasFees - packed gas fields maxPriorityFeePerGas and maxFeePerGas - Same as EIP-1559 gas parameters. * @param paymasterAndData - If set, this field holds the paymaster address, verification gas limit, postOp gas limit and paymaster-specific extra data * The paymaster will pay for the transaction instead of the sender. * @param signature - Sender-verified signature over the entire request, the EntryPoint address and the chain ID. */ struct PackedUserOperation { address sender; uint256 nonce; bytes initCode; bytes callData; bytes32 accountGasLimits; uint256 preVerificationGas; bytes32 gasFees; bytes paymasterAndData; bytes signature; } interface IAccount { /** * Validate user's signature and nonce * the entryPoint will make the call to the recipient only if this validation call returns successfully. * signature failure should be reported by returning SIG_VALIDATION_FAILED (1). * This allows making a "simulation call" without a valid signature * Other failures (e.g. nonce mismatch, or invalid signature format) should still revert to signal failure. * * @dev Must validate caller is the entryPoint. * Must validate the signature and nonce * @param userOp - The operation that is about to be executed. * @param userOpHash - Hash of the user's request data. can be used as the basis for signature. * @param missingAccountFunds - Missing funds on the account's deposit in the entrypoint. * This is the minimum amount to transfer to the sender(entryPoint) to be * able to make the call. The excess is left as a deposit in the entrypoint * for future calls. Can be withdrawn anytime using "entryPoint.withdrawTo()". * In case there is a paymaster in the request (or the current deposit is high * enough), this value will be zero. * @return validationData - Packaged ValidationData structure. use `_packValidationData` and * `_unpackValidationData` to encode and decode. * <20-byte> aggregatorOrSigFail - 0 for valid signature, 1 to mark signature failure, * otherwise, an address of an "aggregator" contract. * <6-byte> validUntil - Last timestamp this operation is valid at, or 0 for "indefinitely" * <6-byte> validAfter - First timestamp this operation is valid * If an account doesn't use time-range, it is enough to * return SIG_VALIDATION_FAILED value (1) for signature failure. * Note that the validation code cannot use block.timestamp (or block.number) directly. */ function validateUserOp( PackedUserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds ) external returns (uint256 validationData); }
ICheckpointer.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.27; import { Payload } from "../Payload.sol"; /// @notice Snapshot for a specific wallet /// @param imageHash Image hash of the wallet /// @param checkpoint Checkpoint identifier struct Snapshot { bytes32 imageHash; uint256 checkpoint; } /// @title ICheckpointer /// @author Agustin Aguilar /// @notice Interface for the checkpointer module interface ICheckpointer { /// @notice Get the snapshot for a specific wallet /// @param _wallet The wallet address /// @param _proof The proof /// @return snapshot The snapshot function snapshotFor(address _wallet, bytes calldata _proof) external view returns (Snapshot memory snapshot); }
IDelegatedExtension.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.27; /// @title IDelegatedExtension /// @author Agustin Aguilar /// @notice Interface for the delegated extension module interface IDelegatedExtension { /// @notice Handle a sequence delegate call /// @param _opHash The operation hash /// @param _startingGas The starting gas /// @param _index The index /// @param _numCalls The number of calls /// @param _space The space /// @param _data The data function handleSequenceDelegateCall( bytes32 _opHash, uint256 _startingGas, uint256 _index, uint256 _numCalls, uint256 _space, bytes calldata _data ) external; }
IERC1155Receiver.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; /// @title IERC1155Receiver /// @notice Interface for the ERC1155 receiver module interface IERC1155Receiver { /// @notice Called when a single ERC1155 token is transferred to this contract /// @param operator The address which initiated the transfer /// @param from The address which previously owned the token /// @param tokenId The ID of the token being transferred /// @param value The amount of token being transferred /// @param data Additional data with no specified format /// @return magicValue On a success, the selector of the function that was called function onERC1155Received( address operator, address from, uint256 tokenId, uint256 value, bytes calldata data ) external returns (bytes4 magicValue); /// @notice Called when multiple ERC1155 tokens are transferred to this contract /// @param operator The address which initiated the transfer /// @param from The address which previously owned the token /// @param ids The list of token IDs being transferred /// @param values The amounts of each token being transferred /// @param data Additional data with no specified format /// @return magicValue On a success, the selector of the function that was called function onERC1155BatchReceived( address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data ) external returns (bytes4 magicValue); }
IERC1271.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; bytes4 constant IERC1271_MAGIC_VALUE_HASH = 0x1626ba7e; bytes4 constant IERC1271_MAGIC_VALUE_BYTES = 0x20c13b0b; /// @title IERC1271 /// @notice Interface for ERC1271 interface IERC1271 { /// @notice Verifies whether the provided signature is valid with respect to the provided hash /// @dev MUST return the correct magic value if the signature provided is valid for the provided hash /// > The bytes4 magic value to return when signature is valid is 0x1626ba7e : bytes4(keccak256("isValidSignature(bytes32,bytes)") /// > This function MAY modify Ethereum's state /// @param _hash keccak256 hash that was signed /// @param _signature Signature byte array associated with _data /// @return magicValue Magic value 0x1626ba7e if the signature is valid and 0x0 otherwise function isValidSignature(bytes32 _hash, bytes calldata _signature) external view returns (bytes4 magicValue); } /// @title IERC1271Data /// @notice Deprecated interface for ERC1271 using bytes instead of bytes32 interface IERC1271Data { /// @notice Verifies whether the provided signature is valid with respect to the provided hash /// @dev MUST return the correct magic value if the signature provided is valid for the provided hash /// > The bytes4 magic value to return when signature is valid is 0x20c13b0b : bytes4(keccak256("isValidSignature(bytes,bytes)") /// > This function MAY modify Ethereum's state /// @param _data Data that was signed /// @param _signature Signature byte array associated with _data /// @return magicValue Magic value 0x20c13b0b if the signature is valid and 0x0 otherwise function isValidSignature(bytes calldata _data, bytes calldata _signature) external view returns (bytes4 magicValue); }
IERC223Receiver.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; /// @title IERC223Receiver /// @notice Interface for the ERC223 receiver module interface IERC223Receiver { /// @notice Called when ERC223 tokens are received by this contract /// @param from The address which previously owned the tokens /// @param value The amount of tokens being transferred /// @param data Transaction metadata /// @return signature The signature of the function to be called function tokenReceived(address from, uint256 value, bytes calldata data) external returns (bytes4 signature); }
IERC721Receiver.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; /// @title IERC721Receiver /// @notice Interface for the ERC721 receiver module interface IERC721Receiver { /// @notice Called when a single ERC721 token is transferred to this contract /// @param operator The address which initiated the transfer /// @param from The address which previously owned the token /// @param tokenId The ID of the token being transferred /// @param data Additional data with no specified format /// @return magicValue On a success, the selector of the function that was called function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4 magicValue); }
IERC777Receiver.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; /// @title IERC777Receiver /// @notice Interface for the ERC777 receiver module interface IERC777Receiver { /// @notice Called when tokens are received by this contract /// @param operator The address which initiated the transfer /// @param from The address which previously owned the tokens /// @param to The address which is receiving the tokens /// @param amount The amount of tokens being transferred /// @param data Additional data with no specified format /// @param operatorData Additional data with no specified format function tokensReceived( address operator, address from, address to, uint256 amount, bytes calldata data, bytes calldata operatorData ) external; }
IEntryPoint.sol
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; interface IEntryPoint { function depositTo( address account ) external payable; }
IPartialAuth.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.27; import { Payload } from "../Payload.sol"; /// @title IPartialAuth /// @author Agustin Aguilar /// @notice Interface for the partial auth module interface IPartialAuth { /// @notice Recover the partial signature /// @param _payload The payload /// @param _signature The signature to recover /// @return threshold The signature threshold /// @return weight The derived weight /// @return isValidImage Whether the image hash is valid /// @return imageHash The derived image hash /// @return checkpoint The checkpoint identifier /// @return opHash The hash of the payload function recoverPartialSignature( Payload.Decoded calldata _payload, bytes calldata _signature ) external view returns ( uint256 threshold, uint256 weight, bool isValidImage, bytes32 imageHash, uint256 checkpoint, bytes32 opHash ); }
ISapient.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.27; import { Payload } from "../Payload.sol"; /// @title ISapient /// @author Agustin Aguilar, Michael Standen /// @notice Sapient signers take an explicit payload and return their own "imageHash" as result /// @dev The consumer of this signer must validate if the imageHash is valid or not, for the desired configuration interface ISapient { /// @notice Recovers the image hash of a given signature /// @param payload The payload to recover the signature from /// @param signature The signature to recover the image hash from /// @return imageHash The recovered image hash function recoverSapientSignature( Payload.Decoded calldata payload, bytes calldata signature ) external view returns (bytes32 imageHash); } /// @title ISapientCompact /// @author Agustin Aguilar, Michael Standen /// @notice Sapient signers take a compacted payload and return their own "imageHash" as result /// @dev The consumer of this signer must validate if the imageHash is valid or not, for the desired configuration interface ISapientCompact { /// @notice Recovers the image hash of a given signature, using a hashed payload /// @param digest The digest of the payload /// @param signature The signature to recover the image hash from /// @return imageHash The recovered image hash function recoverSapientSignatureCompact( bytes32 digest, bytes calldata signature ) external view returns (bytes32 imageHash); }
LibBytes.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; /// @title Library for reading data from bytes arrays /// @author Agustin Aguilar (aa@horizon.io), Michael Standen (mstan@horizon.io) /// @notice This library contains functions for reading data from bytes arrays. /// @dev These functions do not check if the input index is within the bounds of the data array. /// @dev Reading out of bounds may return dirty values. library LibBytes { function readFirstUint8( bytes calldata _data ) internal pure returns (uint8 a, uint256 newPointer) { assembly { let word := calldataload(_data.offset) a := shr(248, word) newPointer := 1 } } function readUint8(bytes calldata _data, uint256 _index) internal pure returns (uint8 a, uint256 newPointer) { assembly { let word := calldataload(add(_index, _data.offset)) a := shr(248, word) newPointer := add(_index, 1) } } function readUint16(bytes calldata _data, uint256 _index) internal pure returns (uint16 a, uint256 newPointer) { assembly { let word := calldataload(add(_index, _data.offset)) a := shr(240, word) newPointer := add(_index, 2) } } function readUint24(bytes calldata _data, uint256 _index) internal pure returns (uint24 a, uint256 newPointer) { assembly { let word := calldataload(add(_index, _data.offset)) a := shr(232, word) newPointer := add(_index, 3) } } function readUint64(bytes calldata _data, uint256 _index) internal pure returns (uint64 a, uint256 newPointer) { assembly { let word := calldataload(add(_index, _data.offset)) a := shr(192, word) newPointer := add(_index, 8) } } function readUint160(bytes calldata _data, uint256 _index) internal pure returns (uint160 a, uint256 newPointer) { assembly { let word := calldataload(add(_index, _data.offset)) a := shr(96, word) newPointer := add(_index, 20) } } function readUint256(bytes calldata _data, uint256 _index) internal pure returns (uint256 a, uint256 newPointer) { assembly { a := calldataload(add(_index, _data.offset)) newPointer := add(_index, 32) } } function readUintX( bytes calldata _data, uint256 _index, uint256 _length ) internal pure returns (uint256 a, uint256 newPointer) { assembly { let word := calldataload(add(_index, _data.offset)) let shift := sub(256, mul(_length, 8)) a := and(shr(shift, word), sub(shl(mul(8, _length), 1), 1)) newPointer := add(_index, _length) } } function readBytes4(bytes calldata _data, uint256 _pointer) internal pure returns (bytes4 a, uint256 newPointer) { assembly { let word := calldataload(add(_pointer, _data.offset)) a := and(word, 0xffffffff00000000000000000000000000000000000000000000000000000000) newPointer := add(_pointer, 4) } } function readBytes32(bytes calldata _data, uint256 _pointer) internal pure returns (bytes32 a, uint256 newPointer) { assembly { a := calldataload(add(_pointer, _data.offset)) newPointer := add(_pointer, 32) } } function readAddress(bytes calldata _data, uint256 _index) internal pure returns (address a, uint256 newPointer) { assembly { let word := calldataload(add(_index, _data.offset)) a := and(shr(96, word), 0xffffffffffffffffffffffffffffffffffffffff) newPointer := add(_index, 20) } } /// @dev ERC-2098 Compact Signature function readRSVCompact( bytes calldata _data, uint256 _index ) internal pure returns (bytes32 r, bytes32 s, uint8 v, uint256 newPointer) { uint256 yParityAndS; assembly { r := calldataload(add(_index, _data.offset)) yParityAndS := calldataload(add(_index, add(_data.offset, 32))) newPointer := add(_index, 64) } uint256 yParity = uint256(yParityAndS >> 255); s = bytes32(uint256(yParityAndS) & ((1 << 255) - 1)); v = uint8(yParity) + 27; } }
LibOptim.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; /// @title LibOptim /// @author Agustin Aguilar /// @notice Library for optimized EVM operations library LibOptim { /** * @notice Computes the keccak256 hash of two 32-byte inputs. * @dev It uses only scratch memory space. * @param _a The first 32 bytes of the hash. * @param _b The second 32 bytes of the hash. * @return c The keccak256 hash of the two 32-byte inputs. */ function fkeccak256(bytes32 _a, bytes32 _b) internal pure returns (bytes32 c) { assembly { mstore(0, _a) mstore(32, _b) c := keccak256(0, 64) } } /** * @notice Returns the return data from the last call. * @return r The return data from the last call. */ function returnData() internal pure returns (bytes memory r) { assembly { let size := returndatasize() r := mload(0x40) let start := add(r, 32) mstore(0x40, add(start, size)) mstore(r, size) returndatacopy(start, 0, size) } } /** * @notice Calls another contract with the given parameters. * @dev This method doesn't increase the memory pointer. * @param _to The address of the contract to call. * @param _val The value to send to the contract. * @param _gas The amount of gas to provide for the call. * @param _data The data to send to the contract. * @return r The success status of the call. */ function call(address _to, uint256 _val, uint256 _gas, bytes memory _data) internal returns (bool r) { assembly { r := call(_gas, _to, _val, add(_data, 32), mload(_data), 0, 0) } } /** * @notice Calls another contract with the given parameters, using delegatecall. * @dev This method doesn't increase the memory pointer. * @param _to The address of the contract to call. * @param _gas The amount of gas to provide for the call. * @param _data The data to send to the contract. * @return r The success status of the call. */ function delegatecall(address _to, uint256 _gas, bytes memory _data) internal returns (bool r) { assembly { r := delegatecall(_gas, _to, add(_data, 32), mload(_data), 0, 0) } } }
Gas Token: