Address Contract Partially Verified
Address
0xdfa1468D07Fc86840A6EB53E0e65CEBDE81D1af9
Balance
0 ETH
Nonce
1
Code Size
8344 bytes
Creator
0x46cf7ddb...5D11 at tx 0x89f6d30f...6305ae
Indexed Transactions
0
Contract Bytecode
8344 bytes
0x6080604052600436106100c45763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166316c3e22f81146100c957806319ab453c146100f05780632d0335ab14610113578063315a7af3146101345780633fd8b02f146101555780635a1db8c41461016a5780639769c3fe146101915780639be65a60146101e9578063aacaaf881461020a578063b0ba4da014610261578063c90db44714610288578063c9b5ef8e146102a9578063d89784fc146102ca575b600080fd5b3480156100d557600080fd5b506100de6102fb565b60408051918252519081900360200190f35b3480156100fc57600080fd5b50610111600160a060020a0360043516610301565b005b34801561011f57600080fd5b506100de600160a060020a03600435166103a7565b34801561014057600080fd5b50610111600160a060020a03600435166103c2565b34801561016157600080fd5b506100de6106fe565b34801561017657600080fd5b50610111600160a060020a0360043581169060243516610704565b34801561019d57600080fd5b506101b2600160a060020a036004351661090a565b60408051600160a060020a03909416845267ffffffffffffffff909216602084015263ffffffff1682820152519081900360600190f35b3480156101f557600080fd5b50610111600160a060020a0360043516610974565b34801561021657600080fd5b5061024d60048035600160a060020a031690602480358082019290810135916044359160643591820191013560843560a435610aa8565b604080519115158252519081900360200190f35b34801561026d57600080fd5b50610111600160a060020a0360043581169060243516610da2565b34801561029457600080fd5b50610111600160a060020a03600435166111ac565b3480156102b557600080fd5b506100de600160a060020a036004351661137c565b3480156102d657600080fd5b506102df61138e565b60408051600160a060020a039092168252519081900360200190f35b60035481565b8033600160a060020a03821614610367576040805160008051602061204d833981519152815260206004820152601960248201527f424d3a2063616c6c6572206d7573742062652077616c6c657400000000000000604482015290519081900360640190fd5b60408051600160a060020a038416815290517f9fcca3f73f85397e2bf03647abf243c20b753bd54463ff3cae74de2971c112fa9181900360200190a15050565b600160a060020a031660009081526001602052604090205490565b6000333014610420576040805160008051602061204d833981519152815260206004820181905260248201527f524d3a206d7573742062652063616c6c65642076696120657865637574652829604482015290519081900360640190fd5b600160a060020a03821660009081526002602052604081205483917401000000000000000000000000000000000000000090910467ffffffffffffffff16116104de576040805160008051602061204d833981519152815260206004820152602560248201527f524d3a207468657265206d75737420626520616e206f6e676f696e672072656360448201527f6f76657279000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b600160a060020a0383166000908152600260205260409020805490925067ffffffffffffffff74010000000000000000000000000000000000000000909104811642909116116105a3576040805160008051602061204d833981519152815260206004820152602760248201527f524d3a20746865207265636f7665727920706572696f64206973206e6f74206f60448201527f7665722079657400000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b8154604080517f13af4035000000000000000000000000000000000000000000000000000000008152600160a060020a0392831660048201529051918516916313af40359160248082019260009290919082900301818387803b15801561060957600080fd5b505af115801561061d573d6000803e3d6000fd5b50508354604051600160a060020a03918216935090861691507fd8667de85dae2d56d76e700d16de53d21ac2ce4d5549cb0bf51c55fdc37f0bc190600090a3600554604080517fb0fc29e6000000000000000000000000000000000000000000000000000000008152600160a060020a038681166004830152600060248301819052925193169263b0fc29e69260448084019391929182900301818387803b1580156106c857600080fd5b505af11580156106dc573d6000803e3d6000fd5b505050600160a060020a03909316600090815260026020526040812055505050565b60045481565b8161070f813361139d565b1515610790576040805160008051602061204d833981519152815260206004820152602e60248201527f424d3a206d73672e73656e646572206d75737420626520616e206f776e65722060448201527f666f72207468652077616c6c6574000000000000000000000000000000000000606482015290519081900360840190fd5b60008054604080517f0bcd4ebb000000000000000000000000000000000000000000000000000000008152600160a060020a03868116600483015291519190921692630bcd4ebb92602480820193602093909283900390910190829087803b1580156107fb57600080fd5b505af115801561080f573d6000803e3d6000fd5b505050506040513d602081101561082557600080fd5b50511515610882576040805160008051602061204d833981519152815260206004820152601c60248201527f424d3a206d6f64756c65206973206e6f74207265676973746572656400000000604482015290519081900360640190fd5b604080517f1f17732d000000000000000000000000000000000000000000000000000000008152600160a060020a03848116600483015260016024830152915191851691631f17732d9160448082019260009290919082900301818387803b1580156108ed57600080fd5b505af1158015610901573d6000803e3d6000fd5b50505050505050565b600160a060020a039081166000908152600260205260409020549081169174010000000000000000000000000000000000000000820467ffffffffffffffff16917c0100000000000000000000000000000000000000000000000000000000900463ffffffff1690565b604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529051600091600160a060020a038416916370a082319160248082019260209290919082900301818787803b1580156109d857600080fd5b505af11580156109ec573d6000803e3d6000fd5b505050506040513d6020811015610a0257600080fd5b505160008054604080517fa9059cbb000000000000000000000000000000000000000000000000000000008152600160a060020a0392831660048201526024810185905290519394509085169263a9059cbb92604480840193602093929083900390910190829087803b158015610a7857600080fd5b505af1158015610a8c573d6000803e3d6000fd5b505050506040513d6020811015610aa257600080fd5b50505050565b6000806000805a9250610af3308d60008e8e8080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050508d8b8b61143d565b9150610b008c8a84611631565b1515610b5b576040805160008051602061204d833981519152815260206004820152601560248201527f524d3a204475706c696361746520726571756573740000000000000000000000604482015290519081900360640190fd5b610b958c8c8c8080601f016020809104026020016040519081016040528093929190818152602001838380828437506116a3945050505050565b1515610c3c576040805160008051602061204d833981519152815260206004820152604a60248201527f524d3a207468652077616c6c657420617574686f72697a65642069732064696660448201527f666572656e74207468656e2074686520746172676574206f662074686520726560648201527f6c61796564206461746100000000000000000000000000000000000000000000608482015290519081900360a40190fd5b610c768c8c8c8080601f0160208091040260200160405190810160405280939291908181526020018383808284375061172d945050505050565b905060418102871415610d5157610c8f8c8688846119b1565b15610d5157801580610d0a5750610d0a8c8c8c8080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050848b8b8080601f01602080910402602001604051908101604052809392919081815260200183838082843750611a86945050505050565b15610d515730600160a060020a03168b8b604051808383808284378201915050925050506000604051808303816000865af19150509350610d518c5a850388888533611c26565b60408051838152905185151591600160a060020a038f16917f6bb0b384ce772133df63560651bc8c727c53306cec1d51e2cbf8ea35fb8f2ec19181900360200190a350505098975050505050505050565b6000333014610e00576040805160008051602061204d833981519152815260206004820181905260248201527f524d3a206d7573742062652063616c6c65642076696120657865637574652829604482015290519081900360640190fd5b600160a060020a038316600090815260026020526040902054839074010000000000000000000000000000000000000000900467ffffffffffffffff1615610ebd576040805160008051602061204d833981519152815260206004820152602760248201527f524d3a2074686572652063616e6e6f7420626520616e206f6e676f696e67207260448201527f65636f7665727900000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b600160a060020a0383161515610f48576040805160008051602061204d833981519152815260206004820152602360248201527f524d3a207265636f7665727920616464726573732063616e6e6f74206265206e60448201527f756c6c0000000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b600160a060020a03808516600081815260026020908152604080832080548987167fffffffffffffffffffffffff00000000000000000000000000000000000000009091161780825560035467ffffffffffffffff429091011674010000000000000000000000000000000000000000027fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff90911617815560055482517f5040fb7600000000000000000000000000000000000000000000000000000000815260048101969096529151909750941693635040fb7693602480820194918390030190829087803b15801561103b57600080fd5b505af115801561104f573d6000803e3d6000fd5b505050506040513d602081101561106557600080fd5b5051825463ffffffff9091167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911617825560055460048054604080517fb0fc29e6000000000000000000000000000000000000000000000000000000008152600160a060020a038981169482019490945242909201602483015251919092169163b0fc29e691604480830192600092919082900301818387803b15801561112857600080fd5b505af115801561113c573d6000803e3d6000fd5b50508354604080517401000000000000000000000000000000000000000090920467ffffffffffffffff16825251600160a060020a038781169450881692507f5f59bfd9baba55ae30bb440923cbbe30987d50e12a4e9134ffac3fd9afc3526d916020908290030190a350505050565b600033301461120a576040805160008051602061204d833981519152815260206004820181905260248201527f524d3a206d7573742062652063616c6c65642076696120657865637574652829604482015290519081900360640190fd5b600160a060020a03821660009081526002602052604081205483917401000000000000000000000000000000000000000090910467ffffffffffffffff16116112c8576040805160008051602061204d833981519152815260206004820152602560248201527f524d3a207468657265206d75737420626520616e206f6e676f696e672072656360448201527f6f76657279000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b600160a060020a03808416600081815260026020526040808220805491519096509316927fc45926607303da71dbeffd2ed5c6b00f581982586b697655d19ae4c4d558f2599190a3600554604080517fb0fc29e6000000000000000000000000000000000000000000000000000000008152600160a060020a038681166004830152600060248301819052925193169263b0fc29e69260448084019391929182900301818387803b1580156106c857600080fd5b60016020526000908152604090205481565b600554600160a060020a031681565b600081600160a060020a031683600160a060020a0316638da5cb5b6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b15801561140057600080fd5b505af1158015611414573d6000803e3d6000fd5b505050506040513d602081101561142a57600080fd5b5051600160a060020a0316149392505050565b6040517f190000000000000000000000000000000000000000000000000000000000000060208083018281526000602185018190526c01000000000000000000000000600160a060020a03808e16820260228801528c16026036860152604a85018a90528851909485938d938d938d938d938d938d938d939192606a909201918701908083835b602083106114e35780518252601f1990920191602091820191016114c4565b6001836020036101000a03801982511681845116808217855250505050505090500184815260200183815260200182815260200199505050505050505050506040516020818303038152906040526040518082805190602001908083835b602083106115605780518252601f199092019160209182019101611541565b51815160209384036101000a6000190180199092169116179052604080519290940182900382207f19457468657265756d205369676e6564204d6573736167653a0a33320000000083830152603c8084019190915284518084039091018152605c9092019384905281519195509293508392850191508083835b602083106115f95780518252601f1990920191602091820191016115da565b5181516020939093036101000a600019018019909116921691909117905260405192018290039091209b9a5050505050505050505050565b600160a060020a0383166000908152600160208181526040808420858552830190915282205460ff161515141561166a5750600061169c565b50600160a060020a03831660009081526001602081815260408084208585528301909152909120805460ff1916821790555b9392505050565b6000806024835110151515611707576040805160008051602061204d833981519152815260206004820152601660248201527f524d3a20496e76616c6964206461746157616c6c657400000000000000000000604482015290519081900360640190fd5b6024830151905083600160a060020a031681600160a060020a03161491505b5092915050565b60008061173983611cf5565b604080517f657865637574655265636f7665727928616464726573732c6164647265737329815290519081900360200190209091507fffffffff000000000000000000000000000000000000000000000000000000008083169116141561184457600554604080517f5040fb76000000000000000000000000000000000000000000000000000000008152600160a060020a038781166004830152915161183d939290921691635040fb76916024808201926020929091908290030181600087803b15801561180757600080fd5b505af115801561181b573d6000803e3d6000fd5b505050506040513d602081101561183157600080fd5b50516001016002611d60565b9150611726565b604080517f66696e616c697a655265636f7665727928616464726573732900000000000000815290519081900360190190207fffffffff00000000000000000000000000000000000000000000000000000000828116911614156118ab5760009150611726565b604080517f63616e63656c5265636f76657279286164647265737329000000000000000000815290519081900360170190207fffffffff000000000000000000000000000000000000000000000000000000008281169116141561195c57600160a060020a03841660009081526002602081905260409091205461183d917c010000000000000000000000000000000000000000000000000000000090910463ffffffff9081166001011690611d60565b6040805160008051602061204d833981519152815260206004820152601360248201527f524d3a20756e6b6e6f776e20206d6574686f6400000000000000000000000000604482015290519081900360640190fd5b600080831180156119c25750600182115b8015611a6d575082840285600160a060020a0316311080611a6d5750604080517fd6eb1bbf0000000000000000000000000000000000000000000000000000000081523060048201529051600160a060020a0387169163d6eb1bbf9160248083019260209291908290030181600087803b158015611a3f57600080fd5b505af1158015611a53573d6000803e3d6000fd5b505050506040513d6020811015611a6957600080fd5b5051155b15611a7a57506000611a7e565b5060015b949350505050565b600554604080517ff18858ab000000000000000000000000000000000000000000000000000000008152600160a060020a03878116600483015291516000938493606093859384938493169163f18858ab916024808301928692919082900301818387803b158015611af757600080fd5b505af1158015611b0b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015611b3457600080fd5b810190808051640100000000811115611b4c57600080fd5b82016020810184811115611b5f57600080fd5b8151856020820283011164010000000082111715611b7c57600080fd5b5090975060009650869550505050505b8651604190048260ff161015611c1457611baa88888460ff16611d96565b905060ff8216158015611bc25750611bc28a8261139d565b15611bcc57611c09565b600160a060020a0380861690821611611be85760009550611c19565b809450611bf58482611e43565b94509250821515611c095760009550611c19565b600190910190611b8c565b600195505b5050505050949350505050565b61726c8501600085118015611c3b5750600183115b8015611c475750838111155b15610901573a851115611c5b573a02611c5e565b84025b604080517f8f6f0332000000000000000000000000000000000000000000000000000000008152600160a060020a03848116600483015260248201849052606060448301526000606483018190529251908a1692638f6f03329260a4808201939182900301818387803b158015611cd457600080fd5b505af1158015611ce8573d6000803e3d6000fd5b5050505050505050505050565b60006004825110151515611d58576040805160008051602061204d833981519152815260206004820152601a60248201527f524d3a20496e76616c69642066756e6374696f6e507265666978000000000000604482015290519081900360640190fd5b506020015190565b6000808284811515611d6e57fe5b0490508284811515611d7c57fe5b061515611d8b57809150611726565b806001019150611726565b6041808202830160208101516040820151919092015160009260ff9190911691601b831480611dc857508260ff16601c145b1515611dd357600080fd5b60408051600080825260208083018085528b905260ff8716838501526060830186905260808301859052925160019360a0808501949193601f19840193928390039091019190865af1158015611e2d573d6000803e3d6000fd5b5050604051601f19015198975050505050505050565b6000606060006060600080875160001480611e655750600160a060020a038716155b15611e765760008895509550611fbf565b600093506001885103604051908082528060200260200182016040528015611ea8578160200160208202803883390190505b50925060009150600090505b8751811015611fa957831515611f54578781815181101515611ed257fe5b90602001906020020151600160a060020a031687600160a060020a03161415611efe5760019350611fa1565b611f1e8882815181101515611f0f57fe5b90602001906020020151611fca565b8015611f465750611f468882815181101515611f3657fe5b9060200190602002015188611fd8565b15611f545760019350611fa1565b8251821015611fa1578781815181101515611f6b57fe5b906020019060200201518383815181101515611f8357fe5b600160a060020a039092166020928302909101909101526001909101905b600101611eb4565b83611fb657600088611fba565b6001835b955095505b505050509250929050565b6000903b63ffffffff161190565b604080517f6f776e657228290000000000000000000000000000000000000000000000000081529051908190036007018120808252600091829190602081818189611388fa600181141561202b57815193505b505083600160a060020a031682600160a060020a0316149250505092915050560008c379a000000000000000000000000000000000000000000000000000000000a165627a7a723058205545813c8ff5f82ec30694aa855cdae73e071df9957707273a7cfa589b758a570029
Verified Source Code Partial Match
Compiler: v0.4.24+commit.e67f0147
EVM: byzantium
Optimization: Yes (999 runs)
RecoveryManager.sol 1071 lines
pragma solidity ^0.4.24; /** * @title Module * @dev Interface for a module. * A module MUST implement the addModule() method to ensure that a wallet with at least one module * can never end up in a "frozen" state. * @author Julien Niset - <[email protected]> */ interface Module { /** * @dev Inits a module for a wallet by e.g. setting some wallet specific parameters in storage. * @param _wallet The wallet. */ function init(BaseWallet _wallet) external; /** * @dev Adds a module to a wallet. * @param _wallet The target wallet. * @param _module The modules to authorise. */ function addModule(BaseWallet _wallet, Module _module) external; /** * @dev Utility method to recover any ERC20 token that was sent to the * module by mistake. * @param _token The token to recover. */ function recoverToken(address _token) external; } /** * @title BaseModule * @dev Basic module that contains some methods common to all modules. * @author Julien Niset - <[email protected]> */ contract BaseModule is Module { // The adddress of the module registry. ModuleRegistry internal registry; event ModuleCreated(bytes32 name); event ModuleInitialised(address wallet); constructor(ModuleRegistry _registry, bytes32 _name) public { registry = _registry; emit ModuleCreated(_name); } /** * @dev Throws if the sender is not the target wallet of the call. */ modifier onlyWallet(BaseWallet _wallet) { require(msg.sender == address(_wallet), "BM: caller must be wallet"); _; } /** * @dev Throws if the sender is not the owner of the target wallet or the module itself. */ modifier onlyOwner(BaseWallet _wallet) { require(msg.sender == address(this) || isOwner(_wallet, msg.sender), "BM: must be an owner for the wallet"); _; } /** * @dev Throws if the sender is not the owner of the target wallet. */ modifier strictOnlyOwner(BaseWallet _wallet) { require(isOwner(_wallet, msg.sender), "BM: msg.sender must be an owner for the wallet"); _; } /** * @dev Inits the module for a wallet by logging an event. * The method can only be called by the wallet itself. * @param _wallet The wallet. */ function init(BaseWallet _wallet) external onlyWallet(_wallet) { emit ModuleInitialised(_wallet); } /** * @dev Adds a module to a wallet. First checks that the module is registered. * @param _wallet The target wallet. * @param _module The modules to authorise. */ function addModule(BaseWallet _wallet, Module _module) external strictOnlyOwner(_wallet) { require(registry.isRegisteredModule(_module), "BM: module is not registered"); _wallet.authoriseModule(_module, true); } /** * @dev Utility method enbaling anyone to recover ERC20 token sent to the * module by mistake and transfer them to the Module Registry. * @param _token The token to recover. */ function recoverToken(address _token) external { uint total = ERC20(_token).balanceOf(address(this)); ERC20(_token).transfer(address(registry), total); } /** * @dev Helper method to check if an address is the owner of a target wallet. * @param _wallet The target wallet. * @param _addr The address. */ function isOwner(BaseWallet _wallet, address _addr) internal view returns (bool) { return _wallet.owner() == _addr; } } /** * @title RelayerModule * @dev Base module containing logic to execute transactions signed by eth-less accounts and sent by a relayer. * @author Julien Niset - <[email protected]> */ contract RelayerModule is Module { uint256 constant internal BLOCKBOUND = 10000; mapping (address => RelayerConfig) public relayer; struct RelayerConfig { uint256 nonce; mapping (bytes32 => bool) executedTx; } event TransactionExecuted(address indexed wallet, bool indexed success, bytes32 signedHash); /** * @dev Throws if the call did not go through the execute() method. */ modifier onlyExecute { require(msg.sender == address(this), "RM: must be called via execute()"); _; } /* ***************** Abstract method ************************* */ /** * @dev Gets the number of valid signatures that must be provided to execute a * specific relayed transaction. * @param _wallet The target wallet. * @param _data The data of the relayed transaction. * @return The number of required signatures. */ function getRequiredSignatures(BaseWallet _wallet, bytes _data) internal view returns (uint256); /** * @dev Validates the signatures provided with a relayed transaction. * The method MUST throw if one or more signatures are not valid. * @param _wallet The target wallet. * @param _data The data of the relayed transaction. * @param _signHash The signed hash representing the relayed transaction. * @param _signatures The signatures as a concatenated byte array. */ function validateSignatures(BaseWallet _wallet, bytes _data, bytes32 _signHash, bytes _signatures) internal view returns (bool); /* ************************************************************ */ /** * @dev Executes a relayed transaction. * @param _wallet The target wallet. * @param _data The data for the relayed transaction * @param _nonce The nonce used to prevent replay attacks. * @param _signatures The signatures as a concatenated byte array. * @param _gasPrice The gas price to use for the gas refund. * @param _gasLimit The gas limit to use for the gas refund. */ function execute( BaseWallet _wallet, bytes _data, uint256 _nonce, bytes _signatures, uint256 _gasPrice, uint256 _gasLimit ) external returns (bool success) { uint startGas = gasleft(); bytes32 signHash = getSignHash(address(this), _wallet, 0, _data, _nonce, _gasPrice, _gasLimit); require(checkAndUpdateUniqueness(_wallet, _nonce, signHash), "RM: Duplicate request"); require(verifyData(address(_wallet), _data), "RM: the wallet authorized is different then the target of the relayed data"); uint256 requiredSignatures = getRequiredSignatures(_wallet, _data); if((requiredSignatures * 65) == _signatures.length) { if(verifyRefund(_wallet, _gasLimit, _gasPrice, requiredSignatures)) { if(requiredSignatures == 0 || validateSignatures(_wallet, _data, signHash, _signatures)) { // solium-disable-next-line security/no-call-value success = address(this).call(_data); refund(_wallet, startGas - gasleft(), _gasPrice, _gasLimit, requiredSignatures, msg.sender); } } } emit TransactionExecuted(_wallet, success, signHash); } /** * @dev Gets the current nonce for a wallet. * @param _wallet The target wallet. */ function getNonce(BaseWallet _wallet) external view returns (uint256 nonce) { return relayer[_wallet].nonce; } /** * @dev Generates the signed hash of a relayed transaction according to ERC 1077. * @param _from The starting address for the relayed transaction (should be the module) * @param _to The destination address for the relayed transaction (should be the wallet) * @param _value The value for the relayed transaction * @param _data The data for the relayed transaction * @param _nonce The nonce used to prevent replay attacks. * @param _gasPrice The gas price to use for the gas refund. * @param _gasLimit The gas limit to use for the gas refund. */ function getSignHash( address _from, address _to, uint256 _value, bytes _data, uint256 _nonce, uint256 _gasPrice, uint256 _gasLimit ) internal pure returns (bytes32) { return keccak256( abi.encodePacked( "\x19Ethereum Signed Message:\n32", keccak256(abi.encodePacked(byte(0x19), byte(0), _from, _to, _value, _data, _nonce, _gasPrice, _gasLimit)) )); } /** * @dev Checks if the relayed transaction is unique. * @param _wallet The target wallet. * @param _nonce The nonce * @param _signHash The signed hash of the transaction */ function checkAndUpdateUniqueness(BaseWallet _wallet, uint256 _nonce, bytes32 _signHash) internal returns (bool) { if(relayer[_wallet].executedTx[_signHash] == true) { return false; } relayer[_wallet].executedTx[_signHash] = true; return true; } /** * @dev Checks that a nonce has the correct format and is valid. * It must be constructed as nonce = {block number}{timestamp} where each component is 16 bytes. * @param _wallet The target wallet. * @param _nonce The nonce */ function checkAndUpdateNonce(BaseWallet _wallet, uint256 _nonce) internal returns (bool) { if(_nonce <= relayer[_wallet].nonce) { return false; } uint256 nonceBlock = (_nonce & 0xffffffffffffffffffffffffffffffff00000000000000000000000000000000) >> 128; if(nonceBlock > block.number + BLOCKBOUND) { return false; } relayer[_wallet].nonce = _nonce; return true; } /** * @dev Recovers the signer at a given position from a list of concatenated signatures. * @param _signedHash The signed hash * @param _signatures The concatenated signatures. * @param _index The index of the signature to recover. */ function recoverSigner(bytes32 _signedHash, bytes _signatures, uint _index) internal pure returns (address) { uint8 v; bytes32 r; bytes32 s; // we jump 32 (0x20) as the first slot of bytes contains the length // we jump 65 (0x41) per signature // for v we load 32 bytes ending with v (the first 31 come from s) then apply a mask // solium-disable-next-line security/no-inline-assembly assembly { r := mload(add(_signatures, add(0x20,mul(0x41,_index)))) s := mload(add(_signatures, add(0x40,mul(0x41,_index)))) v := and(mload(add(_signatures, add(0x41,mul(0x41,_index)))), 0xff) } require(v == 27 || v == 28); return ecrecover(_signedHash, v, r, s); } /** * @dev Refunds the gas used to the Relayer. * For security reasons the default behavior is to not refund calls with 0 or 1 signatures. * @param _wallet The target wallet. * @param _gasUsed The gas used. * @param _gasPrice The gas price for the refund. * @param _gasLimit The gas limit for the refund. * @param _signatures The number of signatures used in the call. * @param _relayer The address of the Relayer. */ function refund(BaseWallet _wallet, uint _gasUsed, uint _gasPrice, uint _gasLimit, uint _signatures, address _relayer) internal { uint256 amount = 29292 + _gasUsed; // 21000 (transaction) + 7620 (execution of refund) + 672 to log the event + _gasUsed // only refund if gas price not null, more than 1 signatures, gas less than gasLimit if(_gasPrice > 0 && _signatures > 1 && amount <= _gasLimit) { if(_gasPrice > tx.gasprice) { amount = amount * tx.gasprice; } else { amount = amount * _gasPrice; } _wallet.invoke(_relayer, amount, ""); } } /** * @dev Returns false if the refund is expected to fail. * @param _wallet The target wallet. * @param _gasUsed The expected gas used. * @param _gasPrice The expected gas price for the refund. */ function verifyRefund(BaseWallet _wallet, uint _gasUsed, uint _gasPrice, uint _signatures) internal view returns (bool) { if(_gasPrice > 0 && _signatures > 1 && (address(_wallet).balance < _gasUsed * _gasPrice || _wallet.authorised(this) == false)) { return false; } return true; } /** * @dev Checks that the wallet address provided as the first parameter of the relayed data is the same * as the wallet passed as the input of the execute() method. @return false if the addresses are different. */ function verifyData(address _wallet, bytes _data) private pure returns (bool) { require(_data.length >= 36, "RM: Invalid dataWallet"); address dataWallet; // solium-disable-next-line security/no-inline-assembly assembly { //_data = {length:32}{sig:4}{_wallet:32}{...} dataWallet := mload(add(_data, 0x24)) } return dataWallet == _wallet; } /** * @dev Parses the data to extract the method signature. */ function functionPrefix(bytes _data) internal pure returns (bytes4 prefix) { require(_data.length >= 4, "RM: Invalid functionPrefix"); // solium-disable-next-line security/no-inline-assembly assembly { prefix := mload(add(_data, 0x20)) } } } /** * ERC20 contract interface. */ contract ERC20 { function totalSupply() public view returns (uint); function decimals() public view returns (uint); function balanceOf(address tokenOwner) public view returns (uint balance); function allowance(address tokenOwner, address spender) public view returns (uint remaining); function transfer(address to, uint tokens) public returns (bool success); function approve(address spender, uint tokens) public returns (bool success); function transferFrom(address from, address to, uint tokens) public returns (bool success); } /** * @title Owned * @dev Basic contract to define an owner. * @author Julien Niset - <[email protected]> */ contract Owned { // The owner address public owner; event OwnerChanged(address indexed _newOwner); /** * @dev Throws if the sender is not the owner. */ modifier onlyOwner { require(msg.sender == owner, "Must be owner"); _; } constructor() public { owner = msg.sender; } /** * @dev Lets the owner transfer ownership of the contract to a new owner. * @param _newOwner The new owner. */ function changeOwner(address _newOwner) external onlyOwner { require(_newOwner != address(0), "Address must not be null"); owner = _newOwner; emit OwnerChanged(_newOwner); } } /** * @title ModuleRegistry * @dev Registry of authorised modules. * Modules must be registered before they can be authorised on a wallet. * @author Julien Niset - <[email protected]> */ contract ModuleRegistry is Owned { mapping (address => Info) internal modules; mapping (address => Info) internal upgraders; event ModuleRegistered(address indexed module, bytes32 name); event ModuleDeRegistered(address module); event UpgraderRegistered(address indexed upgrader, bytes32 name); event UpgraderDeRegistered(address upgrader); struct Info { bool exists; bytes32 name; } /** * @dev Registers a module. * @param _module The module. * @param _name The unique name of the module. */ function registerModule(address _module, bytes32 _name) external onlyOwner { require(!modules[_module].exists, "MR: module already exists"); modules[_module] = Info({exists: true, name: _name}); emit ModuleRegistered(_module, _name); } /** * @dev Deregisters a module. * @param _module The module. */ function deregisterModule(address _module) external onlyOwner { require(modules[_module].exists, "MR: module does not exists"); delete modules[_module]; emit ModuleDeRegistered(_module); } /** * @dev Registers an upgrader. * @param _upgrader The upgrader. * @param _name The unique name of the upgrader. */ function registerUpgrader(address _upgrader, bytes32 _name) external onlyOwner { require(!upgraders[_upgrader].exists, "MR: upgrader already exists"); upgraders[_upgrader] = Info({exists: true, name: _name}); emit UpgraderRegistered(_upgrader, _name); } /** * @dev Deregisters an upgrader. * @param _upgrader The _upgrader. */ function deregisterUpgrader(address _upgrader) external onlyOwner { require(upgraders[_upgrader].exists, "MR: upgrader does not exists"); delete upgraders[_upgrader]; emit UpgraderDeRegistered(_upgrader); } /** * @dev Utility method enbaling the owner of the registry to claim any ERC20 token that was sent to the * registry. * @param _token The token to recover. */ function recoverToken(address _token) external onlyOwner { uint total = ERC20(_token).balanceOf(address(this)); ERC20(_token).transfer(msg.sender, total); } /** * @dev Gets the name of a module from its address. * @param _module The module address. * @return the name. */ function moduleInfo(address _module) external view returns (bytes32) { return modules[_module].name; } /** * @dev Gets the name of an upgrader from its address. * @param _upgrader The upgrader address. * @return the name. */ function upgraderInfo(address _upgrader) external view returns (bytes32) { return upgraders[_upgrader].name; } /** * @dev Checks if a module is registered. * @param _module The module address. * @return true if the module is registered. */ function isRegisteredModule(address _module) external view returns (bool) { return modules[_module].exists; } /** * @dev Checks if a list of modules are registered. * @param _modules The list of modules address. * @return true if all the modules are registered. */ function isRegisteredModule(address[] _modules) external view returns (bool) { for(uint i = 0; i < _modules.length; i++) { if (!modules[_modules[i]].exists) { return false; } } return true; } /** * @dev Checks if an upgrader is registered. * @param _upgrader The upgrader address. * @return true if the upgrader is registered. */ function isRegisteredUpgrader(address _upgrader) external view returns (bool) { return upgraders[_upgrader].exists; } } /** * @title BaseWallet * @dev Simple modular wallet that authorises modules to call its invoke() method. * Based on https://gist.github.com/Arachnid/a619d31f6d32757a4328a428286da186 by * @author Julien Niset - <[email protected]> */ contract BaseWallet { // The implementation of the proxy address public implementation; // The owner address public owner; // The authorised modules mapping (address => bool) public authorised; // The enabled static calls mapping (bytes4 => address) public enabled; // The number of modules uint public modules; event AuthorisedModule(address indexed module, bool value); event EnabledStaticCall(address indexed module, bytes4 indexed method); event Invoked(address indexed module, address indexed target, uint indexed value, bytes data); event Received(uint indexed value, address indexed sender, bytes data); event OwnerChanged(address owner); /** * @dev Throws if the sender is not an authorised module. */ modifier moduleOnly { require(authorised[msg.sender], "BW: msg.sender not an authorized module"); _; } /** * @dev Inits the wallet by setting the owner and authorising a list of modules. * @param _owner The owner. * @param _modules The modules to authorise. */ function init(address _owner, address[] _modules) external { require(owner == address(0) && modules == 0, "BW: wallet already initialised"); require(_modules.length > 0, "BW: construction requires at least 1 module"); owner = _owner; modules = _modules.length; for(uint256 i = 0; i < _modules.length; i++) { require(authorised[_modules[i]] == false, "BW: module is already added"); authorised[_modules[i]] = true; Module(_modules[i]).init(this); emit AuthorisedModule(_modules[i], true); } } /** * @dev Enables/Disables a module. * @param _module The target module. * @param _value Set to true to authorise the module. */ function authoriseModule(address _module, bool _value) external moduleOnly { if (authorised[_module] != _value) { if(_value == true) { modules += 1; authorised[_module] = true; Module(_module).init(this); } else { modules -= 1; require(modules > 0, "BW: wallet must have at least one module"); delete authorised[_module]; } emit AuthorisedModule(_module, _value); } } /** * @dev Enables a static method by specifying the target module to which the call * must be delegated. * @param _module The target module. * @param _method The static method signature. */ function enableStaticCall(address _module, bytes4 _method) external moduleOnly { require(authorised[_module], "BW: must be an authorised module for static call"); enabled[_method] = _module; emit EnabledStaticCall(_module, _method); } /** * @dev Sets a new owner for the wallet. * @param _newOwner The new owner. */ function setOwner(address _newOwner) external moduleOnly { require(_newOwner != address(0), "BW: address cannot be null"); owner = _newOwner; emit OwnerChanged(_newOwner); } /** * @dev Performs a generic transaction. * @param _target The address for the transaction. * @param _value The value of the transaction. * @param _data The data of the transaction. */ function invoke(address _target, uint _value, bytes _data) external moduleOnly { // solium-disable-next-line security/no-call-value require(_target.call.value(_value)(_data), "BW: call to target failed"); emit Invoked(msg.sender, _target, _value, _data); } /** * @dev This method makes it possible for the wallet to comply to interfaces expecting the wallet to * implement specific static methods. It delegates the static call to a target contract if the data corresponds * to an enabled method, or logs the call otherwise. */ function() public payable { if(msg.data.length > 0) { address module = enabled[msg.sig]; if(module == address(0)) { emit Received(msg.value, msg.sender, msg.data); } else { require(authorised[module], "BW: must be an authorised module for static call"); // solium-disable-next-line security/no-inline-assembly assembly { calldatacopy(0, 0, calldatasize()) let result := staticcall(gas, module, 0, calldatasize(), 0, 0) returndatacopy(0, 0, returndatasize()) switch result case 0 {revert(0, returndatasize())} default {return (0, returndatasize())} } } } } } /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { /** * @dev Returns ceil(a / b). */ function ceil(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a / b; if(a % b == 0) { return c; } else { return c + 1; } } } /** * @title Storage * @dev Base contract for the storage of a wallet. * @author Julien Niset - <[email protected]> */ contract Storage { /** * @dev Throws if the caller is not an authorised module. */ modifier onlyModule(BaseWallet _wallet) { require(_wallet.authorised(msg.sender), "TS: must be an authorized module to call this method"); _; } } /** * @title GuardianStorage * @dev Contract storing the state of wallets related to guardians and lock. * The contract only defines basic setters and getters with no logic. Only modules authorised * for a wallet can modify its state. * @author Julien Niset - <[email protected]> * @author Olivier Van Den Biggelaar - <[email protected]> */ contract GuardianStorage is Storage { struct GuardianStorageConfig { // the list of guardians address[] guardians; // the info about guardians mapping (address => GuardianInfo) info; // the lock's release timestamp uint256 lock; // the module that set the last lock address locker; } struct GuardianInfo { bool exists; uint128 index; } // wallet specific storage mapping (address => GuardianStorageConfig) internal configs; // *************** External Functions ********************* // /** * @dev Lets an authorised module add a guardian to a wallet. * @param _wallet The target wallet. * @param _guardian The guardian to add. */ function addGuardian(BaseWallet _wallet, address _guardian) external onlyModule(_wallet) { GuardianStorageConfig storage config = configs[_wallet]; config.info[_guardian].exists = true; config.info[_guardian].index = uint128(config.guardians.push(_guardian) - 1); } /** * @dev Lets an authorised module revoke a guardian from a wallet. * @param _wallet The target wallet. * @param _guardian The guardian to revoke. */ function revokeGuardian(BaseWallet _wallet, address _guardian) external onlyModule(_wallet) { GuardianStorageConfig storage config = configs[_wallet]; address lastGuardian = config.guardians[config.guardians.length - 1]; if (_guardian != lastGuardian) { uint128 targetIndex = config.info[_guardian].index; config.guardians[targetIndex] = lastGuardian; config.info[lastGuardian].index = targetIndex; } config.guardians.length--; delete config.info[_guardian]; } /** * @dev Returns the number of guardians for a wallet. * @param _wallet The target wallet. * @return the number of guardians. */ function guardianCount(BaseWallet _wallet) external view returns (uint256) { return configs[_wallet].guardians.length; } /** * @dev Gets the list of guaridans for a wallet. * @param _wallet The target wallet. * @return the list of guardians. */ function getGuardians(BaseWallet _wallet) external view returns (address[]) { GuardianStorageConfig storage config = configs[_wallet]; address[] memory guardians = new address[](config.guardians.length); for (uint256 i = 0; i < config.guardians.length; i++) { guardians[i] = config.guardians[i]; } return guardians; } /** * @dev Checks if an account is a guardian for a wallet. * @param _wallet The target wallet. * @param _guardian The account. * @return true if the account is a guardian for a wallet. */ function isGuardian(BaseWallet _wallet, address _guardian) external view returns (bool) { return configs[_wallet].info[_guardian].exists; } /** * @dev Lets an authorised module set the lock for a wallet. * @param _wallet The target wallet. * @param _releaseAfter The epoch time at which the lock should automatically release. */ function setLock(BaseWallet _wallet, uint256 _releaseAfter) external onlyModule(_wallet) { configs[_wallet].lock = _releaseAfter; if(_releaseAfter != 0 && msg.sender != configs[_wallet].locker) { configs[_wallet].locker = msg.sender; } } /** * @dev Checks if the lock is set for a wallet. * @param _wallet The target wallet. * @return true if the lock is set for the wallet. */ function isLocked(BaseWallet _wallet) external view returns (bool) { return configs[_wallet].lock > now; } /** * @dev Gets the time at which the lock of a wallet will release. * @param _wallet The target wallet. * @return the time at which the lock of a wallet will release, or zero if there is no lock set. */ function getLock(BaseWallet _wallet) external view returns (uint256) { return configs[_wallet].lock; } /** * @dev Gets the address of the last module that modified the lock for a wallet. * @param _wallet The target wallet. * @return the address of the last module that modified the lock for a wallet. */ function getLocker(BaseWallet _wallet) external view returns (address) { return configs[_wallet].locker; } } library GuardianUtils { /** * @dev Checks if an address is an account guardian or an account authorised to sign on behalf of a smart-contract guardian * given a list of guardians. * @param _guardians the list of guardians * @param _guardian the address to test * @return true and the list of guardians minus the found guardian upon success, false and the original list of guardians if not found. */ function isGuardian(address[] _guardians, address _guardian) internal view returns (bool, address[]) { if(_guardians.length == 0 || _guardian == address(0)) { return (false, _guardians); } bool isFound = false; address[] memory updatedGuardians = new address[](_guardians.length - 1); uint256 index = 0; for (uint256 i = 0; i < _guardians.length; i++) { if(!isFound) { // check if _guardian is an account guardian if(_guardian == _guardians[i]) { isFound = true; continue; } // check if _guardian is the owner of a smart contract guardian if(isContract(_guardians[i]) && isGuardianOwner(_guardians[i], _guardian)) { isFound = true; continue; } } if(index < updatedGuardians.length) { updatedGuardians[index] = _guardians[i]; index++; } } return isFound ? (true, updatedGuardians) : (false, _guardians); } /** * @dev Checks if an address is a contract. * @param _addr The address. */ function isContract(address _addr) internal view returns (bool) { uint32 size; // solium-disable-next-line security/no-inline-assembly assembly { size := extcodesize(_addr) } return (size > 0); } /** * @dev Checks if an address is the owner of a guardian contract. * The method does not revert if the call to the owner() method consumes more then 5000 gas. * @param _guardian The guardian contract * @param _owner The owner to verify. */ function isGuardianOwner(address _guardian, address _owner) internal view returns (bool) { address owner = address(0); bytes4 sig = bytes4(keccak256("owner()")); // solium-disable-next-line security/no-inline-assembly assembly { let ptr := mload(0x40) mstore(ptr,sig) let result := staticcall(5000, _guardian, ptr, 0x20, ptr, 0x20) if eq(result, 1) { owner := mload(ptr) } } return owner == _owner; } } /** * @title RecoveryManager * @dev Module to manage the recovery of a wallet owner. * Recovery is executed by a consensus of the wallet's guardians and takes * 24 hours before it can be finalized. Once finalised the ownership of the wallet * is transfered to a new address. * @author Julien Niset - <[email protected]> * @author Olivier Van Den Biggelaar - <[email protected]> */ contract RecoveryManager is BaseModule, RelayerModule { bytes32 constant NAME = "RecoveryManager"; bytes4 constant internal EXECUTE_PREFIX = bytes4(keccak256("executeRecovery(address,address)")); bytes4 constant internal FINALIZE_PREFIX = bytes4(keccak256("finalizeRecovery(address)")); bytes4 constant internal CANCEL_PREFIX = bytes4(keccak256("cancelRecovery(address)")); struct RecoveryManagerConfig { address recovery; uint64 executeAfter; uint32 guardianCount; } // the wallet specific storage mapping (address => RecoveryManagerConfig) internal configs; // Recovery period uint256 public recoveryPeriod; // Lock period uint256 public lockPeriod; // location of the Guardian storage GuardianStorage public guardianStorage; // *************** Events *************************** // event RecoveryExecuted(address indexed wallet, address indexed _recovery, uint64 executeAfter); event RecoveryFinalized(address indexed wallet, address indexed _recovery); event RecoveryCanceled(address indexed wallet, address indexed _recovery); // *************** Modifiers ************************ // /** * @dev Throws if there is no ongoing recovery procedure. */ modifier onlyWhenRecovery(BaseWallet _wallet) { require(configs[_wallet].executeAfter > 0, "RM: there must be an ongoing recovery"); _; } /** * @dev Throws if there is an ongoing recovery procedure. */ modifier notWhenRecovery(BaseWallet _wallet) { require(configs[_wallet].executeAfter == 0, "RM: there cannot be an ongoing recovery"); _; } // *************** Constructor ************************ // constructor( ModuleRegistry _registry, GuardianStorage _guardianStorage, uint256 _recoveryPeriod, uint256 _lockPeriod ) BaseModule(_registry, NAME) public { guardianStorage = _guardianStorage; recoveryPeriod = _recoveryPeriod; lockPeriod = _lockPeriod; } // *************** External functions ************************ // /** * @dev Lets the guardians start the execution of the recovery procedure. * Once triggered the recovery is pending for the security period before it can * be finalised. * Must be confirmed by N guardians, where N = ((Nb Guardian + 1) / 2). * @param _wallet The target wallet. * @param _recovery The address to which ownership should be transferred. */ function executeRecovery(BaseWallet _wallet, address _recovery) external onlyExecute notWhenRecovery(_wallet) { require(_recovery != address(0), "RM: recovery address cannot be null"); RecoveryManagerConfig storage config = configs[_wallet]; config.recovery = _recovery; config.executeAfter = uint64(now + recoveryPeriod); config.guardianCount = uint32(guardianStorage.guardianCount(_wallet)); guardianStorage.setLock(_wallet, now + lockPeriod); emit RecoveryExecuted(_wallet, _recovery, config.executeAfter); } /** * @dev Finalizes an ongoing recovery procedure if the security period is over. * The method is public and callable by anyone to enable orchestration. * @param _wallet The target wallet. */ function finalizeRecovery(BaseWallet _wallet) external onlyExecute onlyWhenRecovery(_wallet) { RecoveryManagerConfig storage config = configs[_wallet]; require(uint64(now) > config.executeAfter, "RM: the recovery period is not over yet"); _wallet.setOwner(config.recovery); emit RecoveryFinalized(_wallet, config.recovery); guardianStorage.setLock(_wallet, 0); delete configs[_wallet]; } /** * @dev Lets the owner cancel an ongoing recovery procedure. * Must be confirmed by N guardians, where N = ((Nb Guardian + 1) / 2) - 1. * @param _wallet The target wallet. */ function cancelRecovery(BaseWallet _wallet) external onlyExecute onlyWhenRecovery(_wallet) { RecoveryManagerConfig storage config = configs[_wallet]; emit RecoveryCanceled(_wallet, config.recovery); guardianStorage.setLock(_wallet, 0); delete configs[_wallet]; } /** * @dev Gets the details of the ongoing recovery procedure if any. * @param _wallet The target wallet. */ function getRecovery(BaseWallet _wallet) public view returns(address _address, uint64 _executeAfter, uint32 _guardianCount) { RecoveryManagerConfig storage config = configs[_wallet]; return (config.recovery, config.executeAfter, config.guardianCount); } // *************** Implementation of RelayerModule methods ********************* // function validateSignatures(BaseWallet _wallet, bytes _data, bytes32 _signHash, bytes _signatures) internal view returns (bool) { address lastSigner = address(0); address[] memory guardians = guardianStorage.getGuardians(_wallet); bool isGuardian = false; for (uint8 i = 0; i < _signatures.length / 65; i++) { address signer = recoverSigner(_signHash, _signatures, i); if(i == 0 && isOwner(_wallet, signer)) { // first signer can be owner continue; } else { if(signer <= lastSigner) { return false; } // "RM: signers must be different" lastSigner = signer; (isGuardian, guardians) = GuardianUtils.isGuardian(guardians, signer); if(!isGuardian) { return false; } // "RM: signatures not valid" } } return true; } function getRequiredSignatures(BaseWallet _wallet, bytes _data) internal view returns (uint256) { bytes4 methodId = functionPrefix(_data); if (methodId == EXECUTE_PREFIX) { return SafeMath.ceil(guardianStorage.guardianCount(_wallet) + 1, 2); } if (methodId == FINALIZE_PREFIX) { return 0; } if(methodId == CANCEL_PREFIX) { return SafeMath.ceil(configs[_wallet].guardianCount + 1, 2); } revert("RM: unknown method"); } }
Read Contract
getNonce 0x2d0335ab → uint256
getRecovery 0x9769c3fe → address, uint64, uint32
guardianStorage 0xd89784fc → address
lockPeriod 0x3fd8b02f → uint256
recoveryPeriod 0x16c3e22f → uint256
relayer 0xc9b5ef8e → uint256
Write Contract 7 functions
These functions modify contract state and require a wallet transaction to execute.
addModule 0x5a1db8c4
address _wallet
address _module
cancelRecovery 0xc90db447
address _wallet
execute 0xaacaaf88
address _wallet
bytes _data
uint256 _nonce
bytes _signatures
uint256 _gasPrice
uint256 _gasLimit
returns: bool
executeRecovery 0xb0ba4da0
address _wallet
address _recovery
finalizeRecovery 0x315a7af3
address _wallet
init 0x19ab453c
address _wallet
recoverToken 0x9be65a60
address _token
Recent Transactions
No transactions found for this address