Skip to content
Back to skills

Token Compatibility

ASecurity

Detect ERC-20 token compatibility issues — fee-on-transfer, rebasing, non-standard return values, missing decimals(), low-decimal tokens, blacklistable tokens (USDC), pausable tokens. Activate on any ERC-20 integration, `transfer`/`transferFrom` use, balance-based accounting, decimal scaling.

  • 36 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 4, 2026
blockchain

Security analysis

A100/100

Scanned October 4, 2026

npx -y skills add iktok90-design/ai-smart-contract-auditor --skill token-compatibility --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Token Compatibility?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Token Compatibility
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/iktok90-design-token-compatibility/badge)](https://www.skillsdirectory.com/skills/iktok90-design-token-compatibility)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: token-compatibility
description: Detect ERC-20 token compatibility issues — fee-on-transfer, rebasing, non-standard return values, missing decimals(), low-decimal tokens, blacklistable tokens (USDC), pausable tokens. Activate on any ERC-20 integration, `transfer`/`transferFrom` use, balance-based accounting, decimal scaling.
---

# Token compatibility detection

## When this applies

- Any ERC-20 integration: lending, AMM, vault, staking, bridge
- Balance-based accounting (`balanceOf(this) - prevBalance`)
- Decimal scaling between tokens
- Cross-chain bridges with various token implementations
- Aggregators / routers handling user-supplied tokens

## Detection patterns

### Fee-on-transfer assumed away (HIGH)
```solidity
uint256 before = token.balanceOf(address(this));
token.transferFrom(user, address(this), amount);
shares = amount * X / Y;        // ← uses `amount`, not actual received
```
Tokens like USDT (when fee enabled), SafeMoon, etc. take a fee. Use `balanceOf(this) - before` as the effective amount.

### Rebasing token accounting (HIGH)
stETH, aUSDC, AMPL — balances change without `transfer`. Vaults assuming `balanceOf` ≡ deposit will mis-account. Either don't accept rebasing tokens, or use share-based accounting (wstETH-style wrappers).

### Non-standard return values — USDT (HIGH)
USDT's `transfer` does NOT return bool (pre-`SafeERC20` use breaks):
```solidity
bool ok = IERC20(usdt).transfer(to, amt);   // ← reverts: ABI mismatch
```
Use OZ `SafeERC20` which uses low-level call + return-data inspection.

### Blacklistable tokens (HIGH)
USDC, USDT can blacklist addresses. If a user gets blacklisted after deposit, the protocol may be unable to return funds → other users' funds stuck if pooled.

### Pausable tokens (HIGH)
USDC, USDT can pause transfers. Protocol functions that must succeed (liquidations) may revert.

### Low-decimal tokens (MEDIUM-HIGH)
GUSD has 2 decimals. Math assuming 18 decimals causes huge rounding errors.
```solidity
shares = assets * 1e18 / something;   // ← shares now astronomical or zero
```

### Tokens with hooks (ERC-777) (HIGH)
Reentrancy surface (see [[reentrancy]]). Many protocols implicitly assume vanilla ERC-20.

### Tokens with double-entry (HIGH)
TUSD historically had two valid addresses. `transferFrom` could be invoked from either. Approval to one didn't bind the other.

### Missing return on approve / non-zero approve revert (HIGH)
Some tokens (early USDT) require `approve(spender, 0)` before `approve(spender, X)`. Use `safeApprove` or `forceApprove`. See [[approval-issues]].

### Missing `decimals()` (LOW)
Non-standard tokens may lack `decimals()`. Guard with try/catch and default to 18.

### Tokens that revert on zero-amount transfer (LOW-MEDIUM)
LEND, others. Cap-by-zero guards needed.

## Severity rubric

| Pattern | Severity |
|---|---|
| Vault accepts fee-on-transfer with no balance-delta accounting | **High** |
| Protocol accepts rebasing token without share-wrapper | **High** |
| Naive ERC-20 transfer (no SafeERC20) | **High** |
| Pooled funds with blacklistable token + no escape hatch | **High** |
| Low-decimal token with 18-decimal-assumed math | **High** |
| Pausable token in liquidation path | **High** |
| Approve-non-zero revert ignored | **High** |
| Zero-amount-revert in batch flow | **Medium** |
| Missing `decimals()` graceful handling | **Low** |

## Remediation patterns

- **Allowlist tokens** — easier than supporting every token.
- **Use SafeERC20** — covers most return-value quirks.
- **Balance-delta accounting** — measure `balanceOf(this) - prev` after transferFrom.
- **Disallow rebasing tokens** — or only accept their share-wrapped form (wstETH not stETH).
- **Reset approval before set** — `forceApprove` from OZ ≥4.9, or `safeApprove(0)` then `safeApprove(amount)`.
- **Escape hatches** — admin can swap a single user's blacklisted balance out of pooled state into a side mapping.

## False-positive notes

- Protocol explicitly lists supported tokens and tests against them → not a finding for unsupported.
- Test/mock tokens in test/ → ignore.

## Related

- [[unchecked-calls]]
- [[approval-issues]]
- [[reentrancy]] — ERC-777 hooks
- [[integer-issues]] — decimals math

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…