# argent钱包源码-（四）中继者发起交易流程

By [daxiong](https://paragraph.com/@daxiong) · 2022-10-05

---

argent钱包中的relayerMananger中的excute方法，是发起中继者交易的入口；

transactionManager中的mutilCall方法最终验证交易，并且发起交易，此方法支持发起批量交易；

1、调用relayerManager中的excute方法发起交易；

    function execute(
            address _wallet,
            bytes calldata _data,
            uint256 _nonce,
            bytes calldata _signatures,
            uint256 _gasPrice,
            uint256 _gasLimit,
            address _refundToken,
            address _refundAddress
        )
            external
            returns (bool)
        {
            initial gas = 21k + non_zero_bytes * 16 + zero_bytes * 4
            //            ~= 21k + calldata.length * [1/3 * 16 + 2/3 * 4]
            uint256 startGas = gasleft() + 21000 + msg.data.length * 8;
            require(startGas >= _gasLimit, "RM: not enough gas provided");
            //验证要执行的data的第一个参数必须是钱包地址
            require(verifyData(_wallet, _data), "RM: Target of _data != _wallet");
    
            require(!_isLocked(_wallet) || _gasPrice == 0, "RM: Locked wallet refund");
    
            StackExtension memory stack;
            (stack.requiredSignatures, stack.ownerSignatureRequirement) = getRequiredSignatures(_wallet, _data);
    
            require(stack.requiredSignatures > 0 || stack.ownerSignatureRequirement == OwnerSignature.Anyone, "RM: Wrong signature requirement");
            require(stack.requiredSignatures * 65 == _signatures.length, "RM: Wrong number of signatures");
            stack.signHash = getSignHash(
                address(this),
                0,
                _data,
                _nonce,
                _gasPrice,
                _gasLimit,
                _refundToken,
                _refundAddress);
            require(checkAndUpdateUniqueness(
                _wallet,
                _nonce,
                stack.signHash,
                stack.requiredSignatures,
                stack.ownerSignatureRequirement), "RM: Duplicate request");
    
            if (stack.ownerSignatureRequirement == OwnerSignature.Session) {
    //验签
                require(validateSession(_wallet, stack.signHash, _signatures), "RM: Invalid session");
            } else {
    //验签
                require(validateSignatures(_wallet, stack.signHash, _signatures, stack.ownerSignatureRequirement), "RM: Invalid signatures");
            }
    //调用mutilCall方法
            (stack.success, stack.returnData) = address(this).call(_data);
    //退回手续费
            refund(
                _wallet,
                startGas,
                _gasPrice,
                _gasLimit,
                _refundToken,
                _refundAddress,
                stack.requiredSignatures,
                stack.ownerSignatureRequirement);
            emit TransactionExecuted(_wallet, stack.success, stack.returnData, stack.signHash);
            return stack.success;
        }
    

2、验签：签名必须是钱包主人或者钱包监护人；

    function validateSignatures(address _wallet, bytes32 _signHash, bytes memory _signatures, OwnerSignature _option) internal view returns (bool)
        {
            if (_signatures.length == 0) {
                return true;
            }
            address lastSigner = address(0);
            address[] memory guardians;
            if (_option != OwnerSignature.Required || _signatures.length > 65) {
                guardians = guardianStorage.getGuardians(_wallet); // guardians are only read if they may be needed
            }
            bool isGuardian;
    
            for (uint256 i = 0; i < _signatures.length / 65; i++) {
                address signer = Utils.recoverSigner(_signHash, _signatures, i);
    
                if (i == 0) {
                    if (_option == OwnerSignature.Required) {
                        // First signer must be owner
                        if (_isOwner(_wallet, signer)) {
                            continue;
                        }
                        return false;
                    } else if (_option == OwnerSignature.Optional) {
                        // First signer can be owner
                        if (_isOwner(_wallet, signer)) {
                            continue;
                        }
                    }
                }
                if (signer <= lastSigner) {
                    return false; // Signers must be different
                }
                lastSigner = signer;
                (isGuardian, guardians) = Utils.isGuardianOrGuardianSigner(guardians, signer);
                if (!isGuardian) {
                    return false;
                }
            }
            return true;
        }
    

3、调用transactionManager中的mutilCall方法（支持批量交易），验证交易的白名单，并且发起交易；

4、调用baseModule中的invokeWallet方法发起对wallet的交易；

    function multiCall(
            address _wallet,
            Call[] calldata _transactions
        )
            external
            onlySelf()
            onlyWhenUnlocked(_wallet)
            returns (bytes[] memory)
        {
            bytes[] memory results = new bytesUnsupported embed;
            for(uint i = 0; i < _transactions.length; i++) {
                //获得交易目标地址  
                address spender = Utils.recoverSpender(_transactions[i].to, _transactions[i].data);
                //验证最终的目标地址必须在白名单内      
                require(
                    (_transactions[i].value == 0 || spender == _transactions[i].to) &&
                    (isWhitelisted(_wallet, spender) || authoriser.isAuthorised(_wallet, spender, _transactions[i].to, _transactions[i].data)),
                    "TM: call not authorised");
                //调用baseModule中的invokeWallet方法发起对wallet的交易
                results[i] = invokeWallet(_wallet, _transactions[i].to, _transactions[i].value, _transactions[i].data);
            }
            return results;
        }
    

5、调用wallet中的invoke方法，最终调用data数据；

    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);
        }
    

