From d9c9504f51b39551b58b984e64481a93e5f27d98 Mon Sep 17 00:00:00 2001
From: Full-Hat
Date: Wed, 24 Apr 2024 12:01:19 +0300
Subject: [PATCH 01/15] start to rewrite patment processing & jetton processing
---
docs/develop/dapps/asset-processing/README.md | 300 ++++++++++--------
.../develop/dapps/asset-processing/jettons.md | 220 +++++--------
2 files changed, 251 insertions(+), 269 deletions(-)
diff --git a/docs/develop/dapps/asset-processing/README.md b/docs/develop/dapps/asset-processing/README.md
index 4c0c3b7026..c12bddb731 100644
--- a/docs/develop/dapps/asset-processing/README.md
+++ b/docs/develop/dapps/asset-processing/README.md
@@ -1,104 +1,54 @@
import Button from '@site/src/components/button'
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
-# Processing Global Overview
+# Payments processing
-This page contains an overview and specific details that explain how to process (send and accept) digital assets on the TON blockchain.
+This page contains an overview on TON transactions and specific details that explain how to work with ton wallets and process (send and accept) digital assets on the TON blockchain.
-:::info Transaction Confirmation
-TON transactions are irreversible after just one confirmation. For the best user experience, it is suggested to avoid waiting on additional blocks once transactions are finalized on the TON Blockchain. Read more in the [Catchain.pdf](https://docs.ton.org/catchain.pdf#page=3).
-:::
-
-## Best Practices
-
-### Toncoin
-
-#### Toncoin Deposits
-
-:::info
-It is suggested to set several MEMO deposit wallets for better performance.
-:::
-
-- [MEMO Deposits](https://github.com/toncenter/examples/blob/main/deposits.js)
-
-#### Toncoin Withdrawals
-
-- [Batched withdrawals](https://github.com/toncenter/examples/blob/main/withdrawals-highload-batch.js)
-- [Withdrawals](https://github.com/toncenter/examples/blob/main/withdrawals-highload.js)
-
-
-- [Detailed info](/develop/dapps/asset-processing#global-overview)
-
-### Jetton
-
-- [Read Jetton Proccesing](/develop/dapps/asset-processing/jettons)
-
-
-## Ready Solutions for CEX
-
-### Tonapi Embed
+## Global overview
+Embodying a fully asynchronous approach, TON Blockchain involves a few concepts which are uncommon to traditional blockchains. Particularly, each interaction of any actor with the blockchain consists of a graph of asynchronously transferred [messages](/develop/smart-contracts/guidelines/message-delivery-guarantees) between smart contracts and/or the external world. Each transaction consists of one incoming message and up to 512 outgoing messages.
-Tonapi Embed - on-premises solution designed to operate with deposits and withdrawals, ensuring high-performance and lightweight deployment.
+There are 3 types of messages, that are fully described [here](/develop/smart-contracts/messages#types-of-messages). To put it briefly:
+* [external message](/develop/smart-contracts/guidelines/external-messages):
+ * `external in message` (sometimes called just `external message`) is message that is sent from *outside* of the blockchain to a smart contract *inside* the blockchain.
+ * `external out message` (usually called `logs message`) is sent from a *blockchain entity* to the *outer world*.
+* [internal message](/develop/smart-contracts/guidelines/internal-messages) is sent from one *blockchain entity* to *another*, can carry some amount of digital assets and arbitrary portion of data.
-* Trust-less system running on any TON Liteservers.
-* Maintaining deposits and withdrawals for Toncoin and Jettons as well.
-* Solution developed according to the recommended MEMO-deposits and highload withdrawals guidelines provided by TF Core team.
+The common path of any interaction starts with an external message sent to a `wallet` smart contract, which authenticates the message sender using public-key cryptography, takes charge of fee payment, and sends internal blockchain messages. That messages queue form directional acyclic graph, or a tree.
-For cooperation, please contact to [@tonrostislav](https://t.me/tonrostislav).
+Each action, when contract take message as input (triggered by it), process it and generate outgoing messages as output, called `transaction`. Read more about transactions [here](/develop/smart-contracts/guidelines/message-delivery-guarantees#what-is-a-transaction).
-## Made by TON Community
+That `transactions` can span a **prolonged period** of time. Technically, transactions with queues of messages are aggregated into blocks processed by validators. The asynchronous nature of the TON Blockchain **does not allow to predict the hash and lt (logical time) of a transaction** at the stage of sending a message.
-#### GO
+The `transaction` accepted to the block is final and cannot be modified.
-- [Gobicycle](https://github.com/gobicycle/bicycle) - service is focused on replenishing user balances and sending payments to blockchain accounts. Both TONs and Jettons are supported. The service is written with numerous pitfalls in mind that a developer might encounter (all checks for jettons, correct operations status check, resending messages, performance during high load when blockchain is splitted by shards). Provide simple HTTP API, rabbit and webhook notifications about new payments.
-- [GO examples](https://github.com/xssnick/tonutils-go#how-to-use)
-
-#### JavaScript
-
-Using ton.js SDK (supported by TON Community):
-
-- [Create a key pair, a wallet and get a wallet address](https://github.com/toncenter/examples/blob/main/common.js)
-- [Create a wallet, get its balance, make a transfer](https://github.com/ton-community/ton#usage)
-
-#### Python
-
-Using tonsdk library (similar to tonweb):
-
-- [Init wallet, create external message to deploy the wallet](https://github.com/tonfactory/tonsdk#create-mnemonic-init-wallet-class-create-external-message-to-deploy-the-wallet)
-
-
-## Global Overview
-Embodying a fully asynchronous approach, TON Blockchain involves a few concepts which are uncommon to traditional blockchains. Particularly, each interaction of any actor with the blockchain consists of a graph of asynchronously transferred messages between smart contracts and/or the external world. The common path of any interaction starts with an external message sent to a `wallet` smart contract, which authenticates the message sender using public-key cryptography, takes charge of fee payment, and sends inner blockchain messages. That way, transactions on the TON network are not synonymous with user interaction with the blockchain but merely nodes of the message graph: the result of accepting and processing a message by a smart contract, which may or may not lead to the emergence of new messages. The interaction may consist of an arbitrary number of messages and transactions and span a prolonged period of time. Technically, transactions with queues of messages are aggregated into blocks processed by validators. The asynchronous nature of the TON Blockchain **does not allow to predict the hash and lt (logical time) of a transaction** at the stage of sending a message. The transaction accepted to the block is final and cannot be modified.
-
-**Each inner blockchain message is a message from one smart contract to another, which bears some amount of digital assets, as well as an arbitrary portion of data.**
-
-Smart contract guidelines recommend treating the data payload, which begins with 32 binary zeros, as a human-readable text message. Most software, such as wallets and libraries, support this specification and allow to send text comments along with Toncoin as well as display comments in other messages.
-
-Smart contracts **pay fees for transactions** (usually from the balance of an incoming message) as well as a **storage fee for the contract's stored code and data**. Fees depend on workchain configs with maximal fees on `masterchain` and substantially lower fees on `basechain`.
-
-
+:::info Transaction Confirmation
+TON transactions are irreversible after just one confirmation. For the best user experience, it is suggested to avoid waiting on additional blocks once transactions are finalized on the TON Blockchain. Read more in the [Catchain.pdf](https://docs.ton.org/catchain.pdf#page=3).
+:::
-* `external message` is the input message for `wallet A v4` contract with empty soure (a message from nowhere, such as [Tonkeeper](https://tonkeeper.com/)).
-* `outgoing message` is the output message for `wallet A v4` contract and input message for `wallet B v4` contract with `wallet A v4` source and `wallet B v4` destination.
+Smart contracts pay several types of [fees](/develop/smart-contracts/fees) for transactions (usually from the balance of an incoming message, behavior depends on [message mode](/develop/smart-contracts/messages#message-modes)). Amount of fees depends on workchain configs with maximal fees on `masterchain` and substantially lower fees on `basechain`.
-As a result there are 2 transactions with their set of input and output messages.
+## Digital asset types on TON
-## Digital assets on TON
TON has three types of digital assets.
- Toncoin, the main token of the network. It is used for all basic operations on the blockchain, for example, paying gas fees or staking for validation.
-- Native tokens, which are special kinds of assets that can be attached to any message on the network. These assets are currently not in use since the functionality for issuing new native tokens is closed.
-- Contract assets, such as tokens and NFTs, which are analogous to the ERC-20/ERC-721 standards and are managed by arbitrary contracts and thus can require custom rules for processing. You can find more info on it's processin in [process NFTs](/develop/dapps/asset-processing/nfts) and [process Jettons](/develop/dapps/asset-processing/jettons) articles.
-
+- Contract assets, such as tokens and NFTs, which are analogous to the ERC-20/ERC-721 standards and are managed by arbitrary contracts and thus can require custom rules for processing. You can find more info on it's processing in [process NFTs](/develop/dapps/asset-processing/nfts) and [process Jettons](/develop/dapps/asset-processing/jettons) articles.
+- Native token, which is special kind of assets that can be attached to any message on the network. But these asset is currently not in use since the functionality for issuing new native tokens is closed.
-### Simple Toncoin transfer
-To send Toncoin, the user needs to send a request via an external message, that is, a message from the outside world to the blockchain, to a special `wallet` smart contract (see below). Upon receiving this request, `wallet` will send an inner message with the desired amount of assets and optional data payload, for instance a text comment.
+## Interaction with TON blockchain
+Basic operations on TON Blockchain can be carried out via TonLib. It is a shared library which can be compiled along with a TON node and expose APIs for interaction with the blockchain via so-called lite servers (servers for lite clients). TonLib follows a trustless approach by checking proofs for all incoming data; thus, there is no necessity for a trusted data provider. Methods available to TonLib are listed [in the TL scheme](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L234). They can be used either as a shared library via wrappers like [pyTON](https://github.com/EmelyanenkoK/pyTON) or [tonlib-go](https://github.com/mercuryoio/tonlib-go/tree/master/v2) (technically those are the wrappers for `tonlibjson`) or through `tonlib-cli`.
## Wallet smart contract
+
Wallet smart contracts are contracts on the TON Network which serve the task of allowing actors outside the blockchain to interact with blockchain entities. Generally, it solves three challenges:
* authenticates owner: Rejects to process and pay fees for non-owners' requests.
* replays protection: Prohibits the repetitive execution of one request, for instance sending assets to some other smart contract.
* initiates arbitrary interaction with other smart contracts.
-Standard solution for the first challenge is public-key cryptography: `wallet` stores the public key and checks that an incoming message with a request is signed by the corresponding private key which is known only by the owner. The solution to the third challenge is common as well; generally, a request contains a fully formed inner message `wallet` sends to the network. However, for replay protection, there are a few different approaches.
+Standard solution for the first challenge is public-key cryptography: `wallet` stores the public key and checks that an incoming message with a request is signed by the corresponding private key which is known only by the owner.
+
+The solution to the third challenge is common as well; generally, a request contains a fully formed inner message `wallet` sends to the network. However, for replay protection, there are a few different approaches.
### Seqno-based wallets
Seqno-based wallets follow the most simple approach to sequencing messages. Each message has a special `seqno` integer that must coincide with the counter stored in the `wallet` smart contract. `wallet` updates its counter on each request, thus ensuring that one request will not be processed twice. There are a few `wallet` versions that differ in publicly available methods: the ability to limit requests by expiration time, and the ability to have multiple wallets with the same public key. However, an inherent requirement of that approach is to send requests one by one, since any gap in `seqno` sequence will result in the inability to process all subsequent requests.
@@ -106,14 +56,10 @@ Seqno-based wallets follow the most simple approach to sequencing messages. Each
### High-load wallets
This `wallet` type follows an approach based on storing the identifier of the non-expired processed requests in smart-contract storage. In this approach, any request is checked for being a duplicate of an already processed request and, if a replay is detected, dropped. Due to expiration, the contract may not store all requests forever, but it will remove those that cannot be processed due to the expiration limit. Requests to this `wallet` may be sent in parallel without interfering with each other; however, this approach requires more sophisticated monitoring of request processing.
-## Interaction with blockchain
-Basic operations on TON Blockchain can be carried out via TonLib. It is a shared library which can be compiled along with a TON node and expose APIs for interaction with the blockchain via so-called lite servers (servers for lite clients). TonLib follows a trustless approach by checking proofs for all incoming data; thus, there is no necessity for a trusted data provider. Methods available to TonLib are listed [in the TL scheme](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L234). They can be used either as a shared library via wrappers like [pyTON](https://github.com/EmelyanenkoK/pyTON) or [tonlib-go](https://github.com/mercuryoio/tonlib-go/tree/master/v2) (technically those are the wrappers for `tonlibjson`) or through `tonlib-cli`.
-
-
-## Wallet deployment
+### Wallet deployment
To deploy a wallet via TonLib one needs to:
1. Generate a private/public key pair via [createNewKey](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L213) or its wrapper functions (example in [tonlib-go](https://github.com/mercuryoio/tonlib-go/tree/master/v2#create-new-private-key)). Note that the private key is generated locally and does not leave the host machine.
-2. Form [InitialAccountWallet](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L60) structure corresponding to one of the enabled `wallets`. Currently `wallet.v3`, `wallet.highload.v1`, `wallet.highload.v2` are available.
+2. Form [InitialAccountWallet](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L60) structure corresponding to one of the enabled `wallets`. Currently `wallet.v3`, `wallet.v4`, `wallet.highload.v1`, `wallet.highload.v2` are available.
3. Calculate the address of a new `wallet` smart contract via the [getAccountAddress](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L249) method. We recommend using a default revision `0` and also deploying wallets in the basechain `workchain=0` for lower processing and storage fees.
4. Send some Toncoin to the calculated address. Note that you need to send them in `non-bounce` mode since this address has no code yet and thus cannot process incoming messages. `non-bounce` flag indicates that even if processing fails, money should not be returned with a bounce message. We do not recommend using the `non-bounce` flag for other transactions, especially when carrying large sums, since the bounce mechanism provides some degree of protection against mistakes.
5. Form the desired [action](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L148), for instance `actionNoop` for deploy only. Then use [createQuery](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L255) and [sendQuery](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L260) to initiate interactions with the blockchain.
@@ -123,13 +69,9 @@ To deploy a wallet via TonLib one needs to:
Read more in the [Wallet Tutorial](/develop/smart-contracts/tutorials/wallet#-deploying-a-wallet)
:::
+## Work with transfers
-## Incoming message value
-To calculate the incoming value that the message brings to the contract, one needs to parse the transaction. It happens when the message hits the contract. A transaction can be obtained using [getTransactions](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L236). For an incoming wallet transaction, the correct data consists of one incoming message and zero outgoing messages. Otherwise, either an external message is sent to the wallet, in which case the owner spends Toncoin, or the wallet is not deployed and the incoming transaction bounces back.
-
-Anyway, in general, the amount that a message brings to the contract can be calculated as the value of the incoming message minus the sum of the values of the outgoing messages minus the fee: `value_{in_msg} - SUM(value_{out_msg}) - fee`. Technically, transaction representation contains three different fields with `fee` in name: `fee`, `storage_fee`, and `other_fee`, that is, a total fee, a part of the fee related to storage costs, and a part of the fee related to transaction processing. Only the first one should be used.
-
-## Checking contract's transactions
+### Check contract's transactions
A contract's transactions can be obtained using [getTransactions](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L236). This method allows to get 10 transactions from some `transactionId` and earlier. To process all incoming transactions, the following steps should be followed:
1. The latest `last_transaction_id` can be obtained using [getAccountState](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L235)
2. List of 10 transactions should be loaded via the `getTransactions` method.
@@ -137,54 +79,31 @@ A contract's transactions can be obtained using [getTransactions](https://github
4. Incoming payments are transactions in which the incoming message has a source address; outgoing payments are transactions in which the incoming message has no source address and also presents the outgoing messages. These transactions should be processed accordingly.
5. If all of those 10 transactions are unseen, the next 10 transactions should be loaded and steps 2,3,4,5 should be repeated.
-## Checking transactions' flow
+### Checking transaction's flow
It's possible to track messages flow during transaction processing. Since the message flow is a DAG it's enough to get the input `in_msg` or output `out_msgs` messages of current transaction using [getTransactions](https://toncenter.com/api/v2/#/transactions/get_transactions_getTransactions_get) method to find incoming transaction with [tryLocateResultTx](https://testnet.toncenter.com/api/v2/#/transactions/get_try_locate_result_tx_tryLocateResultTx_get) or outgoing transactions with [tryLocateSourceTx](https://testnet.toncenter.com/api/v2/#/transactions/get_try_locate_source_tx_tryLocateSourceTx_get).
-## Accepting payments
-There are a few approaches to accepting payments that differ in their method of distinguishing users.
+### Send payments
+
+1. Service should deploy a `wallet` and keep it funded to prevent contract destruction due to storage fees. Note that storage fees are generally less than 1 Toncoin per year.
+2. Service should get from the user `destination_address` and optional `comment`. Note that for the meantime, we recommend either prohibiting unfinished outgoing payments with the same (`destination_address`, `value`, `comment`) set or proper scheduling of those payments; that way, the next payment is initiated only after the previous one is confirmed.
+3. Form [msg.dataText](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L98) with `comment` as text.
+4. Form [msg.message](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L108) which contains `destination_address`, empty `public_key`, `amount` and `msg.dataText`.
+5. Form [Action](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L149) which contains a set of outgoing messages.
+6. Use [createQuery](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L255) and [sendQuery](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L260) queries to send outgoing payments.
+7. Service should regularly poll the getTransactions method for the `wallet` contract. Matching confirmed transactions with the outgoing payments by (`destination_address`, `value`, `comment`) allows to mark payments as finished; detect and show the user the corresponding transaction hash and lt (logical time).
+8. Requests to `v3` of `high-load` wallets have an expiration time equal to 60 seconds by default. After that time unprocessed requests can be safely resent to the network (see steps 3-6).
-### Invoice-based approach
+## Invoice-based payment accept
To accept payments based on attached comments, the service should
1. Deploy the `wallet` contract.
2. Generate a unique `invoice` for each user. String representation of uuid32 will be enough.
3. Users should be instructed to send Toncoin to the service's `wallet` contract with an attached `invoice` as a comment.
4. Service should regularly poll the getTransactions method for the `wallet` contract.
-5. For new transactions, the incoming message should be extracted, `comment` matched against the database, and the value (see **Incoming message value** paragraph) deposited to the user's account.
+5. For new transactions, the incoming message should be extracted, `comment` matched against the database, and the **incoming message value** deposited to the user's account.
-## Invoices
-
-### Invoices with ton:// link
-
-If you need an easy integration for a simple user flow, it is suitable to use the ton:// link.
-Best suited for one-time payments and invoices with Toncoin.
-
-```text
-ton://transfer/?
- [amount=&]
- [text=]
-```
-
-Example of ton:// link generation:
-
-```typescript
-const tonLink = `ton://transfer/${address.toString({
- urlSafe: true,
-})}?amount=${amount}${text ? `&text=${encodeURIComponent(text)}` : ''}`;
-```
-
-- ✅ Easy integration
-- ✅ No need to connect a wallet
-
-- ❌ Users need to scan a new QR code for each payment
-- ❌ It's not possible to track whether the user has signed the transaction or not
-- ❌ No information about the user's address
-- ❌ Workarounds are needed on platforms where such links are not clickable (e.g. messages from bots for Telegram desktop clients )
+To calculate the **incoming message value** that the message brings to the contract, one needs to parse the transaction. It happens when the message hits the contract. A transaction can be obtained using [getTransactions](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L236). For an incoming wallet transaction, the correct data consists of one incoming message and zero outgoing messages. Otherwise, either an external message is sent to the wallet, in which case the owner spends Toncoin, or the wallet is not deployed and the incoming transaction bounces back.
-
-
+Anyway, in general, the amount that a message brings to the contract can be calculated as the value of the incoming message minus the sum of the values of the outgoing messages minus the fee: `value_{in_msg} - SUM(value_{out_msg}) - fee`. Technically, transaction representation contains three different fields with `fee` in name: `fee`, `storage_fee`, and `other_fee`, that is, a total fee, a part of the fee related to storage costs, and a part of the fee related to transaction processing. Only the first one should be used.
### Invoices with TON Connect
@@ -204,17 +123,32 @@ colorType="primary" sizeType={'lg'}>
Learn More
+### Invoices with ton:// link
-## Sending payments
+:::warning
+Ton link is deprecated, avoid using it
+:::
-1. Service should deploy a `wallet` and keep it funded to prevent contract destruction due to storage fees. Note that storage fees are generally less than 1 Toncoin per year.
-2. Service should get from the user `destination_address` and optional `comment`. Note that for the meantime, we recommend either prohibiting unfinished outgoing payments with the same (`destination_address`, `value`, `comment`) set or proper scheduling of those payments; that way, the next payment is initiated only after the previous one is confirmed.
-3. Form [msg.dataText](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L98) with `comment` as text.
-4. Form [msg.message](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L108) which contains `destination_address`, empty `public_key`, `amount` and `msg.dataText`.
-5. Form [Action](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L149) which contains a set of outgoing messages.
-6. Use [createQuery](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L255) and [sendQuery](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L260) queries to send outgoing payments.
-7. Service should regularly poll the getTransactions method for the `wallet` contract. Matching confirmed transactions with the outgoing payments by (`destination_address`, `value`, `comment`) allows to mark payments as finished; detect and show the user the corresponding transaction hash and lt (logical time).
-8. Requests to `v3` of `high-load` wallets have an expiration time equal to 60 seconds by default. After that time unprocessed requests can be safely resent to the network (see steps 3-6).
+If you need an easy integration for a simple user flow, it is suitable to use the ton:// link.
+Best suited for one-time payments and invoices.
+
+```bash
+ton://transfer/?
+ [nft=&]
+ [fee-amount=&]
+ [forward-amount=]
+```
+
+- ✅ Easy integration
+- ✅ No need to connect a wallet
+
+- ❌ Users need to scan a new QR code for each payment
+- ❌ It's not possible to track whether the user has signed the transaction or not
+- ❌ No information about the user's address
+- ❌ Workarounds are needed on platforms where such links are not clickable (e.g. messages from bots for Telegram desktop clients )
+
+
+[Learn more about ton links here](https://github.com/tonkeeper/wallet-api#payment-urls)
## Explorers
@@ -227,3 +161,95 @@ To generate a transaction link in the explorer, the service needs to get the lt
`https://tonscan.org/tx/{lt as int}:{txhash as base64url}:{account address}`
`https://explorer.toncoin.org/transaction?account={account address}<={lt as int}&hash={txhash as base64url}`
+
+## Best Practices
+
+### Wallet creation
+
+
+
+
+[Wallet creation + get wallet address](https://github.com/toncenter/examples/blob/main/common.js)
+
+
+
+
+[Wallet creation + get balance](https://github.com/ton-community/ton#usage)
+
+
+
+
+[Wallet creation + get wallet address](https://github.com/psylopunk/pytonlib/blob/main/examples/generate_wallet.py)
+
+
+
+
+[Wallet creation + get balance](https://github.com/xssnick/tonutils-go?tab=readme-ov-file#wallet)
+
+
+
+
+### Toncoin Deposits (Get toncoins)
+
+
+
+
+[Process Toncoins deposit](https://github.com/toncenter/examples/blob/main/deposits.js)
+
+[Process Toncoins deposit multi wallets](https://github.com/toncenter/examples/blob/main/deposits-multi-wallets.js)
+
+
+
+
+### Toncoin Withdrawals (Send toncoins)
+
+
+
+
+[Withdraw Toncoins from a wallet in batches](https://github.com/toncenter/examples/blob/main/withdrawals-highload-batch.js)
+
+[Withdraw Toncoins from a wallet](https://github.com/toncenter/examples/blob/main/withdrawals-highload.js)
+
+
+
+
+[Withdraw Toncoins from a wallet](https://github.com/ton-community/ton#usage)
+
+
+
+
+[Withdraw Toncoins from a wallet](https://github.com/psylopunk/pytonlib/blob/main/examples/transactions.py)
+
+
+
+
+ [Withdraw Toncoins from a wallet](https://github.com/xssnick/tonutils-go?tab=readme-ov-file#wallet)
+
+
+
+
+## Repositories
+
+### Golang
+
+- [Tonutils](https://github.com/xssnick/tonutils-go#how-to-use)
+- **Self-hosted service**
+
+ [Gobicycle](https://github.com/gobicycle/bicycle) service is focused on replenishing user balances and sending payments to blockchain accounts. Both TONs and Jettons are supported. The service is written with numerous pitfalls in mind that a developer might encounter (all checks for jettons, correct operations status check, resending messages, performance during high load when blockchain is splitted by shards). Provide simple HTTP API, rabbit and webhook notifications about new payments.
+
+### JavaScript
+
+ Using ton.js SDK (supported by TON Community):
+
+- [Create a wallet, get its balance, make a transfer](https://github.com/ton-community/ton#usage)
+
+### Python
+
+ Using psylopunk/pytonlib (Simple Python client for The Open Network):
+
+- [Sending transactions](https://github.com/psylopunk/pytonlib/blob/main/examples/transactions.py)
+
+ Using tonsdk library (similar to tonweb):
+
+- [Init wallet, create external message to deploy the wallet](https://github.com/tonfactory/tonsdk#create-mnemonic-init-wallet-class-create-external-message-to-deploy-the-wallet)
+
diff --git a/docs/develop/dapps/asset-processing/jettons.md b/docs/develop/dapps/asset-processing/jettons.md
index 530dcd044b..79fd4bf6fa 100644
--- a/docs/develop/dapps/asset-processing/jettons.md
+++ b/docs/develop/dapps/asset-processing/jettons.md
@@ -2,80 +2,24 @@ import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import Button from '@site/src/components/button';
-# Jetton Processing
+# TON Jetton processing
-## Best Practices on Jettons Processing
-
-Jettons are tokens on TON Blockchain - one can consider them similarly to ERC-20 tokens on Ethereum.
-
-:::info Transaction Confirmation
-TON transactions are irreversible after just one confirmation. For the best UX/UI avoid additional waiting.
-:::
-
-#### Withdrawal
-
-[Highload Wallet v3](/participate/wallets/contracts#highload-wallet-v3) - this is TON Blockchain latest solution which is the gold standard for jetton withdrawals. It allows you to take advantage of batched withdrawals.
-
-[Batched withdrawals](https://github.com/toncenter/examples/blob/main/withdrawals-jettons-highload-batch.js) - Meaning that multiple withdrawals are sent in batches, allowing for quick and cheap withdrawals.
-
-#### Deposits
-:::info
-It is suggested to set several MEMO deposit wallets for better performance.
-:::
-
-[Memo Deposits](https://github.com/toncenter/examples/blob/main/deposits-jettons.js) - This allows you to keep one deposit wallet, and users add a memo in order to be identified by your system. This means that you don’t need to scan the entire blockchain, but is slightly less easy for users.
-
-[Memo-less deposits](https://github.com/gobicycle/bicycle) - This solution also exists, but is more difficult to integrate. However, we can assist with this, if you would prefer to take this route. Please notify us before deciding to implement this approach.
-
-
-### Additional Info
-
-:::caution Transaction Notification
-if you will be allowing your users set a custom memo when withdrawing jettons - make sure to set forwardAmount to 0.000000001 TON (1 nanoton) whenever a memo (text comment) is attached, otherwise the transfer will not be standard compliant and will not be able to be processed by other CEXes and other such services.
-:::
-
-- Please find the JS lib example - [tonweb](https://github.com/toncenter/tonweb) - which is the official JS library from the TON Foundation.
-
-- If you want to use Java, you can look into [ton4j](https://github.com/neodix42/ton4j/tree/main).
-
-- For Go, one should consider [tonutils-go](https://github.com/xssnick/tonutils-go). At the moment, we recommend the JS lib.
-
-## Ready Solutions for CEX
-
-### Tonapi Embed
-
-Tonapi Embed - on-premises solution designed to operate with deposits and withdrawals, ensuring high-performance and lightweight deployment.
-
-* Trust-less system running on any TON Liteservers.
-* Maintaining deposits and withdrawals for Toncoin and Jettons as well.
-* Solution developed according to the recommended MEMO-deposits and highload withdrawals guidelines provided by TF Core team.
-
-For cooperation, please contact to [@tonrostislav](https://t.me/tonrostislav).
-
-
-## Jetton Processing Global Overview
-
-### Content List
-
-:::tip
-In following docs offers details about Jettons architecture generally, as well as core concepts of TON which may be different from EVM-like and other blockchains. This is crucial reading in order for one to grasp a good understanding of TON, and will greatly help you.
-:::
+## Content List
This document describes the following in order:
-1. Introduction
+1. Overview
2. Architecture
-2. Jetton Master Contract (Token Minter)
-3. Jetton Wallet Contract (User Wallet)
-4. Message Layouts
-4. Jetton Processing (off-chain)
-5. Jetton Processing (on-chain)
-6. Wallet processing
-7. Best Practices
+3. Jetton Master Contract (Token Minter)
+4. Jetton Wallet Contract (User Wallet)
+5. Message Layouts
+6. Jetton Processing (off-chain)
+7. Jetton Processing (on-chain)
+8. Wallet processing
+9. Best Practices
-### Introduction
+## Overview
:::info
-TON transactions are irreversible after just one confirmation.
For clear understanding, the reader should be familiar with the basic principles of asset processing described in [this section of our documentation](/develop/dapps/asset-processing/). In particular, it is important to be familiar with [contracts](/learn/overviews/addresses#everything-is-a-smart-contract), [wallets](/develop/smart-contracts/tutorials/wallet), [messages](/develop/smart-contracts/guidelines/message-delivery-guarantees) and deployment process.
:::
@@ -83,8 +27,8 @@ Quick jump to the core description of jetton processing:
@@ -98,9 +42,6 @@ A less formal sharding-focused overview of jetton architecture can be found in o
We have also provided specific details discussing our third-party open-source TON Payment Processor ([bicycle](https://github.com/gobicycle/bicycle)) which allows users to deposit and withdraw both Toncoin and jettons using a separate deposit address without using a text memo.
-
-
-
## Jetton Architecture
Standardized tokens on TON are implemented using a set of smart contracts, including:
@@ -114,27 +55,27 @@ Standardized tokens on TON are implemented using a set of smart contracts, inclu
## Jetton master smart contract
-The jetton master smart contract stores general information about the jetton (
-
-including the total supply, a metadata link, or the metadata itself).
+The jetton master smart contract stores general information about the jetton (including the total supply, a metadata link, or the metadata itself).
-It is possible for any user to create a counterfeit clone of a valuable jetton (using an arbitrary name, ticker, image, etc.) that is nearly identical to the original. Thankfully, counterfeit jettons are distinguishable by their addresses and can be identified quite easily.
+:::info Jetton scam
+It is possible for any user to create a counterfeit **clone** of a valuable jetton (using an arbitrary name, ticker, image, etc.) that is **nearly identical to the original**. Thankfully, counterfeit jettons are **distinguishable by their addresses** and can be identified quite easily.
To eliminate the possibility of fraud for TON users, please look up the original jetton address (Jetton master contract) for specific jetton types or follow the project’s official social media channel or website to find the correct information. Check assets to eliminate the possibility of fraud with [Tonkeeper ton-assets list](https://github.com/tonkeeper/ton-assets).
+:::
### Retrieving Jetton data
-To retrieve more specific Jetton data, the `get_jetton_data()` get method is used.
+To retrieve more specific Jetton data use _get_ method `get_jetton_data()`.
This method returns the following data:
-| Name | Type | Description |
-|--------------------|-------|-------------------- |
-| `total_supply` | `int` | the total number of issued jettons measured in indivisible units. |
-| `mintable` | `int` | details whether new jettons can be minted or not. This value is either -1 (can be minted) or 0 (cannot be minted). |
-| `admin_address` | `slice` | |
-| `jetton_content` | `cell` | data in accordance with [TEP-64](https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md). |
-| `jetton_wallet_code` | `cell` | |
+| Name | Type | Description |
+|--------------------|-------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `total_supply` | `int` | the total number of issued jettons measured in indivisible units. |
+| `mintable` | `int` | details whether new jettons can be minted or not. This value is either -1 (can be minted) or 0 (cannot be minted). |
+| `admin_address` | `slice` | |
+| `jetton_content` | `cell` | data in accordance with [TEP-64](https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md), check [jetton metadata parsing page](/develop/dapps/asset-processing/metadata) for more. |
+| `jetton_wallet_code` | `cell` | |
@@ -151,10 +92,7 @@ console.log('Total supply:', data.totalSupply.toString());
console.log('URI to off-chain metadata:', data.jettonContentUri);
```
-#### Jetton metadata
-More info on parsing metadata is provided [here](/develop/dapps/asset-processing/metadata).
-
-## Jetton Wallet smart contracts
+## Jetton wallet smart contract
Jetton wallet contracts are used to send, receive, and burn jettons. Each _jetton wallet contract_ stores wallet balance information for specific users.
In specific instances, jetton wallets are used for individual jetton holders for each jetton type.
@@ -169,17 +107,24 @@ When Alice initiates the sending of jettons in a wallet she manages, she sends e
and as a result, _her wallet_ sends an internal message to _her jetton wallet_ and
then the jetton wallet actually executes the token transfer.
+### Jetton Wallet Deployment
+When transferring jettons between wallets, transactions (messages) require a certain amount of TON
+as payment for network gas fees and the execution of actions according to the Jetton wallet contract's code.
+This means that the recipient does not need to deploy a jetton wallet prior to receiving jettons.
+The recipient's jetton wallet will be deployed automatically as long as the sender holds enough TON
+in the wallet to pay the required gas fees.
+
### Retrieving Jetton wallet addresses for a given user
To retrieve a jetton wallet address using an owner address (a TON Wallet address),
the Jetton master contract provides the get method `get_wallet_address(slice owner_address)`.
-#### Retrieve using API
-The application serializes the owner’s address to a cell using
-the `/runGetMethod` method from the [Toncenter API](https://toncenter.com/api/v3/#/default/run_get_method_api_v3_runGetMethod_post).
+
+
-#### Retrieve using SDK
-This process can also be initiated using ready to use methods present in our various SDKs, for instance,
-using the Tonweb SDK, this process can be initiated by entering the following strings:
+> Run `get_wallet_address(slice owner_address)` through `/runGetMethod` method from the [Toncenter API](https://toncenter.com/api/v3/#/default/run_get_method_api_v3_runGetMethod_post).
+
+
+
```js
import TonWeb from "tonweb";
@@ -197,13 +142,13 @@ if (jettonData.jettonMinterAddress.toString(false) !== new TonWeb.utils.Address(
console.log('Jetton wallet address:', address.toString(true, true, true));
```
-:::tip
-For more examples read the [TON Cookbook](/develop/dapps/cookbook#how-to-calculate-users-jetton-wallet-address).
-:::
+
+
+
### Retrieving data for a specific Jetton wallet
-To retrieve the wallet’s account balance, owner identification information, and other info related to a specific jetton wallet contract, the `get_wallet_data()` get method is used within the jetton wallet contract.
+To retrieve the wallet’s account balance, owner identification information, and other info related to a specific jetton wallet contract use the `get_wallet_data()` get method within the jetton wallet contract.
This method returns the following data:
@@ -215,7 +160,14 @@ This method returns the following data:
| jetton | slice |
| jetton_wallet_code | cell |
-It is also possible to use the `/jetton/wallets` get method using the [Toncenter API](https://toncenter.com/api/v3/#/default/get_jetton_wallets_api_v3_jetton_wallets_get) to retrieve previously decoded jetton wallet data (or methods within an SDK). For instance, using Tonweb:
+
+
+
+> Use the `/jetton/wallets` get method from the [Toncenter API](https://toncenter.com/api/v3/#/default/get_jetton_wallets_api_v3_jetton_wallets_get) to retrieve previously decoded jetton wallet data.
+
+
+
+
```js
import TonWeb from "tonweb";
@@ -235,14 +187,10 @@ if (expectedJettonWalletAddress.toString(false) !== new TonWeb.utils.Address(wal
console.log('Jetton master address:', data.jettonMinterAddress.toString(true, true, true));
```
-### Jetton Wallet Deployment
-When transferring jettons between wallets, transactions (messages) require a certain amount of TON
-as payment for network gas fees and the execution of actions according to the Jetton wallet contract's code.
-This means that the recipient does not need to deploy a jetton wallet prior to receiving jettons.
-The recipient's jetton wallet will be deployed automatically as long as the sender holds enough TON
-in the wallet to pay the required gas fees.
+
+
-## Message Layouts
+## Messages in jetton wallets communication
:::tip Messages
Read more about Messages [here](/develop/smart-contracts/guidelines/message-delivery-guarantees).
@@ -253,30 +201,30 @@ Communication between Jetton wallets and TON wallets occurs through the followin

-`Sender -> sender' jetton wallet` means the _transfer_ message body contains the following data:
+`Sender -> sender's jetton wallet` means the _transfer_ message body contains the following data:
-| Name | Type |
-|----------------------|---------|
-| `query_id ` | uint64 |
-| `amount ` | coins |
-| `destination ` | address |
+| Name | Type |
+|------------------|---------|
+| `query_id` | uint64 |
+| `amount` | coins |
+| `destination` | address |
| `response_destination` | address |
-| `custom_payload ` | cell |
-| `forward_ton_amount` | coins |
-| `forward_payload` | cell |
+| `custom_payload` | cell |
+| `forward_ton_amount` | coins |
+| `forward_payload` | cell |
-`payee' jetton wallet -> payee` means the message notification body contains the following data:
+`payee's jetton wallet -> payee` means the message notification body contains the following data:
-| Name | Type |
-|-----------------|---------|
-| query_id ` | uint64 |
-| amount ` | coins |
-| sender ` | address |
-| forward_payload` | cell |
+| Name | Type |
+|----------|---------|
+| `query_id` | uint64 |
+| `amount` | coins |
+| `sender` | address |
+| `forward_payload` | cell |
-`payee' jetton wallet -> Sender` means the excess message body contains the following data:
+`payee's jetton wallet -> Sender` means the excess message body contains the following data:
| Name | Type |
@@ -298,9 +246,7 @@ a sufficient amount of TON must be attached to the message being sent by setting
value and, if necessary, attaching a text comment to the `forward_payload`.
A text comment is encoded similarly to a text comment when sending Toncoin.
-[Fees for sending Jettons](https://docs.ton.org/develop/smart-contracts/fees#fees-for-sending-jettons)
-
-However, the commission depends on several factors including the Jetton code details and the need to deploy a new Jetton wallet for recipients.
+However, the [fee for sending Jettons](https://docs.ton.org/develop/smart-contracts/fees#fees-for-sending-jettons) depends on several factors including the Jetton code details and the need to deploy a new Jetton wallet for recipients.
Therefore, it is recommended to attach Toncoin with a margin and then set the address as the `response_destination`
to retrieve `Excesses` messages. For example, 0.05 TON can be attached to the message while setting the `forward_ton_amount`
value to 0.01 TON (this amount of TON will be attached to the `Transfer notification` message).
@@ -318,7 +264,7 @@ await wallet.methods.transfer({
seqno: seqno,
payload: await jettonWallet.createTransferBody({
jettonAmount: TonWeb.utils.toNano('500'), // Jetton amount (in basic indivisible units)
- toAddress: new TonWeb.utils.Address(WALLET2_ADDRESS), // recipient user's wallet address (not Jetton wallet)
+ toAddress: new TonWeb.utils.Address(WALLET2_ADDRESS), // recepient user's wallet address (not Jetton wallet)
forwardAmount: TonWeb.utils.toNano('0.01'), // some amount of TONs to invoke Transfer notification message
forwardPayload: comment, // text comment for Transfer notification message
responseAddress: walletAddress // return the TONs after deducting commissions back to the sender's wallet address
@@ -384,11 +330,6 @@ If they match, it’s ideal. If not, then you likely received a scam token that
To prevent a bottleneck in incoming transactions to a single wallet, it is suggested to accept deposits across multiple wallets and to expand the number of these wallets as needed.
:::
-
-:::caution Transaction Notification
-if you will be allowing your users set a custom memo when withdrawing jettons - make sure to set forwardAmount to 0.000000001 TON (1 nanoton) whenever a memo (text comment) is attached, otherwise the transfer will not be standard compliant and will not be able to be processed by other CEXes and other such services.
-:::
-
In this scenario, the payment service creates a unique memo identifier for each sender disclosing
the address of the centralized wallet and the amounts being sent. The sender sends the tokens
to the specified centralized address with the obligatory memo in the comment.
@@ -918,6 +859,21 @@ export async function tryProcessJetton(orderId: string) : Promise {
```
+# Done
+Best practices with comments on jettons processing:
+
+- [JS algo to accept jettons deposits](https://github.com/toncenter/examples/blob/main/deposits-jettons.js)
+
+- [JS algo to jettons withdrawals](https://github.com/toncenter/examples/blob/main/withdrawals-jettons-highload.js)
+
+- [JS code to withdraw (send) jettons from a wallet in batches](https://github.com/toncenter/examples/blob/main/withdrawals-jettons-highload-batch.js)
+
+:::info Transaction Confirmation
+TON transactions are irreversible after just one confirmation. For the best user experience, it is suggested to avoid waiting on additional blocks once transactions are finalized on the TON Blockchain. Read more in the [Catchain.pdf](https://docs.ton.org/catchain.pdf#page=3).
+:::
+
+In most cases, this should be enough for you, if not, you can find detailed information below.
+
## See Also
From 9f18bd0b86fa3325c4e3f93174b8c9ec6b0d40ef Mon Sep 17 00:00:00 2001
From: Full-Hat
Date: Wed, 24 Apr 2024 14:17:07 +0300
Subject: [PATCH 02/15] complete rewriting payments processing
---
docs/develop/dapps/asset-processing/README.md | 103 ++++++++++--------
.../img/docs/asset-processing/alicemsgDAG.svg | 4 +
2 files changed, 61 insertions(+), 46 deletions(-)
create mode 100644 static/img/docs/asset-processing/alicemsgDAG.svg
diff --git a/docs/develop/dapps/asset-processing/README.md b/docs/develop/dapps/asset-processing/README.md
index c12bddb731..3a5a962c72 100644
--- a/docs/develop/dapps/asset-processing/README.md
+++ b/docs/develop/dapps/asset-processing/README.md
@@ -17,7 +17,16 @@ There are 3 types of messages, that are fully described [here](/develop/smart-co
The common path of any interaction starts with an external message sent to a `wallet` smart contract, which authenticates the message sender using public-key cryptography, takes charge of fee payment, and sends internal blockchain messages. That messages queue form directional acyclic graph, or a tree.
-Each action, when contract take message as input (triggered by it), process it and generate outgoing messages as output, called `transaction`. Read more about transactions [here](/develop/smart-contracts/guidelines/message-delivery-guarantees#what-is-a-transaction).
+For example:
+
+
+
+* `external message` is the input message for `wallet A v4` contract with empty soure (a message from nowhere, such as [Tonkeeper](https://tonkeeper.com/)).
+* `outgoing message` is the output message for `wallet A v4` contract and input message for `wallet B v4` contract with `wallet A v4` source and `wallet B v4` destination.
+
+As a result there are 2 transactions with their set of input and output messages.
+
+Each action, when contract take message as input (triggered by it), process it and generate or not generate outgoing messages as output, called `transaction`. Read more about transactions [here](/develop/smart-contracts/guidelines/message-delivery-guarantees#what-is-a-transaction).
That `transactions` can span a **prolonged period** of time. Technically, transactions with queues of messages are aggregated into blocks processed by validators. The asynchronous nature of the TON Blockchain **does not allow to predict the hash and lt (logical time) of a transaction** at the stage of sending a message.
@@ -37,7 +46,7 @@ TON has three types of digital assets.
- Native token, which is special kind of assets that can be attached to any message on the network. But these asset is currently not in use since the functionality for issuing new native tokens is closed.
## Interaction with TON blockchain
-Basic operations on TON Blockchain can be carried out via TonLib. It is a shared library which can be compiled along with a TON node and expose APIs for interaction with the blockchain via so-called lite servers (servers for lite clients). TonLib follows a trustless approach by checking proofs for all incoming data; thus, there is no necessity for a trusted data provider. Methods available to TonLib are listed [in the TL scheme](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L234). They can be used either as a shared library via wrappers like [pyTON](https://github.com/EmelyanenkoK/pyTON) or [tonlib-go](https://github.com/mercuryoio/tonlib-go/tree/master/v2) (technically those are the wrappers for `tonlibjson`) or through `tonlib-cli`.
+Basic operations on TON Blockchain can be carried out via TonLib. It is a shared library which can be compiled along with a TON node and expose APIs for interaction with the blockchain via so-called lite servers (servers for lite clients). TonLib follows a trustless approach by checking proofs for all incoming data; thus, there is no necessity for a trusted data provider. Methods available to TonLib are listed [in the TL scheme](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L234). They can be used either as a shared library via [wrappers](/develop/dapps/asset-processing/#repositories).
## Wallet smart contract
@@ -72,7 +81,7 @@ Read more in the [Wallet Tutorial](/develop/smart-contracts/tutorials/wallet#-de
## Work with transfers
### Check contract's transactions
-A contract's transactions can be obtained using [getTransactions](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L236). This method allows to get 10 transactions from some `transactionId` and earlier. To process all incoming transactions, the following steps should be followed:
+A contract's transactions can be obtained using [getTransactions](https://toncenter.com/api/v2/). This method allows to get 10 transactions from some `transactionId` and earlier. To process all incoming transactions, the following steps should be followed:
1. The latest `last_transaction_id` can be obtained using [getAccountState](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L235)
2. List of 10 transactions should be loaded via the `getTransactions` method.
3. Unseen transactions from this list should be processed.
@@ -167,24 +176,25 @@ To generate a transaction link in the explorer, the service needs to get the lt
### Wallet creation
-
+
-[Wallet creation + get wallet address](https://github.com/toncenter/examples/blob/main/common.js)
+- **toncenter:**
+ - [Wallet creation + get wallet address](https://github.com/toncenter/examples/blob/main/common.js)
-
-
-
-[Wallet creation + get balance](https://github.com/ton-community/ton#usage)
+- **ton-community/ton:**
+ - [Wallet creation + get balance](https://github.com/ton-community/ton#usage)
-
+
-[Wallet creation + get wallet address](https://github.com/psylopunk/pytonlib/blob/main/examples/generate_wallet.py)
+- **psylopunk/pythonlib:**
+ - [Wallet creation + get wallet address](https://github.com/psylopunk/pytonlib/blob/main/examples/generate_wallet.py)
-
+
-[Wallet creation + get balance](https://github.com/xssnick/tonutils-go?tab=readme-ov-file#wallet)
+- **xssnick/tonutils-go:**
+ - [Wallet creation + get balance](https://github.com/xssnick/tonutils-go?tab=readme-ov-file#wallet)
@@ -192,11 +202,11 @@ To generate a transaction link in the explorer, the service needs to get the lt
### Toncoin Deposits (Get toncoins)
-
-
-[Process Toncoins deposit](https://github.com/toncenter/examples/blob/main/deposits.js)
+
-[Process Toncoins deposit multi wallets](https://github.com/toncenter/examples/blob/main/deposits-multi-wallets.js)
+- **toncenter:**
+ - [Process Toncoins deposit](https://github.com/toncenter/examples/blob/main/deposits.js)
+ - [Process Toncoins deposit multi wallets](https://github.com/toncenter/examples/blob/main/deposits-multi-wallets.js)
@@ -204,52 +214,53 @@ To generate a transaction link in the explorer, the service needs to get the lt
### Toncoin Withdrawals (Send toncoins)
-
+
-[Withdraw Toncoins from a wallet in batches](https://github.com/toncenter/examples/blob/main/withdrawals-highload-batch.js)
+- **toncenter:**
+ - [Withdraw Toncoins from a wallet in batches](https://github.com/toncenter/examples/blob/main/withdrawals-highload-batch.js)
+ - [Withdraw Toncoins from a wallet](https://github.com/toncenter/examples/blob/main/withdrawals-highload.js)
-[Withdraw Toncoins from a wallet](https://github.com/toncenter/examples/blob/main/withdrawals-highload.js)
+- **ton-community/ton:**
+ - [Withdraw Toncoins from a wallet](https://github.com/ton-community/ton#usage)
-
+
-[Withdraw Toncoins from a wallet](https://github.com/ton-community/ton#usage)
+- **psylopunk/pythonlib:**
+ - [Withdraw Toncoins from a wallet](https://github.com/psylopunk/pytonlib/blob/main/examples/transactions.py)
-
+
-[Withdraw Toncoins from a wallet](https://github.com/psylopunk/pytonlib/blob/main/examples/transactions.py)
+- **xssnick/tonutils-go:**
+ - [Withdraw Toncoins from a wallet](https://github.com/xssnick/tonutils-go?tab=readme-ov-file#wallet)
-
-
- [Withdraw Toncoins from a wallet](https://github.com/xssnick/tonutils-go?tab=readme-ov-file#wallet)
-
-
-
-
-## Repositories
-
-### Golang
-
-- [Tonutils](https://github.com/xssnick/tonutils-go#how-to-use)
-- **Self-hosted service**
+
- [Gobicycle](https://github.com/gobicycle/bicycle) service is focused on replenishing user balances and sending payments to blockchain accounts. Both TONs and Jettons are supported. The service is written with numerous pitfalls in mind that a developer might encounter (all checks for jettons, correct operations status check, resending messages, performance during high load when blockchain is splitted by shards). Provide simple HTTP API, rabbit and webhook notifications about new payments.
+### Get contract's transactions
-### JavaScript
+
+
- Using ton.js SDK (supported by TON Community):
+- **ton-community/ton:**
+ - [Client with getTransaction method](https://github.com/ton-community/ton/blob/master/src/client/TonClient.ts)
-- [Create a wallet, get its balance, make a transfer](https://github.com/ton-community/ton#usage)
+
+
-### Python
+- **psylopunk/pythonlib:**
+ - [Get transactions](https://github.com/psylopunk/pytonlib/blob/main/examples/transactions.py)
- Using psylopunk/pytonlib (Simple Python client for The Open Network):
+
+
-- [Sending transactions](https://github.com/psylopunk/pytonlib/blob/main/examples/transactions.py)
+- **xssnick/tonutils-go:**
+ - [Get transactions](https://github.com/xssnick/tonutils-go?tab=readme-ov-file#account-info-and-transactions)
- Using tonsdk library (similar to tonweb):
+
+
-- [Init wallet, create external message to deploy the wallet](https://github.com/tonfactory/tonsdk#create-mnemonic-init-wallet-class-create-external-message-to-deploy-the-wallet)
+## SDKs
+You can find a list of SDKs for various languages (js, python, golang, C#, Rust, etc.) list [here](/develop/dapps/apis/sdk).
diff --git a/static/img/docs/asset-processing/alicemsgDAG.svg b/static/img/docs/asset-processing/alicemsgDAG.svg
new file mode 100644
index 0000000000..ad0c595c0b
--- /dev/null
+++ b/static/img/docs/asset-processing/alicemsgDAG.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
From e3bfe6481a2d3544a93f3b8d22a9dba93cab1297 Mon Sep 17 00:00:00 2001
From: Gleb Karavatski
Date: Wed, 24 Apr 2024 16:28:19 +0300
Subject: [PATCH 03/15] add Jetton Transfer Parse Go example
---
.../develop/dapps/asset-processing/jettons.md | 205 +++++++++++++++++-
1 file changed, 199 insertions(+), 6 deletions(-)
diff --git a/docs/develop/dapps/asset-processing/jettons.md b/docs/develop/dapps/asset-processing/jettons.md
index 79fd4bf6fa..6ce58ad043 100644
--- a/docs/develop/dapps/asset-processing/jettons.md
+++ b/docs/develop/dapps/asset-processing/jettons.md
@@ -662,6 +662,9 @@ await my_wallet.transfer_jetton_by_jetton_wallet(destination_address='address',
### Jetton Transfer with Comment parse
+
+
+
```ts
import {
Address,
@@ -768,7 +771,6 @@ export async function tryProcessJetton(orderId: string) : Promise {
}
// Subscribe
-
const Subscription = async ():Promise =>{
const client = new TonClient({
@@ -783,13 +785,10 @@ export async function tryProcessJetton(orderId: string) : Promise {
return transactions;
}
-
-
-
return retry(async () => {
await prepare();
- const Transactions = await Subscription();
+ const Transactions = await Subscription();
for (const tx of Transactions) {
@@ -828,7 +827,7 @@ export async function tryProcessJetton(orderId: string) : Promise {
let body = originalBody?.clone();
const op = body?.loadUint(32);
if (!(op == 0x7362d09c)) {
- continue; // op == transfer_notification
+ continue; // op != transfer_notification
}
console.log('op code check passed', tx.hash().toString('hex'));
@@ -856,9 +855,203 @@ export async function tryProcessJetton(orderId: string) : Promise {
throw new Error('Transaction not found');
}, {retries: 30, delay: 1000});
}
+```
+
+
+
+
+```go
+import (
+ "context"
+ "fmt"
+ "log"
+
+ "github.com/xssnick/tonutils-go/address"
+ "github.com/xssnick/tonutils-go/liteclient"
+ "github.com/xssnick/tonutils-go/tlb"
+ "github.com/xssnick/tonutils-go/ton"
+ "github.com/xssnick/tonutils-go/ton/jetton"
+ "github.com/xssnick/tonutils-go/tvm/cell"
+)
+
+const (
+ MainnetConfig = "https://ton.org/global.config.json"
+ TestnetConfig = "https://ton.org/global.config.json"
+ MyWalletAddress = "INSERT-YOUR-HOT-WALLET-ADDRESS"
+)
+
+type JettonInfo struct {
+ address string
+ decimals int
+}
+
+type Jettons struct {
+ jettonMinter *jetton.Client
+ jettonWalletAddress string
+ jettonWallet *jetton.WalletClient
+}
+func prepare(api ton.APIClientWrapped, jettonsInfo map[string]JettonInfo) (map[string]Jettons, error) {
+ userAddress := address.MustParseAddr(MyWalletAddress)
+ block, err := api.CurrentMasterchainInfo(context.Background())
+ if err != nil {
+ return nil, err
+ }
+
+ jettons := make(map[string]Jettons)
+
+ for name, info := range jettonsInfo {
+ jettonMaster := jetton.NewJettonMasterClient(api, address.MustParseAddr(info.address))
+ jettonWallet, err := jettonMaster.GetJettonWallet(context.Background(), userAddress)
+ if err != nil {
+ return nil, err
+ }
+
+ jettonUserAddress := jettonWallet.Address()
+
+ jettonData, err := api.RunGetMethod(context.Background(), block, jettonUserAddress, "get_wallet_data")
+ if err != nil {
+ return nil, err
+ }
+
+ slice := jettonData.MustCell(0).BeginParse()
+ slice.MustLoadCoins() // skip balance
+ slice.MustLoadAddr() // skip owneer address
+ adminAddress := slice.MustLoadAddr()
+
+ if adminAddress.String() != info.address {
+ return nil, fmt.Errorf("jetton minter address from jetton wallet doesnt match config")
+ }
+
+ jettons[name] = Jettons{
+ jettonMinter: jettonMaster,
+ jettonWalletAddress: jettonUserAddress.String(),
+ jettonWallet: jettonWallet,
+ }
+ }
+
+ return jettons, nil
+}
+
+func jettonWalletAddressToJettonName(jettons map[string]Jettons, jettonWalletAddress string) string {
+ for name, info := range jettons {
+ if info.jettonWallet.Address().String() == jettonWalletAddress {
+ return name
+ }
+ }
+ return ""
+}
+
+func GetTransferTransactions(orderId string, foundTransfer chan<- *tlb.Transaction) {
+ jettonsInfo := map[string]JettonInfo{
+ "jUSDC": {address: "EQB-MPwrd1G6WKNkLz_VnV6WqBDd142KMQv-g1O-8QUA3728", decimals: 6},
+ "jUSDT": {address: "EQBynBO23ywHy_CgarY9NK9FTz0yDsG82PtcbSTQgGoXwiuA", decimals: 6},
+ }
+
+ client := liteclient.NewConnectionPool()
+
+ cfg, err := liteclient.GetConfigFromUrl(context.Background(), MainnetConfig)
+ if err != nil {
+ log.Fatalln("get config err: ", err.Error())
+ }
+
+ // connect to lite servers
+ err = client.AddConnectionsFromConfig(context.Background(), cfg)
+ if err != nil {
+ log.Fatalln("connection err: ", err.Error())
+ }
+
+ // initialize ton api lite connection wrapper
+ api := ton.NewAPIClient(client, ton.ProofCheckPolicySecure).WithRetry()
+ master, err := api.CurrentMasterchainInfo(context.Background())
+ if err != nil {
+ log.Fatalln("get masterchain info err: ", err.Error())
+ }
+
+ // address on which we are accepting payments
+ treasuryAddress := address.MustParseAddr("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N")
+
+ acc, err := api.GetAccount(context.Background(), master, treasuryAddress)
+ if err != nil {
+ log.Fatalln("get masterchain info err: ", err.Error())
+ }
+
+ jettons, err := prepare(api, jettonsInfo)
+ if err != nil {
+ log.Fatalln("can't prepare jettons data: ", err.Error())
+ }
+
+ lastProcessedLT := acc.LastTxLT
+
+ transactions := make(chan *tlb.Transaction)
+
+ go api.SubscribeOnTransactions(context.Background(), treasuryAddress, lastProcessedLT, transactions)
+
+ log.Println("waiting for transfers...")
+
+ // listen for new transactions from channel
+ for tx := range transactions {
+ if tx.IO.In == nil || tx.IO.In.MsgType != tlb.MsgTypeInternal {
+ // external message - not related to jettons
+ continue
+ }
+
+ msg := tx.IO.In.Msg
+ sourceAddress := msg.SenderAddr()
+
+ // jetton master contract address check
+ jettonName := jettonWalletAddressToJettonName(jettons, sourceAddress.String())
+ if len(jettonName) == 0 {
+ // unknown or fake jetton transfer
+ continue
+ }
+
+ if msg.Payload() == nil || msg.Payload() == cell.BeginCell().EndCell() {
+ // no in_msg body
+ continue
+ }
+
+ msgBodySlice := msg.Payload().BeginParse()
+
+ op := msgBodySlice.MustLoadUInt(32)
+ if op != 0x7362d09c {
+ continue // op != transfer_notification
+ }
+
+ // just skip bits
+ msgBodySlice.MustLoadUInt(64)
+ amount := msgBodySlice.MustLoadCoins()
+ msgBodySlice.MustLoadAddr()
+
+ payload := msgBodySlice.MustLoadMaybeRef()
+ payloadOp := payload.MustLoadUInt(32)
+ if payloadOp == 0 {
+ log.Println("no text comment in transfer_notification")
+ continue
+ }
+
+ comment := payload.MustLoadStringSnake()
+ if comment != orderId {
+ continue
+ }
+
+ // process transaction
+ log.Printf("Got %s jetton deposit %d units with text comment %s\n", jettonName, amount, comment)
+ foundTransfer <- tx
+ }
+}
```
+
+
+
+```py
+```
+
+
+
+
+
# Done
Best practices with comments on jettons processing:
From ee7db2ab0c5ead4bdfc914347760a77094b5f807 Mon Sep 17 00:00:00 2001
From: Gleb Karavatski
Date: Thu, 25 Apr 2024 16:28:36 +0300
Subject: [PATCH 04/15] add Check contract's transactions Go example + fix some
links
---
docs/develop/dapps/asset-processing/README.md | 103 +++++++++++++++++-
1 file changed, 97 insertions(+), 6 deletions(-)
diff --git a/docs/develop/dapps/asset-processing/README.md b/docs/develop/dapps/asset-processing/README.md
index 3a5a962c72..35ca0eab02 100644
--- a/docs/develop/dapps/asset-processing/README.md
+++ b/docs/develop/dapps/asset-processing/README.md
@@ -21,6 +21,7 @@ For example:

+* `Alice` use e.g [Tonkeeper](https://tonkeeper.com/) to send an `external message` to her wallet.
* `external message` is the input message for `wallet A v4` contract with empty soure (a message from nowhere, such as [Tonkeeper](https://tonkeeper.com/)).
* `outgoing message` is the output message for `wallet A v4` contract and input message for `wallet B v4` contract with `wallet A v4` source and `wallet B v4` destination.
@@ -81,15 +82,105 @@ Read more in the [Wallet Tutorial](/develop/smart-contracts/tutorials/wallet#-de
## Work with transfers
### Check contract's transactions
-A contract's transactions can be obtained using [getTransactions](https://toncenter.com/api/v2/). This method allows to get 10 transactions from some `transactionId` and earlier. To process all incoming transactions, the following steps should be followed:
-1. The latest `last_transaction_id` can be obtained using [getAccountState](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L235)
+A contract's transactions can be obtained using [getTransactions](https://toncenter.com/api/v2/#/accounts/get_transactions_getTransactions_get). This method allows to get 10 transactions from some `last_transaction_id` and earlier. To process all incoming transactions, the following steps should be followed:
+1. The latest `last_transaction_id` can be obtained using [getAddressInformation](https://toncenter.com/api/v2/#/accounts/get_address_information_getAddressInformation_get)
2. List of 10 transactions should be loaded via the `getTransactions` method.
-3. Unseen transactions from this list should be processed.
-4. Incoming payments are transactions in which the incoming message has a source address; outgoing payments are transactions in which the incoming message has no source address and also presents the outgoing messages. These transactions should be processed accordingly.
-5. If all of those 10 transactions are unseen, the next 10 transactions should be loaded and steps 2,3,4,5 should be repeated.
+3. Process transactions with not empty source in incoming message and destination equals to account address.
+4. If all of those 10 transactions are unseen, the next 10 transactions should be loaded and steps 2,3,4,5 should be repeated.
+
+
+
+
+
+Checking incoming transactions
+
+```go
+package main
+
+import (
+ "context"
+ "encoding/base64"
+ "log"
+
+ "github.com/xssnick/tonutils-go/address"
+ "github.com/xssnick/tonutils-go/liteclient"
+ "github.com/xssnick/tonutils-go/ton"
+)
+
+const (
+ num = 10
+)
+
+func main() {
+ client := liteclient.NewConnectionPool()
+ err := client.AddConnectionsFromConfigUrl(context.Background(), "https://ton.org/global.config.json")
+ if err != nil {
+ panic(err)
+ }
+
+ api := ton.NewAPIClient(client, ton.ProofCheckPolicyFast).WithRetry()
+
+ accountAddr := address.MustParseAddr("0QA__NJI1SLHyIaG7lQ6OFpAe9kp85fwPr66YwZwFc0p5wIu")
+
+ // we need fresh block info to run get methods
+ b, err := api.CurrentMasterchainInfo(context.Background())
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // we use WaitForBlock to make sure block is ready,
+ // it is optional but escapes us from liteserver block not ready errors
+ res, err := api.WaitForBlock(b.SeqNo).GetAccount(context.Background(), b, accountAddr)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ lastTransactionId := res.LastTxHash
+ lastTransactionLT := res.LastTxLT
+
+ headSeen := false
+
+ for {
+ trxs, err := api.ListTransactions(context.Background(), accountAddr, num, lastTransactionLT, lastTransactionId)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ for i, tx := range trxs {
+ // should include only first time lastTransactionLT
+ if !headSeen {
+ headSeen = true
+ } else if i == 0 {
+ continue
+ }
+
+ if tx.IO.In == nil || tx.IO.In.Msg.SenderAddr().IsAddrNone() {
+ // external message should be omitted
+ continue
+ }
+
+ if string(tx.IO.In.Msg.DestAddr().Data()) == string(accountAddr.Data()) {
+ // process trx
+ log.Printf("found in transaction hash %s", base64.StdEncoding.EncodeToString(tx.Hash))
+ }
+ }
+
+ if len(trxs) == 0 || (headSeen && len(trxs) == 1) {
+ break
+ }
+
+ lastTransactionId = trxs[0].Hash
+ lastTransactionLT = trxs[0].LT
+ }
+}
+```
+
+
+
+
### Checking transaction's flow
-It's possible to track messages flow during transaction processing. Since the message flow is a DAG it's enough to get the input `in_msg` or output `out_msgs` messages of current transaction using [getTransactions](https://toncenter.com/api/v2/#/transactions/get_transactions_getTransactions_get) method to find incoming transaction with [tryLocateResultTx](https://testnet.toncenter.com/api/v2/#/transactions/get_try_locate_result_tx_tryLocateResultTx_get) or outgoing transactions with [tryLocateSourceTx](https://testnet.toncenter.com/api/v2/#/transactions/get_try_locate_source_tx_tryLocateSourceTx_get).
+It's possible to track messages flow during transaction processing. Since the message flow is a DAG it's enough to get the input `in_msg` or output `out_msgs` messages of the current transaction using [getTransactions](https://toncenter.com/api/v2/#/transactions/get_transactions_getTransactions_get) method to find incoming transaction with [tryLocateResultTx](https://toncenter.com/api/v2/#/transactions/get_try_locate_result_tx_tryLocateResultTx_get) or outgoing transactions with [tryLocateSourceTx](https://toncenter.com/api/v2/#/transactions/get_try_locate_source_tx_tryLocateSourceTx_get).
### Send payments
From 4c490619c3cdcd01dd79faeaae6de65eb1f8093b Mon Sep 17 00:00:00 2001
From: Full-Hat
Date: Thu, 25 Apr 2024 21:34:54 +0300
Subject: [PATCH 05/15] rewrite jetton processing page
---
.../develop/dapps/asset-processing/jettons.md | 399 ++++++++----------
1 file changed, 175 insertions(+), 224 deletions(-)
diff --git a/docs/develop/dapps/asset-processing/jettons.md b/docs/develop/dapps/asset-processing/jettons.md
index 6ce58ad043..bd36fde9da 100644
--- a/docs/develop/dapps/asset-processing/jettons.md
+++ b/docs/develop/dapps/asset-processing/jettons.md
@@ -4,44 +4,18 @@ import Button from '@site/src/components/button';
# TON Jetton processing
-## Content List
-
-This document describes the following in order:
-1. Overview
-2. Architecture
-3. Jetton Master Contract (Token Minter)
-4. Jetton Wallet Contract (User Wallet)
-5. Message Layouts
-6. Jetton Processing (off-chain)
-7. Jetton Processing (on-chain)
-8. Wallet processing
-9. Best Practices
-
## Overview
:::info
-For clear understanding, the reader should be familiar with the basic principles of asset processing described in [this section of our documentation](/develop/dapps/asset-processing/). In particular, it is important to be familiar with [contracts](/learn/overviews/addresses#everything-is-a-smart-contract), [wallets](/develop/smart-contracts/tutorials/wallet), [messages](/develop/smart-contracts/guidelines/message-delivery-guarantees) and deployment process.
+For clear understanding, the reader should be familiar with the basic principles of asset processing described in [payments processing section](/develop/dapps/asset-processing/) of our documentation.
:::
-Quick jump to the core description of jetton processing:
-
-
-
-
-
-
-
TON Blockchain and its underlying ecosystem classifies fungible tokens (FTs) as jettons. Because sharding is applied on TON Blockchain, our implementation of fungible tokens is unique when compared to similar blockchain models.
In this analysis, we take a deeper dive into the formal standards detailing jetton [behavior](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md) and [metadata](https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md).
A less formal sharding-focused overview of jetton architecture can be found in our
[anatomy of jettons blog post](https://blog.ton.org/how-to-shard-your-ton-smart-contract-and-why-studying-the-anatomy-of-tons-jettons).
-We have also provided specific details discussing our third-party open-source TON Payment Processor ([bicycle](https://github.com/gobicycle/bicycle)) which allows users to deposit and withdraw both Toncoin and jettons using a separate deposit address without using a text memo.
-
## Jetton Architecture
Standardized tokens on TON are implemented using a set of smart contracts, including:
@@ -57,15 +31,20 @@ Standardized tokens on TON are implemented using a set of smart contracts, inclu
## Jetton master smart contract
The jetton master smart contract stores general information about the jetton (including the total supply, a metadata link, or the metadata itself).
-:::info Jetton scam
+:::warning Beware of Jetton scam
It is possible for any user to create a counterfeit **clone** of a valuable jetton (using an arbitrary name, ticker, image, etc.) that is **nearly identical to the original**. Thankfully, counterfeit jettons are **distinguishable by their addresses** and can be identified quite easily.
-To eliminate the possibility of fraud for TON users, please look up the original jetton address (Jetton master contract) for specific jetton types or follow the project’s official social media channel or website to find the correct information. Check assets to eliminate the possibility of fraud with [Tonkeeper ton-assets list](https://github.com/tonkeeper/ton-assets).
+Jettons with the `symbol`==`TON` or those that contain system notification messages, such as:
+`ERROR`, `SYSTEM`, and others. Be sure to check that jettons are displayed in your interface in such a way that they cannot
+be mixed with TON transfers, system notifications, etc.. At times, even the `symbol`,`name` and `image`
+will be created to look nearly identical to the original with the hopes of misleading users.
+
+To eliminate the possibility of fraud for TON users, please look up the **original jetton address** (Jetton master contract) for specific jetton types or **follow the project’s official social media** channel or website to find the **correct information**. Check assets to eliminate the possibility of fraud with [Tonkeeper ton-assets list](https://github.com/tonkeeper/ton-assets).
:::
### Retrieving Jetton data
-To retrieve more specific Jetton data use _get_ method `get_jetton_data()`.
+To retrieve more specific Jetton data use contract's _get_ method `get_jetton_data()`.
This method returns the following data:
@@ -77,11 +56,15 @@ This method returns the following data:
| `jetton_content` | `cell` | data in accordance with [TEP-64](https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md), check [jetton metadata parsing page](/develop/dapps/asset-processing/metadata) for more. |
| `jetton_wallet_code` | `cell` | |
+You can call it via [Toncenter API](https://toncenter.com/api/v3/#/default/get_jetton_masters_api_v3_jetton_masters_get) or one of the [SDKs](https://docs.ton.org/develop/dapps/apis/sdk).
+
+
-It is also possible to use the method `/jetton/masters` from the [Toncenter API](https://toncenter.com/api/v3/#/default/get_jetton_masters_api_v3_jetton_masters_get) to retrieve the already decoded Jetton data and metadata. We have also developed methods for (js) [tonweb](https://github.com/toncenter/tonweb/blob/master/src/contract/token/ft/JettonMinter.js#L85) and (js) [ton-core/ton](https://github.com/ton-core/ton/blob/master/src/jetton/JettonMaster.ts#L28), (go) [tongo](https://github.com/tonkeeper/tongo/blob/master/liteapi/jetton.go#L48) and (go) [tonutils-go](https://github.com/xssnick/tonutils-go/blob/33fd62d754d3a01329ed5c904db542ab4a11017b/ton/jetton/jetton.go#L79), (python) [pytonlib](https://github.com/toncenter/pytonlib/blob/d96276ec8a46546638cb939dea23612876a62881/pytonlib/client.py#L742) and many other SDKs.
+> Run `jetton/masters` method from the [Toncenter API](https://toncenter.com/api/v3/#/default/get_jetton_masters_api_v3_jetton_masters_get)
-Example of using [Tonweb](https://github.com/toncenter/tonweb) to run a get method and get url for off-chain metadata:
+
+
```js
import TonWeb from "tonweb";
@@ -92,31 +75,27 @@ console.log('Total supply:', data.totalSupply.toString());
console.log('URI to off-chain metadata:', data.jettonContentUri);
```
+
+
+
## Jetton wallet smart contract
-Jetton wallet contracts are used to send, receive, and burn jettons. Each _jetton wallet contract_ stores wallet balance information for specific users.
+`Jetton wallet` contracts are used to **send**, **receive**, and **burn** jettons. Each _jetton wallet contract_ stores wallet balance information for specific users.
In specific instances, jetton wallets are used for individual jetton holders for each jetton type.
-Jetton wallets should not be confused with wallet’s meant for blockchain interaction and storing
+`Jetton wallets` **should not be confused with wallet’s** meant for blockchain interaction and storing
only the Toncoin asset (e.g., v3R2 wallets, highload wallets, and others),
-which is responsible for supporting and managing only a specific jetton type.
-
-Jetton wallets make use of smart contracts and are managed using internal messages between
-the owner's wallet and the jetton wallet. For instance, say if Alice manages a wallet with jettons inside,
-the scheme is as follows: Alice owns a wallet designed specifically for jetton use (such as wallet version v3r2).
-When Alice initiates the sending of jettons in a wallet she manages, she sends external messages to her wallet,
-and as a result, _her wallet_ sends an internal message to _her jetton wallet_ and
-then the jetton wallet actually executes the token transfer.
+which is responsible for supporting and managing **only a specific jetton type**.
### Jetton Wallet Deployment
-When transferring jettons between wallets, transactions (messages) require a certain amount of TON
-as payment for network gas fees and the execution of actions according to the Jetton wallet contract's code.
-This means that the recipient does not need to deploy a jetton wallet prior to receiving jettons.
+When `transferring jettons` between wallets, transactions (messages) require a certain amount of TON
+as payment for network **gas fees** and the execution of actions according to the Jetton wallet contract's code.
+This means that the **recipient does not need to deploy a jetton wallet prior to receiving jettons**.
The recipient's jetton wallet will be deployed automatically as long as the sender holds enough TON
in the wallet to pay the required gas fees.
### Retrieving Jetton wallet addresses for a given user
-To retrieve a jetton wallet address using an owner address (a TON Wallet address),
-the Jetton master contract provides the get method `get_wallet_address(slice owner_address)`.
+To retrieve a `jetton wallet` `address` using an `owner address` (a TON Wallet address),
+the `Jetton master contract` provides the get method `get_wallet_address(slice owner_address)`.
@@ -153,12 +132,12 @@ To retrieve the wallet’s account balance, owner identification information, an
This method returns the following data:
-| Name | Type |
-|--------------------|-------|
-| balance | int |
-| owner | slice |
-| jetton | slice |
-| jetton_wallet_code | cell |
+| Name | Type |
+|----------------------|---------|
+| `balance` | int |
+| `owner` | slice |
+| `jetton` | slice |
+| `jetton_wallet_code` | cell |
@@ -190,138 +169,98 @@ console.log('Jetton master address:', data.jettonMinterAddress.toString(true, tr
-## Messages in jetton wallets communication
-
-:::tip Messages
-Read more about Messages [here](/develop/smart-contracts/guidelines/message-delivery-guarantees).
-:::
+## Jetton wallets communication overview
Communication between Jetton wallets and TON wallets occurs through the following communication sequence:

+#### Message 0
+`Sender -> sender's jetton wallet`. _Transfer_ message contains the following data:
-`Sender -> sender's jetton wallet` means the _transfer_ message body contains the following data:
-
-
-| Name | Type |
-|------------------|---------|
-| `query_id` | uint64 |
-| `amount` | coins |
-| `destination` | address |
-| `response_destination` | address |
-| `custom_payload` | cell |
-| `forward_ton_amount` | coins |
-| `forward_payload` | cell |
+| Name | Type | Description |
+|------------------------|------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `query_id` | uint64 | Allows applications to link three messaging types `Transfer`, `Transfer notification` and `Excesses` to each other. For this process to be carried out correctly it is recommended to **always use a unique query id**. |
+| `amount` | coins | Total `ton coin` amount, that will be send with message. |
+| `destination` | address | Address of the new owner of the jettons |
+| `response_destination` | address | Wallet address used to return remained ton coins with excesses message. |
+| `custom_payload` | maybe cell | Size always is >= 1 bit. Custom data (which is used by either sender or receiver jetton wallet for inner logic). |
+| `forward_ton_amount` | coins | Must be > 0 if you want to send `transfer notification message` with `forward payload`. It's a **part of `amount` value** and **must be lesser than `amount`** |
+| `forward_payload` | maybe cell | Size always is >= 1 bit. If first 32 bits = 0x0 it's just a simple message. |
-`payee's jetton wallet -> payee` means the message notification body contains the following data:
+#### Message 2'
+`payee's jetton wallet -> payee`. Transfer notification message. **Only sent if** `forward_ton_amount` **not zero**. Contains the following data:
-| Name | Type |
-|----------|---------|
-| `query_id` | uint64 |
-| `amount` | coins |
-| `sender` | address |
+| Name | Type |
+|-------------------|---------|
+| `query_id` | uint64 |
+| `amount` | coins |
+| `sender` | address |
| `forward_payload` | cell |
-`payee's jetton wallet -> Sender` means the excess message body contains the following data:
+Here `sender` address is an address of Alice's `Jetton wallet`.
+#### Message 2''
+`payee's jetton wallet -> Sender`. Excess message body. **Only sent if any ton coins are left after paying the fees**. Contains the following data:
| Name | Type |
|----------------------|----------------|
| `query_id` | uint64 |
-A detailed description of the jetton wallet contract fields can be found in the [TEP-74](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md) Jetton standard interface description.
-
-Messages using the `Transfer notification` and `Excesses` parameters are optional and depend on the amount of TON attached
-to the `Transfer` message and the value of the `forward_ton_amount` field.
-
-The `query_id` identifier allows applications to link three messaging types `Transfer`, `Transfer notification` and `Excesses` to each other.
-For this process to be carried out correctly it is recommended to always use a unique query id.
+:::tip Jettons standard
+A detailed description of the jetton wallet contract fields can be found in the [TEP-74](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md) `Jetton standard` interface description.
+:::
-### How to send Jetton transfers with comments and notifications
+## Send Jettons with comments
-In order to make a transfer with a notification (which is then used in-wallet for notification purposes),
-a sufficient amount of TON must be attached to the message being sent by setting a non-zero `forward_ton_amount`
-value and, if necessary, attaching a text comment to the `forward_payload`.
-A text comment is encoded similarly to a text comment when sending Toncoin.
+This transfer require some ton coins for **fees** and, optionally, **transfer notification message** (check forward amount field).
-However, the [fee for sending Jettons](https://docs.ton.org/develop/smart-contracts/fees#fees-for-sending-jettons) depends on several factors including the Jetton code details and the need to deploy a new Jetton wallet for recipients.
-Therefore, it is recommended to attach Toncoin with a margin and then set the address as the `response_destination`
-to retrieve `Excesses` messages. For example, 0.05 TON can be attached to the message while setting the `forward_ton_amount`
-value to 0.01 TON (this amount of TON will be attached to the `Transfer notification` message).
+To send **comment** you need setup `forward payload`. Set **first 32 bits to 0x0** and append **your text**.
-[Jetton transfers with comment examples using the Tonweb SDK](https://github.com/toncenter/tonweb/blob/b550969d960235314974008d2c04d3d4e5d1f546/src/test-jetton.js#L128):
+`forward payload` is sent in `transfer notification` internal message. It will be generated only if `forward amount` > 0.
-```js
-// first 4 bytes are tag of text comment
-const comment = new Uint8Array([... new Uint8Array(4), ... new TextEncoder().encode('text comment')]);
+Finally, to retrieve `Excess` message you must set up `response destination`.
-await wallet.methods.transfer({
- secretKey: keyPair.secretKey,
- toAddress: JETTON_WALLET_ADDRESS, // address of Jetton wallet of Jetton sender
- amount: TonWeb.utils.toNano('0.05'), // total amount of TONs attached to the transfer message
- seqno: seqno,
- payload: await jettonWallet.createTransferBody({
- jettonAmount: TonWeb.utils.toNano('500'), // Jetton amount (in basic indivisible units)
- toAddress: new TonWeb.utils.Address(WALLET2_ADDRESS), // recepient user's wallet address (not Jetton wallet)
- forwardAmount: TonWeb.utils.toNano('0.01'), // some amount of TONs to invoke Transfer notification message
- forwardPayload: comment, // text comment for Transfer notification message
- responseAddress: walletAddress // return the TONs after deducting commissions back to the sender's wallet address
- }),
- sendMode: 3,
-}).send()
-```
:::tip
-For more examples read the [TON Cookbook](/develop/dapps/cookbook#how-to-construct-a-message-for-a-jetton-transfer-with-a-comment).
+Check [best practices](/develop/dapps/asset-processing/jettons#best-practices) for _"send jettons with comments"_ example.
:::
## Jetton off-chain processing
-
:::info Transaction Confirmation
TON transactions are irreversible after just one confirmation. For the best user experience, it is suggested to avoid waiting on additional blocks once transactions are finalized on the TON Blockchain. Read more in the [Catchain.pdf](https://docs.ton.org/catchain.pdf#page=3).
:::
-Several scenarios that allow a user to accept Jettons are possible. Jettons can be accepted within a centralized hot wallet; as well, they can also be accepted using a wallet with a separate address for each individual user.
+ There are two ways to accept Jettons:
+- within a **centralized hot wallet**.
+- using a wallet with a **separate address** for **each individual user**.
-To process Jettons, unlike individualized TON processing, a hot wallet is required (a v3R2, highload wallet) in addition
-to a Jetton wallet or more than one Jetton wallet. Jetton hot wallet deployment is described in the [wallet deployment](/develop/dapps/asset-processing/#wallet-deployment) of our documentation.
-That said, deployment of Jetton wallets according to the [Jetton wallet deployment](#jetton-wallet-deployment) criteria is not required.
-However, when Jettons are received, Jetton wallets are deployed automatically, meaning that when Jettons are withdrawn,
-it is assumed that they are already in the user’s possession.
-
-For security reasons it is preferable to be in possession of separate hot wallets for separate Jettons (many wallets for each asset type).
+For security reasons it is preferable to be in possession of **separate hot wallets** for **separate Jettons** (many wallets for each asset type).
When processing funds, it is also recommended to provide a cold wallet for storing excess funds which do not participate in the automatic deposit and withdrawal processes.
### Adding new Jettons for asset processing and initial verification
-1. To find the correct smart contract token master address please see the following source: [How to find the right Jetton master contract](#jetton-master-smart-contract)
-2. Additionally, to retrieve the metadata for a specific Jetton please see the following source: [How to receive Jetton metadata](#retrieving-jetton-data).
- In order to correctly display new Jettons to users, the correct `decimals` and `symbol` are needed.
-
-For the safety of all of our users, it is critical to avoid Jettons that could be counterfeited (fake). For example,
-Jettons with the `symbol`==`TON` or those that contain system notification messages, such as:
-`ERROR`, `SYSTEM`, and others. Be sure to check that jettons are displayed in your interface in such a way that they cannot
-be mixed with TON transfers, system notifications, etc.. At times, even the `symbol`,`name` and `image`
-will be created to look nearly identical to the original with the hopes of misleading users.
+1. Find correct [smart contract address](/develop/dapps/asset-processing/jettons#jetton-master-smart-contract).
+2. Get [metadata](/develop/dapps/asset-processing/jettons#retrieving-jetton-data).
+3. Check for a [scam](/develop/dapps/asset-processing/jettons#jetton-master-smart-contract).
### Identification of an unknown Jetton when receiving a transfer notification message
-1. If a transfer notification message is received within your wallet regarding an unknown Jetton, then your wallet
-has been created to hold the specific Jetton. Next, it is important to perform several verification processes.
-2. The sender address of the internal message containing the `Transfer notification` body is the address of the new Jetton wallet.
-Not to be confused with the `sender` field in the `Transfer notification` body, the address of the Jetton wallet
-is the address from the source of the message.
-3. Retrieving the Jetton master address for the new Jetton wallet: [How to retrieve data for the Jetton wallet](#retrieving-jetton-data).
- To carry out this process, the `jetton` parameter is required and is the address that makes up the Jetton master contract.
-4. Retrieving the Jetton wallet address for your wallet address (as an owner) using the Jetton master contract: [How to retrieve Jetton wallet address for a given user](#retrieving-jetton-wallet-addresses-for-a-given-user)
-5. Compare the address returned by the master contract and the actual address of the wallet token.
+ If a transfer notification message is received within your wallet regarding an unknown Jetton, then your wallet
+ has been created to hold the specific Jetton.
+
+The sender address of the internal message containing the `Transfer notification` body is the address of the new Jetton wallet.
+It should not to be confused with the `sender` field in the `Transfer notification` [body](/develop/dapps/asset-processing/jettons#jetton-wallets-communication-overview).
+
+1. Retrieve the Jetton master address for the new Jetton wallet by [getting wallet data](/develop/dapps/asset-processing/jettons#retrieving-data-for-a-specific-jetton-wallet).
+2. Retrieve the Jetton wallet address for your wallet address (as an owner) using the Jetton master contract: [How to retrieve Jetton wallet address for a given user](#retrieving-jetton-wallet-addresses-for-a-given-user)
+3. Compare the address returned by the master contract and the actual address of the wallet token.
If they match, it’s ideal. If not, then you likely received a scam token that is counterfeit.
-6. Retrieving Jetton metadata: [How to receive Jetton metadata](#retrieving-jetton-data).
-7. Check the `symbol` and `name` fields for signs of a scam. Warn the user if necessary. [Adding a new Jettons for processing and initial checks](#adding-new-jettons-for-asset-processing-and-initial-verification).
+4. Retrieve Jetton metadata: [How to receive Jetton metadata](#retrieving-jetton-data).
+5. Check the `symbol` and `name` fields for signs of a scam. Warn the user if necessary. [Adding a new Jettons for processing and initial checks](#adding-new-jettons-for-asset-processing-and-initial-verification).
### Accepting Jettons from users through a centralized wallet
@@ -338,29 +277,20 @@ to the specified centralized address with the obligatory memo in the comment.
**Cons of this method:** this method requires that all users attach a comment to the transfer which can lead to a greater number of deposit mistakes (forgotten memos, incorrect memos, etc.), meaning a higher workload for support staff.
-Tonweb examples:
-
-1. [Accepting Jetton deposits to an individual HOT wallet with comments (memo)](https://github.com/toncenter/examples/blob/main/deposits-jettons-single-wallet.js)
-2. [Jettons withdrawals example](https://github.com/toncenter/examples/blob/main/jettons-withdrawals.js)
-
#### Preparations
-1. Prepare a list of accepted Jettons: [Adding new Jettons for processing and initial verification](#adding-new-jettons-for-asset-processing-and-initial-verification).
-2. Hot wallet deployment (using v3R2 if no Jetton withdrawals are expected; highload v2 - if Jetton withdrawals are expected) [Wallet deployment](/develop/dapps/asset-processing/#wallet-deployment).
+1. [Prepare a list of accepted Jettons](/develop/dapps/asset-processing/jettons#adding-new-jettons-for-asset-processing-and-initial-verification) (Jetton master addresses).
+2. Deploy hot wallet (using v3R2 if no Jetton withdrawals are expected; highload v2 - if Jetton withdrawals are expected). [Wallet deployment](/develop/dapps/asset-processing/#wallet-deployment).
3. Perform a test Jetton transfer using the hot wallet address to initialize the wallet.
#### Processing incoming Jettons
-1. Load the list of accepted Jettons
-2. Retrieve a Jetton wallet address for your deployed hot wallet: [How to retrieve a Jetton wallet address for a given user](#retrieving-jetton-wallet-addresses-for-a-given-user)
-3. Retrieve a Jetton master address for each Jetton wallet: [How to retrieve data for a Jetton wallet](#retrieving-data-for-a-specific-jetton-wallet).
- To carry out this process, the `jetton` parameter is required (which is actually the address
-of the Jetton master contract).
-4. Compare the addresses of the Jetton master contracts from step 1. and step 3 (directly above).
-If the addresses do not match, a Jetton address verification error must be reported.
+1. Load the list of accepted Jettons.
+2. [Retrieve a Jetton wallet address](#retrieving-jetton-wallet-addresses-for-a-given-user) for your deployed hot wallet.
+3. Retrieve a Jetton master address for each Jetton wallet using [getting wallet data](/develop/dapps/asset-processing/jettons#retrieving-data-for-a-specific-jetton-wallet).
+4. Compare the addresses of the Jetton master contracts from step 1. and step 3 (directly above).
+ If the addresses do not match, a Jetton address verification error must be reported.
5. Retrieve a list of the most recent unprocessed transactions using a hot wallet account and
-iterate it (by sorting through each transaction one by one). See: [Checking contract's transactions](https://docs.ton.org/develop/dapps/asset-processing/#checking-contracts-transactions),
-or use the [Tonweb example](https://github.com/toncenter/examples/blob/9f20f7104411771793dfbbdf07f0ca4860f12de2/deposits-single-wallet.js#L43)
-or use Toncenter API `/getTransactions` method.
+iterate it (by sorting through each transaction one by one). See: [Checking contract's transactions](https://docs.ton.org/develop/dapps/asset-processing/#checking-contracts-transactions).
6. Check the input message (in_msg) for transactions and retrieve the source address from the input message. [Tonweb example](https://github.com/toncenter/examples/blob/9f20f7104411771793dfbbdf07f0ca4860f12de2/deposits-jettons-single-wallet.js#L84)
7. If the source address matches the address within a Jetton wallet, then it is necessary to continue processing the transaction.
If not, then skip processing the transaction and check the next transaction.
@@ -391,7 +321,7 @@ On TON, the functionality required to create a subwallet is supported by version
#### Creating a subwallet in Tonweb
-```Tonweb
+```js
const WalletClass = tonweb.wallet.all['v3R2'];
const wallet = new WalletClass(tonweb.provider, {
publicKey: keyPair.publicKey,
@@ -402,8 +332,8 @@ const wallet = new WalletClass(tonweb.provider, {
#### Preparation
-1. Prepare a list of accepted Jettons: [Adding new Jettons for processing and initial checks](#adding-new-jettons-for-asset-processing-and-initial-verification)
-2. Hot wallet [Wallet Deployment](/develop/dapps/asset-processing/#wallet-deployment)
+1. [Prepare a list of accepted Jettons](#adding-new-jettons-for-asset-processing-and-initial-verification).
+2. Deploy hot wallet (using v3R2 if no Jetton withdrawals are expected; highload v2 - if Jetton withdrawals are expected). [Wallet deployment](/develop/dapps/asset-processing/#wallet-deployment).
#### Creating deposits
@@ -423,7 +353,7 @@ TON transactions are irreversible after just one confirmation. For the best user
:::
It is not always possible to determine the exact amount of Jettons received from the message, because Jetton
-wallets may not send `transfer notification`, `excesses`, and `internal transfer` messages are not standardized. This means
+wallets may not send `transfer notification`, `excesses`, and `internal transfer` messages. They are not standardized. This means
that there is no guarantee that the `internal transfer` message can be decoded.
Therefore, to determine the amount received in the wallet, balances need to be requested using the get method.
@@ -451,14 +381,14 @@ can be used if there is one such transaction or block data present (if several a
#### Withdrawals made from deposits
Transfers should not be made from a deposit to a hot wallet with each deposit replenishment,
-because of the fact that a commission in TON is taken for the transfer operation (paid in network gas fees).
+because a commission in TON is taken for the transfer operation (paid in network gas fees).
It is important to determine a certain minimum amount of Jettons which are required to make a
transfer worthwhile (and thus deposit).
By default, wallet owners of Jetton deposit wallets are not initialized. This is because there is no predetermined
requirement to pay storage fees. Jetton deposit wallets can be deployed when sending messages with a
`transfer` body which can then be destroyed immediately. To do this, the engineer must use a special
-mechanism for sending messages: 128 + 32.
+mechanism for sending messages: [128 + 32](/develop/smart-contracts/messages#message-modes).
1. Retrieve a list of deposits marked for withdrawal to a hot wallet
@@ -480,6 +410,12 @@ taking into consideration the [processing of incoming Jettons info found here](#
### Jetton withdrawals
+:::info Important
+It's **recommended** to read and **understand** [how does jetton transfer work](/develop/dapps/asset-processing/jettons#jetton-wallets-communication-overview) and [how to send jettons with comment](/develop/dapps/asset-processing/jettons#jetton-off-chain-processing) articles before reading this section.
+
+Below you'll find step-by-step guide how to process jetton withdrawals.
+:::
+
To withdraw Jettons, the wallet sends messages with the `transfer` body to its corresponding Jetton wallet.
The Jetton wallet then sends the Jettons to the recipient. In good faith, it is important to attach some TON
as the `forward_ton_amount` (and optional comment to `forward_payload`) to trigger a `transfer notification`.
@@ -500,13 +436,7 @@ See: [Jetton contracts message layouts](#jetton-contract-message-layouts)
4. Compare the addresses from Jetton master contracts from step 1. and step 3. If the addresses do not match, then a Jetton address verification error should be reported.
5. Withdrawal requests are received which actually indicate the type of Jetton, the amount being transferred, and the recipient wallet address.
6. Check the balance of the Jetton wallet to ensure there are enough funds present to carry out withdrawal.
-7. Generate a message using the Jetton `transfer` body by filling in the required fields, including: the query_id, amount being sent,
-destination (the recipient's non-Jetton wallet address), response_destination (it is recommended to specify the user’s hot wallet),
-forward_ton_amount (it is recommended to set this to at least 0.05 TON to invoke a `transfer notification`), `forward_payload`
- (optional, if sending a comment is needed). [Jetton contracts message layouts](#jetton-contract-message-layouts),
-[Tonweb example](https://github.com/toncenter/examples/blob/9f20f7104411771793dfbbdf07f0ca4860f12de2/jettons-withdrawals.js#L69)
- In order to check the successful validation of the transaction, it is necessary to assign a unique value to the
- `query_id` for each message.
+7. Generate a [message](/develop/dapps/asset-processing/jettons#message-0).
8. When using a highload wallet, it is recommended that a batch of messages is collected and that one batch at a time is sent to optimize fees.
9. Save the expiration time for outgoing external messages (this is the time until the wallet successfully
processes the message, after this is completed, the wallet will no longer accept the message)
@@ -522,71 +452,90 @@ Retrieved `query_id`s need to be marked as successfully sent.
the expiration time and the outgoing message with the given `query_id`
is not found, then the request should (this is optional) be marked as expired and should be safely resent.
15. Look for incoming messages in the account.
-16. If a message exists that uses the `excesses` op code, the message should be decoded and the `query_id`
+16. If a message that uses the `excesses` op code exists, the message should be decoded and the `query_id`
value should be retrieved. A found `query_id` must be marked as successfully delivered.
17. Go to step 5. Expired requests that have not been successfully sent should be pushed back to the withdrawal list.
-## Jetton processing on-chain
+## Jetton on-chain processing
+
+Generally, to accept and process jettons, a message handler responsible for internal messages uses the `op=0x7362d09c` op code.
:::info Transaction Confirmation
TON transactions are irreversible after just one confirmation. For the best user experience, it is suggested to avoid waiting on additional blocks once transactions are finalized on the TON Blockchain. Read more in the [Catchain.pdf](https://docs.ton.org/catchain.pdf#page=3).
:::
-Generally, to accept and process jettons, a message handler responsible for internal messages uses the `op=0x7362d09c` op code.
-
-Below is a list of recommendations that must be considered when carrying out on-chain jetton processing:
+### On-chain processing recommendations
+Below is a `list of recommendations` that must be considered when **carrying out on-chain jetton processing**:
-1. Identify incoming jettons using their wallet type, not their Jetton master contract. In other words, your contract should interact (receive and send messages) with a specific jetton wallet (not with some unknown wallet using a specific Jetton master contract).
-2. When linking a Jetton Wallet and a Jetton Master, check that this connection is bidirectional where the wallet recognizes the master contract and vice versa. For instance, if your contract-system receives a notification from a jetton wallet (which considers its MySuperJetton as its master contract) its transfer information must be displayed to the user, prior to showing the `symbol`, `name` and `image`
+1. **Identify incoming jettons** using their wallet type, not their Jetton master contract. In other words, your contract should interact (receive and send messages) with a specific jetton wallet (not with some unknown wallet using a specific Jetton master contract).
+2. When linking a Jetton Wallet and a Jetton Master, **check** that this **connection is bidirectional** where the wallet recognizes the master contract and vice versa. For instance, if your contract-system receives a notification from a jetton wallet (which considers its MySuperJetton as its master contract) its transfer information must be displayed to the user, prior to showing the `symbol`, `name` and `image`
of the MySuperJetton contract, check that the MySuperJetton wallet uses the correct contract system. In turn, if your contract system for some reason needs to send jettons using the MySuperJetton or MySuperJetton master contracts verify that wallet X as is the wallet using the same contract parameters.
Additionally, prior to sending a `transfer` request to X, make sure it recognizes MySuperJetton as its master.
-3. The true power of decentralized finance (DeFi) is based on the ability to stack protocols on top of each other like lego blocks. For instance, say jetton A is swapped for jetton B, which in turn, is then used as leverage within a lending protocol (when a user supplies liquidity) which is then used to buy an NFT .... and so on. Therefore, consider how the contract is able to serve, not only off-chain users, but on-chain entities as well by attaching tokenized value to a transfer notification, adding a custom payload that can be sent with a transfer notification.
-4. Be aware that not all contracts follow the same standards. Unfortunately, some jettons may be hostile (using attack-based vectors) and created for the sole purposes of attacking unsuspecting users. For security purposes, if the protocol in question consists of many contracts, do not create a large number of jetton wallets of the same type. In particular, do not send jettons inside the protocol between the deposit contract, vault contract, or user account contract etc. Attackers may intentionally interfere with contract logic by forging transfer notifications, jetton amounts, or payload parameters. Reduce the potential for attack potential by using only one wallet in the system per jetton (for all deposits and withdrawals).
-5. It is also often a good idea to create subcontracts for each individualized jetton to reduce the chances of address spoofing (for example, when a transfer message is sent to jetton B using a contract intended for jetton A).
-6. It is strongly recommended to work with indivisible jetton units on the contract level. Decimal-related logic is typically used to enhance the diplay’s user interface (UI), and is not related to numerical on-chain record keeping.
-7. To learn more about [Secure Smart Contract Programming in FunC by CertiK](https://blog.ton.org/secure-smart-contract-programming-in-func), feel free to read this resource. It is recommended that developers handle all smart contract exceptions so they are never skipped during application development.
+3. The **true power** of decentralized finance (DeFi) is based on the ability to stack protocols on top of each other like lego blocks. For instance, say jetton A is swapped for jetton B, which in turn, is then used as leverage within a lending protocol (when a user supplies liquidity) which is then used to buy an NFT .... and so on. Therefore, consider how the contract is able to serve, not only off-chain users, but on-chain entities as well by attaching tokenized value to a transfer notification, adding a custom payload that can be sent with a transfer notification.
+4. **Be aware** that not all contracts follow the same standards. Unfortunately, some jettons may be hostile (using attack-based vectors) and created for the sole purposes of attacking unsuspecting users. For security purposes, if the protocol in question consists of many contracts, do not create a large number of jetton wallets of the same type. In particular, do not send jettons inside the protocol between the deposit contract, vault contract, or user account contract etc. Attackers may intentionally interfere with contract logic by forging transfer notifications, jetton amounts, or payload parameters. Reduce the potential for attack potential by using only one wallet in the system per jetton (for all deposits and withdrawals).
+5. It is also **often a good idea** to create subcontracts for each individualized jetton to reduce the chances of address spoofing (for example, when a transfer message is sent to jetton B using a contract intended for jetton A).
+6. It is **strongly recommended** to work with indivisible jetton units on the contract level. Decimal-related logic is typically used to enhance the diplay’s user interface (UI), and is not related to numerical on-chain record keeping.
+
+To learn **more** about [Secure Smart Contract Programming in FunC by CertiK](https://blog.ton.org/secure-smart-contract-programming-in-func), feel free to read this resource. It is recommended that developers **handle all smart contract exceptions,** so they are never skipped during application development.
-## Jetton wallet processing
+## Jetton wallet processing recommendations
Generally, all verification procedures used for off-chain jetton processing are suitable for wallets as well. For Jetton wallet processing our most important recommendations are as follows:
-1. When a wallet receives a transfer notification from an unknown jetton wallet, it is vitally important to trust the jetton wallet and its master address because it could be a malicious counterfeit. To protect yourself, check the Jetton Master (the master contract) using its provided address to ensure your verification processes recognize the jetton wallet as legitimate. After you trust the wallet and it is verified as legitimate, you can allow it to access your account balances and other in-wallet data. If the Jetton Master does not recognize this wallet it is recommended to not initiate or disclose your jetton transfers at all and to only show incoming TON transfers (of Toncoin attached to the transfer notifications) only.
+1. When a wallet receives a transfer notification from an unknown jetton wallet, it is **vitally important** to trust the jetton wallet and its master address because it could be a malicious counterfeit. To protect yourself, check the Jetton Master (the master contract) using its provided address to ensure your verification processes recognize the jetton wallet as legitimate. After you trust the wallet and it is verified as legitimate, you can allow it to access your account balances and other in-wallet data. If the Jetton Master does not recognize this wallet it is recommended to not initiate or disclose your jetton transfers at all and to only show incoming TON transfers (of Toncoin attached to the transfer notifications) only.
2. In practice, if the user wants to interact with a Jetton and not a jetton wallet. In other words, users send wTON/oUSDT/jUSDT, jUSDC, jDAI instead of `EQAjN...`/`EQBLE...`
- etc.. Often this means that when a user is initiating a jetton transfer, the wallet asks the corresponding jetton master which jetton wallet (owned by the user) should initiate the transfer request. It is important to never blindly trust this data from the Master (the master contract). Prior to sending the transfer request to a jetton wallet, always ensure that the jetton wallet indeed belongs to the Jetton Master it claims to belong to.
-3. Be aware that hostile Jetton Masters/jetton wallets may change their wallets/Masters over time. Therefore, it is imperative that users do their due diligence and check the legitimacy of any wallets they interact with prior to each use.
-4. Always ensure that you display jettons in your interface in a manner that will not mix with TON transfers, system notifications, etc.. Even the `symbol`,`name` and `image`
+ etc.. Often this means that when a user is initiating a jetton transfer, the wallet asks the corresponding jetton master which jetton wallet (owned by the user) should initiate the transfer request. It is **important to never blindly trust** this data from the Master (the master contract). Prior to sending the transfer request to a jetton wallet, always ensure that the jetton wallet indeed belongs to the Jetton Master it claims to belong to.
+3. **Be aware** that hostile Jetton Masters/jetton wallets **may change** their wallets/Masters over time. Therefore, it is imperative that users do their due diligence and check the legitimacy of any wallets they interact with prior to each use.
+4. **Always ensure** that you display jettons in your interface in a manner that **will not mix with TON transfers**, system notifications, etc.. Even the `symbol`,`name` and `image`
parameters can be crafted to mislead users, leaving those affected as potential fraud victims. There have been several instances, when malicious jettons were used to impersonate TON transfers, notification errors, reward earnings, or asset freezing announcements.
-5. Always be on the lookout for potential malicious actors that create counterfeit jettons, it is always a good idea to give users the functionality needed to eliminate unwanted jettons in their main user interface.
+5. **Always be on the lookout for potential malicious actors** that create counterfeit jettons, it is always a good idea to give users the functionality needed to eliminate unwanted jettons in their main user interface.
Authored by [kosrk](https://github.com/kosrk), [krigga](https://github.com/krigga), [EmelyanenkoK](https://github.com/EmelyanenkoK/) and [tolya-yanot](https://github.com/tolya-yanot/).
## Best Practices
-Here we have provided several examples of jetton code processing created by TON Community members:
+If you want ready to test examples check [SDKs](/develop/dapps/asset-processing/jettons#sdks) and try to run them. Below are code snippets that will help you understand jetton processing through code examples.
+
+### Send Jettons with comment
+
+
+Source code
+
+
```js
-const transfer = await wallet.methods.transfer({
+// first 4 bytes are tag of text comment
+const comment = new Uint8Array([... new Uint8Array(4), ... new TextEncoder().encode('text comment')]);
+
+await wallet.methods.transfer({
secretKey: keyPair.secretKey,
- toAddress: jettonWalletAddress,
- amount: 0,
+ toAddress: JETTON_WALLET_ADDRESS, // address of Jetton wallet of Jetton sender
+ amount: TonWeb.utils.toNano('0.05'), // total amount of TONs attached to the transfer message
seqno: seqno,
- sendMode: 128 + 32, // mode 128 is used for messages that are to carry all the remaining balance; mode 32 means that the current account must be destroyed if its resulting balance is zero;
payload: await jettonWallet.createTransferBody({
- queryId: seqno, // any number
- jettonAmount: jettonBalance, // jetton amount in units
- toAddress: new TonWeb.utils.Address(MY_HOT_WALLET_ADDRESS),
- responseAddress: new TonWeb.utils.Address(MY_HOT_WALLET_ADDRESS),
+ jettonAmount: TonWeb.utils.toNano('500'), // Jetton amount (in basic indivisible units)
+ toAddress: new TonWeb.utils.Address(WALLET2_ADDRESS), // recepient user's wallet address (not Jetton wallet)
+ forwardAmount: TonWeb.utils.toNano('0.01'), // some amount of TONs to invoke Transfer notification message
+ forwardPayload: comment, // text comment for Transfer notification message
+ responseAddress: walletAddress // return the TONs after deducting commissions back to the sender's wallet address
}),
-});
-await transfer.send();
+ sendMode: 3,
+}).send()
```
+
+
+
+
+Source code
+
+
```go
client := liteclient.NewConnectionPool()
@@ -643,9 +592,16 @@ if err != nil {
log.Println("transaction confirmed, hash:", base64.StdEncoding.EncodeToString(tx.Hash))
```
+
+
+
+
+Source code
+
+
```py
my_wallet = Wallet(provider=client, mnemonics=my_wallet_mnemonics, version='v4r2')
@@ -656,15 +612,22 @@ await my_wallet.transfer_jetton(destination_address='address', jetton_master_add
await my_wallet.transfer_jetton_by_jetton_wallet(destination_address='address', jetton_wallet='your jetton wallet address', jettons_amount=1000, fee=0.1)
```
+
+
-### Jetton Transfer with Comment parse
+### Accept Jetton Transfer with comment parse
+
+
+Source code
+
+
```ts
import {
Address,
@@ -857,9 +820,16 @@ export async function tryProcessJetton(orderId: string) : Promise {
}
```
+
+
+
+
+Source code
+
+
```go
import (
"context"
@@ -1042,31 +1012,12 @@ func GetTransferTransactions(orderId string, foundTransfer chan<- *tlb.Transacti
}
```
+
-
-
-```py
-```
-
-
-
-# Done
-Best practices with comments on jettons processing:
-
-- [JS algo to accept jettons deposits](https://github.com/toncenter/examples/blob/main/deposits-jettons.js)
-
-- [JS algo to jettons withdrawals](https://github.com/toncenter/examples/blob/main/withdrawals-jettons-highload.js)
-
-- [JS code to withdraw (send) jettons from a wallet in batches](https://github.com/toncenter/examples/blob/main/withdrawals-jettons-highload-batch.js)
-
-:::info Transaction Confirmation
-TON transactions are irreversible after just one confirmation. For the best user experience, it is suggested to avoid waiting on additional blocks once transactions are finalized on the TON Blockchain. Read more in the [Catchain.pdf](https://docs.ton.org/catchain.pdf#page=3).
-:::
-
-In most cases, this should be enough for you, if not, you can find detailed information below.
-
+## SDKs
+You can find a list of SDKs for various languages (js, python, golang, C#, Rust, etc.) list [here](/develop/dapps/apis/sdk).
## See Also
From 065aa23b959adaaa40bf91822cbeb0e21ebe8531 Mon Sep 17 00:00:00 2001
From: Gleb Karavatski
Date: Fri, 26 Apr 2024 09:38:46 +0300
Subject: [PATCH 06/15] [HOTFIX] change trxs processing last point
---
docs/develop/dapps/asset-processing/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/develop/dapps/asset-processing/README.md b/docs/develop/dapps/asset-processing/README.md
index 35ca0eab02..6133566e50 100644
--- a/docs/develop/dapps/asset-processing/README.md
+++ b/docs/develop/dapps/asset-processing/README.md
@@ -86,7 +86,7 @@ A contract's transactions can be obtained using [getTransactions](https://toncen
1. The latest `last_transaction_id` can be obtained using [getAddressInformation](https://toncenter.com/api/v2/#/accounts/get_address_information_getAddressInformation_get)
2. List of 10 transactions should be loaded via the `getTransactions` method.
3. Process transactions with not empty source in incoming message and destination equals to account address.
-4. If all of those 10 transactions are unseen, the next 10 transactions should be loaded and steps 2,3,4,5 should be repeated.
+4. The next 10 transactions should be loaded and steps 2,3,4,5 should be repeated until you processed all incoming transactions.
From ea8f7475f207412fecd74be2e2098258b2d4c080 Mon Sep 17 00:00:00 2001
From: Gleb Karavatski
Date: Fri, 26 Apr 2024 17:13:52 +0300
Subject: [PATCH 07/15] refactoring
---
docs/develop/dapps/asset-processing/README.md | 247 +++++++++---------
1 file changed, 130 insertions(+), 117 deletions(-)
diff --git a/docs/develop/dapps/asset-processing/README.md b/docs/develop/dapps/asset-processing/README.md
index 6133566e50..66232eeb4f 100644
--- a/docs/develop/dapps/asset-processing/README.md
+++ b/docs/develop/dapps/asset-processing/README.md
@@ -88,97 +88,6 @@ A contract's transactions can be obtained using [getTransactions](https://toncen
3. Process transactions with not empty source in incoming message and destination equals to account address.
4. The next 10 transactions should be loaded and steps 2,3,4,5 should be repeated until you processed all incoming transactions.
-
-
-
-
-Checking incoming transactions
-
-```go
-package main
-
-import (
- "context"
- "encoding/base64"
- "log"
-
- "github.com/xssnick/tonutils-go/address"
- "github.com/xssnick/tonutils-go/liteclient"
- "github.com/xssnick/tonutils-go/ton"
-)
-
-const (
- num = 10
-)
-
-func main() {
- client := liteclient.NewConnectionPool()
- err := client.AddConnectionsFromConfigUrl(context.Background(), "https://ton.org/global.config.json")
- if err != nil {
- panic(err)
- }
-
- api := ton.NewAPIClient(client, ton.ProofCheckPolicyFast).WithRetry()
-
- accountAddr := address.MustParseAddr("0QA__NJI1SLHyIaG7lQ6OFpAe9kp85fwPr66YwZwFc0p5wIu")
-
- // we need fresh block info to run get methods
- b, err := api.CurrentMasterchainInfo(context.Background())
- if err != nil {
- log.Fatal(err)
- }
-
- // we use WaitForBlock to make sure block is ready,
- // it is optional but escapes us from liteserver block not ready errors
- res, err := api.WaitForBlock(b.SeqNo).GetAccount(context.Background(), b, accountAddr)
- if err != nil {
- log.Fatal(err)
- }
-
- lastTransactionId := res.LastTxHash
- lastTransactionLT := res.LastTxLT
-
- headSeen := false
-
- for {
- trxs, err := api.ListTransactions(context.Background(), accountAddr, num, lastTransactionLT, lastTransactionId)
- if err != nil {
- log.Fatal(err)
- }
-
- for i, tx := range trxs {
- // should include only first time lastTransactionLT
- if !headSeen {
- headSeen = true
- } else if i == 0 {
- continue
- }
-
- if tx.IO.In == nil || tx.IO.In.Msg.SenderAddr().IsAddrNone() {
- // external message should be omitted
- continue
- }
-
- if string(tx.IO.In.Msg.DestAddr().Data()) == string(accountAddr.Data()) {
- // process trx
- log.Printf("found in transaction hash %s", base64.StdEncoding.EncodeToString(tx.Hash))
- }
- }
-
- if len(trxs) == 0 || (headSeen && len(trxs) == 1) {
- break
- }
-
- lastTransactionId = trxs[0].Hash
- lastTransactionLT = trxs[0].LT
- }
-}
-```
-
-
-
-
-
### Checking transaction's flow
It's possible to track messages flow during transaction processing. Since the message flow is a DAG it's enough to get the input `in_msg` or output `out_msgs` messages of the current transaction using [getTransactions](https://toncenter.com/api/v2/#/transactions/get_transactions_getTransactions_get) method to find incoming transaction with [tryLocateResultTx](https://toncenter.com/api/v2/#/transactions/get_try_locate_result_tx_tryLocateResultTx_get) or outgoing transactions with [tryLocateSourceTx](https://toncenter.com/api/v2/#/transactions/get_try_locate_source_tx_tryLocateSourceTx_get).
@@ -190,7 +99,7 @@ It's possible to track messages flow during transaction processing. Since the me
4. Form [msg.message](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L108) which contains `destination_address`, empty `public_key`, `amount` and `msg.dataText`.
5. Form [Action](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L149) which contains a set of outgoing messages.
6. Use [createQuery](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L255) and [sendQuery](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L260) queries to send outgoing payments.
-7. Service should regularly poll the getTransactions method for the `wallet` contract. Matching confirmed transactions with the outgoing payments by (`destination_address`, `value`, `comment`) allows to mark payments as finished; detect and show the user the corresponding transaction hash and lt (logical time).
+7. Service should regularly poll the [getTransactions](https://toncenter.com/api/v2/#/transactions/get_transactions_getTransactions_get) method for the `wallet` contract. Matching confirmed transactions with the outgoing payments by (`destination_address`, `value`, `comment`) allows to mark payments as finished; detect and show the user the corresponding transaction hash and lt (logical time).
8. Requests to `v3` of `high-load` wallets have an expiration time equal to 60 seconds by default. After that time unprocessed requests can be safely resent to the network (see steps 3-6).
## Invoice-based payment accept
@@ -198,7 +107,7 @@ To accept payments based on attached comments, the service should
1. Deploy the `wallet` contract.
2. Generate a unique `invoice` for each user. String representation of uuid32 will be enough.
3. Users should be instructed to send Toncoin to the service's `wallet` contract with an attached `invoice` as a comment.
-4. Service should regularly poll the getTransactions method for the `wallet` contract.
+4. Service should regularly poll the [getTransactions](https://toncenter.com/api/v2/#/transactions/get_transactions_getTransactions_get) method for the `wallet` contract.
5. For new transactions, the incoming message should be extracted, `comment` matched against the database, and the **incoming message value** deposited to the user's account.
To calculate the **incoming message value** that the message brings to the contract, one needs to parse the transaction. It happens when the message hits the contract. A transaction can be obtained using [getTransactions](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L236). For an incoming wallet transaction, the correct data consists of one incoming message and zero outgoing messages. Otherwise, either an external message is sent to the wallet, in which case the owner spends Toncoin, or the wallet is not deployed and the incoming transaction bounces back.
@@ -254,7 +163,7 @@ ton://transfer/?
The blockchain explorer is https://tonscan.org.
-To generate a transaction link in the explorer, the service needs to get the lt (logic time), transaction hash, and account address (account address for which lt and txhash were retrieved via the getTransactions method). https://tonscan.org and https://explorer.toncoin.org/ may then show the page for that tx in the following format:
+To generate a transaction link in the explorer, the service needs to get the lt (logic time), transaction hash, and account address (account address for which lt and txhash were retrieved via the [getTransactions](https://toncenter.com/api/v2/#/transactions/get_transactions_getTransactions_get) method). https://tonscan.org and https://explorer.toncoin.org/ may then show the page for that tx in the following format:
`https://tonviewer.com/transaction/{txhash as base64url}`
@@ -267,7 +176,7 @@ To generate a transaction link in the explorer, the service needs to get the lt
### Wallet creation
-
+
- **toncenter:**
- [Wallet creation + get wallet address](https://github.com/toncenter/examples/blob/main/common.js)
@@ -276,36 +185,134 @@ To generate a transaction link in the explorer, the service needs to get the lt
- [Wallet creation + get balance](https://github.com/ton-community/ton#usage)
-
-- **psylopunk/pythonlib:**
- - [Wallet creation + get wallet address](https://github.com/psylopunk/pytonlib/blob/main/examples/generate_wallet.py)
-
-
-
+
- **xssnick/tonutils-go:**
- [Wallet creation + get balance](https://github.com/xssnick/tonutils-go?tab=readme-ov-file#wallet)
+
+
+
+- **psylopunk/pythonlib:**
+ - [Wallet creation + get wallet address](https://github.com/psylopunk/pytonlib/blob/main/examples/generate_wallet.py)
+
+
+
### Toncoin Deposits (Get toncoins)
-
+
- **toncenter:**
- [Process Toncoins deposit](https://github.com/toncenter/examples/blob/main/deposits.js)
- [Process Toncoins deposit multi wallets](https://github.com/toncenter/examples/blob/main/deposits-multi-wallets.js)
+
+
+
+- **xssnick/tonutils-go:**
+
+
+Checking deposits
+
+```go
+package main
+
+import (
+ "context"
+ "encoding/base64"
+ "log"
+
+ "github.com/xssnick/tonutils-go/address"
+ "github.com/xssnick/tonutils-go/liteclient"
+ "github.com/xssnick/tonutils-go/ton"
+)
+
+const (
+ num = 10
+)
+
+func main() {
+ client := liteclient.NewConnectionPool()
+ err := client.AddConnectionsFromConfigUrl(context.Background(), "https://ton.org/global.config.json")
+ if err != nil {
+ panic(err)
+ }
+
+ api := ton.NewAPIClient(client, ton.ProofCheckPolicyFast).WithRetry()
+
+ accountAddr := address.MustParseAddr("0QA__NJI1SLHyIaG7lQ6OFpAe9kp85fwPr66YwZwFc0p5wIu")
+
+ // we need fresh block info to run get methods
+ b, err := api.CurrentMasterchainInfo(context.Background())
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // we use WaitForBlock to make sure block is ready,
+ // it is optional but escapes us from liteserver block not ready errors
+ res, err := api.WaitForBlock(b.SeqNo).GetAccount(context.Background(), b, accountAddr)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ lastTransactionId := res.LastTxHash
+ lastTransactionLT := res.LastTxLT
+
+ headSeen := false
+
+ for {
+ trxs, err := api.ListTransactions(context.Background(), accountAddr, num, lastTransactionLT, lastTransactionId)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ for i, tx := range trxs {
+ // should include only first time lastTransactionLT
+ if !headSeen {
+ headSeen = true
+ } else if i == 0 {
+ continue
+ }
+
+ if tx.IO.In == nil || tx.IO.In.Msg.SenderAddr().IsAddrNone() {
+ // external message should be omitted
+ continue
+ }
+
+ if tx.IO.Out != nil {
+ // no outgoing messages - this is incoming Toncoins
+ continue
+ }
+
+ // process trx
+ log.Printf("found in transaction hash %s", base64.StdEncoding.EncodeToString(tx.Hash))
+ }
+
+ if len(trxs) == 0 || (headSeen && len(trxs) == 1) {
+ break
+ }
+
+ lastTransactionId = trxs[0].Hash
+ lastTransactionLT = trxs[0].LT
+ }
+}
+```
+
+
+
+
### Toncoin Withdrawals (Send toncoins)
-
+
- **toncenter:**
- [Withdraw Toncoins from a wallet in batches](https://github.com/toncenter/examples/blob/main/withdrawals-highload-batch.js)
@@ -315,43 +322,49 @@ To generate a transaction link in the explorer, the service needs to get the lt
- [Withdraw Toncoins from a wallet](https://github.com/ton-community/ton#usage)
-
-- **psylopunk/pythonlib:**
- - [Withdraw Toncoins from a wallet](https://github.com/psylopunk/pytonlib/blob/main/examples/transactions.py)
-
-
-
+
- **xssnick/tonutils-go:**
- [Withdraw Toncoins from a wallet](https://github.com/xssnick/tonutils-go?tab=readme-ov-file#wallet)
+
+
+
+- **psylopunk/pythonlib:**
+ - [Withdraw Toncoins from a wallet](https://github.com/psylopunk/pytonlib/blob/main/examples/transactions.py)
+
+
+
### Get contract's transactions
-
+
- **ton-community/ton:**
- [Client with getTransaction method](https://github.com/ton-community/ton/blob/master/src/client/TonClient.ts)
-
-
-- **psylopunk/pythonlib:**
- - [Get transactions](https://github.com/psylopunk/pytonlib/blob/main/examples/transactions.py)
-
-
+
- **xssnick/tonutils-go:**
- [Get transactions](https://github.com/xssnick/tonutils-go?tab=readme-ov-file#account-info-and-transactions)
+
+
+
+- **psylopunk/pythonlib:**
+ - [Get transactions](https://github.com/psylopunk/pytonlib/blob/main/examples/transactions.py)
+
+
+
## SDKs
-You can find a list of SDKs for various languages (js, python, golang, C#, Rust, etc.) list [here](/develop/dapps/apis/sdk).
+You can find a list of SDKs for various languages (JS, Python, Golang, C#, Rust, etc.) list [here](/develop/dapps/apis/sdk).
From b6df27b34e31e70bf73a163b9cdca392e4ba302c Mon Sep 17 00:00:00 2001
From: Andrey Semenov
Date: Tue, 30 Apr 2024 13:46:03 +0200
Subject: [PATCH 08/15] add(pythoniq): Examples for jettons processing
---
.../develop/dapps/asset-processing/jettons.md | 166 ++++++++++++++++++
1 file changed, 166 insertions(+)
diff --git a/docs/develop/dapps/asset-processing/jettons.md b/docs/develop/dapps/asset-processing/jettons.md
index bd36fde9da..01992b4e0d 100644
--- a/docs/develop/dapps/asset-processing/jettons.md
+++ b/docs/develop/dapps/asset-processing/jettons.md
@@ -614,6 +614,57 @@ await my_wallet.transfer_jetton_by_jetton_wallet(destination_address='address',
+
+
+
+
+
+
+Source code
+
+
+```py
+from pytoniq import LiteBalancer, WalletV4R2, begin_cell
+import asyncio
+
+mnemonics = ["your", "mnemonics", "here"]
+
+async def main():
+ provider = LiteBalancer.from_mainnet_config(1)
+ await provider.start_up()
+
+ wallet = await WalletV4R2.from_mnemonic(provider=provider, mnemonics=mnemonics)
+ USER_ADDRESS = wallet.address
+ JETTON_MASTER_ADDRESS = "EQBlqsm144Dq6SjbPI4jjZvA1hqTIP3CvHovbIfW_t-SCALE"
+ DESTINATION_ADDRESS = "EQAsl59qOy9C2XL5452lGbHU9bI3l4lhRaopeNZ82NRK8nlA"
+
+ USER_JETTON_WALLET = (await provider.run_get_method(address=JETTON_MASTER_ADDRESS,
+ method="get_wallet_address",
+ stack=[begin_cell().store_address(USER_ADDRESS).end_cell().begin_parse()]))[0].load_address()
+ forward_payload = (begin_cell()
+ .store_uint(0, 32) # TextComment op-code
+ .store_snake_string("Comment")
+ .end_cell())
+ transfer_cell = (begin_cell()
+ .store_uint(0xf8a7ea5, 32) # Jetton Transfer op-code
+ .store_uint(0, 64) # query_id
+ .store_coins(1 * 10**9) # Jetton amount to transfer in nanojetton
+ .store_address(DESTINATION_ADDRESS) # Destination address
+ .store_address(USER_ADDRESS) # Response address
+ .store_bit(0) # Custom payload is None
+ .store_coins(1) # Ton forward amount in nanoton
+ .store_bit(1) # Store forward_payload as a reference
+ .store_ref(forward_payload) # Forward payload
+ .end_cell())
+
+ await wallet.transfer(destination=USER_JETTON_WALLET, amount=int(0.05*1e9), body=transfer_cell)
+ await provider.close_all()
+
+asyncio.run(main())
+```
+
+
+
@@ -1012,6 +1063,121 @@ func GetTransferTransactions(orderId string, foundTransfer chan<- *tlb.Transacti
}
```
+
+
+
+
+
+
+
+Source code
+
+
+```py
+import asyncio
+
+from pytoniq import LiteBalancer, begin_cell
+
+MY_WALLET_ADDRESS = "EQAsl59qOy9C2XL5452lGbHU9bI3l4lhRaopeNZ82NRK8nlA"
+
+
+async def parse_transactions(provider: LiteBalancer, transactions):
+ for transaction in transactions:
+ if not transaction.in_msg.is_internal:
+ continue
+ if transaction.in_msg.info.dest.to_str(1, 1, 1) != MY_WALLET_ADDRESS:
+ continue
+
+ sender = transaction.in_msg.info.src.to_str(1, 1, 1)
+ value = transaction.in_msg.info.value_coins
+ if value != 0:
+ value = value / 1e9
+
+ if len(transaction.in_msg.body.bits) < 32:
+ print(f"TON transfer from {sender} with value {value} TON")
+ else:
+ body_slice = transaction.in_msg.body.begin_parse()
+ op_code = body_slice.load_uint(32)
+
+ # TextComment
+ if op_code == 0:
+ print(f"TON transfer from {sender} with value {value} TON and comment: {body_slice.load_snake_string()}")
+
+ # Jetton Transfer Notification
+ elif op_code == 0x7362d09c:
+ body_slice.load_bits(64) # skip query_id
+ jetton_amount = body_slice.load_coins() / 1e9
+ jetton_sender = body_slice.load_address().to_str(1, 1, 1)
+ if body_slice.load_bit():
+ forward_payload = body_slice.load_ref().begin_parse()
+ else:
+ forward_payload = body_slice
+
+ jetton_master = (await provider.run_get_method(address=sender, method="get_wallet_data", stack=[]))[2].load_address()
+ jetton_wallet = (await provider.run_get_method(address=jetton_master, method="get_wallet_address",
+ stack=[
+ begin_cell().store_address(MY_WALLET_ADDRESS).end_cell().begin_parse()
+ ]))[0].load_address().to_str(1, 1, 1)
+
+ if jetton_wallet != sender:
+ print("FAKE Jetton Transfer")
+ continue
+
+ if len(forward_payload.bits) < 32:
+ print(f"Jetton transfer from {jetton_sender} with value {jetton_amount} Jetton")
+ else:
+ forward_payload_op_code = forward_payload.load_uint(32)
+ if forward_payload_op_code == 0:
+ print(f"Jetton transfer from {jetton_sender} with value {jetton_amount} Jetton and comment: {forward_payload.load_snake_string()}")
+ else:
+ print(f"Jetton transfer from {jetton_sender} with value {jetton_amount} Jetton and unknown payload: {forward_payload} ")
+
+ # NFT Transfer Notification
+ elif op_code == 0x05138d91:
+ body_slice.load_bits(64) # skip query_id
+ prev_owner = body_slice.load_address().to_str(1, 1, 1)
+ if body_slice.load_bit():
+ forward_payload = body_slice.load_ref().begin_parse()
+ else:
+ forward_payload = body_slice
+
+ stack = await provider.run_get_method(address=sender, method="get_nft_data", stack=[])
+ index = stack[1]
+ collection = stack[2].load_address()
+ item_address = (await provider.run_get_method(address=collection, method="get_nft_address_by_index",
+ stack=[index]))[0].load_address().to_str(1, 1, 1)
+
+ if item_address != sender:
+ print("FAKE NFT Transfer")
+ continue
+
+ if len(forward_payload.bits) < 32:
+ print(f"NFT transfer from {prev_owner}")
+ else:
+ forward_payload_op_code = forward_payload.load_uint(32)
+ if forward_payload_op_code == 0:
+ print(f"NFT transfer from {prev_owner} with comment: {forward_payload.load_snake_string()}")
+ else:
+ print(f"NFT transfer from {prev_owner} with unknown payload: {forward_payload}")
+
+ print(f"NFT Item: {item_address}")
+ print(f"NFT Collection: {collection}")
+ print(f"Transaction hash: {transaction.cell.hash.hex()}")
+ print(f"Transaction lt: {transaction.lt}")
+
+
+async def main():
+ provider = LiteBalancer.from_mainnet_config(1)
+ await provider.start_up()
+ transactions = await provider.get_transactions(address=MY_WALLET_ADDRESS, count=5)
+ await parse_transactions(provider, transactions)
+ await provider.close_all()
+
+
+if __name__ == "__main__":
+ asyncio.run(main())
+```
+
From 805877d65cff895bd1392bf83540ca7abcd492de Mon Sep 17 00:00:00 2001
From: Andrey Semenov
Date: Tue, 30 Apr 2024 13:47:27 +0200
Subject: [PATCH 09/15] add(pytoniq): Examples of wallet creation, deposits
listing and transaction sending
---
docs/develop/dapps/asset-processing/README.md | 124 ++++++++++++++++++
1 file changed, 124 insertions(+)
diff --git a/docs/develop/dapps/asset-processing/README.md b/docs/develop/dapps/asset-processing/README.md
index 66232eeb4f..2e84c002d4 100644
--- a/docs/develop/dapps/asset-processing/README.md
+++ b/docs/develop/dapps/asset-processing/README.md
@@ -197,6 +197,32 @@ To generate a transaction link in the explorer, the service needs to get the lt
- **psylopunk/pythonlib:**
- [Wallet creation + get wallet address](https://github.com/psylopunk/pytonlib/blob/main/examples/generate_wallet.py)
+
+- **pytoniq**
+
+Create wallet
+```py
+import asyncio
+
+from pytoniq.contract.wallets.wallet import WalletV4R2
+from pytoniq.liteclient.balancer import LiteBalancer
+
+
+async def main():
+ provider = LiteBalancer.from_mainnet_config(2)
+ await provider.start_up()
+
+ mnemonics, wallet = await WalletV4R2.create(provider)
+ print(f"{wallet.address=} and {mnemonics=}")
+
+ await provider.close_all()
+
+
+if __name__ == "__main__":
+ asyncio.run(main())
+```
+
+
@@ -307,6 +333,71 @@ func main() {
+
+
+- **yungwine/pytoniq:**
+
+
+Checking deposits
+
+```python
+import asyncio
+
+from pytoniq_core import Transaction
+
+from pytoniq import LiteClient, Address
+
+MY_ADDRESS = Address("kf8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM_BP")
+
+
+async def main():
+ client = LiteClient.from_mainnet_config(ls_i=0, trust_level=2)
+
+ await client.connect()
+
+ last_block = await client.get_trusted_last_mc_block()
+
+ _account, shard_account = await client.raw_get_account_state(MY_ADDRESS, last_block)
+ assert shard_account
+
+ last_trans_lt, last_trans_hash = (
+ shard_account.last_trans_lt,
+ shard_account.last_trans_hash,
+ )
+
+ while True:
+ print(f"Waiting for{last_block=}")
+
+ transactions = await client.get_transactions(
+ MY_ADDRESS, 1024, last_trans_lt, last_trans_hash
+ )
+ toncoin_deposits = [tx for tx in transactions if filter_toncoin_deposit(tx)]
+ print(f"Got {len(transactions)=} with {len(toncoin_deposits)=}")
+
+ for deposit_tx in toncoin_deposits:
+ # Process toncoin deposit transaction
+ print(deposit_tx.cell.hash.hex())
+
+ last_trans_lt = transactions[0].lt
+ last_trans_hash = transactions[0].cell.hash
+
+
+def filter_toncoin_deposit(tx: Transaction):
+ if tx.out_msgs:
+ return False
+
+ if tx.in_msg:
+ return False
+
+ return True
+
+
+if __name__ == "__main__":
+ asyncio.run(main())
+```
+
+
+
### Toncoin Withdrawals (Send toncoins)
@@ -335,6 +426,36 @@ func main() {
- **psylopunk/pythonlib:**
- [Withdraw Toncoins from a wallet](https://github.com/psylopunk/pytonlib/blob/main/examples/transactions.py)
+- **yungwine/pytoniq:**
+
+
+```python
+import asyncio
+
+from pytoniq_core import Address
+from pytoniq.contract.wallets.wallet import WalletV4R2
+from pytoniq.liteclient.balancer import LiteBalancer
+
+
+MY_MNEMONICS = "one two tree ..."
+DESTINATION_WALLET = Address("Destination wallet address")
+
+
+async def main():
+ provider = LiteBalancer.from_mainnet_config()
+ await provider.start_up()
+
+ wallet = await WalletV4R2.from_mnemonic(provider, MY_MNEMONICS)
+
+ await wallet.transfer(DESTINATION_WALLET, 5)
+
+
+if __name__ == "__main__":
+ asyncio.run(main())
+```
+
+
+
@@ -360,6 +481,9 @@ func main() {
- **psylopunk/pythonlib:**
- [Get transactions](https://github.com/psylopunk/pytonlib/blob/main/examples/transactions.py)
+
+- **yungwine/pytoniq:**
+ - [Get transactions](https://github.com/yungwine/pytoniq/blob/master/examples/transactions.py)
From b29332b636c1e8e73075e5df0cb15ad845275725 Mon Sep 17 00:00:00 2001
From: Full-Hat
Date: Mon, 29 Apr 2024 19:48:12 +0300
Subject: [PATCH 10/15] Merge updates
---
docs/develop/dapps/asset-processing/README.md | 246 ++++---------
.../asset-processing/address-verification.mdx | 71 ----
.../develop/dapps/asset-processing/jettons.md | 338 +++++-------------
.../dapps/asset-processing/overview.md | 58 +++
sidebars.js | 4 +-
5 files changed, 215 insertions(+), 502 deletions(-)
delete mode 100644 docs/develop/dapps/asset-processing/address-verification.mdx
create mode 100644 docs/develop/dapps/asset-processing/overview.md
diff --git a/docs/develop/dapps/asset-processing/README.md b/docs/develop/dapps/asset-processing/README.md
index 2e84c002d4..29913e78f8 100644
--- a/docs/develop/dapps/asset-processing/README.md
+++ b/docs/develop/dapps/asset-processing/README.md
@@ -4,50 +4,8 @@ import TabItem from '@theme/TabItem';
# Payments processing
-This page contains an overview on TON transactions and specific details that explain how to work with ton wallets and process (send and accept) digital assets on the TON blockchain.
-
-## Global overview
-Embodying a fully asynchronous approach, TON Blockchain involves a few concepts which are uncommon to traditional blockchains. Particularly, each interaction of any actor with the blockchain consists of a graph of asynchronously transferred [messages](/develop/smart-contracts/guidelines/message-delivery-guarantees) between smart contracts and/or the external world. Each transaction consists of one incoming message and up to 512 outgoing messages.
-
-There are 3 types of messages, that are fully described [here](/develop/smart-contracts/messages#types-of-messages). To put it briefly:
-* [external message](/develop/smart-contracts/guidelines/external-messages):
- * `external in message` (sometimes called just `external message`) is message that is sent from *outside* of the blockchain to a smart contract *inside* the blockchain.
- * `external out message` (usually called `logs message`) is sent from a *blockchain entity* to the *outer world*.
-* [internal message](/develop/smart-contracts/guidelines/internal-messages) is sent from one *blockchain entity* to *another*, can carry some amount of digital assets and arbitrary portion of data.
-
-The common path of any interaction starts with an external message sent to a `wallet` smart contract, which authenticates the message sender using public-key cryptography, takes charge of fee payment, and sends internal blockchain messages. That messages queue form directional acyclic graph, or a tree.
-
-For example:
-
-
-
-* `Alice` use e.g [Tonkeeper](https://tonkeeper.com/) to send an `external message` to her wallet.
-* `external message` is the input message for `wallet A v4` contract with empty soure (a message from nowhere, such as [Tonkeeper](https://tonkeeper.com/)).
-* `outgoing message` is the output message for `wallet A v4` contract and input message for `wallet B v4` contract with `wallet A v4` source and `wallet B v4` destination.
-
-As a result there are 2 transactions with their set of input and output messages.
-
-Each action, when contract take message as input (triggered by it), process it and generate or not generate outgoing messages as output, called `transaction`. Read more about transactions [here](/develop/smart-contracts/guidelines/message-delivery-guarantees#what-is-a-transaction).
-
-That `transactions` can span a **prolonged period** of time. Technically, transactions with queues of messages are aggregated into blocks processed by validators. The asynchronous nature of the TON Blockchain **does not allow to predict the hash and lt (logical time) of a transaction** at the stage of sending a message.
-
-The `transaction` accepted to the block is final and cannot be modified.
-
-:::info Transaction Confirmation
-TON transactions are irreversible after just one confirmation. For the best user experience, it is suggested to avoid waiting on additional blocks once transactions are finalized on the TON Blockchain. Read more in the [Catchain.pdf](https://docs.ton.org/catchain.pdf#page=3).
-:::
-
-Smart contracts pay several types of [fees](/develop/smart-contracts/fees) for transactions (usually from the balance of an incoming message, behavior depends on [message mode](/develop/smart-contracts/messages#message-modes)). Amount of fees depends on workchain configs with maximal fees on `masterchain` and substantially lower fees on `basechain`.
-
-## Digital asset types on TON
-
-TON has three types of digital assets.
-- Toncoin, the main token of the network. It is used for all basic operations on the blockchain, for example, paying gas fees or staking for validation.
-- Contract assets, such as tokens and NFTs, which are analogous to the ERC-20/ERC-721 standards and are managed by arbitrary contracts and thus can require custom rules for processing. You can find more info on it's processing in [process NFTs](/develop/dapps/asset-processing/nfts) and [process Jettons](/develop/dapps/asset-processing/jettons) articles.
-- Native token, which is special kind of assets that can be attached to any message on the network. But these asset is currently not in use since the functionality for issuing new native tokens is closed.
-
-## Interaction with TON blockchain
-Basic operations on TON Blockchain can be carried out via TonLib. It is a shared library which can be compiled along with a TON node and expose APIs for interaction with the blockchain via so-called lite servers (servers for lite clients). TonLib follows a trustless approach by checking proofs for all incoming data; thus, there is no necessity for a trusted data provider. Methods available to TonLib are listed [in the TL scheme](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L234). They can be used either as a shared library via [wrappers](/develop/dapps/asset-processing/#repositories).
+This page **explains how to process** (send and accept) `digital assets` on the TON blockchain.
+It **mostly** describes how to work with `TON coins`, but **theoretical part** is **important** even if you want to process only `jettons`.
## Wallet smart contract
@@ -56,7 +14,7 @@ Wallet smart contracts are contracts on the TON Network which serve the task of
* replays protection: Prohibits the repetitive execution of one request, for instance sending assets to some other smart contract.
* initiates arbitrary interaction with other smart contracts.
-Standard solution for the first challenge is public-key cryptography: `wallet` stores the public key and checks that an incoming message with a request is signed by the corresponding private key which is known only by the owner.
+Standard solution for the first challenge is public-key cryptography: `wallet` stores the public key and checks that an incoming message with a request is signed by the corresponding private key which is known only by the owner.
The solution to the third challenge is common as well; generally, a request contains a fully formed inner message `wallet` sends to the network. However, for replay protection, there are a few different approaches.
@@ -79,13 +37,73 @@ To deploy a wallet via TonLib one needs to:
Read more in the [Wallet Tutorial](/develop/smart-contracts/tutorials/wallet#-deploying-a-wallet)
:::
+### Check the validity of wallet address
+
+Most SDKs force you to verify address (most verify it during wallet creation or transaction preparation process), so, usually, it's not require any additional complex steps from you.
+
+
+
+
+
+ ```js
+ const TonWeb = require("tonweb")
+ TonWeb.utils.Address.isValid('...')
+ ```
+
+
+
+
+ ```python
+ package main
+
+ import (
+ "fmt"
+ "github.com/xssnick/tonutils-go/address"
+ )
+
+ if _, err := address.ParseAddr("EQCD39VS5j...HUn4bpAOg8xqB2N"); err != nil {
+ return errors.New("invalid address")
+ }
+ ```
+
+
+
+
+
+ ```javascript
+ try {
+ Address.of("...");
+ } catch (e) {
+ // not valid address
+ }
+ ```
+
+
+
+
+```javascript
+ try {
+ AddrStd("...")
+ } catch(e: IllegalArgumentException) {
+ // not valid address
+ }
+```
+
+
+
+
+:::tip
+Full Address description on the [Smart Contract Addresses](/learn/overviews/addresses) page.
+:::
+
+
## Work with transfers
### Check contract's transactions
A contract's transactions can be obtained using [getTransactions](https://toncenter.com/api/v2/#/accounts/get_transactions_getTransactions_get). This method allows to get 10 transactions from some `last_transaction_id` and earlier. To process all incoming transactions, the following steps should be followed:
1. The latest `last_transaction_id` can be obtained using [getAddressInformation](https://toncenter.com/api/v2/#/accounts/get_address_information_getAddressInformation_get)
2. List of 10 transactions should be loaded via the `getTransactions` method.
-3. Process transactions with not empty source in incoming message and destination equals to account address.
+3. Process transactions with not empty source in incoming message and destination equals to account address.
4. The next 10 transactions should be loaded and steps 2,3,4,5 should be repeated until you processed all incoming transactions.
### Checking transaction's flow
@@ -178,7 +196,7 @@ To generate a transaction link in the explorer, the service needs to get the lt
-- **toncenter:**
+- **toncenter:**
- [Wallet creation + get wallet address](https://github.com/toncenter/examples/blob/main/common.js)
- **ton-community/ton:**
@@ -188,41 +206,15 @@ To generate a transaction link in the explorer, the service needs to get the lt
-- **xssnick/tonutils-go:**
+- **xssnick/tonutils-go:**
- [Wallet creation + get balance](https://github.com/xssnick/tonutils-go?tab=readme-ov-file#wallet)
-- **psylopunk/pythonlib:**
+- **psylopunk/pythonlib:**
- [Wallet creation + get wallet address](https://github.com/psylopunk/pytonlib/blob/main/examples/generate_wallet.py)
-
-- **pytoniq**
-
-Create wallet
-```py
-import asyncio
-
-from pytoniq.contract.wallets.wallet import WalletV4R2
-from pytoniq.liteclient.balancer import LiteBalancer
-
-
-async def main():
- provider = LiteBalancer.from_mainnet_config(2)
- await provider.start_up()
-
- mnemonics, wallet = await WalletV4R2.create(provider)
- print(f"{wallet.address=} and {mnemonics=}")
-
- await provider.close_all()
-
-
-if __name__ == "__main__":
- asyncio.run(main())
-```
-
-
@@ -233,8 +225,8 @@ if __name__ == "__main__":
-- **toncenter:**
- - [Process Toncoins deposit](https://github.com/toncenter/examples/blob/main/deposits.js)
+- **toncenter:**
+ - [Process Toncoins deposit](https://github.com/toncenter/examples/blob/main/deposits.js)
- [Process Toncoins deposit multi wallets](https://github.com/toncenter/examples/blob/main/deposits-multi-wallets.js)
@@ -333,71 +325,6 @@ func main() {
-
-
-- **yungwine/pytoniq:**
-
-
-Checking deposits
-
-```python
-import asyncio
-
-from pytoniq_core import Transaction
-
-from pytoniq import LiteClient, Address
-
-MY_ADDRESS = Address("kf8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM_BP")
-
-
-async def main():
- client = LiteClient.from_mainnet_config(ls_i=0, trust_level=2)
-
- await client.connect()
-
- last_block = await client.get_trusted_last_mc_block()
-
- _account, shard_account = await client.raw_get_account_state(MY_ADDRESS, last_block)
- assert shard_account
-
- last_trans_lt, last_trans_hash = (
- shard_account.last_trans_lt,
- shard_account.last_trans_hash,
- )
-
- while True:
- print(f"Waiting for{last_block=}")
-
- transactions = await client.get_transactions(
- MY_ADDRESS, 1024, last_trans_lt, last_trans_hash
- )
- toncoin_deposits = [tx for tx in transactions if filter_toncoin_deposit(tx)]
- print(f"Got {len(transactions)=} with {len(toncoin_deposits)=}")
-
- for deposit_tx in toncoin_deposits:
- # Process toncoin deposit transaction
- print(deposit_tx.cell.hash.hex())
-
- last_trans_lt = transactions[0].lt
- last_trans_hash = transactions[0].cell.hash
-
-
-def filter_toncoin_deposit(tx: Transaction):
- if tx.out_msgs:
- return False
-
- if tx.in_msg:
- return False
-
- return True
-
-
-if __name__ == "__main__":
- asyncio.run(main())
-```
-
-
-
### Toncoin Withdrawals (Send toncoins)
@@ -426,36 +353,6 @@ if __name__ == "__main__":
- **psylopunk/pythonlib:**
- [Withdraw Toncoins from a wallet](https://github.com/psylopunk/pytonlib/blob/main/examples/transactions.py)
-- **yungwine/pytoniq:**
-
-
-```python
-import asyncio
-
-from pytoniq_core import Address
-from pytoniq.contract.wallets.wallet import WalletV4R2
-from pytoniq.liteclient.balancer import LiteBalancer
-
-
-MY_MNEMONICS = "one two tree ..."
-DESTINATION_WALLET = Address("Destination wallet address")
-
-
-async def main():
- provider = LiteBalancer.from_mainnet_config()
- await provider.start_up()
-
- wallet = await WalletV4R2.from_mnemonic(provider, MY_MNEMONICS)
-
- await wallet.transfer(DESTINATION_WALLET, 5)
-
-
-if __name__ == "__main__":
- asyncio.run(main())
-```
-
-
-
@@ -481,9 +378,6 @@ if __name__ == "__main__":
- **psylopunk/pythonlib:**
- [Get transactions](https://github.com/psylopunk/pytonlib/blob/main/examples/transactions.py)
-
-- **yungwine/pytoniq:**
- - [Get transactions](https://github.com/yungwine/pytoniq/blob/master/examples/transactions.py)
@@ -491,4 +385,4 @@ if __name__ == "__main__":
## SDKs
-You can find a list of SDKs for various languages (JS, Python, Golang, C#, Rust, etc.) list [here](/develop/dapps/apis/sdk).
+You can find a list of SDKs for various languages (JS, Python, Golang, C#, Rust, etc.) list [here](/develop/dapps/apis/sdk).
\ No newline at end of file
diff --git a/docs/develop/dapps/asset-processing/address-verification.mdx b/docs/develop/dapps/asset-processing/address-verification.mdx
deleted file mode 100644
index 6f6bf975b4..0000000000
--- a/docs/develop/dapps/asset-processing/address-verification.mdx
+++ /dev/null
@@ -1,71 +0,0 @@
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-# Wallet Address Validation
-
-### How to Check the Validity of a TON Address?
-
-
-
-
-
-
-
- ```js
- const TonWeb = require("tonweb")
- TonWeb.utils.Address.isValid('...')
- ```
-
-
-
-
- ```python
- package main
-
- import (
- "fmt"
- "github.com/xssnick/tonutils-go/address"
- )
-
- if _, err := address.ParseAddr("EQCD39VS5j...HUn4bpAOg8xqB2N"); err != nil {
- return errors.New("invalid address")
- }
- ```
-
-
-
-
-
- ```javascript
- /* Maven
-
- io.github.neodix42
- address
- 0.3.2
-
- */
-
- try {
- Address.of("...");
- } catch (Exception e) {
- // not valid address
- }
- ```
-
-
-
-
-```javascript
- try {
- AddrStd("...")
- } catch (e: IllegalArgumentException) {
- // not valid address
- }
-```
-
-
-
-
-:::tip
-Full Address description on the [Smart Contract Addresses](/learn/overviews/addresses) page.
-:::
diff --git a/docs/develop/dapps/asset-processing/jettons.md b/docs/develop/dapps/asset-processing/jettons.md
index 01992b4e0d..b1982dadf7 100644
--- a/docs/develop/dapps/asset-processing/jettons.md
+++ b/docs/develop/dapps/asset-processing/jettons.md
@@ -4,8 +4,6 @@ import Button from '@site/src/components/button';
# TON Jetton processing
-## Overview
-
:::info
For clear understanding, the reader should be familiar with the basic principles of asset processing described in [payments processing section](/develop/dapps/asset-processing/) of our documentation.
:::
@@ -79,11 +77,11 @@ console.log('URI to off-chain metadata:', data.jettonContentUri);
## Jetton wallet smart contract
-`Jetton wallet` contracts are used to **send**, **receive**, and **burn** jettons. Each _jetton wallet contract_ stores wallet balance information for specific users.
+`Jetton wallet` contracts are used to **send**, **receive**, and **burn** jettons. Each _jetton wallet contract_ stores wallet balance information for specific users.
In specific instances, jetton wallets are used for individual jetton holders for each jetton type.
-`Jetton wallets` **should not be confused with wallet’s** meant for blockchain interaction and storing
-only the Toncoin asset (e.g., v3R2 wallets, highload wallets, and others),
+`Jetton wallets` **should not be confused with wallet’s** meant for blockchain interaction and storing
+only the Toncoin asset (e.g., v3R2 wallets, highload wallets, and others),
which is responsible for supporting and managing **only a specific jetton type**.
### Jetton Wallet Deployment
@@ -94,7 +92,7 @@ The recipient's jetton wallet will be deployed automatically as long as the send
in the wallet to pay the required gas fees.
### Retrieving Jetton wallet addresses for a given user
-To retrieve a `jetton wallet` `address` using an `owner address` (a TON Wallet address),
+To retrieve a `jetton wallet` `address` using an `owner address` (a TON Wallet address),
the `Jetton master contract` provides the get method `get_wallet_address(slice owner_address)`.
@@ -233,7 +231,7 @@ Check [best practices](/develop/dapps/asset-processing/jettons#best-practices) f
TON transactions are irreversible after just one confirmation. For the best user experience, it is suggested to avoid waiting on additional blocks once transactions are finalized on the TON Blockchain. Read more in the [Catchain.pdf](https://docs.ton.org/catchain.pdf#page=3).
:::
- There are two ways to accept Jettons:
+There are two ways to accept Jettons:
- within a **centralized hot wallet**.
- using a wallet with a **separate address** for **each individual user**.
@@ -249,17 +247,17 @@ When processing funds, it is also recommended to provide a cold wallet for stori
### Identification of an unknown Jetton when receiving a transfer notification message
- If a transfer notification message is received within your wallet regarding an unknown Jetton, then your wallet
- has been created to hold the specific Jetton.
+If a transfer notification message is received within your wallet regarding an unknown Jetton, then your wallet
+has been created to hold the specific Jetton.
-The sender address of the internal message containing the `Transfer notification` body is the address of the new Jetton wallet.
+The sender address of the internal message containing the `Transfer notification` body is the address of the new Jetton wallet.
It should not to be confused with the `sender` field in the `Transfer notification` [body](/develop/dapps/asset-processing/jettons#jetton-wallets-communication-overview).
1. Retrieve the Jetton master address for the new Jetton wallet by [getting wallet data](/develop/dapps/asset-processing/jettons#retrieving-data-for-a-specific-jetton-wallet).
2. Retrieve the Jetton wallet address for your wallet address (as an owner) using the Jetton master contract: [How to retrieve Jetton wallet address for a given user](#retrieving-jetton-wallet-addresses-for-a-given-user)
-3. Compare the address returned by the master contract and the actual address of the wallet token.
-If they match, it’s ideal. If not, then you likely received a scam token that is counterfeit.
-4. Retrieve Jetton metadata: [How to receive Jetton metadata](#retrieving-jetton-data).
+3. Compare the address returned by the master contract and the actual address of the wallet token.
+ If they match, it’s ideal. If not, then you likely received a scam token that is counterfeit.
+4. Retrieve Jetton metadata: [How to receive Jetton metadata](#retrieving-jetton-data).
5. Check the `symbol` and `name` fields for signs of a scam. Warn the user if necessary. [Adding a new Jettons for processing and initial checks](#adding-new-jettons-for-asset-processing-and-initial-verification).
@@ -269,8 +267,8 @@ If they match, it’s ideal. If not, then you likely received a scam token that
To prevent a bottleneck in incoming transactions to a single wallet, it is suggested to accept deposits across multiple wallets and to expand the number of these wallets as needed.
:::
-In this scenario, the payment service creates a unique memo identifier for each sender disclosing
-the address of the centralized wallet and the amounts being sent. The sender sends the tokens
+In this scenario, the payment service creates a unique memo identifier for each sender disclosing
+the address of the centralized wallet and the amounts being sent. The sender sends the tokens
to the specified centralized address with the obligatory memo in the comment.
**Pros of this method:** this method is very simple because there are no additional fees when accepting tokens and they are retrieved directly in the hot wallet.
@@ -290,32 +288,32 @@ to the specified centralized address with the obligatory memo in the comment.
4. Compare the addresses of the Jetton master contracts from step 1. and step 3 (directly above).
If the addresses do not match, a Jetton address verification error must be reported.
5. Retrieve a list of the most recent unprocessed transactions using a hot wallet account and
-iterate it (by sorting through each transaction one by one). See: [Checking contract's transactions](https://docs.ton.org/develop/dapps/asset-processing/#checking-contracts-transactions).
+ iterate it (by sorting through each transaction one by one). See: [Checking contract's transactions](https://docs.ton.org/develop/dapps/asset-processing/#checking-contracts-transactions).
6. Check the input message (in_msg) for transactions and retrieve the source address from the input message. [Tonweb example](https://github.com/toncenter/examples/blob/9f20f7104411771793dfbbdf07f0ca4860f12de2/deposits-jettons-single-wallet.js#L84)
-7. If the source address matches the address within a Jetton wallet, then it is necessary to continue processing the transaction.
-If not, then skip processing the transaction and check the next transaction.
-8. Ensure that the message body is not empty and that the first 32 bits of the message match the `transfer notification` op code `0x7362d09c`.
-[Tonweb example](https://github.com/toncenter/examples/blob/9f20f7104411771793dfbbdf07f0ca4860f12de2/deposits-jettons-single-wallet.js#L91)
+7. If the source address matches the address within a Jetton wallet, then it is necessary to continue processing the transaction.
+ If not, then skip processing the transaction and check the next transaction.
+8. Ensure that the message body is not empty and that the first 32 bits of the message match the `transfer notification` op code `0x7362d09c`.
+ [Tonweb example](https://github.com/toncenter/examples/blob/9f20f7104411771793dfbbdf07f0ca4860f12de2/deposits-jettons-single-wallet.js#L91)
If the message body is empty or the op code is invalid - skip the transaction.
-9. Read the message body’s other data, including the `query_id`, `amount`, `sender`, `forward_payload`.
-[Jetton contracts message layouts](#jetton-contract-message-layouts), [Tonweb example](https://github.com/toncenter/examples/blob/9f20f7104411771793dfbbdf07f0ca4860f12de2/deposits-jettons-single-wallet.js#L105)
-10. Try to retrieve text comments from the `forward_payload` data. The first 32 bits must match
-the text comment op code `0x00000000` and the remaining - UTF-8 encoded text.
-[Tonweb example](https://github.com/toncenter/examples/blob/9f20f7104411771793dfbbdf07f0ca4860f12de2/deposits-jettons-single-wallet.js#L110)
+9. Read the message body’s other data, including the `query_id`, `amount`, `sender`, `forward_payload`.
+ [Jetton contracts message layouts](#jetton-contract-message-layouts), [Tonweb example](https://github.com/toncenter/examples/blob/9f20f7104411771793dfbbdf07f0ca4860f12de2/deposits-jettons-single-wallet.js#L105)
+10. Try to retrieve text comments from the `forward_payload` data. The first 32 bits must match
+ the text comment op code `0x00000000` and the remaining - UTF-8 encoded text.
+ [Tonweb example](https://github.com/toncenter/examples/blob/9f20f7104411771793dfbbdf07f0ca4860f12de2/deposits-jettons-single-wallet.js#L110)
11. If the `forward_payload` data is empty or the op code is invalid - skip the transaction.
12. Compare the received comment with the saved memos. If there is a match (user identification is always possible) - deposit the transfer.
13. Restart from step 5 and repeat the process until you have walked through the entire list of transactions.
### Accepting Jettons from user deposit addresses
-To accept Jettons from user deposit addresses, it is necessary that the payment service creates its
-own individual address (deposit) for each participant sending funds. The service provision in this case involves
-the execution of several parallel processes including creating new deposits, scanning blocks for transactions,
+To accept Jettons from user deposit addresses, it is necessary that the payment service creates its
+own individual address (deposit) for each participant sending funds. The service provision in this case involves
+the execution of several parallel processes including creating new deposits, scanning blocks for transactions,
withdrawing funds from deposits to a hot wallet, and so on.
-Because a hot wallet can make use of one Jetton wallet for each Jetton type, it is necessary to create multiple
-wallets to initiate deposits. In order to create a large number of wallets, but at the same time manage them with
-one seed phrase (or private key), it is necessary to specify a different `subwallet_id` when creating a wallet.
+Because a hot wallet can make use of one Jetton wallet for each Jetton type, it is necessary to create multiple
+wallets to initiate deposits. In order to create a large number of wallets, but at the same time manage them with
+one seed phrase (or private key), it is necessary to specify a different `subwallet_id` when creating a wallet.
On TON, the functionality required to create a subwallet is supported by version v3 wallets and higher.
@@ -339,11 +337,11 @@ const wallet = new WalletClass(tonweb.provider, {
1. Accept a request to create a new deposit for the user.
2. Generate a new subwallet (v3R2) address based on the hot wallet seed. [Creating a subwallet in Tonweb](#creating-a-subwallet-in-tonweb)
-3. The receiving address can be given to the user as the address used for Jetton deposits (this is the address of
-the owner of the deposit Jetton wallet). Wallet initialization is not required, this can be
-accomplished when withdrawing Jettons from the deposit.
-4. For this address, it is necessary to calculate the address of the Jetton wallet through the Jetton master contract.
-[How to retrieve a Jetton wallet address for a given user](#retrieving-jetton-wallet-addresses-for-a-given-user).
+3. The receiving address can be given to the user as the address used for Jetton deposits (this is the address of
+ the owner of the deposit Jetton wallet). Wallet initialization is not required, this can be
+ accomplished when withdrawing Jettons from the deposit.
+4. For this address, it is necessary to calculate the address of the Jetton wallet through the Jetton master contract.
+ [How to retrieve a Jetton wallet address for a given user](#retrieving-jetton-wallet-addresses-for-a-given-user).
5. Add the Jetton wallet address to the address pool for transaction monitoring and save the subwallet address.
#### Processing transactions
@@ -352,12 +350,12 @@ accomplished when withdrawing Jettons from the deposit.
TON transactions are irreversible after just one confirmation. For the best user experience, it is suggested to avoid waiting on additional blocks once transactions are finalized on the TON Blockchain. Read more in the [Catchain.pdf](https://docs.ton.org/catchain.pdf#page=3).
:::
-It is not always possible to determine the exact amount of Jettons received from the message, because Jetton
-wallets may not send `transfer notification`, `excesses`, and `internal transfer` messages. They are not standardized. This means
+It is not always possible to determine the exact amount of Jettons received from the message, because Jetton
+wallets may not send `transfer notification`, `excesses`, and `internal transfer` messages. They are not standardized. This means
that there is no guarantee that the `internal transfer` message can be decoded.
-Therefore, to determine the amount received in the wallet, balances need to be requested using the get method.
-To retrieve key data when requesting balances, blocks are used according to the account’s state for a particular block on-chain.
+Therefore, to determine the amount received in the wallet, balances need to be requested using the get method.
+To retrieve key data when requesting balances, blocks are used according to the account’s state for a particular block on-chain.
[Preparation for block acceptance using Tonweb](https://github.com/toncenter/tonweb/blob/master/src/test-block-subscribe.js).
This process is conducted as follows:
@@ -366,26 +364,26 @@ This process is conducted as follows:
2. Retrieve a new block and save the previous block ID.
3. Receive transactions from blocks.
4. Filter transactions used only with addresses from the deposit Jetton wallet pool.
-5. Decode messages using the `transfer notification` body to receive more detailed data including the
-`sender` address, Jetton `amount` and comment. (See: [Processing incoming Jettons](#processing-incoming-jettons))
-6. If there is at least one transaction with non-decodable out messages (the message body does not contain op codes for
-`transfer notification` and op codes for `excesses`) or without out messages present within the
-account, then the Jetton balance must be requested using the get method for the current block, while the previous
-block is used to calculate the difference in balances. Now the total balance deposit changes are revealed due
-to the transactions being conducted within the block.
-7. As an identifier for an unidentified transfer of Jettons (without a `transfer notification`), transaction data
-can be used if there is one such transaction or block data present (if several are present within a block).
+5. Decode messages using the `transfer notification` body to receive more detailed data including the
+ `sender` address, Jetton `amount` and comment. (See: [Processing incoming Jettons](#processing-incoming-jettons))
+6. If there is at least one transaction with non-decodable out messages (the message body does not contain op codes for
+ `transfer notification` and op codes for `excesses`) or without out messages present within the
+ account, then the Jetton balance must be requested using the get method for the current block, while the previous
+ block is used to calculate the difference in balances. Now the total balance deposit changes are revealed due
+ to the transactions being conducted within the block.
+7. As an identifier for an unidentified transfer of Jettons (without a `transfer notification`), transaction data
+ can be used if there is one such transaction or block data present (if several are present within a block).
8. Now it’s necessary to check to ensure the deposit balance is correct. If the deposit balance is sufficient enough to initiate a transfer between a hot wallet and the existing Jetton wallet, Jettons need to be withdrawn to ensure the wallet balance has decreased.
9. Restart from step 2 and repeat the entire process.
#### Withdrawals made from deposits
-Transfers should not be made from a deposit to a hot wallet with each deposit replenishment,
-because a commission in TON is taken for the transfer operation (paid in network gas fees).
-It is important to determine a certain minimum amount of Jettons which are required to make a
+Transfers should not be made from a deposit to a hot wallet with each deposit replenishment,
+because a commission in TON is taken for the transfer operation (paid in network gas fees).
+It is important to determine a certain minimum amount of Jettons which are required to make a
transfer worthwhile (and thus deposit).
-By default, wallet owners of Jetton deposit wallets are not initialized. This is because there is no predetermined
+By default, wallet owners of Jetton deposit wallets are not initialized. This is because there is no predetermined
requirement to pay storage fees. Jetton deposit wallets can be deployed when sending messages with a
`transfer` body which can then be destroyed immediately. To do this, the engineer must use a special
mechanism for sending messages: [128 + 32](/develop/smart-contracts/messages#message-modes).
@@ -393,20 +391,20 @@ mechanism for sending messages: [128 + 32](/develop/smart-contracts/messages#mes
1. Retrieve a list of deposits marked for withdrawal to a hot wallet
2. Retrieve saved owner addresses for each deposit
-3. Messages are then sent to each owner address (by combining several such messages into a batch) from a highload
-wallet with an attached TON Jetton amount. This is determined by adding the fees used for v3R2 wallet
-initialization + the fees for sending a message with the `transfer` body + an arbitrary TON amount related to the `forward_ton_amount`
-(if necessary). The attached TON amount is determined by adding the fees for v3R2 wallet initialization (value) +
-the fees for sending a message with the `transfer` body (value) + an arbitrary TON amount
-for `forward_ton_amount` (value) (if necessary).
-4. When the balance on the address becomes non-zero, the account status changes. Wait a few seconds and check the status
-of the account, it will soon change from the `nonexists` state to `uninit`.
-5. For each owner address (with `uninit` status), it is necessary to send an external message with the v3R2 wallet
-init and body with the `transfer` message for depositing into the Jetton wallet = 128 + 32. For the `transfer`,
-the user must specify the address of the hot wallet as the `destination` and `response destination`.
-A text comment can be added to make it simpler to identify the transfer.
-6. It is possible to verify Jetton delivery using the deposit address to the hot wallet address by
-taking into consideration the [processing of incoming Jettons info found here](#processing-incoming-jettons).
+3. Messages are then sent to each owner address (by combining several such messages into a batch) from a highload
+ wallet with an attached TON Jetton amount. This is determined by adding the fees used for v3R2 wallet
+ initialization + the fees for sending a message with the `transfer` body + an arbitrary TON amount related to the `forward_ton_amount`
+ (if necessary). The attached TON amount is determined by adding the fees for v3R2 wallet initialization (value) +
+ the fees for sending a message with the `transfer` body (value) + an arbitrary TON amount
+ for `forward_ton_amount` (value) (if necessary).
+4. When the balance on the address becomes non-zero, the account status changes. Wait a few seconds and check the status
+ of the account, it will soon change from the `nonexists` state to `uninit`.
+5. For each owner address (with `uninit` status), it is necessary to send an external message with the v3R2 wallet
+ init and body with the `transfer` message for depositing into the Jetton wallet = 128 + 32. For the `transfer`,
+ the user must specify the address of the hot wallet as the `destination` and `response destination`.
+ A text comment can be added to make it simpler to identify the transfer.
+6. It is possible to verify Jetton delivery using the deposit address to the hot wallet address by
+ taking into consideration the [processing of incoming Jettons info found here](#processing-incoming-jettons).
### Jetton withdrawals
@@ -416,10 +414,10 @@ It's **recommended** to read and **understand** [how does jetton transfer work](
Below you'll find step-by-step guide how to process jetton withdrawals.
:::
-To withdraw Jettons, the wallet sends messages with the `transfer` body to its corresponding Jetton wallet.
-The Jetton wallet then sends the Jettons to the recipient. In good faith, it is important to attach some TON
-as the `forward_ton_amount` (and optional comment to `forward_payload`) to trigger a `transfer notification`.
-See: [Jetton contracts message layouts](#jetton-contract-message-layouts)
+To withdraw Jettons, the wallet sends messages with the `transfer` body to its corresponding Jetton wallet.
+The Jetton wallet then sends the Jettons to the recipient. In good faith, it is important to attach some TON
+as the `forward_ton_amount` (and optional comment to `forward_payload`) to trigger a `transfer notification`.
+See: [Jetton contracts message layouts](#jetton-contract-message-layouts)
#### Preparation
@@ -438,22 +436,22 @@ See: [Jetton contracts message layouts](#jetton-contract-message-layouts)
6. Check the balance of the Jetton wallet to ensure there are enough funds present to carry out withdrawal.
7. Generate a [message](/develop/dapps/asset-processing/jettons#message-0).
8. When using a highload wallet, it is recommended that a batch of messages is collected and that one batch at a time is sent to optimize fees.
-9. Save the expiration time for outgoing external messages (this is the time until the wallet successfully
-processes the message, after this is completed, the wallet will no longer accept the message)
+9. Save the expiration time for outgoing external messages (this is the time until the wallet successfully
+ processes the message, after this is completed, the wallet will no longer accept the message)
10. Send a single message or more than one message (batch messaging).
-11. Retrieve the list of the latest unprocessed transactions within the hot wallet account and iterate it.
-Learn more here: [Checking contract's transactions](/develop/dapps/asset-processing/#checking-contracts-transactions),
-[Tonweb example](https://github.com/toncenter/examples/blob/9f20f7104411771793dfbbdf07f0ca4860f12de2/deposits-single-wallet.js#L43) or
-use the Toncenter API `/getTransactions` method.
+11. Retrieve the list of the latest unprocessed transactions within the hot wallet account and iterate it.
+ Learn more here: [Checking contract's transactions](/develop/dapps/asset-processing/#checking-contracts-transactions),
+ [Tonweb example](https://github.com/toncenter/examples/blob/9f20f7104411771793dfbbdf07f0ca4860f12de2/deposits-single-wallet.js#L43) or
+ use the Toncenter API `/getTransactions` method.
12. Look at outgoing messages in the account.
13. If a message exists with the `transfer` op code, then it should be decoded to retrieve the `query_id` value.
-Retrieved `query_id`s need to be marked as successfully sent.
-14. If the time it takes for the current scanned transaction to be processed is greater than
-the expiration time and the outgoing message with the given `query_id`
-is not found, then the request should (this is optional) be marked as expired and should be safely resent.
+ Retrieved `query_id`s need to be marked as successfully sent.
+14. If the time it takes for the current scanned transaction to be processed is greater than
+ the expiration time and the outgoing message with the given `query_id`
+ is not found, then the request should (this is optional) be marked as expired and should be safely resent.
15. Look for incoming messages in the account.
16. If a message that uses the `excesses` op code exists, the message should be decoded and the `query_id`
-value should be retrieved. A found `query_id` must be marked as successfully delivered.
+ value should be retrieved. A found `query_id` must be marked as successfully delivered.
17. Go to step 5. Expired requests that have not been successfully sent should be pushed back to the withdrawal list.
## Jetton on-chain processing
@@ -469,8 +467,8 @@ Below is a `list of recommendations` that must be considered when **carrying out
1. **Identify incoming jettons** using their wallet type, not their Jetton master contract. In other words, your contract should interact (receive and send messages) with a specific jetton wallet (not with some unknown wallet using a specific Jetton master contract).
2. When linking a Jetton Wallet and a Jetton Master, **check** that this **connection is bidirectional** where the wallet recognizes the master contract and vice versa. For instance, if your contract-system receives a notification from a jetton wallet (which considers its MySuperJetton as its master contract) its transfer information must be displayed to the user, prior to showing the `symbol`, `name` and `image`
-of the MySuperJetton contract, check that the MySuperJetton wallet uses the correct contract system. In turn, if your contract system for some reason needs to send jettons using the MySuperJetton or MySuperJetton master contracts verify that wallet X as is the wallet using the same contract parameters.
-Additionally, prior to sending a `transfer` request to X, make sure it recognizes MySuperJetton as its master.
+ of the MySuperJetton contract, check that the MySuperJetton wallet uses the correct contract system. In turn, if your contract system for some reason needs to send jettons using the MySuperJetton or MySuperJetton master contracts verify that wallet X as is the wallet using the same contract parameters.
+ Additionally, prior to sending a `transfer` request to X, make sure it recognizes MySuperJetton as its master.
3. The **true power** of decentralized finance (DeFi) is based on the ability to stack protocols on top of each other like lego blocks. For instance, say jetton A is swapped for jetton B, which in turn, is then used as leverage within a lending protocol (when a user supplies liquidity) which is then used to buy an NFT .... and so on. Therefore, consider how the contract is able to serve, not only off-chain users, but on-chain entities as well by attaching tokenized value to a transfer notification, adding a custom payload that can be sent with a transfer notification.
4. **Be aware** that not all contracts follow the same standards. Unfortunately, some jettons may be hostile (using attack-based vectors) and created for the sole purposes of attacking unsuspecting users. For security purposes, if the protocol in question consists of many contracts, do not create a large number of jetton wallets of the same type. In particular, do not send jettons inside the protocol between the deposit contract, vault contract, or user account contract etc. Attackers may intentionally interfere with contract logic by forging transfer notifications, jetton amounts, or payload parameters. Reduce the potential for attack potential by using only one wallet in the system per jetton (for all deposits and withdrawals).
5. It is also **often a good idea** to create subcontracts for each individualized jetton to reduce the chances of address spoofing (for example, when a transfer message is sent to jetton B using a contract intended for jetton A).
@@ -484,9 +482,9 @@ Generally, all verification procedures used for off-chain jetton processing are
1. When a wallet receives a transfer notification from an unknown jetton wallet, it is **vitally important** to trust the jetton wallet and its master address because it could be a malicious counterfeit. To protect yourself, check the Jetton Master (the master contract) using its provided address to ensure your verification processes recognize the jetton wallet as legitimate. After you trust the wallet and it is verified as legitimate, you can allow it to access your account balances and other in-wallet data. If the Jetton Master does not recognize this wallet it is recommended to not initiate or disclose your jetton transfers at all and to only show incoming TON transfers (of Toncoin attached to the transfer notifications) only.
2. In practice, if the user wants to interact with a Jetton and not a jetton wallet. In other words, users send wTON/oUSDT/jUSDT, jUSDC, jDAI instead of `EQAjN...`/`EQBLE...`
etc.. Often this means that when a user is initiating a jetton transfer, the wallet asks the corresponding jetton master which jetton wallet (owned by the user) should initiate the transfer request. It is **important to never blindly trust** this data from the Master (the master contract). Prior to sending the transfer request to a jetton wallet, always ensure that the jetton wallet indeed belongs to the Jetton Master it claims to belong to.
-3. **Be aware** that hostile Jetton Masters/jetton wallets **may change** their wallets/Masters over time. Therefore, it is imperative that users do their due diligence and check the legitimacy of any wallets they interact with prior to each use.
+3. **Be aware** that hostile Jetton Masters/jetton wallets **may change** their wallets/Masters over time. Therefore, it is imperative that users do their due diligence and check the legitimacy of any wallets they interact with prior to each use.
4. **Always ensure** that you display jettons in your interface in a manner that **will not mix with TON transfers**, system notifications, etc.. Even the `symbol`,`name` and `image`
-parameters can be crafted to mislead users, leaving those affected as potential fraud victims. There have been several instances, when malicious jettons were used to impersonate TON transfers, notification errors, reward earnings, or asset freezing announcements.
+ parameters can be crafted to mislead users, leaving those affected as potential fraud victims. There have been several instances, when malicious jettons were used to impersonate TON transfers, notification errors, reward earnings, or asset freezing announcements.
5. **Always be on the lookout for potential malicious actors** that create counterfeit jettons, it is always a good idea to give users the functionality needed to eliminate unwanted jettons in their main user interface.
@@ -614,57 +612,6 @@ await my_wallet.transfer_jetton_by_jetton_wallet(destination_address='address',
-
-
-
-
-
-
-Source code
-
-
-```py
-from pytoniq import LiteBalancer, WalletV4R2, begin_cell
-import asyncio
-
-mnemonics = ["your", "mnemonics", "here"]
-
-async def main():
- provider = LiteBalancer.from_mainnet_config(1)
- await provider.start_up()
-
- wallet = await WalletV4R2.from_mnemonic(provider=provider, mnemonics=mnemonics)
- USER_ADDRESS = wallet.address
- JETTON_MASTER_ADDRESS = "EQBlqsm144Dq6SjbPI4jjZvA1hqTIP3CvHovbIfW_t-SCALE"
- DESTINATION_ADDRESS = "EQAsl59qOy9C2XL5452lGbHU9bI3l4lhRaopeNZ82NRK8nlA"
-
- USER_JETTON_WALLET = (await provider.run_get_method(address=JETTON_MASTER_ADDRESS,
- method="get_wallet_address",
- stack=[begin_cell().store_address(USER_ADDRESS).end_cell().begin_parse()]))[0].load_address()
- forward_payload = (begin_cell()
- .store_uint(0, 32) # TextComment op-code
- .store_snake_string("Comment")
- .end_cell())
- transfer_cell = (begin_cell()
- .store_uint(0xf8a7ea5, 32) # Jetton Transfer op-code
- .store_uint(0, 64) # query_id
- .store_coins(1 * 10**9) # Jetton amount to transfer in nanojetton
- .store_address(DESTINATION_ADDRESS) # Destination address
- .store_address(USER_ADDRESS) # Response address
- .store_bit(0) # Custom payload is None
- .store_coins(1) # Ton forward amount in nanoton
- .store_bit(1) # Store forward_payload as a reference
- .store_ref(forward_payload) # Forward payload
- .end_cell())
-
- await wallet.transfer(destination=USER_JETTON_WALLET, amount=int(0.05*1e9), body=transfer_cell)
- await provider.close_all()
-
-asyncio.run(main())
-```
-
-
-
@@ -1063,121 +1010,6 @@ func GetTransferTransactions(orderId string, foundTransfer chan<- *tlb.Transacti
}
```
-
-
-
-
-
-
-
-Source code
-
-
-```py
-import asyncio
-
-from pytoniq import LiteBalancer, begin_cell
-
-MY_WALLET_ADDRESS = "EQAsl59qOy9C2XL5452lGbHU9bI3l4lhRaopeNZ82NRK8nlA"
-
-
-async def parse_transactions(provider: LiteBalancer, transactions):
- for transaction in transactions:
- if not transaction.in_msg.is_internal:
- continue
- if transaction.in_msg.info.dest.to_str(1, 1, 1) != MY_WALLET_ADDRESS:
- continue
-
- sender = transaction.in_msg.info.src.to_str(1, 1, 1)
- value = transaction.in_msg.info.value_coins
- if value != 0:
- value = value / 1e9
-
- if len(transaction.in_msg.body.bits) < 32:
- print(f"TON transfer from {sender} with value {value} TON")
- else:
- body_slice = transaction.in_msg.body.begin_parse()
- op_code = body_slice.load_uint(32)
-
- # TextComment
- if op_code == 0:
- print(f"TON transfer from {sender} with value {value} TON and comment: {body_slice.load_snake_string()}")
-
- # Jetton Transfer Notification
- elif op_code == 0x7362d09c:
- body_slice.load_bits(64) # skip query_id
- jetton_amount = body_slice.load_coins() / 1e9
- jetton_sender = body_slice.load_address().to_str(1, 1, 1)
- if body_slice.load_bit():
- forward_payload = body_slice.load_ref().begin_parse()
- else:
- forward_payload = body_slice
-
- jetton_master = (await provider.run_get_method(address=sender, method="get_wallet_data", stack=[]))[2].load_address()
- jetton_wallet = (await provider.run_get_method(address=jetton_master, method="get_wallet_address",
- stack=[
- begin_cell().store_address(MY_WALLET_ADDRESS).end_cell().begin_parse()
- ]))[0].load_address().to_str(1, 1, 1)
-
- if jetton_wallet != sender:
- print("FAKE Jetton Transfer")
- continue
-
- if len(forward_payload.bits) < 32:
- print(f"Jetton transfer from {jetton_sender} with value {jetton_amount} Jetton")
- else:
- forward_payload_op_code = forward_payload.load_uint(32)
- if forward_payload_op_code == 0:
- print(f"Jetton transfer from {jetton_sender} with value {jetton_amount} Jetton and comment: {forward_payload.load_snake_string()}")
- else:
- print(f"Jetton transfer from {jetton_sender} with value {jetton_amount} Jetton and unknown payload: {forward_payload} ")
-
- # NFT Transfer Notification
- elif op_code == 0x05138d91:
- body_slice.load_bits(64) # skip query_id
- prev_owner = body_slice.load_address().to_str(1, 1, 1)
- if body_slice.load_bit():
- forward_payload = body_slice.load_ref().begin_parse()
- else:
- forward_payload = body_slice
-
- stack = await provider.run_get_method(address=sender, method="get_nft_data", stack=[])
- index = stack[1]
- collection = stack[2].load_address()
- item_address = (await provider.run_get_method(address=collection, method="get_nft_address_by_index",
- stack=[index]))[0].load_address().to_str(1, 1, 1)
-
- if item_address != sender:
- print("FAKE NFT Transfer")
- continue
-
- if len(forward_payload.bits) < 32:
- print(f"NFT transfer from {prev_owner}")
- else:
- forward_payload_op_code = forward_payload.load_uint(32)
- if forward_payload_op_code == 0:
- print(f"NFT transfer from {prev_owner} with comment: {forward_payload.load_snake_string()}")
- else:
- print(f"NFT transfer from {prev_owner} with unknown payload: {forward_payload}")
-
- print(f"NFT Item: {item_address}")
- print(f"NFT Collection: {collection}")
- print(f"Transaction hash: {transaction.cell.hash.hex()}")
- print(f"Transaction lt: {transaction.lt}")
-
-
-async def main():
- provider = LiteBalancer.from_mainnet_config(1)
- await provider.start_up()
- transactions = await provider.get_transactions(address=MY_WALLET_ADDRESS, count=5)
- await parse_transactions(provider, transactions)
- await provider.close_all()
-
-
-if __name__ == "__main__":
- asyncio.run(main())
-```
-
@@ -1189,4 +1021,4 @@ You can find a list of SDKs for various languages (js, python, golang, C#, Rust,
* [Payments Processing](/develop/dapps/asset-processing/)
* [NFT processing on TON](/develop/dapps/asset-processing/nfts)
-* [Metadata parsing on TON](/develop/dapps/asset-processing/metadata)
+* [Metadata parsing on TON](/develop/dapps/asset-processing/metadata)
\ No newline at end of file
diff --git a/docs/develop/dapps/asset-processing/overview.md b/docs/develop/dapps/asset-processing/overview.md
new file mode 100644
index 0000000000..a1b299efe4
--- /dev/null
+++ b/docs/develop/dapps/asset-processing/overview.md
@@ -0,0 +1,58 @@
+import Button from '@site/src/components/button'
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+# Asset Processing Overview
+
+Here you can find a **short overview** on [how TON transfers work](/develop/dapps/asset-processing/overview#overview-on-messages-and-transactions), what [asset types](/develop/dapps/asset-processing/overview#digital-asset-types-on-ton) can you find in TON (and what about will you read [next](/develop/dapps/asset-processing/overview#read-next)) and how to [interact with ton](/develop/dapps/asset-processing/overview#interaction-with-ton-blockchain) using your programming language, it's recommended to understand all information, discovered below, before going to the next pages.
+
+## Overview on messages and transactions
+
+Embodying a fully asynchronous approach, TON Blockchain involves a few concepts which are uncommon to traditional blockchains. Particularly, each interaction of any actor with the blockchain consists of a graph of asynchronously transferred [messages](/develop/smart-contracts/guidelines/message-delivery-guarantees) between smart contracts and/or the external world. Each transaction consists of one incoming message and up to 512 outgoing messages.
+
+There are 3 types of messages, that are fully described [here](/develop/smart-contracts/messages#types-of-messages). To put it briefly:
+* [external message](/develop/smart-contracts/guidelines/external-messages):
+ * `external in message` (sometimes called just `external message`) is message that is sent from *outside* of the blockchain to a smart contract *inside* the blockchain.
+ * `external out message` (usually called `logs message`) is sent from a *blockchain entity* to the *outer world*.
+* [internal message](/develop/smart-contracts/guidelines/internal-messages) is sent from one *blockchain entity* to *another*, can carry some amount of digital assets and arbitrary portion of data.
+
+The common path of any interaction starts with an external message sent to a `wallet` smart contract, which authenticates the message sender using public-key cryptography, takes charge of fee payment, and sends internal blockchain messages. That messages queue form directional acyclic graph, or a tree.
+
+For example:
+
+
+
+* `Alice` use e.g [Tonkeeper](https://tonkeeper.com/) to send an `external message` to her wallet.
+* `external message` is the input message for `wallet A v4` contract with empty soure (a message from nowhere, such as [Tonkeeper](https://tonkeeper.com/)).
+* `outgoing message` is the output message for `wallet A v4` contract and input message for `wallet B v4` contract with `wallet A v4` source and `wallet B v4` destination.
+
+As a result there are 2 transactions with their set of input and output messages.
+
+Each action, when contract take message as input (triggered by it), process it and generate or not generate outgoing messages as output, called `transaction`. Read more about transactions [here](/develop/smart-contracts/guidelines/message-delivery-guarantees#what-is-a-transaction).
+
+That `transactions` can span a **prolonged period** of time. Technically, transactions with queues of messages are aggregated into blocks processed by validators. The asynchronous nature of the TON Blockchain **does not allow to predict the hash and lt (logical time) of a transaction** at the stage of sending a message.
+
+The `transaction` accepted to the block is final and cannot be modified.
+
+:::info Transaction Confirmation
+TON transactions are irreversible after just one confirmation. For the best user experience, it is suggested to avoid waiting on additional blocks once transactions are finalized on the TON Blockchain. Read more in the [Catchain.pdf](https://docs.ton.org/catchain.pdf#page=3).
+:::
+
+Smart contracts pay several types of [fees](/develop/smart-contracts/fees) for transactions (usually from the balance of an incoming message, behavior depends on [message mode](/develop/smart-contracts/messages#message-modes)). Amount of fees depends on workchain configs with maximal fees on `masterchain` and substantially lower fees on `basechain`.
+
+## Digital asset types on TON
+
+TON has three types of digital assets.
+- Toncoin, the main token of the network. It is used for all basic operations on the blockchain, for example, paying gas fees or staking for validation.
+- Contract assets, such as tokens and NFTs, which are analogous to the ERC-20/ERC-721 standards and are managed by arbitrary contracts and thus can require custom rules for processing. You can find more info on it's processing in [process NFTs](/develop/dapps/asset-processing/nfts) and [process Jettons](/develop/dapps/asset-processing/jettons) articles.
+- Native token, which is special kind of assets that can be attached to any message on the network. But these asset is currently not in use since the functionality for issuing new native tokens is closed.
+
+## Interaction with TON blockchain
+Basic operations on TON Blockchain can be carried out via TonLib. It is a shared library which can be compiled along with a TON node and expose APIs for interaction with the blockchain via so-called lite servers (servers for lite clients). TonLib follows a trustless approach by checking proofs for all incoming data; thus, there is no necessity for a trusted data provider. Methods available to TonLib are listed [in the TL scheme](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L234). They can be used either as a shared library via [wrappers](/develop/dapps/asset-processing/#repositories).
+
+## Read next
+
+After reading this article you can check:
+1. [Payments processing](/develop/dapps/asset-processing/) to get how to work with `TON coins`
+2. [Jetton processing](/develop/dapps/asset-processing/jettons) to get how to work with `jettons` (sometime called `tokens`)
+3. [NFT processing](/develop/dapps/asset-processing/nfts) to get how to work with `NFT` (that is the special type of `jetton`)
diff --git a/sidebars.js b/sidebars.js
index 014168a84d..6de13910c3 100644
--- a/sidebars.js
+++ b/sidebars.js
@@ -353,10 +353,10 @@ const sidebars = {
},
{
type: 'category',
- label: 'Advanced Asset Holding',
+ label: 'Advanced Asset Processing',
items: [
+ 'develop/dapps/asset-processing/overview',
'develop/dapps/asset-processing/README',
- 'develop/dapps/asset-processing/address-verification',
'develop/dapps/asset-processing/jettons',
'develop/dapps/asset-processing/nfts',
'develop/dapps/asset-processing/metadata',
From 3b6abcde8e1da907cab86750d4416fa4f652eb16 Mon Sep 17 00:00:00 2001
From: Full-Hat
Date: Mon, 29 Apr 2024 20:04:13 +0300
Subject: [PATCH 11/15] Add updates from original repo, discarded in merge
process
---
docs/develop/dapps/asset-processing/README.md | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/docs/develop/dapps/asset-processing/README.md b/docs/develop/dapps/asset-processing/README.md
index 29913e78f8..42e15a8a14 100644
--- a/docs/develop/dapps/asset-processing/README.md
+++ b/docs/develop/dapps/asset-processing/README.md
@@ -106,8 +106,14 @@ A contract's transactions can be obtained using [getTransactions](https://toncen
3. Process transactions with not empty source in incoming message and destination equals to account address.
4. The next 10 transactions should be loaded and steps 2,3,4,5 should be repeated until you processed all incoming transactions.
-### Checking transaction's flow
-It's possible to track messages flow during transaction processing. Since the message flow is a DAG it's enough to get the input `in_msg` or output `out_msgs` messages of the current transaction using [getTransactions](https://toncenter.com/api/v2/#/transactions/get_transactions_getTransactions_get) method to find incoming transaction with [tryLocateResultTx](https://toncenter.com/api/v2/#/transactions/get_try_locate_result_tx_tryLocateResultTx_get) or outgoing transactions with [tryLocateSourceTx](https://toncenter.com/api/v2/#/transactions/get_try_locate_source_tx_tryLocateSourceTx_get).
+### Check contract's transactions
+
+A contract's transactions can be obtained using [getTransactions](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L236). This method allows to get 10 transactions from some `transactionId` and earlier. To process all incoming transactions, the following steps should be followed:
+1. The latest `last_transaction_id` can be obtained using [getAccountState](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L235)
+2. List of 10 transactions should be loaded via the `getTransactions` method.
+3. Unseen transactions from this list should be processed.
+4. Incoming payments are transactions in which the incoming message has a source address; outgoing payments are transactions in which the incoming message has no source address and also presents the outgoing messages. These transactions should be processed accordingly.
+5. If all of those 10 transactions are unseen, the next 10 transactions should be loaded and steps 2,3,4,5 should be repeated.
### Send payments
@@ -120,7 +126,7 @@ It's possible to track messages flow during transaction processing. Since the me
7. Service should regularly poll the [getTransactions](https://toncenter.com/api/v2/#/transactions/get_transactions_getTransactions_get) method for the `wallet` contract. Matching confirmed transactions with the outgoing payments by (`destination_address`, `value`, `comment`) allows to mark payments as finished; detect and show the user the corresponding transaction hash and lt (logical time).
8. Requests to `v3` of `high-load` wallets have an expiration time equal to 60 seconds by default. After that time unprocessed requests can be safely resent to the network (see steps 3-6).
-## Invoice-based payment accept
+## Invoice-based approach
To accept payments based on attached comments, the service should
1. Deploy the `wallet` contract.
2. Generate a unique `invoice` for each user. String representation of uuid32 will be enough.
From cfc1d51c18e569ef005376d3a1bbdf43370177b5 Mon Sep 17 00:00:00 2001
From: Full-Hat <68519677+Full-Hat@users.noreply.github.com>
Date: Tue, 30 Apr 2024 10:42:25 +0300
Subject: [PATCH 12/15] Update jettons.md
---
docs/develop/dapps/asset-processing/jettons.md | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/docs/develop/dapps/asset-processing/jettons.md b/docs/develop/dapps/asset-processing/jettons.md
index b1982dadf7..913a1e39b9 100644
--- a/docs/develop/dapps/asset-processing/jettons.md
+++ b/docs/develop/dapps/asset-processing/jettons.md
@@ -8,12 +8,16 @@ import Button from '@site/src/components/button';
For clear understanding, the reader should be familiar with the basic principles of asset processing described in [payments processing section](/develop/dapps/asset-processing/) of our documentation.
:::
-TON Blockchain and its underlying ecosystem classifies fungible tokens (FTs) as jettons. Because sharding is applied on TON Blockchain, our implementation of fungible tokens is unique when compared to similar blockchain models.
+Jettons are tokens on TON Blockchain - one can consider them similarly to ERC-20 tokens on Ethereum.
In this analysis, we take a deeper dive into the formal standards detailing jetton [behavior](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md) and [metadata](https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md).
A less formal sharding-focused overview of jetton architecture can be found in our
[anatomy of jettons blog post](https://blog.ton.org/how-to-shard-your-ton-smart-contract-and-why-studying-the-anatomy-of-tons-jettons).
+Also, you should keep in mind that there are two approaches to working with jetton withdraws:
+- [Memo Deposits](https://github.com/toncenter/examples/blob/main/deposits-jettons.js) - This allows you to keep one deposit wallet, and users add a memo in order to be identified by your system. This means that you don’t need to scan the entire blockchain, but is slightly less easy for users.
+- [Memo-less deposits](https://github.com/gobicycle/bicycle) - This solution also exists, but is more difficult to integrate. However, we can assist with this, if you would prefer to take this route. Please notify us before deciding to implement this approach.
+
## Jetton Architecture
Standardized tokens on TON are implemented using a set of smart contracts, including:
@@ -1021,4 +1025,4 @@ You can find a list of SDKs for various languages (js, python, golang, C#, Rust,
* [Payments Processing](/develop/dapps/asset-processing/)
* [NFT processing on TON](/develop/dapps/asset-processing/nfts)
-* [Metadata parsing on TON](/develop/dapps/asset-processing/metadata)
\ No newline at end of file
+* [Metadata parsing on TON](/develop/dapps/asset-processing/metadata)
From 52a44e9d4d5f79c3ef497df7ffc1e35dfd268e99 Mon Sep 17 00:00:00 2001
From: Andrey Semenov
Date: Tue, 30 Apr 2024 15:02:45 +0200
Subject: [PATCH 13/15] fix(pytoniq): Broken `details` and `TabItem` tags
---
docs/develop/dapps/asset-processing/README.md | 120 +++++++++++++++++-
1 file changed, 119 insertions(+), 1 deletion(-)
diff --git a/docs/develop/dapps/asset-processing/README.md b/docs/develop/dapps/asset-processing/README.md
index 42e15a8a14..9408ffc992 100644
--- a/docs/develop/dapps/asset-processing/README.md
+++ b/docs/develop/dapps/asset-processing/README.md
@@ -1,3 +1,4 @@
+
import Button from '@site/src/components/button'
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
@@ -221,6 +222,28 @@ To generate a transaction link in the explorer, the service needs to get the lt
- **psylopunk/pythonlib:**
- [Wallet creation + get wallet address](https://github.com/psylopunk/pytonlib/blob/main/examples/generate_wallet.py)
+
+- **yungwine/pytoniq:**
+```py
+import asyncio
+
+from pytoniq.contract.wallets.wallet import WalletV4R2
+from pytoniq.liteclient.balancer import LiteBalancer
+
+
+async def main():
+ provider = LiteBalancer.from_mainnet_config(2)
+ await provider.start_up()
+
+ mnemonics, wallet = await WalletV4R2.create(provider)
+ print(f"{wallet.address=} and {mnemonics=}")
+
+ await provider.close_all()
+
+
+if __name__ == "__main__":
+ asyncio.run(main())
+```
@@ -331,6 +354,69 @@ func main() {
+
+
+- **yungwine/pytoniq:**
+
+Checking deposits
+
+```python
+import asyncio
+
+from pytoniq_core import Transaction
+
+from pytoniq import LiteClient, Address
+
+MY_ADDRESS = Address("kf8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM_BP")
+
+
+async def main():
+ client = LiteClient.from_mainnet_config(ls_i=0, trust_level=2)
+
+ await client.connect()
+
+ last_block = await client.get_trusted_last_mc_block()
+
+ _account, shard_account = await client.raw_get_account_state(MY_ADDRESS, last_block)
+ assert shard_account
+
+ last_trans_lt, last_trans_hash = (
+ shard_account.last_trans_lt,
+ shard_account.last_trans_hash,
+ )
+
+ while True:
+ print(f"Waiting for{last_block=}")
+
+ transactions = await client.get_transactions(
+ MY_ADDRESS, 1024, last_trans_lt, last_trans_hash
+ )
+ toncoin_deposits = [tx for tx in transactions if filter_toncoin_deposit(tx)]
+ print(f"Got {len(transactions)=} with {len(toncoin_deposits)=}")
+
+ for deposit_tx in toncoin_deposits:
+ # Process toncoin deposit transaction
+ print(deposit_tx.cell.hash.hex())
+
+ last_trans_lt = transactions[0].lt
+ last_trans_hash = transactions[0].cell.hash
+
+
+def filter_toncoin_deposit(tx: Transaction):
+ if tx.out_msgs:
+ return False
+
+ if tx.in_msg:
+ return False
+
+ return True
+
+
+if __name__ == "__main__":
+ asyncio.run(main())
+```
+
+
### Toncoin Withdrawals (Send toncoins)
@@ -359,6 +445,35 @@ func main() {
- **psylopunk/pythonlib:**
- [Withdraw Toncoins from a wallet](https://github.com/psylopunk/pytonlib/blob/main/examples/transactions.py)
+- **yungwine/pytoniq:**
+
+```python
+import asyncio
+
+from pytoniq_core import Address
+from pytoniq.contract.wallets.wallet import WalletV4R2
+from pytoniq.liteclient.balancer import LiteBalancer
+
+
+MY_MNEMONICS = "one two tree ..."
+DESTINATION_WALLET = Address("Destination wallet address")
+
+
+async def main():
+ provider = LiteBalancer.from_mainnet_config()
+ await provider.start_up()
+
+ wallet = await WalletV4R2.from_mnemonic(provider, MY_MNEMONICS)
+
+ await wallet.transfer(DESTINATION_WALLET, 5)
+
+ await provider.close_all()
+
+
+if __name__ == "__main__":
+ asyncio.run(main())
+```
+
@@ -384,6 +499,9 @@ func main() {
- **psylopunk/pythonlib:**
- [Get transactions](https://github.com/psylopunk/pytonlib/blob/main/examples/transactions.py)
+
+- **yungwine/pytoniq:**
+ - [Get transactions](https://github.com/yungwine/pytoniq/blob/master/examples/transactions.py)
@@ -391,4 +509,4 @@ func main() {
## SDKs
-You can find a list of SDKs for various languages (JS, Python, Golang, C#, Rust, etc.) list [here](/develop/dapps/apis/sdk).
\ No newline at end of file
+You can find a list of SDKs for various languages (JS, Python, Golang, C#, Rust, etc.) list [here](/develop/dapps/apis/sdk).
From 1f687140f505d9def5492e5ddf5458e31eac1ef3 Mon Sep 17 00:00:00 2001
From: Andrey Semenov
Date: Tue, 30 Apr 2024 15:04:08 +0200
Subject: [PATCH 14/15] refactor(pytoniq): Reformat & clean accept jetton
transfer example
---
.../develop/dapps/asset-processing/jettons.md | 155 ++++++++++++++++++
1 file changed, 155 insertions(+)
diff --git a/docs/develop/dapps/asset-processing/jettons.md b/docs/develop/dapps/asset-processing/jettons.md
index 913a1e39b9..4a26edfb7c 100644
--- a/docs/develop/dapps/asset-processing/jettons.md
+++ b/docs/develop/dapps/asset-processing/jettons.md
@@ -616,6 +616,57 @@ await my_wallet.transfer_jetton_by_jetton_wallet(destination_address='address',
+
+
+
+
+
+
+Source code
+
+
+```py
+from pytoniq import LiteBalancer, WalletV4R2, begin_cell
+import asyncio
+
+mnemonics = ["your", "mnemonics", "here"]
+
+async def main():
+ provider = LiteBalancer.from_mainnet_config(1)
+ await provider.start_up()
+
+ wallet = await WalletV4R2.from_mnemonic(provider=provider, mnemonics=mnemonics)
+ USER_ADDRESS = wallet.address
+ JETTON_MASTER_ADDRESS = "EQBlqsm144Dq6SjbPI4jjZvA1hqTIP3CvHovbIfW_t-SCALE"
+ DESTINATION_ADDRESS = "EQAsl59qOy9C2XL5452lGbHU9bI3l4lhRaopeNZ82NRK8nlA"
+
+ USER_JETTON_WALLET = (await provider.run_get_method(address=JETTON_MASTER_ADDRESS,
+ method="get_wallet_address",
+ stack=[begin_cell().store_address(USER_ADDRESS).end_cell().begin_parse()]))[0].load_address()
+ forward_payload = (begin_cell()
+ .store_uint(0, 32) # TextComment op-code
+ .store_snake_string("Comment")
+ .end_cell())
+ transfer_cell = (begin_cell()
+ .store_uint(0xf8a7ea5, 32) # Jetton Transfer op-code
+ .store_uint(0, 64) # query_id
+ .store_coins(1 * 10**9) # Jetton amount to transfer in nanojetton
+ .store_address(DESTINATION_ADDRESS) # Destination address
+ .store_address(USER_ADDRESS) # Response address
+ .store_bit(0) # Custom payload is None
+ .store_coins(1) # Ton forward amount in nanoton
+ .store_bit(1) # Store forward_payload as a reference
+ .store_ref(forward_payload) # Forward payload
+ .end_cell())
+
+ await wallet.transfer(destination=USER_JETTON_WALLET, amount=int(0.05*1e9), body=transfer_cell)
+ await provider.close_all()
+
+asyncio.run(main())
+```
+
+
+
@@ -1014,6 +1065,110 @@ func GetTransferTransactions(orderId string, foundTransfer chan<- *tlb.Transacti
}
```
+
+
+
+
+
+
+
+Source code
+
+
+```py
+import asyncio
+
+from pytoniq import LiteBalancer, begin_cell
+
+MY_WALLET_ADDRESS = "EQAsl59qOy9C2XL5452lGbHU9bI3l4lhRaopeNZ82NRK8nlA"
+
+
+async def parse_transactions(provider: LiteBalancer, transactions):
+ for transaction in transactions:
+ if not transaction.in_msg.is_internal:
+ continue
+ if transaction.in_msg.info.dest.to_str(1, 1, 1) != MY_WALLET_ADDRESS:
+ continue
+
+ sender = transaction.in_msg.info.src.to_str(1, 1, 1)
+ value = transaction.in_msg.info.value_coins
+ if value != 0:
+ value = value / 1e9
+
+ if len(transaction.in_msg.body.bits) < 32:
+ print(f"TON transfer from {sender} with value {value} TON")
+ continue
+
+ body_slice = transaction.in_msg.body.begin_parse()
+ op_code = body_slice.load_uint(32)
+ if op_code != 0x7362D09C:
+ continue
+
+ body_slice.load_bits(64) # skip query_id
+ jetton_amount = body_slice.load_coins() / 1e9
+ jetton_sender = body_slice.load_address().to_str(1, 1, 1)
+ if body_slice.load_bit():
+ forward_payload = body_slice.load_ref().begin_parse()
+ else:
+ forward_payload = body_slice
+
+ jetton_master = (
+ await provider.run_get_method(
+ address=sender, method="get_wallet_data", stack=[]
+ )
+ )[2].load_address()
+ jetton_wallet = (
+ (
+ await provider.run_get_method(
+ address=jetton_master,
+ method="get_wallet_address",
+ stack=[
+ begin_cell()
+ .store_address(MY_WALLET_ADDRESS)
+ .end_cell()
+ .begin_parse()
+ ],
+ )
+ )[0]
+ .load_address()
+ .to_str(1, 1, 1)
+ )
+
+ if jetton_wallet != sender:
+ print("FAKE Jetton Transfer")
+ continue
+
+ if len(forward_payload.bits) < 32:
+ print(
+ f"Jetton transfer from {jetton_sender} with value {jetton_amount} Jetton"
+ )
+ else:
+ forward_payload_op_code = forward_payload.load_uint(32)
+ if forward_payload_op_code == 0:
+ print(
+ f"Jetton transfer from {jetton_sender} with value {jetton_amount} Jetton and comment: {forward_payload.load_snake_string()}"
+ )
+ else:
+ print(
+ f"Jetton transfer from {jetton_sender} with value {jetton_amount} Jetton and unknown payload: {forward_payload} "
+ )
+
+ print(f"Transaction hash: {transaction.cell.hash.hex()}")
+ print(f"Transaction lt: {transaction.lt}")
+
+
+async def main():
+ provider = LiteBalancer.from_mainnet_config(1)
+ await provider.start_up()
+ transactions = await provider.get_transactions(address=MY_WALLET_ADDRESS, count=5)
+ await parse_transactions(provider, transactions)
+ await provider.close_all()
+
+
+if __name__ == "__main__":
+ asyncio.run(main())
+```
+
From 6840b7f7be35d4bc828961a81abf2ead12b90c1c Mon Sep 17 00:00:00 2001
From: Aliaksandr Bahdanau
Date: Thu, 2 May 2024 12:06:38 +0300
Subject: [PATCH 15/15] add transaction traverse
---
docs/develop/dapps/asset-processing/README.md | 65 +++++++++++++++++--
1 file changed, 58 insertions(+), 7 deletions(-)
diff --git a/docs/develop/dapps/asset-processing/README.md b/docs/develop/dapps/asset-processing/README.md
index 9408ffc992..2638a33270 100644
--- a/docs/develop/dapps/asset-processing/README.md
+++ b/docs/develop/dapps/asset-processing/README.md
@@ -107,14 +107,65 @@ A contract's transactions can be obtained using [getTransactions](https://toncen
3. Process transactions with not empty source in incoming message and destination equals to account address.
4. The next 10 transactions should be loaded and steps 2,3,4,5 should be repeated until you processed all incoming transactions.
-### Check contract's transactions
+### Get incoming/outgoing transactions
+It's possible to track messages flow during transaction processing. Since the message flow is a DAG it's enough to get current transaction using [getTransactions](https://toncenter.com/api/v2/#/transactions/get_transactions_getTransactions_get) method and find incoming transaction by `out_msg` with [tryLocateResultTx](https://testnet.toncenter.com/api/v2/#/transactions/get_try_locate_result_tx_tryLocateResultTx_get) or outgoing transactions by `in_msg` with [tryLocateSourceTx](https://testnet.toncenter.com/api/v2/#/transactions/get_try_locate_source_tx_tryLocateSourceTx_get).
-A contract's transactions can be obtained using [getTransactions](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L236). This method allows to get 10 transactions from some `transactionId` and earlier. To process all incoming transactions, the following steps should be followed:
-1. The latest `last_transaction_id` can be obtained using [getAccountState](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L235)
-2. List of 10 transactions should be loaded via the `getTransactions` method.
-3. Unseen transactions from this list should be processed.
-4. Incoming payments are transactions in which the incoming message has a source address; outgoing payments are transactions in which the incoming message has no source address and also presents the outgoing messages. These transactions should be processed accordingly.
-5. If all of those 10 transactions are unseen, the next 10 transactions should be loaded and steps 2,3,4,5 should be repeated.
+
+
+
+```ts
+import { TonClient, Transaction } from '@ton/ton';
+import { getHttpEndpoint } from '@orbs-network/ton-access';
+import { CommonMessageInfoInternal } from '@ton/core';
+
+async function findIncomingTransaction(client: TonClient, transaction: Transaction): Promise {
+ const inMessage = transaction.inMessage?.info;
+ if (inMessage?.type !== 'internal') return null;
+ return client.tryLocateSourceTx(inMessage.src, inMessage.dest, inMessage.createdLt.toString());
+}
+
+async function findOutgoingTransactions(client: TonClient, transaction: Transaction): Promise {
+ const outMessagesInfos = transaction.outMessages.values()
+ .map(message => message.info)
+ .filter((info): info is CommonMessageInfoInternal => info.type === 'internal');
+
+ return Promise.all(
+ outMessagesInfos.map((info) => client.tryLocateResultTx(info.src, info.dest, info.createdLt.toString())),
+ );
+}
+
+async function traverseIncomingTransactions(client: TonClient, transaction: Transaction): Promise {
+ const inTx = await findIncomingTransaction(client, transaction);
+ // now you can traverse this transaction graph backwards
+ if (!inTx) return;
+ await traverseIncomingTransactions(client, inTx);
+}
+
+async function traverseOutgoingTransactions(client: TonClient, transaction: Transaction): Promise {
+ const outTxs = await findOutgoingTransactions(client, transaction);
+ // do smth with out txs
+ for (const out of outTxs) {
+ await traverseOutgoingTransactions(client, out);
+ }
+}
+
+async function main() {
+ const endpoint = await getHttpEndpoint({ network: 'testnet' });
+ const client = new TonClient({
+ endpoint,
+ apiKey: '[API-KEY]',
+ });
+
+ const transaction: Transaction = ...; // Obtain first transaction to start traversing
+ await traverseIncomingTransactions(client, transaction);
+ await traverseOutgoingTransactions(client, transaction);
+}
+
+main();
+```
+
+
+
### Send payments