Reproduced Exploit
Arrakis V1 G-UNI — Uniswap V3 spot-priced `mint`/`burn` sandwich (typed reconstruction)
Arrakis V1 (G-UNI) is a shared Uniswap V3 LP token. mint() and burn() size deposits and redemptions from getUnderlyingBalances(), which reads the pool's instantaneous slot0() sqrt price. The contract's TWAP / slippage guard wraps only the manager rebalance() path.
Loss
2.941350352900037140 WETH (~$7,170) from the legacy G-UNI ENS/WETH vault output.txt
Chain
Ethereum
Category
defi
Date
Aug 2026
Source
DeFiHackLabs
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. Exploit reproduction, trace data, and analysis adapted from DeFiHackLabs by SunWeb3Sec — an open registry of reproduced on-chain exploits. Standalone Foundry PoC and full write-up: 2026-08-ArrakisGUNI_exp in the
evm-hack-registrymirror. Upstream DeFiHackLabs PoC:src/test/…/ArrakisGUNI_exp.sol.
Vulnerability classes: vuln/defi/sandwich-attack · vuln/oracle/spot-price · vuln/oracle/missing-circuit-breaker
Reproduction: the PoC compiles & runs in an isolated Foundry project at this project folder. Full verbose trace: output.txt. Source test: test/ArrakisGUNI_exp.sol. Sibling exact-source writeup of the same incident:
2026-08-ArrakisFinance_exp(verified ArrakisVaultV1 / Uni V3 / Morpho sources live there).
Key info#
| Loss | 2.941350352900037140 WETH (~$7,170) from the legacy G-UNI ENS/WETH vault output.txt |
| Vulnerable contract | G-UNI ENS/WETH vault proxy 0x7c687f775A3b73BBAb0E15832F24caaB5D53bDDe |
| Implementation | ArrakisVaultV1 0xd68b055fb444D136e3aC4df023f4C42334F06395 |
| Attacker EOA | 0xa3B096e4df1247794599a37Af8F5b8CB05D5EB44 |
| Attacker contract | 0x028d9C17B1a097e7e115A6400203df86339BAf4a (unverified; not replayed here) |
| Attack tx | 0x6ae3af4b2f25a56594de99cfb31369150dd9ac059c49efe04b9e3e0163dbc672 — block 25,817,966 |
| Chain / block / date | Ethereum / fork 25,817,965 / 2026-08-23 |
| Compiler | Solidity v0.8.4, optimizer 1 run (ArrakisVaultV1) |
| Bug class | Permissionless mint/burn value the Uni V3 position off live pool.slot0(); TWAP/deviation wraps only keeper rebalance() |
This folder is the typed-call reconstruction: ordinary IArrakisVaultV1.mint / burn / IUniV3Pool.swap / Morpho flashLoan with the exact on-chain amounts. No bytecode blob, no redeploy of 0x028d9C17…. Arrakis Pro is unaffected.
TL;DR#
Arrakis V1 (G-UNI) is a shared Uniswap V3 LP token. mint() and burn() size deposits and redemptions from getUnderlyingBalances(), which reads the pool's instantaneous slot0() sqrt price. The contract's TWAP / slippage guard wraps only the manager rebalance() path.
The attacker, in one Morpho flash loan of 1,800 WETH:
- Swaps 150 WETH into the ENS/WETH 0.3% pool down to the min-price bound (~145.42 WETH in, ~13,292.5 ENS out) — tick walks below the vault range.
vault.mint(4_486.619135659964587643, this)— deposits ~1,253.39 WETH + ~13,159.59 ENS priced at the skewed spot.- Swaps ~132.93 ENS back (partial tick restore; fees accrue on the now-larger position).
vault.burnthe same shares — redeems ~1,248.41 WETH + ~13,283.30 ENS (WETH-poorer, ENS-richer).- Sells all ENS back to WETH, repays Morpho. Surplus 2.9414 WETH.
Background#
ArrakisVaultV1 (2021, formerly Gelato Uniswap / G-UNI) wraps a single Uni V3 position as an ERC-20. Depositors call mint(mintAmount, receiver); burn returns a pro-rata slice of in-range liquidity, uncollected fees, and idle token0/token1.
This ENS–WETH vault is an EIP-173 proxy onto 0xd68b055f…. Manager and upgrade roles were later renounced. Dust remained — enough for a flash-loan sandwich.
token0 = WETH, token1 = ENS. Vault range at the attack: ticks 32000–60000. Dumping WETH pushes spot below lowerTick, so the position is 100% WETH; idle ENS is still counted in getUnderlyingBalances, so a minter must deposit ENS that never enters the Uni V3 mint.
The vulnerable code#
Verified ArrakisVaultV1 (quoted from the sibling registry sources; same bytecode):
function mint(uint256 mintAmount, address receiver)
external nonReentrant
returns (uint256 amount0, uint256 amount1, uint128 liquidityMinted)
{
require(mintAmount > 0, "mint 0");
uint256 totalSupply = totalSupply();
(uint160 sqrtRatioX96, , , , , , ) = pool.slot0(); // live spot
if (totalSupply > 0) {
(uint256 amount0Current, uint256 amount1Current) = getUnderlyingBalances();
amount0 = FullMath.mulDivRoundingUp(amount0Current, mintAmount, totalSupply);
amount1 = FullMath.mulDivRoundingUp(amount1Current, mintAmount, totalSupply);
}
// transferFrom amount0/amount1, then pool.mint using the same sqrtRatioX96
}
function getUnderlyingBalances() public view returns (uint256 amount0Current, uint256 amount1Current) {
(uint160 sqrtRatioX96, int24 tick, , , , , ) = pool.slot0();
return _getUnderlyingBalances(sqrtRatioX96, tick);
}
_checkSlippage / pool.observe(gelatoSlippageInterval) is only called from Gelato rebalance(). User mint/burn have no TWAP, no deviation band, no "same-tx mint+burn" lock.
Root cause#
mint and burn in the same transaction do not see "the same" pool if the caller can move slot0 around the vault ticks first. Out-of-range mint issues shares against a 100% WETH position plus idle ENS; restoring the tick puts the (now larger) position in range, accrues the restoring swap's fees, and burn pays a richer mix.
No privileged role is required. Morpho flashLoan + Uni V3 swap + vault mint/burn are all public.
Preconditions#
- Vault still has idle inventory / in-range liquidity worth sandwiching (~dust, but 2.94 WETH).
- Attacker can move the ENS/WETH 0.3% pool across the vault's tick range inside one tx (1,800 WETH flash is ample).
restrictedMintToggleis not locking mint to the (renounced) manager.
Attack walkthrough (on-chain amounts from the PoC header)#
| # | Step | Amounts |
|---|---|---|
| 1 | Morpho flashLoan WETH | 1,800 WETH |
| 2 | Uni V3 swap zeroForOne to MIN_SQRT | 150 WETH in bound; ~145.42 WETH actually swapped, ~13,292.5 ENS out |
| 3 | vault.mint(4486619135659964587643, this) | ~1,253.39 WETH + ~13,159.59 ENS in |
| 4 | Uni V3 swap oneForZero | 132.925127733721320656 ENS in (partial restore) |
| 5 | vault.burn same shares | ~1,248.41 WETH + ~13,283.30 ENS out |
| 6 | Sell remaining ENS → WETH, repay Morpho | surplus 2.941350352900037140 WETH |
The live attack contract skimmed a fixed 0.05 ETH builder tip; this PoC measures the raw WETH surplus and omits the skim.
attacker WETH surplus: 2.941350352900037140
[PASS] testExploit()
Diagrams#
Remediation#
- Snapshot valuation at the start of the tx (or use a manipulation-resistant TWAP) for both
mintandburn. Do not readpool.slot0()to size a share issue the same account can sandwich. - Extend
_checkSlippagetomint/burn, not onlyrebalance(). - Reject mint+burn in the same transaction from the same address, or lock newly minted shares for N blocks.
- Deprecate leftover V1 vaults (Arrakis already moved to Arrakis Pro); withdraw remaining TVL.
How to reproduce#
_shared/run_poc.sh 2026-08-ArrakisGUNI_exp --mt testExploit -vvvvv
Expected: [PASS] testExploit() with attacker WETH surplus: 2.941350352900037140.
Reference: https://x.com/SlowMist_Team/status/2091738634210996429
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 2026-08-ArrakisGUNI_exp (evm-hack-registry mirror).
- Upstream DeFiHackLabs PoC:
ArrakisGUNI_exp.sol. - Attack transaction: view on explorer.
Alerts & third-party analyses
- Original alert / thread: post on X.
- DeFiHackLabs incident explorer: search "Arrakis V1 G-UNI".
- Web3Sec X hacked database: search.
- Rekt leaderboard: search.
- Solodit incident search: search.
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.