ERC20 Violations Exploit Generator
Sigvex exploit generator that validates ERC20 standard violations by simulating non-standard token behaviors including missing return values, fee-on-transfer tokens, and rebase tokens.
ERC20 Violations Exploit Generator
Overview
The ERC20 violations exploit generator validates findings from the erc20_violation, token_standard, and fee_on_transfer detectors by analyzing how the contract’s token integration logic handles non-standard ERC20 token behaviors. The generator classifies the specific violation type from the finding description and produces a targeted proof-of-concept for each.
ERC20 non-compliance affects a large subset of production tokens. USDT (Tether) omits return values from transfer and transferFrom. SAFEMOON and many DeFi tokens take a percentage fee on each transfer. AMPL and stETH are rebase tokens whose balances change without explicit transfers. Each of these deviations causes integration failures or accounting exploits in protocols that assume standard ERC20 behavior.
Note: Exploit generation in Sigvex is for vulnerability validation purposes only.
Attack Scenario
Missing return value (USDT-style):
- The target protocol calls
token.transferFrom(msg.sender, address(this), amount)and wraps it inrequire(...). - The integration is deployed with USDT, which has no return value.
- The
requirestatement receives empty returndata and interprets it asfalse— the deposit reverts. - Alternatively, if the protocol omits the
require, the transfer may silently fail and the user is credited tokens they did not actually send.
Fee-on-transfer token:
- A lending protocol records
balances[msg.sender] += amountafter a deposit call. - The token deducts a 2% fee during
transferFrom. The protocol receives 980 tokens but records 1000. - Later withdrawals drain 1000 tokens from the pool, creating a 20-token shortfall that accumulates per deposit.
- Eventually, the last depositors cannot withdraw because the pool is insolvent.
Rebase token:
- A protocol tracks shares using a balance snapshot at deposit time.
- After a positive rebase (stETH yield accrual or AMPL supply expansion), all token balances increase proportionally.
- The protocol’s stored
depositedAmount[user]no longer reflects actual current value. - Users can withdraw more than they deposited, or the accounting becomes permanently inconsistent.
Exploit Mechanics
The generator checks the finding’s detector and description to classify the violation, then simulates a concrete token interaction:
| Violation type | Trigger condition | Simulated impact |
|---|---|---|
missing_return_value | Description contains “return” or “bool” | Silent transfer failure, false credit |
fee_on_transfer | Description contains “fee” or “transfer” | 2% fee: 1000 deposited, 980 received, 20-token shortfall |
rebase_token | Description contains “rebase” | Balance drift after supply adjustment |
pausable_token | Description contains “pause” | All transfers blocked by owner |
non_standard_decimals | Description contains “decimal” | Calculation mismatch |
general_erc20_violation | Other | Integration assumption failure |
The fee-on-transfer simulation computes:
transfer_amount = 1000 tokens (1000 * 1e18)
fee_amount = transfer_amount * 2 / 100 = 20 tokens
actual_received = 980 tokens
Estimated gas: 75,000. The confidence level defaults to the finding’s own confidence since the generator relies on bytecode-level detection rather than live VM execution.
The generated PoC includes:
NonStandardToken: No return value on transfer/transferFromFeeOnTransferToken: 2% fee deducted on every transferRebaseToken: Balance changes viarebase(newTotalSupply)SecureTokenIntegration: Safe deposit usingbalanceAfter - balanceBeforeaccounting
// SECURE: Check actual received amount
function depositFeeToken(IERC20 token, uint256 amount) external {
uint256 balanceBefore = token.balanceOf(address(this));
safeTransferFrom(token, msg.sender, address(this), amount);
uint256 balanceAfter = token.balanceOf(address(this));
uint256 actualReceived = balanceAfter - balanceBefore;
// Credit actual amount received, not specified amount
deposits[msg.sender] += actualReceived;
}
Remediation
- Detector: ERC20 Violations Detector
- Remediation Guide: ERC20 Remediation
Use OpenZeppelin’s SafeERC20 library for all token operations, and measure actual received amounts:
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract SecureVault {
using SafeERC20 for IERC20;
// SafeERC20 handles USDT-style missing return values
function deposit(IERC20 token, uint256 amount) external {
uint256 balanceBefore = token.balanceOf(address(this));
token.safeTransferFrom(msg.sender, address(this), amount);
uint256 actualReceived = token.balanceOf(address(this)) - balanceBefore;
// Credit actual amount to handle fee-on-transfer tokens
deposits[msg.sender] += actualReceived;
}
}
Known non-standard tokens that require special handling:
- USDT, BNB, OMG: No return value from
transfer/transferFrom - SAFEMOON, REFLECT, many DeFi tokens: Fee-on-transfer
- AMPL, stETH, aTokens: Rebasing or interest-bearing balances
- USDC, many stablecoins: Pausable transfers