Skip to content

Add EVM contract data + search support — AgentKeys v2 stage-1 contracts on Heima mainnet #4

@YoshiyukiSakura

Description

@YoshiyukiSakura

"## Background\n\nAgentKeys (litentry/agentKeys) v2 stage 1 has deployed its four anchor contracts to Heima mainnet (chain_id 212013). These are written in Solidity 0.8.20, target EVM london (matching Heima's Frontier EVM level), and are the on-chain source of truth for the AgentKeys protocol's identity + scope + audit state.\n\nThe existing subscan-essentials explorer is Substrate-side only — it indexes pallet extrinsics + events but does NOT decode EVM contract calls, EVM events, or EVM contract storage. Today, the only way to inspect these contracts is via direct RPC (eth_getCode, cast call, cast logs). For operators + auditors, that's friction; for the project's "everything auditable on-chain" guarantee (see arch.md §6 + §15.3 in litentry/agentKeys), we need a first-class explorer view of the AgentKeys contracts.\n\nThis issue tracks adding EVM contract data + search support to subscan-essentials, scoped to a basic-but-extensible MVP. Future stages add ABI decoding for arbitrary contracts (not just AgentKeys'); this stage hard-codes our four.\n\n## Live mainnet addresses to support\n\nAll on Heima mainnet (chain_id 212013, RPC https://rpc.heima-parachain.heima.network):\n\n| Contract | Address | Bytecode size |\n|---|---|---|\n| AgentKeysScope | 0x14C23B5D1cE20c094af643a20e6b0972dAD12aa8 | 3146 bytes |\n| SidecarRegistry | 0x76D574a107727bE87fc1422661A030FEFda70786 | 3301 bytes |\n| K3EpochCounter | 0x8396dEc50ff755d6DE7728DABB00Be2eFBCdf4dF | 687 bytes |\n| CredentialAudit | 0x1801ded1a4FBD8c9224Ab18B9EcbB293B8674c06 | 1421 bytes |\n\nABIs + source live in crates/agentkeys-chain/ in the agentKeys repo. Canonical address registry: docs/spec/deployed-contracts.md.\n\n## MVP scope (what this issue ships)\n\n### 1. EVM contract index ingestion\n\nThe backend indexer learns to ingest contract data from the Frontier RPC namespace:\n\n- Bytecode — per address, via eth_getCode(addr, 'latest'). Indexed at deploy block (one-shot; updates only if selfdestruct triggers, which AgentKeys contracts don't use).\n- Transaction list — every to == contract_addr transaction. Indexed continuously via eth_getBlockByNumber walking. Each entry stores: block, txHash, from, value, calldata, status, gasUsed.\n- Event log — every Log whose address == contract_addr. Index by topic[0] (event signature hash) for cheap filtering. Each entry stores: block, txHash, logIndex, topics[], data.\n- Storage layout snapshot (optional, v1.1) — eth_getStorageAt(addr, slot) for declared slots from the ABI's storage layout output.\n\n### 2. Per-contract view (UI)\n\nFor each of the four AgentKeys contracts, the subscan-essentials-ui-react frontend adds a route:\n\n\n/contract/<addr> → overview: name, bytecode hash, deploy date, balance, tx count\n/contract/<addr>/txs → paginated tx list (decoded function name + args via ABI)\n/contract/<addr>/events → paginated event list (decoded event name + indexed topics + data)\n/contract/<addr>/call → read-only view function caller (cast call equivalent)\n\n\nABI decoding is hard-coded for the four AgentKeys contracts in MVP (ship the ABIs in the indexer's config). v1.1+ adds operator-supplied ABI uploads.\n\n### 3. Search\n\nSearch bar accepts:\n\n- Contract address (0x...40 hex) — direct route to /contract/<addr>\n- Tx hash (0x...64 hex) — existing substrate tx route + new EVM tx route (auto-detect by hash length)\n- Block number — existing route\n- EVM event filter — new: \Sid:keccak256(EventSig)\\ syntax routes to events page filtered by topic[0]. E.g., \SidecarRegistry.DeviceRegistered\\ → \/contract/0x76D5.../events?topic0=0x<sig>\\. Hard-coded keyword → topic mapping for AgentKeys events in MVP.\n- Per-operator filter — \actor_omni:<64hex>\\ filters events across ALL four contracts where any indexed topic matches that actor_omni. Backend joins across the contract event indexes.\n\n### 4. Per-actor cross-contract view (THIS is what makes the explorer useful for AgentKeys)\n\nA new top-level route /agentkeys/actor/<operator_omni_hex> shows ONE operator's activity across ALL four contracts:\n\n- Devices bound to them (from SidecarRegistry.DeviceRegistered filtered on indexed operatorOmni)\n- Their scope grants (from AgentKeysScope.ScopeUpdated)\n- Their audit log entries (from CredentialAudit.AuditAppended)\n- Current K3 epoch (from K3EpochCounter.K3Rotated, latest)\n\nThis is the auditor's view: "show me everything this operator has done on-chain."\n\n## Acceptance criteria / test standard\n\nThe MVP is complete when ALL of these pass:\n\n### A. Backend ingestion (Go service)\n\nA.1. make build + make test pass with the new indexer modules added.\nA.2. For each of the four AgentKeys contracts, running the indexer against Heima mainnet produces:\n- A non-empty contract row in the DB\n- A bytecode_size matching the on-chain bytecode size (3146, 3301, 687, 1421 bytes respectively)\n- All historical transactions to that address recorded with status + gas + decoded function names\n- All historical events recorded with topic0 → event name decoded\n\nA.3. Re-running the indexer is idempotent (no duplicate rows).\nA.4. The indexer keeps up with chain head — when a new tx hits one of the contracts, it appears in the DB within 1 block of inclusion.\n\n### B. Frontend (React)\n\nB.1. \/contract/0x14C23B5D1cE20c094af643a20e6b0972dAD12aa8\\ (AgentKeysScope) loads and shows:\n- Name: \AgentKeysScope\\\n- Bytecode size: 3146 bytes\n- Deploy tx: linkable to the original deploy tx page\n- Function call counter (number of writes since deploy)\n- Tab navigation: Overview / Transactions / Events / Read Functions\n\nB.2. \/contract/0x76D574a107727bE87fc1422661A030FEFda70786/events\\ (SidecarRegistry) lists at least the one DeviceRegistered event from block 9620483, with topic[0] decoded as DeviceRegistered(bytes32,bytes32,bytes32,uint8,uint8,bytes32) and indexed args (deviceKeyHash, operatorOmni, actorOmni) shown.\n\nB.3. \/contract/0x76D574a107727bE87fc1422661A030FEFda70786/call\\ exposes a form for the read-only functions (isActive(bytes32), getDevice(bytes32), operatorMasterWallet(bytes32), etc.) — filling the args + clicking "Call" returns the decoded result without a tx.\n\nB.4. \/agentkeys/actor/0x941cb1c3260518bbf40eac7d02663517fc7cff304d9b03e80d2cc54126c6bef2\\ (the live registered operator from block 9620483) shows:\n- 1 device registered\n- 0 scope grants (we haven't issued any yet)\n- 0 audit entries\n- Current K3 epoch = 1\n\n### C. Search\n\nC.1. Pasting 0x14C23B5D1cE20c094af643a20e6b0972dAD12aa8 into the search bar routes to the contract overview page.\nC.2. Pasting 0x8f1d7cca5710c2859b4f8b942c36df41d3c6b8b02a862d1f506285a6176c988b (the device-register tx) routes to the EVM tx detail page, with the decoded function call SidecarRegistry.registerMasterDevice(...) and all its args shown.\nC.3. Typing the keyword DeviceRegistered → autocomplete suggests the SidecarRegistry events route filtered to topic[0] of that event sig.\n\n### D. Performance (acceptance smoke)\n\nD.1. Indexer cold-start (from genesis) on Heima mainnet completes the four contracts' history in under 30 minutes on a 4-core / 8GB host.\nD.2. Frontend pages (overview, txs, events) load under 500ms p50 on the indexed data.\n\n### E. Backward-compatibility\n\nE.1. All existing subscan-essentials Substrate-side routes (\/extrinsic/<hash>\\, \/account/<ss58>\\, etc.) continue to work unchanged.\nE.2. No schema migration breaks for existing operators upgrading from the previous indexer version. New EVM tables are additive.\n\n## Future revision plan (out of scope for this issue)\n\nThese are tracked as follow-up issues, NOT blocking MVP:\n\n1. Operator-supplied ABI uploads — let any operator point at any contract address + upload its ABI; the explorer decodes it. Today's MVP hard-codes the four AgentKeys contracts.\n2. Bytecode verification — match deployed bytecode against compiled source (forge inspect) and flag verified contracts with a checkmark.\n3. Per-actor activity dashboards — analytics across operators (top-N by audit volume, scope-change rate, etc.).\n4. Cross-contract trace — given a tx hash that calls A which calls B, render the full call tree (would need debug_traceTransaction RPC support on Heima collators).\n5. WebSocket subscriptions — real-time event push so dashboards update without refresh.\n\n## Reference\n\n- AgentKeys v2 architecture: arch.md §22a.6 (explorer integration target)\n- AgentKeys deployed contracts: docs/spec/deployed-contracts.md\n- AgentKeys contract sources: crates/agentkeys-chain/src/\n- ABI reference: produced by forge inspect <Contract> abi in the chain crate\n- Heima RPC: https://rpc.heima-parachain.heima.network (mainnet) | https://rpc.paseo-parachain.heima.network (testnet, currently halted)\n- Heima Statescan (Substrate-side, current state): https://heima.statescan.io\n- UI counterpart repo: https://github.com/litentry/subscan-essentials-ui-react\n\n🤖 Generated with Claude Code\n\n---\n\n## CrossAgent 前端交付说明\n\n- Copied from litentry/subscan-essentials#4: https://github.com/litentry/subscan-essentials/issues/4\n- 本任务已迁移到前端仓库 litentry/subscan-essentials-ui-react 执行。\n- 所有工作分支都从 crossagent 切出;创建 PR 时合并目标也使用 crossagent。\n- 测试环境:https://test-explorer.heima.network/\n- 推送到 crossagent 后会自动触发该测试环境部署;完成后请在这个域名确认效果。"

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions