Skip to content

feat(evm): add ERC-8004 agent identity approvals#415

Open
iJaack wants to merge 7 commits into
ava-labs:mainfrom
iJaack:feat/erc8004-agent-identity
Open

feat(evm): add ERC-8004 agent identity approvals#415
iJaack wants to merge 7 commits into
ava-labs:mainfrom
iJaack:feat/erc8004-agent-identity

Conversation

@iJaack

@iJaack iJaack commented Apr 10, 2026

Copy link
Copy Markdown

Summary

  • add shared ERC-8004 agent identity types and avalanche_declareAgentIdentity to the shared RPC surface
  • add EVM-side agent identity resolution, context parsing, and declaration handler wiring
  • thread resolved agent identity through EVM sign and transaction approval payloads
  • add focused tests for the new handler and approval payload propagation

Testing

  • pnpm --filter @avalabs/vm-module-types build
  • pnpm --filter @avalabs/evm-module build
  • pnpm --filter @avalabs/evm-module exec jest --runInBand src/handlers/avalanche-declare-agent-identity/avalanche-declare-agent-identity.test.ts src/handlers/eth-sign/eth-sign.test.ts src/handlers/eth-send-transaction/eth-send-transaction.test.ts src/handlers/eth-send-transaction-batch/eth-send-transaction-batch.test.ts

Follow-up to ava-labs/core-extension#809.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d9e7147f95

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +117 to +121
const provider = new JsonRpcProvider(rpcUrl);
const identityContract = new Contract(parsedRegistry.address, IDENTITY_ABI, provider);

const reputationRegistry = DEFAULT_REPUTATION_REGISTRIES[parsedRegistry.chainId];
const reputationContract = reputationRegistry ? new Contract(reputationRegistry, REPUTATION_ABI, provider) : null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Validate registry chain before resolving identity

The resolver parses the agentRegistry CAIP chain ID but then always reads contracts through rpcUrl, so if the request is on a different network than the declared registry chain, the owner/metadata/reputation are fetched from the wrong chain and displayed as if they came from the declared registry. In practice, a dapp can provide agentRegistry for eip155:43114 while connected to another EVM network and get misleading identity/trust data from a contract at the same address on that other network.

Useful? React with 👍 / 👎.

