The Two Forms of CAIP-10
CAIP-10 identifies an account. V2 uses two forms:Chain-agnostic: <namespace>:0:<address>
Used when an account exists at the same address on every chain in the
namespace — i.e., an EVM externally-owned account (EOA). The chain reference
is the literal string 0 per
CAIP-10 §Abstract Account Addresses.
ownerAccountId), pooled KMS
agents (agentAccountId).
Chain-bound: <namespace>:<chainId>:<address>
Used when the account only exists at that (chain, address) pair — every
smart-contract account is chain-bound, because a deployment exists on one
chain only.
Vault.accountId), withdrawal
recipients (recipientAccountId).
Rules Per Field
| Field | Form | Why |
|---|---|---|
ownerAccountId | Chain-agnostic eip155:0:<addr> | The owner EOA works on every EIP-155 chain |
agentAccountId | Chain-agnostic eip155:0:<addr> | Same reasoning |
Vault.accountId | Chain-bound eip155:<chainId>:<addr> | Kernel smart account exists only at that chain/address |
recipientAccountId | Chain-bound — rejected if chain-agnostic | Funds move on a specific chain; eip155:0:<addr> is ambiguous |
Validation
All CAIP-10 strings are validated and normalized at the route boundary. Mixed-case input is accepted; the address is lowercased server-side before downstream processing. Soeip155:1:0xABCDEF... and eip155:1:0xabcdef...
are equivalent for lookup but you will always receive the lowercase form in
responses.
CAIP-19 for Assets
CAIP-19 identifies an asset. V2 uses it for strategy allocationassetIds and withdrawal assetIds.
ERC-20 on EVM
SPL on Solana
Common IDs
| Asset | CAIP-19 |
|---|---|
| USDC (Ethereum) | eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 |
| USDT (Ethereum) | eip155:1/erc20:0xdac17f958d2ee523a2206206994597c13d831ec7 |
| USDC (Base) | eip155:8453/erc20:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913 |
Building CAIP Strings
Avoid hand-concatenating CAIP strings on the client. Small mistakes (missing0x, wrong checksum case, chain reference off by one) produce
400 API_400 at the route layer with a cryptic message.
If you are writing TypeScript server-side code inside this repo, use
@repo/caip’s Builder:
Documented Exception: EIP-712 verifyingContract
Inside a signed EIP-712 typed-data envelope only, domain.verifyingContract
is a bare EVM address, not a CAIP-10. This is dictated by EIP-712
itself — the field is what the user’s wallet renders during signing, and
wallets do not understand CAIP.
This exception is scoped to the signed envelope. Do not generalize it to
other fields or other endpoints.
Common Pitfalls
- Sending
0xabc...to a*AccountIdfield. Bare hex is always rejected. Wrap it in the appropriate CAIP-10 form first. - Sending
eip155:0:<addr>as a withdrawal recipient. The chain-bound form is required; chain-agnostic is explicitly rejected. - Sending a chain-bound owner to enrollment. Enrollment expects
chain-agnostic
eip155:0:<addr>for owners. Chain-bound owners will fail the EVM wallet refinement check. - Mixing cases for lookups.
eip155:1:0xABC...andeip155:1:0xabc...normalize to the same value, but comparing raw strings on your side may miss a match. Compare either both normalized, or always compare via CAIP-10 parsing.