Jump Farm Epoch Harvest
Exploit Transactions
0x6189ad07894507d15c5dff83f547294e72f18561dc5662a8113f7eb932a5b079Victim Addresses
0x05999eb831ae28ca920ce645a5164fbdb1d74fe9Ethereum0x1e5f868a297fa80e6a03df69bc3e21c8145fcbb4Ethereum0xab01b9419ed5b82c9342886206eacc5059268cb3EthereumLoss Breakdown
Similar Incidents
Floor DAO Stale Epoch Harvesting
48%HATE Rebase-Before-Burn Exploit
30%QUATERNION Pair-Rebase Accounting Drift Enables Permissionless Drain
29%SorraV2 staking withdraw bug enables repeated SOR reward drain
28%TheNFTV2 Stale Burn Approval
28%Public Curve Treasury Drain
28%Root Cause Analysis
Jump Farm Epoch Harvest
1. Incident Overview TL;DR
On Ethereum mainnet block 18070347, attacker EOA 0x6ce9fa08f139f5e48bc607845e57efe9aa34c9f6 used freshly deployed helper contract 0x154863eb71de4a34f88ea57450840eab1c71aba6 to execute exploit transaction 0x6189ad07894507d15c5dff83f547294e72f18561dc5662a8113f7eb932a5b079. The helper borrowed 15 WETH from Balancer, bought JUMP, repeatedly alternated Jump Farm stake and unstake(..., true) calls while the staking epoch was badly stale, then sold the inflated JUMP back to WETH and exited with profit.
The root cause is a permissionless stale-epoch catch-up path in Jump Farm Staking. Both stake() and unstake(..., true) call rebase() before moving balances, while rebase() advances only one overdue epoch per call. When many epochs are overdue, a flash-loaned attacker can repeatedly enter and exit within one transaction and harvest rewards that should have accrued only to long-lived stakers.
2. Key Background
Jump Farm uses rebasing sJUMP as the staked representation of JUMP. A rebase increases redeemable value by adjusting the rebasing token supply accounting rather than minting a separate claim token. As a result, a user that is staked during a rebase can later redeem more underlying JUMP.
The staking contract uses 8-hour epochs. If no one calls rebase() for a long period, epoch.end can lag far behind block.timestamp, leaving a backlog of overdue epochs.
Reward issuance is funded through the protocol treasury. Distributor.nextReward() is capped by Treasury.excessReserves(), so as long as treasury reserves remain positive, each successful overdue-epoch settlement can mint fresh JUMP into the staking system.
3. Vulnerability Analysis & Root Cause Summary
The bug is a protocol-level accounting failure in reward settlement, not a privileged compromise. Jump Farm exposed a public catch-up mechanism for overdue epochs but settled only one epoch per call. That design becomes exploitable because the same transaction can call back into the public staking entry points repeatedly. The vulnerable ordering is critical: stake() rebases before accepting fresh JUMP, and unstake(..., true) rebases before redeeming sJUMP. This lets transient capital appear staked for each overdue settlement step even though it was not continuously staked across the elapsed time. Every catch-up step also triggers Distributor::distribute(), which mints new JUMP through Treasury while reserves stay positive. The result is that one attacker can serialize many delayed reward settlements into one flash-loaned transaction and extract the backlog as immediate sellable value.
4. Detailed Root Cause Analysis
The verified staking code shows the exploitable ordering directly:
function stake(address _to, uint256 _amount) external {
rebase();
TOKEN.transferFrom(msg.sender, address(this), _amount);
sTOKEN.transfer(_to, _amount);
}
function unstake(address _to, uint256 _amount, bool _rebase) external {
if (_rebase) rebase();
sTOKEN.transferFrom(msg.sender, address(this), _amount);
TOKEN.transfer(_to, _amount);
}
function rebase() public {
if (epoch.end <= block.timestamp) {
sTOKEN.rebase(epoch.distribute, epoch.number);
epoch.end = epoch.end + epoch.length;
epoch.number++;
distributor.distribute();
...
}
}
This creates the broken invariant: an elapsed epoch reward should accrue only to stake that remained present through that epoch, but Jump Farm instead let a caller repeatedly become the current staker immediately before each overdue epoch settlement. Because rebase() advances only one epoch per call, the contract remains callable again when many epochs are overdue.
Reward monetization is also explicit in verified code:
function distribute() external {
require(msg.sender == staking, "Only staking");
treasury.mint(staking, nextReward());
}
function nextReward() public view returns (uint256 _reward) {
uint256 excessReserves = treasury.excessReserves();
_reward = nextRewardAt(rate);
if (excessReserves < _reward) _reward = excessReserves;
}
function excessReserves() external view returns (uint256 value_) {
uint256 _balance = IERC20(WETH).balanceOf(address(this));
uint256 _value = (_balance * 1e9) / BACKING;
if (IERC20(TOKEN).totalSupply() > _value) return 0;
return (_value - IERC20(TOKEN).totalSupply());
}
The exploit trace confirms the mechanism. Inside the single seed transaction, the helper first receives a Balancer flash loan, swaps WETH for JUMP, then alternates Staking::stake and Staking::unstake(..., true). The trace repeatedly shows emit LogSupply(epoch: 20 ...), Distributor::distribute(), and Treasury::mint(...), then continues through overdue epochs up to 81. That is direct evidence that the attacker consumed a large stale backlog one epoch at a time inside one transaction.
The submitted profit predicate is deterministic. The native balance diff for the attacker EOA shows before_wei = 26741732613979005590, after_wei = 28932182029895228702, and delta_wei = 2190449415916223112. The helper realized 2408896405802220360 wei gross WETH/ETH before gas, and the net difference is consistent with 218446989885997248 wei of fees/gas.
5. Adversary Flow Analysis
The attack had two transactions:
-
0x55a4858c663267dd5e2ed6a5987873e6d5e22cb86955735bbbd9e0a09fe97508The attacker EOA deployed helper contract0x154863eb71de4a34f88ea57450840eab1c71aba6. -
0x6189ad07894507d15c5dff83f547294e72f18561dc5662a8113f7eb932a5b079The helper executed the exploit end to end.
Representative trace sequence:
BalancerVault::flashLoan(..., [WETH], [15 ether], ...)
UniswapV2Router::swapExactTokensForTokens(15 ether, 0, [WETH, JUMP], helper, ...)
Staking::stake(helper, 277039687340311)
emit LogSupply(epoch: 20, ...)
Distributor::distribute()
Treasury::mint(Staking, 12081089504435)
Staking::unstake(helper, 277039687340311, true)
emit LogSupply(epoch: 21, ...)
...
WETH9::withdraw(314409646579908154)
Operationally, the helper used flash-loaned WETH to buy JUMP, staked JUMP, let rebase() settle one overdue epoch, unstaked with _rebase=true to settle the next overdue epoch while still holding sJUMP, then repeated. Each pass made the redeemed JUMP balance larger. Once the backlog was exhausted, the helper sold JUMP back to WETH, repaid Balancer, withdrew residual WETH to ETH, and sent the proceeds to the originating EOA.
6. Impact & Losses
The immediate measurable loss was 2408896405802220360 wei of WETH-equivalent value, or 2.408896405802220360 WETH, extracted from the exploit path's market exit. This value came from monetizing Treasury-backed inflation that should have been distributed over time to legitimate stakers rather than captured instantly by transient capital.
The exploit also caused protocol state distortion beyond the direct realized profit. Repeated backlog settlement increased JUMP supply through the Distributor/Treasury path and exhausted delayed epoch rewards in a way that violated the intended time-weighted staking model.
7. References
- Seed exploit transaction:
0x6189ad07894507d15c5dff83f547294e72f18561dc5662a8113f7eb932a5b079 - Helper deployment transaction:
0x55a4858c663267dd5e2ed6a5987873e6d5e22cb86955735bbbd9e0a09fe97508 - Jump Farm Staking:
0x05999eb831ae28ca920ce645a5164fbdb1d74fe9 - Jump Farm Distributor:
0x1e5f868a297fa80e6a03df69bc3e21c8145fcbb4 - Jump Farm Treasury:
0xab01b9419ed5b82c9342886206eacc5059268cb3 - Attacker EOA:
0x6ce9fa08f139f5e48bc607845e57efe9aa34c9f6 - Attacker helper:
0x154863eb71de4a34f88ea57450840eab1c71aba6 - Evidence set: exploit metadata, exploit trace, balance diff, verified victim contract source, and helper creation artifact under
/workspace/session/artifacts/collector/