This documentation is focused on the contracts but also clarifies assumptions made by off-chain services.

L2 Contracts

The Identity, Storage and Key Registry contracts are deployed on OP Mainnet (chainid: 10).

Id Registry

IdRegistry lets any Ethereum address claim a unique OpenRealm ID or qid. qids are unique integers that map 1:1 to an Ethereum address known as the custody address. An address can own one qid at a time and transfer it to another address. The custody address may nominate a recovery address that can transfer the qid to a new custody address. The custody address can always change or remove the recovery address.

Invariants

  1. One qid per address: An address can only own one qid at a given time.
  2. One address per qid: Multiple addresses cannot own the same qid at the same time.
  3. Sequential IDs: If qid n was registered, then all qids from 1…n must also be registered.
  4. One recovery address: An qid can have only one recovery address.

Assumptions

  1. owner is not malicious.

Administration

The owner can pause and unpause the contract, which pauses registration, transfer, and recovery. The owner has one time use permissions to migrate data during the migration phase.

Migration

The IdRegistry is deployed in a trusted state where keys may not be registered by anyone except the owner. The owner will populate the KeyRegistry with existing state by using bulk operations. Once complete, the owner will call migrate() to set a migration timestamp and emit an event. Hubs watch for the Migrated event and 24 hours after it is emitted, they cut over to this contract as the source of truth.

Upgradeability

The IdRegistry contract may need to be upgraded in case a bug is discovered or the logic needs to be changed. In such cases:

  1. A new IdRegistry contract is deployed.
  2. The current IdRegistry contract is paused.
  3. The new IdRegistry is seeded with all the registered qids in the old contract.
  4. The KeyRegistry is updated to point to the new IdRegistry.
  5. A new Bundler contract is deployed, pointing to the correct contracts.

Id Gateway

The IdManager is responsible for qid registration. While IdRegistry defines the rules of qid ownership, transfers, and recovery, the manager is responsible for the the actual registration logic. To prevent spamming qid registrations, the IdManager requires callers to rent 1 storage unit at qid registration time.

Invariants

  1. In untrusted mode, callers must rent 1 storage unit at qid registration time.
  2. The contract can only transition to untrusted mode once, and never back to trusted mode.

Assumptions

  1. owner is not malicious.

Administration

The owner can pause and unpause the contract, which pauses registration, transfer, and recovery.

State Machine

An qid can exist in two states:

  1. registrable - the qid has never been issued and can be registered by anyone
  2. registered - the qid has been issued to an address

The qid state transitions when users take specific actions:

  1. register - register a new qid from any address
  2. transfer - move an qid to a new custody address
  3. recover - recover (move) an qid to a new custody address

Upgradeability

The IdManager contract may need to be upgraded in case a bug is discovered or the logic needs to be changed. Since the IdManager depends on storage, we expect to update this contract in the future if the storage system changes.

In such cases:

  1. A new IdManager contract is deployed in the seedable state.
  2. The IdRegistry is updated to point to the new IdManager.
  3. The old IdManager is paused.
  4. A new Bundler contract is deployed, pointing to the correct contracts.
  5. The new IdManager is moved to the registrable state where anyone can register an qid.

Storage Registry

The StorageRegistry contract lets anyone rent units of storage space on OpenRealm Hubs for a given qid. Payment must be made in Ethereum to acquire storage for a year. Acquiring storage emits an event that is read off-chain by the OpenRealm Hubs, which allocate space to the user. The contract will deprecate itself one year after deployment, and we expect to launch a new contract with updated logic.

Pricing

The rental price of a storage unit is fixed in USD but must be paid in ETH. A Chainlink price oracle is used to determine the exchange rate. Prices are updated periodically, though checks are in place to revert if prices are stale, out of bounds, or if the sequencer was recently restarted.

A price refresh occurs when a transaction is made after the cache period has passed, which fetches the latest rate from the oracle. All transactions in that block can still pay the old price, and the refreshed price is applied to transactions in future blocks. A manual override is also present which can be used to fix the price and override the oracle.

Invariants

  1. Supply: rentedUnits never exceed maxUnits.
  2. Pricing: Estimated price equals actual price within a block, i.e. price(x) == _price(x).
  3. Oracle override: price is calculated with fixedEthUsdPrice instead of ethUsdPrice if fixedEthUsdPrice > 0.
  4. Caching: ethUsdPrice is updated from Chainlink no more often than priceFeedCacheDuration, if fixedEthUsdPrice is not set.
  5. Deprecation: storage cannot be purchased after deprecationTimestamp.

Assumptions

  1. Rented units are never released since we expect to renew the contract after a year, and this avoids expensive calculations.
  2. Chainlink oracle always returns a valid price for ETH-USD. (or it must be manually overridden).
  3. role admin, admin, treasurer and operator are not malicious

Migration

The StorageRegistry contract does not contain any special states for migration. Once deployed, the operator can use the credit functions to award storage units to qids if necessary. We intend to use this function to credit existing users with storage.

Administration

StorageRent defines multiple roles rather than a single owner address.

An operator role can credit storage to qids without the payment of rent. This is used for the initial migration to assign storage to existing users, so that their messages aren’t auto-expired from Hubs.

A treasurer role can move funds from the contract to a pre-defined vault address, but cannot change this destination. Only the owner may change the vault address to a new destination. The treasurer may also refresh the oracle price.

An owner role can modify many parameters including the total supply of storage units, the price of rent, the duration for which exchange prices are valid and the deprecation timestamp. The owner may also pause and unpause the contract, disabling/enabling rentals and credits.

Upgradeability

