Axir Wallet Core
Overview
Axir Wallet Core
is a comprehensive smart contract that implements various functionalities to manage account operations, module installations, and user operations validation. It is designed to be modular and extensible, integrating multiple modules to enhance its capabilities.
Key Functionalities
Initialization
initialize
: Sets up the initial owner and installs specified modules.
function initialize(
address initialK1Owner,
address[] calldata modules,
uint256[] calldata moduleTypeIds,
bytes[] calldata initData
) external {
_k1AddOwner(initialK1Owner);
for (uint256 i = 0; i < modules.length; ) {
if (moduleTypeIds[i] == MODULE_TYPE_VALIDATOR) {
_installValidator(modules[i], initData[i]);
} else if (moduleTypeIds[i] == MODULE_TYPE_EXECUTOR) {
_installExecutor(modules[i], initData[i]);
} else if (moduleTypeIds[i] == MODULE_TYPE_FALLBACK) {
_installFallbackHandler(modules[i], initData[i]);
} else if (moduleTypeIds[i] == MODULE_TYPE_HOOK) {
_installHook(modules[i], initData[i]);
} else {
revert UnsupportedModuleType(moduleTypeIds[i]);
}
unchecked {
i++;
}
}
}
Execution
execute
: Executes a batch or single call based on the mode provided.executeFromExecutor
: Executes operations from an executor module.
function execute(
ModeCode mode,
bytes calldata executionCalldata // onlyEntryPointOrSelf
) external payable withHook(address(this), executionCalldata) {
CallType callType = mode.getCallType();
if (callType == CALLTYPE_BATCH) {
Execution[] calldata executions = executionCalldata.decodeBatch();
_execute(executions);
} else if (callType == CALLTYPE_SINGLE) {
(
address target,
uint256 value,
bytes calldata callData
) = executionCalldata.decodeSingle();
_execute(target, value, callData);
} else {
revert UnsupportedCallType(callType);
}
}
function executeFromExecutor(
ModeCode mode,
bytes calldata executionCalldata
)
external
payable
withHook(address(this), executionCalldata)
returns (
// onlyExecutorModule
bytes[] memory returnData // TODO returnData is not used
)
{
CallType callType = mode.getCallType();
if (callType == CALLTYPE_BATCH) {
Execution[] calldata executions = executionCalldata.decodeBatch();
returnData = _execute(executions);
} else if (callType == CALLTYPE_SINGLE) {
(
address target,
uint256 value,
bytes calldata callData
) = executionCalldata.decodeSingle();
returnData = new bytes ;
returnData[0] = _execute(target, value, callData);
} else {
revert UnsupportedCallType(callType);
}
}
User Operation
executeUserOp
: Executes user operations as per ERC-4337 specifications.validateUserOp
: Validates user operations to ensure they are authorized and correctly signed.
function executeUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash // onlyEntryPoint
) external payable {
bytes calldata callData = userOp.callData[4:];
(bool success, ) = address(this).delegatecall(callData);
if (!success) revert ExecutionFailed();
}
function validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
)
external
payable
virtual
returns (
// onlyEntryPoint
// payPrefund(missingAccountFunds)
uint256 validSignature
)
{
_onlyEntryPoint();
assembly {
if missingAccountFunds {
// ignore failure (its EntryPoint's job to verify, not account.)
pop(
call(
gas(),
caller(),
missingAccountFunds,
0x00,
0x00,
0x00,
0x00
)
)
}
}
address validator = _decodeValidatorFromNonce(userOp.nonce);
// check if validator is enabled. If not terminate the validation phase.
if (!_isValidatorInstalled(validator)) {
return SIG_VALIDATION_FAILED;
}
// bubble up the return value of the validator module
validSignature = IValidator(validator).validateUserOp(
userOp,
userOpHash
);
}
Module Management
installModule
: Installs a new module based on its type.uninstallModule
: Uninstalls a module based on its type.
function installModule(
uint256 moduleTypeId,
address module,
bytes calldata initData // onlyEntryPointOrSelf
) external payable {
if (!IModule(module).isModuleType(moduleTypeId))
revert MismatchModuleTypeId(moduleTypeId);
if (moduleTypeId == MODULE_TYPE_VALIDATOR)
_installValidator(module, initData);
else if (moduleTypeId == MODULE_TYPE_EXECUTOR)
_installExecutor(module, initData);
else if (moduleTypeId == MODULE_TYPE_FALLBACK)
_installFallbackHandler(module, initData);
else if (moduleTypeId == MODULE_TYPE_HOOK)
_installHook(module, initData);
else revert UnsupportedModuleType(moduleTypeId);
emit ModuleInstalled(moduleTypeId, module);
}
function uninstallModule(
uint256 moduleTypeId,
address module,
bytes calldata deInitData // onlyEntryPointOrSelf
) external payable {
if (moduleTypeId == MODULE_TYPE_VALIDATOR) {
_uninstallValidator(module, deInitData);
} else if (moduleTypeId == MODULE_TYPE_EXECUTOR) {
_uninstallExecutor(module, deInitData);
} else if (moduleTypeId == MODULE_TYPE_FALLBACK) {
_uninstallFallbackHandler(module, deInitData);
} else if (moduleTypeId == MODULE_TYPE_HOOK) {
_uninstallHook(module, deInitData);
} else {
revert UnsupportedModuleType(moduleTypeId);
}
emit ModuleUninstalled(moduleTypeId, module);
}
Signature Validation
isValidSignature
: Validates a smart account signature against a specified hash and data.
function isValidSignature(
bytes32 hash,
bytes calldata data
) external view virtual override returns (bytes4) {
address validator = address(bytes20(data[0:20]));
if (!_isValidatorInstalled(validator)) revert InvalidModule(validator);
return
IValidator(validator).isValidSignatureWithSender(
msg.sender,
hash,
data[20:]
);
}