Address Contract Verified
Address
0x536384FCd25b576265B6775F383D5ac408FF9dB7
Balance
0 ETH
Nonce
33212
Code Size
6357 bytes
Creator
0x46cf7ddb...5D11 at tx 0xc12ef5bf...0c8991
Indexed Transactions
0
Contract Bytecode
6357 bytes
0x608060405234801561001057600080fd5b50600436106100d45760003560e01c80638da5cb5b11610081578063d89784fc1161005b578063d89784fc146101b5578063fbe2ec7d146101dc578063fdff9b4d146101ef576100d4565b80638da5cb5b1461017c578063a6f9dae11461018f578063a80dd9df146101a2576100d4565b80632d06177a116100b25780632d06177a1461012f578063377e32e6146101425780638117abc114610155576100d4565b80630cb61f6c146100d957806319ab453c146101095780632c8285251461011c575b600080fd5b6002546100ec906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b61011a6101173660046112a1565b50565b005b61011a61012a3660046112a1565b610222565b61011a61013d3660046112a1565b610328565b61011a6101503660046112a1565b610437565b6100ec7f000000000000000000000000ab00ea153c43575184ff11dd5e713c96be00557381565b6000546100ec906001600160a01b031681565b61011a61019d3660046112a1565b61047f565b6100ec6101b036600461133b565b610574565b6100ec7f00000000000000000000000044da3a8051ba88eab0440db3779cab9d679ae76f81565b6100ec6101ea3660046112c4565b610790565b6102126101fd3660046112a1565b60016020526000908152604090205460ff1681565b6040519015158152602001610100565b6000546001600160a01b031633146102715760405162461bcd60e51b815260206004820152600d60248201526c26bab9ba1031329037bbb732b960991b60448201526064015b60405180910390fd5b6001600160a01b0381166102c75760405162461bcd60e51b815260206004820152601760248201527f57463a2063616e6e6f742073657420746f20656d7074790000000000000000006044820152606401610268565b6002805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040519081527fa7cb165192538768851363c5aa55b1ade75d692a51063730feccdd57d002a6ed9060200160405180910390a150565b6000546001600160a01b031633146103725760405162461bcd60e51b815260206004820152600d60248201526c26bab9ba1031329037bbb732b960991b6044820152606401610268565b6001600160a01b0381166103c85760405162461bcd60e51b815260206004820152601b60248201527f4d3a2041646472657373206d757374206e6f74206265206e756c6c00000000006044820152606401610268565b6001600160a01b03811660009081526001602052604090205460ff16610117576001600160a01b0381166000818152600160208190526040808320805460ff1916909217909155517f3b4a40cccf2058c593542587329dd385be4f0b588db5471fbd9598e56dd7093a9190a250565b60405162461bcd60e51b815260206004820152601d60248201527f57463a206d616e61676572732063616e2774206265207265766f6b65640000006044820152606401610268565b6000546001600160a01b031633146104c95760405162461bcd60e51b815260206004820152600d60248201526c26bab9ba1031329037bbb732b960991b6044820152606401610268565b6001600160a01b03811661051f5760405162461bcd60e51b815260206004820152601860248201527f41646472657373206d757374206e6f74206265206e756c6c00000000000000006044820152606401610268565b6000805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b038316908117825560405190917fa2ea9883a321a3e97b8266c2b078bfeec6d50c711ed71f874a90d500ae2eaf3691a250565b60006105828c8c8c8c610898565b6000610591898e8e8e8e6109f9565b90506000817f000000000000000000000000ab00ea153c43575184ff11dd5e713c96be0055736040516105c3906111dd565b6001600160a01b0390911681526020018190604051809103906000f59050801580156105f3573d6000803e3d6000fd5b5090506106368186868080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610a6692505050565b610643818f8f8f8f610b4b565b6000891180156106535750604186145b1561069b5761069b818f8b8b8b8b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610d9e92505050565b6040517f1f17732d000000000000000000000000000000000000000000000000000000008152306004820152600060248201526001600160a01b03821690631f17732d90604401600060405180830381600087803b1580156106fc57600080fd5b505af1158015610710573d6000803e3d6000fd5b505050508a6001600160a01b03168e6001600160a01b0316826001600160a01b03167facd43e061b8ee386537b57919358cfa44933c4e47ccd4b2e1916e54133cec7488b8d6040516107779291906001600160a01b03929092168252602082015260400190565b60405180910390a49d9c50505050505050505050505050565b600061079e86868686610898565b60006107ad83888888886109f9565b90506000604051806020016107c1906111dd565b601f1982820381018352601f90910116604081905261080e91906001600160a01b037f000000000000000000000000ab00ea153c43575184ff11dd5e713c96be0055731690602001611565565b60408051808303601f1901815282825280516020918201207fff00000000000000000000000000000000000000000000000000000000000000828501523060601b6bffffffffffffffffffffffff19166021850152603584019590955260558084019590955281518084039095018552607590920190528251920191909120979650505050505050565b6001600160a01b0384166108ee5760405162461bcd60e51b815260206004820152601760248201527f57463a20656d707479206f776e657220616464726573730000000000000000006044820152606401610268565b806001600160a01b0316846001600160a01b031614156109505760405162461bcd60e51b815260206004820152601c60248201527f57463a206f776e65722063616e6e6f7420626520677561726469616e000000006044820152606401610268565b8161099d5760405162461bcd60e51b815260206004820152601160248201527f57463a20656d707479206d6f64756c65730000000000000000000000000000006044820152606401610268565b6001600160a01b0381166109f35760405162461bcd60e51b815260206004820152601260248201527f57463a20656d70747920677561726469616e00000000000000000000000000006044820152606401610268565b50505050565b600084848484604051602001610a1294939291906114e2565b60408051601f198184030181528282528051602091820120908301526bffffffffffffffffffffffff1988169082015260540160405160208183030381529060405280519060200120905095945050505050565b60008151604114610a78575033610ade565b6040517f19457468657265756d205369676e6564204d6573736167653a0a33320000000060208201526001600160a01b038416603c820152600090605c01604051602081830303815290604052805190602001209050610ada81846000610f9c565b9150505b6001600160a01b03811660009081526001602052604090205460ff16610b465760405162461bcd60e51b815260206004820181905260248201527f57463a20756e617574686f72697365642077616c6c6574206372656174696f6e6044820152606401610268565b505050565b6000610b5883600161162b565b67ffffffffffffffff811115610b7e57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015610ba7578160200160208202803683370190505b5090503081600081518110610bcc57634e487b7160e01b600052603260045260246000fd5b60200260200101906001600160a01b031690816001600160a01b03168152505060005b83811015610c8057848482818110610c1757634e487b7160e01b600052603260045260246000fd5b9050602002016020810190610c2c91906112a1565b82610c3883600161162b565b81518110610c5657634e487b7160e01b600052603260045260246000fd5b6001600160a01b039092166020928302919091019091015280610c788161166f565b915050610bef565b506040517f3c5a3cea0000000000000000000000000000000000000000000000000000000081526001600160a01b03871690633c5a3cea90610cc89088908590600401611587565b600060405180830381600087803b158015610ce257600080fd5b505af1158015610cf6573d6000803e3d6000fd5b50506040517fc68452100000000000000000000000000000000000000000000000000000000081526001600160a01b03898116600483015285811660248301527f00000000000000000000000044da3a8051ba88eab0440db3779cab9d679ae76f16925063c68452109150604401600060405180830381600087803b158015610d7e57600080fd5b505af1158015610d92573d6000803e3d6000fd5b50505050505050505050565b6040516bffffffffffffffffffffffff19606087811b821660208401526034830186905284901b16605482015260009060680160408051601f198184030181529082905280516020918201207f19457468657265756d205369676e6564204d6573736167653a0a33320000000091830191909152603c820152605c016040516020818303038152906040528051906020012090506000610e4082846000610f9c565b9050856001600160a01b0316816001600160a01b03161415610f93576001600160a01b038416610e9b57600254604080516020810190915260008152610e959189916001600160a01b039091169088906110dc565b50610f93565b6002546040516001600160a01b0390911660248201526044810186905260009060640160408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb0000000000000000000000000000000000000000000000000000000017905290506000610f26898783856110dc565b805190915015610f905780806020019051810190610f449190611424565b610f905760405162461bcd60e51b815260206004820152601a60248201527f57463a20526566756e64207472616e73666572206661696c65640000000000006044820152606401610268565b50505b50505050505050565b6041808202830160208101516040820151919092015160009260ff9190911691601b831480610fce57508260ff16601c145b61101a5760405162461bcd60e51b815260206004820152601f60248201527f5574696c733a2062616420762076616c756520696e207369676e6174757265006044820152606401610268565b604080516000808252602082018084528a905260ff861692820192909252606081018490526080810183905260019060a0016020604051602081039080840390855afa15801561106e573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166110d15760405162461bcd60e51b815260206004820152601b60248201527f5574696c733a2065637265636f7665722072657475726e6564203000000000006044820152606401610268565b979650505050505050565b60606000856001600160a01b03168585856040516024016110ff939291906115e1565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f8f6f033200000000000000000000000000000000000000000000000000000000179052516111629190611549565b6000604051808303816000865af19150503d806000811461119f576040519150601f19603f3d011682016040523d82523d6000602084013e6111a4565b606091505b509250905080156111ca57818060200190518101906111c39190611444565b91506111d4565b3d6000803e3d6000fd5b50949350505050565b6101d4806116cc83390190565b80356111f5816116b6565b919050565b60008083601f84011261120b578081fd5b50813567ffffffffffffffff811115611222578182fd5b6020830191508360208260051b850101111561123d57600080fd5b9250929050565b80356bffffffffffffffffffffffff19811681146111f557600080fd5b60008083601f840112611272578182fd5b50813567ffffffffffffffff811115611289578182fd5b60208301915083602082850101111561123d57600080fd5b6000602082840312156112b2578081fd5b81356112bd816116b6565b9392505050565b6000806000806000608086880312156112db578081fd5b85356112e6816116b6565b9450602086013567ffffffffffffffff811115611301578182fd5b61130d888289016111fa565b9095509350506040860135611321816116b6565b915061132f60608701611244565b90509295509295909350565b60008060008060008060008060008060006101008c8e03121561135c578586fd5b6113658c6111ea565b9a5067ffffffffffffffff8060208e01351115611380578687fd5b6113908e60208f01358f016111fa565b909b5099506113a160408e016111ea565b98506113af60608e01611244565b975060808d013596506113c460a08e016111ea565b95508060c08e013511156113d6578485fd5b6113e68e60c08f01358f01611261565b909550935060e08d01358110156113fb578283fd5b5061140c8d60e08e01358e01611261565b81935080925050509295989b509295989b9093969950565b600060208284031215611435578081fd5b815180151581146112bd578182fd5b600060208284031215611455578081fd5b815167ffffffffffffffff8082111561146c578283fd5b818401915084601f83011261147f578283fd5b815181811115611491576114916116a0565b604051601f8201601f19908116603f011681019083821181831017156114b9576114b96116a0565b816040528281528760208487010111156114d1578586fd5b6110d1836020830160208801611643565b60006bffffffffffffffffffffffff19808760601b1683526014830186835b87811015611532578135611514816116b6565b6001600160a01b031683526020928301929190910190600101611501565b505060609490941b16835250506014019392505050565b6000825161155b818460208701611643565b9190910192915050565b60008351611577818460208801611643565b9190910191825250602001919050565b6000604082016001600160a01b03808616845260206040818601528286518085526060870191508288019450855b818110156115d35785518516835294830194918301916001016115b5565b509098975050505050505050565b60006001600160a01b0385168252836020830152606060408301528251806060840152611615816080850160208701611643565b601f01601f191691909101608001949350505050565b6000821982111561163e5761163e61168a565b500190565b60005b8381101561165e578181015183820152602001611646565b838111156109f35750506000910152565b60006000198214156116835761168361168a565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461011757600080fdfe60a060405234801561001057600080fd5b506040516101d43803806101d483398101604081905261002f91610044565b60601b6001600160601b031916608052610072565b600060208284031215610055578081fd5b81516001600160a01b038116811461006b578182fd5b9392505050565b60805160601c61013f610095600039600081816069015260be015261013f6000f3fe6080604052600436106100225760003560e01c80635c60da1b146100ac57610067565b3661006757604080516020808252600090820152339134917f606834f57405380c4fb88d1f4850326ad3885f014bab3b568dfbf7a041eef738910160405180910390a3005b7f00000000000000000000000000000000000000000000000000000000000000003660008037600080366000845af43d6000803e8080156100a7573d6000f35b3d6000fd5b3480156100b857600080fd5b506100e07f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f3fea2646970667358221220b88a14f52e9d465328c9b3ab476e4b7fa40ed3615fd5409a6afc9885366e03a964736f6c63430008030033a2646970667358221220dec5ae87a38d164692a0d9f7a489556203efa01d593e3bbffea1447231dc89d664736f6c63430008030033
Verified Source Code Full Match
Compiler: v0.8.3+commit.8d00100c
EVM: istanbul
Optimization: Yes (999 runs)
Proxy.sol 51 lines
// Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.3; /** * @title Proxy * @notice Basic proxy that delegates all calls to a fixed implementing contract. * The implementing contract cannot be upgraded. * @author Julien Niset - <[email protected]> */ contract Proxy { address immutable public implementation; event Received(uint indexed value, address indexed sender, bytes data); constructor(address _implementation) { implementation = _implementation; } fallback() external payable { address target = implementation; // solhint-disable-next-line no-inline-assembly assembly { calldatacopy(0, 0, calldatasize()) let result := delegatecall(gas(), target, 0, calldatasize(), 0, 0) returndatacopy(0, 0, returndatasize()) switch result case 0 {revert(0, returndatasize())} default {return (0, returndatasize())} } } receive() external payable { emit Received(msg.value, msg.sender, ""); } }
IWallet.sol 69 lines
// Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz>
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.5.4 <0.9.0;
/**
* @title IWallet
* @notice Interface for the BaseWallet
*/
interface IWallet {
/**
* @notice Returns the wallet owner.
* @return The wallet owner address.
*/
function owner() external view returns (address);
/**
* @notice Returns the number of authorised modules.
* @return The number of authorised modules.
*/
function modules() external view returns (uint);
/**
* @notice Sets a new owner for the wallet.
* @param _newOwner The new owner.
*/
function setOwner(address _newOwner) external;
/**
* @notice Checks if a module is authorised on the wallet.
* @param _module The module address to check.
* @return `true` if the module is authorised, otherwise `false`.
*/
function authorised(address _module) external view returns (bool);
/**
* @notice Returns the module responsible for a static call redirection.
* @param _sig The signature of the static call.
* @return the module doing the redirection
*/
function enabled(bytes4 _sig) external view returns (address);
/**
* @notice 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;
/**
* @notice 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;
}
BaseWallet.sol 164 lines
// Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.3; import "../modules/common/IModule.sol"; import "./IWallet.sol"; /** * @title BaseWallet * @notice Simple modular wallet that authorises modules to call its invoke() method. * @author Julien Niset - <[email protected]> */ contract BaseWallet is IWallet { // The owner address public override owner; // The authorised modules mapping (address => bool) public override authorised; // module executing static calls address public staticCallExecutor; // The number of modules uint public override modules; event AuthorisedModule(address indexed module, bool value); 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); /** * @notice Throws if the sender is not an authorised module. */ modifier moduleOnly { require(authorised[msg.sender], "BW: sender not authorized"); _; } /** * @notice 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[] calldata _modules) external { require(owner == address(0) && modules == 0, "BW: wallet already initialised"); require(_modules.length > 0, "BW: empty modules"); 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; IModule(_modules[i]).init(address(this)); emit AuthorisedModule(_modules[i], true); } if (address(this).balance > 0) { emit Received(address(this).balance, address(0), ""); } } /** * @inheritdoc IWallet */ function authoriseModule(address _module, bool _value) external override moduleOnly { if (authorised[_module] != _value) { emit AuthorisedModule(_module, _value); if (_value == true) { modules += 1; authorised[_module] = true; IModule(_module).init(address(this)); } else { modules -= 1; require(modules > 0, "BW: cannot remove last module"); delete authorised[_module]; } } } /** * @inheritdoc IWallet */ function enabled(bytes4 _sig) public view override returns (address) { address executor = staticCallExecutor; if(executor != address(0) && IModule(executor).supportsStaticCall(_sig)) { return executor; } return address(0); } /** * @inheritdoc IWallet */ function enableStaticCall(address _module, bytes4 /* _method */) external override moduleOnly { if(staticCallExecutor != _module) { require(authorised[_module], "BW: unauthorized executor"); staticCallExecutor = _module; } } /** * @inheritdoc IWallet */ function setOwner(address _newOwner) external override moduleOnly { require(_newOwner != address(0), "BW: address cannot be null"); owner = _newOwner; emit OwnerChanged(_newOwner); } /** * @notice 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 calldata _data) external moduleOnly returns (bytes memory _result) { bool success; (success, _result) = _target.call{value: _value}(_data); if (!success) { // solhint-disable-next-line no-inline-assembly assembly { returndatacopy(0, 0, returndatasize()) revert(0, returndatasize()) } } emit Invoked(msg.sender, _target, _value, _data); } /** * @notice This method delegates the static call to a target contract if the data corresponds * to an enabled module, or logs the call otherwise. */ fallback() external payable { address module = enabled(msg.sig); if (module == address(0)) { emit Received(msg.value, msg.sender, msg.data); } else { require(authorised[module], "BW: unauthorised module"); // solhint-disable-next-line 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())} } } } receive() external payable { } }
Utils.sol 194 lines
// Copyright (C) 2020 Argent Labs Ltd. <https://argent.xyz>
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.3;
/**
* @title Utils
* @notice Common utility methods used by modules.
*/
library Utils {
// ERC20, ERC721 & ERC1155 transfers & approvals
bytes4 private constant ERC20_TRANSFER = bytes4(keccak256("transfer(address,uint256)"));
bytes4 private constant ERC20_APPROVE = bytes4(keccak256("approve(address,uint256)"));
bytes4 private constant ERC721_SET_APPROVAL_FOR_ALL = bytes4(keccak256("setApprovalForAll(address,bool)"));
bytes4 private constant ERC721_TRANSFER_FROM = bytes4(keccak256("transferFrom(address,address,uint256)"));
bytes4 private constant ERC721_SAFE_TRANSFER_FROM = bytes4(keccak256("safeTransferFrom(address,address,uint256)"));
bytes4 private constant ERC721_SAFE_TRANSFER_FROM_BYTES = bytes4(keccak256("safeTransferFrom(address,address,uint256,bytes)"));
bytes4 private constant ERC1155_SAFE_TRANSFER_FROM = bytes4(keccak256("safeTransferFrom(address,address,uint256,uint256,bytes)"));
bytes4 private constant OWNER_SIG = 0x8da5cb5b;
/**
* @notice Helper method to recover 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 memory _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
// solhint-disable-next-line 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, "Utils: bad v value in signature");
address recoveredAddress = ecrecover(_signedHash, v, r, s);
require(recoveredAddress != address(0), "Utils: ecrecover returned 0");
return recoveredAddress;
}
/**
* @notice Helper method to recover the spender from a contract call.
* The method returns the contract unless the call is to a standard method of a ERC20/ERC721/ERC1155 token
* in which case the spender is recovered from the data.
* @param _to The target contract.
* @param _data The data payload.
*/
function recoverSpender(address _to, bytes memory _data) internal pure returns (address spender) {
if(_data.length >= 68) {
bytes4 methodId;
// solhint-disable-next-line no-inline-assembly
assembly {
methodId := mload(add(_data, 0x20))
}
if(
methodId == ERC20_TRANSFER ||
methodId == ERC20_APPROVE ||
methodId == ERC721_SET_APPROVAL_FOR_ALL)
{
// solhint-disable-next-line no-inline-assembly
assembly {
spender := mload(add(_data, 0x24))
}
return spender;
}
if(
methodId == ERC721_TRANSFER_FROM ||
methodId == ERC721_SAFE_TRANSFER_FROM ||
methodId == ERC721_SAFE_TRANSFER_FROM_BYTES ||
methodId == ERC1155_SAFE_TRANSFER_FROM)
{
// solhint-disable-next-line no-inline-assembly
assembly {
spender := mload(add(_data, 0x44))
}
return spender;
}
}
spender = _to;
}
/**
* @notice Helper method to parse data and extract the method signature.
*/
function functionPrefix(bytes memory _data) internal pure returns (bytes4 prefix) {
require(_data.length >= 4, "Utils: Invalid functionPrefix");
// solhint-disable-next-line no-inline-assembly
assembly {
prefix := mload(add(_data, 0x20))
}
}
/**
* @notice Checks if an address is a contract.
* @param _addr The address.
*/
function isContract(address _addr) internal view returns (bool) {
uint32 size;
// solhint-disable-next-line no-inline-assembly
assembly {
size := extcodesize(_addr)
}
return (size > 0);
}
/**
* @notice Checks if an address is a 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 isGuardianOrGuardianSigner(address[] memory _guardians, address _guardian) internal view returns (bool, address[] memory) {
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);
}
/**
* @notice 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 25000 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);
// solhint-disable-next-line no-inline-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr,OWNER_SIG)
let result := staticcall(25000, _guardian, ptr, 0x20, ptr, 0x20)
if eq(result, 1) {
owner := mload(ptr)
}
}
return owner == _owner;
}
/**
* @notice 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;
}
}
}
IModule.sol 45 lines
// Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.3; /** * @title IModule * @notice Interface for a Module. * @author Julien Niset - <[email protected]>, Olivier VDB - <[email protected]> */ interface IModule { /** * @notice Adds a module to a wallet. Cannot execute when wallet is locked (or under recovery) * @param _wallet The target wallet. * @param _module The modules to authorise. */ function addModule(address _wallet, address _module) external; /** * @notice Inits a Module for a wallet by e.g. setting some wallet specific parameters in storage. * @param _wallet The wallet. */ function init(address _wallet) external; /** * @notice Returns whether the module implements a callback for a given static call method. * @param _methodId The method id. */ function supportsStaticCall(bytes4 _methodId) external view returns (bool _isSupported); }
Owned.sol 52 lines
// Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.5.4 <0.9.0; /** * @title Owned * @notice Basic contract to define an owner. * @author Julien Niset - <[email protected]> */ contract Owned { // The owner address public owner; event OwnerChanged(address indexed _newOwner); /** * @notice Throws if the sender is not the owner. */ modifier onlyOwner { require(msg.sender == owner, "Must be owner"); _; } constructor() public { owner = msg.sender; } /** * @notice 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); } }
Managed.sol 64 lines
// Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.3; import "./Owned.sol"; /** * @title Managed * @notice Basic contract that defines a set of managers. Only the owner can add/remove managers. * @author Julien Niset, Olivier VDB - <[email protected]>, <[email protected]> */ contract Managed is Owned { // The managers mapping (address => bool) public managers; /** * @notice Throws if the sender is not a manager. */ modifier onlyManager { require(managers[msg.sender] == true, "M: Must be manager"); _; } event ManagerAdded(address indexed _manager); event ManagerRevoked(address indexed _manager); /** * @notice Adds a manager. * @param _manager The address of the manager. */ function addManager(address _manager) external onlyOwner { require(_manager != address(0), "M: Address must not be null"); if (managers[_manager] == false) { managers[_manager] = true; emit ManagerAdded(_manager); } } /** * @notice Revokes a manager. * @param _manager The address of the manager. */ function revokeManager(address _manager) external virtual onlyOwner { // solhint-disable-next-line reason-string require(managers[_manager] == true, "M: Target must be an existing manager"); delete managers[_manager]; emit ManagerRevoked(_manager); } }
WalletFactory.sol 287 lines
// Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.3; import "../wallet/Proxy.sol"; import "../wallet/BaseWallet.sol"; import "./base/Managed.sol"; import "./storage/IGuardianStorage.sol"; import "../modules/common/Utils.sol"; /** * @title WalletFactory * @notice The WalletFactory contract creates and assigns wallets to accounts. * @author Julien Niset, Olivier VDB - <[email protected]>, <[email protected]> */ contract WalletFactory is Managed { address constant internal ETH_TOKEN = address(0); // The address of the base wallet implementation address immutable public walletImplementation; // The address of the GuardianStorage address immutable public guardianStorage; // The recipient of the refund address public refundAddress; // *************** Events *************************** // event RefundAddressChanged(address addr); event WalletCreated(address indexed wallet, address indexed owner, address indexed guardian, address refundToken, uint256 refundAmount); // *************** Constructor ********************** // /** * @notice Default constructor. */ constructor(address _walletImplementation, address _guardianStorage, address _refundAddress) { require(_walletImplementation != address(0), "WF: empty wallet implementation"); require(_guardianStorage != address(0), "WF: empty guardian storage"); require(_refundAddress != address(0), "WF: empty refund address"); walletImplementation = _walletImplementation; guardianStorage = _guardianStorage; refundAddress = _refundAddress; } // *************** External Functions ********************* // /** * @notice Disables the ability for the owner of the factory to revoke a manager. */ function revokeManager(address /*_manager*/) override external pure { revert("WF: managers can't be revoked"); } /** * @notice Creates a wallet for an owner account at a specific address. * The wallet is initialised with the target modules and a first guardian by default. * The wallet is created using the CREATE2 opcode and must have been approved * by a manager of the factory. * @param _owner The account address. * @param _modules The list of modules for the wallet. * @param _guardian The guardian address. * @param _salt The salt. * @param _refundAmount The amount to refund to the relayer. * @param _refundToken The token to use to refund the relayer. * @param _ownerSignature The owner signature on the refund info. * @param _managerSignature The manager signature on the wallet address. */ function createCounterfactualWallet( address _owner, address[] calldata _modules, address _guardian, bytes20 _salt, uint256 _refundAmount, address _refundToken, bytes calldata _ownerSignature, bytes calldata _managerSignature ) external returns (address _wallet) { validateInputs(_owner, _modules, _guardian); bytes32 newsalt = newSalt(_salt, _owner, _modules, _guardian); address payable wallet = payable(new Proxy{salt: newsalt}(walletImplementation)); validateAuthorisedCreation(wallet, _managerSignature); configureWallet(BaseWallet(wallet), _owner, _modules, _guardian); if (_refundAmount > 0 && _ownerSignature.length == 65) { validateAndRefund(wallet, _owner, _refundAmount, _refundToken, _ownerSignature); } // remove the factory from the authorised modules BaseWallet(wallet).authoriseModule(address(this), false); // emit event emit WalletCreated(wallet, _owner, _guardian, _refundToken, _refundAmount); return wallet; } /** * @notice Gets the address of a counterfactual wallet with a first default guardian. * @param _owner The account address. * @param _modules The list of modules for wallet. * @param _guardian The guardian address. * @param _salt The salt. * @return _wallet The address that the wallet will have when created using CREATE2 and the same input parameters. */ function getAddressForCounterfactualWallet( address _owner, address[] calldata _modules, address _guardian, bytes20 _salt ) external view returns (address _wallet) { validateInputs(_owner, _modules, _guardian); bytes32 newsalt = newSalt(_salt, _owner, _modules, _guardian); bytes memory code = abi.encodePacked(type(Proxy).creationCode, uint256(uint160(walletImplementation))); bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff), address(this), newsalt, keccak256(code))); _wallet = address(uint160(uint256(hash))); } /** * @notice Lets the owner of the factory change the refund address. * @param _refundAddress The address to use for refunds. */ function changeRefundAddress(address _refundAddress) external onlyOwner { require(_refundAddress != address(0), "WF: cannot set to empty"); refundAddress = _refundAddress; emit RefundAddressChanged(_refundAddress); } /** * @notice Required to make the factory a module during the * initialisation of the wallet. * @param _wallet The wallet. */ function init(BaseWallet _wallet) external pure { //do nothing } // *************** Internal Functions ********************* // /** * @notice Helper method to configure a wallet for a set of input parameters. * @param _wallet The target wallet * @param _owner The owner address. * @param _modules The list of modules. * @param _guardian The guardian. */ function configureWallet(BaseWallet _wallet, address _owner, address[] calldata _modules, address _guardian) internal { // add the factory to modules so it can add the first guardian and trigger the refund address[] memory extendedModules = new address[](_modules.length + 1); extendedModules[0] = address(this); for (uint i = 0; i < _modules.length; i++) { extendedModules[i + 1] = _modules[i]; } // initialise the wallet with the owner and the extended modules _wallet.init(_owner, extendedModules); // add the first guardian IGuardianStorage(guardianStorage).addGuardian(address(_wallet), _guardian); } /** * @notice Generates a new salt based on a provided salt, an owner, a list of modules and an optional guardian. * The extra parameters are pre-hashed to be compatible with zk-sync CREATE2 API (!! the order of the parameters * assumes https://github.com/matter-labs/zksync/pull/259 has been merged !!). * @param _salt The salt provided. In practice the hash of the L2 public key. * @param _owner The owner address. * @param _modules The list of modules for wallet. * @param _guardian The guardian address. */ function newSalt(bytes20 _salt, address _owner, address[] calldata _modules, address _guardian) internal pure returns (bytes32) { return keccak256(abi.encodePacked(keccak256(abi.encodePacked(_owner, _modules, _guardian)), _salt)); } /** * @notice Throws if the owner, guardian, or module array is invalid. * @param _owner The owner address. * @param _modules The list of modules for the wallet. * @param _guardian The guardian address. */ function validateInputs(address _owner, address[] calldata _modules, address _guardian) internal pure { require(_owner != address(0), "WF: empty owner address"); require(_owner != _guardian, "WF: owner cannot be guardian"); require(_modules.length > 0, "WF: empty modules"); require(_guardian != (address(0)), "WF: empty guardian"); } /** * @notice Throws if the sender is not a manager and the manager's signature for the * creation of the new wallet is invalid. * @param _wallet The wallet address * @param _managerSignature The manager's signature */ function validateAuthorisedCreation(address _wallet, bytes memory _managerSignature) internal view { address manager; if(_managerSignature.length != 65) { manager = msg.sender; } else { bytes32 signedHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", bytes32(uint256(uint160(_wallet))))); manager = Utils.recoverSigner(signedHash, _managerSignature, 0); } require(managers[manager], "WF: unauthorised wallet creation"); } /** * @notice Refunds the creation of the wallet when provided with a valid signature from the wallet owner. * @param _wallet The wallet created * @param _owner The owner address * @param _refundAmount The amount to refund * @param _refundToken The token to use for the refund * @param _ownerSignature A signature from the wallet owner approving the refund amount and token. */ function validateAndRefund( address _wallet, address _owner, uint256 _refundAmount, address _refundToken, bytes memory _ownerSignature ) internal { bytes32 signedHash = keccak256(abi.encodePacked( "\x19Ethereum Signed Message:\n32", keccak256(abi.encodePacked(_wallet, _refundAmount, _refundToken)) )); address signer = Utils.recoverSigner(signedHash, _ownerSignature, 0); if (signer == _owner) { if (_refundToken == ETH_TOKEN) { invokeWallet(_wallet, refundAddress, _refundAmount, ""); } else { bytes memory methodData = abi.encodeWithSignature("transfer(address,uint256)", refundAddress, _refundAmount); bytes memory transferSuccessBytes = invokeWallet(_wallet, _refundToken, 0, methodData); if (transferSuccessBytes.length > 0) { require(abi.decode(transferSuccessBytes, (bool)), "WF: Refund transfer failed"); } } } } /** * @notice Invoke the wallet to execute the refund transfer. * @param _wallet The wallet * @param _to The destination of the call * @param _value The value of the call * @param _data The data associated to the call */ function invokeWallet( address _wallet, address _to, uint256 _value, bytes memory _data ) internal returns (bytes memory _res) { bool success; (success, _res) = _wallet.call(abi.encodeWithSignature("invoke(address,uint256,bytes)", _to, _value, _data)); if (success) { (_res) = abi.decode(_res, (bytes)); } else { // solhint-disable-next-line no-inline-assembly assembly { returndatacopy(0, 0, returndatasize()) revert(0, returndatasize()) } } } }
IGuardianStorage.sol 54 lines
// Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz>
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.5.4 <0.9.0;
interface IGuardianStorage {
/**
* @notice Lets an authorised module add a guardian to a wallet.
* @param _wallet The target wallet.
* @param _guardian The guardian to add.
*/
function addGuardian(address _wallet, address _guardian) external;
/**
* @notice Lets an authorised module revoke a guardian from a wallet.
* @param _wallet The target wallet.
* @param _guardian The guardian to revoke.
*/
function revokeGuardian(address _wallet, address _guardian) external;
/**
* @notice 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(address _wallet, address _guardian) external view returns (bool);
function isLocked(address _wallet) external view returns (bool);
function getLock(address _wallet) external view returns (uint256);
function getLocker(address _wallet) external view returns (address);
function setLock(address _wallet, uint256 _releaseAfter) external;
function getGuardians(address _wallet) external view returns (address[] memory);
function guardianCount(address _wallet) external view returns (uint256);
}
Read Contract
getAddressForCounterfactualWallet 0xfbe2ec7d → address
guardianStorage 0xd89784fc → address
init 0x19ab453c
managers 0xfdff9b4d → bool
owner 0x8da5cb5b → address
refundAddress 0x0cb61f6c → address
revokeManager 0x377e32e6
walletImplementation 0x8117abc1 → address
Write Contract 4 functions
These functions modify contract state and require a wallet transaction to execute.
addManager 0x2d06177a
address _manager
changeOwner 0xa6f9dae1
address _newOwner
changeRefundAddress 0x2c828525
address _refundAddress
createCounterfactualWallet 0xa80dd9df
address _owner
address[] _modules
address _guardian
bytes20 _salt
uint256 _refundAmount
address _refundToken
bytes _ownerSignature
bytes _managerSignature
returns: address
Token Balances (1)
View Transfers →Recent Transactions
No transactions found for this address