solidity-argus 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +37 -0
- package/LICENSE +21 -0
- package/README.md +249 -0
- package/package.json +43 -0
- package/skills/INVENTORY.md +79 -0
- package/skills/README.md +56 -0
- package/skills/checklists/cyfrin-best-practices-runtime/SKILL.md +424 -0
- package/skills/checklists/cyfrin-best-practices-upgrades/SKILL.md +157 -0
- package/skills/checklists/cyfrin-defi-core/SKILL.md +373 -0
- package/skills/checklists/cyfrin-defi-integrations/SKILL.md +412 -0
- package/skills/checklists/cyfrin-gas/SKILL.md +55 -0
- package/skills/checklists/general-audit/SKILL.md +433 -0
- package/skills/methodology/audit-workflow/SKILL.md +129 -0
- package/skills/methodology/report-template/SKILL.md +190 -0
- package/skills/methodology/severity-classification/SKILL.md +179 -0
- package/skills/protocol-patterns/amm-dex/SKILL.md +229 -0
- package/skills/protocol-patterns/bridges-cross-chain/SKILL.md +317 -0
- package/skills/protocol-patterns/dao-governance/SKILL.md +281 -0
- package/skills/protocol-patterns/lending-borrowing/SKILL.md +221 -0
- package/skills/protocol-patterns/staking-vesting/SKILL.md +247 -0
- package/skills/references/exploit-reference/SKILL.md +259 -0
- package/skills/references/smartbugs-examples/SKILL.md +296 -0
- package/skills/vulnerability-patterns/access-control/SKILL.md +298 -0
- package/skills/vulnerability-patterns/arbitrary-storage-location/SKILL.md +59 -0
- package/skills/vulnerability-patterns/assert-violation/SKILL.md +59 -0
- package/skills/vulnerability-patterns/asserting-contract-from-code-size/SKILL.md +61 -0
- package/skills/vulnerability-patterns/authorization-txorigin/SKILL.md +55 -0
- package/skills/vulnerability-patterns/default-visibility/SKILL.md +62 -0
- package/skills/vulnerability-patterns/delegatecall-untrusted-callee/SKILL.md +60 -0
- package/skills/vulnerability-patterns/dos-gas-limit/SKILL.md +59 -0
- package/skills/vulnerability-patterns/dos-revert/SKILL.md +72 -0
- package/skills/vulnerability-patterns/flash-loan-attacks/SKILL.md +249 -0
- package/skills/vulnerability-patterns/floating-pragma/SKILL.md +51 -0
- package/skills/vulnerability-patterns/hash-collision/SKILL.md +52 -0
- package/skills/vulnerability-patterns/inadherence-to-standards/SKILL.md +61 -0
- package/skills/vulnerability-patterns/incorrect-constructor/SKILL.md +60 -0
- package/skills/vulnerability-patterns/incorrect-inheritance-order/SKILL.md +59 -0
- package/skills/vulnerability-patterns/insufficient-gas-griefing/SKILL.md +61 -0
- package/skills/vulnerability-patterns/lack-of-precision/SKILL.md +61 -0
- package/skills/vulnerability-patterns/logic-errors/SKILL.md +333 -0
- package/skills/vulnerability-patterns/missing-protection-signature-replay/SKILL.md +60 -0
- package/skills/vulnerability-patterns/msgvalue-loop/SKILL.md +66 -0
- package/skills/vulnerability-patterns/off-by-one/SKILL.md +67 -0
- package/skills/vulnerability-patterns/oracle-manipulation/SKILL.md +252 -0
- package/skills/vulnerability-patterns/outdated-compiler-version/SKILL.md +65 -0
- package/skills/vulnerability-patterns/overflow-underflow/SKILL.md +61 -0
- package/skills/vulnerability-patterns/reentrancy/SKILL.md +266 -0
- package/skills/vulnerability-patterns/shadowing-state-variables/SKILL.md +72 -0
- package/skills/vulnerability-patterns/signature-malleability/SKILL.md +59 -0
- package/skills/vulnerability-patterns/unbounded-return-data/SKILL.md +63 -0
- package/skills/vulnerability-patterns/unchecked-return-values/SKILL.md +52 -0
- package/skills/vulnerability-patterns/unencrypted-private-data-on-chain/SKILL.md +65 -0
- package/skills/vulnerability-patterns/unexpected-ecrecover-null-address/SKILL.md +61 -0
- package/skills/vulnerability-patterns/uninitialized-storage-pointer/SKILL.md +63 -0
- package/skills/vulnerability-patterns/unsafe-low-level-call/SKILL.md +56 -0
- package/skills/vulnerability-patterns/unsecure-signatures/SKILL.md +80 -0
- package/skills/vulnerability-patterns/unsupported-opcodes/SKILL.md +69 -0
- package/skills/vulnerability-patterns/unused-variables/SKILL.md +70 -0
- package/skills/vulnerability-patterns/use-of-deprecated-functions/SKILL.md +81 -0
- package/skills/vulnerability-patterns/weak-sources-randomness/SKILL.md +77 -0
- package/skills/vulnerability-patterns/weird-tokens/SKILL.md +294 -0
- package/src/agents/argus-prompt.ts +407 -0
- package/src/agents/pythia-prompt.ts +134 -0
- package/src/agents/scribe-prompt.ts +87 -0
- package/src/agents/sentinel-prompt.ts +133 -0
- package/src/cli/cli-program.ts +67 -0
- package/src/cli/commands/doctor.ts +83 -0
- package/src/cli/commands/init.ts +46 -0
- package/src/cli/commands/install.ts +55 -0
- package/src/cli/index.ts +13 -0
- package/src/cli/tui-prompts.ts +75 -0
- package/src/cli/types.ts +9 -0
- package/src/config/index.ts +3 -0
- package/src/config/loader.ts +36 -0
- package/src/config/schema.ts +82 -0
- package/src/config/types.ts +4 -0
- package/src/constants/defaults.ts +6 -0
- package/src/create-hooks.ts +84 -0
- package/src/create-managers.ts +26 -0
- package/src/create-tools.ts +30 -0
- package/src/features/audit-enforcer/audit-enforcer.ts +34 -0
- package/src/features/audit-enforcer/index.ts +1 -0
- package/src/features/background-agent/background-manager.ts +200 -0
- package/src/features/background-agent/index.ts +1 -0
- package/src/features/context-monitor/context-monitor.ts +48 -0
- package/src/features/context-monitor/index.ts +4 -0
- package/src/features/context-monitor/tool-output-truncator.ts +17 -0
- package/src/features/error-recovery/index.ts +2 -0
- package/src/features/error-recovery/session-recovery.ts +27 -0
- package/src/features/error-recovery/tool-error-recovery.ts +35 -0
- package/src/features/index.ts +5 -0
- package/src/features/persistent-state/audit-state-manager.ts +121 -0
- package/src/features/persistent-state/index.ts +1 -0
- package/src/hooks/compaction-hook.ts +50 -0
- package/src/hooks/config-handler.ts +116 -0
- package/src/hooks/event-hook-v2.ts +93 -0
- package/src/hooks/event-hook.ts +74 -0
- package/src/hooks/hook-system.ts +9 -0
- package/src/hooks/index.ts +5 -0
- package/src/hooks/knowledge-sync-hook.ts +57 -0
- package/src/hooks/safe-create-hook.ts +15 -0
- package/src/hooks/system-prompt-hook.ts +126 -0
- package/src/hooks/tool-tracking-hook.ts +234 -0
- package/src/hooks/types.ts +16 -0
- package/src/index.ts +36 -0
- package/src/knowledge/scvd-client.ts +242 -0
- package/src/knowledge/scvd-index.ts +183 -0
- package/src/knowledge/scvd-sync.ts +85 -0
- package/src/managers/index.ts +1 -0
- package/src/managers/types.ts +85 -0
- package/src/plugin-interface.ts +38 -0
- package/src/shared/binary-utils.ts +63 -0
- package/src/shared/deep-merge.ts +71 -0
- package/src/shared/file-utils.ts +56 -0
- package/src/shared/index.ts +5 -0
- package/src/shared/jsonc-parser.ts +39 -0
- package/src/shared/logger.ts +36 -0
- package/src/state/audit-state.ts +27 -0
- package/src/state/finding-store.ts +126 -0
- package/src/state/plugin-state.ts +14 -0
- package/src/state/types.ts +61 -0
- package/src/tools/contract-analyzer-tool.ts +184 -0
- package/src/tools/forge-fuzz-tool.ts +311 -0
- package/src/tools/forge-test-tool.ts +397 -0
- package/src/tools/pattern-checker-tool.ts +337 -0
- package/src/tools/report-generator-tool.ts +308 -0
- package/src/tools/slither-tool.ts +465 -0
- package/src/tools/solodit-search-tool.ts +131 -0
- package/src/tools/sync-knowledge-tool.ts +116 -0
- package/src/utils/project-detector.ts +133 -0
- package/src/utils/solidity-parser.ts +174 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: unused-variables
|
|
3
|
+
description: - Contract declares state variables, local variables, function parameters, or imports that are never referenced
|
|
4
|
+
---
|
|
5
|
+
<!-- Source: kadenzipfel/smart-contract-vulnerabilities (MIT) -->
|
|
6
|
+
|
|
7
|
+
# Presence of Unused Variables
|
|
8
|
+
|
|
9
|
+
## Preconditions
|
|
10
|
+
- Contract declares state variables, local variables, function parameters, or imports that are never referenced
|
|
11
|
+
- OR: return values from function calls are silently discarded
|
|
12
|
+
|
|
13
|
+
## Vulnerable Pattern
|
|
14
|
+
```solidity
|
|
15
|
+
contract Vault {
|
|
16
|
+
uint256 public totalDeposits;
|
|
17
|
+
uint256 public unusedCounter; // Declared but never read or written
|
|
18
|
+
|
|
19
|
+
function deposit(uint256 amount, bytes memory data) external {
|
|
20
|
+
// `data` parameter never used — possible missing validation
|
|
21
|
+
totalDeposits += amount;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function process() external {
|
|
25
|
+
// Return value from transfer silently discarded
|
|
26
|
+
// This may indicate missing success check
|
|
27
|
+
IERC20(token).transfer(recipient, amount);
|
|
28
|
+
|
|
29
|
+
uint256 result = _calculate();
|
|
30
|
+
// `result` computed but never used — missing logic?
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Detection Heuristics
|
|
36
|
+
1. Search for state variables that are never referenced in any function (only declared)
|
|
37
|
+
2. Search for function parameters that are never used in the function body
|
|
38
|
+
3. Search for local variables that are assigned but never read
|
|
39
|
+
4. Check for return values from external calls that are not captured or checked
|
|
40
|
+
5. For each unused variable, determine: is it dead code (safe to remove) or does it indicate missing logic (a bug)?
|
|
41
|
+
6. Check compiler warnings for unused variable alerts
|
|
42
|
+
|
|
43
|
+
## False Positives
|
|
44
|
+
- The variable is part of an interface implementation and must be declared for signature compatibility even if unused
|
|
45
|
+
- The variable is used in a commented-out or conditional compilation path
|
|
46
|
+
- The variable is reserved for future use and documented as such
|
|
47
|
+
- Function parameters prefixed with `_` to explicitly mark as unused (e.g., `function hook(uint256 /* _amount */)`)
|
|
48
|
+
|
|
49
|
+
## Remediation
|
|
50
|
+
- For dead code: remove the unused variable entirely
|
|
51
|
+
- For missing logic: implement the intended use (e.g., check the return value, use the parameter for validation)
|
|
52
|
+
- For interface-required but unused parameters: use the unnamed parameter syntax
|
|
53
|
+
```solidity
|
|
54
|
+
// Remove dead state variables
|
|
55
|
+
// uint256 public unusedCounter; — DELETE
|
|
56
|
+
|
|
57
|
+
// Unnamed parameters for interface compliance
|
|
58
|
+
function onERC721Received(
|
|
59
|
+
address, // operator — unused
|
|
60
|
+
address, // from — unused
|
|
61
|
+
uint256, // tokenId — unused
|
|
62
|
+
bytes memory // data — unused
|
|
63
|
+
) external pure returns (bytes4) {
|
|
64
|
+
return this.onERC721Received.selector;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Check return values — use SafeERC20 for non-compliant tokens
|
|
68
|
+
bool success = IERC20(token).transfer(recipient, amount);
|
|
69
|
+
require(success);
|
|
70
|
+
```
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: use-of-deprecated-functions
|
|
3
|
+
description: - Contract uses Solidity functions, keywords, or language features that have been deprecated or removed
|
|
4
|
+
---
|
|
5
|
+
<!-- Source: kadenzipfel/smart-contract-vulnerabilities (MIT) -->
|
|
6
|
+
|
|
7
|
+
# Use of Deprecated Functions
|
|
8
|
+
|
|
9
|
+
## Preconditions
|
|
10
|
+
- Contract uses Solidity functions, keywords, or language features that have been deprecated or removed
|
|
11
|
+
- The deprecated feature may behave differently than expected or may not compile on newer Solidity versions
|
|
12
|
+
|
|
13
|
+
## Vulnerable Pattern
|
|
14
|
+
```solidity
|
|
15
|
+
pragma solidity ^0.4.24;
|
|
16
|
+
|
|
17
|
+
contract Legacy {
|
|
18
|
+
function destroy() external {
|
|
19
|
+
// suicide is deprecated — renamed to selfdestruct
|
|
20
|
+
// selfdestruct itself is now deprecated post-Dencun
|
|
21
|
+
suicide(msg.sender);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getHash(bytes memory data) external view returns (bytes32) {
|
|
25
|
+
return sha3(data); // Deprecated — use keccak256
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getBlockHash(uint256 n) external view returns (bytes32) {
|
|
29
|
+
return block.blockhash(n); // Deprecated — use blockhash(n)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getRemainingGas() external view returns (uint256) {
|
|
33
|
+
return msg.gas; // Deprecated — use gasleft()
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
| Deprecated | Replacement |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `suicide(address)` | `selfdestruct(address)` (also deprecated) |
|
|
41
|
+
| `block.blockhash(uint)` | `blockhash(uint)` |
|
|
42
|
+
| `sha3(...)` | `keccak256(...)` |
|
|
43
|
+
| `callcode(...)` | `delegatecall(...)` |
|
|
44
|
+
| `throw` | `revert()` |
|
|
45
|
+
| `msg.gas` | `gasleft()` |
|
|
46
|
+
| `constant` (function modifier) | `view` |
|
|
47
|
+
| `var` | Explicit type name |
|
|
48
|
+
|
|
49
|
+
## Detection Heuristics
|
|
50
|
+
1. Search for each deprecated keyword: `suicide`, `sha3`, `block.blockhash`, `callcode`, `throw`, `msg.gas`, `constant` (as function modifier), `var`
|
|
51
|
+
2. Search for `selfdestruct` — while it's the replacement for `suicide`, it is itself deprecated post-Dencun and non-functional on some chains
|
|
52
|
+
3. Check compiler warnings for deprecation notices
|
|
53
|
+
4. Flag any usage and recommend the modern replacement
|
|
54
|
+
|
|
55
|
+
## False Positives
|
|
56
|
+
- The deprecated function appears in comments or documentation, not in executable code
|
|
57
|
+
- The contract is intentionally targeting an old Solidity version where the deprecated feature is still standard
|
|
58
|
+
- Interface definitions that reference deprecated patterns for backward compatibility
|
|
59
|
+
|
|
60
|
+
## Remediation
|
|
61
|
+
- Replace each deprecated function with its modern equivalent (see table above)
|
|
62
|
+
- For `selfdestruct`: remove reliance entirely, as it is deprecated and non-functional on some chains post-Dencun
|
|
63
|
+
- Upgrade the Solidity version to benefit from compiler enforcement of modern syntax
|
|
64
|
+
```solidity
|
|
65
|
+
// Modern equivalents
|
|
66
|
+
pragma solidity 0.8.24;
|
|
67
|
+
|
|
68
|
+
contract Modern {
|
|
69
|
+
function getHash(bytes memory data) external pure returns (bytes32) {
|
|
70
|
+
return keccak256(data);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function getBlockHash(uint256 n) external view returns (bytes32) {
|
|
74
|
+
return blockhash(n);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function getRemainingGas() external view returns (uint256) {
|
|
78
|
+
return gasleft();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: weak-sources-randomness
|
|
3
|
+
description: - Contract generates \"random\" values using on-chain data: `block.timestamp`, `blockhash`, `block.difficulty` / `block.prevrandao`, `block.number`, or combinations thereof
|
|
4
|
+
---
|
|
5
|
+
<!-- Source: kadenzipfel/smart-contract-vulnerabilities (MIT) -->
|
|
6
|
+
|
|
7
|
+
# Weak Sources of Randomness from Chain Attributes
|
|
8
|
+
|
|
9
|
+
## Preconditions
|
|
10
|
+
- Contract generates "random" values using on-chain data: `block.timestamp`, `blockhash`, `block.difficulty` / `block.prevrandao`, `block.number`, or combinations thereof
|
|
11
|
+
- The random value determines outcomes with economic value (lotteries, games, minting, distributions)
|
|
12
|
+
|
|
13
|
+
## Vulnerable Pattern
|
|
14
|
+
```solidity
|
|
15
|
+
function drawLottery() external {
|
|
16
|
+
// All inputs are deterministic and publicly visible
|
|
17
|
+
// Another contract can compute the same value in the same tx
|
|
18
|
+
uint256 random = uint256(keccak256(abi.encodePacked(
|
|
19
|
+
block.timestamp,
|
|
20
|
+
block.prevrandao,
|
|
21
|
+
msg.sender
|
|
22
|
+
))) % 100;
|
|
23
|
+
|
|
24
|
+
if (random < 5) {
|
|
25
|
+
_payWinner(msg.sender);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Attacker contract
|
|
30
|
+
contract Exploit {
|
|
31
|
+
function attack(Lottery target) external {
|
|
32
|
+
// Compute the same "random" value before calling
|
|
33
|
+
uint256 random = uint256(keccak256(abi.encodePacked(
|
|
34
|
+
block.timestamp,
|
|
35
|
+
block.prevrandao,
|
|
36
|
+
address(this)
|
|
37
|
+
))) % 100;
|
|
38
|
+
|
|
39
|
+
// Only call if we'll win
|
|
40
|
+
if (random < 5) {
|
|
41
|
+
target.drawLottery();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Detection Heuristics
|
|
48
|
+
1. Search for `block.timestamp`, `block.prevrandao`, `block.difficulty`, `blockhash`, `block.number` used as inputs to `keccak256` or arithmetic operations producing a "random" value
|
|
49
|
+
2. Check if the resulting value determines an outcome with economic impact (winner selection, token distribution, NFT rarity, game outcome)
|
|
50
|
+
3. If randomness is derived exclusively from on-chain data, flag it — a contract in the same transaction can compute the identical value
|
|
51
|
+
4. Check if `blockhash` is used for a future block (returns 0 for blocks not yet mined) or a block older than 256 blocks (also returns 0)
|
|
52
|
+
5. Check if validators/miners can influence the inputs to bias the outcome
|
|
53
|
+
|
|
54
|
+
## False Positives
|
|
55
|
+
- Chainlink VRF or another verifiable randomness oracle is used
|
|
56
|
+
- On-chain data is combined with an off-chain commit-reveal scheme (the on-chain part alone doesn't determine the outcome)
|
|
57
|
+
- The randomness doesn't determine anything with economic value
|
|
58
|
+
- `block.prevrandao` on PoS Ethereum provides sufficient entropy for the specific use case (not for high-value outcomes)
|
|
59
|
+
|
|
60
|
+
## Remediation
|
|
61
|
+
- Use Chainlink VRF (Verifiable Random Function) for provably fair randomness
|
|
62
|
+
- Implement a commit-reveal scheme: users commit hashed choices, reveal in a later block
|
|
63
|
+
- Never use `block.timestamp`, `blockhash`, or `block.prevrandao` alone for randomness in high-value contexts
|
|
64
|
+
```solidity
|
|
65
|
+
import {VRFConsumerBaseV2} from "@chainlink/contracts/src/v0.8/vrf/VRFConsumerBaseV2.sol";
|
|
66
|
+
|
|
67
|
+
contract FairLottery is VRFConsumerBaseV2 {
|
|
68
|
+
function requestRandom() external {
|
|
69
|
+
requestRandomWords(keyHash, subId, confirmations, gasLimit, 1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function fulfillRandomWords(uint256, uint256[] memory randomWords) internal override {
|
|
73
|
+
uint256 winner = randomWords[0] % participants.length;
|
|
74
|
+
_payWinner(participants[winner]);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: weird-tokens
|
|
3
|
+
description: Non-standard ERC20 behaviors, integration pitfalls, and token-handling safeguards.
|
|
4
|
+
---
|
|
5
|
+
<!-- Source: DeFiFoFum/fofum-solidity-skills (MIT) -->
|
|
6
|
+
|
|
7
|
+
# Weird ERC20 Tokens Reference
|
|
8
|
+
|
|
9
|
+
Tokens that don't behave like standard ERC20. Integrating with these requires special handling.
|
|
10
|
+
|
|
11
|
+
## Quick Reference Table
|
|
12
|
+
|
|
13
|
+
| Pattern | Example Tokens | Risk | Mitigation |
|
|
14
|
+
|---------|---------------|------|------------|
|
|
15
|
+
| Missing return values | USDT, BNB, OMG | Silent failures | SafeERC20 |
|
|
16
|
+
| Fee on transfer | STA, PAXG, SAFEMOON | Balance mismatch | Measure before/after |
|
|
17
|
+
| Rebasing | AMPL, stETH, OHM | Cached balance wrong | Don't cache balances |
|
|
18
|
+
| Pausable | BNB, ZIL | DOS | Handle gracefully |
|
|
19
|
+
| Blocklist | USDC, USDT | User funds frozen | Document risk |
|
|
20
|
+
| Upgradeable | USDC, USDT | Behavior can change | Monitor upgrades |
|
|
21
|
+
| Flash mintable | DAI | Infinite supply attacks | Check total supply |
|
|
22
|
+
| Multiple addresses | TUSD | Accounting errors | Verify canonical address |
|
|
23
|
+
| Low decimals | USDC (6), GUSD (2) | Precision loss | Handle decimals explicitly |
|
|
24
|
+
| High decimals | YAM-V2 (24) | Overflow risk | Check bounds |
|
|
25
|
+
| Approval race | USDT, KNC | Front-running | Set to 0 first |
|
|
26
|
+
| Revert on zero | LEND | Unexpected reverts | Check amount > 0 |
|
|
27
|
+
| Non-string metadata | MKR | Type errors | Handle bytes32 |
|
|
28
|
+
| ERC777 hooks | imBTC | Reentrancy | Treat as untrusted |
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Detailed Patterns
|
|
33
|
+
|
|
34
|
+
### 1. Missing Return Values
|
|
35
|
+
|
|
36
|
+
**Tokens:** USDT, BNB, OMG, BADGER
|
|
37
|
+
|
|
38
|
+
**Problem:**
|
|
39
|
+
```solidity
|
|
40
|
+
// Returns nothing, but ERC20 interface expects bool
|
|
41
|
+
function transfer(address to, uint value) public {
|
|
42
|
+
// ... no return statement
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Exploit:**
|
|
47
|
+
```solidity
|
|
48
|
+
// This compiles but fails at runtime for USDT
|
|
49
|
+
IERC20(usdt).transfer(to, amount); // Reverts: expected return data
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Fix:**
|
|
53
|
+
```solidity
|
|
54
|
+
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
55
|
+
using SafeERC20 for IERC20;
|
|
56
|
+
|
|
57
|
+
IERC20(usdt).safeTransfer(to, amount); // Works
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
### 2. Fee on Transfer
|
|
63
|
+
|
|
64
|
+
**Tokens:** STA, PAXG, SAFEMOON, most "reflection" tokens
|
|
65
|
+
|
|
66
|
+
**Problem:**
|
|
67
|
+
```solidity
|
|
68
|
+
function transfer(address to, uint value) public returns (bool) {
|
|
69
|
+
uint fee = value * feePercent / 100;
|
|
70
|
+
_transfer(msg.sender, feeCollector, fee);
|
|
71
|
+
_transfer(msg.sender, to, value - fee);
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Exploit:**
|
|
77
|
+
```solidity
|
|
78
|
+
// User deposits 100, protocol credits 100
|
|
79
|
+
// But only 98 actually arrived (2% fee)
|
|
80
|
+
// User can withdraw 100, stealing 2 from protocol
|
|
81
|
+
function deposit(uint amount) external {
|
|
82
|
+
token.transferFrom(msg.sender, address(this), amount);
|
|
83
|
+
balances[msg.sender] += amount; // WRONG!
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Fix:**
|
|
88
|
+
```solidity
|
|
89
|
+
function deposit(uint amount) external {
|
|
90
|
+
uint balanceBefore = token.balanceOf(address(this));
|
|
91
|
+
token.transferFrom(msg.sender, address(this), amount);
|
|
92
|
+
uint received = token.balanceOf(address(this)) - balanceBefore;
|
|
93
|
+
balances[msg.sender] += received; // Credit actual amount
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
### 3. Rebasing Tokens
|
|
100
|
+
|
|
101
|
+
**Tokens:** AMPL, stETH (rebases), OHM, AAVE aTokens
|
|
102
|
+
|
|
103
|
+
**Problem:**
|
|
104
|
+
```solidity
|
|
105
|
+
// Balance changes without any transfers
|
|
106
|
+
function rebase(int supplyDelta) external {
|
|
107
|
+
totalSupply = totalSupply + supplyDelta;
|
|
108
|
+
// All balances scale proportionally
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Exploit:**
|
|
113
|
+
```solidity
|
|
114
|
+
// Protocol caches balance
|
|
115
|
+
uint cachedBalance = steth.balanceOf(address(this));
|
|
116
|
+
|
|
117
|
+
// ... time passes, rebase happens ...
|
|
118
|
+
|
|
119
|
+
// Cached balance is now wrong
|
|
120
|
+
// Can lead to accounting errors, insolvency
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Fix:**
|
|
124
|
+
- Don't cache balances
|
|
125
|
+
- Use wrapper tokens (wstETH instead of stETH)
|
|
126
|
+
- Track shares, not absolute amounts
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
### 4. Pausable Tokens
|
|
131
|
+
|
|
132
|
+
**Tokens:** BNB, ZIL, USDC (admin can pause)
|
|
133
|
+
|
|
134
|
+
**Problem:**
|
|
135
|
+
```solidity
|
|
136
|
+
modifier whenNotPaused() {
|
|
137
|
+
require(!paused, "Paused");
|
|
138
|
+
_;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function transfer(address to, uint value) public whenNotPaused {
|
|
142
|
+
// ...
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Impact:**
|
|
147
|
+
- Protocol can be DOS'd if token pauses
|
|
148
|
+
- User funds stuck
|
|
149
|
+
- Liquidations fail
|
|
150
|
+
|
|
151
|
+
**Fix:**
|
|
152
|
+
- Have emergency withdrawal that doesn't rely on transfers
|
|
153
|
+
- Document risk to users
|
|
154
|
+
- Consider timelock on protocol side
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
### 5. Blocklist/Blacklist Tokens
|
|
159
|
+
|
|
160
|
+
**Tokens:** USDC, USDT (compliance blacklists)
|
|
161
|
+
|
|
162
|
+
**Problem:**
|
|
163
|
+
```solidity
|
|
164
|
+
function transfer(address to, uint value) public {
|
|
165
|
+
require(!blacklisted[msg.sender], "Blacklisted");
|
|
166
|
+
require(!blacklisted[to], "Blacklisted");
|
|
167
|
+
// ...
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**Impact:**
|
|
172
|
+
- Blacklisted users can't withdraw
|
|
173
|
+
- Protocol holding funds for blacklisted user is stuck
|
|
174
|
+
|
|
175
|
+
**Fix:**
|
|
176
|
+
- Document risk
|
|
177
|
+
- Consider allowing admin rescue to different address
|
|
178
|
+
- Use wrapper tokens where possible
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
### 6. Upgradeable Tokens
|
|
183
|
+
|
|
184
|
+
**Tokens:** USDC, USDT, many newer tokens
|
|
185
|
+
|
|
186
|
+
**Problem:**
|
|
187
|
+
- Token logic can change after integration
|
|
188
|
+
- New fee mechanisms could be added
|
|
189
|
+
- Pausability could be added
|
|
190
|
+
|
|
191
|
+
**Impact:**
|
|
192
|
+
- Your integration assumptions may break post-upgrade
|
|
193
|
+
|
|
194
|
+
**Fix:**
|
|
195
|
+
- Monitor token upgrades
|
|
196
|
+
- Have upgrade handlers in your protocol
|
|
197
|
+
- Test against upgraded implementations
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
### 7. Flash Mintable Tokens
|
|
202
|
+
|
|
203
|
+
**Tokens:** DAI (flash mint), some wrapped tokens
|
|
204
|
+
|
|
205
|
+
**Problem:**
|
|
206
|
+
```solidity
|
|
207
|
+
function flashLoan(uint amount) external {
|
|
208
|
+
_mint(msg.sender, amount);
|
|
209
|
+
IFlashBorrower(msg.sender).onFlashLoan(amount);
|
|
210
|
+
_burn(msg.sender, amount);
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**Impact:**
|
|
215
|
+
- Attacker can temporarily have infinite tokens
|
|
216
|
+
- Breaks governance (voting with flash minted tokens)
|
|
217
|
+
- Price manipulation
|
|
218
|
+
|
|
219
|
+
**Fix:**
|
|
220
|
+
- Use TWAP for prices
|
|
221
|
+
- Snapshot voting tokens before proposals
|
|
222
|
+
- Check totalSupply before/after for sanity
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
### 8. Approval Race Condition
|
|
227
|
+
|
|
228
|
+
**Tokens:** USDT requires setting to 0 first
|
|
229
|
+
|
|
230
|
+
**Problem:**
|
|
231
|
+
```solidity
|
|
232
|
+
// USDT's approve reverts if allowance > 0 and newValue > 0
|
|
233
|
+
function approve(address spender, uint value) public {
|
|
234
|
+
require(value == 0 || allowance[msg.sender][spender] == 0);
|
|
235
|
+
// ...
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**Fix:**
|
|
240
|
+
```solidity
|
|
241
|
+
// Always set to 0 first
|
|
242
|
+
token.approve(spender, 0);
|
|
243
|
+
token.approve(spender, newAmount);
|
|
244
|
+
|
|
245
|
+
// Or use SafeERC20
|
|
246
|
+
token.safeIncreaseAllowance(spender, amount);
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
### 9. ERC777 Hooks (Reentrancy)
|
|
252
|
+
|
|
253
|
+
**Tokens:** imBTC, any ERC777
|
|
254
|
+
|
|
255
|
+
**Problem:**
|
|
256
|
+
```solidity
|
|
257
|
+
// ERC777 calls hooks on sender and receiver
|
|
258
|
+
function _send(address from, address to, uint256 amount) internal {
|
|
259
|
+
_callTokensToSend(from, to, amount); // HOOK - reentrancy!
|
|
260
|
+
_transfer(from, to, amount);
|
|
261
|
+
_callTokensReceived(from, to, amount); // HOOK - reentrancy!
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**Impact:**
|
|
266
|
+
- Reentrancy even without ETH transfers
|
|
267
|
+
- imBTC was exploited via this
|
|
268
|
+
|
|
269
|
+
**Fix:**
|
|
270
|
+
- Treat ERC777 transfers as untrusted external calls
|
|
271
|
+
- Apply CEI pattern
|
|
272
|
+
- Use ReentrancyGuard
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Integration Checklist
|
|
277
|
+
|
|
278
|
+
When integrating any token:
|
|
279
|
+
|
|
280
|
+
- [ ] Check for missing return values (SafeERC20)
|
|
281
|
+
- [ ] Check for fee-on-transfer (measure actual received)
|
|
282
|
+
- [ ] Check for rebasing (don't cache balances)
|
|
283
|
+
- [ ] Check for pausability (emergency paths)
|
|
284
|
+
- [ ] Check for blocklists (document risk)
|
|
285
|
+
- [ ] Check for upgradeability (monitor changes)
|
|
286
|
+
- [ ] Check for flash minting (use TWAP)
|
|
287
|
+
- [ ] Check for ERC777 hooks (reentrancy guard)
|
|
288
|
+
- [ ] Check decimals (6, 8, 18, 24 all exist)
|
|
289
|
+
- [ ] Check approval behavior (set to 0 first for USDT)
|
|
290
|
+
|
|
291
|
+
## Resources
|
|
292
|
+
|
|
293
|
+
- **weird-erc20:** <https://github.com/d-xo/weird-erc20>
|
|
294
|
+
- **Token Integration Checklist:** <https://github.com/crytic/building-secure-contracts/blob/master/development-guidelines/token_integration.md>
|