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