Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0x76FE1Ecb4a94f1B88E8B75dE11445160a492ea5a
Balance 0 ETH
Nonce 1
Code Size 7057 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

7057 bytes
0x6080604052600436106100b95763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166319ab453c81146100be5780632d0335ab146100e15780632f6c493c146101145780633fd8b02f146101355780634a4fbeec1461014a5780635a1db8c41461017f5780636b9db4e6146101a65780639be65a60146101e4578063aacaaf8814610205578063c9b5ef8e14610248578063d89784fc14610269578063f435f5a71461029a575b600080fd5b3480156100ca57600080fd5b506100df600160a060020a03600435166102bb565b005b3480156100ed57600080fd5b50610102600160a060020a0360043516610361565b60408051918252519081900360200190f35b34801561012057600080fd5b506100df600160a060020a036004351661037c565b34801561014157600080fd5b506101026107bb565b34801561015657600080fd5b5061016b600160a060020a03600435166107c1565b604080519115158252519081900360200190f35b34801561018b57600080fd5b506100df600160a060020a036004358116906024351661085e565b3480156101b257600080fd5b506101c7600160a060020a0360043516610a64565b6040805167ffffffffffffffff9092168252519081900360200190f35b3480156101f057600080fd5b506100df600160a060020a0360043516610b0d565b34801561021157600080fd5b5061016b60048035600160a060020a031690602480358082019290810135916044359160643591820191013560843560a435610c41565b34801561025457600080fd5b50610102600160a060020a0360043516610f3b565b34801561027557600080fd5b5061027e610f4d565b60408051600160a060020a039092168252519081900360200190f35b3480156102a657600080fd5b506100df600160a060020a0360043516610f5c565b8033600160a060020a038216146103215760408051600080516020611b46833981519152815260206004820152601960248201527f424d3a2063616c6c6572206d7573742062652077616c6c657400000000000000604482015290519081900360640190fd5b60408051600160a060020a038416815290517f9fcca3f73f85397e2bf03647abf243c20b753bd54463ff3cae74de2971c112fa9181900360200190a15050565b600160a060020a031660009081526001602052604090205490565b600254604080517ff18858ab000000000000000000000000000000000000000000000000000000008152600160a060020a03808516600483015291516000938593859361048093919092169163f18858ab916024808301928792919082900301818387803b1580156103ed57600080fd5b505af1158015610401573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561042a57600080fd5b81019080805164010000000081111561044257600080fd5b8201602081018481111561045557600080fd5b815185602082028301116401000000008211171561047257600080fd5b50509291905050503361120e565b5090503330148061048e5750805b15156104e95760408051600080516020611b46833981519152815260206004820152601b60248201527f47443a2077616c6c6574206d75737420626520756e6c6f636b65640000000000604482015290519081900360640190fd5b600254604080517f4a4fbeec000000000000000000000000000000000000000000000000000000008152600160a060020a038088166004830152915187939290921691634a4fbeec916024808201926020929091908290030181600087803b15801561055457600080fd5b505af1158015610568573d6000803e3d6000fd5b505050506040513d602081101561057e57600080fd5b505115156105db5760408051600080516020611b46833981519152815260206004820152601960248201527f47443a2077616c6c6574206d757374206265206c6f636b656400000000000000604482015290519081900360640190fd5b600254604080517f919884bf000000000000000000000000000000000000000000000000000000008152600160a060020a0388811660048301529151919092169163919884bf9160248083019260209291908290030181600087803b15801561064357600080fd5b505af1158015610657573d6000803e3d6000fd5b505050506040513d602081101561066d57600080fd5b50519350600160a060020a03841630146106fc5760408051600080516020611b46833981519152815260206004820152603c60248201527f4c4d3a2063616e6e6f7420756e6c6f636b20612077616c6c657420746861742060448201527f776173206c6f636b656420627920616e6f74686572206d6f64756c6500000000606482015290519081900360840190fd5b600254604080517fb0fc29e6000000000000000000000000000000000000000000000000000000008152600160a060020a038881166004830152600060248301819052925193169263b0fc29e69260448084019391929182900301818387803b15801561076857600080fd5b505af115801561077c573d6000803e3d6000fd5b5050604051600160a060020a03881692507f7e6adfec7e3f286831a0200a754127c171a2da564078722cb97704741bbdb0ea9150600090a25050505050565b60035481565b600254604080517f4a4fbeec000000000000000000000000000000000000000000000000000000008152600160a060020a03848116600483015291516000939290921691634a4fbeec9160248082019260209290919082900301818787803b15801561082c57600080fd5b505af1158015610840573d6000803e3d6000fd5b505050506040513d602081101561085657600080fd5b505192915050565b816108698133611395565b15156108ea5760408051600080516020611b46833981519152815260206004820152602e60248201527f424d3a206d73672e73656e646572206d75737420626520616e206f776e65722060448201527f666f72207468652077616c6c6574000000000000000000000000000000000000606482015290519081900360840190fd5b60008054604080517f0bcd4ebb000000000000000000000000000000000000000000000000000000008152600160a060020a03868116600483015291519190921692630bcd4ebb92602480820193602093909283900390910190829087803b15801561095557600080fd5b505af1158015610969573d6000803e3d6000fd5b505050506040513d602081101561097f57600080fd5b505115156109dc5760408051600080516020611b46833981519152815260206004820152601c60248201527f424d3a206d6f64756c65206973206e6f74207265676973746572656400000000604482015290519081900360640190fd5b604080517f1f17732d000000000000000000000000000000000000000000000000000000008152600160a060020a03848116600483015260016024830152915191851691631f17732d9160448082019260009290919082900301818387803b158015610a4757600080fd5b505af1158015610a5b573d6000803e3d6000fd5b50505050505050565b600254604080517f6b9db4e6000000000000000000000000000000000000000000000000000000008152600160a060020a038481166004830152915160009384931691636b9db4e691602480830192602092919082900301818787803b158015610acd57600080fd5b505af1158015610ae1573d6000803e3d6000fd5b505050506040513d6020811015610af757600080fd5b5051905042811115610b07578091505b50919050565b604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529051600091600160a060020a038416916370a082319160248082019260209290919082900301818787803b158015610b7157600080fd5b505af1158015610b85573d6000803e3d6000fd5b505050506040513d6020811015610b9b57600080fd5b505160008054604080517fa9059cbb000000000000000000000000000000000000000000000000000000008152600160a060020a0392831660048201526024810185905290519394509085169263a9059cbb92604480840193602093929083900390910190829087803b158015610c1157600080fd5b505af1158015610c25573d6000803e3d6000fd5b505050506040513d6020811015610c3b57600080fd5b50505050565b6000806000805a9250610c8c308d60008e8e8080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050508d8b8b611435565b9150610c998c8a84611629565b1515610cf45760408051600080516020611b46833981519152815260206004820152601560248201527f524d3a204475706c696361746520726571756573740000000000000000000000604482015290519081900360640190fd5b610d2e8c8c8c8080601f0160208091040260200160405190810160405280939291908181526020018383808284375061163d945050505050565b1515610dd55760408051600080516020611b46833981519152815260206004820152604a60248201527f524d3a207468652077616c6c657420617574686f72697a65642069732064696660448201527f666572656e74207468656e2074686520746172676574206f662074686520726560648201527f6c61796564206461746100000000000000000000000000000000000000000000608482015290519081900360a40190fd5b610e0f8c8c8c8080601f016020809104026020016040519081016040528093929190818152602001838380828437506116c7945050505050565b905060418102871415610eea57610e288c8688846116cf565b15610eea57801580610ea35750610ea38c8c8c8080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050848b8b8080601f016020809104026020016040519081016040528093929190818152602001838380828437506117a3945050505050565b15610eea5730600160a060020a03168b8b604051808383808284378201915050925050506000604051808303816000865af19150509350610eea8c5a8503888885336118ba565b60408051838152905185151591600160a060020a038f16917f6bb0b384ce772133df63560651bc8c727c53306cec1d51e2cbf8ea35fb8f2ec19181900360200190a350505098975050505050505050565b60016020526000908152604090205481565b600254600160a060020a031681565b600254604080517ff18858ab000000000000000000000000000000000000000000000000000000008152600160a060020a03808516600483015291518493600093610fca9391169163f18858ab91602480820192879290919082900301818387803b1580156103ed57600080fd5b50905033301480610fd85750805b15156110335760408051600080516020611b46833981519152815260206004820152601b60248201527f47443a2077616c6c6574206d75737420626520756e6c6f636b65640000000000604482015290519081900360640190fd5b600254604080517f4a4fbeec000000000000000000000000000000000000000000000000000000008152600160a060020a038087166004830152915186939290921691634a4fbeec916024808201926020929091908290030181600087803b15801561109e57600080fd5b505af11580156110b2573d6000803e3d6000fd5b505050506040513d60208110156110c857600080fd5b5051156111245760408051600080516020611b46833981519152815260206004820152601b60248201527f47443a2077616c6c6574206d75737420626520756e6c6f636b65640000000000604482015290519081900360640190fd5b600254600354604080517fb0fc29e6000000000000000000000000000000000000000000000000000000008152600160a060020a0388811660048301524290930160248201529051919092169163b0fc29e691604480830192600092919082900301818387803b15801561119757600080fd5b505af11580156111ab573d6000803e3d6000fd5b5050505083600160a060020a03167f6395bace6e0acbe4f22761b149d3cc2e88c7dde6bf4d8481825eef404cf989a16003544201604051808267ffffffffffffffff1667ffffffffffffffff16815260200191505060405180910390a250505050565b60006060600060606000808751600014806112305750600160a060020a038716155b15611241576000889550955061138a565b600093506001885103604051908082528060200260200182016040528015611273578160200160208202803883390190505b50925060009150600090505b87518110156113745783151561131f57878181518110151561129d57fe5b90602001906020020151600160a060020a031687600160a060020a031614156112c9576001935061136c565b6112e988828151811015156112da57fe5b90602001906020020151611989565b80156113115750611311888281518110151561130157fe5b9060200190602002015188611997565b1561131f576001935061136c565b825182101561136c57878181518110151561133657fe5b90602001906020020151838381518110151561134e57fe5b600160a060020a039092166020928302909101909101526001909101905b60010161127f565b8361138157600088611385565b6001835b955095505b505050509250929050565b600081600160a060020a031683600160a060020a0316638da5cb5b6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b1580156113f857600080fd5b505af115801561140c573d6000803e3d6000fd5b505050506040513d602081101561142257600080fd5b5051600160a060020a0316149392505050565b6040517f190000000000000000000000000000000000000000000000000000000000000060208083018281526000602185018190526c01000000000000000000000000600160a060020a03808e16820260228801528c16026036860152604a85018a90528851909485938d938d938d938d938d938d938d939192606a909201918701908083835b602083106114db5780518252601f1990920191602091820191016114bc565b6001836020036101000a03801982511681845116808217855250505050505090500184815260200183815260200182815260200199505050505050505050506040516020818303038152906040526040518082805190602001908083835b602083106115585780518252601f199092019160209182019101611539565b51815160209384036101000a6000190180199092169116179052604080519290940182900382207f19457468657265756d205369676e6564204d6573736167653a0a33320000000083830152603c8084019190915284518084039091018152605c9092019384905281519195509293508392850191508083835b602083106115f15780518252601f1990920191602091820191016115d2565b5181516020939093036101000a600019018019909116921691909117905260405192018290039091209b9a5050505050505050505050565b60006116358484611a0b565b949350505050565b60008060248351101515156116a15760408051600080516020611b46833981519152815260206004820152601660248201527f524d3a20496e76616c6964206461746157616c6c657400000000000000000000604482015290519081900360640190fd5b6024830151905083600160a060020a031681600160a060020a03161491505b5092915050565b600192915050565b600080831180156116e05750600182115b801561178b575082840285600160a060020a031631108061178b5750604080517fd6eb1bbf0000000000000000000000000000000000000000000000000000000081523060048201529051600160a060020a0387169163d6eb1bbf9160248083019260209291908290030181600087803b15801561175d57600080fd5b505af1158015611771573d6000803e3d6000fd5b505050506040513d602081101561178757600080fd5b5051155b1561179857506000611635565b506001949350505050565b600254604080517ff18858ab000000000000000000000000000000000000000000000000000000008152600160a060020a038781166004830152915160009384936118af9391169163f18858ab91602480820192879290919082900301818387803b15801561181157600080fd5b505af1158015611825573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561184e57600080fd5b81019080805164010000000081111561186657600080fd5b8201602081018481111561187957600080fd5b815185602082028301116401000000008211171561189657600080fd5b50509291905050506118aa86866000611a98565b61120e565b509695505050505050565b61726c85016000851180156118cf5750600183115b80156118db5750838111155b15610a5b573a8511156118ef573a026118f2565b84025b604080517f8f6f0332000000000000000000000000000000000000000000000000000000008152600160a060020a03848116600483015260248201849052606060448301526000606483018190529251908a1692638f6f03329260a4808201939182900301818387803b15801561196857600080fd5b505af115801561197c573d6000803e3d6000fd5b5050505050505050505050565b6000903b63ffffffff161190565b604080517f6f776e657228290000000000000000000000000000000000000000000000000081529051908190036007018120808252600091829190602081818189611388fa60018114156119ea57815193505b505083600160a060020a031682600160a060020a0316149250505092915050565b600160a060020a03821660009081526001602052604081205481908311611a3557600091506116c0565b507001000000000000000000000000000000006fffffffffffffffffffffffffffffffff198316044361271001811115611a7257600091506116c0565b5050600160a060020a039190911660009081526001602081905260409091209190915590565b6041808202830160208101516040820151919092015160009260ff9190911691601b831480611aca57508260ff16601c145b1515611ad557600080fd5b60408051600080825260208083018085528b905260ff8716838501526060830186905260808301859052925160019360a0808501949193601f19840193928390039091019190865af1158015611b2f573d6000803e3d6000fd5b5050604051601f19015198975050505050505050560008c379a000000000000000000000000000000000000000000000000000000000a165627a7a72305820f19f76ab7a1a80aac40b54247b2316f3a86a7ae633e62775ebbc30dd79d9225b0029

