argent钱包源码-(一)架构

argent钱包源码主要分三大部分:

1、钱包合约:分为 proxy代理合约和逻辑合约,钱包合约没有实现任何的逻辑;

2、模块合约:分为baseWallet合约和模块逻辑合约,通过钱包模块加载机制将模块功能绑定到钱包,该部分实现了钱包的所有功能,包括转账、白名单相关的功能;

3、工厂合约:主要是初始化和创建钱包功能;

结构关系图如下:

argent钱包的钱包合约部分,本身并没有任何功能,所有的功能均由模块合约提供

接下来,展开说下这三个部分的主要功能:

钱包合约:

1、init方法:初始化钱包,设置钱包的拥有者和模块。

/**
     * @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));
            if(i>0){
                staticCallExecutor = _modules[i];
            }
            emit AuthorisedModule(_modules[i], true);
        }
        if (address(this).balance > 0) {
            emit Received(address(this).balance, address(0), "");
        }
    }

2、invoke:由合约本身发起调用其他合约的方法。该方法智能被模块合约调用。

/**
     * @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);
    }

    function onERC721Received(address, uint256, bytes calldata) public pure returns(bytes4) {
        return ERC721_RECEIVED;
    }

3、fallback:此方法允许携带eth。当请求的方法不存在时,会执行该方法。此方法内的staticcall 直接表明了,该方法会调用模块合约的相关功能

/**
     * @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())}
            }
        }
    }

模块合约:

1、baseWallet合约中的invokeWalle:此方法只能内部调用,逻辑是:调用钱包合约的invoke方法,间接的让钱包合约发起对其他合约的调用

/**
     * @notice Helper method to invoke a wallet.
     * @param _wallet The target wallet.
     * @param _to The target address for the transaction.
     * @param _value The value of the transaction.
     * @param _data The data of the transaction.
     */
    function invokeWallet(address _wallet, address _to, uint256 _value, bytes memory _data) internal returns (bytes memory _res) {
        bool success;
        //解析_data中的方法,如果是transfer或者空,那么要验证锁定的金额
        (success, _res) = _wallet.call(abi.encodeWithSignature("invoke(address,uint256,bytes)", _to, _value, _data));
        if (success && _res.length > 0) { //_res is empty if _wallet is an "old" BaseWallet that can't return output values
            (_res) = abi.decode(_res, (bytes));
        } else if (_res.length > 0) {
            // solhint-disable-next-line no-inline-assembly
            assembly {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
        } else if (!success) {
            revert("BM: wallet invoke reverted");
        }
    }

2、getRequiredSignatures方法:返回模块合约中所有方法中的某个方法应该的签名人和签名次数

/**
     * @inheritdoc RelayerManager
     */
    function getRequiredSignatures(address _wallet, bytes calldata _data) public view override returns (uint256, OwnerSignature) {
        bytes4 methodId = Utils.functionPrefix(_data);
        if (methodId == TransactionManager.multiCall.selector){
            // owner or platformSigner
            return (1, OwnerSignature.RequiredPlatform);
        }

        if (methodId == TransactionManager.addToWhitelist.selector ||
            methodId == TransactionManager.removeFromWhitelist.selector ||
            methodId == TransactionManager.enableERC1155TokenReceiver.selector ||
            methodId == TransactionManager.clearSession.selector ||
            methodId == UniearnModule.addModule.selector ||
            methodId == SecurityManager.addGuardian.selector ||
            methodId == SecurityManager.revokeGuardian.selector ||
            methodId == SecurityManager.cancelGuardianAddition.selector ||
            methodId == SecurityManager.cancelGuardianRevokation.selector)
        {
            // owner
            return (1, OwnerSignature.Required);
        }
        if (methodId == TransactionManager.multiCallWithSession.selector) {
            return (1, OwnerSignature.Session);
        }
        if (methodId == SecurityManager.executeRecovery.selector) {
            // majority of guardians
            uint numberOfSignaturesRequired = _majorityOfGuardians(_wallet);
            require(numberOfSignaturesRequired > 0, "AM: no guardians set on wallet");
            return (numberOfSignaturesRequired, OwnerSignature.Disallowed);
        }
        if (methodId == SecurityManager.cancelRecovery.selector) {
            // majority of (owner + guardians)
            uint numberOfSignaturesRequired = Utils.ceil(recoveryConfigs[_wallet].guardianCount + 1, 2);
            return (numberOfSignaturesRequired, OwnerSignature.Optional);
        }
        if (methodId == TransactionManager.multiCallWithGuardians.selector ||
            methodId == TransactionManager.multiCallWithGuardiansAndStartSession.selector ||
            methodId == SecurityManager.transferOwnership.selector)
        {
            // owner + majority of guardians
            uint majorityGuardians = _majorityOfGuardians(_wallet);
            uint numberOfSignaturesRequired = majorityGuardians + 1;
            return (numberOfSignaturesRequired, OwnerSignature.Required);
        }
        if (methodId == SecurityManager.finalizeRecovery.selector ||
            methodId == SecurityManager.confirmGuardianAddition.selector ||
            methodId == SecurityManager.confirmGuardianRevokation.selector)
        {
            // anyone
            return (0, OwnerSignature.Anyone);
        }
        if (methodId == SecurityManager.lock.selector || methodId == SecurityManager.unlock.selector) {
            // any guardian
            return (1, OwnerSignature.Disallowed);
        }
        revert("SM: unknown method");
    }

工厂合约:

最重要的方法是createCounterfactualWalletFromOwner,用来创建钱包

/**
     * @notice Creates a wallet by 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
     * @param _modules The list of modules for the wallet.
     * @param _guardian The guardian address.
     * @param _salt The salt.
     */
    function createCounterfactualWalletFromOwner(
        address[] calldata _modules,
        address _guardian,
        bytes20 _salt
    )
        external
        returns (address _wallet)
    {
        address _owner = msg.sender;
        validateInputs(_owner, _modules, _guardian);
        bytes32 newsalt = newSalt(_salt, _owner, _modules, _guardian);
        address payable wallet = payable(new Proxy{salt: newsalt}(walletImplementation));
        configureWallet(BaseWallet(wallet), _owner, _modules, _guardian);
        // remove the factory from the authorised modules
        BaseWallet(wallet).authoriseModule(address(this), false);

        // emit event
        emit WalletCreatedFromOwner(wallet, _owner, _guardian);

        return wallet;
    }
Subscribe to daxiong
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.