Skip to content

fix(audit): tighten ERC-721 decode to require empty data#48

Merged
satyakwok merged 1 commit into
mainfrom
fix/audit-m4-strict-erc721
May 21, 2026
Merged

fix(audit): tighten ERC-721 decode to require empty data#48
satyakwok merged 1 commit into
mainfrom
fix/audit-m4-strict-erc721

Conversation

@satyakwok
Copy link
Copy Markdown
Member

@satyakwok satyakwok commented May 21, 2026

Audit M-4 follow-up against #46.

Problem

Transfer selector 0xddf252ad… is shared by ERC-20, ERC-721, AND any custom Transfer(addr, addr, addr, uint256)-shaped event. The original decoder distinguished only on topic count, so custom 3-indexed events got recorded as ERC-721 with fabricated amount = 1 and a token_id taken from whatever the third indexed argument was. False NFT rows polluted token_transfers and broke balance aggregation on /accounts/{addr}/tokens.

Fix

Per ERC-721 spec the Transfer event has NO unindexed data. Add a data-must-be-empty gate to the ERC-721 path; return None if data is present.

Test plan

  • All 5 token_decode tests pass including new skips_three_indexed_args_with_nonempty_data
  • Spot-check live testnet: confirm sUSDC transfers still index as ERC-20
  • Spot-check no new token_transfers rows appear for non-NFT contracts emitting 3-indexed events

Summary by CodeRabbit

  • Bug Fixes

    • Corrected token event decoding to prevent custom events from being incorrectly classified as token transfers.
  • Tests

    • Added unit test to ensure proper token event decoding and prevent regression.

Review Change Stack

Audit M-4 follow-up against #46. The Transfer selector
`0xddf252ad…` is shared by:

- ERC-20 `Transfer(address indexed, address indexed, uint256)` —
  three topics + 32-byte unindexed data carrying amount.
- ERC-721 `Transfer(address indexed, address indexed, uint256 indexed)`
  — four topics, no unindexed data.

Custom events with three indexed args + unindexed data (eg.
`Transfer(address indexed, address indexed, address indexed, uint256)`)
hit the same selector. The original decoder distinguished only on
topic count, so any such custom event got recorded as an ERC-721 with
a fabricated `amount = 1` and a token_id taken from whatever the third
indexed argument happened to be. That polluted `token_transfers` with
false NFT rows and broke balance aggregation on the indexer's
`/accounts/{addr}/tokens` endpoint.

Per ERC-721 spec the Transfer event has NO unindexed data. Add a
data-must-be-empty gate to the ERC-721 path; return `None` if data is
present so the row drops out of the index entirely. Real NFT contracts
emit empty data and continue to decode correctly. Real custom events
with three indexed args fall through silently — the cost is missing
them from `token_transfers`, but they weren't meaningfully decoded
before either.

Adds a regression test (`skips_three_indexed_args_with_nonempty_data`).
@satyakwok satyakwok merged commit ff71624 into main May 21, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 04eb667a-58ed-4f2a-b972-96f0b104e03a

📥 Commits

Reviewing files that changed from the base of the PR and between ab9e05d and 7e921e5.

📒 Files selected for processing (1)
  • crates/sync/src/token_decode.rs

📝 Walkthrough

Walkthrough

The PR adds a validation check to the ERC-721 decoding branch in decode_transfer that rejects logs where topic3 is present but the data field contains non-empty content. This prevents custom events with three indexed arguments from being incorrectly decoded as NFT transfers with an implicit amount = 1. A unit test was added to verify that logs matching this invalid pattern are skipped during decoding.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • Sentriscloud/indexer-rs#46: This PR refines the ERC-20/ERC-721 token decoding behavior introduced in that PR by adding stricter validation to distinguish between actual NFT transfers and custom events with similar structure.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/audit-m4-strict-erc721

Comment @coderabbitai help to get the list of available commands and usage tips.

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.

1 participant