Comment on lines +93 to +96
const cached = cache.get(`${rpcUrl}:${declaration.agentRegistry}:${declaration.agentId}`);
if (cached && cached.expiresAt > Date.now()) {
return cached.value;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Evict expired entries from agent identity cache

The cache checks TTL on reads but never removes expired keys, so entries for one-off (rpcUrl, agentRegistry, agentId) combinations remain in memory indefinitely unless the same key is requested again. In a long-lived extension process that sees many unique agents, this creates unbounded cache growth and avoidable memory pressure.

Useful? React with 👍 / 👎.

@iJaack

iJaack commented May 2, 2026

Copy link
Copy Markdown
Author

@meeh0w just making sure this PR's review is properly prioritized :)

@meeh0w

meeh0w commented May 4, 2026

Copy link
Copy Markdown
Contributor

@iJaack I have some questions :)

  1. What's the purpose of avalanche_declareAgentIdentity if we do the same thing on eth_sendTransaction and message signing calls? If declaring the agent identity means all subsequent calls coming from a given dApp should reuse that identity - this part is missing. Or maybe it's some clever way to pre-fetch this information so the subseqeuent calls already use cached data?
  2. Core Extension will strip your RpcRequest['context'] object by default and only pass-through whatever is explicitly allowed. Your previous PR "whitelisted" that in the DappRequestHandlerMiddleware, but that PR is closed so this new field will never get into the EVM Module.
  3. Rather than adding a separate agentIdentity property on the DisplayData object, could it get added into the existing DisplayData['details'] as another section? Done this way, the UI would simply start displaying this information (no UI work needed on the wallets' side). This, however, needs a convo with PMs / UX team to decide how we'd like to display that info (if we want to get fancy here, I'd stick with your approach).

@iJaack

iJaack commented May 14, 2026

Copy link
Copy Markdown
Author

@meeh0w thank you for the feedback, I addressed all 3 points.

  1. avalanche_declareAgentIdentity
    I agree the semantics were muddy in the first version. I tightened this so it’s just an explicit declaration / prefetch path, not implicit persisted per-dApp state. The approval flow still derives from per-request context.

  2. request.context
    Also agreed here. I patched Core Extension to explicitly allowlist and forward context.agentIdentity into the VM module. I also added coverage to make sure malformed values are dropped, payload-level request.params.request.context.agentIdentity is ignored, and unrelated context keys don’t get forwarded.

  3. DisplayData
    Also fixed. I moved agent identity into displayData.details as a normal section instead of a separate top-level field, so it renders through the existing approval UI path without wallet-side UI work.

Net result:

  • Core Extension now forwards the allowlisted identity declaration end-to-end
  • EVM approvals render it via the normal details section
  • the declaration method no longer implies hidden session-level reuse

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 61fee298ec

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +120 to +121
const reputationRegistry = DEFAULT_REPUTATION_REGISTRIES[parsedRegistry.chainId];
const reputationContract = reputationRegistry ? new Contract(reputationRegistry, REPUTATION_ABI, provider) : null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Verify reputation registry matches identity registry

When the declared agentRegistry is on Avalanche but is not the identity registry associated with this hard-coded reputation contract, this still reads a score for the same numeric agentId from the default reputation registry and shows it as this agent’s trust level. Since reputation registries are scoped to an identity registry, a dapp can declare another registry at eip155:43114:<otherRegistry> and get an unrelated agent’s reputation attached to the owner/metadata fetched from <otherRegistry>; only use the default reputation contract after verifying its identity registry matches parsedRegistry.address (or key the mapping by identity registry address).

Useful? React with 👍 / 👎.

@meeh0w

meeh0w commented May 25, 2026

Copy link
Copy Markdown
Contributor

Hi @iJaack, thank you for your patience -- I was OOO for the past 2 weeks.

Taking a look at this right now -- can you provide some example agent IDs and registry I could use for testing locally?

@iJaack

iJaack commented Jun 3, 2026

Copy link
Copy Markdown
Author

@meeh0w yep, here are concrete values you can use locally.

Main example I’ve been testing against on Avalanche mainnet:

  • agentId: 1599
  • agentRegistry: eip155:43114:0x8004A169FB4a3325136EB29fA0ceB6D2e539a432

That same pair is what I used in the focused tests in this PR.

If useful, the equivalent raw identity-registry address is:

  • 0x8004A169FB4a3325136EB29fA0ceB6D2e539a432

And for reference, the official Avalanche mainnet reputation registry is:

  • 0x8004BAa17C55a88189AE136b182e5fdA19dE9b63

Example payloads:

avalanche_declareAgentIdentity

{
  "agentId": "1599",
  "agentRegistry": "eip155:43114:0x8004A169FB4a3325136EB29fA0ceB6D2e539a432"
}

request context on signing / tx calls

{
  "context": {
    "agentIdentity": {
      "agentId": "1599",
      "agentRegistry": "eip155:43114:0x8004A169FB4a3325136EB29fA0ceB6D2e539a432"
    }
  }
}

If you want, I can also add a second Fuji test vector in the tests / PR description so there’s an explicit testnet example too.

meeh0w
meeh0w previously approved these changes Jun 18, 2026

@meeh0w meeh0w left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Managed to finally test it, working fine! I say let's merge it in. There are some visual tweaks that can be done (e.g. data uri not rendering), but I don't want to hold you any longer.

The only thing you'll need to do is rebase and run pnpm changeset to generate a changelog for this.

Happy to re-approve afterwards 👍

}

if (agentIdentity.metadataUri) {
items.push(linkItem('Metadata', { url: agentIdentity.metadataUri, name: agentIdentity.metadataUri }));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a data uri, so linkItem doesn't really work here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — I made metadata rendering scheme-aware. data: URIs now render as non-clickable text (Embedded data URI) instead of going through linkItem, while navigable schemes like http(s) and ipfs still render as links. I also added a focused unit test for both paths.

@iJaack

iJaack commented Jun 18, 2026

Copy link
Copy Markdown
Author

@meeh0w hey, thanks! Also, do you want me to solve the conflicts so that the PR is safe to merge?

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ccccb42497

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +17 to +21
'function getScore(uint256 agentId) view returns (int256)',
'function getReputation(uint256 agentId) view returns (int256)',
'function reputationScores(uint256 agentId) view returns (int256)',
'function scores(uint256 agentId) view returns (int256)',
'function reputations(uint256 agentId) view returns (int256)',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use the ERC-8004 reputation read API

The configured 0x8004BAa... contract is the standard ERC-8004 Reputation Registry, whose read interface is getSummary(...)/readFeedback(...) rather than these score-style selectors (see https://eips.ethereum.org/EIPS/eip-8004#reputation-registry). Against the Avalanche registry, every call in this list will revert, so reputationScore stays null and approvals show unknown trust even for agents with feedback; wire this to the standard summary/read functions or to a documented scoring contract ABI.

Useful? React with 👍 / 👎.

@meeh0w

meeh0w commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

@iJaack Yes, please :)

@meeh0w

meeh0w commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

I posted a PR to make it work in extension (will need VM Modules updated to stable versions after this is merged & released).

@iJaack

iJaack commented Jun 19, 2026

Copy link
Copy Markdown
Author

@meeh0w OK this should be fine to merge now!

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 445d1e562c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


const fallback = buildFallback(normalizedDeclaration);

const provider = new JsonRpcProvider(rpcUrl);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Pass custom RPC headers to identity calls

On networks configured with customRpcHeaders, the transaction/signing paths use getProvider(..., customRpcHeaders) for their normal RPC calls, but this resolver creates a separate bare JsonRpcProvider from only the URL. For RPC endpoints that require header-based auth, every identity eth_call here is sent unauthenticated and falls back to an unknown identity even though the same network works for the rest of the request; pass the network headers through or reuse the already configured provider.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iJaack Can you re-use getProvider() helper here?

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 39531ca15a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


const clientAddresses = dedupeAddresses(getFeedbackClients(feedbackResult));
if (clientAddresses.length) {
const summary = getSummaryValue(await reputationContract.getSummary(agentId, clientAddresses, STARRED_TAG, ''));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle reputation summary failures without aborting approvals

When getSummary reverts for a particular agent/client set, this unhandled await rejects resolveReputationScore, which then rejects the surrounding Promise.all in resolveAgentIdentity; the eth_sign/eth_sendTransaction callers do not catch that, so an otherwise valid request with agent identity context fails before showing the approval instead of falling back to raw feedback or an unknown reputation. Catch summary/reputation read failures and continue to the average/null fallback so reputation outages do not block signing or transactions.

Useful? React with 👍 / 👎.

@iJaack

iJaack commented Jun 23, 2026

Copy link
Copy Markdown
Author

@meeh0w all review suggestion fixes have been applied, please do one final check, it should be ready to merge now!

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6a73c66413

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return null;
}

const feedbackResult = await reputationContract.readAllFeedback(agentId, [], STARRED_TAG, '', false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bound reputation reads before approval

When the declared agent has many starred feedback entries, this approval path calls readAllFeedback with an empty client filter, which materializes every matching client/index/value before the optional summary lookup. A popular or spammed agent can therefore make eth_sign/transaction approvals wait on a very large eth_call or hit provider response/gas limits and fall back to unknown reputation; use a bounded/trusted client set, an indexer, or a timeout/best-effort path before prompting.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants