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 bytes[](_transactions.length);
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);
}
}
完整的流程图如下: