Read this article after understanding the Jetton architecture.
JettonNotify message shows up.
Known Jetton, trusted deployer
When someone does a Jetton transfer, they deploy a newJettonWallet, that has an address depending on the address of JettonMinter and the address of the receiver, and then send a message with a type JettonNotify to the regular wallet (or other kind of contract) that tells the transfer succeeded. The attacker might send a JettonNotify message from a contract that is not a valid JettonWallet, so handling it requires care.
To handle the JettonNotify message, it’s preferable to precompute the address of the JettonWallet for a specific minter (i.e. Jetton type) in advance, so that the validation only consists of checking two addresses for equality.
Here is an example of a contract that handles JettonNotify messages. It’s deployed along with a SetTrustedJettonWallet message with an address of JettonWallet that is owned by this same contract.
When deploying with this pattern, calculate the Jetton wallet address that must receive top-ups. It is impossible to hardcode the jetton wallet address in the contract’s StateInit, because the wallet address depends on the contract address and creates a circular dependency.
To handle several Jetton types with a single contract, trustedJettonWallet should be modified to store several addresses.
The jettonMinter address is included in Storage, because otherwise every instance of this contract owned by the same owner will deploy to the same address, even if there was an intent to have them with different trustedJettonWallets.
This approach is used by Bidask.
Unknown TEP-89 Jetton or untrusted deployer
In the previous example, addresses ofjettonMinter and trustedJettonWallet are chosen off-chain by a deploying party. Someone who depends on this contract has to trust that the wallet belongs to this minter, which is not a problem, for example, for an exchange. If deployer might be monetarily interested to break this invariant, the previous approach is not suitable, and the address of the trustedJettonWallet address must be computed on-chain.
Most modern Jettons implement TEP-89, that defines a message provide_wallet_address that requests a Jetton wallet address for an arbitrary owner, and take_wallet_address message with a response.
The contract works similarly to the example above, except it’s deployed along with a InitContract message, that asks a JettonMinter what jettonWalletAddress should be.
This approach is used by DeDust.
The contract that initializes the jetton wallet with this pattern can look like this:
Unknown TEP-74 Jetton and untrusted deployer
Jettons that do not have TEP-89 methods for computing a wallet address on-chain are rare. TONCO DEX rejects them, while platforms such as DeDust allow them after a manual approval flow. There are two approaches, both using get-method emulation.Running get-method on-chain
Given a Jetton minter state, we can emulate execution of the minter on-chain via RUNVM instruction. During the emulation, it calls theget_wallet_address get-method to derive the wallet address for any owner.
This helper uses Fift, because it’s impossible to assign a type to c7.
Approach 1: Non-vanity addresses
If theJettonMinter was not deployed with a vanity contract, address and StateInit match. The logic for the contract that handles JettonNotify messages is thus
- get the
StateInitofJettonMinteroff-chain; - deploy the contract with the address of
JettonMinterin its data; - send a message to the contract with the
StateInitofJettonMinter; - contract validates that this
StateInitmatches the address of theJettonMinter; - contract runs
calculateJettonWalletowner- address of the contract (contract.getAddress());jettonData,jettonCode-StateInitofJettonMinter;jettonMinter- address ofJettonMinter.
Approach 2: where the previous method fails
If theJettonMinter was deployed with a vanity contract, or otherwise lacks get_wallet_address method, or get_wallet_address returns incorrect addresses, we have to use the current state of the contract instead.
To prove that the state is currently at the Jetton minter address, a full state proof is required.