All articles
Comparisons·May 31, 2026·6 min read

TON Jetton vs ERC-20: Security Pitfalls Compared

TON jetton security vs ERC-20: why fake jetton-wallet notifications and bounce-message draining are exploit classes EVM auditors never see.

Two Token Standards, Two Threat Models

ERC-20 and TON jettons both claim to do the same job — let a contract represent a fungible token — but they solve it with opposite architectures, and that difference is where most of the interesting bugs live. ERC-20 keeps one contract holding a mapping(address => uint256) for every balance. Every transfer is a function call into that single contract, and the EVM's synchronous execution model means you can trace a transfer start to finish in one transaction. TON jettons do the opposite: every holder gets their own jetton-wallet contract, deployed from the jetton-master, and a "transfer" is actually a chain of asynchronous internal messages bouncing between two or three separate contracts. That's the actor model, and it's why an auditor who's only ever reviewed EVM code walks into a TON codebase and misses an entire category of bugs.

I've reviewed both, and the honest answer is neither is "more secure" in the abstract. They just fail differently.

How ERC-20 Actually Breaks

The ERC-20 attack surface is well-mapped at this point, but teams still ship the same three mistakes:

  • The approve/transferFrom race. Changing an allowance from N to M without zeroing it first lets a watching attacker front-run the new approval and spend both amounts. increaseAllowance/decreaseAllowance patch this, but plenty of 2024-2026 codebases still use raw approve.
  • Missing return-value checks. Some tokens (USDT famously) don't return bool on transfer, they just revert or silently do nothing. A contract that does token.transfer(to, amount) without using SafeERC20 can treat a failed transfer as successful.
  • Fee-on-transfer and rebasing tokens breaking invariants. If your vault assumes amountIn == amountReceived, a deflationary token will quietly desync your accounting until someone drains the difference.

None of this requires understanding anything beyond "one contract, one storage slot per balance." That's the appeal of ERC-20 for auditors — the state is centralized and synchronous, so you can reason about a transfer as a single atomic operation. Reentrancy is possible if the token has hooks (ERC-777-style tokensReceived), but plain ERC-20 has no callback into the recipient, which closes off a whole class of reentrancy bugs by design.

How Jettons Actually Break

Jettons don't have a global balance mapping. TEP-74 splits state across contracts: the jetton-master tracks total supply and mints, and each owner's balance lives in their own jetton-wallet contract, deployed deterministically from (jetton_master_address, owner_address, jetton_wallet_code). A transfer is: owner's wallet debits itself, sends an internal message to the recipient's wallet, which credits itself and optionally forwards a transfer-notification message to the recipient's contract if it's a smart contract (like a DEX pool or vault).

This is where the two TON-specific exploit classes an EVM auditor won't have muscle memory for come in.

1. Fake jetton wallet notifications. A contract that accepts jetton deposits — say, a swap pool — typically listens for a transfer_notification message and credits the sender based on the jetton_amount field in that message. The catch: anyone can deploy any contract and send it a message shaped exactly like a transfer_notification. If the receiving contract doesn't verify that msg.sender is the specific jetton-wallet address that would be deployed for that jetton-master and that owner, an attacker just deploys a look-alike contract, sends a fake notification claiming a 10,000 USDT deposit, and the pool credits them for tokens that were never moved. This is the single most common finding I see in TON DeFi audits — it's the rough equivalent of an ERC-20 contract trusting msg.sender claims about balances instead of reading balanceOf.

The fix is always the same: recompute the expected jetton-wallet address from its state init on-chain and compare.

;; Verify the incoming message is really from OUR jetton wallet,
;; not an attacker-deployed lookalike.
cell expected_state_init = calculate_jetton_wallet_state_init(
    my_address(), jetton_master_address, jetton_wallet_code
);
slice expected_wallet_addr = calculate_address(0, expected_state_init);

throw_unless(707, equal_slices(sender_address, expected_wallet_addr));

Skip this check and your "deposit" flow is a free-mint bug.

2. Bounce-message draining. Internal TON messages can bounce: if the receiving contract throws (out of gas, wrong opcode, whatever), and the message was sent with the bounce flag set, TON automatically returns the message body to the sender with the bounced bit flipped. A jetton-wallet transfer that debits the sender's balance before confirming the recipient actually accepted it has to handle that bounce correctly, restoring the debited amount. Contracts that don't implement bounced() handling — or implement it naively — leave a window where a transfer can be debited on one side, bounce, and either strand the tokens in limbo or, worse, let an attacker script repeated failed transfers that drain the gas reserve funding the bounce round-trips, a slow griefing attack that doesn't show up in a quick functional test. There's no equivalent primitive on EVM: a failed call just reverts the whole transaction, state and all, in one atomic step. TON's asynchronous, multi-message nature means partial-failure states are a first-class thing you must design for, not an edge case.

Side by Side

ERC-20 (EVM) Jetton (TON)
Balance storage Single contract, one mapping Per-holder wallet contract
Transfer execution Synchronous, atomic Async, multi-message chain
Failure mode Revert (all-or-nothing) Bounce (partial state possible)
Trust assumption to verify Return value / allowance state Sender is the real jetton wallet (state-init check)
Common exploit approve race, fee-token desync fake wallet notification, bounce drain
Reentrancy surface Only via hook-token callbacks Inherent to message-passing model
Gas/fee risk Gas estimation failure Insufficient forward_ton_amount drops notifications

Which to Pick When

If you're building on EVM and sticking to plain ERC-20 with SafeERC20 and OpenZeppelin's tested implementations, your main job is checking integration points — how other contracts consume your token, not the token contract itself. That's a comparatively contained audit scope.

If you're building on TON, budget more review time for every contract that receives jettons, not just the jetton-wallet code. The wallet-per-holder architecture is genuinely better for parallelism and avoids EVM's global-lock bottleneck, but it pushes verification responsibility onto every downstream contract in the message chain. A TON audit that only reads the jetton-wallet source and skips the receiving vault's sender-verification logic is an incomplete audit — and I'd argue that's the single biggest gap I see in solo or first-pass reviews of Tact and FunC jetton integrations, a point covered in more depth in our FunC vs Tact security comparison.

Neither standard is inherently riskier; they just require different mental models. Teams shipping on both chains should treat "read the whole message flow" as a TON-specific checklist item the way "check the approve pattern" is an EVM-specific one — and if you're weighing whether that check happens in-house or externally, it's worth reading our take on in-house review versus third-party audits for TON contracts before deciding. For teams still choosing tooling, our breakdown of manual audits versus automated scanners across Solana and EVM covers why static analyzers still miss most jetton-specific logic bugs.

None of this is theoretical — fake jetton wallet notifications alone have drained real TON pools, and it's the first thing we check in a smart contract audit engagement, alongside a full code review of every message handler that touches jettons. If you're mid-build and want a second pair of eyes before mainnet, a strategy consultation is a low-cost way to catch architecture-level issues before they become audit findings.

Get a smart contract audit before your jetton integration goes live — the fake-wallet check takes five minutes to add and an incident to regret skipping.

Need a bot like this built?

We design, build and run trading bots on Solana, Hyperliquid and Polymarket.

Start a project
#TON#jettons#ERC-20#smart contract security#audits