7、发起交易成功，调用relayerManager中的refund方法从钱包中退回手续费到refundAddress；

退回的逻辑：

7.1、如果refundToken是erc20协议的token，那么从uniswap中获取价格，然后按照手续费计算出数量，从钱包地址转移到refundAddress；

7.2、如果refundToken是ETH，那么直接将等量的ETH从钱包地址转移到refundAddress；

    function refund(
            address _wallet,
            uint _startGas,
            uint _gasPrice,
            uint _gasLimit,
            address _refundToken,
            address _refundAddress,
            uint256 _requiredSignatures,
            OwnerSignature _option
        )
            internal
        {
            // Only refund when the owner is one of the signers or a session key was used
            if (_gasPrice > 0 && (_option == OwnerSignature.Required || _option == OwnerSignature.Session)) {
                address refundAddress = _refundAddress == address(0) ? msg.sender : _refundAddress;
                if (_requiredSignatures == 1 && _option == OwnerSignature.Required) {
                        // refundAddress must be whitelisted/authorised
                        if (!authoriser.isAuthorised(_wallet, refundAddress, address(0), EMPTY_BYTES)) {
                            uint whitelistAfter = userWhitelist.getWhitelist(_wallet, refundAddress);
                            require(whitelistAfter > 0 && whitelistAfter < block.timestamp, "RM: refund not authorised");
                        }
                }
                uint256 refundAmount;
                if (_refundToken == ETH_TOKEN) {
                    // 23k as an upper bound to cover the rest of refund logic
                    uint256 gasConsumed = _startGas - gasleft() + 23000;
                    refundAmount = Math.min(gasConsumed, _gasLimit) * (Math.min(_gasPrice, tx.gasprice));
                    invokeWallet(_wallet, refundAddress, refundAmount, EMPTY_BYTES);
                } else {
                    // 37.5k as an upper bound to cover the rest of refund logic
                    uint256 gasConsumed = _startGas - gasleft() + 37500;
                    uint256 tokenGasPrice = inToken(_refundToken, tx.gasprice);
                    refundAmount = Math.min(gasConsumed, _gasLimit) * (Math.min(_gasPrice, tokenGasPrice));
                    bytes memory methodData = abi.encodeWithSelector(ERC20.transfer.selector, refundAddress, refundAmount);
                    bytes memory transferSuccessBytes = invokeWallet(_wallet, _refundToken, 0, methodData);
                    // Check token refund is successful, when `transfer` returns a success bool result
                    if (transferSuccessBytes.length > 0) {
                        require(abi.decode(transferSuccessBytes, (bool)), "RM: Refund transfer failed");
                    }
                }
                emit Refund(_wallet, refundAddress, _refundToken, refundAmount);    
            }
        }
    

完整的流程图如下：

![](https://storage.googleapis.com/papyrus_images/5945c9d34a10f8378c7e175910249293bd97608cc4fd021fa7065432283adcb3.png)

---

*Originally published on [daxiong](https://paragraph.com/@daxiong/argent-4)*
