Jetton and NFT Standard Pitfalls That Fail a TON Audit
A jetton contract audit keeps flagging the same TEP-74/TEP-62 mistakes: bounce handling, gas forwarding, and minter access control gone wrong.
Why TON's Token Standards Fail More Audits Than You'd Expect
Jetton and NFT contracts on TON look deceptively simple next to an ERC-20 or ERC-721 implementation. There's no inheritance chain, no OpenZeppelin base to import, and the reference implementations from the TON Foundation fit in a few hundred lines of FunC. That simplicity is exactly why teams get burned: every jetton contract audit we run turns up the same handful of mistakes, repeated by developers who copy the sample code, change a few lines, and assume the actor model handles the rest for them. It doesn't. TON's asynchronous message-passing architecture punishes sloppy edge-case handling in ways EVM chains never do, because a failed transaction on TON doesn't just revert — it can strand funds, break bounce guarantees, or leave a wallet contract permanently undeployed.
Below are the failure patterns that show up most often when we review TEP-74 (jetton) and TEP-62 (NFT) implementations before mainnet, roughly in order of how often we flag them.
1. Bounce Handling That Doesn't Actually Bounce
TON messages carry a bounce flag. When a receiving contract throws an exception, the network sends the original message back to the sender — but only if bounce was set to true and only if the contract lets the bounce happen instead of catching every possible failure silently. The TEP-74 reference jetton wallet explicitly checks msg_flags for the bounced bit at the top of recv_internal:
() recv_internal(int msg_value, cell in_msg_full, slice in_msg_body) impure {
slice cs = in_msg_full.begin_parse();
int flags = cs~load_uint(4);
if (flags & 1) {
;; bounced message, handle balance rollback and return
return ();
}
...
}
We see two variants of the same bug. First, contracts that skip the bounced-message check entirely, so a failed transfer silently drops the sender's expected balance rollback and the tokens vanish from circulation from the user's perspective. Second, contracts that set bounce: false on outbound jetton transfer notifications to save a few nanotons of gas, which means if the destination contract throws, the message is just gone — no refund, no error surfaced to the sender, no trace. If your DEX router or vault is on the receiving end of jetton transfers and doesn't get a bounce back on failure, it can't reconcile balances, and users start filing support tickets about tokens that "disappeared."
2. Gas Forwarding That Assumes Happy-Path Costs
TEP-74 specifies a forward_ton_amount field in the transfer message specifically so the sender can fund a notification to the recipient's own contract logic (a DEX pool crediting a swap, a staking contract crediting a deposit). The pitfall is developers hardcoding this value based on testnet gas costs, then never revisiting it. Mainnet gas prices fluctuate with network congestion, and jetton wallet code size differs slightly between forks — we've seen teams fork a wallet contract, add a fee-on-transfer hook, and not increase the forwarded gas to cover the extra opcodes, so the notification message runs out of gas mid-execution and bounces.
The other half of this pitfall is on the receiving side: contracts that don't validate msg_value is sufficient before doing expensive storage writes. A malicious or just cheap sender can send a jetton transfer notification with forward_ton_amount set to 1 nanoton, and if your receiving contract doesn't check in_msg_value against a minimum before processing, it can panic partway through a state update and leave storage in a partially-mutated condition. The fix is boring but non-negotiable: compute your worst-case gas cost, add a margin (we typically recommend 15-20% over measured worst case), and reject the message outright if forwarded value is short of that threshold — do it before any storage write, not after.
3. Minter Access Control That Trusts the Wrong Field
This is the one that turns into a real incident, not just a UX complaint. TEP-74 jetton minters authorize mint operations by checking the sender's address against a stored admin_address. The bug we find constantly: contracts that check admin_address against msg::sender derived from the message's src field without verifying that field against the actual computed address of the sending contract, or — worse — minters that accept a mint request based on a signature or op-code alone with no address check at all, treating any correctly-formatted op::mint message as authorized. On an EVM chain msg.sender is unforgeable by construction. On TON, the src address in an internal message is exactly that: information within the message body, and while the runtime does populate it correctly for genuine internal messages, contracts that reconstruct or partially trust admin logic from op-codes rather than the verified sender slice open a path for anyone to forge a mint request.
The other recurring minter issue is a missing check on total_supply overflow and no cap enforcement, meaning a compromised or buggy admin key can mint unbounded supply with zero contract-level guardrail. If your tokenomics promise a fixed cap, that cap needs to live in the minter's code, not just in your whitepaper.
4. NFT Ownership Transfer Skipping the Excess Return
TEP-62 requires NFT transfer handlers to return any unused TON from the transaction back to a response_destination address. We regularly see collections that forward the entire message value to the new owner's wallet or, worse, leave it stuck in the NFT item contract's balance, slowly accumulating dust that's neither refunded nor swept. It's not a security hole by itself, but on high-volume marketplace collections it adds up to real value sitting inert, and auditors will flag it as a spec violation that erodes trust with integrators expecting standard behavior.
5. Missing Op-Code Validation on Unknown Messages
Both standards define a small, closed set of op-codes. Contracts that don't throw_unless on an unrecognized op-code, and instead silently accept and ignore unknown messages, create a situation where future protocol messages — or attacker-crafted ones — get swallowed without any error surfaced. Always throw on unhandled ops; silent no-ops are a debugging nightmare during integration testing on top of being a spec violation.
None of these issues are exotic. They're the direct result of TON's actor model requiring explicit handling of failure paths that other chains handle implicitly, and reference implementations only get you partway if you fork them without understanding why each check is there. If you're integrating a fork of the standard jetton or NFT contracts, budget real review time for exactly these five areas — read through our breakdown of what a TON audit actually costs and takes in 2026 for how scope and timeline shift based on how far your code has drifted from the reference implementation.
Ongoing token contracts also need a second look any time you touch the minter or wallet code post-launch — our post-launch maintenance and support work exists precisely because "audited once" doesn't mean "safe forever" once you ship a hotfix. And if the goal is understanding these tradeoffs at a lighter touch than a full audit, a targeted code review on just the mint and transfer paths, cross-referenced against the audit cost breakdown, is often enough to catch the five patterns above before they reach mainnet.
Get your jetton or NFT contract in front of a dedicated smart contract audit before mainnet, not after a bounce failure teaches your users the hard way.
Need a bot like this built?
We design, build and run trading bots on Solana, Hyperliquid and Polymarket.
Start a projectMore from the blog
Public vs Paid RPC: How to Run a Fair Benchmark
A fair comparison of public and paid RPC endpoints must account for quotas, workload, freshness and support guarantees.
Read articleRPC Benchmark Percentiles Explained: p50, p95 and p99
Why averages hide the latency spikes that break trading bots, wallets and production blockchain applications.
Read articleHyperliquid REST vs WebSocket Benchmark: What to Measure
A benchmark plan for Hyperliquid market data that separates request latency from streaming freshness and recovery.
Read article