Currently, only validator, fallback and executor module types are supported (as defined in the standard).
To install, uninstall or unlink a module, call the corresponding core functions on the account contract:
installModule(uint256 typeId, address module, bytes calldata initData)uninstallModule(uint256 typeId, address module, bytes calldata deinitData)unlinkModule(uint256 typeId, address module, bytes calldata deinitData)
typeId, according to the standard:
- 1 for validator
- 2 for executor
- 3 for fallback
The account will call module's onInstall(bytes) hook upon installation and onUninstall(bytes) hook upon uninstall and unlink. The format for the supplied data is different for each module and is described below.
Unlinking is the same as uninstalling, but does not fail if onUninstall call to the module fails. Instead, error is emitted as ModuleUnlinked(uint256 indexed typeId, address indexed module, bytes errorMsg) event.
A single contract can house multiple module types, each type is installed separately.
Stores EOA addresses as account owners. Each address has full "admin" privileges to the account, as long as this validator is installed.
onInstalldata format: ABI-encoded array of addresses - initial ownersonUninstalldata format: ABI-encoded array of addresses - owners to remove
Other methods:
addOwner(address owner)- adds an EOA owner, emitsOwnerAdded(address indexed account, address indexed owner)removeOwner(address owner)- removes existing EOA owner, emitsOwnerRemoved(address indexed account, address indexed owner)isOwnerOf(address account, address owner) returns (bool)- whether or not an address is an owner of the account
Stores WebAuthn passkeys per origin domain for each account. Each passkey has full "admin" privileges to the account, as long as this validator is installed.
onInstalldata format: ABI-encoded(bytes credentialId, bytes32[2] publicKey, string domain)- initial passkey, or emptyonUninstalldata format: ABI-encoded array of(string domain, bytes credentialId)- passkeys to remove
Other methods:
addValidationKey(bytes credentialId, bytes32[2] newKey, string domain)- adds new passkeyremoveValidationKey(bytes credentialId, string domain)- removes existing passkeygetAccountKey(string domain, bytes credentialId, address account) returns (bytes32[2])- account's public key on the domain with given credential IDgetAccountList(string domain, bytes credentialId) returns (address[])- list of accounts on the domain with given credential ID (normally length of 1)
Grants a 3rd party limited access to the account with configured permissions.
A session is defined by the following structure:
struct SessionSpec {
address signer;
uint48 expiresAt;
UsageLimit feeLimit;
CallSpec[] callPolicies;
TransferSpec[] transferPolicies;
}signer- Address corresponding to an EOA private key that will be used to sign session transactions. Signers are required to be globally unique.expiresAt- Timestamp after which the session no longer can be used. Session expiration is required to be no earlier than 60 seconds after session creation.feeLimit- aUsageLimit(explained below) structure that limits how much fees this session can spend. Required to not beUnlimited.callPolicies- aCallSpec(explained below) array that defines what kinds of calls are permitted in the session. The array has to have unique (target,selector) pairs.transferPolicies- aTransferSpec(explained below) array that defines what kinds of transfers (calls with no calldata) are permitted in the session. The array has to have unique targets.
All usage limits are defined by the following structure:
struct UsageLimit {
LimitType limitType; // can be Unlimited (0), Lifetime (1) or Allowance (2)
uint256 limit; // ignored if limitType == Unlimited
uint48 period; // ignored if limitType != Allowance
}limitTypedefines what kind of limit (if any) this is.Unlimiteddoes not define any limits.Lifetimedefines a cumulative lifetime limit: sum of all uses of the value in the current session has to not surpasslimit.Allowancedefines a periodically refreshing limit: sum of all uses of the value during the currentperiodhas to not surpasslimit.
limit- the actual number to limit by.period- length of the period in seconds.
Transfer policies are defined by the following structure:
struct TransferSpec {
address target;
uint256 maxValuePerUse;
UsageLimit valueLimit;
}target- address to which transfer is being made.maxValuePerUse- maximum value that is possible to send in one transfer.valueLimit- cumulative transfer value limit.
Call policies are defined by the following structure:
struct CallSpec {
address target;
bytes4 selector;
uint256 maxValuePerUse;
UsageLimit valueLimit;
Constraint[] constraints;
}target- address to which call is being made.selector- selector of the method being called.maxValuePerUse- maximum value that is possible to send in one call.valueLimit- cumulative call value limit.constraints- array ofConstraint(explained below) structures that define constraints on method arguments.
Call constraints are defined by the following structures:
struct Constraint {
Condition condition;
uint64 index;
bytes32 refValue;
UsageLimit limit;
}
enum Condition {
Unconstrained,
Equal,
Greater,
Less,
GreaterOrEqual,
LessOrEqual,
NotEqual
}index- index of the argument in the called method, starting with 0, assuming all arguments are 32-byte words after ABI-encoding.- E.g., specifying
index: Xwill constrain calldata bytes4+32*X:4+32*(X+1)
- E.g., specifying
limit- usage limit for the argument interpreted asuint256.condition- how the argument is required to relate torefValue: seeenum Conditionabove.refValue- reference value for the condition; ignored if condition isUnconstrained.
onInstalldata format: emptyonUninstalldata format: ABI-encoded array of session hashes to revoke
Other methods:
createSession(SessionSpec spec, bytes proof)- create a new session; requiresproof- a signature of the hashkeccak256(abi.encode(sessionHash, accountAddress))signed by sessionsignerrevokeKey(bytes32 sessionHash)- closes an active session by the provided hashrevokeKeys(bytes32[] sessionHashes)- same asrevokeKeybut closes multiple sessions at oncesessionStatus(address account, bytes32 sessionHash) returns (SessionStatus)- returnsNotInitialized(0),Active(1) orClosed(2); note: expired sessions are still considered active if not revoked explicitlysessionState(address account, SessionSpec spec) returns (SessionState)- returns the session status and the state of all cumulative limits used in the session as a following structure:
// Info about remaining session limits and its status
struct SessionState {
Status status;
uint256 feesRemaining;
LimitState[] transferValue;
LimitState[] callValue;
LimitState[] callParams;
}
struct LimitState {
uint256 remaining; // this might also be limited by a constraint or `maxValuePerUse`, which is not reflected here
address target;
bytes4 selector; // ignored for transfer value
uint256 index; // ignored for transfer and call value
}Note: sessionHash is what is stored on-chain, and is defined by keccak256(abi.encode(sessionSpec)).
Stores addresses trusted by the account to perform an EOA or WebAuthn key recovery. Either EOAKeyValidator or WebAuthnValidator must be installed.
The flow is the following:
sequenceDiagram
actor Guardian
participant GuardiansExecutor
participant SmartAccount as SmartAccount (ERC-7579)
participant WebauthnValidator
actor User
User->>SmartAccount: proposeGuardian(guardian)
SmartAccount-->>WebauthnValidator: validate
WebauthnValidator-->>SmartAccount: ok
SmartAccount->>GuardiansExecutor: proposeGuardian(guardian)
Guardian->>GuardiansExecutor: acceptGuardian(account)
Note over User: Lose passkey
Guardian->>GuardiansExecutor: initializeRecovery(account, new passkey)
Note over Guardian: Wait 24 hours
Guardian->>GuardiansExecutor: finalizeRecovery(account, new passkey)
GuardiansExecutor->>SmartAccount: executeFromExecutor("add new passkey")
SmartAccount->>WebauthnValidator: addValidationKey(new passkey)
Important notes:
- Account owner has to first propose to another address to be its guardian
- After the guardian address accepts, it can initiate a recovery
- Recovery can be either for an EOA key or a Webauthn passkey, given that a corresponding validator is installed on the account
- Any guardian can initiate a recovery alone. Guardians can themselves be multisig accounts if that is desired
- A user can discard an initiated recovery in case one of the guardians is malicious
- Recovery can be finalized not earlier than 24 hours and not later than 72 hours after initiating it
- Only one recovery can be ongoing at a time
onInstalldata format: emptyonUninstalldata format: empty
Other methods:
proposeGuardian(address newGuardian)- propose an address to be a guardianacceptGuardian(address accountToGuard)- an address that was proposed to can accept its role as a guardianinitializeRecovery(address accountToRecover, RecoveryType recoveryType, bytes data)- initialize recovery of an EOA key (recoveryType1) or passkey (recoveryType2) of an account;datais ABI-encoded arguments toEOAKeyValidator.addOwnerorWebAuthnValidator.addValidationKeyfinalizeRecovery(address account, bytes data)- finalize an ongoing recovery; the same data has to be passed in as was passed during initializingdiscardRecovery()- discard an ongoing recoveryguardianStatusFor(address account, address guardian) returns (bool isPresent, bool isActive)- whether a given address was proposed to (is present) and has accepted (is active)