# argent钱包源码-(四)中继者发起交易流程 **Published by:** [daxiong](https://paragraph.com/@daxiong/) **Published on:** 2022-10-05 **URL:** https://paragraph.com/@daxiong/argent-4 ## Content 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); } } 完整的流程图如下: ## Publication Information - [daxiong](https://paragraph.com/@daxiong/): Publication homepage - [All Posts](https://paragraph.com/@daxiong/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@daxiong): Subscribe to updates - [Twitter](https://twitter.com/soliditier): Follow on Twitter