ByAUJay
Composable Address Books Across Chains: Indexing the ‘User’ Not the Chain
A composable address book treats people and organizations as first-class objects you can reference across chains, wallets, and apps. This guide shows decision-makers how to design a chain-agnostic contact layer using today’s standards (CAIP‑10, did:pkh, SIWE/ReCaps), name systems (ENSv2/CCIP‑Read), attestations (EAS), and account advances (EIP‑7702, ERC‑7579), with concrete examples and implementation tips.
Summary: Stop indexing chain addresses; start indexing users. With CAIP-10 identifiers, ENSv2 offchain/L2 resolution, EAS-backed proofs, and smart-account upgrades like EIP‑7702, you can ship an address book that’s portable, verifiable, and secure across any chain.
Why a “user-first” address book now
- Users routinely hop between L1 and multiple L2s, sometimes using EOAs on one and smart accounts on another. A chain-bound address book doesn’t survive this reality.
- Standards that make a cross-chain contact layer viable are mature and live in production: CAIP‑10 “account IDs,” did:pkh, SIWE (EIP‑4361) plus ReCaps (EIP‑5573), WalletConnect Namespaces, and ENS offchain/L2 resolution. (chainagnostic.org)
- Ethereum’s Pectra upgrade (May 7, 2025) shipped EIP‑7702, letting EOAs opt-in to smart-account logic without changing the user’s address. That removes one of the biggest UX blockers to chain-agnostic identity and payments. (blog.ethereum.org)
The takeaway: your “contact” should be a user object with stable, chain-agnostic identifiers and verifiable links to any number of onchain accounts.
The building blocks
1) Canonical identifiers for accounts: CAIP‑10 and did:pkh
- CAIP‑10 defines a chain-agnostic account string: e.g., eip155:1:0xabc… or cosmos:cosmoshub-4:cosmos1… Use it to key every contact-method your app stores (EOAs, smart accounts, Solana, Cosmos, BTC, etc.). (chainagnostic.org)
- did:pkh wraps CAIP‑10 into a Decentralized Identifier (DID): did:pkh:eip155:1:0xabc… Use it when you need a DID Document or to interop with VC/SSI stacks. (hackmd.io)
Practical tip: Store both forms per entry—CAIP‑10 for concise machine lookups and did:pkh for DID/VC compatibility.
2) Wallet and session interoperability: WalletConnect Namespaces and CAIP‑25
- WalletConnect v2 Namespaces unify chain/method/event permissions using CAIP chain identifiers. Align your address book permissions with the same namespaces to keep UX consistent between dapp sessions and your contact layer. (specs.walletconnect.com)
- CAIP‑25 standardizes a “create session” JSON‑RPC handshake—useful if your address-book service runs as an agent that wallets/dapps connect to. (chainagnostic.org)
3) Offchain/L2 resolution for names: ENS CCIP‑Read and ENSv2
- ENS supports CCIP‑Read (EIP‑3668) so resolvers can fetch records from L2s or offchain APIs. Your address book should resolve ENS names this way transparently. (docs.ens.domains)
- ENSv2 re-architects ENS for a multi-chain world with “One name. Any chain.” Namechain and per‑name registries make cross-chain profiles cheaper and more flexible. Plan to make ENS a primary handle in your contact object. (ens.domains)
4) Attested links: Ethereum Attestation Service (EAS)
- Use EAS schemas to bind “user ↔ wallet” or “brand ↔ payout address” relationships and to verify third-party claims (e.g., “Supplier A’s verified USDC settlement address on Base”). EAS runs on mainnet and major L2s, with canonical contract addresses publicly published. (attest.org)
- You can even use community resolvers (e.g., Farcaster wallet attestations) to turn a social identifier (FID) into a verified address list. (github.com)
5) Authentication and capability grants: SIWE + ReCaps
- Sign-In with Ethereum (EIP‑4361) remains the simplest way to authenticate a user to your contact-service API. ReCaps (EIP‑5573) let the user grant your service scoped capabilities (e.g., “read contacts,” “write label,” “add verification”). (eips.ethereum.org)
6) Account models are changing: EIP‑7702 and ERC‑7579
- EIP‑7702 (Pectra) lets EOAs delegate to contract logic without abandoning their familiar address. Your address book should treat the address as stable while recognizing capability changes over time. (blog.ethereum.org)
- ERC‑7579 is the leading modular smart‑account interface; modules (validators, executors, hooks) are becoming portable across wallets. Tag smart accounts with their module capabilities when relevant to a workflow (e.g., “supports passkeys,” “session keys installed”). (eips.ethereum.org)
- Passkeys: P‑256 (secp256r1) verification is landing broadly (RIP‑7212/EIP‑7951 and several L2/mainnets). Expect more passkey‑native smart accounts; you can store that capability on the contact. (eips.ethereum.org)
Reference data model for a composable, chain-agnostic contact
Keep contact data small, structured, and privacy‑preserving. A minimal JSON record (persisted server‑side or client‑side, encrypted at rest):
{ "id": "did:pkh:eip155:1:0xA1b2...fE", "displayName": "Alice Nguyen", "handles": { "ens": ["alice.eth", "alice.base.eth"], "farcaster": { "fid": 12345 }, "dns": ["alice.example.com"] }, "accounts": [ { "caip10": "eip155:1:0xA1b2...fE", "kind": "EOA", "proofs": ["eas:0xUID1"] }, { "caip10": "eip155:8453:0x9C8...12", "kind": "Safe", "capabilities": ["erc7579:validation:v1"], "proofs": ["eas:0xUID2"] }, { "caip10": "solana:mainnet:3h9...XJ", "kind": "SVM", "proofs": [] } ], "policies": { "preferredSettlement": ["eip155:8453", "eip155:42161"], "risk": { "status": "clean", "sources": ["chainabuse:0 reports", "scamsniffer:0"] } }, "metadata": { "createdAt": "2025-11-20T12:00:00Z", "ownerProof": "siwe:msg-hash", "labels": ["supplier", "priority"] } }
- id is the canonical user key (did:pkh).
- accounts is a set of CAIP‑10 entries with proofs linking them to the user (EAS attestations, SIWE signatures, Farcaster links).
- policies encodes routing and risk controls.
- handles allows human-friendly discovery via ENS/CCIP‑Read, DNS (ENSIP‑17), or social IDs. (docs.ens.domains)
Resolution pipeline: how your app turns “Alice” into the right address on any chain
- Normalize input
- If user typed an ENS name, resolve with CCIP‑Read; cache the target CAIP‑10(s). If a cb.id or base.eth style subname is used, follow Coinbase/ENS guidance—many are offchain and must be read via CCIP‑Read gateways today. (docs.ens.domains)
- If input is a chain address, convert to CAIP‑10 and compute checksum (EIP‑55/1191) where applicable before storing. (eips.ethereum.org)
- Fetch the contact record
- Key lookup by canonical did:pkh id; if unknown, attempt to “compose” a contact from public data: ENS records (addresses, text), Farcaster custody/signers, and last-known EAS attestations. (docs.farcaster.xyz)
- Verify link proofs
- Validate EAS attestations under your accepted schema(s) against the EAS contracts for the target chain. Record the UID and block timestamp. (github.com)
- For Farcaster, map FID to custody address and verify signer authorizations on OP Mainnet (Farcaster contracts). (docs.farcaster.xyz)
- Choose the best account for the target chain
- If the contact has a chain‑native account for the destination, use it.
- If not, prefer accounts with smart‑account capabilities (7702 delegated EOAs or ERC‑7579 accounts) that can operate on multiple chains via modules/intents. (blog.ethereum.org)
- Risk screen and finalize
- Screen destination against Chainabuse, ScamSniffer, and your internal allowlist. Block “address poisoning” patterns by never auto‑suggesting from recent mempool neighbors; only from verified contacts and attestations. (docs.chainabuse.com)
Two concrete patterns you can ship this quarter
Pattern A — Supplier payouts that “just work” across chains
Scenario: Your treasury team pays 300 suppliers in USDC monthly, with each supplier preferring a different chain.
- Discovery: Supplier registers an ENS name and posts an EAS attestation “preferred settlement” for chains X,Y with verified addresses. Your app resolves “supplier.eth,” fetches EAS UIDs to bind the set of CAIP‑10 accounts. (docs.ens.domains)
- Routing: Your payout engine checks the supplier’s preferredSettlement list. If L2 address missing, but the supplier’s EOA supports 7702 delegation (post‑Pectra), route through a smart‑EOA policy wallet on the same address, keeping UX consistent. (blog.ethereum.org)
- Safety: Before release, the engine screens all destination accounts with Chainabuse and ScamSniffer APIs; failed checks require a human override. (docs.chainabuse.com)
Benefits: Ops can switch settlement chains without new onboarding. Finance sees a single “Supplier A” identity regardless of chain.
Pattern B — Consumer app: map social identity to wallets safely
Scenario: Your app uses Farcaster IDs (FIDs) as user handles.
- Identity: On signup, user proves control of FID and posts an EAS attestation linking their EOA/Smart Account to their FID (or you rely on community resolvers that verify FID↔wallet). (github.com)
- Resolution: When sending value, your app resolves “@alice” → FID → verified wallet list, picks the chain-native account, falls back to EOA+7702 if needed. Farcaster’s onchain registries (OP Mainnet) let you verify custody and signer status. (docs.farcaster.xyz)
Benefits: Real names stay private; handles stay constant; wallets remain swappable by the user via attestations instead of support tickets.
How to back your address book with real onchain data
- Index once, reuse everywhere: Deploy subgraphs or Substreams to materialize “Contact → Accounts → Last activity → Risk tags.” The Graph now spans 40+ chains, with real‑time Substreams for high‑TPS networks (Solana, TRON) and cross‑chain token flows. (coindesk.com)
- Cross-chain verification: When you must prove something about a remote chain (e.g., “this address owns X on chain B”), prefer verifiable proofs or proven messaging layers:
- Proofs: Light client/state‑proof solutions like Lagrange State Committees and Herodotus storage proofs reduce trust assumptions. (lagrange.dev)
- Messaging: Chainlink CCIP (programmable token/data transfers), LayerZero v2 (OFT/ONFT), or Axelar GMP for call‑across-chain patterns—useful to sync labels or attested links between chain-local registries. (docs.chain.link)
Security hardening against address-book poisoning
- Enforce checksums and chain IDs (EIP‑55/EIP‑1191) on every EVM address; never store lower‑case raw addresses. (eips.ethereum.org)
- Don’t suggest “recently interacted” addresses; poisoning attacks spiked and continue to evolve. Show only allowlisted contacts, ENS names you’ve resolved yourself, or EAS‑verified links. (chainalysis.com)
- Integrate multiple threat feeds: Chainabuse (multi‑chain reports), ScamSniffer (delayed open feeds; real‑time API available). Cache verdicts and show explainable reasons to users. (docs.chainabuse.com)
- Record provenance: For each address entry, store resolver → data source → block/time. On dispute, you can prove what you resolved and when.
Implementation snippets
Normalize input to CAIP‑10 (TypeScript with viem)
import { getAddress } from 'viem'; // EIP-55 checksum export function toCaip10(namespace: string, chainRef: string, addr: string): string { if (namespace === 'eip155') addr = getAddress(addr); // checksum return `${namespace}:${chainRef}:${addr}`; }
Resolve ENS via CCIP‑Read and prefer L2 records when present
import { createPublicClient, http } from 'viem'; import { mainnet } from 'viem/chains'; export async function resolveEnsToCaip10(name: string, chain='1') { const client = createPublicClient({ chain: mainnet, transport: http() }); const addr = await client.getEnsAddress({ name }); // viem unwraps CCIP-Read if (!addr) throw new Error('ENS not resolvable'); return toCaip10('eip155', chain, addr); }
Verify an EAS attestation linking a contact to an account
import { ethers } from 'ethers'; import EASAbi from '@ethereum-attestation-service/eas-contracts/deployments/EAS.json'; export async function isLinkedByEAS(easAddr: string, uid: string, provider: ethers.Provider) { const eas = new ethers.Contract(easAddr, EASAbi.abi, provider); const att = await eas.getAttestation(uid); return att?.revocationTime === 0 && att?.time > 0; } // Mainnet EAS: 0xA1207F3BBa224E2c9c3c6D5aF63D0eb1582Ce587; SchemaRegistry: 0xA7b392...BDF
Reference: EAS mainnet contract addresses. (github.com)
Emerging best practices we apply at 7Block Labs
- Use CAIP‑10 and did:pkh as your canonical keys; store ENS and social handles as discoverability hints.
- Back every link with a verifiable proof:
- ENS resolution via CCIP‑Read (and soon ENSv2/Namechain).
- EAS schemas for “Verified payout address,” “Verified signer,” etc. (docs.ens.domains)
- Embrace 7702 and modular accounts:
- Prefer a user’s stable EOA address even as they add smart behaviors (batched tx, sponsored gas) via EIP‑7702 delegations.
- Detect ERC‑7579 modules to understand capabilities (e.g., passkeys, session keys) without vendor lock‑in. (blog.ethereum.org)
- Make passkeys a first‑class capability: If your customers want Face ID/YubiKey sign‑ins, note where P‑256 precompile support exists and expose those paths first. (eips.ethereum.org)
- Index once, serve many: Power your address book analytics with The Graph (Subgraphs/Substreams) and cache per‑contact rollups like “last active chain,” “avg fee chain,” “risk flags.” (coindesk.com)
- Defense in depth:
- Multi‑feed risk checks (Chainabuse, ScamSniffer).
- Strict checksum validation.
- No “recent tx” auto-suggestions. (docs.chainabuse.com)
A note on cross-chain “synonyms” for the same user
- EOAs are naturally “same address across EVM chains.” Smart accounts are often per‑chain deployments with different addresses. Your address book should:
- Group per‑chain accounts under one user, keyed by did:pkh for the root EOA and by attested links for additional smart accounts. (chainagnostic.org)
- Prefer “on this chain” accounts when available; otherwise use routing—either a 7702‑enabled EOA or cross‑chain actions (CCIP/L0/Axelar) to reach the destination safely. (docs.chain.link)
KPIs and SLOs for a production address-book service
- p95 resolution time: <150 ms (warm cache) for ENS/CCIP‑Read and EAS checks.
- False‑positive rate on risk blocks: <0.5% (measure by human review outcomes).
- Link freshness: 99% of “verified address” links refreshed within 7 days of an update event (listen to EAS event streams/subgraphs).
- Coverage: >95% of sends resolved via verified identities (ENS/EAS/Farcaster) vs raw addresses.
What to watch in 2026
- ENSv2 migration toolchain and Namechain general availability (“One name. Any chain.” becomes the default). (ens.domains)
- Wider mainnet adoption of P‑256 precompile (EIP‑7951) → passkey-native wallets as table stakes. (eips.ethereum.org)
- A converged module ecosystem around ERC‑7579 (validators/executors/hooks) and deeper 7702 integrations in mainstream wallets. (eips.ethereum.org)
How 7Block Labs can help
We’ve implemented user‑centric, chain‑agnostic address books for consumer apps, DAOs, and payment ops. Our playbook combines CAIP‑10/did:pkh identifiers, ENSv2 resolution, EAS attestations, Farcaster mappings, and 7702/7579 account detection—backed by Subgraphs/Substreams and risk feeds. If you want a composable address book that your product teams and auditors both love, we’re ready to ship.
Sources and further reading
- CAIP‑10 account IDs; WalletConnect Namespaces; CAIP‑25 session handshake. (chainagnostic.org)
- ENS CCIP‑Read; ENSv2 “One name. Any chain.”; Gasless DNS Resolution (ENSIP‑17). (docs.ens.domains)
- EAS overview and mainnet contracts. (attest.org)
- Farcaster contracts and schema docs. (docs.farcaster.xyz)
- SIWE (EIP‑4361) and ReCaps (EIP‑5573). (eips.ethereum.org)
- EIP‑7702 (Pectra mainnet May 7, 2025); ERC‑7579 modular accounts. (blog.ethereum.org)
- P‑256 precompile (EIP‑7951/RIP‑7212) and chain support. (eips.ethereum.org)
- The Graph multi‑chain indexing and Substreams. (coindesk.com)
- Chainabuse API; ScamSniffer lists; address checksums (EIP‑55/1191). (docs.chainabuse.com)
Like what you're reading? Let's build together.
Get a free 30‑minute consultation with our engineering team.

