Reproduced Exploit
veRAAC: a dust lock plus increase mints almost twice the voting power
veRAACToken.increase calls increaseLock, which already adds amount to the stored lock. It then computes the new bias from userLock.amount + amount. A 1 wei lock followed by an increase of 100 ether - 1 mints 200 ether - 1 of ve and voting power. Locking 100 RAAC in one call mints 100.
Loss
100 RAAC mints 199.999999999999999999 veRAAC and the same voting power
Chain
Ethereum
Category
logic
Date
Feb 2025
Source
AuditVault
EVM Playground
Source-level debugger — step opcodes and Solidity in sync
The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Balances (native / ERC-20 / NFT), Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.
Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 57208-attackers-can-double-voting-power-and-vetoken-amount-by-lock. Standalone Foundry PoC and full write-up: 57208-attackers-can-double-voting-power-and-vetoken-amount-by-lock_exp in the
evm-hack-registrymirror.
Vulnerability classes: vuln/logic/state-update
Key info#
| Field | Value |
|---|---|
| Loss | 100 RAAC mints 199.999999999999999999 veRAAC and the same voting power |
| Vulnerable contract | VeToken.increase |
| Attacker EOA | 0x0000000000000000000000000000000000057208 |
| Attack contract | Exploit |
| Attack tx | Exploit.run() |
| Chain / block / date | Ethereum model · block 0 · 2025-02 |
| Compiler | solc 0.8.27, cancun, via IR |
| Bug class | increase adds amount twice when it computes bias |
TL;DR#
veRAACToken.increase calls increaseLock, which already adds amount to the stored lock. It then computes the new bias from userLock.amount + amount. A 1 wei lock followed by an increase of 100 ether - 1 mints 200 ether - 1 of ve and voting power. Locking 100 RAAC in one call mints 100.
Background#
Codehawks contest on the February 2025 RAAC core contracts, reported by newspacexyz. Voting power and the ve mint are lockAmount * duration / MAX_LOCK_DURATION. At the maximum duration that ratio is 1. The PoC in the report shows alice at 100 ve and eve at 199.999999999999999999 ve for the same 100 RAAC.
The vulnerable code#
_increaseLock(msg.sender, amount);
uint256 userAmount = lockedAmount[msg.sender];
// @> VULN: userAmount already includes `amount`, and amount is added again
uint256 counted = userAmount + amount;
uint256 newPower = counted * MAX_LOCK_DURATION / MAX_LOCK_DURATION;
balanceOf[msg.sender] = newPower;
votingPower[msg.sender] = newPower;
_increaseLock is lockedAmount[user] += amount. After a 1 wei lock and an increase of 100 ether - 1, userAmount is 100 ether and counted is 200 ether - 1.
Root cause#
The lock manager and the bias update both add the same amount. The recommended diff in the report drops the extra + amount and passes userLock.amount alone into calculateAndUpdatePower.
Preconditions#
- The caller has 100 ether of RAAC approved to the locker.
MAX_LOCK_DURATIONis the duration used for both the dust lock and the increase, so the bias is 1 ve per 1 RAAC before the double count.increaseis only reachable after a lock exists, which is why the dust lock comes first.
Attack walkthrough#
lock(1, MAX_LOCK_DURATION)pulls 1 wei and mints 1 ve.increase(100 ether - 1)pulls the rest of the RAAC.increaseLockstores100 ether.countedadds100 ether - 1again.balanceOfandvotingPowerare set to200 ether - 1.- The control writes the lock once and the balance stays
100 ether.
Diagrams#
Impact#
The attacker deposits 100 RAAC and receives 200 ether - 1 wei of veRAAC, which is also the voting power. An honest lock of the same 100 RAAC receives 100 ve. The extra ve is unbacked voting power and an inflated locker balance. The control's increase sets the balance to lockedAmount and the caller holds exactly 100 ether of ve.
Remediation#
Pass userLock.amount into the bias update. Do not add amount again. The report's diff is that one-line change in veRAACToken.increase.
How to reproduce#
cd 57208-attackers-can-double-voting-power-and-vetoken-amount-by-lock_exp
forge test -vv
test_exploit_dust_lock_then_increase_mints_double_ve asserts 200 ether - 1 of voting power. test_control_increase_uses_the_lock_once asserts 100 ve. Captured output is output.txt.
Sources#
- AuditVault finding: https://github.com/Auditware/AuditVault/blob/main/findings/57208-attackers-can-double-voting-power-and-vetoken-amount-by-lock.md
- Code:
veRAACToken.increasein Cyfrin/2025-02-raac
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 57208-attackers-can-double-voting-power-and-vetoken-amount-by-lock_exp (evm-hack-registry mirror).
- AuditVault finding: 57208-attackers-can-double-voting-power-and-vetoken-amount-by-lock.
Alerts & third-party analyses
These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.