Auto-loaded by token-auditor agent during Phase 2...
This skill provides comprehensive knowledge for identifying token-related vulnerabilities in smart contracts.
Not all tokens follow the "happy path" of standard implementations. Protocols must handle edge cases from fee tokens, rebasing tokens, callback tokens, and malicious tokens.
Vulnerable Pattern:
// DANGEROUS: Assumes transfer amount equals received amount
function deposit(uint256 amount) external {
token.transferFrom(msg.sender, address(this), amount);
balances[msg.sender] += amount; // May be more than received!
}
Secure Pattern:
function deposit(uint256 amount) external {
uint256 balanceBefore = token.balanceOf(address(this));
token.transferFrom(msg.sender, address(this), amount);
uint256 received = token.balanceOf(address(this)) - balanceBefore;
balances[msg.sender] += received;
}
Common Fee Tokens:
Detection:
Grep("transferFrom.*\\+=|transfer.*\\+=", glob="**/*.sol")
Vulnerable Pattern:
// DANGEROUS: Caches balance that will change
function stake(uint256 amount) external {
token.transferFrom(msg.sender, address(this), amount);
stakedAmount[msg.sender] = amount; // Becomes stale!
}
Secure Pattern:
// Use shares instead of amounts
function stake(uint256 amount) external {
uint256 totalBefore = token.balanceOf(address(this));
token.transferFrom(msg.sender, address(this), amount);
uint256 received = token.balanceOf(address(this)) - totalBefore;
uint256 shares = totalShares == 0
? received
: received * totalShares / totalBefore;
userShares[msg.sender] += shares;
totalShares += shares;
}
Common Rebasing Tokens:
Detection:
Vulnerable Pattern:
// DANGEROUS: State updated after transfer
function withdraw(uint256 amount) external {
require(balances[msg.sender] >= amount);
token.transfer(msg.sender, amount); // Triggers tokensReceived!
balances[msg.sender] -= amount; // Too late!
}
ERC777 Hooks:
tokensToSend() - Called BEFORE transfer on sendertokensReceived() - Called AFTER transfer on recipientDetection:
Grep("IERC777|tokensReceived|tokensToSend", glob="**/*.sol")
Vulnerable Pattern:
// DANGEROUS: safeTransfer triggers callback
function claimNFT(uint256 tokenId) external {
nft.safeTransferFrom(address(this), msg.sender, tokenId);
// onERC721Received callback runs HERE
claimed[tokenId] = true; // State update after callback!
}
Callback Functions:
onERC721Received() - ERC721 safeTransferonERC1155Received() - ERC1155 safeTransferonERC1155BatchReceived() - ERC1155 safeBatchTransferDetection:
Grep("safeTransferFrom|safeMint|safeTransfer", glob="**/*.sol")
Vulnerable Pattern:
// DANGEROUS: USDT doesn't return bool
function deposit(uint256 amount) external {
bool success = token.transfer(address(this), amount);
require(success, "Transfer failed"); // USDT reverts here!
}
Secure Pattern:
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
function deposit(uint256 amount) external {
token.safeTransferFrom(msg.sender, address(this), amount);
}
Tokens Without Return:
Detection:
Grep("transfer\\(|transferFrom\\(", glob="**/*.sol")
Check if using SafeERC20 wrapper.
Risk: Transfers can be blocked, causing DoS.
Affected Tokens:
Vulnerable Pattern:
// DANGEROUS: Can be blocked if recipient blacklisted
function withdraw() external {
token.transfer(msg.sender, balances[msg.sender]); // May revert!
}
Mitigation:
Vulnerable Pattern:
// DANGEROUS: Precision loss with low decimals
// WBTC has 8 decimals, not 18
uint256 priceInWei = wbtcAmount * ethPrice / 1e18; // Wrong scale!
Common Low Decimal Tokens:
Detection:
Grep("decimals|1e18|1e6|1e8", glob="**/*.sol")
Vulnerable Pattern:
// DANGEROUS: Front-runnable approve
token.approve(spender, newAmount);
Attack:
Secure Pattern:
// Reset to 0 first, or use increaseAllowance
token.approve(spender, 0);
token.approve(spender, newAmount);
// Or use OZ increaseAllowance/decreaseAllowance
token.increaseAllowance(spender, amount);
| Token | Fee | Rebase | Blacklist | Callback | Return |
|---|---|---|---|---|---|
| USDT | No* | No | Yes | No | None |
| USDC | No | No | Yes | No | Yes |
| DAI | No | No | No | No | Yes |
| stETH | No | Yes | No | No | Yes |
| WBTC | No | No | No | No | Yes |
| PAXG | Yes | No | No | No | Yes |
| ERC777 | No | No | No | Yes | Yes |
*USDT has fee mechanism but set to 0