From 59dd95847289ac909ee06c8366bb239713a39bc8 Mon Sep 17 00:00:00 2001 From: Gusarich Date: Sat, 18 Nov 2023 18:11:52 +0300 Subject: [PATCH 1/8] add example to cookbook --- docs/develop/dapps/cookbook.md | 52 +++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/docs/develop/dapps/cookbook.md b/docs/develop/dapps/cookbook.md index dc74967857..56bbfe5a05 100644 --- a/docs/develop/dapps/cookbook.md +++ b/docs/develop/dapps/cookbook.md @@ -785,4 +785,54 @@ console.log(text); -This example will help you understand how you can work with such cells using recursion. \ No newline at end of file +This example will help you understand how you can work with such cells using recursion. + +### How to parse transactions of an account? + +The list of transactions on an account can be fetched through `getTransactions` API method. It returns an array of `Transaction` objects, with each item having lots of attributes. However, the fields that are the most commonly used are: + - Sender, Body and Value of the message that initiated this transaction + - Transaction's hash and logical time (LT) + +Below is an example on how you can fetch 5 most recent transactions on any blockchain account and print out these fields in a loop. + + + + +```js +import { Address, TonClient, fromNano } from '@ton/ton'; + +async function main() { + const client = new TonClient({ endpoint: 'https://toncenter.com/api/v2/jsonRPC' }); + + const transactions = await client.getTransactions( + Address.parse('EQBKgXCNLPexWhs2L79kiARR1phGH1LwXxRbNsCFF9doc2lN'), // address that you want to fetch transactions from + { + limit: 5, + } + ); + + transactions.forEach((tx) => { + const inMsg = tx.inMessage; + + console.log(`inMsg Body: ${inMsg?.body}`); + if (inMsg?.info.type == 'internal') { + // we only process internal messages here because they are used the most + // for external messages some of the fields are empty, but the main structure is similar + console.log(`inMsg Sender: ${inMsg?.info.src}`); + console.log(`inMsg Value: ${fromNano(inMsg?.info.value.coins)}`); + } + console.log(`Hash: ${tx.hash().toString('hex')}`); + console.log(`LT: ${tx.lt}`); + console.log(); + }); +} + +main().finally(() => console.log('Exiting...')); +``` + + + + +Note that this example covers only the simplest case, where it is enough to fetch the transactions on a single account. If you want to go deeper and handle more complex chains of transactions and messages, you should take `tx.outMessages` field into an account. It contains the list of the output messages sent by smart-contract in the result of this transaction. To understand the whole logic better, you can read these articles: + * [Internal messages](/develop/smart-contracts/guidelines/internal-messages) + * [Message Delivery Guarantees](/develop/smart-contracts/guidelines/message-delivery-guarantees) \ No newline at end of file From 45b07dc93f618ec583f1cd89cc67377f163a8e40 Mon Sep 17 00:00:00 2001 From: Gusarich Date: Sat, 25 Nov 2023 10:33:02 +0300 Subject: [PATCH 2/8] update cookbook example --- docs/develop/dapps/cookbook.md | 142 ++++++++++++++++++++++++++++++--- 1 file changed, 130 insertions(+), 12 deletions(-) diff --git a/docs/develop/dapps/cookbook.md b/docs/develop/dapps/cookbook.md index 56bbfe5a05..8454957167 100644 --- a/docs/develop/dapps/cookbook.md +++ b/docs/develop/dapps/cookbook.md @@ -787,22 +787,27 @@ console.log(text); This example will help you understand how you can work with such cells using recursion. -### How to parse transactions of an account? +### How to parse transactions of an account (Transfers, Jettons, NFTs)? The list of transactions on an account can be fetched through `getTransactions` API method. It returns an array of `Transaction` objects, with each item having lots of attributes. However, the fields that are the most commonly used are: - Sender, Body and Value of the message that initiated this transaction - Transaction's hash and logical time (LT) -Below is an example on how you can fetch 5 most recent transactions on any blockchain account and print out these fields in a loop. +_Sender_ and _Body_ fields may be used to determine the type of message (regular transfer, jetton transfer, nft transfer etc). + +Below is an example on how you can fetch 5 most recent transactions on any blockchain account, parse them depending on the type and print out in a loop. ```js -import { Address, TonClient, fromNano } from '@ton/ton'; +import { Address, TonClient, beginCell, fromNano } from '@ton/ton'; async function main() { - const client = new TonClient({ endpoint: 'https://toncenter.com/api/v2/jsonRPC' }); + const client = new TonClient({ + endpoint: 'https://toncenter.com/api/v2/jsonRPC', + apiKey: '1b312c91c3b691255130350a49ac5a0742454725f910756aff94dfe44858388e', + }); const transactions = await client.getTransactions( Address.parse('EQBKgXCNLPexWhs2L79kiARR1phGH1LwXxRbNsCFF9doc2lN'), // address that you want to fetch transactions from @@ -811,20 +816,133 @@ async function main() { } ); - transactions.forEach((tx) => { + for (const tx of transactions) { const inMsg = tx.inMessage; - console.log(`inMsg Body: ${inMsg?.body}`); if (inMsg?.info.type == 'internal') { // we only process internal messages here because they are used the most // for external messages some of the fields are empty, but the main structure is similar - console.log(`inMsg Sender: ${inMsg?.info.src}`); - console.log(`inMsg Value: ${fromNano(inMsg?.info.value.coins)}`); + const sender = inMsg?.info.src; + const value = inMsg?.info.value.coins; + + const originalBody = inMsg?.body.beginParse(); + let body = originalBody.clone(); + if (body.remainingBits < 32) { + // if body doesn't have opcode: it's a simple message without comment + console.log(`Simple transfer from ${sender} with value ${fromNano(value)} TON`); + } else { + const op = body.loadUint(32); + if (op == 0) { + // if opcode is 0: it's a simple message with comment + const comment = body.loadStringTail(); + console.log( + `Simple transfer from ${sender} with value ${fromNano(value)} TON and comment: "${comment}"` + ); + } else if (op == 0x7362d09c) { + // if opcode is 0x7362d09c: it's a Jetton transfer notification + + body.skip(64); // skip query_id + const jettonAmount = body.loadCoins(); + const jettonSender = body.loadAddress(); + const originalForwardPayload = body.loadBit() ? body.loadRef().beginParse() : body; + let forwardPayload = originalForwardPayload.clone(); + + // IMPORTANT: we have to verify the source of this message because it can be faked + const runStack = (await client.runMethod(sender, 'get_wallet_data')).stack; + runStack.skip(2); + const jettonMaster = runStack.readAddress(); + const jettonSenderJettonWallet = ( + await client.runMethod(jettonMaster, 'get_wallet_address', [ + { type: 'slice', cell: beginCell().storeAddress(jettonSender).endCell() }, + ]) + ).stack.readAddress(); + if (!jettonSenderJettonWallet.equals(sender)) { + console.log(`FAKE Jetton transfer`); + continue; + } + + if (forwardPayload.remainingBits < 32) { + // if forward payload doesn't have opcode: it's a simple Jetton transfer + console.log(`Jetton transfer from ${jettonSender} with value ${fromNano(jettonAmount)} Jetton`); + } else { + const forwardOp = forwardPayload.loadUint(32); + if (forwardOp == 0) { + // if forward payload opcode is 0: it's a simple Jetton transfer with comment + const comment = forwardPayload.loadStringTail(); + console.log( + `Jetton transfer from ${jettonSender} with value ${fromNano( + jettonAmount + )} Jetton and comment: "${comment}"` + ); + } else { + // if forward payload opcode is something else: it's some message with arbitrary structure + // you may parse it manually if you know other opcodes or just print it as hex + console.log( + `Jetton transfer with unknown payload structure from ${jettonSender} with value ${fromNano( + jettonAmount + )} Jetton and payload: ${originalForwardPayload}` + ); + } + + console.log(`Jetton Master: ${jettonMaster}`); + } + } else if (op == 0x05138d91) { + // if opcode is 0x05138d91: it's a NFT transfer notification + + body.skip(64); // skip query_id + const prevOwner = body.loadAddress(); + const originalForwardPayload = body.loadBit() ? body.loadRef().beginParse() : body; + let forwardPayload = originalForwardPayload.clone(); + + // IMPORTANT: we have to verify the source of this message because it can be faked + const runStack = (await client.runMethod(sender, 'get_nft_data')).stack; + runStack.skip(1); + const index = runStack.readBigNumber(); + const collection = runStack.readAddress(); + const itemAddress = ( + await client.runMethod(collection, 'get_nft_address_by_index', [{ type: 'int', value: index }]) + ).stack.readAddress(); + + if (!itemAddress.equals(sender)) { + console.log(`FAKE NFT Transfer`); + continue; + } + + if (forwardPayload.remainingBits < 32) { + // if forward payload doesn't have opcode: it's a simple NFT transfer + console.log(`NFT transfer from ${prevOwner}`); + } else { + const forwardOp = forwardPayload.loadUint(32); + if (forwardOp == 0) { + // if forward payload opcode is 0: it's a simple NFT transfer with comment + const comment = forwardPayload.loadStringTail(); + console.log(`NFT transfer from ${prevOwner} with comment: "${comment}"`); + } else { + // if forward payload opcode is something else: it's some message with arbitrary structure + // you may parse it manually if you know other opcodes or just print it as hex + console.log( + `NFT transfer with unknown payload structure from ${prevOwner} and payload: ${originalForwardPayload}` + ); + } + } + + console.log(`NFT Item: ${itemAddress}`); + console.log(`NFT Collection: ${collection}`); + } else { + // if opcode is something else: it's some message with arbitrary structure + // you may parse it manually if you know other opcodes or just print it as hex + console.log( + `Message with unknown structure from ${sender} with value ${fromNano( + value + )} TON and body: ${originalBody}` + ); + } + } } - console.log(`Hash: ${tx.hash().toString('hex')}`); - console.log(`LT: ${tx.lt}`); + console.log(`Transaction Hash: ${tx.hash().toString('hex')}`); + console.log(`Transaction LT: ${tx.lt}`); console.log(); - }); + } } main().finally(() => console.log('Exiting...')); @@ -833,6 +951,6 @@ main().finally(() => console.log('Exiting...')); -Note that this example covers only the simplest case, where it is enough to fetch the transactions on a single account. If you want to go deeper and handle more complex chains of transactions and messages, you should take `tx.outMessages` field into an account. It contains the list of the output messages sent by smart-contract in the result of this transaction. To understand the whole logic better, you can read these articles: +Note that this example covers only the simplest case with incoming messages, where it is enough to fetch the transactions on a single account. If you want to go deeper and handle more complex chains of transactions and messages, you should take `tx.outMessages` field into an account. It contains the list of the output messages sent by smart-contract in the result of this transaction. To understand the whole logic better, you can read these articles: * [Internal messages](/develop/smart-contracts/guidelines/internal-messages) * [Message Delivery Guarantees](/develop/smart-contracts/guidelines/message-delivery-guarantees) \ No newline at end of file From 10f2448fc2d34f6443cc545502556011b7f7a179 Mon Sep 17 00:00:00 2001 From: Mehdi Baneshi Date: Sat, 2 Dec 2023 22:46:39 +0330 Subject: [PATCH 3/8] fix broken link.md fix broken link --- 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 e2385a014f..248bc8ae3a 100644 --- a/docs/develop/dapps/asset-processing/README.md +++ b/docs/develop/dapps/asset-processing/README.md @@ -63,7 +63,7 @@ Smart contracts **pay fees for transactions** (usually from the balance of an in 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/nfts) and [process Jettons](/develop/dapps/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 processin in [process NFTs](/develop/dapps/asset-processing/nfts) and [process Jettons](/develop/dapps/jettons) articles. ### 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. From 82398d44669ebc8817967b377219dbc52f240464 Mon Sep 17 00:00:00 2001 From: Mehdi Baneshi Date: Sat, 2 Dec 2023 22:49:06 +0330 Subject: [PATCH 4/8] Update README.md --- 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 e2385a014f..f28929fa6e 100644 --- a/docs/develop/dapps/asset-processing/README.md +++ b/docs/develop/dapps/asset-processing/README.md @@ -63,7 +63,7 @@ Smart contracts **pay fees for transactions** (usually from the balance of an in 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/nfts) and [process Jettons](/develop/dapps/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 processin in [process NFTs](/develop/dapps/nfts) and [process Jettons](/develop/dapps/asset-processing/jettons) articles. ### 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. From 18d5f83e6d8f461d6ef3dcf38ee0c9b823fe5b70 Mon Sep 17 00:00:00 2001 From: Dmytro Date: Mon, 4 Dec 2023 19:47:45 +0000 Subject: [PATCH 5/8] Update contracts.md add space --- docs/participate/wallets/contracts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/participate/wallets/contracts.md b/docs/participate/wallets/contracts.md index 515685d6ad..225ab87aa1 100644 --- a/docs/participate/wallets/contracts.md +++ b/docs/participate/wallets/contracts.md @@ -52,7 +52,7 @@ Wallet source code: It is the most modern wallet version at the moment. It still has all the functionality of the previous versions, but also introduces something very powerful — `plugins`. -This feature allows developers to implement complex logic that will work in tandem with a user's wallet. For example, some DAppmay require a user to pay a small amount of coins every day to use some features, so the user will need to install the plugin on their wallet by signing a transaction. This plugin will send coins to the destination address every day when it will be reqested by an external message. +This feature allows developers to implement complex logic that will work in tandem with a user's wallet. For example, some DApp may require a user to pay a small amount of coins every day to use some features, so the user will need to install the plugin on their wallet by signing a transaction. This plugin will send coins to the destination address every day when it will be reqested by an external message. This is a very customizable feature which is unique to TON Blockchain. From 0ec6bb39debc0a14f1ef62b6a66b6eafe3261bcc Mon Sep 17 00:00:00 2001 From: Gusarich Date: Tue, 5 Dec 2023 01:03:51 +0300 Subject: [PATCH 6/8] update --- docs/develop/dapps/cookbook.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/develop/dapps/cookbook.md b/docs/develop/dapps/cookbook.md index 8454957167..9df3ea95c7 100644 --- a/docs/develop/dapps/cookbook.md +++ b/docs/develop/dapps/cookbook.md @@ -809,12 +809,11 @@ async function main() { apiKey: '1b312c91c3b691255130350a49ac5a0742454725f910756aff94dfe44858388e', }); - const transactions = await client.getTransactions( - Address.parse('EQBKgXCNLPexWhs2L79kiARR1phGH1LwXxRbNsCFF9doc2lN'), // address that you want to fetch transactions from - { - limit: 5, - } - ); + const myAddress = Address.parse('EQBKgXCNLPexWhs2L79kiARR1phGH1LwXxRbNsCFF9doc2lN'); // address that you want to fetch transactions from + + const transactions = await client.getTransactions(myAddress, { + limit: 5, + }); for (const tx of transactions) { const inMsg = tx.inMessage; @@ -843,7 +842,7 @@ async function main() { body.skip(64); // skip query_id const jettonAmount = body.loadCoins(); - const jettonSender = body.loadAddress(); + const jettonSender = body.loadAddressAny(); const originalForwardPayload = body.loadBit() ? body.loadRef().beginParse() : body; let forwardPayload = originalForwardPayload.clone(); @@ -851,12 +850,13 @@ async function main() { const runStack = (await client.runMethod(sender, 'get_wallet_data')).stack; runStack.skip(2); const jettonMaster = runStack.readAddress(); - const jettonSenderJettonWallet = ( + const jettonWallet = ( await client.runMethod(jettonMaster, 'get_wallet_address', [ - { type: 'slice', cell: beginCell().storeAddress(jettonSender).endCell() }, + { type: 'slice', cell: beginCell().storeAddress(myAddress).endCell() }, ]) ).stack.readAddress(); - if (!jettonSenderJettonWallet.equals(sender)) { + if (!jettonWallet.equals(sender)) { + // if sender is not our real JettonWallet: this message was faked console.log(`FAKE Jetton transfer`); continue; } From 1828ddb109e15768a746a061b762bbf17872a772 Mon Sep 17 00:00:00 2001 From: AlexG Date: Tue, 5 Dec 2023 19:37:46 +0900 Subject: [PATCH 7/8] security_providers_list_update --- docs/develop/companies/auditors.mdx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/develop/companies/auditors.mdx b/docs/develop/companies/auditors.mdx index cc82a32fba..80135492bf 100644 --- a/docs/develop/companies/auditors.mdx +++ b/docs/develop/companies/auditors.mdx @@ -8,11 +8,12 @@ import Button from '@site/src/components/button' Test your software with the following quality assurance providers: * [skynet.certik.com](https://skynet.certik.com/) +* [quantstamp.com](https://quantstamp.com/) * [chainsulting.de](https://chainsulting.de/) * [slowmist.com](https://slowmist.com/) * [hexens.io](https://hexens.io/) -* [quantstamp.com](https://quantstamp.com/) * [vidma.io](https://vidma.io/) +* [scalebit](https://www.scalebit.xyz/) ## Add New SAP From ac53e1e861b9278a5b46e26821f018539d826103 Mon Sep 17 00:00:00 2001 From: AlexG Date: Tue, 5 Dec 2023 19:48:13 +0900 Subject: [PATCH 8/8] update_func_libs_list --- docs/develop/smart-contracts/libraries.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/develop/smart-contracts/libraries.md b/docs/develop/smart-contracts/libraries.md index 2e093f165b..b11756c812 100644 --- a/docs/develop/smart-contracts/libraries.md +++ b/docs/develop/smart-contracts/libraries.md @@ -6,7 +6,8 @@ ## Libraries from Community - - [open-contracts/utils](https://github.com/TonoxDeFi/open-contracts/tree/main/contracts/utils) — Utility library + - [continuation-team/openlib.func](https://github.com/continuation-team/openlib.func) - Reduces transaction fees in common scenarios. + - [open-contracts/utils](https://github.com/TonoxDeFi/open-contracts/tree/main/contracts/utils) — Utility library - [open-contracts/strings](https://github.com/TonoxDeFi/open-contracts/tree/main/contracts/strings) — Provides operations on strings - [open-contracts/math](https://github.com/TonoxDeFi/open-contracts/tree/main/contracts/math) — Math library that extends FunC arithmetic operations - [open-contracts/tuples](https://github.com/TonoxDeFi/open-contracts/tree/main/contracts/tuples) — Collection of tuple-releated functions for FunC