Skip to content

chore: flag fee-on-transfer tokens on mainnet - #153

Open
ScreamingHawk wants to merge 2 commits into
masterfrom
chore/flag-fee-on-transfer-tokens
Open

chore: flag fee-on-transfer tokens on mainnet#153
ScreamingHawk wants to merge 2 commits into
masterfrom
chore/flag-fee-on-transfer-tokens

Conversation

@ScreamingHawk

Copy link
Copy Markdown

What

Adds extensions.feeOnTransfer: true to 132 mainnet ERC-20s, plus tools/sync-fee-on-transfer.ts to refresh the flag.

Fee-on-transfer tokens (transfer taxes, reflection, burn-on-transfer) credit the recipient less than the sender sends. Any integration that assumes transfer(x) delivers x breaks on them — the recipient balance can never reach the expected amount, so balance-based preconditions are unsatisfiable by construction and no amount the user sends will fix it.

Example: SHUF (0x3a9fff45…6b9e) burns 1% and redistributes 1% on every transfer. A send of 1237.674738 credits 1212.921243240.

Data source

GoPlus token security API (transfer_tax != 0), free unauthenticated tier. New third-party dependency for this repo — previously only CoinGecko was used. CoinGecko was checked first and publishes nothing equivalent: no tax, honeypot or transfer-behaviour field on any endpoint. The only signals in this repo today are free-text description prose, which is not machine-actionable.

Coverage is mainnet-only in practice:

chain scanned flagged no data
mainnet 1987 132 286
polygon 411 0 230
base 99 0 25
optimism 77 0 77
arbitrum 75 0 32
avalanche 34 0 34
bnb 28 0 8
gnosis 12 0 12
berachain / soneium / sonic / monad 15 0 13

717 of 2738 scanned (26%) returned no tax data. Outside mainnet, "0 flagged" mostly means "GoPlus has no data", not "clean" — optimism is 77/77 unknown, avalanche 34/34, gnosis 12/12. An empty transfer_tax is treated as unknown, not as zero, and such tokens are left unflagged.

Verification

The GoPlus results were independently double-checked against on-chain state for all 132 flagged tokens, using a separate script (deliberately not committed — it is a one-off audit, not something to maintain). Method: eth_simulateV1, transfer from a token's Uniswap V2 pair to a fresh address, diff recipient balance before/after in the same simulated block.

Outcome:

  • 114 confirmed directly — measured tax matched GoPlus.
  • 16 initially read as 0%, all traced to the tokens exempting the Uniswap pair from their fee. Re-measured with an ordinary holder→holder transfer, they charge the full rate and match GoPlus: CHADS 49.9999% (claimed 50%), TCORE 23.9999% (24%), PAMP 7.9999% (8%). These were a flaw in the audit method, not bad flags.
  • 2 unverifiable: DGX (V2 pair holds 3 base units, too small to probe) and TNI (non-standard ERC-20, transfer returns no data). Neither is contradicted; both keep the flag. Unverifiable is not treated as clean.

Net: 130/132 confirmed on-chain, 0 genuine contradictions.

One correction to GoPlus surfaced: XVIX reports 0.5% but measures 0.6987%. The flag is still correct. Since exact rates can be wrong, feeOnTransfer is stored as a boolean only — no rate is persisted.

Tool

pnpm sync-fee-on-transfer — dry-run by default, --write applies, --chain <name> scopes. Follows the existing sync-coingecko.ts conventions.

Notes for anyone re-running it:

  • Requests are serial. GoPlus only computes a fresh report for single-address requests; multi-address requests return only what it already has cached (30 addresses returned 0 results).
  • The free tier rate-limits aggressively under sustained load. A full scan takes ~4h. Results are cached to a gitignored file, so an interrupted run resumes for free.
  • Rate limits, 5xx, malformed bodies and transient DNS/socket failures are retried with bounded backoff. An exhausted retry throws rather than silently recording "no tax" — a false negative here would strand funds.

Scope

Data + tooling only. Consumers do not read this field yet. go-tokendirectory's ContractInfoExtension needs a matching FeeOnTransfer bool before anything downstream can act on it.

ScreamingHawk and others added 2 commits August 19, 2026 14:10
Tokens that tax transfers credit the recipient less than the sender sends,
which breaks any integration that assumes transfer(x) delivers x.

Add extensions.feeOnTransfer to the 132 mainnet ERC-20s GoPlus reports a
non-zero transfer_tax for, and a tool to refresh the flag.
@miguelmota

Copy link
Copy Markdown
Member

Great call, LGTM

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