Jettons are fungible tokens on TON. Each jetton has a master contract (minter) and separate wallet contracts for every holder. Read more in How Jettons Work.
Jetton processing is security-critical. Incorrect validation of jetton wallet addresses or transfer notifications can lead to accepting fake tokens or crediting wrong amounts.
Key concepts
Before implementing jetton payment processing, understand these core concepts: Jetton architecture: Each jetton type has one master contract that stores metadata and total supply. Each address holding the jetton has a separate jetton wallet contract at a deterministic address derived from the master contract and owner address. Transfer flow: Jetton transfers involve multiple messages. A user sends atransfer message to their jetton wallet, which sends an internal_transfer to the recipient’s jetton wallet, which then sends a transfer_notification to the recipient’s address if forward_ton_amount > 0.
Jetton transfers are considered successful only when the recipient receives
transfer_notification. Services must set forward_ton_amount to at least 0.000000001 TON (1 nanoton) when sending tokens to trigger notifications. Without this, transfers won’t be compliant and may not be processed by exchanges and other services.Processing deposits
Funds at riskSkipping any validation step or changing their order can lead to incorrect deposit processing and potential loss of funds.
Setup
Processing jetton deposits requires:- Allowlist of trusted jetton masters: List of jetton master contract addresses to accept
- Deposit wallet address: Service wallet (e.g., wallet v4 or v5)
Initial configuration
- For each allowlisted jetton master, derive the jetton wallet address for the deposit wallet using the master contract’s
get_wallet_address()method - Store the mapping of
jetton master→jetton wallet→deposit walletin the database - Begin monitoring transactions to the deposit wallet address
Processing incoming transactions
When a transaction arrives at the deposit wallet:- Check that
tx.in_msg.sourcematches a known jetton wallet for this deposit wallet - Verify the master → jetton wallet relationship:
- Call
get_wallet_address(deposit-wallet)on the master contract - Confirm the returned address matches the sender
- Call
- Verify there are no outgoing messages (
tx.out_msgs.length === 0) - Parse the message body:
- Check the opcode (first 32 bits of
tx.in_msg.body) equals0x7362d09c(transfer_notification) - Extract
query_id,amount,sender, andforward_payloadaccording to TL-B
- Check the opcode (first 32 bits of
- Verify the amount matches the expected value
Crediting user accounts
After validation, extract deposit information:- For invoice-based deposits: Parse the invoice ID from
forward_payload, match it against the database, and credit the corresponding user account - For address-based deposits: Match the
deposit-walletaddress against the database and credit the user account
Not production-ready code, use only for educational purposes
Security considerations
Master-wallet verification
Never trust jetton wallet addresses without verification. Always perform these checks:- Get the jetton master address from the allowlist
- Call
jetton_master.get_wallet_address(owner_address) - Verify the returned address matches the jetton wallet that sent the notification
Transfer notification validation
When processing deposits viatransfer_notification:
- Verify the opcode is exactly
0x7362d09c - Check the sender address is an expected jetton wallet
- Extract
amountin base units (not decimal) - Validate the
senderfield against expected user addresses - Parse
forward_payloadcarefully—it may be malformed - Check for bounce indicators (single outgoing message back to sender)
Fake jetton detection
Attackers can deploy jettons with identical names, symbols, and images:- Always verify the jetton master address against the allowlist
- Never trust metadata (name, symbol, image) for authentication
- Display the master contract address in admin interfaces
- Implement a manual approval workflow for adding new jettons
Common attack patterns
Fake jetton wallets
Attack: Attacker deploys a contract claiming to be a jetton wallet with an inflated balance. Mitigation: Verify the master-wallet relationship by callingget_wallet_address() on the master contract.
Invoice ID reuse
Attack: User attempts to reuse a settled invoice identifier. Mitigation: Mark invoices as used after the first successful deposit.Master contract spoofing
Attack: Deploying a fake master contract that validates the attacker’s fake jetton wallets. Mitigation: Maintain a strict allowlist of trusted master contracts and verify all jetton wallets against it.Implementation checklist
Before enabling jetton processing in production:Testing
- Deploy and test on testnet with real user scenarios
- Verify master-wallet relationships for all allowlisted jettons
- Test with fake jetton wallets to confirm rejection
- Validate transfer notification parsing with various payload formats
- Test bounce detection and handling
- Test invoice ID collision and reuse scenarios
- Test full flow: deposit → credit → withdrawal → confirmation