Accounts on the Axiom keystore are identified by a new bytes32 keystoreAddress format, which users can counterfactually initialize using the process described in Account Initialization. The state of the Axiom keystore is a mapping

state: mapping(keystoreAddress: bytes32 => (data: bytes, vkey: bytes))

where:

  • bytes data holds the signing data for the keystore account
  • bytes vkey is a verification key for a ZK circuit that ingests data and verifies a ZK authentication proof for the update rule for keystoreAddress which will enable a user to update the (data, vkey) pair.

The keystore state is structured as an Indexed Merkle Tree (IMT), and committed on L1 using the IMT root during each finalization on L1. These state roots can be trustlessly synced to L2s using native message passing or storage proofs, enabling users to prove their signing data against the keystore state root on L2. We use this to create a keystore-enabled smart account transaction flow, which we explain in the rest of this section.

Integrating Smart Accounts with the Keystore Validator

Smart accounts on L2 can become keystore-enabled by using the Keystore Validator (KV) contract. This is an ERC-7579 and ERC-6900 compatible smart account validation module which trustlessly caches keystore state roots on L2, facilitates IMT Merkle proofs to verify data against the state root, and validates smart account authentication data against data.

To enable this integration, the KV must be installed as a module within the smart account, which requires compatibility with either the ERC-7579 or ERC-6900 standard. Without compatibility with one of these standards, the smart account cannot support the Keystore Validator.

Deployment details of the KV across all supported L2s are available in Contract Addresses.

The KV expects data to conform to the following structure:

data[0] - domain separator (should be 0x00)
data[1..33] - key data consumer codehash
data[33..] - arbitrary key data

The KV itself will verify a (data, vkey) pair with an IMT proof, but will outsource further authentication against data to an external Key Data Consumer (KDC). There is (generally) a one-to-one correspondence between a vkey and a KDC and data[1..33] allows a vkey to signal how it desires to be authenticated on chain through a codehash which commits to the validation logic of a KDC.

Syncing Keystore IMT State Roots

The Keystore Validator can receive state roots from L1 in two ways:

  • L1 Merkle Trie Proof: For L2s that expose L1 blockhash access to its execution environment, the KV can receive state roots from L1 via a storage trie proof.
  • Bridge Transaction: For other L2s, the KV can receive state roots from L1 via a native bridge transaction.

Axiom is committed to syncing state roots for all supported L2s approximately once per hour.

In cases where this sync is delayed, or if there is a need to expedite the propagation of a finalized state root, a manual sync—whether via a trie proof or a bridge transaction—can also be triggered permissionlessly.

Expiry Checks

When Keystore IMT state roots are persisted to the KV, they are tagged with an l1BlockTimestamp—the timestamp of the L1 block whose blockhash against which the Merkle trie proof was verified or the bridge transaction was sent. This metadata enables the KV to perform expiry checks, leveraging ERC-4337’s validateUserOp interface, which incorporates native timestamp validation through a return value embedding the validAfter and validUntil fields.

During the installation of the KV, the smart account specifies an invalidationTime. The validAfter and validUntil fields are then derived as l1BlockTimestamp and l1BlockTimestamp + invalidationTime, respectively. Given that Axiom guarantees a refresh interval of one hour, it is recommended to set the invalidationTime to at least 1 hour.

Next Steps

In the upcoming sections, we will explore interacting with the keystore, both directly and from within smart accounts on other rollups, as well as run through an example. However, if you are looking for something specific, you can go there directly:

  • To set up a smart account that reads from a keystore account (without transacting on the keystore itself), check out the guide on Account Initialization.
  • To learn how to construct a userOp bundle for a smart account that interacts with the keystore, including details on its structure and signature formatting, refer to the guide on Transacting on L2s.
  • For information on rotating/recovering keys on the keystore, refer to the guide on Key Rotation and Recovery.