Skip to content

fix(agentkit): compare EVM addresses case-insensitively in erc20, sushi and yelay - #1507

Open
creativityventures wants to merge 7 commits into
coinbase:mainfrom
creativityventures:fix/case-insensitive-address-comparisons
Open

creativityventures wants to merge 7 commits into
coinbase:mainfrom
creativityventures:fix/case-insensitive-address-comparisons

Conversation

@creativityventures

Copy link
Copy Markdown

Description

Three action providers compare EVM addresses as raw strings, so the same address written in a different case does not match. Addresses reach these providers from an LLM — the Zod schemas accept ^0x[a-fA-F0-9]{40}$, so any casing — and from external APIs, so this is not a hypothetical.

Where Comparison What happens when the case differs
erc20ActionProvider.ts · transfer args.tokenAddress === args.destinationAddress The guardrail that refuses to send a token to its own contract never fires. The transfer is still caught by the ERC20-contract check below it, but only after an extra getCode + multicall, and only for as long as that second check holds. The Python SDK compares .lower() at this exact spot.
sushiRouterActionProvider.ts · fetchDecimals token === nativeAddress nativeAddress from sushi/evm is lowercase, so a checksummed native address falls through to decimals() on an address with no code, and the quote or swap fails with a contract error. Lines 314 and 435 of the same file already compare .toLowerCase().
yelayActionProvider.ts · deposit / redeem / balance vault.address === args.vaultAddress A vault the backend returns in a different case than the agent supplies is reported as Vault not found.

Lowercasing both sides is the convention already used elsewhere: 15 other address comparisons under typescript/agentkit/src do it, including two in the sushi file being changed.

Tests

pnpm --filter @coinbase/agentkit test906 passed (903 on main, +3 added, one per fixed comparison). Each new test fails on main:

✕ Sushi Action Provider › swap › should treat a mixed-case native address as native
✕ Transfer Action › should refuse a transfer whose destination is the token contract in a different case
✕ YelayActionProvider › deposit action › should find the vault when its address case differs from the API response

Tests: 3 failed, 64 passed, 67 total

eslint -c .eslintrc.json "src/**/*.ts", prettier --check and tsc --noEmit are all clean.

I did not run one of the example chatbots for this one: it needs live RPC access and provider keys I don't have here. The three cases are covered at the unit level instead, using each provider's existing mocks — the sushi test asserts readContract is never called, which is the observable difference between the native path and the ERC20 path.

Checklist

  • Added documentation to all relevant README.md files — no README changes: no action, schema or documented behaviour changes, only the comparison
  • Added a changelog entry

Written with AI assistance (Claude); the diff is six files plus a changeset, and every number above is reproducible with the commands given.

@cb-heimdall

Copy link
Copy Markdown

🟡 Heimdall Review Status

Requirement Status More Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot 0
1 if user is external 0
2 if repo is sensitive 0
From .codeflow.yml 1
Additional review requirements
Show calculation
Max 0
0
From CODEOWNERS 0
Global minimum 0
Max 1
1
1 if commit is unverified 0
Sum 1

@github-actions github-actions Bot added documentation Improvements or additions to documentation action provider New action provider typescript labels Sep 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action provider New action provider documentation Improvements or additions to documentation typescript

Development

Successfully merging this pull request may close these issues.

2 participants