example-fork-detection
TEMPLATE — replace with the description of your rule. Should activate on the specific code patterns your fork has. Activate on…
Detect fee-on-transfer / deflationary / rebasing token accounting bugs — crediting the *passed amount* instead of the measured balance delta. Activate whenever code calls transfer/transferFrom and then credits, mints shares for, or records the literal amount argument, in
Claude Code
$ npx -y skills add omermaksutii/RugProof --skill fee-on-transfer --agent claude-code
Codex
$ npx skills add omermaksutii/RugProof --skill fee-on-transfer -a codex -y
Cursor
$ npx skills add omermaksutii/RugProof --skill fee-on-transfer -a cursor -y
Gemini CLI
$ npx skills add omermaksutii/RugProof --skill fee-on-transfer -a gemini-cli -y
GitHub Copilot
$ npx skills add omermaksutii/RugProof --skill fee-on-transfer -a github-copilot -y
OpenCode
$ npx skills add omermaksutii/RugProof --skill fee-on-transfer -a opencode -y
Windsurf
$ npx skills add omermaksutii/RugProof --skill fee-on-transfer -a windsurf -y
name: fee-on-transfer description: Detect fee-on-transfer / deflationary / rebasing token accounting bugs — crediting the *passed amount* instead of the measured balance delta. Activate whenever code calls transfer/transferFrom and then credits, mints shares for, or records the literal amount argument, in deposits, AMM swaps, lending collateral, vaults, staking, or bridges — without measuring balanceAfter - balanceBefore.
Trigger on any of:
function deposit(uint256 amount) external {
token.transferFrom(msg.sender, address(this), amount);
balanceOf[msg.sender] += amount; // ← amount, but FoT token delivered less
totalDeposited += amount;
}**Signal:** the contract believes it holds more than it does. On a fee-on-transfer token (USDT with fee switch enabled, PAXG, many meme tokens), `totalDeposited` exceeds the real balance — last withdrawers are insolvent, or an attacker mints excess shares/LP.
Router records input `amountIn` into reserves but the pair received `amountIn - fee`. `k` invariant is computed on phantom reserves, mispricing the swap and letting an attacker drain the shortfall over time (the Uniswap-V2-style "supports FoT" `*SupportingFeeOnTransferTokens` functions exist precisely because of this).
uint256 shares = amount * totalShares / totalAssets; // totalAssets from stored var
**Signal:** stored `totalAssets` vs live `token.balanceOf(this)` diverge as a positive-rebasing token (stETH, aTokens, OHM) grows. Crediting against the stale stored figure under- or over-mints shares; an attacker times deposits around rebase.
Lock `amount` on chain A, emit message, mint `amount` on chain B — but only `amount - fee` was locked. Repeated deposits accumulate an unbacked mint surplus that can be withdrawn on A by honest users, draining the lockbox.
| Pattern | Severity | Notes | |---|---|---| | Bridge mints unbacked surplus | **Critical** | Cross-chain insolvency | | Vault/LP shares minted on phantom amount | **High** | Direct over-mint, fund loss | | AMM reserve recorded as passed amount | **High** | Mispricing, drainable shortfall | | Lending collateral over-credited | **High** | Under-collateralized borrow | | Rebasing stored-vs-live `totalAssets` drift | **Medium** | Timing-dependent | | FoT token on a hard whitelist of non-FoT tokens | **Info** | Surface closed by policy |
1. **Measure the delta:** `uint256 before = token.balanceOf(address(this)); token.transferFrom(...); uint256 received = token.balanceOf(address(this)) - before;` and credit `received`. 2. **Reject FoT on entry** — if `received != amount`, revert (OpenZeppelin `SafeERC20` does not do this for you). 3. **Use live balance, not stored totals**, for rebasing tokens, or wrap them (wstETH) so balances are non-rebasing. 4. **Token allowlist** for assets known not to take a transfer fee; document the assumption. 5. **On bridges**, lock the *measured* received amount and mint exactly that.
Rugproof your code before someone else does. 🌐 Live site: omermaksutii.github.io/RugProof 📦 Latest: v1.0.0 — 45 commands · 23 agents · 45 skills · 13 MCP servers · tested, offline-first, with rule packs, a benchmark, non-EVM coverage, and post-deploy
TEMPLATE — replace with the description of your rule. Should activate on the specific code patterns your fork has. Activate on…
Detect unsafe assumptions about Solady's gas-optimized ERC20/ERC2612 permit and DN404 metadata. Solady's ERC20 uses custom…
Detect front-runnable ownership initialization in Solady Ownable / OwnableRoles. Solady's `_initializeOwner` is a guarded…
Detect Solady SafeTransferLib calls that assume the token has code. SafeTransferLib.safeTransfer/safeTransferFrom/safeApprove…
Detect Uniswap V4 hooks that fail to settle currency deltas with the PoolManager. Every credit/debit a hook creates…
Detect Uniswap V4 hooks whose address-encoded permission flags don't match the callbacks the hook actually implements. In V4 the…
Use for authorized security assessment of REST, GraphQL, WebSocket, or SOAP APIs, including discovery, authentication,…
Extract and decrypt Windows DPAPI-protected secrets (Credential Manager, browser logins/cookies, Wi-Fi credentials, KeePass keys)…
Clean and finalize authorized natural-language text intended for readers by auditing suspicious invisible Unicode and rewriting…