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.
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.
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.
The system for creating reserved ownership accounts consists of:
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.
The Account Registry MUST implement the following interface:
createAccount is used to deploy the Account Instance for a given salt.
AccountCreated event.claimAccount is used to claim ownership of the Account Instance for a given salt.
claimAccount, the registry MUST completely relinquish control over the Account Instance, and assign ownership to the initial owner by calling setOwner on the Account Instance.AccountClaimed event.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.
The Account Instance MUST implement the following interface:
setOwner SHOULD update the owner and SHOULD be callable by the current owner of the Account Instance.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:
isValidSignature SHOULD verify that the message hash and signature are valid for the current owner of the Account Instance.isValidSignature SHOULD forward the message hash and signature to the Account Registry Instance's isValidSignature function.concat(abi.encode((registryAddress, createAccountCalldata, compositeHashSignature), (address, bytes, bytes)), magicBytes)).Signature validation for Account Instances should be done according to ERC-6492.
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:
claimAccount method via ECDSA for EOA signers, or ERC-1271 validation for smart contract signersclaimAccount validationSince 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.
createAccount and claimAccount OperationsOperations 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.
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.
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 and related rights waived via CC0.