Address: 0x4f8ce847174b32cBe21b3887Be894e0DEBC28952
Balance (XRP): 0 XRP
Bytecode: 0x60806040526004361061019a5760003560e01c8063853c5068116100e1578063a38cef191161008a578063bc197c8111610064578063bc197c81146105cd578063c71f1f9614610615578063d0748f711461062a578063f23a6e611461064a576101a1565b8063a38cef1914610578578063affed0e014610598578063b93ea7ad146105ad576101a1565b80638efa6441116100bb5780638efa64411461052e57806390042baf146105505780639bd58b1614610563576101a1565b8063853c5068146104a65780638976c44c146104ee5780638c3f55631461050e576101a1565b8063295614261161014357806357c56d6b1161011d57806357c56d6b1461043257806361c2926c146104665780637a9a162814610486576101a1565b806329561426146103cf5780634fcf3eca146103ef57806351605d801461040f576101a1565b80631626ba7e116101745780631626ba7e1461034a5780631a9b23371461036a57806320c13b0b146103af576101a1565b806301ffc9a71461027f578063025b22bc146102b4578063150b7a02146102d4576101a1565b366101a157005b6004361061027d5760006101d86000357fffffffff0000000000000000000000000000000000000000000000000000000016610690565b905073ffffffffffffffffffffffffffffffffffffffff81161561027b576000808273ffffffffffffffffffffffffffffffffffffffff16600036604051610221929190612ada565b600060405180830381855af49150503d806000811461025c576040519150601f19603f3d011682016040523d82523d6000602084013e610261565b606091505b50915091508161027357805160208201fd5b805160208201f35b505b005b34801561028b57600080fd5b5061029f61029a366004612b18565b6106e4565b60405190151581526020015b60405180910390f35b3480156102c057600080fd5b5061027d6102cf366004612b5e565b6106ef565b3480156102e057600080fd5b506103196102ef366004612bc2565b7f150b7a020000000000000000000000000000000000000000000000000000000095945050505050565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020016102ab565b34801561035657600080fd5b50610319610365366004612c31565b610741565b34801561037657600080fd5b5061038a610385366004612b18565b610758565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102ab565b3480156103bb57600080fd5b506103196103ca366004612c7d565b610763565b3480156103db57600080fd5b5061027d6103ea366004612ce9565b61077c565b3480156103fb57600080fd5b5061027d61040a366004612b18565b6107c6565b34801561041b57600080fd5b5061042461088e565b6040519081526020016102ab565b34801561043e57600080fd5b506104247f8713a7c4465f6fbee2b6e9d6646d1d9f83fec929edfc4baf661f3c865bdd04d181565b34801561047257600080fd5b5061027d610481366004612d47565b6108bd565b34801561049257600080fd5b5061027d6104a1366004612d89565b610943565b3480156104b257600080fd5b506104c66104c1366004612c31565b610a48565b604080519586526020860194909452928401919091526060830152608082015260a0016102ab565b3480156104fa57600080fd5b5061027d610509366004612ce9565b610c10565b34801561051a57600080fd5b50610424610529366004612ce9565b610ce8565b34801561053a57600080fd5b50610543610d14565b6040516102ab9190612e60565b61038a61055e366004612ea2565b610d95565b34801561056f57600080fd5b50610424610e7f565b34801561058457600080fd5b5061027d610593366004612ce9565b610ea9565b3480156105a457600080fd5b50610424610ef3565b3480156105b957600080fd5b5061027d6105c8366004612f71565b610eff565b3480156105d957600080fd5b506103196105e8366004612fa6565b7fbc197c810000000000000000000000000000000000000000000000000000000098975050505050505050565b34801561062157600080fd5b50610424610fca565b34801561063657600080fd5b5061027d610645366004613061565b610ff4565b34801561065657600080fd5b50610319610665366004613083565b7ff23a6e61000000000000000000000000000000000000000000000000000000009695505050505050565b60006106de7fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1207fffffffff000000000000000000000000000000000000000000000000000000008416611047565b92915050565b60006106de826110a5565b333014610735576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044015b60405180910390fd5b61073e81611101565b50565b600061074e8484846111b5565b90505b9392505050565b60006106de82610690565b600061077185858585611200565b90505b949350505050565b3330146107bd576040517fe125889400000000000000000000000000000000000000000000000000000000815233600482015230602482015260440161072c565b61073e81611263565b333014610807576040517fe125889400000000000000000000000000000000000000000000000000000000815233600482015230602482015260440161072c565b600061081282610690565b73ffffffffffffffffffffffffffffffffffffffff1603610883576040517f1c3812cc0000000000000000000000000000000000000000000000000000000081527fffffffff000000000000000000000000000000000000000000000000000000008216600482015260240161072c565b61073e8160006112f3565b60006108b87fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf85490565b905090565b3330146108fe576040517fe125889400000000000000000000000000000000000000000000000000000000815233600482015230602482015260440161072c565b600061093183836040516020016109169291906132a3565b604051602081830303815290604052805190602001206113b3565b905061093e818484611438565b505050565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004f8ce847174b32cbe21b3887be894e0debc289521630036109b2576040517f0a57d61d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109bb836115c0565b6000806109f38588886040516020016109d6939291906132eb565b6040516020818303038152906040528051906020012085856116bd565b9150915081610a34578084846040517f8f4a234f00000000000000000000000000000000000000000000000000000000815260040161072c93929190613305565b610a3f818888611438565b50505050505050565b60008060008060008087876000818110610a6457610a6461331f565b909101357fff00000000000000000000000000000000000000000000000000000000000000169150819050610aba57610a9c896113b3565b9250610aa98389896116fb565b92985090965094509150610c059050565b7fff0000000000000000000000000000000000000000000000000000000000000081811601610af957610aec896113b3565b9250610aa983898961174c565b7ffe000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821601610b4b57610aec89611778565b7ffd000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821601610baf57610b9f8989896117e5565b9550955095509550955050610c05565b6040517f6085cd820000000000000000000000000000000000000000000000000000000081527fff000000000000000000000000000000000000000000000000000000000000008216600482015260240161072c565b939792965093509350565b333014610c51576040517fe125889400000000000000000000000000000000000000000000000000000000815233600482015230602482015260440161072c565b80610c88576040517fb24b5b3a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610cb17f8c8764b3a50fee69c9bee6e956047501f434fb0e2349c75844a401a7f2a020d2829055565b6040518181527f1f63199319eff813052575c41087f618aba07b006664fed6c01f7ee9c5716835906020015b60405180910390a150565b60006106de7f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e83611047565b6060610d71610d6c610d24610fca565b6040517f017012200000000000000000000000000000000000000000000000000000000060208201526024810191909152604401604051602081830303815290604052611962565b611b7b565b604051602001610d81919061334e565b604051602081830303815290604052905090565b6000333014610dd8576040517fe125889400000000000000000000000000000000000000000000000000000000815233600482015230602482015260440161072c565b81516020830134f0905073ffffffffffffffffffffffffffffffffffffffff8116610e3157816040517f0d25719100000000000000000000000000000000000000000000000000000000815260040161072c9190612e60565b60405173ffffffffffffffffffffffffffffffffffffffff821681527fa506ad4e7f05eceba62a023c3219e5bd98a615f4fa87e2afb08a2da5cf62bf0c9060200160405180910390a1919050565b60006108b87f8c8764b3a50fee69c9bee6e956047501f434fb0e2349c75844a401a7f2a020d25490565b333014610eea576040517fe125889400000000000000000000000000000000000000000000000000000000815233600482015230602482015260440161072c565b61073e81611ba4565b60006108b86000610ce8565b333014610f40576040517fe125889400000000000000000000000000000000000000000000000000000000815233600482015230602482015260440161072c565b6000610f4b83610690565b73ffffffffffffffffffffffffffffffffffffffff1614610fbc576040517f5b4d6d6a0000000000000000000000000000000000000000000000000000000081527fffffffff000000000000000000000000000000000000000000000000000000008316600482015260240161072c565b610fc682826112f3565b5050565b60006108b87f0eecac93ced8722d209199364cda3bc33da3bc3a23daef6be49ebd780511d0335490565b333014611035576040517fe125889400000000000000000000000000000000000000000000000000000000815233600482015230602482015260440161072c565b61103e82611263565b610fc681611ba4565b6000808383604051602001611066929190918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012054949350505050565b60007f2e74b92a000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316016110f857506001919050565b6106de82611bfd565b73ffffffffffffffffffffffffffffffffffffffff81163b611167576040517f0c76093700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015260240161072c565b61116f813055565b60405173ffffffffffffffffffffffffffffffffffffffff821681527f310ba5f1d2ed074b51e2eccd052a47ae9ab7c6b800d1fca3db3999d6a592ca0390602001610cdd565b6000806111c3858585611c59565b50905080156111f557507f1626ba7e000000000000000000000000000000000000000000000000000000009050610751565b506000949350505050565b6000806112258686604051611216929190612ada565b60405180910390208585611c59565b509050801561125757507f20c13b0b000000000000000000000000000000000000000000000000000000009050610774565b50600095945050505050565b8061129a576040517f4294d12700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6112c37fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8829055565b6040518181527f307ed6bd941ee9fc80f369c94af5fa11e25bab5102a6140191756c5474a30bfa90602001610cdd565b604080517fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1206020808301919091527fffffffff000000000000000000000000000000000000000000000000000000008516828401819052835180840385018152606084018086528151919093012073ffffffffffffffffffffffffffffffffffffffff8616908190559152608082015290517f0d7fc113eaf016db4681a1ba86d083ce3e0961f321062a75ac2b0aeb33deeed19181900360a00190a15050565b6040517f190100000000000000000000000000000000000000000000000000000000000060208201524660228201527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b166042820152605681018290526000906076015b604051602081830303815290604052805190602001209050919050565b8060005b818110156115b957368484838181106114575761145761331f565b90506020028101906114699190613393565b90506040810135805a10156114be5782815a6040517f2bb3e3ba00000000000000000000000000000000000000000000000000000000815260048101939093526024830191909152604482015260640161072c565b60006114cd60208401846133d1565b1561150c576115056114e56080850160608601612b5e565b83156114f157836114f3565b5a5b61150060a08701876133ec565b611c8a565b9050611547565b61154461151f6080850160608601612b5e565b608085013584156115305784611532565b5a5b61153f60a08801886133ec565b611ca5565b90505b801561158c57877f5c4eeb02dabf8976016ab414d617f9a162936dcace3cdef8c69ef6e262ad5ae78560405161157f91815260200190565b60405180910390a26115ae565b6115ae61159f60408501602086016133d1565b89866115a9611cc2565b611ce1565b50505060010161143c565b5050505050565b606081901c6bffffffffffffffffffffffff821660006115df83610ce8565b905081811461162b576040517f9b6514f400000000000000000000000000000000000000000000000000000000815260048101849052602481018390526044810182905260640161072c565b604080517f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e60208083019190915281830186905282518083038401815260609092019092528051910120600183019081905560408051858152602081018390527f1f180c27086c7a39ea2a7b25239d1ab92348f07ca7bb59d1438fcf527568f881910160405180910390a15050505050565b60008060008060006116d0888888610a48565b509650919450925090508282108015906116ee57506116ee81611d2f565b9450505050935093915050565b600080808061171687611711876006818b613451565b611d3a565b6000908152873560f01c6020818152604080842084526002909a013560e01c908190529890912090999198509695509350505050565b600080808061176787611762876001818b613451565b6116fb565b935093509350935093509350935093565b6040517f190100000000000000000000000000000000000000000000000000000000000060208201526000602282018190527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b166042830152605682018390529060760161141b565b6000808080806004600188013560e81c8261180083836134aa565b90506118128b6104c183868d8f613451565b939b509199509750955093508787101561186a5761183281848b8d613451565b89896040517fb006aba000000000000000000000000000000000000000000000000000000000815260040161072c94939291906134bd565b8092505b888310156119545760038301928a013560e81c915061188d83836134aa565b905060006118af61189d886121d0565b8c8c879086926104c193929190613451565b939c50919a5098509091505088881015611907576118cf82858c8e613451565b8a8a6040517fb006aba000000000000000000000000000000000000000000000000000000000815260040161072c94939291906134bd565b84811061194a576040517f37daf62b000000000000000000000000000000000000000000000000000000008152600481018290526024810186905260440161072c565b935091508161186e565b505050939792965093509350565b8051606090600381901b60006005600483010467ffffffffffffffff81111561198d5761198d612e73565b6040519080825280601f01601f1916602001820160405280156119b7576020820181803683370190505b5090506000806000805b86811015611acb578881815181106119db576119db61331f565b01602001516008948501949390931b60f89390931c92909217915b60058410611ac3576040805180820190915260208082527f6162636465666768696a6b6c6d6e6f707172737475767778797a323334353637818301527ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb90950194601f85871c16908110611a6c57611a6c61331f565b602001015160f81c60f81b858381518110611a8957611a8961331f565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506001909101906119f6565b6001016119c1565b508215611b6f576040518060400160405280602081526020017f6162636465666768696a6b6c6d6e6f707172737475767778797a3233343536378152508360050383901b601f1681518110611b2257611b2261331f565b602001015160f81c60f81b848281518110611b3f57611b3f61331f565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053505b50919695505050505050565b606081604051602001611b8e91906134e4565b6040516020818303038152906040529050919050565b611bcd7f0eecac93ced8722d209199364cda3bc33da3bc3a23daef6be49ebd780511d033829055565b6040518181527f20d3ef1b5738a9f6d7beae515432206e7a8e2740ca6dcf46a952190ad01bcb5190602001610cdd565b60007f6ffbd451000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831601611c5057506001919050565b6106de82612204565b6000806000806000611c6c888888610a48565b509650919450925090508282108015906116ee57506116ee81612345565b60006040518284823760008084838989f49695505050505050565b6000604051828482376000808483898b8af1979650505050505050565b60603d604051915060208201818101604052818352816000823e505090565b8315611cef57805160208201fd5b827fab46c69f7f32e1bf09b0725853da82a211e5402a0600296ab499a2fb5ea3b4198383604051611d21929190613529565b60405180910390a250505050565b60006106de8261237c565b60008060005b838110156121c757600181019085013560f81c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101611de157601582019186013560f881901c9060581c73ffffffffffffffffffffffffffffffffffffffff81169074ff000000000000000000000000000000000000000016811785611dc75780611dd6565b60008681526020829052604090205b955050505050611d40565b80611e775760018201918681013560f81c906043016000611e0d8a611e0884888c8e613451565b6123af565b60ff841697909701969194508491905060a083901b74ff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff82161786611e5c5780611e6b565b60008781526020829052604090205b96505050505050611d40565b60028103611f9f576000808784013560f881901c9060581c73ffffffffffffffffffffffffffffffffffffffff16601586019550909250905060008885013560e81c600386018162ffffff169150809650819250505060008186019050611ef08b848c8c8a908692611eeb93929190613451565b612672565b611f38578a83611f0283898d8f613451565b6040517f9a94623200000000000000000000000000000000000000000000000000000000815260040161072c9493929190613542565b60ff8416979097019694508460a084901b74ff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84161787611f835780611f92565b60008881526020829052604090205b9750505050505050611d40565b60038103611fd257602082019186013583611fba5780611fc9565b60008481526020829052604090205b93505050611d40565b6004810361201e576003808301928781013560e81c9190820101600080611fff8b61171185898d8f613451565b60009889526020526040909720969097019650909350611d4092505050565b600681036121265760008287013560f81c60018401935060ff16905060008784013560f01c60028501945061ffff16905060008885013560e81c600386018162ffffff16915080965081925050506000818601905060008061208c8d8d8d8b90879261171193929190613451565b939850889390925090508482106120a257988501985b604080517f53657175656e6365206e657374656420636f6e6669673a0a0000000000000000602080830191909152603882018490526058820188905260788083018a90528351808403909101815260989092019092528051910120896121085780612117565b60008a81526020829052604090205b99505050505050505050611d40565b60058103612192576020820191860135878103612161577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff94505b600061216c82612859565b9050846121795780612188565b60008581526020829052604090205b9450505050611d40565b6040517fb2505f7c0000000000000000000000000000000000000000000000000000000081526004810182905260240161072c565b50935093915050565b7f8713a7c4465f6fbee2b6e9d6646d1d9f83fec929edfc4baf661f3c865bdd04d160009081526020829052604081206106de565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fec6aba5000000000000000000000000000000000000000000000000000000000148061229757507fffffffff0000000000000000000000000000000000000000000000000000000082167f4e2312e000000000000000000000000000000000000000000000000000000000145b806122e357507fffffffff0000000000000000000000000000000000000000000000000000000082167f150b7a0200000000000000000000000000000000000000000000000000000000145b8061232f57507fffffffff0000000000000000000000000000000000000000000000000000000082167fc0ee0b8a00000000000000000000000000000000000000000000000000000000145b1561233c57506001919050565b6106de82612894565b600081158015906106de57507f8c8764b3a50fee69c9bee6e956047501f434fb0e2349c75844a401a7f2a020d2545b821492915050565b600081158015906106de57507fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf854612374565b6000604282146123ef5782826040517f2ee17a3d00000000000000000000000000000000000000000000000000000000815260040161072c929190613582565b60006124086123ff600185613596565b85013560f81c90565b60ff169050604084013560f81c843560208601357f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a081111561247c578686826040517fad4aac7600000000000000000000000000000000000000000000000000000000815260040161072c939291906135a9565b8260ff16601b1415801561249457508260ff16601c14155b156124d1578686846040517fe578897e00000000000000000000000000000000000000000000000000000000815260040161072c939291906135cd565b6001840361253e576040805160008152602081018083528a905260ff851691810191909152606081018390526080810182905260019060a0015b6020604051602081039080840390855afa15801561252d573d6000803e3d6000fd5b505050602060405103519450612616565b600284036125db576040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101899052600190605c01604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff861690820152606081018490526080810183905260a00161250b565b86868560016040517f9dfba85200000000000000000000000000000000000000000000000000000000815260040161072c94939291906135f4565b73ffffffffffffffffffffffffffffffffffffffff85166126675786866040517f6c1719d200000000000000000000000000000000000000000000000000000000815260040161072c929190613582565b505050509392505050565b60008181036126ad576040517fac241e1100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083836126bc600182613596565b8181106126cb576126cb61331f565b919091013560f81c91505060018114806126e55750600281145b1561272a578473ffffffffffffffffffffffffffffffffffffffff1661270c8786866123af565b73ffffffffffffffffffffffffffffffffffffffff16149150612850565b600381036128155773ffffffffffffffffffffffffffffffffffffffff8516631626ba7e878660008761275e600182613596565b9261276b93929190613451565b6040518463ffffffff1660e01b815260040161278993929190613305565b602060405180830381865afa1580156127a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127ca9190613620565b7fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e00000000000000000000000000000000000000000000000000000000149150612850565b83838260006040517f9dfba85200000000000000000000000000000000000000000000000000000000815260040161072c94939291906135f4565b50949350505050565b6040517f53657175656e636520737461746963206469676573743a0a000000000000000060208201526038810182905260009060580161141b565b60007ffda4dd44000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316016128e757506001919050565b6106de8260007fe4a77bbc000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083160161293e57506001919050565b6106de8260007fda44f878000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083160161299557506001919050565b6106de8260007fae9fa280000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316016129ec57506001919050565b6106de8260007fffffffff0000000000000000000000000000000000000000000000000000000082167fac6a444e000000000000000000000000000000000000000000000000000000001480612a8357507fffffffff0000000000000000000000000000000000000000000000000000000082167f36e7817500000000000000000000000000000000000000000000000000000000145b15612a9057506001919050565b7f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316146106de565b8183823760009101908152919050565b7fffffffff000000000000000000000000000000000000000000000000000000008116811461073e57600080fd5b600060208284031215612b2a57600080fd5b813561075181612aea565b803573ffffffffffffffffffffffffffffffffffffffff81168114612b5957600080fd5b919050565b600060208284031215612b7057600080fd5b61075182612b35565b60008083601f840112612b8b57600080fd5b50813567ffffffffffffffff811115612ba357600080fd5b602083019150836020828501011115612bbb57600080fd5b9250929050565b600080600080600060808688031215612bda57600080fd5b612be386612b35565b9450612bf160208701612b35565b935060408601359250606086013567ffffffffffffffff811115612c1457600080fd5b612c2088828901612b79565b969995985093965092949392505050565b600080600060408486031215612c4657600080fd5b83359250602084013567ffffffffffffffff811115612c6457600080fd5b612c7086828701612b79565b9497909650939450505050565b60008060008060408587031215612c9357600080fd5b843567ffffffffffffffff80821115612cab57600080fd5b612cb788838901612b79565b90965094506020870135915080821115612cd057600080fd5b50612cdd87828801612b79565b95989497509550505050565b600060208284031215612cfb57600080fd5b5035919050565b60008083601f840112612d1457600080fd5b50813567ffffffffffffffff811115612d2c57600080fd5b6020830191508360208260051b8501011115612bbb57600080fd5b60008060208385031215612d5a57600080fd5b823567ffffffffffffffff811115612d7157600080fd5b612d7d85828601612d02565b90969095509350505050565b600080600080600060608688031215612da157600080fd5b853567ffffffffffffffff80821115612db957600080fd5b612dc589838a01612d02565b9097509550602088013594506040880135915080821115612de557600080fd5b50612c2088828901612b79565b60005b83811015612e0d578181015183820152602001612df5565b50506000910152565b60008151808452612e2e816020860160208601612df2565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006107516020830184612e16565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600060208284031215612eb457600080fd5b813567ffffffffffffffff80821115612ecc57600080fd5b818401915084601f830112612ee057600080fd5b813581811115612ef257612ef2612e73565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715612f3857612f38612e73565b81604052828152876020848701011115612f5157600080fd5b826020860160208301376000928101602001929092525095945050505050565b60008060408385031215612f8457600080fd5b8235612f8f81612aea565b9150612f9d60208401612b35565b90509250929050565b60008060008060008060008060a0898b031215612fc257600080fd5b612fcb89612b35565b9750612fd960208a01612b35565b9650604089013567ffffffffffffffff80821115612ff657600080fd5b6130028c838d01612d02565b909850965060608b013591508082111561301b57600080fd5b6130278c838d01612d02565b909650945060808b013591508082111561304057600080fd5b5061304d8b828c01612b79565b999c989b5096995094979396929594505050565b6000806040838503121561307457600080fd5b50508035926020909101359150565b60008060008060008060a0878903121561309c57600080fd5b6130a587612b35565b95506130b360208801612b35565b94506040870135935060608701359250608087013567ffffffffffffffff8111156130dd57600080fd5b6130e989828a01612b79565b979a9699509497509295939492505050565b80358015158114612b5957600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b81835260006020808501808196508560051b810191508460005b8781101561329657828403895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff418836030181126131ad57600080fd5b870160c06131ba826130fb565b151586526131c98783016130fb565b15158688015260408281013590870152606073ffffffffffffffffffffffffffffffffffffffff6131fb828501612b35565b16908701526080828101359087015260a080830135368490037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe101811261324157600080fd5b90920187810192903567ffffffffffffffff81111561325f57600080fd5b80360384131561326e57600080fd5b8282890152613280838901828661310b565b9c89019c9750505092860192505060010161316e565b5091979650505050505050565b60408152600560408201527f73656c663a000000000000000000000000000000000000000000000000000000606082015260806020820152600061074e608083018486613154565b838152604060208201526000610771604083018486613154565b83815260406020820152600061077160408301848661310b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f697066733a2f2f00000000000000000000000000000000000000000000000000815260008251613386816007850160208701612df2565b9190910160070192915050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff418336030181126133c757600080fd5b9190910192915050565b6000602082840312156133e357600080fd5b610751826130fb565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261342157600080fd5b83018035915067ffffffffffffffff82111561343c57600080fd5b602001915036819003821315612bbb57600080fd5b6000808585111561346157600080fd5b8386111561346e57600080fd5b5050820193919092039150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808201808211156106de576106de61347b565b6060815260006134d160608301868861310b565b6020830194909452506040015292915050565b7f620000000000000000000000000000000000000000000000000000000000000081526000825161351c816001850160208701612df2565b9190910160010192915050565b82815260406020820152600061074e6040830184612e16565b84815273ffffffffffffffffffffffffffffffffffffffff8416602082015260606040820152600061357860608301848661310b565b9695505050505050565b60208152600061074e60208301848661310b565b818103818111156106de576106de61347b565b6040815260006135bd60408301858761310b565b9050826020830152949350505050565b6040815260006135e160408301858761310b565b905060ff83166020830152949350505050565b60608152600061360860608301868861310b565b60208301949094525090151560409091015292915050565b60006020828403121561363257600080fd5b815161075181612aea56fea26469706673582212209ea1f29a81ef04f0d1c452da3db48e1d4423c64089333bd5bf9b44b4cb4b18cf64736f6c63430008120033
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) * @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. * Reading out of bounds may return dirty values. */ library LibBytes { /** * @notice Returns the bytes32 value at the given index in the input data. * @param data The input data. * @param index The index of the value to retrieve. * @return a The bytes32 value at the given index. */ function readBytes32( bytes calldata data, uint256 index ) internal pure returns ( bytes32 a ) { assembly { a := calldataload(add(data.offset, index)) } } /** * @notice Returns the uint8 value at the given index in the input data. * @param data The input data. * @param index The index of the value to retrieve. * @return a The uint8 value at the given index. */ function readUint8( bytes calldata data, uint256 index ) internal pure returns ( uint8 a ) { assembly { let word := calldataload(add(index, data.offset)) a := shr(248, word) } } /** * @notice Returns the first uint16 value in the input data. * @param data The input data. * @return a The first uint16 value in the input data. */ function readFirstUint16( bytes calldata data ) internal pure returns ( uint16 a ) { assembly { let word := calldataload(data.offset) a := shr(240, word) } } /** * @notice Returns the uint32 value at the given index in the input data. * @param data The input data. * @param index The index of the value to retrieve. * @return a The uint32 value at the given index. */ function readUint32( bytes calldata data, uint256 index ) internal pure returns ( uint32 a ) { assembly { let word := calldataload(add(index, data.offset)) a := shr(224, word) } } function readMBytes4( bytes memory data, uint256 index ) internal pure returns ( bytes4 a ) { assembly { let word := mload(add(add(data, 0x20), index)) a := and(word, 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000) } } function readMBytes32( bytes memory data, uint256 index ) internal pure returns ( bytes32 a ) { assembly { a := mload(add(add(data, 0x20), index)) } } }
LibOptim.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; /** * @title Library for optimized EVM operations * @author Agustin Aguilar (aa@horizon.io) * @notice This library contains functions for optimizing certain 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 calldata _data ) internal returns (bool r) { assembly { let tmp := mload(0x40) calldatacopy(tmp, _data.offset, _data.length) r := call( _gas, _to, _val, tmp, _data.length, 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 calldata _data ) internal returns (bool r) { assembly { let tmp := mload(0x40) calldatacopy(tmp, _data.offset, _data.length) r := delegatecall( _gas, _to, tmp, _data.length, 0, 0 ) } } }
LibString.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; /** * @title Library for string manipulation operations * @notice This library contains functions for manipulating strings in Solidity. */ library LibString { bytes private constant ALPHABET_HEX_16 = '0123456789abcdef'; bytes private constant ALPHABET_32 = 'abcdefghijklmnopqrstuvwxyz234567'; /** * @notice Prefixes a hexadecimal string with "0x". * @param _hex The hexadecimal string to prefix. * @return The prefixed hexadecimal string. */ function prefixHexadecimal(string memory _hex) internal pure returns (string memory) { return string(abi.encodePacked('0x', _hex)); } /** * @notice Prefixes a base32 string with "b". * @param _base32 The base32 string to prefix. * @return The prefixed base32 string. */ function prefixBase32(string memory _base32) internal pure returns (string memory) { return string(abi.encodePacked('b', _base32)); } /** * @notice Converts a byte array to a hexadecimal string. * @param _bytes The byte array to convert. * @return The resulting hexadecimal string. */ function bytesToHexadecimal(bytes memory _bytes) internal pure returns (string memory) { uint256 bytesLength = _bytes.length; bytes memory bytesArray = new bytes(bytesLength << 1); unchecked { for (uint256 i = 0; i < bytesLength; i++) { uint256 word = uint8(_bytes[i]); uint256 ib = i << 1; bytesArray[ib] = bytes1(ALPHABET_HEX_16[word >> 4]); bytesArray[ib + 1] = bytes1(ALPHABET_HEX_16[word & 0xf]); } } return string(bytesArray); } /** * @notice Converts a byte array to a base32 string. * @param _bytes The byte array to convert. * @return The resulting base32 string. */ function bytesToBase32(bytes memory _bytes) internal pure returns (string memory) { uint256 bytesLength = _bytes.length; uint256 t1 = bytesLength << 3; unchecked { // base32-encoded length = ceil(# of bits / 5) bytes memory bytesArray = new bytes((t1 + 4) / 5); uint256 bits = 0; uint256 buffer = 0; uint256 pointer = 0; for (uint256 i = 0; i < bytesLength; i++) { buffer = (buffer << 8) | uint8(_bytes[i]); bits += 8; while (bits >= 5) { bits -= 5; bytesArray[pointer] = bytes1(ALPHABET_32[(buffer >> bits) & 0x1f]); pointer++; } } if (bits > 0) { bytesArray[pointer] = bytes1(ALPHABET_32[(buffer << (5 - bits)) & 0x1f]); } return string(bytesArray); } } }
LibAddress.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; library LibAddress { /** * @notice Will return true if provided address is a contract * @param account Address to verify if contract or not * @dev This contract will return false if called within the constructor of * a contract's deployment, as the code is not yet stored on-chain. */ function isContract(address account) internal view returns (bool) { uint256 csize; // solhint-disable-next-line no-inline-assembly assembly { csize := extcodesize(account) } return csize != 0; } }
LibBytesPointer.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; /** * @title Library for reading data from bytes arrays with a pointer * @author Agustin Aguilar (aa@horizon.io) * @notice This library contains functions for reading data from bytes arrays with a pointer. * * @dev These functions do not check if the input index is within the bounds of the data array. * Reading out of bounds may return dirty values. */ library LibBytesPointer { /** * @dev Returns the first uint16 value in the input data and updates the pointer. * @param _data The input data. * @return a The first uint16 value. * @return newPointer The new pointer. */ function readFirstUint16( bytes calldata _data ) internal pure returns ( uint16 a, uint256 newPointer ) { assembly { let word := calldataload(_data.offset) a := shr(240, word) newPointer := 2 } } /** * @notice Returns the uint8 value at the given index in the input data and updates the pointer. * @param _data The input data. * @param _index The index of the value to retrieve. * @return a The uint8 value at the given index. * @return newPointer The new pointer. */ 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 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) } } /** * @notice Returns the uint8 value and the address at the given index in the input data and updates the pointer. * @param _data The input data. * @param _index The index of the value to retrieve. * @return a The uint8 value at the given index. * @return b The following address value. * @return newPointer The new pointer. */ function readUint8Address( bytes calldata _data, uint256 _index ) internal pure returns ( uint8 a, address b, uint256 newPointer ) { assembly { let word := calldataload(add(_index, _data.offset)) a := shr(248, word) b := and(shr(88, word), 0xffffffffffffffffffffffffffffffffffffffff) newPointer := add(_index, 21) } } /** * @notice Returns the uint16 value at the given index in the input data and updates the pointer. * @param _data The input data. * @param _index The index of the value to retrieve. * @return a The uint16 value at the given index. * @return newPointer The new pointer. */ function readUint16( bytes calldata _data, uint256 _index ) internal pure returns ( uint16 a, uint256 newPointer ) { assembly { let word := calldataload(add(_index, _data.offset)) a := and(shr(240, word), 0xffff) newPointer := add(_index, 2) } } function readUintX( bytes calldata _data, uint256 _bytes, uint256 _index ) internal pure returns ( uint256 a, uint256 newPointer ) { assembly { let word := calldataload(add(_index, _data.offset)) let shift := sub(256, mul(_bytes, 8)) a := and(shr(shift, word), sub(shl(mul(8, _bytes), 1), 1)) newPointer := add(_index, _bytes) } } /** * @notice Returns the uint24 value at the given index in the input data and updates the pointer. * @param _data The input data. * @param _index The index of the value to retrieve. * @return a The uint24 value at the given index. * @return newPointer The new pointer. */ function readUint24( bytes calldata _data, uint256 _index ) internal pure returns ( uint24 a, uint256 newPointer ) { assembly { let word := calldataload(add(_index, _data.offset)) a := and(shr(232, word), 0xffffff) newPointer := add(_index, 3) } } /** * @notice Returns the uint64 value at the given index in the input data and updates the pointer. * @param _data The input data. * @param _index The index of the value to retrieve. * @return a The uint64 value at the given index. * @return newPointer The new pointer. */ function readUint64( bytes calldata _data, uint256 _index ) internal pure returns ( uint64 a, uint256 newPointer ) { assembly { let word := calldataload(add(_index, _data.offset)) a := and(shr(192, word), 0xffffffffffffffff) newPointer := add(_index, 8) } } function readBytes4( bytes calldata _data, uint256 _pointer ) internal pure returns ( bytes4 a, uint256 newPointer ) { assembly { a := calldataload(add(_pointer, _data.offset)) a := and(a, 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000) newPointer := add(_pointer, 4) } } /** * @notice Returns the bytes32 value at the given index in the input data and updates the pointer. * @param _data The input data. * @param _pointer The index of the value to retrieve. * @return a The bytes32 value at the given index. * @return newPointer The new pointer. */ 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) } } }
SignatureValidator.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; import "../interfaces/IERC1271Wallet.sol"; import "./LibBytes.sol"; /** * @dev Contains logic for signature validation. * Signatures from wallet contracts assume ERC-1271 support (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1271.md) * Notes: Methods are strongly inspired by contracts in https://github.com/0xProject/0x-monorepo/blob/development/ */ library SignatureValidator { // Errors error InvalidSignatureLength(bytes _signature); error EmptySignature(); error InvalidSValue(bytes _signature, bytes32 _s); error InvalidVValue(bytes _signature, uint256 _v); error UnsupportedSignatureType(bytes _signature, uint256 _type, bool _recoverMode); error SignerIsAddress0(bytes _signature); using LibBytes for bytes; /***********************************| | Variables | |__________________________________*/ // bytes4(keccak256("isValidSignature(bytes,bytes)")) bytes4 constant internal ERC1271_MAGICVALUE = 0x20c13b0b; // bytes4(keccak256("isValidSignature(bytes32,bytes)")) bytes4 constant internal ERC1271_MAGICVALUE_BYTES32 = 0x1626ba7e; // Allowed signature types. uint256 private constant SIG_TYPE_EIP712 = 1; uint256 private constant SIG_TYPE_ETH_SIGN = 2; uint256 private constant SIG_TYPE_WALLET_BYTES32 = 3; /***********************************| | Signature Functions | |__________________________________*/ /** * @notice Recover the signer of hash, assuming it's an EOA account * @dev Only for SignatureType.EIP712 and SignatureType.EthSign signatures * @param _hash Hash that was signed * encoded as (bytes32 r, bytes32 s, uint8 v, ... , SignatureType sigType) */ function recoverSigner( bytes32 _hash, bytes calldata _signature ) internal pure returns (address signer) { if (_signature.length != 66) revert InvalidSignatureLength(_signature); uint256 signatureType = _signature.readUint8(_signature.length - 1); // Variables are not scoped in Solidity. uint8 v = _signature.readUint8(64); bytes32 r = _signature.readBytes32(0); bytes32 s = _signature.readBytes32(32); // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. // // Source OpenZeppelin // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/cryptography/ECDSA.sol if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { revert InvalidSValue(_signature, s); } if (v != 27 && v != 28) { revert InvalidVValue(_signature, v); } // Signature using EIP712 if (signatureType == SIG_TYPE_EIP712) { signer = ecrecover(_hash, v, r, s); // Signed using web3.eth_sign() or Ethers wallet.signMessage() } else if (signatureType == SIG_TYPE_ETH_SIGN) { signer = ecrecover( keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _hash)), v, r, s ); } else { // We cannot recover the signer for any other signature type. revert UnsupportedSignatureType(_signature, signatureType, true); } // Prevent signer from being 0x0 if (signer == address(0x0)) revert SignerIsAddress0(_signature); return signer; } /** * @notice Returns true if the provided signature is valid for the given signer. * @dev Supports SignatureType.EIP712, SignatureType.EthSign, and ERC1271 signatures * @param _hash Hash that was signed * @param _signer Address of the signer candidate * @param _signature Signature byte array */ function isValidSignature( bytes32 _hash, address _signer, bytes calldata _signature ) internal view returns (bool valid) { if (_signature.length == 0) { revert EmptySignature(); } uint256 signatureType = uint8(_signature[_signature.length - 1]); if (signatureType == SIG_TYPE_EIP712 || signatureType == SIG_TYPE_ETH_SIGN) { // Recover signer and compare with provided valid = recoverSigner(_hash, _signature) == _signer; } else if (signatureType == SIG_TYPE_WALLET_BYTES32) { // Remove signature type before calling ERC1271, restore after call valid = ERC1271_MAGICVALUE_BYTES32 == IERC1271Wallet(_signer).isValidSignature(_hash, _signature[0:_signature.length - 1]); } else { // We cannot validate any other signature type. // We revert because we can say nothing about its validity. revert UnsupportedSignatureType(_signature, signatureType, false); } } }
IERC1271Wallet.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; interface IERC1271Wallet { /** * @notice Verifies whether the provided signature is valid with respect to the provided data * @dev MUST return the correct magic value if the signature provided is valid for the provided data * > 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 Arbitrary length data signed on the behalf of address(this) * @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); /** * @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 _hash keccak256 hash 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( bytes32 _hash, bytes calldata _signature) external view returns (bytes4 magicValue); }
ModuleAuth.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; import "../../utils/LibBytes.sol"; import "../../interfaces/IERC1271Wallet.sol"; import "./interfaces/IModuleAuth.sol"; import "./ModuleERC165.sol"; import "./submodules/auth/SequenceBaseSig.sol"; import "./submodules/auth/SequenceDynamicSig.sol"; import "./submodules/auth/SequenceNoChainIdSig.sol"; import "./submodules/auth/SequenceChainedSig.sol"; abstract contract ModuleAuth is IModuleAuth, ModuleERC165, IERC1271Wallet, SequenceChainedSig { using LibBytes for bytes; bytes1 internal constant LEGACY_TYPE = hex"00"; bytes1 internal constant DYNAMIC_TYPE = hex"01"; bytes1 internal constant NO_CHAIN_ID_TYPE = hex"02"; bytes1 internal constant CHAINED_TYPE = hex"03"; bytes4 internal constant SELECTOR_ERC1271_BYTES_BYTES = 0x20c13b0b; bytes4 internal constant SELECTOR_ERC1271_BYTES32_BYTES = 0x1626ba7e; /** * @notice Recovers the threshold, weight, imageHash, subdigest, and checkpoint of a signature. * @dev The signature must be prefixed with a type byte, which is used to determine the recovery method. * * @param _digest Digest of the signed data. * @param _signature A Sequence signature. * * @return threshold The required number of signatures needed to consider the signature valid. * @return weight The actual number of signatures collected in the signature. * @return imageHash The imageHash of the configuration that signed the message. * @return subdigest A modified version of the original digest, unique for each wallet/network. * @return checkpoint A nonce that is incremented every time a new configuration is set. */ function signatureRecovery( bytes32 _digest, bytes calldata _signature ) public override virtual view returns ( uint256 threshold, uint256 weight, bytes32 imageHash, bytes32 subdigest, uint256 checkpoint ) { bytes1 signatureType = _signature[0]; if (signatureType == LEGACY_TYPE) { // networkId digest + base recover subdigest = SequenceBaseSig.subdigest(_digest); (threshold, weight, imageHash, checkpoint) = SequenceBaseSig.recover(subdigest, _signature); return (threshold, weight, imageHash, subdigest, checkpoint); } if (signatureType == DYNAMIC_TYPE) { // networkId digest + dynamic recover subdigest = SequenceBaseSig.subdigest(_digest); (threshold, weight, imageHash, checkpoint) = SequenceDynamicSig.recover(subdigest, _signature); return (threshold, weight, imageHash, subdigest, checkpoint); } if (signatureType == NO_CHAIN_ID_TYPE) { // noChainId digest + dynamic recover subdigest = SequenceNoChainIdSig.subdigest(_digest); (threshold, weight, imageHash, checkpoint) = SequenceDynamicSig.recover(subdigest, _signature); return (threshold, weight, imageHash, subdigest, checkpoint); } if (signatureType == CHAINED_TYPE) { // original digest + chained recover // (subdigest will be computed in the chained recover) return chainedRecover(_digest, _signature); } revert InvalidSignatureType(signatureType); } /** * @dev Validates a signature. * * @param _digest Digest of the signed data. * @param _signature A Sequence signature. * * @return isValid Indicates whether the signature is valid or not. * @return subdigest A modified version of the original digest, unique for each wallet/network. */ function _signatureValidation( bytes32 _digest, bytes calldata _signature ) internal override virtual view returns ( bool isValid, bytes32 subdigest ) { uint256 threshold; uint256 weight; bytes32 imageHash; (threshold, weight, imageHash, subdigest,) = signatureRecovery(_digest, _signature); isValid = weight >= threshold && _isValidImage(imageHash); } /** * @notice Verifies whether the provided signature is valid with respect to the provided data * @dev MUST return the correct magic value if the signature provided is valid for the provided data * > The bytes4 magic value to return when signature is valid is 0x20c13b0b : bytes4(keccak256("isValidSignature(bytes,bytes)")) * @param _data Arbitrary length data signed on the behalf of address(this) * @param _signatures Signature byte array associated with _data. * Encoded as abi.encode(Signature[], Configs) * @return magicValue Magic value 0x20c13b0b if the signature is valid and 0x0 otherwise */ function isValidSignature( bytes calldata _data, bytes calldata _signatures ) public override virtual view returns (bytes4) { // Validate signatures (bool isValid,) = _signatureValidation(keccak256(_data), _signatures); if (isValid) { return SELECTOR_ERC1271_BYTES_BYTES; } return bytes4(0); } /** * @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)")) * @param _hash keccak256 hash that was signed * @param _signatures Signature byte array associated with _data. * Encoded as abi.encode(Signature[], Configs) * @return magicValue Magic value 0x1626ba7e if the signature is valid and 0x0 otherwise */ function isValidSignature( bytes32 _hash, bytes calldata _signatures ) public override virtual view returns (bytes4) { // Validate signatures (bool isValid,) = _signatureValidation(_hash, _signatures); if (isValid) { return SELECTOR_ERC1271_BYTES32_BYTES; } return bytes4(0); } /** * @notice Query if a contract implements an interface * @param _interfaceID The interface identifier, as specified in ERC-165 * @return `true` if the contract implements `_interfaceID` */ function supportsInterface(bytes4 _interfaceID) public override virtual pure returns (bool) { if ( _interfaceID == type(IModuleAuth).interfaceId || _interfaceID == type(IERC1271Wallet).interfaceId ) { return true; } return super.supportsInterface(_interfaceID); } /** * @notice Updates the signers configuration of the wallet * @param _imageHash New required image hash of the signature */ function updateImageHash(bytes32 _imageHash) external override virtual onlySelf { _updateImageHash(_imageHash); } }
ModuleIPFS.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; import "./ModuleSelfAuth.sol"; import "./ModuleStorage.sol"; import "../../utils/LibString.sol"; contract ModuleIPFS is ModuleSelfAuth { event IPFSRootUpdated(bytes32 _hash); // IPFS_ROOT_KEY = keccak256("sequence.ipfs.root") bytes32 private constant IPFS_ROOT_KEY = bytes32(0x0eecac93ced8722d209199364cda3bc33da3bc3a23daef6be49ebd780511d033); function ipfsRootBytes32() public view returns (bytes32) { return ModuleStorage.readBytes32(IPFS_ROOT_KEY); } function ipfsRoot() public view returns (string memory) { return string( abi.encodePacked( "ipfs://", LibString.prefixBase32( LibString.bytesToBase32( abi.encodePacked( hex'01701220', ipfsRootBytes32() ) ) ) ) ); } function updateIPFSRoot(bytes32 _hash) external onlySelf { _updateIPFSRoot(_hash); } function _updateIPFSRoot(bytes32 _hash) internal { ModuleStorage.writeBytes32(IPFS_ROOT_KEY, _hash); emit IPFSRootUpdated(_hash); } }
ModuleCalls.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; import "./ModuleSelfAuth.sol"; import "./ModuleStorage.sol"; import "./ModuleERC165.sol"; import "./ModuleNonce.sol"; import "./ModuleOnlyDelegatecall.sol"; import "./interfaces/IModuleCalls.sol"; import "./interfaces/IModuleAuth.sol"; import "./submodules/nonce/SubModuleNonce.sol"; import "./submodules/auth/SequenceBaseSig.sol"; import "../../utils/LibOptim.sol"; abstract contract ModuleCalls is IModuleCalls, IModuleAuth, ModuleERC165, ModuleOnlyDelegatecall, ModuleSelfAuth, ModuleNonce { /** * @notice Allow wallet owner to execute an action * @dev Relayers must ensure that the gasLimit specified for each transaction * is acceptable to them. A user could specify large enough that it could * consume all the gas available. * @param _txs Transactions to process * @param _nonce Signature nonce (may contain an encoded space) * @param _signature Encoded signature */ function execute( Transaction[] calldata _txs, uint256 _nonce, bytes calldata _signature ) external override virtual onlyDelegatecall { // Validate and update nonce _validateNonce(_nonce); // Hash and verify transaction bundle (bool isValid, bytes32 txHash) = _signatureValidation( keccak256( abi.encode( _nonce, _txs ) ), _signature ); if (!isValid) { revert InvalidSignature(txHash, _signature); } // Execute the transactions _execute(txHash, _txs); } /** * @notice Allow wallet to execute an action * without signing the message * @param _txs Transactions to execute */ function selfExecute( Transaction[] calldata _txs ) external override virtual onlySelf { // Hash transaction bundle bytes32 txHash = SequenceBaseSig.subdigest( keccak256( abi.encode('self:', _txs) ) ); // Execute the transactions _execute(txHash, _txs); } /** * @notice Executes a list of transactions * @param _txHash Hash of the batch of transactions * @param _txs Transactions to execute */ function _execute( bytes32 _txHash, Transaction[] calldata _txs ) private { unchecked { // Execute transaction uint256 size = _txs.length; for (uint256 i = 0; i < size; i++) { Transaction calldata transaction = _txs[i]; uint256 gasLimit = transaction.gasLimit; if (gasleft() < gasLimit) revert NotEnoughGas(i, gasLimit, gasleft()); bool success; if (transaction.delegateCall) { success = LibOptim.delegatecall( transaction.target, gasLimit == 0 ? gasleft() : gasLimit, transaction.data ); } else { success = LibOptim.call( transaction.target, transaction.value, gasLimit == 0 ? gasleft() : gasLimit, transaction.data ); } if (success) { emit TxExecuted(_txHash, i); } else { // Avoid copy of return data until neccesary _revertBytes( transaction.revertOnError, _txHash, i, LibOptim.returnData() ); } } } } /** * @notice Logs a failed transaction, reverts if the transaction is not optional * @param _revertOnError Signals if it should revert or just log * @param _txHash Hash of the transaction * @param _index Index of the transaction in the batch * @param _reason Encoded revert message */ function _revertBytes( bool _revertOnError, bytes32 _txHash, uint256 _index, bytes memory _reason ) internal { if (_revertOnError) { assembly { revert(add(_reason, 0x20), mload(_reason)) } } else { emit TxFailed(_txHash, _index, _reason); } } /** * @notice Query if a contract implements an interface * @param _interfaceID The interface identifier, as specified in ERC-165 * @return `true` if the contract implements `_interfaceID` */ function supportsInterface(bytes4 _interfaceID) public override virtual pure returns (bool) { if (_interfaceID == type(IModuleCalls).interfaceId) { return true; } return super.supportsInterface(_interfaceID); } }
ModuleHooks.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; import "./interfaces/IModuleHooks.sol"; import "./ModuleSelfAuth.sol"; import "./ModuleStorage.sol"; import "./ModuleERC165.sol"; import "../../interfaces/receivers/IERC1155Receiver.sol"; import "../../interfaces/receivers/IERC721Receiver.sol"; import "../../interfaces/receivers/IERC223Receiver.sol"; contract ModuleHooks is IERC1155Receiver, IERC721Receiver, IModuleHooks, ModuleERC165, ModuleSelfAuth { // HOOKS_KEY = keccak256("org.arcadeum.module.hooks.hooks"); bytes32 private constant HOOKS_KEY = bytes32(0xbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a120); /** * @notice Reads the implementation hook of a signature * @param _signature Signature function * @return The address of the implementation hook, address(0) if none */ function readHook(bytes4 _signature) external override virtual view returns (address) { return _readHook(_signature); } /** * @notice Adds a new hook to handle a given function selector * @param _signature Signature function linked to the hook * @param _implementation Hook implementation contract * @dev Can't overwrite hooks that are part of the main module (those defined below) */ function addHook(bytes4 _signature, address _implementation) external override virtual onlySelf { if (_readHook(_signature) != address(0)) revert HookAlreadyExists(_signature); _writeHook(_signature, _implementation); } /** * @notice Removes a registered hook * @param _signature Signature function linked to the hook * @dev Can't remove hooks that are part of the main module (those defined below) * without upgrading the wallet */ function removeHook(bytes4 _signature) external override virtual onlySelf { if (_readHook(_signature) == address(0)) revert HookDoesNotExist(_signature); _writeHook(_signature, address(0)); } /** * @notice Reads the implementation hook of a signature * @param _signature Signature function * @return The address of the implementation hook, address(0) if none */ function _readHook(bytes4 _signature) private view returns (address) { return address(uint160(uint256(ModuleStorage.readBytes32Map(HOOKS_KEY, _signature)))); } /** * @notice Writes the implementation hook of a signature * @param _signature Signature function * @param _implementation Hook implementation contract */ function _writeHook(bytes4 _signature, address _implementation) private { ModuleStorage.writeBytes32Map(HOOKS_KEY, _signature, bytes32(uint256(uint160(_implementation)))); emit DefinedHook(_signature, _implementation); } /** * @notice Handle the receipt of a single ERC1155 token type. * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` */ function onERC1155Received( address, address, uint256, uint256, bytes calldata ) external override virtual returns (bytes4) { return ModuleHooks.onERC1155Received.selector; } /** * @notice Handle the receipt of multiple ERC1155 token types. * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` */ function onERC1155BatchReceived( address, address, uint256[] calldata, uint256[] calldata, bytes calldata ) external override virtual returns (bytes4) { return ModuleHooks.onERC1155BatchReceived.selector; } /** * @notice Handle the receipt of a single ERC721 token. * @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` */ function onERC721Received(address, address, uint256, bytes calldata) external override virtual returns (bytes4) { return ModuleHooks.onERC721Received.selector; } /** * @notice Routes fallback calls through hooks */ fallback() external payable { if (msg.data.length >= 4) { address target = _readHook(msg.sig); if (target != address(0)) { (bool success, bytes memory result) = target.delegatecall(msg.data); assembly { if iszero(success) { revert(add(result, 0x20), mload(result)) } return(add(result, 0x20), mload(result)) } } } } /** * @notice Allows the wallet to receive ETH */ receive() external payable { } /** * @notice Query if a contract implements an interface * @param _interfaceID The interface identifier, as specified in ERC-165 * @return `true` if the contract implements `_interfaceID` */ function supportsInterface(bytes4 _interfaceID) public override virtual pure returns (bool) { if ( _interfaceID == type(IModuleHooks).interfaceId || _interfaceID == type(IERC1155Receiver).interfaceId || _interfaceID == type(IERC721Receiver).interfaceId || _interfaceID == type(IERC223Receiver).interfaceId ) { return true; } return super.supportsInterface(_interfaceID); } }
ModuleNonce.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; import "./ModuleStorage.sol"; import "./submodules/nonce/SubModuleNonce.sol"; contract ModuleNonce { // Events event NonceChange(uint256 _space, uint256 _newNonce); // Errors error BadNonce(uint256 _space, uint256 _provided, uint256 _current); // NONCE_KEY = keccak256("org.arcadeum.module.calls.nonce"); bytes32 private constant NONCE_KEY = bytes32(0x8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e); /** * @notice Returns the next nonce of the default nonce space * @dev The default nonce space is 0x00 * @return The next nonce */ function nonce() external virtual view returns (uint256) { return readNonce(0); } /** * @notice Returns the next nonce of the given nonce space * @param _space Nonce space, each space keeps an independent nonce count * @return The next nonce */ function readNonce(uint256 _space) public virtual view returns (uint256) { return uint256(ModuleStorage.readBytes32Map(NONCE_KEY, bytes32(_space))); } /** * @notice Changes the next nonce of the given nonce space * @param _space Nonce space, each space keeps an independent nonce count * @param _nonce Nonce to write on the space */ function _writeNonce(uint256 _space, uint256 _nonce) internal { ModuleStorage.writeBytes32Map(NONCE_KEY, bytes32(_space), bytes32(_nonce)); } /** * @notice Verify if a nonce is valid * @param _rawNonce Nonce to validate (may contain an encoded space) */ function _validateNonce(uint256 _rawNonce) internal virtual { // Retrieve current nonce for this wallet (uint256 space, uint256 providedNonce) = SubModuleNonce.decodeNonce(_rawNonce); uint256 currentNonce = readNonce(space); if (currentNonce != providedNonce) { revert BadNonce(space, providedNonce, currentNonce); } unchecked { uint256 newNonce = providedNonce + 1; _writeNonce(space, newNonce); emit NonceChange(space, newNonce); return; } } }
ModuleERC165.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; abstract contract ModuleERC165 { /** * @notice Query if a contract implements an interface * @param _interfaceID The interface identifier, as specified in ERC-165 * @dev Adding new hooks will not lead to them being reported by this function * without upgrading the wallet. In addition, developers must ensure that * all inherited contracts by the main module don't conflict and are accounted * to be supported by the supportsInterface method. * @return `true` if the contract implements `_interfaceID` */ function supportsInterface(bytes4 _interfaceID) virtual public pure returns (bool) { return _interfaceID == this.supportsInterface.selector; } }
ModuleUpdate.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; import "./interfaces/IModuleUpdate.sol"; import "./Implementation.sol"; import "./ModuleSelfAuth.sol"; import "./ModuleERC165.sol"; import "../../utils/LibAddress.sol"; contract ModuleUpdate is IModuleUpdate, ModuleERC165, ModuleSelfAuth, Implementation { using LibAddress for address; event ImplementationUpdated(address newImplementation); /** * @notice Updates the implementation of the base wallet * @param _implementation New main module implementation * @dev WARNING Updating the implementation can brick the wallet */ function updateImplementation(address _implementation) external override virtual onlySelf { _updateImplementation(_implementation); } /** * @notice Updates the implementation of the base wallet, used internally. * @param _implementation New main module implementation * @dev WARNING Updating the implementation can brick the wallet */ function _updateImplementation(address _implementation) internal override virtual { if (!_implementation.isContract()) revert InvalidImplementation(_implementation); _setImplementation(_implementation); emit ImplementationUpdated(_implementation); } /** * @notice Query if a contract implements an interface * @param _interfaceID The interface identifier, as specified in ERC-165 * @return `true` if the contract implements `_interfaceID` */ function supportsInterface(bytes4 _interfaceID) public override virtual pure returns (bool) { if (_interfaceID == type(IModuleUpdate).interfaceId) { return true; } return super.supportsInterface(_interfaceID); } }
ModuleCreator.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; import "./interfaces/IModuleCreator.sol"; import "./ModuleSelfAuth.sol"; import "./ModuleERC165.sol"; contract ModuleCreator is IModuleCreator, ModuleERC165, ModuleSelfAuth { event CreatedContract(address _contract); /** * @notice Creates a contract forwarding eth value * @param _code Creation code of the contract * @return addr The address of the created contract */ function createContract(bytes memory _code) public override virtual payable onlySelf returns (address addr) { assembly { addr := create(callvalue(), add(_code, 32), mload(_code)) } if (addr == address(0)) revert CreateFailed(_code); emit CreatedContract(addr); } /** * @notice Query if a contract implements an interface * @param _interfaceID The interface identifier, as specified in ERC-165 * @return `true` if the contract implements `_interfaceID` */ function supportsInterface(bytes4 _interfaceID) public override virtual pure returns (bool) { if (_interfaceID == type(IModuleCreator).interfaceId) { return true; } return super.supportsInterface(_interfaceID); } }
ModuleStorage.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; library ModuleStorage { 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) } } }
Implementation.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; /** * @dev Allows modules to access the implementation slot */ contract Implementation { /** * @notice Updates the Wallet implementation * @param _imp New implementation address * @dev The wallet implementation is stored on the storage slot * defined by the address of the wallet itself * WARNING updating this value may break the wallet and users * must be confident that the new implementation is safe. */ function _setImplementation(address _imp) internal { assembly { sstore(address(), _imp) } } /** * @notice Returns the Wallet implementation * @return _imp The address of the current Wallet implementation */ function _getImplementation() internal view returns (address _imp) { assembly { _imp := sload(address()) } } }
ModuleSelfAuth.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; contract ModuleSelfAuth { error OnlySelfAuth(address _sender, address _self); modifier onlySelf() { if (msg.sender != address(this)) { revert OnlySelfAuth(msg.sender, address(this)); } _; } }
MainModuleUpgradableDuo.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; import "./commons/ModuleAuthUpgradableDuo.sol"; import "./commons/ModuleHooks.sol"; import "./commons/ModuleCalls.sol"; import "./commons/ModuleUpdate.sol"; import "./commons/ModuleCreator.sol"; import "./commons/ModuleExtraAuth.sol"; import "./commons/ModuleAuthConvenience.sol"; /** * @notice Contains an alternative to `MainModuleUpgradable` that allows defining a different * imageHash for `execute` and ERC-1271 (`isValidSignature`) calls. * * @dev This implementation does not include the `ModuleExtraAuth` functionality. * */ contract MainModuleUpgradableDuo is ModuleAuthUpgradableDuo, ModuleCalls, ModuleUpdate, ModuleHooks, ModuleCreator, ModuleAuthConvenience { function _isValidImage( bytes32 _imageHash ) internal override( IModuleAuth, ModuleAuthUpgradable ) view returns (bool) { return super._isValidImage(_imageHash); } function isValidSignature( bytes calldata _data, bytes calldata _signatures ) public override( ModuleAuth, ModuleAuthUpgradableDuo ) virtual view returns (bytes4) { return super.isValidSignature(_data, _signatures); } function isValidSignature( bytes32 _hash, bytes calldata _signatures ) public override( ModuleAuth, ModuleAuthUpgradableDuo ) virtual view returns (bytes4) { return super.isValidSignature(_hash, _signatures); } /** * @notice Query if a contract implements an interface * @param _interfaceID The interface identifier, as specified in ERC-165 * @dev If using a new main module, developers must ensure that all inherited * contracts by the main module don't conflict and are accounted for to be * supported by the supportsInterface method. * @return `true` if the contract implements `_interfaceID` */ function supportsInterface( bytes4 _interfaceID ) public override( ModuleAuthUpgradableDuo, ModuleAuthConvenience, ModuleCalls, ModuleUpdate, ModuleHooks, ModuleCreator ) pure returns (bool) { return super.supportsInterface(_interfaceID); } }
ModuleExtraAuth.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; import "./ModuleAuth.sol"; import "./ModuleStorage.sol"; import "./ModuleSelfAuth.sol"; import "./ModuleERC165.sol"; abstract contract ModuleExtraAuth is ModuleERC165, ModuleSelfAuth, ModuleAuth { // EXTRA_IMAGE_HASH_KEY = keccak256("org.sequence.module.static.auth.extra.image.hash"); bytes32 private constant EXTRA_IMAGE_HASH_KEY = bytes32(0x849e7bdc245db17e50b9f43086f1914d70eb4dab6dd89af4d541d53353ad97de); event SetExtraImageHash(bytes32 indexed _imageHash, uint256 _expiration); function _writeExpirationForImageHash(bytes32 _imageHash, uint256 _expiration) internal { ModuleStorage.writeBytes32Map(EXTRA_IMAGE_HASH_KEY, _imageHash, bytes32(_expiration)); } function _readExpirationForImageHash(bytes32 _imageHash) internal view returns (uint256 _expiration) { return uint256(ModuleStorage.readBytes32Map(EXTRA_IMAGE_HASH_KEY, _imageHash)); } function _isValidImage(bytes32 _imageHash) internal override virtual view returns (bool) { if (super._isValidImage(_imageHash)) { return true; } uint256 expiration = _readExpirationForImageHash(_imageHash); // solhint-disable-next-line not-rely-on-time return expiration != 0 && expiration > block.timestamp; } function extraImageHash(bytes32 _imageHash) public view returns (uint256) { return _readExpirationForImageHash(_imageHash); } function setExtraImageHash(bytes32 _imageHash, uint256 _expiration) external onlySelf { _writeExpirationForImageHash(_imageHash, _expiration); emit SetExtraImageHash(_imageHash, _expiration); } function clearExtraImageHashes(bytes32[] calldata _imageHashes) external onlySelf { unchecked { uint256 imageHashesLength = _imageHashes.length; for (uint256 i = 0; i < imageHashesLength; i++) { bytes32 imageHash = _imageHashes[i]; _writeExpirationForImageHash(imageHash, 0); emit SetExtraImageHash(imageHash, 0); } } } /** * @notice Query if a contract implements an interface * @param _interfaceID The interface identifier, as specified in ERC-165 * @return `true` if the contract implements `_interfaceID` */ function supportsInterface(bytes4 _interfaceID) public override ( ModuleERC165, ModuleAuth ) virtual pure returns (bool) { if (_interfaceID == type(ModuleExtraAuth).interfaceId) { return true; } return super.supportsInterface(_interfaceID); } }
IERC223Receiver.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; interface IERC223Receiver { function tokenFallback(address, uint256, bytes calldata) external; }
IERC721Receiver.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; interface IERC721Receiver { function onERC721Received(address, address, uint256, bytes calldata) external returns (bytes4); }
ModuleAuthUpgradable.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; import "./interfaces/IModuleAuthUpgradable.sol"; import "./ModuleSelfAuth.sol"; import "./ModuleAuth.sol"; import "./ModuleStorage.sol"; abstract contract ModuleAuthUpgradable is IModuleAuthUpgradable, ModuleSelfAuth, ModuleAuth { /** * @notice Updates the signers configuration of the wallet * @param _imageHash New required image hash of the signature */ function _updateImageHash(bytes32 _imageHash) internal override virtual { if (_imageHash == bytes32(0)) revert ImageHashIsZero(); ModuleStorage.writeBytes32(IMAGE_HASH_KEY, _imageHash); emit ImageHashUpdated(_imageHash); } /** * @notice Returns the current image hash of the wallet */ function imageHash() external override virtual view returns (bytes32) { return ModuleStorage.readBytes32(IMAGE_HASH_KEY); } /** * @notice Validates the signature image with a valid image hash defined * in the contract storage * @param _imageHash Hash image of signature * @return true if the signature image is valid */ function _isValidImage(bytes32 _imageHash) internal override virtual view returns (bool) { return _imageHash != bytes32(0) && _imageHash == ModuleStorage.readBytes32(IMAGE_HASH_KEY); } /** * @notice Query if a contract implements an interface * @param _interfaceID The interface identifier, as specified in ERC-165 * @return `true` if the contract implements `_interfaceID` */ function supportsInterface(bytes4 _interfaceID) public override virtual pure returns (bool) { if (_interfaceID == type(IModuleAuthUpgradable).interfaceId) { return true; } return super.supportsInterface(_interfaceID); } }
IERC1155Receiver.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; interface IERC1155Receiver { function onERC1155Received(address, address, uint256, uint256, bytes calldata) external returns (bytes4); function onERC1155BatchReceived(address, address, uint256[] calldata, uint256[] calldata, bytes calldata) external returns (bytes4); }
ModuleAuthConvenience.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; import "./ModuleSelfAuth.sol"; import "./ModuleAuth.sol"; import "./ModuleIPFS.sol"; import "./ModuleERC165.sol"; import "../../utils/LibString.sol"; abstract contract ModuleAuthConvenience is ModuleERC165, ModuleSelfAuth, ModuleAuth, ModuleIPFS { /** * @notice Updates the image hash and the IPFS root in a single operation. * @dev These two operations are often performed together, so this function * allows to save some gas by performing them in a single step. * * @param _imageHash The new image hash to be set. * @param _ipfsRoot The new IPFS root to be set. */ function updateImageHashAndIPFS( bytes32 _imageHash, bytes32 _ipfsRoot ) external onlySelf { _updateImageHash(_imageHash); _updateIPFSRoot(_ipfsRoot); } /** * @notice Query if a contract implements an interface * @param _interfaceID The interface identifier, as specified in ERC-165 * @return `true` if the contract implements `_interfaceID` */ function supportsInterface(bytes4 _interfaceID) public override ( ModuleERC165, ModuleAuth ) virtual pure returns (bool) { if (_interfaceID == type(ModuleAuthConvenience).interfaceId) { return true; } return super.supportsInterface(_interfaceID); } }
ModuleOnlyDelegatecall.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; contract ModuleOnlyDelegatecall { address private immutable self; error OnlyDelegatecall(); constructor() { self = address(this); } /** * @notice Modifier that only allows functions to be called via delegatecall. */ modifier onlyDelegatecall() { if (address(this) == self) { revert OnlyDelegatecall(); } _; } }
IModuleAuth.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; abstract contract IModuleAuth { // IMAGE_HASH_KEY = keccak256("org.arcadeum.module.auth.upgradable.image.hash"); bytes32 internal constant IMAGE_HASH_KEY = bytes32(0xea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8); event ImageHashUpdated(bytes32 newImageHash); // Errors error ImageHashIsZero(); error InvalidSignatureType(bytes1 _type); function _signatureValidation( bytes32 _digest, bytes calldata _signature ) internal virtual view returns ( bool isValid, bytes32 subdigest ); function signatureRecovery( bytes32 _digest, bytes calldata _signature ) public virtual view returns ( uint256 threshold, uint256 weight, bytes32 imageHash, bytes32 subdigest, uint256 checkpoint ); /** * @notice Validates the signature image * @return true if the signature image is valid */ function _isValidImage(bytes32) internal virtual view returns (bool) { return false; } /** * @notice Updates the signers configuration of the wallet * @param _imageHash New required image hash of the signature */ function updateImageHash(bytes32 _imageHash) external virtual; /** * @notice Updates the signers configuration of the wallet * @param _imageHash New required image hash of the signature */ function _updateImageHash(bytes32 _imageHash) internal virtual; }
ModuleAuthUpgradableDuo.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; import "./ModuleAuthUpgradable.sol"; import "./ModuleSelfAuth.sol"; import "./ModuleAuth.sol"; import "./ModuleStorage.sol"; abstract contract ModuleAuthUpgradableDuo is ModuleAuthUpgradable { // EXTERNAL_IMAGE_HASH_KEY = keccak256("org.sequence.module.auth.upgradable.image.hash.external"); bytes32 internal constant EXTERNAL_IMAGE_HASH_KEY = bytes32(0x8c8764b3a50fee69c9bee6e956047501f434fb0e2349c75844a401a7f2a020d2); event ExternalImageHashUpdated(bytes32 newExternalImageHash); error ExternalImageHashIsZero(); /** * @notice Updates the external image hash of the wallet * @param _imageHash New required image hash of the ERC-1271 signatures */ function updateExternalImageHash(bytes32 _imageHash) external onlySelf { // Update imageHash in storage if (_imageHash == bytes32(0)) revert ExternalImageHashIsZero(); ModuleStorage.writeBytes32(EXTERNAL_IMAGE_HASH_KEY, _imageHash); emit ExternalImageHashUpdated(_imageHash); } /** * @notice Returns the current external image hash of the wallet (only for `ERC-1271`) */ function externalImageHash() external view returns (bytes32) { return ModuleStorage.readBytes32(EXTERNAL_IMAGE_HASH_KEY); } /** * @notice Validates the signature image with a valid image hash defined * in the contract storage, for external calls (only for `ERC-1271`) * @param _imageHash Hash image of signature * @return true if the signature image is valid */ function _isValidExternalImage(bytes32 _imageHash) internal view returns (bool) { return _imageHash != bytes32(0) && _imageHash == ModuleStorage.readBytes32(EXTERNAL_IMAGE_HASH_KEY); } /** * @dev Validates a signature (externally). * * @param _digest Digest of the signed data. * @param _signature A Sequence signature. * * @return isValid Indicates whether the signature is valid or not. * @return subdigest A modified version of the original digest, unique for each wallet/network. * * @notice The signature will be checked against the EXTERNAL image hash. * This method is only used for external calls (only for `ERC-1271`). */ function _signatureValidationExternal( bytes32 _digest, bytes calldata _signature ) internal view returns ( bool isValid, bytes32 subdigest ) { uint256 threshold; uint256 weight; bytes32 recoveredImageHash; (threshold, weight, recoveredImageHash, subdigest,) = signatureRecovery(_digest, _signature); isValid = weight >= threshold && _isValidExternalImage(recoveredImageHash); } /** * @notice Verifies whether the provided signature is valid with respect to the provided data * @dev MUST return the correct magic value if the signature provided is valid for the provided data * > The bytes4 magic value to return when signature is valid is 0x20c13b0b : bytes4(keccak256("isValidSignature(bytes,bytes)")) * @param _data Arbitrary length data signed on the behalf of address(this) * @param _signatures Signature byte array associated with _data. * Encoded as abi.encode(Signature[], Configs) * @return magicValue Magic value 0x20c13b0b if the signature is valid and 0x0 otherwise */ function isValidSignature( bytes calldata _data, bytes calldata _signatures ) public override(ModuleAuth) virtual view returns (bytes4) { // Validate signatures (bool isValid,) = _signatureValidationExternal(keccak256(_data), _signatures); if (isValid) { return SELECTOR_ERC1271_BYTES_BYTES; } return bytes4(0); } /** * @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)")) * @param _hash keccak256 hash that was signed * @param _signatures Signature byte array associated with _data. * Encoded as abi.encode(Signature[], Configs) * @return magicValue Magic value 0x1626ba7e if the signature is valid and 0x0 otherwise */ function isValidSignature( bytes32 _hash, bytes calldata _signatures ) public override(ModuleAuth) virtual view returns (bytes4) { // Validate signatures (bool isValid,) = _signatureValidationExternal(_hash, _signatures); if (isValid) { return SELECTOR_ERC1271_BYTES32_BYTES; } return bytes4(0); } /** * @notice Query if a contract implements an interface * @param _interfaceID The interface identifier, as specified in ERC-165 * @return `true` if the contract implements `_interfaceID` */ function supportsInterface(bytes4 _interfaceID) public override virtual pure returns (bool) { if (_interfaceID == type(ModuleAuthUpgradableDuo).interfaceId) { return true; } return super.supportsInterface(_interfaceID); } }
IModuleCalls.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; interface IModuleCalls { // Events event TxFailed(bytes32 indexed _tx, uint256 _index, bytes _reason); event TxExecuted(bytes32 indexed _tx, uint256 _index); // Errors error NotEnoughGas(uint256 _index, uint256 _requested, uint256 _available); error InvalidSignature(bytes32 _hash, bytes _signature); // Transaction structure struct Transaction { bool delegateCall; // Performs delegatecall bool revertOnError; // Reverts transaction bundle if tx fails uint256 gasLimit; // Maximum gas to be forwarded address target; // Address of the contract to call uint256 value; // Amount of ETH to pass with the call bytes data; // calldata to pass } /** * @notice Allow wallet owner to execute an action * @param _txs Transactions to process * @param _nonce Signature nonce (may contain an encoded space) * @param _signature Encoded signature */ function execute( Transaction[] calldata _txs, uint256 _nonce, bytes calldata _signature ) external; /** * @notice Allow wallet to execute an action * without signing the message * @param _txs Transactions to execute */ function selfExecute( Transaction[] calldata _txs ) external; }
IModuleHooks.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; interface IModuleHooks { // Errors error HookAlreadyExists(bytes4 _signature); error HookDoesNotExist(bytes4 _signature); // Events event DefinedHook(bytes4 _signature, address _implementation); /** * @notice Reads the implementation hook of a signature * @param _signature Signature function * @return The address of the implementation hook, address(0) if none */ function readHook(bytes4 _signature) external view returns (address); /** * @notice Adds a new hook to handle a given function selector * @param _signature Signature function linked to the hook * @param _implementation Hook implementation contract */ function addHook(bytes4 _signature, address _implementation) external; /** * @notice Removes a registered hook * @param _signature Signature function linked to the hook */ function removeHook(bytes4 _signature) external; }
IModuleUpdate.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; abstract contract IModuleUpdate { // Errors error InvalidImplementation(address _implementation); /** * @notice Updates the implementation of the base wallet * @param _implementation New main module implementation * @dev WARNING Updating the implementation can brick the wallet */ function updateImplementation(address _implementation) external virtual; /** * @notice Updates the implementation of the base wallet, used internally. * @param _implementation New main module implementation * @dev WARNING Updating the implementation can brick the wallet */ function _updateImplementation(address _implementation) internal virtual; }
IModuleCreator.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; interface IModuleCreator { error CreateFailed(bytes _code); /** * @notice Creates a contract forwarding eth value * @param _code Creation code of the contract * @return addr The address of the created contract */ function createContract(bytes calldata _code) external payable returns (address addr); }
SequenceBaseSig.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; import "../../../../utils/SignatureValidator.sol"; import "../../../../utils/LibBytesPointer.sol"; import "../../../../utils/LibBytes.sol"; import "../../../../utils/LibOptim.sol"; /** * @title SequenceBaseSig Library * @author Agustin Aguilar (aa@horizon.io) * @notice A Solidity implementation for handling signatures in the Sequence protocol. */ library SequenceBaseSig { using LibBytesPointer for bytes; uint256 internal constant FLAG_SIGNATURE = 0; uint256 internal constant FLAG_ADDRESS = 1; uint256 internal constant FLAG_DYNAMIC_SIGNATURE = 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; error InvalidNestedSignature(bytes32 _hash, address _addr, bytes _signature); error InvalidSignatureFlag(uint256 _flag); /** * @notice Generates a subdigest for the input digest (unique for this wallet and network). * @param _digest The input digest to generate the subdigest from. * @return bytes32 The subdigest generated from the input digest. */ function subdigest( bytes32 _digest ) internal view returns (bytes32) { return keccak256( abi.encodePacked( "\x19\x01", block.chainid, address(this), _digest ) ); } /** * @notice Generates the leaf for an address and weight. * @dev The leaf is generated by concatenating the address and weight. * * @param _addr The address to generate the leaf for. * @param _weight The weight to generate the leaf for. * @return bytes32 The leaf generated from the address and weight. */ function _leafForAddressAndWeight( address _addr, uint96 _weight ) internal pure returns (bytes32) { unchecked { return bytes32(uint256(_weight) << 160 | uint256(uint160(_addr))); } } /** * @notice Generates the leaf for a hardcoded subdigest. * @dev The leaf is generated by hashing 'Sequence static digest:\n' and the subdigest. * @param _subdigest The subdigest to generate the leaf for. * @return bytes32 The leaf generated from the hardcoded subdigest. */ function _leafForHardcodedSubdigest( bytes32 _subdigest ) internal pure returns (bytes32) { return keccak256(abi.encodePacked('Sequence static digest:\n', _subdigest)); } /** * @notice Generates the leaf for a nested tree node. * @dev The leaf is generated by hashing 'Sequence nested config:\n', the node, the threshold and the weight. * * @param _node The root of the node to generate the leaf for. * @param _threshold The internal threshold of the tree. * @param _weight The external weight of the tree. * @return bytes32 The leaf generated from the nested tree. */ function _leafForNested( bytes32 _node, uint256 _threshold, uint256 _weight ) internal pure returns (bytes32) { return keccak256(abi.encodePacked('Sequence nested config:\n', _node, _threshold, _weight)); } /** * @notice Returns the weight and root of a signature branch. * @dev If the signature contains a hardcoded subdigest, and it matches the input digest, then the weight is set to 2 ** 256 - 1. * * @param _subdigest The digest to verify the signature against. * @param _signature The signature branch to recover. * @return weight The total weight of the recovered signatures. * @return root The root hash of the recovered configuration. */ function recoverBranch( bytes32 _subdigest, bytes calldata _signature ) internal view returns ( uint256 weight, bytes32 root ) { unchecked { uint256 rindex; // Iterate until the image is completed while (rindex < _signature.length) { // Read next item type uint256 flag; (flag, rindex) = _signature.readUint8(rindex); if (flag == FLAG_ADDRESS) { // Read plain address uint8 addrWeight; address addr; (addrWeight, addr, rindex) = _signature.readUint8Address(rindex); // Write weight and address to image bytes32 node = _leafForAddressAndWeight(addr, addrWeight); root = root != bytes32(0) ? LibOptim.fkeccak256(root, node) : node; continue; } if (flag == FLAG_SIGNATURE) { // Read weight uint8 addrWeight; (addrWeight, rindex) = _signature.readUint8(rindex); // Read single signature and recover signer uint256 nrindex = rindex + 66; address addr = SignatureValidator.recoverSigner(_subdigest, _signature[rindex:nrindex]); rindex = nrindex; // Acumulate total weight of the signature weight += addrWeight; // Write weight and address to image bytes32 node = _leafForAddressAndWeight(addr, addrWeight); root = root != bytes32(0) ? LibOptim.fkeccak256(root, node) : node; continue; } if (flag == FLAG_DYNAMIC_SIGNATURE) { // Read signer and weight uint8 addrWeight; address addr; (addrWeight, addr, rindex) = _signature.readUint8Address(rindex); // Read signature size uint256 size; (size, rindex) = _signature.readUint24(rindex); // Read dynamic size signature uint256 nrindex = rindex + size; if (!SignatureValidator.isValidSignature(_subdigest, addr, _signature[rindex:nrindex])) { revert InvalidNestedSignature(_subdigest, addr, _signature[rindex:nrindex]); } rindex = nrindex; // Acumulate total weight of the signature weight += addrWeight; // Write weight and address to image bytes32 node = _leafForAddressAndWeight(addr, addrWeight); root = root != bytes32(0) ? LibOptim.fkeccak256(root, node) : node; continue; } if (flag == FLAG_NODE) { // Read node hash bytes32 node; (node, rindex) = _signature.readBytes32(rindex); root = root != bytes32(0) ? LibOptim.fkeccak256(root, node) : node; continue; } if (flag == FLAG_BRANCH) { // Enter a branch of the signature merkle tree uint256 size; (size, rindex) = _signature.readUint24(rindex); uint256 nrindex = rindex + size; uint256 nweight; bytes32 node; (nweight, node) = recoverBranch(_subdigest, _signature[rindex:nrindex]); weight += nweight; root = LibOptim.fkeccak256(root, node); rindex = nrindex; continue; } if (flag == FLAG_NESTED) { // Enter a branch of the signature merkle tree // but with an internal threshold and an external fixed weight uint256 externalWeight; (externalWeight, rindex) = _signature.readUint8(rindex); uint256 internalThreshold; (internalThreshold, rindex) = _signature.readUint16(rindex); uint256 size; (size, rindex) = _signature.readUint24(rindex); uint256 nrindex = rindex + size; uint256 internalWeight; bytes32 internalRoot; (internalWeight, internalRoot) = recoverBranch(_subdigest, _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; } if (flag == FLAG_SUBDIGEST) { // A hardcoded always accepted digest // it pushes the weight to the maximum bytes32 hardcoded; (hardcoded, rindex) = _signature.readBytes32(rindex); if (hardcoded == _subdigest) { weight = type(uint256).max; } bytes32 node = _leafForHardcodedSubdigest(hardcoded); root = root != bytes32(0) ? LibOptim.fkeccak256(root, node) : node; continue; } revert InvalidSignatureFlag(flag); } } } /** * @notice Returns the threshold, weight, root, and checkpoint of a signature. * @dev To verify the signature, the weight must be greater than or equal to the threshold, and the root * must match the expected `imageHash` of the wallet. * * @param _subdigest The digest to verify the signature against. * @param _signature The signature to recover. * @return threshold The minimum weight required for the signature to be valid. * @return weight The total weight of the recovered signatures. * @return imageHash The root hash of the recovered configuration * @return checkpoint The checkpoint of the signature. */ function recover( bytes32 _subdigest, bytes calldata _signature ) internal view returns ( uint256 threshold, uint256 weight, bytes32 imageHash, uint256 checkpoint ) { unchecked { (weight, imageHash) = recoverBranch(_subdigest, _signature[6:]); // Threshold & checkpoint are the top nodes // (but they are first on the signature) threshold = LibBytes.readFirstUint16(_signature); checkpoint = LibBytes.readUint32(_signature, 2); imageHash = LibOptim.fkeccak256(imageHash, bytes32(threshold)); imageHash = LibOptim.fkeccak256(imageHash, bytes32(checkpoint)); } } }
SubModuleNonce.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; library SubModuleNonce { // Nonce schema // // - space[160]:nonce[96] // uint256 internal constant NONCE_BITS = 96; bytes32 internal constant NONCE_MASK = bytes32(uint256(type(uint96).max)); /** * @notice Decodes a raw nonce * @dev Schema: space[160]:type[96] * @param _rawNonce Nonce to be decoded * @return _space The nonce space of the raw nonce * @return _nonce The nonce of the raw nonce */ function decodeNonce(uint256 _rawNonce) internal pure returns ( uint256 _space, uint256 _nonce ) { unchecked { // Decode nonce _space = _rawNonce >> NONCE_BITS; _nonce = uint256(bytes32(_rawNonce) & NONCE_MASK); } } function encodeNonce(uint256 _space, uint256 _nonce) internal pure returns (uint256) { unchecked { // Combine space and nonce return (_space << NONCE_BITS) | _nonce; } } }
IModuleAuthUpgradable.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; interface IModuleAuthUpgradable { /** * @notice Returns the current image hash of the wallet */ function imageHash() external view returns (bytes32); }
SequenceChainedSig.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; import "./SequenceBaseSig.sol"; import "../../interfaces/IModuleAuth.sol"; import "../../ModuleSelfAuth.sol"; import "../../ModuleStorage.sol"; import "../../../../utils/LibBytesPointer.sol"; import "../../../../utils/LibOptim.sol"; /** * @title Sequence chained auth recovery submodule * @author Agustin Aguilar (aa@horizon.io) * @notice Defines Sequence signatures that work by delegating control to new configurations. * @dev The delegations can be chained together, the first signature is the one that is used to validate * the message, the last signature must match the current on-chain configuration of the wallet. */ abstract contract SequenceChainedSig is IModuleAuth, ModuleSelfAuth { using LibBytesPointer for bytes; bytes32 public constant SET_IMAGE_HASH_TYPE_HASH = keccak256("SetImageHash(bytes32 imageHash)"); error LowWeightChainedSignature(bytes _signature, uint256 threshold, uint256 _weight); error WrongChainedCheckpointOrder(uint256 _current, uint256 _prev); /** * @notice Defined the special token that must be signed to delegate control to a new configuration. * @param _imageHash The hash of the new configuration. * @return bytes32 The message hash to be signed. */ function _hashSetImageHashStruct(bytes32 _imageHash) internal pure returns (bytes32) { return LibOptim.fkeccak256(SET_IMAGE_HASH_TYPE_HASH, _imageHash); } /** * @notice Returns the threshold, weight, root, and checkpoint of a (chained) signature. * * @dev This method return the `threshold`, `weight` and `imageHash` of the last signature in the chain. * Intermediate signatures are validated directly in this method. The `subdigest` is the one of the * first signature in the chain (since that's the one that is used to validate the message). * * @param _digest The digest to recover the signature from. * @param _signature The signature to recover. * @return threshold The threshold of the (last) signature. * @return weight The weight of the (last) signature. * @return imageHash The image hash of the (last) signature. * @return subdigest The subdigest of the (first) signature in the chain. * @return checkpoint The checkpoint of the (last) signature. */ function chainedRecover( bytes32 _digest, bytes calldata _signature ) internal view returns ( uint256 threshold, uint256 weight, bytes32 imageHash, bytes32 subdigest, uint256 checkpoint ) { uint256 rindex = 1; uint256 sigSize; // // First signature out of the loop // // First uint24 is the size of the signature (sigSize, rindex) = _signature.readUint24(rindex); uint256 nrindex = sigSize + rindex; ( threshold, weight, imageHash, subdigest, checkpoint ) = signatureRecovery( _digest, _signature[rindex:nrindex] ); if (weight < threshold) { revert LowWeightChainedSignature(_signature[rindex:nrindex], threshold, weight); } rindex = nrindex; // The following signatures are handled by this loop. // This is done this way because the first signature does not have a // checkpoint to be validated against. while (rindex < _signature.length) { // First uint24 is the size of the signature (sigSize, rindex) = _signature.readUint24(rindex); nrindex = sigSize + rindex; uint256 nextCheckpoint; ( threshold, weight, imageHash,, // Do not change the subdigest; // it should remain that of the first signature. nextCheckpoint ) = signatureRecovery( _hashSetImageHashStruct(imageHash), _signature[rindex:nrindex] ); // Validate signature if (weight < threshold) { revert LowWeightChainedSignature(_signature[rindex:nrindex], threshold, weight); } // Checkpoints must be provided in descending order // since the first signature is the one that is used to validate the message // and the last signature is the one that is used to validate the current configuration if (nextCheckpoint >= checkpoint) { revert WrongChainedCheckpointOrder(nextCheckpoint, checkpoint); } checkpoint = nextCheckpoint; rindex = nrindex; } } }
SequenceDynamicSig.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; import "./SequenceBaseSig.sol"; library SequenceDynamicSig { /** * @notice Recover a "dynamically encoded" Sequence signature. * @dev The Signature is stripped of the first byte, which is the encoding flag. * * @param _subdigest The digest of the signature. * @param _signature The Sequence signature. * @return threshold The threshold weight required to validate the signature. * @return weight The weight of the signature. * @return imageHash The hash of the recovered configuration. * @return checkpoint The checkpoint of the configuration. */ function recover( bytes32 _subdigest, bytes calldata _signature ) internal view returns ( uint256 threshold, uint256 weight, bytes32 imageHash, uint256 checkpoint ) { return SequenceBaseSig.recover(_subdigest, _signature[1:]); } }
SequenceNoChainIdSig.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; library SequenceNoChainIdSig { /** * @notice Computes a subdigest for a Sequence signature that works on all chains. * @dev The subdigest is computed by removing the chain ID from the digest (using 0 instead). * @param _digest The digest of the chain of signatures. * @return bytes32 The subdigest with no chain ID. */ function subdigest(bytes32 _digest) internal view returns (bytes32) { return keccak256( abi.encodePacked( "\x19\x01", uint256(0), address(this), _digest ) ); } }
Gas Token: