JettonNotify message to notify receiving contracts of incoming jetton transfers, allowing them to react by crediting user accounts or triggering other actions. However, since anyone can send a JettonNotify message, receiving contracts must verify that it comes from the correct jetton wallet address.
Comparison of approaches
There are three approaches to verifyJettonNotify messages, depending on the level of trust in the contract deployer and whether the approach requires TEP-89.
| Requirement | Manual management | Automatic discovery | Get-method emulation |
|---|---|---|---|
| Jetton type known | Yes | No | No |
| Trust in deployer | High | Medium | Low |
| Jetton is TEP-89 compatible | No | Yes | No |
| Implementation complexity | Low | Medium | High |
Manual wallet management
If the receiving contract knows the jetton type in advance, it must only check that the message comes from the correct jetton wallet address. This is the most efficient approach, because it only requires one address comparison. The following example of a receiving contract handlesJettonNotify messages for a trusted jetton wallet address that can be set by the owner of the contract.
Tolk
SetTrustedJettonWallet messages to set the trusted jetton wallet address whose owner is the receiving contract address.
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.
Including jettonMinter in the contract’s Storage ensures that each instance of the contract is unique to a specific jetton, and thus has a unique address. This allows deploying multiple instances of the contract for different jettons, even if they share the same owner. Each instance can then set its own trustedJettonWallet address corresponding to its jettonMinter.
Modify trustedJettonWallet to store several addresses, to handle several jetton types with a single contract.
Automatic wallet discovery
In the first approach, the deploying party chooses addresses of the jetton minter and wallet contracts off-chain. The contract’s dependents have to trust that the wallet belongs to the corresponding minter, which is not a problem for an exchange. However, if the deployer is not trustworthy, the first approach is not suitable, and the contract must compute thetrustedJettonWallet address on-chain.
Most modern jetton contracts implement TEP-89, which defines a provide_wallet_address message that requests a jetton wallet address for an arbitrary owner, and a take_wallet_address message with a response.
The receiving contract works similarly to the first approach, except it is initialized with an InitContract message that asks the jetton minter which jetton wallet address it should use.
The following example of a receiving contract handles JettonNotify messages for a jetton wallet address that is unknown at the moment of deployment, and is set by the JettonMinter in response to the ProvideWalletAddress message sent during initialization.
Tolk
On-chain get-method emulation
If a jetton contract doesn’t implement TEP-89, it is possible to compute the jetton wallet address on-chain by executing theget_wallet_address method of the jetton minter. This approach doesn’t require any trust in the jetton deployer and works for jettons that don’t implement TEP-89.
With the state of a jetton minter, the RUNVM instruction can emulate the execution of the get_wallet_address get-method to derive the wallet address for any owner.
Use the following helper function. It relies on Fift because it’s impossible to assign a type to c7. It executes the get_wallet_address method of a jetton minter on-chain, and calculates the corresponding wallet address for a given owner.
Tolk/Fift
Minters without vanity addresses
Minter address andStateInit will match if the jetton minter was not deployed with a vanity contract. The logic for the contract that handles JettonNotify messages is thus:
- Get the
StateInitof the jetton minter off-chain. - Deploy the receiver contract with the address of the jetton minter in its data.
- Send a message to the contract with the
StateInitof the jetton minter. - Validate that
StateInitmatches the address of the jetton minter. - Run
calculateJettonWalletwith the following arguments:owner: address of the contract (e.g.,contract.getAddress()).jettonDataandjettonCode:StateInitof the jetton minter.jettonMinter: address of the jetton minter.
Minters with vanity addresses or invalid get-methods
If the jetton minter was deployed with a vanity contract, or otherwise lacksget_wallet_address method, or get_wallet_address returns incorrect addresses, use the current state of the contract instead.
A full state proof is required to prove that the state is currently at the jetton minter address.