Address: 0x94Fb1E5196B4eE5A1c9ad737a505CE12bAe7Ca85
Balance (XRP): 0 XRP
Bytecode: 0x6080604052600436106101485760003560e01c806361c2926c116100c05780639bd58b1611610074578063b93ea7ad11610059578063b93ea7ad1461043b578063bc197c811461045b578063f23a6e611461047b5761014f565b80639bd58b1614610411578063affed0e0146104265761014f565b80638976c44c116100a55780638976c44c146103be5780638c3f5563146103de57806390042baf146103fe5761014f565b806361c2926c1461037e5780637a9a16281461039e5761014f565b80631a9b23371161011757806329561426116100fc578063295614261461031c5780634fcf3eca1461033c57806351605d801461035c5761014f565b80631a9b2337146102cf57806320c13b0b146102fc5761014f565b806301ffc9a71461022a578063025b22bc14610260578063150b7a02146102825780631626ba7e146102af5761014f565b3661014f57005b600061017e6000357fffffffff000000000000000000000000000000000000000000000000000000001661049b565b905073ffffffffffffffffffffffffffffffffffffffff811615610227576000808273ffffffffffffffffffffffffffffffffffffffff166000366040518083838082843760405192019450600093509091505080830381855af49150503d8060008114610208576040519150601f19603f3d011682016040523d82523d6000602084013e61020d565b606091505b50915091508161021f57805160208201fd5b805160208201f35b50005b34801561023657600080fd5b5061024a610245366004612aad565b6104f1565b6040516102579190612cc4565b60405180910390f35b34801561026c57600080fd5b5061028061027b3660046127fa565b6104fc565b005b34801561028e57600080fd5b506102a261029d3660046128cb565b61061d565b6040516102579190612cf1565b3480156102bb57600080fd5b506102a26102ca366004612a63565b610647565b3480156102db57600080fd5b506102ef6102ea366004612aad565b61065e565b6040516102579190612ca3565b34801561030857600080fd5b506102a2610317366004612af9565b610669565b34801561032857600080fd5b50610280610337366004612a4b565b610682565b34801561034857600080fd5b50610280610357366004612aad565b610790565b34801561036857600080fd5b5061037161086e565b6040516102579190612ccf565b34801561038a57600080fd5b506102806103993660046129ae565b61089e565b3480156103aa57600080fd5b506102806103b93660046129e1565b610937565b3480156103ca57600080fd5b506102806103d9366004612a4b565b6109b3565b3480156103ea57600080fd5b506103716103f9366004612a4b565b610ac1565b6102ef61040c366004612b62565b610aed565b34801561041d57600080fd5b50610371610ba1565b34801561043257600080fd5b50610371610bcc565b34801561044757600080fd5b50610280610456366004612ac7565b610bd8565b34801561046757600080fd5b506102a2610476366004612814565b610cb1565b34801561048757600080fd5b506102a2610496366004612938565b610cde565b60006104e97fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1207fffffffff000000000000000000000000000000000000000000000000000000008416610d09565b90505b919050565b60006104e982610d36565b333014610554576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061331e6027913960400191505060405180910390fd5b6105738173ffffffffffffffffffffffffffffffffffffffff16610d93565b6105c8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260398152602001806132046039913960400191505060405180910390fd5b6105d181610d99565b6040805173ffffffffffffffffffffffffffffffffffffffff8316815290517f310ba5f1d2ed074b51e2eccd052a47ae9ab7c6b800d1fca3db3999d6a592ca039181900360200190a150565b7f150b7a020000000000000000000000000000000000000000000000000000000095945050505050565b6000610654848484610d9d565b90505b9392505050565b60006104e98261049b565b600061067785858585610def565b90505b949350505050565b3330146106da576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061331e6027913960400191505060405180910390fd5b80610730576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526037815260200180612fb36037913960400191505060405180910390fd5b61075a7fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf882610e57565b6040805182815290517f307ed6bd941ee9fc80f369c94af5fa11e25bab5102a6140191756c5474a30bfa9181900360200190a150565b3330146107e8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061331e6027913960400191505060405180910390fd5b60006107f38261049b565b73ffffffffffffffffffffffffffffffffffffffff161415610860576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b815260200180612fea602b913960400191505060405180910390fd5b61086b816000610e5b565b50565b60006108997fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8610ebe565b905090565b3330146108f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061331e6027913960400191505060405180910390fd5b60006109278260405160200161090c9190612e0f565b60405160208183030381529060405280519060200120610ec2565b90506109338183610f22565b5050565b610940826110f1565b6000610958838560405160200161090c929190612e56565b90506109648183611195565b6109a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161099a90612db2565b60405180910390fd5b6109ad8185610f22565b50505050565b333014610a0b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061331e6027913960400191505060405180910390fd5b80610a61576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526042815260200180612f716042913960600191505060405180910390fd5b610a8b7f8c8764b3a50fee69c9bee6e956047501f434fb0e2349c75844a401a7f2a020d282610e57565b6040805182815290517f1f63199319eff813052575c41087f618aba07b006664fed6c01f7ee9c57168359181900360200190a150565b60006104e97f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e83610d09565b6000333014610b47576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061331e6027913960400191505060405180910390fd5b81516020830134f06040805173ffffffffffffffffffffffffffffffffffffffff8316815290519192507fa506ad4e7f05eceba62a023c3219e5bd98a615f4fa87e2afb08a2da5cf62bf0c919081900360200190a1919050565b60006108997f8c8764b3a50fee69c9bee6e956047501f434fb0e2349c75844a401a7f2a020d2610ebe565b60006108996000610ac1565b333014610c30576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061331e6027913960400191505060405180910390fd5b6000610c3b8361049b565b73ffffffffffffffffffffffffffffffffffffffff1614610ca7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c815260200180613176602c913960400191505060405180910390fd5b6109338282610e5b565b7fbc197c810000000000000000000000000000000000000000000000000000000098975050505050505050565b7ff23a6e61000000000000000000000000000000000000000000000000000000009695505050505050565b60408051602080820194909452808201929092528051808303820181526060909201905280519101205490565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f90042baf000000000000000000000000000000000000000000000000000000001415610d8a575060016104ec565b6104e98261138d565b3b151590565b3055565b600080610db3610dac86610ec2565b85856114ce565b90508015610de457507f1626ba7e000000000000000000000000000000000000000000000000000000009050610657565b506000949350505050565b600080610e1a610dac87876040518083838082843760405192018290039091209350610ec292505050565b90508015610e4b57507f20c13b0b00000000000000000000000000000000000000000000000000000000905061067a565b50600095945050505050565b9055565b6109337fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1207fffffffff00000000000000000000000000000000000000000000000000000000841673ffffffffffffffffffffffffffffffffffffffff841661185f565b5490565b604080517f19010000000000000000000000000000000000000000000000000000000000006020808301919091524660228301523060601b6042830152605680830194909452825180830390940184526076909101909152815191012090565b60005b81518110156110ec576000828281518110610f3c57fe5b602002602001015190506000606082604001515a1015610f88576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161099a90612d55565b82511561102057826060015173ffffffffffffffffffffffffffffffffffffffff168360400151600014610fc0578360400151610fc2565b5a5b8460a00151604051610fd49190612c87565b6000604051808303818686f4925050503d8060008114611010576040519150601f19603f3d011682016040523d82523d6000602084013e611015565b606091505b5090925090506110b5565b826060015173ffffffffffffffffffffffffffffffffffffffff1683608001518460400151600014611056578460400151611058565b5a5b908560a0015160405161106b9190612c87565b600060405180830381858888f193505050503d80600081146110a9576040519150601f19603f3d011682016040523d82523d6000602084013e6110ae565b606091505b5090925090505b81156110d657856040516110c99190612ccf565b60405180910390a06110e1565b6110e183878361188d565b505050600101610f25565b505050565b6000806110fd836118dd565b91509150600061110c83610ac1565b9050808214611147576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161099a90612d1e565b6001820161115584826118f6565b7f1f180c27086c7a39ea2a7b25239d1ab92348f07ca7bb59d1438fcf527568f8818482604051611186929190612e6f565b60405180910390a15050505050565b60008060006111a384611921565b909250905061ffff821660005b855183101561136a57600080806111c7898761198f565b975060ff918216945016915060018314156111ef576111e68987611a10565b96509050611313565b8261121b5760606112008a88611a88565b9750905061120e8b82611b39565b9150828501945050611313565b60028314156112c25761122e8987611a10565b96509050600061123e8a88611ec3565b975061ffff16905060606112538b8984611f34565b985090506112628c8483612023565b6112b7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260328152602001806131446032913960400191505060405180910390fd5b505092810192611313565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c815260200180612f45602c913960400191505060405180910390fd5b848282604051602001808481526020018381526020018273ffffffffffffffffffffffffffffffffffffffff16815260200193505050506040516020818303038152906040528051906020012094505050506111b0565b8361ffff16811015801561138257506113828261226b565b979650505050505050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fec6aba5000000000000000000000000000000000000000000000000000000000148061142057507fffffffff0000000000000000000000000000000000000000000000000000000082167f4e2312e000000000000000000000000000000000000000000000000000000000145b8061146c57507fffffffff0000000000000000000000000000000000000000000000000000000082167f150b7a0200000000000000000000000000000000000000000000000000000000145b806114b857507fffffffff0000000000000000000000000000000000000000000000000000000082167fc0ee0b8a00000000000000000000000000000000000000000000000000000000145b156114c5575060016104ec565b6104e9826122a8565b600080600061151285858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061192192505050565b909250905061ffff821660005b8583101561183b57600080600061156f868b8b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929392505061198f9050565b975060ff918216945016915060018314156115d0576115c7868b8b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293925050611a109050565b965090506117e4565b8261163557606061161a878c8c8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293925050611a889050565b975090506116288c82611b39565b91508285019450506117e4565b600283141561179357611681868b8b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293925050611a109050565b809750819250505060006116ce878c8c8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293925050611ec39050565b8161ffff1691508098508192505050606061172488838e8e8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929493925050611f349050565b985090506117338d8483612023565b611788576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603f81526020018061309c603f913960400191505060405180910390fd5b5050928101926117e4565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260398152602001806130db6039913960400191505060405180910390fd5b848282604051602001808481526020018381526020018273ffffffffffffffffffffffffffffffffffffffff168152602001935050505060405160208183030381529060405280519060200120945050505061151f565b8361ffff168110158015611853575061185382612305565b98975050505050505050565b6040805160208082019590955280820193909352805180840382018152606090930190528151919092012055565b82602001511561189f57805160208201fd5b7f3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd782826040516118d0929190612cd8565b60405180910390a1505050565b606081901c916bffffffffffffffffffffffff90911690565b6109337f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e838361185f565b6020810151815160f09190911c9060029081111561198a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260278152602001806130386027913960400191505060405180910390fd5b915091565b8082016020015160f881901c9060f01c60ff16600283018381116119af57fe5b8451811115611a09576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806132796026913960400191505060405180910390fd5b9250925092565b8082016020015160601c60148201828111611a2757fe5b8351811115611a81576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260238152602001806130156023913960400191505060405180910390fd5b9250929050565b604080516042808252608082019092526060916000919060208201818036833701905050915082840160200180516020840152602081015160408401526022810151604284015250604283019050828111611adf57fe5b8351811115611a81576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260238152602001806131e16023913960400191505060405180910390fd5b60008151604214611b95576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603a815260200180612f0b603a913960400191505060405180910390fd5b600082600184510381518110611ba757fe5b602001015160f81c60f81b60f81c60ff169050600083604081518110611bc957fe5b016020015160f81c90506000611bdf858261233a565b90506000611bee86602061233a565b90507f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0811115611c69576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180612ece603d913960400191505060405180910390fd5b8260ff16601b14158015611c8157508260ff16601c14155b15611cd7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d81526020018061305f603d913960400191505060405180910390fd5b6001841415611d4b5760018784848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611d3a573d6000803e3d6000fd5b505050602060405103519450611e4d565b6002841415611dfc5760018760405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c018281526020019150506040516020818303038152906040528051906020012084848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611d3a573d6000803e3d6000fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c81526020018061323d603c913960400191505060405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8516611eb9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260308152602001806131146030913960400191505060405180910390fd5b5050505092915050565b8082016020015160f01c60028201828111611eda57fe5b8351811115611a81576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806132c06022913960400191505060405180910390fd5b606060008267ffffffffffffffff81118015611f4f57600080fd5b506040519080825280601f01601f191660200182016040528015611f7a576020820181803683370190505b509150838501602001600060205b85811015611fa157908201518482015260208101611f88565b8486016020018051939092015190850152525082820183811015611fc157fe5b845181111561201b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061329f6021913960400191505060405180910390fd5b935093915050565b6000808260018451038151811061203657fe5b016020015160f81c9050600181148061204f5750600281145b15612093578373ffffffffffffffffffffffffffffffffffffffff166120758685611b39565b73ffffffffffffffffffffffffffffffffffffffff16149150612263565b60038114156122125782517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018452604080517f1626ba7e000000000000000000000000000000000000000000000000000000008152600481018881526024820192835286516044830152865173ffffffffffffffffffffffffffffffffffffffff891693631626ba7e938b938a9390929160640190602085019080838360005b8381101561214d578181015183820152602001612135565b50505050905090810190601f16801561217a5780820380516001836020036101000a031916815260200191505b50935050505060206040518083038186803b15801561219857600080fd5b505afa1580156121ac573d6000803e3d6000fd5b505050506040513d60208110156121c257600080fd5b50519084527fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e00000000000000000000000000000000000000000000000000000000149150612263565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603f8152602001806131a2603f913960400191505060405180910390fd5b509392505050565b600081158015906104e957506122a07fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8610ebe565b909114919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f025b22bc0000000000000000000000000000000000000000000000000000000014156122fc575060016104ec565b6104e9826123a2565b600081158015906104e957506122a07f8c8764b3a50fee69c9bee6e956047501f434fb0e2349c75844a401a7f2a020d2610ebe565b60008160200183511015612399576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c8152602001806132e2603c913960400191505060405180910390fd5b50016020015190565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f389901c70000000000000000000000000000000000000000000000000000000014156123f6575060016104ec565b6104e98260007fffffffff0000000000000000000000000000000000000000000000000000000082167f25bb078800000000000000000000000000000000000000000000000000000000141561244e575060016104ec565b6104e98260007fffffffff0000000000000000000000000000000000000000000000000000000082167f783649a60000000000000000000000000000000000000000000000000000000014156124a6575060016104ec565b6104e98260007fffffffff000000000000000000000000000000000000000000000000000000008216158061251c57507fffffffff0000000000000000000000000000000000000000000000000000000082167f36e7817500000000000000000000000000000000000000000000000000000000145b15612529575060016104ec565b7f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316146104e9565b803573ffffffffffffffffffffffffffffffffffffffff811681146104ec57600080fd5b600082601f8301126125a7578081fd5b8135602067ffffffffffffffff808311156125be57fe5b6125cb8283850201612e7d565b83815282810190868401865b868110156126a7578135890160c0807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0838e0301121561261557898afd5b604080518281018181108a8211171561262a57fe5b8252612637848b016126f7565b81526126448285016126f7565b8a8201526060808501358383015260809250612661838601612573565b9082015260a08481013583830152928401359289841115612680578c8dfd5b61268e8f8c86880101612777565b90820152875250505092850192908501906001016125d7565b509098975050505050505050565b60008083601f8401126126c6578182fd5b50813567ffffffffffffffff8111156126dd578182fd5b6020830191508360208083028501011115611a8157600080fd5b803580151581146104ec57600080fd5b80357fffffffff00000000000000000000000000000000000000000000000000000000811681146104ec57600080fd5b60008083601f840112612748578182fd5b50813567ffffffffffffffff81111561275f578182fd5b602083019150836020828501011115611a8157600080fd5b600082601f830112612787578081fd5b813567ffffffffffffffff81111561279b57fe5b6127cc60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601612e7d565b8181528460208386010111156127e0578283fd5b816020850160208301379081016020019190915292915050565b60006020828403121561280b578081fd5b61065782612573565b60008060008060008060008060a0898b03121561282f578384fd5b61283889612573565b975061284660208a01612573565b9650604089013567ffffffffffffffff80821115612862578586fd5b61286e8c838d016126b5565b909850965060608b0135915080821115612886578586fd5b6128928c838d016126b5565b909650945060808b01359150808211156128aa578384fd5b506128b78b828c01612737565b999c989b5096995094979396929594505050565b6000806000806000608086880312156128e2578081fd5b6128eb86612573565b94506128f960208701612573565b935060408601359250606086013567ffffffffffffffff81111561291b578182fd5b61292788828901612737565b969995985093965092949392505050565b60008060008060008060a08789031215612950578182fd5b61295987612573565b955061296760208801612573565b94506040870135935060608701359250608087013567ffffffffffffffff811115612990578283fd5b61299c89828a01612737565b979a9699509497509295939492505050565b6000602082840312156129bf578081fd5b813567ffffffffffffffff8111156129d5578182fd5b61067a84828501612597565b6000806000606084860312156129f5578283fd5b833567ffffffffffffffff80821115612a0c578485fd5b612a1887838801612597565b9450602086013593506040860135915080821115612a34578283fd5b50612a4186828701612777565b9150509250925092565b600060208284031215612a5c578081fd5b5035919050565b600080600060408486031215612a77578283fd5b83359250602084013567ffffffffffffffff811115612a94578283fd5b612aa086828701612737565b9497909650939450505050565b600060208284031215612abe578081fd5b61065782612707565b60008060408385031215612ad9578182fd5b612ae283612707565b9150612af060208401612573565b90509250929050565b60008060008060408587031215612b0e578182fd5b843567ffffffffffffffff80821115612b25578384fd5b612b3188838901612737565b90965094506020870135915080821115612b49578384fd5b50612b5687828801612737565b95989497509550505050565b600060208284031215612b73578081fd5b813567ffffffffffffffff811115612b89578182fd5b61067a84828501612777565b6000815180845260208085019450848183028601828601855b85811015612c305783830389528151805115158452858101511515868501526040808201519085015260608082015173ffffffffffffffffffffffffffffffffffffffff16908501526080808201519085015260a09081015160c091850182905290612c1c81860183612c3d565b9a87019a9450505090840190600101612bae565b5090979650505050505050565b60008151808452612c55816020860160208601612ea1565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60008251612c99818460208701612ea1565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b901515815260200190565b90815260200190565b6000838252604060208301526106546040830184612c3d565b7fffffffff0000000000000000000000000000000000000000000000000000000091909116815260200190565b6020808252601f908201527f4d61696e4d6f64756c65235f617574683a20494e56414c49445f4e4f4e434500604082015260600190565b60208082526024908201527f4d6f64756c6543616c6c73235f657865637574653a204e4f545f454e4f55474860408201527f5f47415300000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526026908201527f4d6f64756c6543616c6c7323657865637574653a20494e56414c49445f53494760408201527f4e41545552450000000000000000000000000000000000000000000000000000606082015260800190565b600060408252600560408301527f73656c663a0000000000000000000000000000000000000000000000000000006060830152608060208301526106576080830184612b95565b6000838252604060208301526106546040830184612b95565b918252602082015260400190565b60405181810167ffffffffffffffff81118282101715612e9957fe5b604052919050565b60005b83811015612ebc578181015183820152602001612ea4565b838111156109ad575050600091015256fe5369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202773272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265206c656e6774684d6f64756c6541757468235f7369676e617475726556616c69646174696f6e20494e56414c49445f464c41474d6f64756c654175746855706772616461626c6544756f2375706461746545787465726e616c496d6167654861736820494e56414c49445f494d4147455f484153484d6f64756c654175746855706772616461626c6523757064617465496d6167654861736820494e56414c49445f494d4147455f484153484d6f64756c65486f6f6b732372656d6f7665486f6f6b3a20484f4f4b5f4e4f545f524547495354455245444c696242797465732372656164416464726573733a204f55545f4f465f424f554e44534c696242797465732372656164466972737455696e7431363a204f55545f4f465f424f554e44535369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202776272076616c75654d6f64756c654175746855706772616461626c6544756f235f7369676e617475726556616c69646174696f6e3a20494e56414c49445f5349474e41545552454d6f64756c654175746855706772616461626c6544756f235f7369676e617475726556616c69646174696f6e20494e56414c49445f464c41475369676e617475726556616c696461746f72237265636f7665725369676e65723a20494e56414c49445f5349474e45524d6f64756c6541757468235f7369676e617475726556616c69646174696f6e3a20494e56414c49445f5349474e41545552454d6f64756c65486f6f6b7323616464486f6f6b3a20484f4f4b5f414c52454144595f524547495354455245445369676e617475726556616c696461746f7223697356616c69645369676e61747572653a20554e535550504f525445445f5349474e41545552455f545950454c696242797465732372656164427974657336363a204f55545f4f465f424f554e44534d6f64756c6555706461746523757064617465496d706c656d656e746174696f6e3a20494e56414c49445f494d504c454d454e544154494f4e5369676e617475726556616c696461746f72237265636f7665725369676e65723a20554e535550504f525445445f5349474e41545552455f545950454c69624279746573237265616455696e743855696e74383a204f55545f4f465f424f554e44534c69624279746573237265616442797465733a204f55545f4f465f424f554e44534c69624279746573237265616455696e7431363a204f55545f4f465f424f554e44534c696242797465732372656164427974657333323a20475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f52455155495245444d6f64756c6553656c6641757468236f6e6c7953656c663a204e4f545f415554484f52495a4544a2646970667358221220b63d49fa50241ad35eac65c5273e69ca090dc2dc1b4526223a791a38eff815e364736f6c63430007060033
LibBytes.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.6; library LibBytes { using LibBytes for bytes; /***********************************| | Read Bytes Functions | |__________________________________*/ /** * @dev Read firsts uint16 value. * @param data Byte array to be read. * @return a uint16 value of data at index zero. * @return newIndex Updated index after reading the values. */ function readFirstUint16( bytes memory data ) internal pure returns ( uint16 a, uint256 newIndex ) { assembly { let word := mload(add(32, data)) a := shr(240, word) newIndex := 2 } require(2 <= data.length, "LibBytes#readFirstUint16: OUT_OF_BOUNDS"); } /** * @dev Reads consecutive bool (8 bits) and uint8 values. * @param data Byte array to be read. * @param index Index in byte array of uint8 and uint8 values. * @return a uint8 value of data at given index. * @return b uint8 value of data at given index + 8. * @return newIndex Updated index after reading the values. */ function readUint8Uint8( bytes memory data, uint256 index ) internal pure returns ( uint8 a, uint8 b, uint256 newIndex ) { assembly { let word := mload(add(index, add(32, data))) a := shr(248, word) b := and(shr(240, word), 0xff) newIndex := add(index, 2) } assert(newIndex > index); require(newIndex <= data.length, "LibBytes#readUint8Uint8: OUT_OF_BOUNDS"); } /** * @dev Reads an address value from a position in a byte array. * @param data Byte array to be read. * @param index Index in byte array of address value. * @return a address value of data at given index. * @return newIndex Updated index after reading the value. */ function readAddress( bytes memory data, uint256 index ) internal pure returns ( address a, uint256 newIndex ) { assembly { let word := mload(add(index, add(32, data))) a := and(shr(96, word), 0xffffffffffffffffffffffffffffffffffffffff) newIndex := add(index, 20) } assert(newIndex > index); require(newIndex <= data.length, "LibBytes#readAddress: OUT_OF_BOUNDS"); } /** * @dev Reads 66 bytes from a position in a byte array. * @param data Byte array to be read. * @param index Index in byte array of 66 bytes value. * @return a 66 bytes bytes array value of data at given index. * @return newIndex Updated index after reading the value. */ function readBytes66( bytes memory data, uint256 index ) internal pure returns ( bytes memory a, uint256 newIndex ) { a = new bytes(66); assembly { let offset := add(32, add(data, index)) mstore(add(a, 32), mload(offset)) mstore(add(a, 64), mload(add(offset, 32))) mstore(add(a, 66), mload(add(offset, 34))) newIndex := add(index, 66) } assert(newIndex > index); require(newIndex <= data.length, "LibBytes#readBytes66: OUT_OF_BOUNDS"); } /** * @dev Reads a bytes32 value from a position in a byte array. * @param b Byte array containing a bytes32 value. * @param index Index in byte array of bytes32 value. * @return result bytes32 value from byte array. */ function readBytes32( bytes memory b, uint256 index ) internal pure returns (bytes32 result) { require( b.length >= index + 32, "LibBytes#readBytes32: GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED" ); // Arrays are prefixed by a 256 bit length parameter uint256 pos = index + 32; // Read the bytes32 from array memory assembly { result := mload(add(b, pos)) } return result; } /** * @dev Reads an uint16 value from a position in a byte array. * @param data Byte array to be read. * @param index Index in byte array of uint16 value. * @return a uint16 value of data at given index. * @return newIndex Updated index after reading the value. */ function readUint16( bytes memory data, uint256 index ) internal pure returns (uint16 a, uint256 newIndex) { assembly { let word := mload(add(index, add(32, data))) a := and(shr(240, word), 0xffff) newIndex := add(index, 2) } assert(newIndex > index); require(newIndex <= data.length, "LibBytes#readUint16: OUT_OF_BOUNDS"); } /** * @dev Reads bytes from a position in a byte array. * @param data Byte array to be read. * @param index Index in byte array of bytes value. * @param size Number of bytes to read. * @return a bytes bytes array value of data at given index. * @return newIndex Updated index after reading the value. */ function readBytes( bytes memory data, uint256 index, uint256 size ) internal pure returns (bytes memory a, uint256 newIndex) { a = new bytes(size); assembly { let offset := add(32, add(data, index)) let i := 0 let n := 32 // Copy each word, except last one for { } lt(n, size) { i := n n := add(n, 32) } { mstore(add(a, n), mload(add(offset, i))) } // Load word after new array let suffix := add(a, add(32, size)) let suffixWord := mload(suffix) // Copy last word, overwrites after array mstore(add(a, n), mload(add(offset, i))) // Restore after array mstore(suffix, suffixWord) newIndex := add(index, size) } assert(newIndex >= index); require(newIndex <= data.length, "LibBytes#readBytes: OUT_OF_BOUNDS"); } }
LibAddress.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.6; 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; } }
SignatureValidator.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.6; 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/ */ contract SignatureValidator { 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 memory _signature ) internal pure returns (address signer) { require(_signature.length == 66, "SignatureValidator#recoverSigner: invalid signature length"); uint256 signatureType = uint8(_signature[_signature.length - 1]); // Variables are not scoped in Solidity. uint8 v = uint8(_signature[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("SignatureValidator#recoverSigner: invalid signature 's' value"); } if (v != 27 && v != 28) { revert("SignatureValidator#recoverSigner: invalid signature 'v' value"); } // 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 { // Anything other signature types are illegal (We do not return false because // the signature may actually be valid, just not in a format // that we currently support. In this case returning false // may lead the caller to incorrectly believe that the // signature was invalid.) revert("SignatureValidator#recoverSigner: UNSUPPORTED_SIGNATURE_TYPE"); } // Prevent signer from being 0x0 require( signer != address(0x0), "SignatureValidator#recoverSigner: INVALID_SIGNER" ); 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 memory _signature ) internal view returns (bool valid) { 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 uint256 prevSize; assembly { prevSize := mload(_signature) mstore(_signature, sub(prevSize, 1)) } valid = ERC1271_MAGICVALUE_BYTES32 == IERC1271Wallet(_signer).isValidSignature(_hash, _signature); assembly { mstore(_signature, prevSize) } } else { // Anything other signature types are illegal (We do not return false because // the signature may actually be valid, just not in a format // that we currently support. In this case returning false // may lead the caller to incorrectly believe that the // signature was invalid.) revert("SignatureValidator#isValidSignature: UNSUPPORTED_SIGNATURE_TYPE"); } } }
IERC1271Wallet.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.6; 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.7.6; import "../../utils/LibBytes.sol"; import "../../utils/SignatureValidator.sol"; import "../../interfaces/IERC1271Wallet.sol"; import "./interfaces/IModuleAuth.sol"; import "./ModuleERC165.sol"; abstract contract ModuleAuth is IModuleAuth, ModuleERC165, SignatureValidator, IERC1271Wallet { using LibBytes for bytes; uint256 private constant FLAG_SIGNATURE = 0; uint256 private constant FLAG_ADDRESS = 1; uint256 private constant FLAG_DYNAMIC_SIGNATURE = 2; bytes4 private constant SELECTOR_ERC1271_BYTES_BYTES = 0x20c13b0b; bytes4 private constant SELECTOR_ERC1271_BYTES32_BYTES = 0x1626ba7e; /** * @notice Verify if signer is default wallet owner * @param _hash Hashed signed message * @param _signature Array of signatures with signers ordered * like the the keys in the multisig configs * * @dev The signature must be solidity packed and contain the total number of owners, * the threshold, the weight and either the address or a signature for each owner. * * Each weight & (address or signature) pair is prefixed by a flag that signals if such pair * contains an address or a signature. The aggregated weight of the signatures must surpass the threshold. * * Flag types: * 0x00 - Signature * 0x01 - Address * * E.g: * abi.encodePacked( * uint16 threshold, * uint8 01, uint8 weight_1, address signer_1, * uint8 00, uint8 weight_2, bytes signature_2, * ... * uint8 01, uint8 weight_5, address signer_5 * ) */ function _signatureValidation( bytes32 _hash, bytes memory _signature ) internal override view returns (bool) { ( uint16 threshold, // required threshold signature uint256 rindex // read index ) = _signature.readFirstUint16(); // Start image hash generation bytes32 imageHash = bytes32(uint256(threshold)); // Acumulated weight of signatures uint256 totalWeight; // Iterate until the image is completed while (rindex < _signature.length) { // Read next item type and addrWeight uint256 flag; uint256 addrWeight; address addr; (flag, addrWeight, rindex) = _signature.readUint8Uint8(rindex); if (flag == FLAG_ADDRESS) { // Read plain address (addr, rindex) = _signature.readAddress(rindex); } else if (flag == FLAG_SIGNATURE) { // Read single signature and recover signer bytes memory signature; (signature, rindex) = _signature.readBytes66(rindex); addr = recoverSigner(_hash, signature); // Acumulate total weight of the signature totalWeight += addrWeight; } else if (flag == FLAG_DYNAMIC_SIGNATURE) { // Read signer (addr, rindex) = _signature.readAddress(rindex); // Read signature size uint256 size; (size, rindex) = _signature.readUint16(rindex); // Read dynamic size signature bytes memory signature; (signature, rindex) = _signature.readBytes(rindex, size); require(isValidSignature(_hash, addr, signature), "ModuleAuth#_signatureValidation: INVALID_SIGNATURE"); // Acumulate total weight of the signature totalWeight += addrWeight; } else { revert("ModuleAuth#_signatureValidation INVALID_FLAG"); } // Write weight and address to image imageHash = keccak256(abi.encode(imageHash, addrWeight, addr)); } return totalWeight >= threshold && _isValidImage(imageHash); } /** * @notice Validates the signature image * @param _imageHash Hashed image of signature * @return true if the signature image is valid */ function _isValidImage(bytes32 _imageHash) internal virtual view returns (bool); /** * @notice Will hash _data to be signed (similar to EIP-712) * @param _digest Pre-final digest * @return hashed data for this wallet */ function _subDigest(bytes32 _digest) internal override view returns (bytes32) { uint256 chainId; assembly { chainId := chainid() } return keccak256( abi.encodePacked( "\x19\x01", chainId, address(this), _digest ) ); } /** * @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 ) external override virtual view returns (bytes4) { // Validate signatures if (_signatureValidation(_subDigest(keccak256(_data)), _signatures)) { return SELECTOR_ERC1271_BYTES_BYTES; } } /** * @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 ) external override virtual view returns (bytes4) { // Validate signatures if (_signatureValidation(_subDigest(_hash), _signatures)) { return SELECTOR_ERC1271_BYTES32_BYTES; } } /** * @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); } }
ModuleCalls.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.6; pragma experimental ABIEncoderV2; import "./ModuleSelfAuth.sol"; import "./ModuleStorage.sol"; import "./ModuleERC165.sol"; import "./interfaces/IModuleCalls.sol"; import "./interfaces/IModuleAuth.sol"; abstract contract ModuleCalls is IModuleCalls, IModuleAuth, ModuleERC165, ModuleSelfAuth { // NONCE_KEY = keccak256("org.arcadeum.module.calls.nonce"); bytes32 private constant NONCE_KEY = bytes32(0x8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e); uint256 private constant NONCE_BITS = 96; bytes32 private constant NONCE_MASK = bytes32((1 << NONCE_BITS) - 1); /** * @notice Returns the next nonce of the default nonce space * @dev The default nonce space is 0x00 * @return The next nonce */ function nonce() external override 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 override 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) private { ModuleStorage.writeBytes32Map(NONCE_KEY, bytes32(_space), bytes32(_nonce)); } /** * @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[] memory _txs, uint256 _nonce, bytes memory _signature ) public override virtual { // Validate and update nonce _validateNonce(_nonce); // Hash transaction bundle bytes32 txHash = _subDigest(keccak256(abi.encode(_nonce, _txs))); // Verify that signatures are valid require( _signatureValidation(txHash, _signature), "ModuleCalls#execute: INVALID_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[] memory _txs ) public override virtual onlySelf { // Hash transaction bundle bytes32 txHash = _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[] memory _txs ) private { // Execute transaction for (uint256 i = 0; i < _txs.length; i++) { Transaction memory transaction = _txs[i]; bool success; bytes memory result; require(gasleft() >= transaction.gasLimit, "ModuleCalls#_execute: NOT_ENOUGH_GAS"); if (transaction.delegateCall) { (success, result) = transaction.target.delegatecall{ gas: transaction.gasLimit == 0 ? gasleft() : transaction.gasLimit }(transaction.data); } else { (success, result) = transaction.target.call{ value: transaction.value, gas: transaction.gasLimit == 0 ? gasleft() : transaction.gasLimit }(transaction.data); } if (success) { emit TxExecuted(_txHash); } else { _revertBytes(transaction, _txHash, result); } } } /** * @notice Verify if a nonce is valid * @param _rawNonce Nonce to validate (may contain an encoded space) * @dev A valid nonce must be above the last one used * with a maximum delta of 100 */ function _validateNonce(uint256 _rawNonce) private { // Retrieve current nonce for this wallet (uint256 space, uint256 providedNonce) = _decodeNonce(_rawNonce); uint256 currentNonce = readNonce(space); // Verify if nonce is valid require( providedNonce == currentNonce, "MainModule#_auth: INVALID_NONCE" ); // Update signature nonce uint256 newNonce = providedNonce + 1; _writeNonce(space, newNonce); emit NonceChange(space, newNonce); } /** * @notice Logs a failed transaction, reverts if the transaction is not optional * @param _tx Transaction that is reverting * @param _txHash Hash of the transaction * @param _reason Encoded revert message */ function _revertBytes( Transaction memory _tx, bytes32 _txHash, bytes memory _reason ) internal { if (_tx.revertOnError) { assembly { revert(add(_reason, 0x20), mload(_reason)) } } else { emit TxFailed(_txHash, _reason); } } /** * @notice Decodes a raw nonce * @dev A raw nonce is encoded using the first 160 bits for the space * and the last 96 bits for the nonce * @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) private pure returns (uint256 _space, uint256 _nonce) { _nonce = uint256(bytes32(_rawNonce) & NONCE_MASK); _space = _rawNonce >> NONCE_BITS; } /** * @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.7.6; 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 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 mainmodule (those defined below) */ function addHook(bytes4 _signature, address _implementation) external override onlySelf { require(_readHook(_signature) == address(0), "ModuleHooks#addHook: HOOK_ALREADY_REGISTERED"); _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 mainmodule (those defined below) * without upgrading the wallet */ function removeHook(bytes4 _signature) external override onlySelf { require(_readHook(_signature) != address(0), "ModuleHooks#removeHook: HOOK_NOT_REGISTERED"); _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(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(_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 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 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 returns (bytes4) { return ModuleHooks.onERC721Received.selector; } /** * @notice Routes fallback calls through hooks */ fallback() external payable { 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); } }
ModuleERC165.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.6; pragma experimental ABIEncoderV2; 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, developpers must ensure that * all inherited contracts by the mainmodule 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.7.6; 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 onlySelf { require(_implementation.isContract(), "ModuleUpdate#updateImplementation: INVALID_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.7.6; 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 payable onlySelf returns (address addr) { assembly { addr := create(callvalue(), add(_code, 32), mload(_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.7.6; 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.7.6; /** * @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.7.6; contract ModuleSelfAuth { modifier onlySelf() { require(msg.sender == address(this), "ModuleSelfAuth#onlySelf: NOT_AUTHORIZED"); _; } }
MainModuleUpgradableDuo.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.6; pragma experimental ABIEncoderV2; import "./commons/ModuleAuthUpgradableDuo.sol"; import "./commons/ModuleHooks.sol"; import "./commons/ModuleCalls.sol"; import "./commons/ModuleUpdate.sol"; import "./commons/ModuleCreator.sol"; /** * @notice Contains the core functionality arcadeum wallets will inherit with * the added functionality that the main-module can be changed. * @dev If using a new main module, developpers must ensure that all inherited * contracts by the mainmodule don't conflict and are accounted for to be * supported by the supportsInterface method. */ contract MainModuleUpgradableDuo is ModuleAuthUpgradableDuo, ModuleCalls, ModuleUpdate, ModuleHooks, ModuleCreator { function isValidSignature( bytes calldata _data, bytes calldata _signatures ) public override( ModuleAuthUpgradableDuo ) virtual view returns (bytes4) { return super.isValidSignature(_data, _signatures); } function isValidSignature( bytes32 _hash, bytes calldata _signatures ) public override( 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, developpers must ensure that all inherited * contracts by the mainmodule 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, ModuleCalls, ModuleUpdate, ModuleHooks, ModuleCreator ) pure returns (bool) { return super.supportsInterface(_interfaceID); } }
IERC223Receiver.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.6; interface IERC223Receiver { function tokenFallback(address, uint256, bytes calldata) external; }
IERC721Receiver.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.6; interface IERC721Receiver { function onERC721Received(address, address, uint256, bytes calldata) external returns (bytes4); }
ModuleAuthUpgradable.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.6; import "./interfaces/IModuleAuthUpgradable.sol"; import "./ModuleSelfAuth.sol"; import "./ModuleAuth.sol"; import "./ModuleStorage.sol"; abstract contract ModuleAuthUpgradable is IModuleAuthUpgradable, ModuleAuth, ModuleSelfAuth { // IMAGE_HASH_KEY = keccak256("org.arcadeum.module.auth.upgradable.image.hash"); bytes32 private constant IMAGE_HASH_KEY = bytes32(0xea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8); event ImageHashUpdated(bytes32 newImageHash); /** * @notice Updates the signers configuration of the wallet * @param _imageHash New required image hash of the signature * @dev It is recommended to not have more than 200 signers as opcode repricing * could make transactions impossible to execute as all the signers must be * passed for each transaction. */ function updateImageHash(bytes32 _imageHash) external override onlySelf { require(_imageHash != bytes32(0), "ModuleAuthUpgradable#updateImageHash INVALID_IMAGE_HASH"); ModuleStorage.writeBytes32(IMAGE_HASH_KEY, _imageHash); emit ImageHashUpdated(_imageHash); } /** * @notice Returns the current image hash of the wallet */ function imageHash() external override 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 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.7.6; 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); }
IModuleAuth.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.6; abstract contract IModuleAuth { /** * @notice Hashed _data to be signed * @param _digest Pre-final digest * @return hashed data for this wallet */ function _subDigest( bytes32 _digest ) internal virtual view returns (bytes32); /** * @notice Verify if signer is default wallet owner * @param _hash Hashed signed message * @param _signature Encoded signature * @return True is the signature is valid */ function _signatureValidation( bytes32 _hash, bytes memory _signature ) internal virtual view returns (bool); }
ModuleAuthUpgradableDuo.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.6; import "../../utils/LibBytes.sol"; import "./ModuleAuthUpgradable.sol"; import "./ModuleStorage.sol"; abstract contract ModuleAuthUpgradableDuo is ModuleAuthUpgradable { using LibBytes for bytes; uint256 private constant FLAG_SIGNATURE = 0; uint256 private constant FLAG_ADDRESS = 1; uint256 private constant FLAG_DYNAMIC_SIGNATURE = 2; bytes4 private constant SELECTOR_ERC1271_BYTES_BYTES = 0x20c13b0b; bytes4 private constant SELECTOR_ERC1271_BYTES32_BYTES = 0x1626ba7e; // 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); /** * @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 require(_imageHash != bytes32(0), "ModuleAuthUpgradableDuo#updateExternalImageHash INVALID_IMAGE_HASH"); 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 _hash Digest of the signed data. * @param _signature A Sequence signature. * * @return isValid Indicates whether the signature is valid or not. * * @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 _hash, bytes calldata _signature ) internal view returns (bool isValid) { // Copied from ModuleAuth.sol // Sequence v1 design doesn't allow for easy reuse of the original method ( uint16 threshold, // required threshold signature uint256 rindex // read index ) = _signature.readFirstUint16(); // Start image hash generation bytes32 imageHash = bytes32(uint256(threshold)); // Acumulated weight of signatures uint256 totalWeight; // Iterate until the image is completed while (rindex < _signature.length) { // Read next item type and addrWeight uint256 flag; uint256 addrWeight; address addr; (flag, addrWeight, rindex) = _signature.readUint8Uint8(rindex); if (flag == FLAG_ADDRESS) { // Read plain address (addr, rindex) = _signature.readAddress(rindex); } else if (flag == FLAG_SIGNATURE) { // Read single signature and recover signer bytes memory signature; (signature, rindex) = _signature.readBytes66(rindex); addr = recoverSigner(_hash, signature); // Acumulate total weight of the signature totalWeight += addrWeight; } else if (flag == FLAG_DYNAMIC_SIGNATURE) { // Read signer (addr, rindex) = _signature.readAddress(rindex); // Read signature size uint256 size; (size, rindex) = _signature.readUint16(rindex); // Read dynamic size signature bytes memory signature; (signature, rindex) = _signature.readBytes(rindex, size); require(isValidSignature(_hash, addr, signature), "ModuleAuthUpgradableDuo#_signatureValidation: INVALID_SIGNATURE"); // Acumulate total weight of the signature totalWeight += addrWeight; } else { revert("ModuleAuthUpgradableDuo#_signatureValidation INVALID_FLAG"); } // Write weight and address to image imageHash = keccak256(abi.encode(imageHash, addrWeight, addr)); } return totalWeight >= threshold && _isValidExternalImage(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(ModuleAuth) virtual view returns (bytes4) { // Validate signatures bool isValid = _signatureValidationExternal(_subDigest(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(_subDigest(_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.7.6; pragma experimental ABIEncoderV2; interface IModuleCalls { // Events event NonceChange(uint256 _space, uint256 _newNonce); event TxFailed(bytes32 _tx, bytes _reason); event TxExecuted(bytes32 _tx) anonymous; // 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 Returns the next nonce of the default nonce space * @dev The default nonce space is 0x00 * @return The next nonce */ function nonce() external view returns (uint256); /** * @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) external view returns (uint256); /** * @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.7.6; interface IModuleHooks { /** * @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.7.6; interface IModuleUpdate { /** * @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; }
IModuleCreator.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.6; interface IModuleCreator { /** * @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); }
IModuleAuthUpgradable.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.6; interface IModuleAuthUpgradable { /** * @notice Updates the signers configuration of the wallet * @param _imageHash New required image hash of the signature */ function updateImageHash(bytes32 _imageHash) external; /** * @notice Returns the current image hash of the wallet */ function imageHash() external view returns (bytes32); }
Gas Token: