02. Deploy an ONCHAINID
Deploying an ONCHAINID creates the on-chain identity of a participant: a smart account that holds its keys, carries its claims, can hold tokens and has its first wallet linked. A participant creates its own identity, or an operator creates it on the participant's behalf: typically the agent that onboards investors for an Issuer, or a Claim Issuer, when it meets a wallet that has no identity. The identity is then created with that wallet as its only manager. It happens once per participant, before any claim can be issued to it and before it can hold any asset.
At a glance
Triggered by
Identity Owner, or a Deployer acting for it
Actors
Identity Owner, Deployer, Platform
Contracts
IdentityFactory, ONCHAINID, ERC734Validator
Preconditions
The identity type is registered in the IdentityFactory; to create for oneself, the type is self-deployable; to create for another account, the caller holds the creation role of that type; the account to link is not bound to any identity and was never revoked
Result
An ONCHAINID exists at a deterministic address, with its type recorded, its keys registered, the modules of its type installed, and its first account linked
Actors and roles
Identity Owner
Creates its own identity, or receives one created for it. Controls it from then on.
The account that becomes the first linked wallet, and the signers that become keys
Deployer
Creates an identity for another account, when the Identity Owner does not do it itself. For an investor, it is an Issuer's onboarding agent or a Claim Issuer.
The creation role of the identity type, in the AccessManager of the network
Platform
Assembles the keys, submits the transaction and records the address.
The key of the party it acts for
Sequence
Step by step
Step 1. Prepare the inputs
Who: Platform, on behalf of the caller
Call: Off-chain, using read calls
Before the call: The participant has the signer it will control the identity with.
What happens: The Platform assembles three inputs.
identityType is the type of the participant: INDIVIDUAL (2), CORPORATE (3), CLAIM_ISSUER (5) and so on. The Platform reads IdentityFactory.getIdentityTypePolicy(identityType) to learn whether the type is self-deployable and which role creates it for others.
salt is a string, not empty, chosen freely.
keys is the list of keys the identity starts with. Each key has:
signerData
The signer: the 20 bytes of an address, or a verifier address followed by a public key for a passkey or another scheme.
keyHash
keccak256 of signerData.
purpose
MANAGEMENT (1), ACTION (2), CLAIM_SIGNER (3), ENCRYPTION (4), CLAIM_ADDER (5) or PROPOSER (6).
keyType
A label for the scheme: ECDSA (1), RSA (2), WebAuthn (3).
clientData
Data a client needs to use the key, for example a WebAuthn credential identifier. Can be empty.
At least one key holds MANAGEMENT. The Platform reads IdentityFactory.getIdentity(account) for the account to link and expects zero.
What changes: Nothing on-chain.
Step 2. Create the identity
Who: Identity Owner, from the account that becomes its first wallet. Or a Deployer holding the creation role of the type.
Call: IdentityFactory.createIdentity(identityType, salt, keys) by the owner. IdentityFactory.createIdentityFor(account, identityType, salt, keys) by a Deployer.
Before the call: Step 1 is done.
What the contract checks:
The type is registered. Otherwise the call reverts with
UnknownIdentityType.For
createIdentity: the type is self-deployable. OtherwiseIdentityTypeNotSelfDeployable.For
createIdentityFor:accountis not zero, and the caller holds the creation role of the type, without an execution delay. OtherwiseZeroAddress,NotAuthorizedForIdentityTypeorDelayedRoleNotSupported.For
createIdentityForon a type whose owner can sign: every key belongs toaccount. OtherwiseKeyNotForAccount.The salt is not empty and there is at least one key. Otherwise
EmptyStringorEmptyListOfKeys.No key is of the module type. Otherwise
CallerKeyCannotBeModule.Each key is well-formed: its hash matches its signer data, the signer data has a valid length, the purpose is one of the six. Otherwise
InvalidSignerData,InvalidSignerLengthorInvalidPurpose.The identity ends up with at least one
MANAGEMENTkey that can sign. OtherwiseNoManagementKeyInKeys.The first account is not an identity, was never revoked and is not bound to another identity. Otherwise
CannotLinkFactoryIdentity,WalletAlreadyRevokedorWalletBoundToAnotherIdentity.For
createIdentityForon a type whose owner can sign:accountis the onlyMANAGEMENTkey. OtherwiseAccountNotSoleManagementKey.
What changes: The factory deploys the identity as a beacon proxy, at an address derived from the type, the salt, the first account and the keys. The identity records its type, installs the modules registered for that type and registers its keys. The factory records the type, makes the identity resolve to itself, and links the first account: the caller for createIdentity, account for createIdentityFor. The call returns the address of the identity.
Events: KeyAdded for each key, IdentityTypeRecorded(identity, identityType), AccountLinked(account, identity, caller) and IdentityDeployed(identity, account, identityType, deployer).
Step 3. Record and verify
Who: Platform
Call: Off-chain, using read calls
Before the call: The transaction of step 2 is confirmed.
What happens: The Platform reads the address from IdentityDeployed. It confirms IdentityFactory.identityTypeOf(identity), IdentityFactory.getIdentity(account), which returns the identity, and Identity.getKeysByPurpose(1), which returns the MANAGEMENT keys.
What changes: Nothing on-chain.
State after the flow
The identity exists, and
IdentityFactory.isFactoryIdentity(identity)is true.IdentityFactory.identityTypeOf(identity)returns its type, which never changes.IdentityFactory.getIdentityreturns the identity for its first account, and for the identity's own address.The identity holds the keys given at creation, and at least one
MANAGEMENTkey that can sign.The modules of its type are installed.
The identity holds no claim. It is not eligible for any asset that requires one.
The party that deployed an identity for another account holds no key on it.
Failure modes
Reverts with UnknownIdentityType
The type is not registered in the factory (check 1)
Use a registered type
Reverts with IdentityTypeNotSelfDeployable
The type cannot be created by its own owner, for example CLAIM_ISSUER (check 2)
Have a party with the creation role use createIdentityFor
Reverts with NotAuthorizedForIdentityType
The caller does not hold the creation role of the type (check 3)
Use an account that holds the role
Reverts with DelayedRoleNotSupported
The caller holds the role with an execution delay (check 3)
Use a holder with no delay on this role
Reverts with KeyNotForAccount or AccountNotSoleManagementKey
A Deployer passed a key that is not the account's, or a second manager (checks 4 and 10)
Pass keys of the account only. The owner adds other keys afterward (22)
Reverts with EmptyString or EmptyListOfKeys
Empty salt, or no key (check 5)
Provide both
Reverts with InvalidSignerData, InvalidSignerLength or InvalidPurpose
A key is malformed (check 7)
Compute keyHash as keccak256(signerData) and use a purpose from 1 to 6
Reverts with NoManagementKeyInKeys
No key holds MANAGEMENT (check 8)
Add a MANAGEMENT key
Reverts with WalletBoundToAnotherIdentity
The first account is already linked to an identity (check 9)
The participant already has an identity. Use it, or use another account
Reverts with WalletAlreadyRevoked
The first account was revoked from an identity (check 9)
Use another account. A revoked wallet is never linked again
Reverts in the deployment itself
An identity already exists for the same type, salt, account and keys
Use that identity, or another salt
Common misunderstandings
Wrong: "The Issuer creates the investor's identity for its token."
Right: An identity belongs to its owner and serves every asset. No Issuer owns it. A party can create one on a participant's behalf, and the factory guarantees that the participant's account is then its only manager. The party that paid for the deployment holds no key.
Wrong: "Having an ONCHAINID makes an investor eligible."
Right: A new identity holds no claim. Eligibility comes from claims signed by Claim Issuers that an asset trusts, for the topics the asset requires.
Wrong: "The identity only stores keys and claims. Tokens stay in the wallet."
Right: An ONCHAINID is a smart account. It holds tokens at its own address and transacts through its keys. The wallet linked at creation is one address of the identity, not the only place where it can hold a position.
Wrong: "The same person can have one identity per chain."
Right: The identity lives on the reference chain and links wallets on any chain. The same inputs also give the same identity address on every EVM chain where the factory sits at the same address.
Integration notes
Store the salt with the participant. With the type, the first account and the keys, it determines the address.
For an investor without a gas token, a party with the creation role deploys the identity with
createIdentityFor. The investor's account is the sole manager from the first block.An onboarding agent or a Claim Issuer that meets a wallet reads
IdentityFactory.getIdentityIncludingRevoked(account)first. It creates an identity only when the wallet was never bound to one.With
createIdentityFor, pass only keys of the named account. A passkey or a second device is added by the owner afterward.With
createIdentity, the owner can register a passkey at creation. The calling account is still linked as the first wallet.Index
IdentityDeployedandIdentityTypeRecordedto maintain the list of identities and their types.Read the type from
IdentityFactory.identityTypeOf(identity), never from the identity.
Related flows
07 Link a wallet to an ONCHAINID : add other addresses
22 Add and remove keys on an ONCHAINID : add a passkey or a second device
28 Install the recovery module and manage guardians : install guardians early
21 Recover access to an ONCHAINID : what the guardians do when the keys are lost
05 Issue a claim to an investor : what makes the identity eligible
01 Deploy a token suite : creates the identity of a token
Last updated