MetaverseToken fee misconfiguration drains USDT from Pancake pair
Exploit Transactions
0xc758ab15fd51e713ff8b4184620610a1ac809be06ec374305c32d3b244256a64Victim Addresses
0x34ce28c4a7d4a796bed899f5b3e5d88256a17a77BSC0xbf4707b7f9f53e3aae29bf2558cb373419ef4d45BSC0x2f3f25046ea518d1e524b8fb6147c656d6722cedBSCLoss Breakdown
Similar Incidents
Public Mint Drains USDT Pair
42%AI IPC destroy-sync mechanism drains IPC-USDT pair USDT reserves
39%STOToken Sell-Hook Reserve Manipulation Drains the STO/WBNB Pancake Pair
38%BRAToken self-transfer tax bug inflates pool and drains USDT
36%BBX auto-burn sync flaw drains USDT from BBX pool
35%Marketplace proxy 0x9b3e9b92 bug drains USDT and mints rewards
35%Root Cause Analysis
MetaverseToken misconfigured fee-on-transfer drains USDT from MT/USDT Pancake pair
1 Incident Overview TL;DR
- On BSC block 74937080, a single adversary-crafted contract-creation transaction (
0xc758ab15fd51e713ff8b4184620610a1ac809be06ec374305c32d3b244256a64) drained36995244786737651151991USDT from the MetaverseToken/USDT Pancake pair at0xbf4707b7f9f53e3aae29bf2558cb373419ef4d45. - The attacker’s EOA
0xe918a1784ceca08e51a1b740f4036fd149339811ends the transaction with USDT holdings increased from3010341524395885913846to40005586311133537065837, fully funded by the MT/USDT pair while the flash-loaned principal is repaid. - The root cause is a misconfigured fee-on-transfer mechanism in MetaverseToken (
0x2f3f25046ea518d1e524b8fb6147c656d6722ced): contract-initiated transfers for non-whitelisted addresses debit about 3.15× the nominal transfer amount, while AMM pricing and swap logic assume only the nominal amount moves. - Because the misconfiguration is encoded entirely in public on-chain code and storage (fee parameters, contractor arrays, and whitelist mapping) and the attack uses only standard router and pair interfaces, this constitutes an ACT opportunity that any unprivileged adversary could realize starting from the same pre-state.
2 Key Background
- MetaverseToken extends ERC20 with additional fee and limit logic. Transfers go through an internal
transactionFeefunction that can apply a configurable percentage fee (_transactFeeValue) and redistribute it to contractor wallets based onContractorsFeearrays, with logic gated on whether the sender or recipient are considered “white-listed” by an external helper contract at0x34ce28c4a7d4a796bed899f5b3e5d88256a17a77. - For non-whitelisted transfers,
transactionFeecomputes a fee amount and then callssuper._transfermultiple times to contractor addresses and once to the nominal recipient. The total amount debited fromfromdepends on_transactFeeValueand the entries inContractorsFee[setType], wheresetTypeselects between contract-to-contract, contract-to-EOA, and EOA-to-contract flows. - The MT/USDT liquidity pool is a standard PancakeSwap pair on BSC with
token0 = MetaverseTokenandtoken1 = BEP20USDT (0x55d398326f99059ff775485246999027b3197955). The attack path usesPancakeRouterV2::swapExactTokensForTokensSupportingFeeOnTransferTokenswith path[USDT, MetaverseToken]and then[MetaverseToken, USDT], which assumes that any fee-on-transfer behaviour merely reduces the effective input amount and does not secretly debit more than 100% of the nominal amount from the AMM’s perspective. - At block 74937080, decoded storage for MetaverseToken shows
_transactFeeValue = 5andContractorsFee[0] = [1000, 1200, 2200]for contract-initiated transfers, while the whitelist helper has no whitelist entries for the attacker, the helper contracts, or the MT/USDT pair. This configuration is sufficient to make contract-to-contract transfers debit more than three times the nominal amount without the AMM being aware.
3 Vulnerability Analysis & Root Cause Summary
- The critical vulnerability lies in how MetaverseToken computes and applies transfer fees for non-whitelisted contract senders. The relevant code from
Contract.solis:
function transactionFee(address from,address to,uint256 amount) internal returns (uint256) {
if (_msgSender() == address(this)
|| from == address(this)
|| to == address(this)
|| IUniswapV2Pair(_dis).isWhite(from)
|| IUniswapV2Pair(_dis).isWhite(to)
) return amount;
uint setType = 2;
if (isContract(from)) {
setType = 0;
} else if (isContract(to)) {
setType = 1;
}
uint256 realAmount = amount;
uint256 transactFeeValue = amount.mul(_transactFeeValue).div(100);
require(balanceOf(from) > amount, "balanceOf is Insufficient");
require(setType == 0 || balanceOf(from).sub(amount) >= BASE_RATIO.div(10000000), "balanceOf is too small");
if (transactFeeValue >= 100) {
realAmount = realAmount.sub(transactFeeValue);
for (uint256 i = 0; i < ContractorsFee[setType].length; i++) {
if (ContractorsFee[setType][i] > 0) {
uint256 value = transactFeeValue.mul(ContractorsFee[setType][i]).div(100);
super._transfer(from, ContractorsAddress[setType][i], value);
}
}
}
return realAmount;
}
function _transfer(address from, address to, uint256 amount) internal {
amount = transactionFee(from, to, amount);
super._transfer(from, to, amount);
}
- Decoded configuration at the incident block confirms the parameters that make this function dangerous for contract senders:
{
"config": {
"contract_address": "0x2f3f25046ea518d1e524b8fb6147c656d6722ced",
"block_number": 74937080,
"transactFee_value": 5,
"dis_helper": "0x34CE28c4a7D4a796beD899F5B3E5D88256a17A77",
"contractors": {
"0": {
"raw": "[1000, 1200, 2200]\n[0xa0f76967e9F36367c9045AdcfEe0F62D17B4F016, 0x869A387Fa1B10A7A3F6361B89e9D0946a40A4F1A, 0xf8E339c3bCF47417E5b1F8B76bf8d8a4034Ef493]"
}
}
}
}
- For a contract-initiated transfer (
setType = 0) of amountAwhen neitherfromnortois whitelisted:transactFeeValue = A * _transactFeeValue / 100 = 0.05A.- Contractor transfers total:
0.05A * (1000 + 1200 + 2200) / 100 = 0.05A * 44 = 2.2A. - Recipient receives
realAmount = A - 0.05A = 0.95A. - The sender loses
2.2A + 0.95A = 3.15Atokens, while external callers (including the AMM) treat the operation as a transfer of onlyA(subject to standard fee-on-transfer assumptions).
- The whitelist helper at
0x34ce28c4a7d4a796bed899f5b3e5d88256a17a77does not exempt the relevant addresses. At block 74937080, its decoded mapping shows:
{
"contract_address": "0x34ce28c4a7d4a796bed899f5b3e5d88256a17a77",
"block_number": 74937080,
"entries": {
"0xbf4707b7f9f53e3aae29bf2558cb373419ef4d45": { "isWhite_bool": false },
"0x303bf3805970bb2ebce7a96f342fdced9486729f": { "isWhite_bool": false },
"0xb64f5d49656fae38655ef2e3c2e3768ddb5f3d5c": { "isWhite_bool": false },
"0xe918a1784ceca08e51a1b740f4036fd149339811": { "isWhite_bool": false }
}
}
- Consequently, both MetaverseToken transfers between the MT/USDT pair and the adversary’s aggregator during the attack are treated as non-whitelisted contract flows and experience the 3.15× over-debit. The AMM, however, updates reserves based on the nominal input amounts implied by the swaps, not the much larger actual token movements, which breaks the constant-product invariant and allows extraction of USDT from the pool.
- The root cause can therefore be summarized as: a misconfigured fee-on-transfer token with contractor fee coefficients far exceeding 100, combined with non-whitelisting of AMM and aggregator contracts, leading to hidden, multiplicative token debits that invalidate AMM pricing assumptions.
4 Detailed Root Cause Analysis
- Pre-state σ_B (block 74937080 pre-state)
- MetaverseToken (
0x2f3f25046ea518d1e524b8fb6147c656d6722ced) has_transactFeeValue = 5andContractorsFee[0] = [1000, 1200, 2200]for contract-initiated transfers, with contractor addresses configured as decoded above. - The whitelist helper (
0x34ce28c4a7d4a796bed899f5b3e5d88256a17a77) reportsisWhite = falsefor all key participants: the MT/USDT pair, the aggregator contract, the intermediate helper, and the attacker EOA. No special whitelisting is required or present. - The MT/USDT Pancake pair (
0xbf4707b7f9f53e3aae29bf2558cb373419ef4d45) holds substantial MetaverseToken reserves, as shown by decoded reserves just before and after the incident transaction:
- MetaverseToken (
{
"contract_address": "0xbf4707b7f9f53e3aae29bf2558cb373419ef4d45",
"txhash": "0xc758ab15fd51e713ff8b4184620610a1ac809be06ec374305c32d3b244256a64",
"reserves_before": {
"reserve0": 25914029372088303000000,
"reserve1": 2,
"k": 51828058744176605000000
},
"reserves_after": {
"reserve0": 564878464577161130000,
"reserve1": 5,
"k": 2824392322885805400000
},
"reserves_deltas": {
"delta_reserve0": -25349150907511140000000,
"delta_reserve1": 3,
"delta_k": -49003666421290805000000
}
}
-
token0is MetaverseToken andtoken1is BEP20USDT, soreserve0tracks MetaverseToken andreserve1tracks USDT. The enormous drop inreserve0and only small increase inreserve1across a single transaction indicates that the AMM invariantkcollapses, consistent with hidden over-debits of MetaverseToken not accounted for by the pricing formula. -
Adversary transaction and two-swap structure
- The seed transaction
0xc758ab15fd51e713ff8b4184620610a1ac809be06ec374305c32d3b244256a64is a contract-creation transaction from EOA0xe918...9811withto = null. The constructor of the deployed helper contract orchestrates the entire attack. - From the opcode-level trace, the helper obtains a USDT flash loan via a Moolah-style lending contract, then directs USDT into an aggregator contract
0x303bf3805970bb2ebce7a96f342fdced9486729f, which interacts with PancakeRouterV2 and the MT/USDT pair. The trace shows calls of the form:
- The seed transaction
PancakeRouterV2_...::swapExactTokensForTokensSupportingFeeOnTransferTokens(
594572298978549731565,
0,
[0x2f3f25046Ea518d1E524B8fB6147c656D6722CeD, 0x55d398326f99059fF775485246999027B3197955],
0x303Bf3805970Bb2ebcE7A96f342FDCEd9486729F,
1768205155
)
-
The transaction encompasses:
- A first swap where the MT/USDT pair sends MetaverseToken to the aggregator in exchange for USDT (USDT→MT path).
- A second swap where the aggregator sends MetaverseToken back to the pair in exchange for USDT (MT→USDT path), again via
swapExactTokensForTokensSupportingFeeOnTransferTokens.
-
Application of misconfigured fee logic
- In both directions, the transfers of MetaverseToken between the pair and aggregator are contract-to-contract transfers involving non-whitelisted addresses. For such transfers:
isContract(from)is true, sosetType = 0.- Neither
fromnortois whitelisted according to the helper’s decoded storage. - The early-return condition in
transactionFeedoes not apply, so the transfer incurs the full fee logic.
- For a nominal transfer of
AMetaverseToken:- The pair or aggregator (as
from) loses3.15Atokens in total. - The counterparty (
to) receives only0.95Atokens. 2.2Atokens are diverted to contractor wallets unaffiliated with the pair or the aggregator.
- The pair or aggregator (as
- The Pancake pair and router, however, continue to apply pricing based on the nominal
Avalues and their view of reserves, unaware that the pair’s MetaverseToken balance is actually being reduced by3.15Aon these interactions.
- In both directions, the transfers of MetaverseToken between the pair and aggregator are contract-to-contract transfers involving non-whitelisted addresses. For such transfers:
-
Resulting invariant break and USDT drain
- The MT/USDT pair’s pre- and post-transaction reserves show that
reserve0(MetaverseToken) drops by approximately2.53e22whilereserve1(USDT) increases by only 3 units, andkfalls from5.1828e22to2.8244e21. This is incompatible with constant-product behaviour under any standard swap, but exactly what is expected if the pair’s MetaverseToken debits are multiplied by 3.15× while swap math assumes a much smaller input. - The extended BEP20USDT balance diff quantifies the value movement:
- The MT/USDT pair’s pre- and post-transaction reserves show that
{
"erc20_balance_deltas": [
{
"token": "0x55d398326f99059ff775485246999027b3197955",
"holder": "0xbf4707b7f9f53e3aae29bf2558cb373419ef4d45",
"before": "37009240317278254683142",
"after": "13995530540603531151",
"delta": "-36995244786737651151991",
"contract_name": "BEP20USDT"
},
{
"token": "0x55d398326f99059ff775485246999027b3197955",
"holder": "0xe918a1784ceca08e51a1b740f4036fd149339811",
"before": "3010341524395885913846",
"after": "40005586311133537065837",
"delta": "36995244786737651151991",
"contract_name": "BEP20USDT"
}
]
}
-
The pair loses exactly
36995244786737651151991USDT, and the attacker EOA gains the same amount. These deltas match thesuccess_predicateandvalue_flow_overviewin the analysis and demonstrate that the misconfigured fee-on-transfer logic is fully sufficient to explain the observed drain without any additional hidden mechanisms. -
ACT opportunity properties
- All ingredients of the exploit are encoded in public on-chain artifacts: MetaverseToken source code and storage, the whitelist helper source and storage, standard Pancake router and pair contracts, and the observable incident transaction.
- The attack transaction is a single contract-creation call from an unprivileged EOA that interacts only with public functions on the flash-loan provider, router, pair, token, and helper. There is no reliance on privileged roles, whitelist entries, or off-chain coordination.
- Any unprivileged adversary, given the same pre-state σ_B, could deploy an equivalent helper/aggregator pair and submit an analogous transaction to realize the same USDT drain, subject only to gas and capital/flash-loan availability.
5 Adversary Flow Analysis
-
High-level strategy summary
- The adversary EOA
0xe918...9811deploys a helper contract whose constructor takes a USDT flash loan, routes the loaned USDT into an aggregator contract, performs two MetaverseToken swaps against the MT/USDT pair that are both subject to the misconfigured fee-on-transfer logic, repays the flash loan, and sends the net USDT profit back to the EOA.
- The adversary EOA
-
Key adversary-related accounts and roles
0xe918a1784ceca08e51a1b740f4036fd149339811– EOA originator of the incident transaction and final recipient of USDT profit (+36995244786737651151991USDT).0x29766568a9c3358a6d94511effa5430dda017cf1– Helper contract deployed in the seed transaction; its constructor orchestrates the flash loan and attack sequence.0xb64f5d49656fae38655ef2e3c2e3768ddb5f3d5c– Intermediate helper used to initiate the flash loan and deploy the aggregator contract.0x303bf3805970bb2ebce7a96f342fdced9486729f– Aggregator contract that interacts directly with PancakeRouterV2, MetaverseToken, and the MT/USDT pair to execute the two critical swaps.- Victim contracts:
- MetaverseToken:
0x2f3f25046ea518d1e524b8fb6147c656d6722ced. - MT/USDT pair:
0xbf4707b7f9f53e3aae29bf2558cb373419ef4d45.
- MetaverseToken:
-
Call and value flow within the incident transaction
- The EOA sends the contract-creation transaction; the helper’s constructor executes:
- Calls into a lending/flash-loan provider to obtain a large amount of BEP20USDT.
- Transfers USDT to the aggregator and sets necessary approvals for the router.
- Aggregator calls PancakeRouterV2 to perform:
- USDT→MetaverseToken swap, receiving MetaverseToken from the MT/USDT pair.
- MetaverseToken→USDT swap, sending MetaverseToken back to the pair and receiving a larger quantity of USDT.
- Helper and aggregator contracts repay the flash loan from the USDT returned by the second swap.
- Residual USDT is transferred to the attacker EOA.
- Throughout these steps, MetaverseToken transfers between the pair and aggregator are contract-to-contract and non-whitelisted, so each transfer experiences the 3.15× over-debit at the token level, but router and pair math operate on nominal amounts. This mismatch is what allows the second swap to pull more USDT from the pair than a constant-product AMM would normally allow.
- The final extended balance diff confirms that all net USDT leaving the MT/USDT pair is accounted for by the increase in the attacker EOA’s USDT balance, with flash-loaned principal fully repaid.
- The EOA sends the contract-creation transaction; the helper’s constructor executes:
6 Impact & Losses
-
Quantified USDT loss
- The MT/USDT pair at
0xbf4707b7f9f53e3aae29bf2558cb373419ef4d45loses36995244786737651151991units of BEP20USDT during the incident transaction, as shown by decoded ERC-20 balance diffs against token0x55d398326f99059ff775485246999027b3197955. - The attacker EOA
0xe918...9811gains the exact same amount of USDT, moving from3010341524395885913846to40005586311133537065837USDT.
- The MT/USDT pair at
-
Effect on liquidity providers and token holders
- The MT/USDT pair’s reserves become severely imbalanced: MetaverseToken reserves drop by roughly
2.53e22units while USDT reserves drop sharply relative to the pre-incident state, and the invariantkcollapses by almost an order of magnitude. - Liquidity providers in the MT/USDT pool suffer a permanent loss of USDT liquidity and are left with a pool dominated by MetaverseToken, whose value is no longer supported by a healthy USDT backing.
- MetaverseToken holders are indirectly harmed because the token’s liquidity and price stability on PancakeSwap are severely degraded by the drain.
- The MT/USDT pair’s reserves become severely imbalanced: MetaverseToken reserves drop by roughly
7 References
- [1] Seed transaction metadata for
0xc758ab15fd51e713ff8b4184620610a1ac809be06ec374305c32d3b244256a64, including RPC and explorer data, used to identify the chain, block, sender EOA, and transaction type (contract creation). - [2] Verified MetaverseToken source code (
Contract.sol) for0x2f3f25046ea518d1e524b8fb6147c656d6722ced, used to analyzetransactionFee,_transfer, and related configuration logic. - [3] Decoded MetaverseToken configuration at block 74937080 (
metaversetoken_config_decoded.json), showing_transactFeeValue = 5,dis_helper, contractor fee vectors, and_limitmapping entries for key addresses. - [4] Decoded whitelist helper storage at block 74937080 (
whitelist_status_decoded.json), confirming that the MT/USDT pair, aggregator, helper contracts, and attacker EOA are all non-whitelisted. - [5] Decoded MT/USDT pair reserves and invariant around the incident (
_reserves_k_decoded.json), establishing the pre- and post-transaction reserve levels and the collapse ofk. - [6] Extended BEP20USDT balance diffs (
balance_diff.extended.json) for the incident transaction, quantifying the exact USDT loss from the pair and gain to the attacker EOA and supporting the profit-based success predicate.