Account
This directory includes contracts to build smart accounts and account modules:
-
RoleAccount: An account bound to a role of anIAccessManager(throughSignerAccessManaged), acting on behalf of the current members of that role for both ERC-1271 signatures and ERC-7821 batched execution. -
RoleAccountFactory: A factory that deploys a deterministic account (RoleAccount) for any role of anyIAccessManager, so the current members of a role can produce signatures and execute calls on its behalf. -
ERC7579Executor: An executor module that enables executing calls from accounts where the it’s installed. -
ERC7579DelayedExecutor: An executor module that adds a delay before executing an account operation. -
ERC7579SelectorExecutor: An executor module that restricts execution to specific function selectors. -
ERC7579Validator: Abstract validator module for ERC-7579 accounts that provides base implementation for signature validation. -
ERC7579Signature: Implementation ofERC7579Validatorusing ERC-7913 signature verification for address-less cryptographic keys and account signatures. -
ERC7579Multisig: An extension ofERC7579Validatorthat enables validation using ERC-7913 signer keys. -
ERC7579MultisigWeighted: An extension ofERC7579Multisigthat allows different weights to be assigned to signers. -
ERC7579MultisigConfirmation: An extension ofERC7579Multisigthat requires each signer to provide a confirmation signature. -
ERC7579MultisigStorage: An extension ofERC7579Multisigthat allows storing presigned approvals in storage.
Accounts
RoleAccount
import "@openzeppelin/community-contracts/account/RoleAccount.sol";
On-chain account managed by the members of a role of an {IAccessManager}.
A RoleAccount is bound to a single role (see SignerAccessManaged) and acts on behalf of whoever currently
holds that role: any member can produce ERC-1271 signatures for the account or trigger batched calls
through it. Because authorization is resolved live against the access manager, granting or revoking
the role immediately grants or revokes control of the account, without touching the account itself.
It composes:
-
SignerAccessManaged: gates signature validation on role membership. -
{ERC7739}: wraps signatures as ERC-7739 nested typed data / personal-sign messages to provide replay-safe ERC-1271 validation on top of
SignerAccessManaged. -
{ERC7821}: minimal batch executor.
These accounts are intended to be deployed as Clones.cloneWithImmutableArgs, once per (access manager,
role) pair, by RoleAccountFactory. The access manager and role id are encoded in the clone’s immutable
arguments, so calling the getters on the implementation directly (outside a clone) reverts.
-
constructor() -
accessManager() -
roleId() -
_erc7821AuthorizedExecutor(caller, mode, executionData) -
receive()
-
_isAuthorizedMember(account) -
_rawSignatureValidation(hash, signature)
-
isValidSignature(hash, signature)
-
_domainSeparatorV4() -
_hashTypedDataV4(structHash) -
eip712Domain() -
_EIP712Name() -
_EIP712Version()
-
execute(mode, executionData) -
supportsExecutionMode(mode)
-
EIP712DomainChanged()
-
RoleAccountDirectCallNotAllowed() -
RoleAccountInvalidImmutableArgs()
-
UnsupportedExecutionMode()
accessManager() → contract IAccessManager accessManager_ public
Returns the access manager this account is bound to, decoded from the clone’s immutable arguments.
Reverts with RoleAccountDirectCallNotAllowed when called on the implementation directly (i.e. not through a
Clones.cloneWithImmutableArgs proxy), and with RoleAccountInvalidImmutableArgs when the immutable arguments
are not 28 bytes long.
roleId() → uint64 roleId_ public
Returns the role id this signer is bound to, decoded from the clone’s immutable arguments.
Reverts with RoleAccountDirectCallNotAllowed when called on the implementation directly (i.e. not through a
Clones.cloneWithImmutableArgs proxy), and with RoleAccountInvalidImmutableArgs when the immutable arguments
are not 28 bytes long.
RoleAccountFactory
import "@openzeppelin/community-contracts/account/RoleAccountFactory.sol";
Factory for building RoleAccount for any role of any access manager.
Each (access manager, role) pair has an associated RoleAccount deployed at an address derived
deterministically from that pair. That account acts on behalf of the current members of the role:
it can produce ERC-1271 signatures and execute batched calls, and its authority follows role
membership as it is granted or revoked through the access manager.
The account address can be computed off-chain (or on-chain via getRoleAccount) before deployment,
so it can be used as an authorization target or funded ahead of time. deployRoleAccount materializes
the clone at that address when needed.
deployRoleAccount is permissionless. Because the deployment is deterministic and behaviorally
fixed, this is harmless (front-running it only produces the same account).
|
-
constructor() -
getRoleAccount(accessManager, roleId) -
getImplementation() -
deployRoleAccount(accessManager, roleId) -
_deployImplementation()
-
RoleAccountDeployed(accessManager, roleId, account)
getRoleAccount(address accessManager, uint64 roleId) → address public
Returns the deterministic address of the RoleAccount for roleId on accessManager, whether
or not it has already been deployed.
deployRoleAccount(address accessManager, uint64 roleId) → address public
Deploys the RoleAccount clone for roleId on accessManager at its deterministic address and
returns it. Reverts if the account has already been deployed.
_deployImplementation() → address internal
Called once during construction to get the implementation used for deploying role accounts. Can be overridden to provide a custom implementation.
RoleAccountDeployed(address indexed accessManager, uint64 indexed roleId, address account) event
Emitted when a RoleAccount is deployed for a role on an access manager.
Modules
ERC7579Executor
import "@openzeppelin/community-contracts/account/modules/ERC7579Executor.sol";
Basic implementation for ERC-7579 executor modules that provides execution functionality for smart accounts.
The module enables accounts to execute arbitrary operations, leveraging the execution
capabilities defined in the ERC-7579 standard. Developers can customize whether an operation
can be executed with custom rules by implementing the _validateExecution function in
derived contracts.
This is a simplified executor that directly executes operations without delay or expiration
mechanisms. For a more advanced implementation with time-delayed execution patterns and
security features, see ERC7579DelayedExecutor.
|
-
isModuleType(moduleTypeId) -
execute(account, salt, mode, data) -
_validateExecution(account, salt, mode, data) -
_execute(account, mode, salt, executionCalldata)
-
onInstall(data) -
onUninstall(data)
-
ERC7579ExecutorOperationExecuted(account, salt, mode, executionCalldata)
execute(address account, bytes32 salt, bytes32 mode, bytes data) → bytes[] returnData public
Executes an operation and returns the result data from the executed operation.
Restricted to the account itself by default. See _execute for requirements and
_validateExecution for authorization checks.
_validateExecution(address account, bytes32 salt, bytes32 mode, bytes data) → bytes internal
Validates whether the execution can proceed. This function is called before executing the operation and returns the execution calldata to be used.
Example extension:
function _validateExecution(address account, bytes32 salt, bytes32 mode, bytes calldata data)
internal
override
returns (bytes calldata)
{
// custom logic
return data;
}
Pack extra data in the data arguments (e.g. a signature) to be used in the
validation process. Calldata can be sliced to extract it and return only the
execution calldata.
|
_execute(address account, bytes32 mode, bytes32 salt, bytes executionCalldata) → bytes[] returnData internal
Internal version of execute. Emits ERC7579ExecutorOperationExecuted event.
Requirements:
-
The
accountmust implement the {IERC7579Execution-executeFromExecutor} function.
ERC7579DelayedExecutor
import "@openzeppelin/community-contracts/account/modules/ERC7579DelayedExecutor.sol";
Extension of ERC7579Executor that allows scheduling and executing delayed operations
with expiration. This module enables time-delayed execution patterns for smart accounts.
Operation Lifecycle
-
Scheduling: Operations are scheduled via
schedulewith a specified delay period. The delay period is set duringonInstalland can be customized viasetDelay. Each operation enters aScheduledstate and must wait for its delay period to elapse. -
Security Window: During the delay period, operations remain in
Scheduledstate but cannot be executed. Through this period, suspicious operations can be monitored and canceled viacancelif appropriate. -
Execution & Expiration: Once the delay period elapses, operations transition to
Readystate. Operations can be executed viaexecuteand have an expiration period after becoming executable. If an operation is not executed within the expiration period, it becomesExpiredand can’t be executed. Expired operations must be rescheduled with a different salt.
Delay Management
Accounts can set their own delay periods during installation or via setDelay.
The delay period is enforced even between installas and uninstalls to prevent
immediate downgrades. When setting a new delay period, the new delay takes effect
after a transition period defined by the current delay or minSetback, whichever
is longer.
Authorization
Authorization for scheduling and canceling operations is controlled through the _validateSchedule
and _validateCancel functions. These functions can be overridden to implement custom
authorization logic, such as requiring specific signers or roles.
Use _scheduleAt to schedule operations at a specific points in time. This is
useful to pre-schedule operations for non-deployed accounts (e.g. subscriptions).
|
-
state(account, salt, mode, executionCalldata) -
state(operationId) -
minSetback() -
getDelay(account) -
getExpiration(account) -
getSchedule(account, salt, mode, executionCalldata) -
getSchedule(operationId) -
hashOperation(account, salt, mode, executionCalldata) -
defaultExpiration() -
onInstall(initData) -
setDelay(newDelay) -
setExpiration(newExpiration) -
schedule(account, salt, mode, data) -
cancel(account, salt, mode, data) -
onUninstall() -
_validateExecution(, , , data) -
_validateCancel(account, , , ) -
_validateSchedule(account, , , ) -
_setDelay(account, newDelay, minimumSetback) -
_setExpiration(account, newExpiration) -
_scheduleAt(account, salt, mode, executionCalldata, timepoint, delay) -
_execute(account, salt, mode, executionCalldata) -
_cancel(account, mode, executionCalldata, salt) -
_validateStateBitmap(operationId, allowedStates) -
_encodeStateBitmap(operationState)
-
isModuleType(moduleTypeId) -
execute(account, salt, mode, data)
-
ERC7579ExecutorOperationScheduled(account, operationId, salt, mode, executionCalldata, schedule) -
ERC7579ExecutorOperationCanceled(account, operationId) -
ERC7579ExecutorDelayUpdated(account, newDelay, effectTime) -
ERC7579ExecutorExpirationUpdated(account, newExpiration)
-
ERC7579ExecutorOperationExecuted(account, salt, mode, executionCalldata)
-
ERC7579ExecutorUnexpectedOperationState(operationId, currentState, allowedStates) -
ERC7579ExecutorModuleNotInstalled()
state(address account, bytes32 salt, bytes32 mode, bytes executionCalldata) → enum ERC7579DelayedExecutor.OperationState public
Current state of an operation.
state(bytes32 operationId) → enum ERC7579DelayedExecutor.OperationState public
Same as state, but for a specific operation id.
getDelay(address account) → uint32 delay, uint32 pendingDelay, uint48 effectTime public
Delay for a specific account.
getSchedule(address account, bytes32 salt, bytes32 mode, bytes executionCalldata) → uint48 scheduledAt, uint48 executableAt, uint48 expiresAt public
Schedule for an operation. Returns default values if not set (i.e. uint48(0), uint48(0), uint48(0)).
getSchedule(bytes32 operationId) → uint48 scheduledAt, uint48 executableAt, uint48 expiresAt public
Same as getSchedule but with the operation id.
hashOperation(address account, bytes32 salt, bytes32 mode, bytes executionCalldata) → bytes32 public
Returns the operation id.
defaultExpiration() → uint32 public
Default expiration for account operations. Set if not provided during onInstall.
onInstall(bytes initData) public
Sets up the module’s initial configuration when installed by an account. The account calling this function becomes registered with the module.
The initData may be abi.encode(uint32(initialDelay), uint32(initialExpiration)).
The delay will be set to the maximum of this value and the minimum delay if provided.
Otherwise, the delay will be set to minSetback and defaultExpiration respectively.
Behaves as a no-op if the module is already installed.
Requirements:
-
The account (i.e
msg.sender) must implement the {IERC7579ModuleConfig} interface. -
initDatamust be empty or decode correctly to(uint32, uint32).
setDelay(uint32 newDelay) public
Allows an account to update its execution delay (see getDelay).
The new delay will take effect after a transition period defined by the current delay
or minSetback, whichever is longer. This prevents immediate security downgrades.
Can only be called by the account itself.
setExpiration(uint32 newExpiration) public
Allows an account to update its execution expiration (see getExpiration).
schedule(address account, bytes32 salt, bytes32 mode, bytes data) public
Schedules an operation to be executed after the account’s delay period (see getDelay).
Operations are uniquely identified by the combination of salt, mode, and data.
See _validateSchedule for authorization checks.
cancel(address account, bytes32 salt, bytes32 mode, bytes data) public
Cancels a previously scheduled operation. Can only be called by the account that
scheduled the operation. See _cancel.
onUninstall(bytes) public
Cleans up the getDelay and getExpiration values by scheduling them to 0
and respecting the previous delay and expiration values.
| This function does not clean up scheduled operations. This means operations could potentially be re-executed if the module is reinstalled later. This is a deliberate design choice for efficiency, but module implementations may want to override this behavior to clear scheduled operations during uninstallation for their specific use cases. |
Calling this function directly will remove the expiration (getExpiration) value and
will schedule a reset of the delay (getDelay) to 0 for the account. Reinstalling the
module will not immediately reset the delay if the delay reset hasn’t taken effect yet.
|
_validateExecution(address, bytes32, bytes32, bytes data) → bytes internal
Returns data as the execution calldata. See ERC7579Executor._execute.
This function relies on the operation state validation in _execute for
authorization. Extensions of this module should override this function to implement
additional validation logic if needed.
|
_validateCancel(address account, bytes32, bytes32, bytes) internal
Validates whether an operation can be canceled.
Example extension:
function _validateCancel(address account, bytes32 salt, bytes32 mode, bytes calldata data) internal override {
// e.g. require(msg.sender == account);
}
_validateSchedule(address account, bytes32, bytes32, bytes) internal
Validates whether an operation can be scheduled.
Example extension:
function _validateSchedule(address account, bytes32 salt, bytes32 mode, bytes calldata data) internal override {
// e.g. require(msg.sender == account);
}
_setDelay(address account, uint32 newDelay, uint32 minimumSetback) internal
Internal implementation for setting an account’s delay. See getDelay.
Emits an ERC7579ExecutorDelayUpdated event.
_setExpiration(address account, uint32 newExpiration) internal
Internal implementation for setting an account’s expiration. See getExpiration.
Emits an ERC7579ExecutorExpirationUpdated event.
_scheduleAt(address account, bytes32 salt, bytes32 mode, bytes executionCalldata, uint48 timepoint, uint32 delay) → bytes32 operationId, struct ERC7579DelayedExecutor.Schedule schedule_ internal
Internal version of schedule that takes an account address to schedule
an operation that starts its security window at at and expires after delay.
Requirements:
-
The operation must be
Unknown.
Emits an ERC7579ExecutorOperationScheduled event.
_execute(address account, bytes32 salt, bytes32 mode, bytes executionCalldata) → bytes[] returnData internal
Requirements:
-
The operation must be
Ready.
_cancel(address account, bytes32 mode, bytes executionCalldata, bytes32 salt) internal
Internal version of cancel that takes an account address as an argument.
Requirements:
-
The operation must be
ScheduledorReady.
Canceled operations can’t be rescheduled. Emits an ERC7579ExecutorOperationCanceled event.
_validateStateBitmap(bytes32 operationId, bytes32 allowedStates) → enum ERC7579DelayedExecutor.OperationState internal
Check that the current state of a operation matches the requirements described by the allowedStates bitmap.
This bitmap should be built using _encodeStateBitmap.
If requirements are not met, reverts with a ERC7579ExecutorUnexpectedOperationState error.
_encodeStateBitmap(enum ERC7579DelayedExecutor.OperationState operationState) → bytes32 internal
Encodes a OperationState into a bytes32 representation where each bit enabled corresponds to
the underlying position in the OperationState enum. For example:
0x000...10000
^^^^^^------ ...
^----- Canceled
^---- Executed
^--- Ready
^-- Scheduled
^- Unknown
ERC7579ExecutorOperationScheduled(address indexed account, bytes32 indexed operationId, bytes32 salt, bytes32 mode, bytes executionCalldata, uint48 schedule) event
Emitted when a new operation is scheduled.
ERC7579ExecutorOperationCanceled(address indexed account, bytes32 indexed operationId) event
Emitted when a new operation is canceled.
ERC7579ExecutorDelayUpdated(address indexed account, uint32 newDelay, uint48 effectTime) event
Emitted when the execution delay is updated.
ERC7579ExecutorExpirationUpdated(address indexed account, uint32 newExpiration) event
Emitted when the expiration delay is updated.
ERC7579ExecutorUnexpectedOperationState(bytes32 operationId, enum ERC7579DelayedExecutor.OperationState currentState, bytes32 allowedStates) error
The current state of a operation is not the expected. The expectedStates is a bitmap with the
bits enabled for each OperationState enum position counting from right to left. See _encodeStateBitmap.
If expectedState is bytes32(0), the operation is expected to not be in any state (i.e. not exist).
|
ERC7579SelectorExecutor
import "@openzeppelin/community-contracts/account/modules/ERC7579SelectorExecutor.sol";
Implementation of an ERC7579Executor that allows authorizing specific function selectors
that can be executed on the account.
This module provides a way to restrict which functions can be executed on the account by maintaining a set of allowed function selectors. Only calls to functions with selectors in the set will be allowed to execute.
-
isAuthorized(account, selector) -
selectors(account) -
onInstall(initData) -
onUninstall() -
addSelectors(newSelectors) -
removeSelectors(oldSelectors) -
_addSelectors(account, newSelectors) -
_removeSelectors(account, oldSelectors) -
_validateExecution(account, , , data)
-
isModuleType(moduleTypeId) -
execute(account, salt, mode, data) -
_execute(account, mode, salt, executionCalldata)
-
ERC7579ExecutorSelectorAuthorized(account, selector) -
ERC7579ExecutorSelectorRemoved(account, selector)
-
ERC7579ExecutorOperationExecuted(account, salt, mode, executionCalldata)
-
ERC7579ExecutorSelectorNotAuthorized(selector)
selectors(address account) → bytes4[] public
Returns the set of authorized selectors for the specified account.
| This operation copies the entire selectors set to memory, which can be expensive or may result in unbounded computation. |
onInstall(bytes initData) public
Sets up the module’s initial configuration when installed by an account.
The initData should be encoded as: abi.encode(bytes4[] selectors)
onUninstall(bytes) public
Cleans up module’s configuration when uninstalled from an account. Clears all selectors.
This function has unbounded gas costs and may become uncallable if the set grows too large.
See EnumerableSetExtended.clear.
|
removeSelectors(bytes4[] oldSelectors) public
Removes a selector from the set for the calling account
_addSelectors(address account, bytes4[] newSelectors) internal
Internal version of addSelectors that takes an account as argument
_removeSelectors(address account, bytes4[] oldSelectors) internal
Internal version of removeSelectors that takes an account as argument
_validateExecution(address account, bytes32, bytes32, bytes data) → bytes internal
See ERC7579Executor._validateExecution.
Validates that the selector (first 4 bytes of the actual callData) is authorized before execution.
ERC7579ExecutorSelectorAuthorized(address indexed account, bytes4 selector) event
Emitted when a selector is added to the set
ERC7579Validator
import "@openzeppelin/community-contracts/account/modules/ERC7579Validator.sol";
Abstract validator module for ERC-7579 accounts.
This contract provides the base implementation for signature validation in ERC-7579 accounts.
Developers must implement the onInstall, onUninstall, and _rawERC7579Validation
functions in derived contracts to define the specific signature validation logic.
Example usage:
contract MyValidatorModule is ERC7579Validator {
function onInstall(bytes calldata data) public {
// Install logic here
}
function onUninstall(bytes calldata data) public {
// Uninstall logic here
}
function _rawERC7579Validation(
address account,
bytes32 hash,
bytes calldata signature
) internal view override returns (bool) {
// Signature validation logic here
}
}
Developers can restrict other operations by using the internal _rawERC7579Validation.
Example usage:
function execute(
address account,
Mode mode,
bytes calldata executionCalldata,
bytes32 salt,
bytes calldata signature
) public virtual {
require(_rawERC7579Validation(account, hash, signature));
// ... rest of execute logic
}
-
isModuleType(moduleTypeId) -
validateUserOp(userOp, userOpHash) -
isValidSignatureWithSender(, hash, signature) -
_rawERC7579Validation(account, hash, signature)
-
onInstall(data) -
onUninstall(data)
validateUserOp(struct PackedUserOperation userOp, bytes32 userOpHash) → uint256 public
Validates a UserOperation
isValidSignatureWithSender(address, bytes32 hash, bytes signature) → bytes4 public
See {IERC7579Validator-isValidSignatureWithSender}.
Ignores the sender parameter and validates using _rawERC7579Validation.
Consider overriding this function to implement custom validation logic
based on the original sender.
ERC7579Signature
import "@openzeppelin/community-contracts/account/modules/ERC7579Signature.sol";
Implementation of ERC7579Validator module using ERC-7913 signature verification.
This validator allows ERC-7579 accounts to integrate with address-less cryptographic keys
and account signatures through the ERC-7913 signature verification system. Each account
can store its own ERC-7913 formatted signer (a concatenation of a verifier address and a
key: verifier || key).
This enables accounts to use signature schemes without requiring each key to have its own Ethereum address.A smart account with this module installed can keep an emergency key as a backup.
-
signer(account) -
onInstall(data) -
onUninstall() -
setSigner(signer_) -
_setSigner(account, signer_) -
_rawERC7579Validation(account, hash, signature)
-
isModuleType(moduleTypeId) -
validateUserOp(userOp, userOpHash) -
isValidSignatureWithSender(, hash, signature)
-
ERC7579SignatureSignerSet(account, signer)
-
ERC7579SignatureInvalidSignerLength()
onInstall(bytes data) public
See {IERC7579Module-onInstall}.
| An account can only call onInstall once. If called directly by the account, the signer will be set to the provided data. Future installations will behave as a no-op. |
onUninstall(bytes) public
See {IERC7579Module-onUninstall}.
| The signer’s key will be removed if the account calls this function, potentially making the account unusable. As an account operator, make sure to uninstall to a predefined path in your account that properly handles side effects of uninstallation. See {AccountERC7579-uninstallModule}. |
setSigner(bytes signer_) public
Sets the ERC-7913 signer (i.e. verifier || key) for the calling account.
_setSigner(address account, bytes signer_) internal
Internal version of setSigner that takes an account as argument without validating signer_.
_rawERC7579Validation(address account, bytes32 hash, bytes signature) → bool internal
Validates a signature using ERC-7913 verification.
This base implementation ignores the sender parameter and validates using
the account’s stored signer. Derived contracts can override this to implement
custom validation logic based on the sender.
ERC7579Multisig
import "@openzeppelin/community-contracts/account/modules/ERC7579Multisig.sol";
Implementation of an ERC7579Validator that uses ERC-7913 signers for multisignature
validation.
This module provides a base implementation for multisignature validation that can be
attached to any function through the _rawERC7579Validation internal function. The signers
are represented using the ERC-7913 format, which concatenates a verifier address and
a key: verifier || key.
A smart account with this module installed can require multiple signers to approve operations before they are executed, such as requiring 3-of-5 guardians to approve a social recovery operation.
-
onInstall(initData) -
onUninstall() -
getSigners(account, start, end) -
getSignerCount(account) -
isSigner(account, signer) -
threshold(account) -
addSigners(newSigners) -
removeSigners(oldSigners) -
setThreshold(newThreshold) -
_rawERC7579Validation(account, hash, signature) -
_addSigners(account, newSigners) -
_removeSigners(account, oldSigners) -
_setThreshold(account, newThreshold) -
_validateReachableThreshold(account) -
_validateSignatures(account, hash, signingSigners, signatures) -
_validateThreshold(account, validatingSigners)
-
isModuleType(moduleTypeId) -
validateUserOp(userOp, userOpHash) -
isValidSignatureWithSender(, hash, signature)
-
ERC7913SignerAdded(account, signer) -
ERC7913SignerRemoved(account, signer) -
ERC7913ThresholdSet(account, threshold)
-
ERC7579MultisigAlreadyExists(signer) -
ERC7579MultisigNonexistentSigner(signer) -
ERC7579MultisigInvalidSigner(signer) -
ERC7579MultisigZeroThreshold() -
ERC7579MultisigUnreachableThreshold(signers, threshold)
onInstall(bytes initData) public
Sets up the module’s initial configuration when installed by an account.
See ERC7579DelayedExecutor.onInstall. Besides the delay setup, the initdata can
include signers and threshold.
The initData should be encoded as:
abi.encode(bytes[] signers, uint64 threshold)
If no signers or threshold are provided, the multisignature functionality will be disabled until they are added later.
| An account can only call onInstall once. If called directly by the account, the signer will be set to the provided data. Future installations will behave as a no-op. |
onUninstall(bytes) public
Cleans up module’s configuration when uninstalled from an account. Clears all signers and resets the threshold.
| This function has unbounded gas costs and may become uncallable if the set grows too large. See {EnumerableSet-clear}. |
getSigners(address account, uint64 start, uint64 end) → bytes[] public
Returns a slice of the set of authorized signers for the specified account.
Using start = 0 and end = type(uint64).max will return the entire set of signers.
Depending on the start and end, this operation can copy a large amount of data to memory, which
can be expensive. This is designed for view accessors queried without gas fees. Using it in state-changing
functions may become uncallable if the slice grows too large.
|
getSignerCount(address account) → uint256 public
Returns the number of authorized signers for the specified account.
isSigner(address account, bytes signer) → bool public
Returns whether the signer is an authorized signer for the specified account.
threshold(address account) → uint64 public
Returns the minimum number of signers required to approve a multisignature operation for the specified account.
addSigners(bytes[] newSigners) public
Adds new signers to the authorized set for the calling account. Can only be called by the account itself.
Requirements:
-
Each of
newSignersmust be at least 20 bytes long. -
Each of
newSignersmust not be already authorized.
removeSigners(bytes[] oldSigners) public
Removes signers from the authorized set for the calling account. Can only be called by the account itself.
Requirements:
-
Each of
oldSignersmust be authorized. -
After removal, the threshold must still be reachable.
setThreshold(uint64 newThreshold) public
Sets the threshold for the calling account. Can only be called by the account itself.
Requirements:
-
The threshold must be reachable with the current number of signers.
_rawERC7579Validation(address account, bytes32 hash, bytes signature) → bool internal
Returns whether the number of valid signatures meets or exceeds the threshold set for the target account.
The signature should be encoded as:
abi.encode(bytes[] signingSigners, bytes[] signatures)
Where signingSigners are the authorized signers and signatures are their corresponding
signatures of the operation hash.
_addSigners(address account, bytes[] newSigners) internal
Adds the newSigners to those allowed to sign on behalf of the account.
Requirements:
-
Each of
newSignersmust be at least 20 bytes long. Reverts withERC7579MultisigInvalidSignerif not. -
Each of
newSignersmust not be authorized. Reverts withERC7579MultisigAlreadyExistsif it already exists.
_removeSigners(address account, bytes[] oldSigners) internal
Removes the oldSigners from the authorized signers for the account.
Requirements:
-
Each of
oldSignersmust be authorized. Reverts withERC7579MultisigNonexistentSignerif not. -
The threshold must remain reachable after removal. See
_validateReachableThresholdfor details.
_setThreshold(address account, uint64 newThreshold) internal
Sets the signatures threshold required to approve a multisignature operation.
Requirements:
-
The threshold must be greater than 0. Reverts with
ERC7579MultisigZeroThresholdif not. -
The threshold must be reachable with the current number of signers. See
_validateReachableThresholdfor details.
_validateReachableThreshold(address account) internal
Validates the current threshold is reachable with the number of {signers}.
Requirements:
-
The number of signers must be >= the threshold. Reverts with
ERC7579MultisigUnreachableThresholdif not.
_validateSignatures(address account, bytes32 hash, bytes[] signingSigners, bytes[] signatures) → bool valid internal
Validates the signatures using the signers and their corresponding signatures. Returns whether the signers are authorized and the signatures are valid for the given hash.
The signers must be ordered by their keccak256 hash to prevent duplications and to optimize
the verification process. The function will return false if any signer is not authorized or
if the signatures are invalid for the given hash.
Requirements:
-
The
signaturesarray must be at least thesignersarray’s length.
_validateThreshold(address account, bytes[] validatingSigners) → bool internal
Validates that the number of signers meets the threshold requirement.
Assumes the signers were already validated. See _validateSignatures for more details.
ERC7579MultisigWeighted
import "@openzeppelin/community-contracts/account/modules/ERC7579MultisigWeighted.sol";
Extension of ERC7579Multisig that supports weighted signatures.
This module extends the multisignature module to allow assigning different weights to each signer, enabling more flexible governance schemes. For example, some guardians could have higher weight than others, allowing for weighted voting or prioritized authorization.
Example use case:
A smart account with this module installed can schedule social recovery operations after obtaining approval from guardians with sufficient total weight (e.g., requiring a total weight of 10, with 3 guardians weighted as 5, 3, and 2), and then execute them after the time delay has passed.
| When setting a threshold value, ensure it matches the scale used for signer weights. For example, if signers have weights like 1, 2, or 3, then a threshold of 4 would require signatures with a total weight of at least 4 (e.g., one with weight 1 and one with weight 3). |
-
onInstall(initData) -
onUninstall(data) -
signerWeight(account, signer) -
totalWeight(account) -
setSignerWeights(signers, weights) -
_setSignerWeights(account, signers, weights) -
_addSigners(account, newSigners) -
_removeSigners(account, oldSigners) -
_validateReachableThreshold(account) -
_validateThreshold(account, validatingSigners)
-
getSigners(account, start, end) -
getSignerCount(account) -
isSigner(account, signer) -
threshold(account) -
addSigners(newSigners) -
removeSigners(oldSigners) -
setThreshold(newThreshold) -
_rawERC7579Validation(account, hash, signature) -
_setThreshold(account, newThreshold) -
_validateSignatures(account, hash, signingSigners, signatures)
-
isModuleType(moduleTypeId) -
validateUserOp(userOp, userOpHash) -
isValidSignatureWithSender(, hash, signature)
-
ERC7579MultisigWeightChanged(account, signer, weight)
-
ERC7913SignerAdded(account, signer) -
ERC7913SignerRemoved(account, signer) -
ERC7913ThresholdSet(account, threshold)
-
ERC7579MultisigInvalidWeight(signer, weight) -
ERC7579MultisigMismatchedLength()
-
ERC7579MultisigAlreadyExists(signer) -
ERC7579MultisigNonexistentSigner(signer) -
ERC7579MultisigInvalidSigner(signer) -
ERC7579MultisigZeroThreshold() -
ERC7579MultisigUnreachableThreshold(signers, threshold)
onInstall(bytes initData) public
Sets up the module’s initial configuration when installed by an account. Besides the standard delay and signer configuration, this can also include signer weights.
The initData should be encoded as:
abi.encode(bytes[] signers, uint64 threshold, uint64[] weights)
If weights are not provided but signers are, all signers default to weight 1.
| An account can only call onInstall once. If called directly by the account, the signer will be set to the provided data. Future installations will behave as a no-op. |
onUninstall(bytes data) public
Cleans up module’s configuration when uninstalled from an account. Clears all signers, weights, and total weights.
signerWeight(address account, bytes signer) → uint64 public
Gets the weight of a signer for a specific account. Returns 0 if the signer is not authorized.
totalWeight(address account) → uint64 public
Gets the total weight of all signers for a specific account.
setSignerWeights(bytes[] signers, uint64[] weights) public
Sets weights for signers for the calling account. Can only be called by the account itself.
_setSignerWeights(address account, bytes[] signers, uint64[] weights) internal
Sets weights for multiple signers at once. Internal version without access control.
Requirements:
-
signersandweightsarrays must have the same length. Reverts withERC7579MultisigMismatchedLengthon mismatch. -
Each signer must exist in the set of authorized signers. Reverts with
ERC7579MultisigNonexistentSignerif not. -
Each weight must be greater than 0. Reverts with
ERC7579MultisigInvalidWeightif not. -
See
_validateReachableThresholdfor the threshold validation.
Emits ERC7579MultisigWeightChanged for each signer.
_addSigners(address account, bytes[] newSigners) internal
Override to add weight tracking. See ERC7579Multisig._addSigners.
Each new signer has a default weight of 1.
In cases where totalWeight is almost type(uint64).max (due to a large _totalExtraWeight), adding new
signers could cause the totalWeight computation to overflow. Adding a totalWeight call after the new
signers are added ensures no such overflow happens.
_removeSigners(address account, bytes[] oldSigners) internal
Override to handle weight tracking during removal. See ERC7579Multisig._removeSigners.
Just like _addSigners, this function does not emit ERC7579MultisigWeightChanged events. The
ERC7913SignerRemoved event emitted by ERC7579Multisig._removeSigners is enough to track weights here.
_validateReachableThreshold(address account) internal
Override to validate threshold against total weight instead of signer count.
This function intentionally does not call super._validateReachableThreshold because the base implementation
assumes each signer has a weight of 1, which is a subset of this weighted implementation. Consider that multiple
implementations of this function may exist in the contract, so important side effects may be missed
depending on the linearization order.
|
_validateThreshold(address account, bytes[] validatingSigners) → bool internal
Validates that the total weight of signers meets the threshold requirement.
Overrides the base implementation to use weights instead of count.
This function intentionally does not call super._validateThreshold because the base implementation
assumes each signer has a weight of 1, which is incompatible with this weighted implementation.
|
ERC7579MultisigWeightChanged(address indexed account, bytes indexed signer, uint64 weight) event
Emitted when a signer’s weight is changed.
Not emitted in _addSigners or _removeSigners. Indexers must rely on ERC7913SignerAdded
and ERC7913SignerRemoved to index a default weight of 1. See signerWeight.
|
ERC7579MultisigConfirmation
import "@openzeppelin/community-contracts/account/modules/ERC7579MultisigConfirmation.sol";
Extension of ERC7579Multisig that requires explicit confirmation signatures
from new signers when they are being added to the multisig.
This module ensures that only willing participants can be added as signers to a multisig by requiring each new signer to provide a valid signature confirming their consent to be added. Each signer must sign an EIP-712 message to confirm their addition.
| Use this module to ensure that all guardians in a social recovery or multisig setup have explicitly agreed to their roles. |
-
_signableConfirmationHash(account, deadline) -
_addSigners(account, newSigners)
-
_domainSeparatorV4() -
_hashTypedDataV4(structHash) -
eip712Domain() -
_EIP712Name() -
_EIP712Version()
-
onInstall(initData) -
onUninstall() -
getSigners(account, start, end) -
getSignerCount(account) -
isSigner(account, signer) -
threshold(account) -
addSigners(newSigners) -
removeSigners(oldSigners) -
setThreshold(newThreshold) -
_rawERC7579Validation(account, hash, signature) -
_removeSigners(account, oldSigners) -
_setThreshold(account, newThreshold) -
_validateReachableThreshold(account) -
_validateSignatures(account, hash, signingSigners, signatures) -
_validateThreshold(account, validatingSigners)
-
isModuleType(moduleTypeId) -
validateUserOp(userOp, userOpHash) -
isValidSignatureWithSender(, hash, signature)
-
EIP712DomainChanged()
-
ERC7913SignerAdded(account, signer) -
ERC7913SignerRemoved(account, signer) -
ERC7913ThresholdSet(account, threshold)
-
ERC7579MultisigInvalidConfirmationSignature(signer) -
ERC7579MultisigExpiredConfirmation(deadline)
-
ERC7579MultisigAlreadyExists(signer) -
ERC7579MultisigNonexistentSigner(signer) -
ERC7579MultisigInvalidSigner(signer) -
ERC7579MultisigZeroThreshold() -
ERC7579MultisigUnreachableThreshold(signers, threshold)
_signableConfirmationHash(address account, uint256 deadline) → bytes32 internal
Generates a hash that signers must sign to confirm their addition to the multisig of account.
_addSigners(address account, bytes[] newSigners) internal
Extends ERC7579Multisig._addSigners _addSigners to require confirmation signatures
Each entry in newSigners must be ABI-encoded as:
abi.encode(deadline,signer,signature); // uint256, bytes, bytes
-
signer: The ERC-7913 signer to add
-
signature: The signature from this signer confirming their addition
The function verifies each signature before adding the signer. If any signature is invalid,
the function reverts with ERC7579MultisigInvalidConfirmationSignature.
ERC7579MultisigStorage
import "@openzeppelin/community-contracts/account/modules/ERC7579MultisigStorage.sol";
Extension of ERC7579Multisig that allows storing presigned approvals in storage.
This module extends the multisignature module to allow signers to presign operations, which are then stored in a mapping and can be used during validation. This enables more flexible multisignature workflows where signatures can be collected over time without requiring all signers to be online simultaneously.
When validating signatures, if a signature is empty, it indicates a presignature and the validation will check the storage mapping instead of cryptographic verification.
-
presigned(account, signer, hash) -
presign(account, signer, hash, signature) -
_validateSignatures(account, hash, signingSigners, signatures)
-
onInstall(initData) -
onUninstall() -
getSigners(account, start, end) -
getSignerCount(account) -
isSigner(account, signer) -
threshold(account) -
addSigners(newSigners) -
removeSigners(oldSigners) -
setThreshold(newThreshold) -
_rawERC7579Validation(account, hash, signature) -
_addSigners(account, newSigners) -
_removeSigners(account, oldSigners) -
_setThreshold(account, newThreshold) -
_validateReachableThreshold(account) -
_validateThreshold(account, validatingSigners)
-
isModuleType(moduleTypeId) -
validateUserOp(userOp, userOpHash) -
isValidSignatureWithSender(, hash, signature)
-
ERC7579MultisigStoragePresigned(account, hash, signer)
-
ERC7913SignerAdded(account, signer) -
ERC7913SignerRemoved(account, signer) -
ERC7913ThresholdSet(account, threshold)
-
ERC7579MultisigAlreadyExists(signer) -
ERC7579MultisigNonexistentSigner(signer) -
ERC7579MultisigInvalidSigner(signer) -
ERC7579MultisigZeroThreshold() -
ERC7579MultisigUnreachableThreshold(signers, threshold)
presigned(address account, bytes signer, bytes32 hash) → bool public
Returns whether a signer has presigned a specific hash for the account
presign(address account, bytes signer, bytes32 hash, bytes signature) public
Allows a signer to presign a hash by providing a valid signature. The signature will be verified and if valid, the presignature will be stored.
Emits ERC7579MultisigStoragePresigned if the signature is valid and the hash is not already
signed, otherwise acts as a no-op.
Does not check if the signer is authorized for the account. Valid signatures from
invalid signers won’t be executable. See _validateSignatures for more details.
|