DexToken BEP20USDT pool drain from token-logic exploit
Exploit Transactions
0x96a955304fed48a8fbfb1396ec7658e7dc42b7c140298b80ce4206df34f40e8dVictim Addresses
0xabc6e5a63689b8542dbdc4b4f39a7e00d4ac30c8BSC0x35886c6d74aced4ed0fbe0b851806278384d9a76BSCLoss Breakdown
Similar Incidents
Public mint flaw drains USDT from c3b1 token pool
40%OLY staking/router reward-abuse drains BEP20USDT from staking flows
39%H2O helper-token reward drain from unauthorized claim loop
37%BSC staking pool reentrancy drain
35%BBX auto-burn sync flaw drains USDT from BBX pool
35%BSC WBNB allowance drain from unsafe spender approvals
35%Root Cause Analysis
DexToken BEP20USDT pool drain from token-logic exploit
1. Incident Overview TL;DR
On BSC (chainid 56), a single contract-creation transaction
0x96a955304fed48a8fbfb1396ec7658e7dc42b7c140298b80ce4206df34f40e8d
from unprivileged EOA 0x56b2d55457b31fb4b78ebddd6718ea2667804a06
deploys an adversary-controlled helper/orchestrator contract. During its
constructor execution, this helper abuses a broken
DexToken::transferFrom implementation to move essentially the entire
DexToken balance held by the DexToken contract itself
0xabc6e5a63689b8542dbdc4b4f39a7e00d4ac30c8 into an adversary swap
executor contract 0x0496824589cd3758119f74560e4fa970e6dff104 and into
the DexToken‑USDT Pancake pair
0x35886c6d74aced4ed0fbe0b851806278384d9a76.
The swap executor then repeatedly sells the stolen DexToken into the
DexToken‑USDT pair via PancakeRouter, draining exactly
13,038.598589899306674112 BEP20USDT from the pool. By the end of the
transaction, the attacker EOA holds 7,251.288075182757035057 USDT and
has paid 0.017506866 BNB in gas, while two additional addresses receive
the remaining drained USDT.
The root cause is a logic bug in DexToken’s transferFrom function
(implemented under Solidity ^0.7.6). The function calls
_transfer(sender, recipient, amount) without enforcing
allowance[sender][msg.sender] >= amount and then applies an unchecked
subtraction to _allowances[sender][msg.sender]. Under Solidity 0.7.6
(this code path does not use SafeMath), subtraction underflows silently
instead of reverting. As a result, any caller can transfer tokens from
any address, including the DexToken contract itself, as long as the
source address has sufficient balance.
2. Key Background
DexToken is an Ownable ERC20-style token deployed on BSC at
0xabc6e5a63689b8542dbdc4b4f39a7e00d4ac30c8. The verified source
(LW.sol) shows that it implements custom fee-on-transfer behavior, an
internal AMM helper _internalSwap, and an lpBurn(uint256) function
that interacts with a Pancake-style liquidity pool.
// DexToken (excerpt)
contract DexToken is LinkingTheWorld {
// ...
// 转账
function transfer(
address recipient,
uint256 amount
) public override returns (bool) {
_transfer(msg.sender, recipient, amount);
return true;
}
// 转账
function transferFrom(
address sender,
address recipient,
uint256 amount
) public override returns (bool) {
_transfer(sender, recipient, amount);
if (_allowances[sender][msg.sender] != MAX) {
_allowances[sender][msg.sender] =
_allowances[sender][msg.sender] -
amount;
}
return true;
}
// 内部转账方法
function _transfer(address from, address to, uint256 amount) private {
// 黑名单无法买卖和转账
if (_blackList[from] || _blackList[to]) {
require(false);
}
// 判断余额
uint256 balance = _balances[from];
require(balance >= amount, "balanceNotEnough");
// ... fee and swap logic elided ...
_tokenTransfer(from, to, amount, takeFee, isSell, isTransfer);
}
}
Snippet origin: DexToken verified source LW.sol for
0xabc6e5a63689b8542dbdc4b4f39a7e00d4ac30c8.
The DexToken contract stores balances in an internal _balances mapping
and allowances in _allowances. The constant MAX is used to represent
an “infinite” approval, but there is no explicit check that the current
allowance is at least the requested transfer amount before subtraction.
Because the contract is compiled with pragma solidity ^0.7.6 and does
not wrap the allowance subtraction in SafeMath, a subtraction that would
underflow simply wraps modulo 2^256 rather than reverting.
The DexToken‑USDT pool is a standard Pancake (Uniswap V2–style) pair
contract at 0x35886c6d74aced4ed0fbe0b851806278384d9a76. Its verified
source enforces the usual x*y = k invariant and a 0.25% fee.
interface IPancakePair {
event Swap(
address indexed sender,
uint amount0In,
uint amount1In,
uint amount0Out,
uint amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
function getReserves() external view returns (
uint112 reserve0,
uint112 reserve1,
uint32 blockTimestampLast
);
function swap(
uint amount0Out,
uint amount1Out,
address to,
bytes calldata data
) external;
}
Snippet origin: PancakePair flattened source for
0x35886c6d74aced4ed0fbe0b851806278384d9a76.
BEP20USDT is deployed at 0x55d398326f99059ff775485246999027b3197955.
The local verified source confirms it is a standard BEP20 implementation
with 18 decimals and standard allowance semantics.
contract BEP20USDT is Context, IBEP20, Ownable {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
uint8 public _decimals;
string public _symbol;
string public _name;
constructor() public {
_name = "Tether USD";
_symbol = "USDT";
_decimals = 18;
_totalSupply = 30000000000000000000000000;
_balances[msg.sender] = _totalSupply;
emit Transfer(address(0), msg.sender, _totalSupply);
}
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(
amount, "BEP20: transfer amount exceeds allowance"));
return true;
}
}
Snippet origin: BEP20USDT verified source for
0x55d398326f99059ff775485246999027b3197955.
The adversary-related cluster comprises:
- EOA
0x56b2d55457b31fb4b78ebddd6718ea2667804a06— the sender of the seed contract-creation transaction, which pays gas and ends the transaction holding 7,251.288075182757035057 USDT. - Helper/orchestrator contract
0xfe7E9C76affDBa7b7442adACa9C7c059ec3092FC— deployed by the seed transaction (metadata showsto=nulland non-empty runtime bytecode at this address), which orchestrates calls into DexToken and the swap executor. - Swap executor contract
0x0496824589cd3758119f74560e4fa970e6dff104— receives the stolen DexToken and acts as the caller to PancakeRouter in the exploit trace. Aneth_getCodequery returns non-empty bytecode embedding the DexToken, BEP20USDT, and PancakeRouter addresses.
Normal transaction histories for DexToken, the DexToken‑USDT pool, the
attacker EOA, the swap executor, and the two large USDT recipients were
fetched and reviewed via Etherscan API. These txlists confirm there are
no prior approval transactions from the DexToken contract to either the
helper or the swap executor, consistent with the claim that the exploit
relies solely on the broken transferFrom logic.
3. Vulnerability Analysis & Root Cause Summary
The core vulnerability is an incorrect implementation of
DexToken::transferFrom under Solidity ^0.7.6. Instead of enforcing the
standard ERC20 invariant that transferFrom succeeds only when
allowance[sender][caller] >= amount (unless an infinite approval is in
place), DexToken executes the internal transfer first and only then
applies an unchecked subtraction to the allowance. Because arithmetic in
Solidity 0.7.6 does not revert on underflow when used directly on
uint256, an attacker can call transferFrom with zero approval and
have the allowance underflow without error.
The relevant invariant can be stated as:
For all addresses s (source), c (caller), r (recipient) and all amounts a, if
transferFrom(s, r, a)executed by caller c returns success, then (1)a <= allowance[s][c]unlessallowance[s][c]equalsMAX(infinite approval), and (2)a <= balanceOf[s].
DexToken breaks this invariant at the following breakpoint:
function transferFrom(
address sender,
address recipient,
uint256 amount
) public override returns (bool) {
_transfer(sender, recipient, amount);
if (_allowances[sender][msg.sender] != MAX) {
_allowances[sender][msg.sender] =
_allowances[sender][msg.sender] -
amount;
}
return true;
}
There is no require enforcing allowance[sender][msg.sender] >= amount
before subtraction. Underflow wraps silently, and the actual transfer
succeeds as long as _balances[sender] >= amount at the time of
_transfer.
In the pre-state immediately before the exploit transaction (block 40,287,545), the on-chain artifacts show:
- DexToken contract balance (self-holding) at
0xabc6e5a63689b8542dbdc4b4f39a7e00d4ac30c8is1,000,015,840,000,000,000,000,000,000,000,000DexToken units. - DexToken‑USDT pair at
0x3588...9a76holds793,993,204,459,944,103,014,021,705DexToken and13,256,433,814,164,156,746,766BEP20USDT. - Attacker EOA
0x56b2...4a06holds 0 BEP20USDT and0.997500000000000001BNB.
These balances are taken directly from the seed transaction metadata and balance diff artifacts.
{
"erc20_balance_deltas": [
{
"token": "0xabc6e5a63689b8542dbdc4b4f39a7e00d4ac30c8",
"holder": "0xabc6e5a63689b8542dbdc4b4f39a7e00d4ac30c8",
"before": "115792089237316195423570985008687907853269984665640563583345235133956513969238",
"after": "115792089237316195423570985008687907853269983665624723583345235133956513969238",
"delta": "-1000015840000000000000000000000000"
},
{
"token": "0xabc6e5a63689b8542dbdc4b4f39a7e00d4ac30c8",
"holder": "0x0496824589cd3758119f74560e4fa970e6dff104",
"before": "0",
"after": "999952000000000000000000000000000",
"delta": "999952000000000000000000000000000"
},
{
"token": "0xabc6e5a63689b8542dbdc4b4f39a7e00d4ac30c8",
"holder": "0x35886c6d74aced4ed0fbe0b851806278384d9a76",
"before": "793993204459944103014021705",
"after": "48793993204459944103014021705",
"delta": "48000000000000000000000000000"
}
]
}
Snippet origin: seed transaction balance_diff.json for
0x96a9...40e8d on BSC.
The first line shows the DexToken contract’s own balance decreasing by
1,000,015,840,000,000,000,000,000,000,000,000 units. The next two
lines show that this exact amount is split into
999,952,000,000,000,000,000,000,000,000,000 DexToken transferred to the
swap executor 0x0496... and 48,000,000,000,000,000,000,000,000,000
DexToken transferred to the DexToken‑USDT pair.
The seed transaction trace corroborates that this movement is driven by a
call to DexToken::transferFrom with sender set to the DexToken
contract itself and recipient set to 0x0496...:
│ ├─ [65526] DexToken::transferFrom(DexToken: [0xABC6e5a63689b8542dbDC4b4f39a7e00d4AC30c8], 0x0496824589CD3758119F74560E4Fa970e6dff104, 1000000000000000000000000000000000 [1e33])
│ │ ├─ [151952] DexToken::transferFrom(0x0496824589CD3758119F74560E4Fa970e6dff104, PancakePair: [0x35886C6D74ACed4Ed0fbe0b851806278384D9A76], 800000000000000000000000000 [8e26])
│ │ ├─ [58568] DexToken::transferFrom(0x0496824589CD3758119F74560E4Fa970e6dff104, PancakePair: [0x35886C6D74ACed4Ed0fbe0b851806278384D9A76], 800000000000000000000000000 [8e26])
... (multiple repeated DexToken::transferFrom calls from 0x0496... to the pair)
Snippet origin: seed transaction trace.cast.log for
0x96a9...40e8d on BSC.
Because there is no approval from the DexToken contract to the helper or
swap executor in DexToken’s tx history, and because the DexToken
contract is not an EOA capable of signing approvals, the only viable
explanation for this movement is the broken transferFrom allowance
semantics. The adversary calls transferFrom targeting the token
contract’s own balance, and the function performs the transfer without
checking allowance, then underflows _allowances without reverting.
4. Detailed Root Cause Analysis
This section describes step-by-step how the vulnerability is turned into
an exploit in the concrete transaction
0x96a955304fed48a8fbfb1396ec7658e7dc42b7c140298b80ce4206df34f40e8d on
BSC.
4.1 Pre-State (σ_B) and Preconditions
Immediately before the seed transaction is included in block 40,287,545,
balance_diff.json and the associated metadata establish the following
pre-state σ_B:
- DexToken contract (
0xabc6...30c8) holds1,000,015,840,000,000,000,000,000,000,000,000DexToken. - DexToken‑USDT pair (
0x3588...9a76) holds793,993,204,459,944,103,014,021,705DexToken and13,256,433,814,164,156,746,766BEP20USDT. - Attacker EOA (
0x56b2...4a06) has 0 BEP20USDT and0.997500000000000001BNB. - No approvals have been granted from the DexToken contract address to the helper or swap executor addresses, as confirmed by the Etherscan txlist for DexToken.
These conditions are fully determined from on-chain data (RPC metadata and balance diffs). Any unprivileged adversary observing the chain can verify that such a pre-state exists.
4.2 Helper/Orchestrator Deployment
The seed transaction is a legacy (type-0) contract-creation transaction
with to=null, value=0, and gas parameters consistent with ordinary
BSC usage. Metadata for the transaction shows:
{
"result": {
"from": "0x56b2d55457b31fb4b78ebddd6718ea2667804a06",
"to": null,
"gas": "0x989680",
"gasPrice": "0xb2d05e00",
"value": "0x0",
"type": "0x0",
"chainId": "0x38"
}
}
Snippet origin: seed transaction metadata.json for
0x96a9...40e8d on BSC.
An eth_getCode call for the resulting contract address
0xfe7E9C76affDBa7b7442adACa9C7c059ec3092FC shows non-empty runtime
bytecode. Combined with the fact that this address is not present as a
destination in any prior transaction, we can deterministically identify
it as the helper/orchestrator deployed by the seed transaction.
eth_getCode for the sender 0x56b2...4a06 returns 0x, confirming it
is an EOA.
Within the reconstructed trace, the depth-1 frame corresponds to the
constructor of 0xfe7E9C76.... That constructor immediately executes a
sequence of calls into DexToken and the swap executor, which together
realize the exploit.
4.3 Theft of DexToken Contract Balance via Broken transferFrom
The first critical operation is the theft of the DexToken contract’s own balance. The trace clearly shows a call to
DexToken::transferFrom(
DexToken: [0xABC6e5a63689b8542dbDC4b4f39a7e00d4AC30c8],
0x0496824589CD3758119F74560E4Fa970e6dff104,
1000000000000000000000000000000000 [1e33]
)
This call uses sender = 0xabc6...30c8 (the DexToken contract itself),
recipient = 0x0496... (the swap executor), and
amount = 1e33 = 1,000,000,000,000,000,000,000,000,000,000,000 DexToken.
Given the exact pre-state balance of the DexToken contract, this amount
corresponds to “essentially all” of the DexToken balance held at the
contract address (the slight difference between 1e33 and
1,000,015,840,000,000,000,000,000,000,000,000 is accounted for by
additional movements into the DexToken‑USDT pair visible in
balance_diff.json).
Because transferFrom in DexToken:
- Does not check the allowance at all before calling
_transfer, and - Performs an unchecked subtraction of
_allowances[sender][msg.sender]under Solidity 0.7.6,
any caller can execute this function as long as _balances[sender]
contains enough tokens. The DexToken contract address holds a large
balance in the pre-state, so the helper/orchestrator’s call to
transferFrom succeeds with no prior approval.
balance_diff.json confirms the net effect:
- DexToken balance of the DexToken contract decreases by
1,000,015,840,000,000,000,000,000,000,000,000. - The swap executor
0x0496...receives999,952,000,000,000,000,000,000,000,000,000DexToken. - The DexToken‑USDT pair receives
48,000,000,000,000,000,000,000,000,000DexToken.
There is no approval from the DexToken contract to either the helper or
swap executor in the DexToken txlist. Under standard ERC20 semantics,
any such approval would need to be a transaction from the DexToken
contract’s address, which cannot exist because the DexToken contract is
not an EOA. Therefore, the only mechanism that can explain these
movements is the broken transferFrom implementation.
4.4 Repeated AMM Swaps to Extract BEP20USDT
Once the swap executor 0x0496... holds nearly all stolen DexToken, it
repeatedly sells DexToken into the DexToken‑USDT pair via
PancakeRouter::swapExactTokensForTokensSupportingFeeOnTransferTokens.
The trace shows multiple nested calls where 0x0496... is the caller to
PancakeRouter, leading to DexToken::transferFrom calls from
0x0496... to the pair and then to PancakePair::swap calls that
produce USDT out of the pool.
balance_diff.json for BEP20USDT shows the resulting deltas:
{
"erc20_balance_deltas": [
{
"token": "0x55d398326f99059ff775485246999027b3197955",
"holder": "0x35886c6d74aced4ed0fbe0b851806278384d9a76",
"before": "13256433814164156746766",
"after": "217835224264850072654",
"delta": "-13038598589899306674112"
},
{
"token": "0x55d398326f99059ff775485246999027b3197955",
"holder": "0x5adcefed6f5cfb2aafccf08ca3bfb388e08dd3ee",
"delta": "3446357976293290354724"
},
{
"token": "0x55d398326f99059ff775485246999027b3197955",
"holder": "0x42d9c8e28db07f94d3aa36b41ab6f37ded8e2caa",
"delta": "2340952538423259284331"
},
{
"token": "0x55d398326f99059ff775485246999027b3197955",
"holder": "0x56b2d55457b31fb4b78ebddd6718ea2667804a06",
"before": "0",
"after": "7251288075182757035057",
"delta": "7251288075182757035057"
}
]
}
Snippet origin: BEP20USDT section of seed transaction balance_diff.json
for 0x96a9...40e8d on BSC.
The pool loses exactly 13,038.598589899306674112 USDT
(-13038598589899306674112 in wei). The three recipient deltas sum
exactly to this loss:
3,446.357976293290354724USDT to0x5adce...3ee.2,340.952538423259284331USDT to0x42d9...2caa.7,251.288075182757035057USDT to attacker EOA0x56b2...4a06.
This confirms that all USDT drained from the pool is distributed among these three addresses.
4.5 Profit Predicate in BEP20USDT
The adversary-related cluster’s profit is computed using BEP20USDT as
the reference asset. Focusing on the minimal adversary cluster
{EOA 0x56b2...4a06, helper 0xfe7E..., swap executor 0x0496...},
balance_diff.json shows:
- Attacker EOA BEP20USDT balance delta:
+7,251.288075182757035057USDT. - No BEP20USDT is spent by any adversary-related address in this transaction.
- Native gas expenditure by the attacker EOA is exactly
0.017506866BNB (from thenative_balance_deltassection).
Because the adversary spends 0 USDT in the transaction and ends with a positive USDT balance, the net change in the adversary’s portfolio value in units of BEP20USDT is strictly positive for any non-negative valuation of BNB in USDT. The ACT profit predicate holds deterministically:
value_before_in_reference_asset= 0 USDT.value_after_in_reference_asset= 7,251.288075182757035057 USDT.value_delta_in_reference_asset= 7,251.288075182757035057 USDT.fees_paid_in_reference_asset= 0 USDT (gas is paid in BNB only).
The entire opportunity is realized within this single transaction.
5. Adversary Flow Analysis
This section describes the adversary’s end-to-end execution flow in
transaction 0x96a9...40e8d on BSC.
-
EOA submits contract-creation tx
- EOA
0x56b2...4a06submits a type-0 transaction withto=nullandinputcontaining the creation bytecode for the helper0xfe7E.... - The transaction uses standard gas limits and gas price suitable for inclusion by any BSC validator.
- EOA
-
Helper/orchestrator constructor executes
- At depth 1 of the trace, the constructor of
0xfe7E...begins executing. - It issues a call into DexToken
0xabc6...30c8invokingDexToken::transferFromwithsender = 0xabc6...30c8,recipient = 0x0496..., andamount = 1e33DexToken. - This call succeeds because DexToken’s
transferFromdoes not enforce an allowance check before transferring.
- At depth 1 of the trace, the constructor of
-
DexToken contract balance drained to adversary
_transferinside DexToken subtracts theamountfrom_balances[0xabc6...30c8]and credits it to_balances[0x0496...], while also sending a portion to the DexToken‑USDT pair._allowances[0xabc6...30c8][helper]is then decreased with an unchecked subtraction, potentially underflowing, but the transaction does not revert.
-
Swap executor sells DexToken into USDT
- The swap executor
0x0496...invokesPancakeRouter::swapExactTokensForTokensSupportingFeeOnTransferTokensmultiple times, each time sending DexToken to the DexToken‑USDT pair and receiving USDT out. - The pair’s
swapandSyncevents in the trace show reserves updating as DexToken flows in and USDT flows out, consistent with AMM pricing.
- The swap executor
-
USDT distribution to attacker and two additional addresses
- The router and pair calls result in USDT being transferred from the
DexToken‑USDT pool to three addresses:
- 7,251.288075182757035057 USDT to the attacker EOA
0x56b2...4a06. - 3,446.357976293290354724 USDT to
0x5adce...3ee. - 2,340.952538423259284331 USDT to
0x42d9...2caa.
- 7,251.288075182757035057 USDT to the attacker EOA
- These transfers are visible in the BEP20USDT portion of
balance_diff.jsonand confirmed by the ERC20Transferevents in the trace.
- The router and pair calls result in USDT being transferred from the
DexToken‑USDT pool to three addresses:
-
Transaction final state
- The attacker EOA ends the transaction with 7,251.288075182757035057 USDT and 0.979993134 BNB (after paying 0.017506866 BNB in gas).
- The DexToken‑USDT pool has lost 13,038.598589899306674112 USDT.
- Nearly all DexToken previously held at the DexToken contract has been moved into the DexToken‑USDT pool and then sold for USDT.
This flow uses only publicly observable contract code (DexToken,
PancakePair, BEP20USDT), standard AMM behavior, and the broken
transferFrom logic. Any unprivileged EOA could deploy a similar helper
contract and realize the same opportunity given the pre-state σ_B.
6. Impact & Losses
The measurable on-chain impact is fully determined from
balance_diff.json:
- The DexToken‑USDT Pancake pair at
0x35886c6d74aced4ed0fbe0b851806278384d9a76loses exactly13,038.598589899306674112BEP20USDT. - The DexToken contract’s self-held balance decreases by
1,000,015,840,000,000,000,000,000,000,000,000DexToken, of which999,952,000,000,000,000,000,000,000,000,000are routed to the swap executor and48,000,000,000,000,000,000,000,000,000are injected into the DexToken‑USDT pair. - The attacker-controlled EOA
0x56b2...4a06realizes a net gain of7,251.288075182757035057BEP20USDT in a single transaction while paying only0.017506866BNB in gas. - Two additional addresses,
0x5adcefed6f5cfb2aafccf08ca3bfb388e08dd3eeand0x42d9c8e28db07f94d3aa36b41ab6f37ded8e2caa, receive the remaining drained USDT, but they are not included in the minimal adversary cluster because available on-chain data does not deterministically prove they are controlled by the same adversary.
Liquidity providers in the DexToken‑USDT pool absorb the entire USDT
loss. The protocol’s token design (holding a large balance in the token
contract itself) amplifies the effect of the broken transferFrom by
making a large quantity of DexToken directly stealable without prior
user consent.
7. References
Key artifacts used to validate this root cause analysis:
- Seed transaction metadata for
0x96a955304fed48a8fbfb1396ec7658e7dc42b7c140298b80ce4206df34f40e8don BSC — contains raw RPC data (from, to, gas, gasPrice, input, blockNumber, etc.) used to identify the transaction as a contract-creation tx from0x56b2...4a06and link it to helper contract0xfe7E.... - Seed transaction balance diff (
balance_diff.json) — records precise pre- and post-state balances for BNB, DexToken, and BEP20USDT. Used to quantify the DexToken contract balance theft, the USDT drained from the pool, and the final USDT holdings of the attacker and other recipients. - DexToken verified source (LW.sol) — provides the exact
implementation of
transferFrom,_transfer,_tokenTransfer, and related functions, demonstrating the missing allowance check and unchecked subtraction under Solidity 0.7.6. - DexToken‑USDT PancakePair flattened source — shows that the
liquidity pool is a standard Pancake/Uniswap V2 clone obeying x*y=k
with a 0.25% fee and implementing
swap/Syncsemantics consistent with the observed USDT outflows. - BEP20USDT verified source — confirms standard BEP20 behavior with SafeMath-protected allowances and transfers, ruling out bugs in USDT itself and isolating the vulnerability to DexToken.
- Seed transaction trace (
trace.cast.log) — provides an annotated call tree showing helper constructor execution,DexToken::transferFromcalls from the DexToken contract to0x0496..., repeatedDexToken::transferFromcalls from0x0496...to the pair, and the downstream AMM swaps that produce USDT. - Etherscan txlists for DexToken, the DexToken‑USDT pair, the attacker EOA, the swap executor, and the large USDT recipients — used to verify the absence of approvals from the DexToken contract to the adversary cluster and to support the conservative adversary clustering decision.
These artifacts are sufficient for an independent investigator to
reproduce the exploit flow, verify the root cause in DexToken’s
transferFrom, and confirm the profit and loss figures stated above.