Verified Source Code Partial Match

Compiler: v0.4.24+commit.e67f0147 EVM: byzantium Optimization: Yes (999 runs)
LockManager.sol 1003 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 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 LockManager
 * @dev Module to manage the state of a wallet's lock.
 * Other modules can use the state of the lock to determine if their operations 
 * should be authorised or blocked. Only the guardians of a wallet can lock and unlock it. 
 * The lock automatically unlocks after a given period. The lock state is stored on a separate
 * contract to facilitate its use by other modules.
 * @author Julien Niset - <[email protected]>
 * @author Olivier Van Den Biggelaar - <[email protected]>
 */
contract LockManager is BaseModule, RelayerModule {

    bytes32 constant NAME = "LockManager";

    // the address of the Guardian storage
    GuardianStorage public guardianStorage;
    // The lock period
    uint256 public lockPeriod;

    // *************** Events *************************** //

    event Locked(address indexed wallet, uint64 releaseAfter);
    event Unlocked(address indexed wallet);
    
    // *************** Modifiers ************************ //

    /**
     * @dev Throws if the wallet is not locked.
     */
    modifier onlyWhenLocked(BaseWallet _wallet) {
        // solium-disable-next-line security/no-block-members
        require(guardianStorage.isLocked(_wallet), "GD: wallet must be locked");
        _;
    }

    /**
     * @dev Throws if the wallet is locked.
     */
    modifier onlyWhenUnlocked(BaseWallet _wallet) {
        // solium-disable-next-line security/no-block-members
        require(!guardianStorage.isLocked(_wallet), "GD: wallet must be unlocked");
        _;
    }

    /**
     * @dev Throws if the caller is not a guardian for the wallet.
     */
    modifier onlyGuardian(BaseWallet _wallet) {
        (bool isGuardian, ) = GuardianUtils.isGuardian(guardianStorage.getGuardians(_wallet), msg.sender);
        require(msg.sender == address(this) || isGuardian, "GD: wallet must be unlocked");
        _;
    }

    // *************** Constructor ************************ //

    constructor(ModuleRegistry _registry, GuardianStorage _guardianStorage, uint256 _lockPeriod) BaseModule(_registry, NAME) public {
        guardianStorage = _guardianStorage;
        lockPeriod = _lockPeriod;
    }

    // *************** External functions ************************ //

    /**
     * @dev Lets a guardian lock a wallet.
     * @param _wallet The target wallet.
     */
    function lock(BaseWallet _wallet) external onlyGuardian(_wallet) onlyWhenUnlocked(_wallet) {
        guardianStorage.setLock(_wallet, now + lockPeriod);
        emit Locked(_wallet, uint64(now + lockPeriod));
    }

    /**
     * @dev Lets a guardian unlock a locked wallet.
     * @param _wallet The target wallet.
     */
    function unlock(BaseWallet _wallet) external onlyGuardian(_wallet) onlyWhenLocked(_wallet) {
        address locker = guardianStorage.getLocker(_wallet);
        require(locker == address(this), "LM: cannot unlock a wallet that was locked by another module");
        guardianStorage.setLock(_wallet, 0);
        emit Unlocked(_wallet);
    }

    /**
     * @dev Returns the release time of a wallet lock or 0 if the wallet is unlocked.
     * @param _wallet The target wallet.
     * @return The epoch time at which the lock will release (in seconds).
     */
    function getLock(BaseWallet _wallet) public view returns(uint64 _releaseAfter) {
        uint256 lockEnd = guardianStorage.getLock(_wallet); 
        if(lockEnd > now) {
            _releaseAfter = uint64(lockEnd);
        }
    }

    /**
     * @dev Checks if a wallet is locked.
     * @param _wallet The target wallet.
     * @return true if the wallet is locked.
     */
    function isLocked(BaseWallet _wallet) external view returns (bool _isLocked) {
        return guardianStorage.isLocked(_wallet);
    }

    // *************** Implementation of RelayerModule methods ********************* //

    // Overrides to use the incremental nonce and save some gas
    function checkAndUpdateUniqueness(BaseWallet _wallet, uint256 _nonce, bytes32 _signHash) internal returns (bool) {
        return checkAndUpdateNonce(_wallet, _nonce);
    }

    function validateSignatures(BaseWallet _wallet, bytes _data, bytes32 _signHash, bytes _signatures) internal view returns (bool) {
        (bool isGuardian, ) = GuardianUtils.isGuardian(guardianStorage.getGuardians(_wallet), recoverSigner(_signHash, _signatures, 0));
        return isGuardian; // "LM: must be a guardian to lock or unlock"
    }

    function getRequiredSignatures(BaseWallet _wallet, bytes _data) internal view returns (uint256) {
        return 1;
    }
}

Read Contract

getLock 0x6b9db4e6 → uint64
getNonce 0x2d0335ab → uint256
guardianStorage 0xd89784fc → address
isLocked 0x4a4fbeec → bool
lockPeriod 0x3fd8b02f → uint256
relayer 0xc9b5ef8e → uint256

Write Contract 6 functions

These functions modify contract state and require a wallet transaction to execute.

addModule 0x5a1db8c4
address _wallet
address _module
execute 0xaacaaf88
address _wallet
bytes _data
uint256 _nonce
bytes _signatures
uint256 _gasPrice
uint256 _gasLimit
returns: bool
init 0x19ab453c
address _wallet
lock 0xf435f5a7
address _wallet
recoverToken 0x9be65a60
address _token
unlock 0x2f6c493c
address _wallet

Recent Transactions

No transactions found for this address