Skip to content

Commit 2a8c11d

Browse files
authored
feat: add jetton processing (#1202)
1 parent 1d8bc30 commit 2a8c11d

File tree

1 file changed

+147
-2
lines changed

1 file changed

+147
-2
lines changed

payments/jettons.mdx

Lines changed: 147 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,151 @@ title: "Jettons payments processing"
33
sidebarTitle: "Jettons"
44
---
55

6-
import { Stub } from '/snippets/stub.jsx';
6+
import { Aside } from "/snippets/aside.jsx";
77

8-
<Stub issue="700" />
8+
Processing jetton payments requires understanding TON's sharded token architecture. Unlike single-contract token systems, each jetton type consists of a master contract and individual wallet contracts for each holder.
9+
10+
<Aside type="note">
11+
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](/standard/tokens/jettons/how-it-works).
12+
</Aside>
13+
14+
<Aside
15+
type="caution"
16+
>
17+
Jetton processing is security-critical. Incorrect validation of jetton wallet addresses or transfer notifications can lead to accepting fake tokens or crediting wrong amounts.
18+
</Aside>
19+
20+
## Key concepts
21+
22+
Before implementing jetton payment processing, understand these core concepts:
23+
24+
**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.
25+
26+
**Transfer flow**: Jetton transfers involve multiple messages. A user sends a `transfer` 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`.
27+
28+
<Aside
29+
type="caution"
30+
>
31+
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.
32+
</Aside>
33+
34+
**Security model**: Always validate that jetton wallets belong to the expected master contract. Anyone can deploy fake jetton wallet contracts with arbitrary balances.
35+
36+
This article covers processing jetton deposits using transfer notifications. All approaches require maintaining an allowlist of trusted jetton master contracts.
37+
38+
For architectural patterns and deposit methods comparison, see [Toncoin processing](/payments/toncoin).
39+
40+
## Processing deposits
41+
42+
<Aside
43+
type="danger"
44+
title="Funds at risk"
45+
>
46+
Skipping any validation step or changing their order can lead to incorrect deposit processing and potential loss of funds.
47+
</Aside>
48+
49+
### Setup
50+
51+
Processing jetton deposits requires:
52+
53+
- **Allowlist of trusted jetton masters**: List of jetton master contract addresses to accept
54+
- **Deposit wallet address**: Service wallet (e.g., wallet v4 or v5)
55+
56+
### Initial configuration
57+
58+
1. For each allowlisted jetton master, derive the jetton wallet address for the deposit wallet using the master contract's `get_wallet_address()` method
59+
1. Store the mapping of `jetton master``jetton wallet``deposit wallet` in the database
60+
1. Begin monitoring transactions to the deposit wallet address
61+
62+
### Processing incoming transactions
63+
64+
When a transaction arrives at the deposit wallet:
65+
66+
1. Check that `tx.in_msg.source` matches a known jetton wallet for this deposit wallet
67+
1. Verify the master → jetton wallet relationship:
68+
- Call `get_wallet_address(deposit-wallet)` on the master contract
69+
- Confirm the returned address matches the sender
70+
1. Verify there are no outgoing messages (`tx.out_msgs.length === 0`)
71+
1. Parse the message body:
72+
- Check the opcode (first 32 bits of `tx.in_msg.body`) equals `0x7362d09c` (transfer\_notification)
73+
- Extract `query_id`, `amount`, `sender`, and `forward_payload` [according to TL-B](/standard/tokens/jettons/api)
74+
1. Verify the amount matches the expected value
75+
76+
### Crediting user accounts
77+
78+
After validation, extract deposit information:
79+
80+
- **For invoice-based deposits**: Parse the invoice ID from `forward_payload`, match it against the database, and credit the corresponding user account
81+
- **For address-based deposits**: Match the `deposit-wallet` address against the database and credit the user account
82+
83+
<Aside type="note">
84+
Not production-ready code, use only for educational purposes
85+
</Aside>
86+
87+
To understand invoice-based deposit approach in greater detail, see the following TypeScript implementation: [Invoice-based Jetton deposits](https://github.com/ton-org/docs-examples/blob/processing/guidebook/payment-processing/src/deposits/jetton-invoices.ts).
88+
89+
For unique TypeScript implementation addresses see: [Unique address Toncoin deposit](https://github.com/ton-org/docs-examples/blob/processing/guidebook/payment-processing/src/deposits/jetton-unique-addresses.ts).
90+
91+
## Security considerations
92+
93+
### Master-wallet verification
94+
95+
Never trust jetton wallet addresses without verification. Always perform these checks:
96+
97+
1. Get the jetton master address from the allowlist
98+
1. Call `jetton_master.get_wallet_address(owner_address)`
99+
1. Verify the returned address matches the jetton wallet that sent the notification
100+
101+
### Transfer notification validation
102+
103+
When processing deposits via `transfer_notification`:
104+
105+
- Verify the opcode is exactly `0x7362d09c`
106+
- Check the sender address is an expected jetton wallet
107+
- Extract `amount` in base units (not decimal)
108+
- Validate the `sender` field against expected user addresses
109+
- Parse `forward_payload` carefully—it may be malformed
110+
- Check for bounce indicators (single outgoing message back to sender)
111+
112+
### Fake jetton detection
113+
114+
Attackers can deploy jettons with identical names, symbols, and images:
115+
116+
- Always verify the jetton master address against the allowlist
117+
- Never trust metadata (name, symbol, image) for authentication
118+
- Display the master contract address in admin interfaces
119+
- Implement a manual approval workflow for adding new jettons
120+
121+
## Common attack patterns
122+
123+
### Fake jetton wallets
124+
125+
**Attack**: Attacker deploys a contract claiming to be a jetton wallet with an inflated balance.
126+
127+
**Mitigation**: Verify the master-wallet relationship by calling `get_wallet_address()` on the master contract.
128+
129+
### Invoice ID reuse
130+
131+
**Attack**: User attempts to reuse a settled invoice identifier.
132+
133+
**Mitigation**: Mark invoices as used after the first successful deposit.
134+
135+
### Master contract spoofing
136+
137+
**Attack**: Deploying a fake master contract that validates the attacker's fake jetton wallets.
138+
139+
**Mitigation**: Maintain a strict allowlist of trusted master contracts and verify all jetton wallets against it.
140+
141+
## Implementation checklist
142+
143+
Before enabling jetton processing in production:
144+
145+
### Testing
146+
147+
- [ ] Deploy and test on testnet with real user scenarios
148+
- [ ] Verify master-wallet relationships for all allowlisted jettons
149+
- [ ] Test with fake jetton wallets to confirm rejection
150+
- [ ] Validate transfer notification parsing with various payload formats
151+
- [ ] Test bounce detection and handling
152+
- [ ] Test invoice ID collision and reuse scenarios
153+
- [ ] Test full flow: deposit → credit → withdrawal → confirmation

0 commit comments

Comments
 (0)