example-fork-detection
TEMPLATE — replace with the description of your rule. Should activate on the specific code patterns your fork has. Activate on `<your trigger keywords or…
Detect vote-escrow (ve) governance manipulation — Curve veCRV, Velodrome/Aerodrome veNFT, Balancer veBAL, and gauge/bribe markets. Activate whenever code reads voting power from a lock balance, computes gauge weights, distributes bribes/incentives, snapshots votes, or lets locks
$ npx -y skills add omermaksutii/RugProof --skill ve-lock-governance --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/ve-lock-governanceContext preview
The summary Claude sees to decide when to auto-load this skill.
Detect vote-escrow (ve) governance manipulation — Curve veCRV, Velodrome/Aerodrome veNFT, Balancer veBAL, and gauge/bribe markets. Activate whenever code reads voting power from a lock balance, computes gauge weights, distributes bribes/incentives, snapshots votes, or lets locks
name: ve-lock-governance description: Detect vote-escrow (ve) governance manipulation — Curve veCRV, Velodrome/Aerodrome veNFT, Balancer veBAL, and gauge/bribe markets. Activate whenever code reads voting power from a lock balance, computes gauge weights, distributes bribes/incentives, snapshots votes, or lets locks be created/extended/merged/split/transferred — especially when vote weight is read live rather than at proposal-creation block.
Trigger on any of:
function castVote(uint256 proposalId, bool support) external {
uint256 weight = ve.balanceOf(msg.sender); // ← read NOW, not at proposal start
proposals[proposalId].votes[support] += weight;
}**Signal:** voting power read at vote time enables flash-lock: borrow, lock, vote, then exit if `withdraw` allowed same block, or vote with freshly minted power. Snapshot at proposal-creation block (`getPastVotes`).
Last-block vote stuffing before the weekly checkpoint redirects emissions. If gauge weights are read at the instant of `checkpoint_gauge` with no time-weighting, a single-block max vote captures a full epoch of emissions (Curve gauge-war griefing class). **Signal:** emission direction determined by instantaneous weight, not a bias-weighted moving average.
function merge(uint256 from, uint256 to) external { ... } // does it clear `voted[from]`?**Signal:** Velodrome-class finding — `split`/`merge` not resetting the per-NFT `voted` flag or per-epoch vote accounting lets one underlying balance vote twice in an epoch. Also check `transferFrom` mid-epoch carrying voting power to a non-voted recipient.
Linear-decay `bias - slope * (t - t0)` underflowing past expiry, or assuming `MAXTIME` lock when `unlock_time` was rounded down to the week. Off-by-one-week rounding inflates or deflates power at epoch boundaries.
Claiming bribes for an epoch you voted in, then re-voting next epoch with the same balance, or claiming on a transferred veNFT — verify bribe accounting is keyed to (tokenId, epoch) and frozen at the vote.
| Pattern | Severity | Notes | |---|---|---| | Flash-lock vote with same-block exit | **Critical** | Direct governance capture | | Live (un-snapshotted) vote weight | **High** | Borrowed-power voting | | merge/split/transfer double-vote | **High** | Velodrome-class veNFT bug | | Last-block gauge stuffing | **High** | Emission theft per epoch | | Decay/rounding precision at week boundary | **Medium** | Bounded mis-weighting | | Bribe claim re-use across epochs | **Medium** | Incentive leakage |
1. **Snapshot voting power** at proposal-creation block via `ERC20Votes.getPastVotes` / checkpoint history — never `balanceOf` at vote time. 2. **Time-bias gauge weights** (Curve's `points_weight` slope/bias with future-epoch bias) so a single block cannot dominate an epoch. 3. **Reset per-NFT vote state on `merge`/`split`/`transfer`** and forbid transfer of a veNFT that has voted in the current epoch (Velodrome `voted` lock). 4. **Key bribe accounting to (tokenId, epoch)** and freeze the balance used at vote time. 5. **Disallow withdraw before unlock** and round `unlock_time` consistently (floor to week) everywhere.
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
Repo: omermaksutii/RugProof
TEMPLATE — replace with the description of your rule. Should activate on the specific code patterns your fork has. Activate on `<your trigger keywords or…
Detect unsafe assumptions about Solady's gas-optimized ERC20/ERC2612 permit and DN404 metadata. Solady's ERC20 uses custom storage slots, returns bools via…
Detect front-runnable ownership initialization in Solady Ownable / OwnableRoles. Solady's `_initializeOwner` is a guarded one-time setter (it reverts with…
Detect Solady SafeTransferLib calls that assume the token has code. SafeTransferLib.safeTransfer/safeTransferFrom/safeApprove deliberately skip the EXTCODESIZE…
Detect Uniswap V4 hooks that fail to settle currency deltas with the PoolManager. Every credit/debit a hook creates (BeforeSwapDelta, afterSwap hookDelta,…
Detect Uniswap V4 hooks whose address-encoded permission flags don't match the callbacks the hook actually implements. In V4 the hook's permissions live in the…