The StorageRegistry contract may need to be upgraded in case a bug is discovered or the logic needs to be changed. In such cases:

  1. A new storage contract is deployed and is paused so that storage cannot be rented.
  2. Hubs are upgraded so that they respect storage events from both contracts.
  3. The older storage contract is deprecated, so that no storage can be rented.
  4. A new Bundler contract is deployed, pointing to the correct contracts.
  5. The new storage contract is unpaused.

Key Registry

The Key Registry contract lets addresses with an qid register or remove public keys. Keys added onchain are tracked by Hubs and can be used to sign OpenRealm messages. The same key can be added by different qids and can exist in different states. Keys contain a key type that indicates how they should be interpreted and used. During registration, metadata can also be emitted to provide additional context about the key. Keys contain a metadata type indicating how this metadata should be validated and interpreted. The Key Registry validates metadata at registration time and rejects keys with invalid metadata.

Key Types

The only key type today is TYPE_1 that indicates that a key is an EdDSA key and should be allowed to sign messages on behalf of this qid on OpenRealm Hubs.

Metadata Types

Key types may have multiple associated metadata types, indicating how their associated metadata should be validated and interpreted. The only metadata type today is key TYPE_1, metadata TYPE_1, for “signed key request” metadata. See section 1.4.1 for more on signed key requests.

Invariants

  1. Addition: A key can only move to the added state if it was previously in the null state.
  2. Valid metadata: A key can only move to the added state if its metadata passes validation.
  3. Removal: A key can only move to the removed state if it was previously in the added state.
  4. Reset: A key can only move to the null state if it was previously in the added state, the contract hasn’t been migrated, and the action was performed by the owner.
  5. Events: Event invariants are specified in comments above each event.
  6. Limits: A new key may not be added if its addition would exceed the keys per qid limit.

Assumptions

  1. The IdRegistry contract is functional.
  2. owner is not malicious.

Migration

The KeyRegistry is deployed in the trusted state where keys may not be registered by anyone except the owner. The owner will populate the KeyRegistry with existing state by using bulk operations. Once complete, the owner will call migrate() to set a migration timestamp and emit an event. Hubs watch for the Migrated event and 24 hours after it is emitted, they cut over to this contract as the source of truth.

State Machine

A key can exist in four states for each possible qid:

  • unmigrated_null - the key has never been registered for the given qid and migration has not completed.
  • migrated_null - the key has never been registered for the given qid and migration has completed.
  • added - the key has been registered for a given qid.
  • removed - the key has been registered and then removed for a given qid.

The key state transitions when qids take specific actions on keys they own:

  1. add - move a key from migrated_null to added for an qid.
  2. remove - move a key from added to removed for an qid.

The key state can also be transitioned by these owner actions that are only possible before the migration:

  1. migrateKeys - move all keys from unmigrated_null to migrated_null.
  2. bulkAdd - move keys from unmigrated_null to added for given qids.
  3. bulkReset - move keys from added to unmigrated_null for given qids.

Upgradeability

The KeyRegistry contract may need to be upgraded in case a bug is discovered or the logic needs to be changed. In such cases:

  1. A new KeyRegistry contract is deployed in a state where only an owner can update keys.
  2. The old KeyRegistry contract has its IdRegistry address set to address(0), which prevents changes.
  3. The state of all existing keys is copied from the old contract to the new one by an owner.
  4. A new Bundler contract is deployed, pointing to the correct contracts.
  5. The contract is set to untrusted state where anyone can register keys.

Key Gateway

The Key Gateway is the user-facing contract responsible for adding new keys to the Key Registry. While IdRegistry defines the rules of key addition and deletion, the Key Gateway is responsible for the the actual addition logic.

Invariants

  1. Fee: A key may only be added

Assumptions

  1. The KeyRegistry contract is functional.
  2. The StorageRegistry contract is functional.
  3. owner is not malicious.

Administration

The Key Gateway owner may can pause and unpause the contract, disabling/enabling adding keys to the Key Registry.

Upgradeability

The KeyManager contract may need to be upgraded in case a bug is discovered or the logic needs to be changed.

In such cases:

  1. A new KeyManager contract is deployed.
  2. The KeyRegistry is updated to point to the new KeyManager.
  3. The old KeyManager is paused.
  4. A new Bundler contract is deployed, pointing to the correct contracts.

Validators

Validators are single purpose contracts that implement a simple interface to validate key metadata. At registration time, the Key Registry looks up the associated validator by key type and metadata type, and calls it to validate the format of provided metadata. This makes the key registry extensible to future key types and metadata formats.

Signed Key Request Validator

The only validator today is the Signed Key Request Validator, which validates that EdDSA key metadata is a “signed key request.” A signed key request represents a third party request to add a public key associated with an qid. Requesting parties must own an qid in order to identify their key requests, and sign a message over their qid and the public key in order to authenticate their request. This allows third party applications requesting signer keys to identify themselves to users, and users to validate the authenticity of signer requests before approving them onchain.

Administration

An owner can update the address of the Id Registry contract.

Bundler

The Bundler contract lets a caller register an qid, rent storage units and register a key in a single transaction to save gas. It is a simple wrapper around contract methods and contains little logic beyond tracking contract addresses, collecting parameters and invoking the appropriate functions.

Recovery Proxy

The Recovery Proxy is an immutable proxy contract that allows the recovery execution logic to change without changing the recovery address associated with an qid. A client or recovery service operator can deploy a recovery proxy and use it as the recovery address for qids. For example, the Warpcast client uses a recovery proxy owned by a 2/3 multisig as the default recovery address for new accounts.

Administration

A recovery proxy can change its owner, and may be owned by an EOA, multisig, or smart contract. The owner of the recovery proxy can change the configured IdRegistry address.