ERC-6981: Reserved Ownership Accounts
A registry for generating future-deployed smart contract accounts owned by users on external services
Abstract
The following specifies a system for services to link their users to a claimable Ethereum address. Services can provide a signed message and unique salt to their users which can be used to deploy a smart contract wallet to the deterministic address through a registry contract using the create2
opcode.
Motivation
It is common for web services to allow their users to hold on-chain assets via custodial wallets. These wallets are typically EOAs, deployed smart contract wallets or omnibus contracts, with private keys or asset ownership information stored on a traditional database. This proposal outlines a solution that avoids the security concerns associated with historical approaches, and rids the need and implications of services controlling user assets
Users on external services that choose to leverage the following specification can be given an Ethereum address to receive assets without the need to do any on-chain transaction. These users can choose to attain control of said addresses at a future point in time. Thus, on-chain assets can be sent to and owned by a user beforehand, therefore enabling the formation of an on-chain identity without requiring the user to interact with the underlying blockchain.
Specification
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
Overview
The system for creating reserved ownership accounts consists of:
- An Account Registry which provides deterministic addresses based on the service users' identifying salts, and implements a signature verified function that enables claiming of Account Instances by the service's end users.
- Account Instances created through the Account Registry by end users which allow access to the assets received at the deterministic address prior to Account Instance deployment.
External services wishing to provide their users with reserved ownership accounts MUST maintain a relationship between a user's identifying credentials and a salt. The external service SHALL refer to an Account Registry Instance to retrieve the deterministic account address for a given salt. Users of a given service MUST be able to create an Account Instance by validating their identifying credentials via the external service, which SHOULD give the user a signed message for their salt. Signatures SHOULD be generated by the external service using an signing address known to the Account Registry Instance. Users SHALL pass this message and signature to the service's Account Registry Instance in a call to claimAccount
to deploy and claim an Account Instance at the deterministic address.
Account Registry
The Account Registry MUST implement the following interface:
createAccount
createAccount
is used to deploy the Account Instance for a given salt.
- This function MUST deploy a new Account Instance as a ERC-1167 proxy pointing to the account implementation.
- This function SHOULD set the initial owner of the Account Instance to the Account Registry Instance.
- The account implementation address MUST be immutable, as it is used to compute the deterministic address for the Account Instance.
- Upon successful deployment of the Account Instance, the registry SHOULD emit an
AccountCreated
event.
claimAccount
claimAccount
is used to claim ownership of the Account Instance for a given salt.
- This function MUST create a new Account Instance if one does not already exist for the given salt.
- This function SHOULD verify that the msg.sender has permission to claim ownership over the Account Instance for the identifying salt and initial owner. Verification SHOULD be done by validating the message and signature against the owner, salt and expiration using ECDSA for EOA signers, or ERC-1271 for smart contract signers.
- This function SHOULD verify that the block.timestamp < expiration or that expiration == 0.
- Upon successful signature verification on calls to
claimAccount
, the registry MUST completely relinquish control over the Account Instance, and assign ownership to the initial owner by callingsetOwner
on the Account Instance. - Upon successful claim of the Account Instance, the registry SHOULD emit an
AccountClaimed
event.
isValidSignature
isValidSignature
is a fallback signature verification function used by unclaimed accounts. Valid signatures SHALL be generated by the registry signer by signing a composite hash of the original message hash, and the Account Instance address (e.g. bytes32 compositeHash = keccak256(abi.encodePacked(originalHash, accountAddress))
). The function MUST reconstruct the composite hash, where originalHash
is the hash passed to the function, and accountAddress
is msg.sender
(the unclaimed Account Instance). The function MUST verify the signature against the composite hash and registry signer.
Account Instance
The Account Instance MUST implement the following interface:
- All Account Instances MUST be created using an Account Registry Instance.
- Account Instances SHOULD provide access to assets previously sent to the address at which the Account Instance is deployed to.
setOwner
SHOULD update the owner and SHOULD be callable by the current owner of the Account Instance.- If an Account Instance is deployed, but not claimed, the owner of the Account Instance MUST be initialized to the Account Registry Instance.
- An Account Instance SHALL determine if it has been claimed by checking if the owner is the Account Registry Instance.
Account Instance Signatures
Account Instances MUST support ERC-1271 by implementing an isValidSignature
function. When the owner of an Account Instance wants to sign a message (e.g. to log in to a dApp), the signature MUST be generated in one of the following ways, depending the state of the Account Instance:
- If the Account instance is deployed and claimed, the owner should generate the signature, and
isValidSignature
SHOULD verify that the message hash and signature are valid for the current owner of the Account Instance. - If the Account Instance is deployed, but unclaimed, the registry signer should generate the signature using a composite hash of the original message and address of the Account Instance described above, and
isValidSignature
SHOULD forward the message hash and signature to the Account Registry Instance'sisValidSignature
function. - If the Account Instance is not deployed, the registry signer should generate a signature on the composite hash as done in situation 2, and wrap the signature according to ERC-6492 (e.g.
concat(abi.encode((registryAddress, createAccountCalldata, compositeHashSignature), (address, bytes, bytes)), magicBytes)
).
Signature validation for Account Instances should be done according to ERC-6492.
Rationale
Service-Owned Registry Instances
While it might seem more user-friendly to implement and deploy a universal registry for reserved ownership accounts, we believe that it is important for external service providers to have the option to own and control their own Account Registry. This provides the flexibility of implementing their own permission controls and account deployment authorization frameworks.
We are providing a reference Registry Factory which can deploy Account Registries for an external service, which comes with:
- Immutable Account Instance implementation
- Validation for the
claimAccount
method via ECDSA for EOA signers, or ERC-1271 validation for smart contract signers - Ability for the Account Registry deployer to change the signing addressed used for
claimAccount
validation
Account Registry and Account Implementation Coupling
Since Account Instances are deployed as ERC-1167 proxies, the account implementation address affects the addresses of accounts deployed from a given Account Registry. Requiring that registry instances be linked to a single, immutable account implementation ensures consistency between a user's salt and linked address on a given Account Registry Instance.
This also allows services to gain the trust of users by deploying their registries with a reference to a trusted account implementation address.
Furthermore, account implementations can be designed as upgradeable, so users are not necessarily bound to the implementation specified by the Account Registry Instance used to create their account.
Separate createAccount
and claimAccount
Operations
Operations to create and claim Account Instances are intentionally separate. This allows services to provide users with valid ERC-6492 signatures before their Account Instance has been deployed.
Reference Implementation
The following is an example of an Account Registry Factory which can be used by external service providers to deploy their own Account Registry Instance.
Account Registry Factory
Account Registry
Example Account Implementation
Security Considerations
Front-running
Deployment of reserved ownership accounts through an Account Registry Instance through calls to createAccount
could be front-run by a malicious actor. However, if the malicious actor attempted to alter the owner
parameter in the calldata, the Account Registry Instance would find the signature to be invalid, and revert the transaction. Thus, any successful front-running transaction would deploy an identical Account Instance to the original transaction, and the original owner would still gain control over the address.
Copyright
Copyright and related rights waived via CC0.