Skip to main content

token

Auto-loaded by token-auditor agent during Phase 2. Provides detection patterns for: fee-on-transfer, rebasing tokens, ERC777 hooks, ERC721/1155 callbacks, missing return values, blacklist/pausable, low decimals. Core artifact: Token Compatibility Matrix.

Aller à l'installation

Informations de source

Dépôt
BitterSecurity/Vigilo
Dernière activité de la source
2 février 2026 à 07:22
Langue détectée de SKILL.md
anglais
Étoiles
66
Forks
17

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
token
description
Auto-loaded by token-auditor agent during Phase 2. Provides detection patterns for: fee-on-transfer, rebasing tokens, ERC777 hooks, ERC721/1155 callbacks, missing return values, blacklist/pausable, low decimals. Core artifact: Token Compatibility Matrix.
user-invocable
false
# Token Vulnerability Patterns This skill provides comprehensive knowledge for identifying token-related vulnerabilities in smart contracts. ## Overview 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. ## Token Weirdness Categories ### 1. Fee-On-Transfer Tokens **Vulnerable Pattern:** ```solidity // 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:** ```solidity 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:** - PAXG (0.02% fee) - STA (1% fee) - USDT (potential fee, currently 0) **Detection:** ``` Grep("transferFrom.*\\+=|transfer.*\\+=", glob="**/*.sol") ``` --- ### 2. Rebasing Tokens **Vulnerable Pattern:** ```solidity // 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:** ```solidity // 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:** - stETH (positive rebase daily) - AMPL (elastic supply) - OHM (rebasing) - aTokens (AAVE interest) **Detection:** - Look for balance caching - Check if protocol claims to support stETH, AMPL --- ### 3. ERC777 Callback Reentrancy **Vulnerable Pattern:** ```solidity // 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 sender - `tokensReceived()` - Called AFTER transfer on recipient **Detection:** ``` Grep("IERC777|tokensReceived|tokensToSend", glob="**/*.sol") ``` --- ### 4. ERC721/1155 Callback Reentrancy **Vulnerable Pattern:** ```solidity // 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 safeTransfer - `onERC1155Received()` - ERC1155 safeTransfer - `onERC1155BatchReceived()` - ERC1155 safeBatchTransfer **Detection:** ``` Grep("safeTransferFrom|safeMint|safeTransfer", glob="**/*.sol") ``` --- ### 5. Missing Return Value (USDT) **Vulnerable Pattern:** ```solidity // 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:** ```solidity import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; function deposit(uint256 amount) external { token.safeTransferFrom(msg.sender, address(this), amount); } ``` **Tokens Without Return:** - USDT - BNB - Some older tokens **Detection:** ``` Grep("transfer\\(|transferFrom\\(", glob="**/*.sol") ``` Check if using SafeERC20 wrapper. --- ### 6. Blacklist/Pausable Tokens **Risk:** Transfers can be blocked, causing DoS. **Affected Tokens:** - USDC (blacklist) - USDT (blacklist + pausable) - DAI (pausable) **Vulnerable Pattern:** ```solidity // DANGEROUS: Can be blocked if recipient blacklisted function withdraw() external { token.transfer(msg.sender, balances[msg.sender]); // May revert! } ``` **Mitigation:** - Use pull-over-push pattern - Allow alternate withdrawal addresses - Handle transfer failures gracefully --- ### 7. Low Decimal Tokens **Vulnerable Pattern:** ```solidity // DANGEROUS: Precision loss with low decimals // WBTC has 8 decimals, not 18 uint256 priceInWei = wbtcAmount * ethPrice / 1e18; // Wrong scale! ``` **Common Low Decimal Tokens:** - WBTC (8 decimals) - USDC (6 decimals) - USDT (6 decimals) **Detection:** ``` Grep("decimals|1e18|1e6|1e8", glob="**/*.sol") ``` --- ### 8. Approval Race Condition **Vulnerable Pattern:** ```solidity // DANGEROUS: Front-runnable approve token.approve(spender, newAmount); ``` **Attack:** 1. User has 100 approved 2. User calls approve(50) to reduce 3. Attacker front-runs: transferFrom(100) 4. Approve(50) executes 5. Attacker calls: transferFrom(50) 6. Total stolen: 150 **Secure Pattern:** ```solidity // 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 Compatibility Matrix | 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 --- ## Token Audit Checklist - [ ] Balance changes measured, not assumed (fee tokens) - [ ] Shares used instead of amounts (rebasing tokens) - [ ] CEI pattern followed for callbacks (ERC721/777/1155) - [ ] SafeERC20 used for transfers - [ ] Blacklist handling considered - [ ] Decimal precision handled correctly - [ ] Approval race condition mitigated - [ ] Zero-amount transfers handled ## Severity Classification ### Critical - ERC777 reentrancy - ERC721/1155 callback reentrancy - Fee token accounting mismatch ### High - Rebasing token balance caching - Missing return value handling - Decimal precision errors ### Medium - Blacklist DoS potential - Approval race condition - No zero-amount validation --- ## Additional Token Patterns (2025-2026) ### 9. ERC-2612 Permit Vulnerabilities **Vulnerable Pattern:** ```solidity // DANGEROUS: No deadline validation function depositWithPermit( uint256 amount, uint8 v, bytes32 r, bytes32 s ) external { token.permit(msg.sender, address(this), amount, type(uint256).max, v, r, s); // @audit deadline = max allows signature to be used forever token.transferFrom(msg.sender, address(this), amount); } ``` **Secure Pattern:** ```solidity function depositWithPermit( uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external { require(deadline >= block.timestamp, "Expired"); token.permit(msg.sender, address(this), amount, deadline, v, r, s); token.transferFrom(msg.sender, address(this), amount); } ``` **Signature Replay Risks:** - Same signature valid on multiple chains (missing chainId) - Permit can be front-run if deadline is far future - DoS by calling permit with invalid signature (some implementations) **Search Queries:** ``` Grep("permit|Permit|PERMIT_TYPEHASH|nonces", glob="**/*.sol") Grep("ecrecover|ECDSA|v.*r.*s", glob="**/*.sol") ``` --- ### 10. Permit2 Vulnerabilities (Uniswap) **Overview**: Permit2 is a universal approval system that introduces new attack vectors. **Vulnerable Pattern:** ```solidity // DANGEROUS: No expiration check on permit2 function swapWithPermit2( ISignatureTransfer.PermitTransferFrom calldata permit, ISignatureTransfer.SignatureTransferDetails calldata transferDetails, bytes calldata signature ) external { permit2.permitTransferFrom(permit, transferDetails, msg.sender, signature); // @audit What if permit.deadline is far in future? } ``` **Permit2 Specific Risks:** 1. **Batch Permits**: Single signature approves multiple tokens 2. **Witness Data**: Additional data can be signed but misinterpreted 3. **Signature Malleability**: Different signatures for same approval 4. **Nonce Invalidation**: Can invalidate others' permits **Search Queries:** ``` Grep("Permit2|permit2|ISignatureTransfer|IAllowanceTransfer", glob="**/*.sol") Grep("permitTransferFrom|permitWitnessTransferFrom", glob="**/*.sol") ``` **Security Checklist:** - [ ] Deadline always validated - [ ] Nonce management understood - [ ] Witness data properly validated - [ ] Signature cannot be reused maliciously --- ### 11. ERC-4626 Token Integration Risks When integrating ERC-4626 vaults as collateral or tokens: **Vulnerable Pattern:** ```solidity // DANGEROUS: Trusting vault's reported value function getCollateralValue(address user) external view returns (uint256) { uint256 shares = vault.balanceOf(user); return vault.convertToAssets(shares); // @audit Can be manipulated via donation! } ``` **Attack Vector:** 1. Attacker donates to vault, inflating `totalAssets` 2. `convertToAssets()` returns inflated value 3. Protocol accepts as collateral at wrong price 4. Attacker borrows against inflated collateral **Secure Pattern:** ```solidity // Use time-weighted or oracle-based pricing for ERC-4626 tokens function getCollateralValue(address user) external view returns (uint256) { uint256 shares = vault.balanceOf(user); // Use external oracle for share price, NOT vault.convertToAssets() uint256 sharePrice = oracle.getPrice(address(vault)); return shares * sharePrice / 1e18; } ``` **Search Queries:** ``` Grep("ERC4626|convertToAssets|convertToShares|previewDeposit", glob="**/*.sol") Grep("vault.*balanceOf|collateral.*vault", glob="**/*.sol") ``` --- ### 12. ERC-1363 (transferAndCall) Reentrancy **Vulnerable Pattern:** ```solidity // DANGEROUS: ERC-1363 triggers callback on receive function receiveApproval( address from, uint256 amount, address token, bytes calldata data ) external { // @audit This is called during transferAndCall/approveAndCall // State may not be updated yet! } ``` **Search Queries:** ``` Grep("ERC1363|transferAndCall|approveAndCall|onApprovalReceived", glob="**/*.sol") ``` --- ### 13. ERC-6909 Minimal Multi-Token Streamlined ERC-1155 alternative: no callbacks, no batching, per-token granular approvals. **Vulnerable Pattern:** ```solidity // DANGEROUS: Missing per-token allowance check require(isApprovedForAll[from][msg.sender], "Not approved");
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub