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 diff --git a/docs/develop/dapps/asset-processing/README.md b/docs/develop/dapps/asset-processing/README.md index e2385a014f..ba07864a6d 100644 --- a/docs/develop/dapps/asset-processing/README.md +++ b/docs/develop/dapps/asset-processing/README.md @@ -63,7 +63,8 @@ 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/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. diff --git a/docs/develop/dapps/cookbook.md b/docs/develop/dapps/cookbook.md index 19b93c2c3e..ea30bb7ee8 100644 --- a/docs/develop/dapps/cookbook.md +++ b/docs/develop/dapps/cookbook.md @@ -971,3 +971,171 @@ 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 (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) + +_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, beginCell, fromNano } from '@ton/ton'; + +async function main() { + const client = new TonClient({ + endpoint: 'https://toncenter.com/api/v2/jsonRPC', + apiKey: '1b312c91c3b691255130350a49ac5a0742454725f910756aff94dfe44858388e', + }); + + 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; + + 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 + 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.loadAddressAny(); + 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 jettonWallet = ( + await client.runMethod(jettonMaster, 'get_wallet_address', [ + { type: 'slice', cell: beginCell().storeAddress(myAddress).endCell() }, + ]) + ).stack.readAddress(); + if (!jettonWallet.equals(sender)) { + // if sender is not our real JettonWallet: this message was faked + 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(`Transaction Hash: ${tx.hash().toString('hex')}`); + console.log(`Transaction LT: ${tx.lt}`); + console.log(); + } +} + +main().finally(() => console.log('Exiting...')); +``` + + + + +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) 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 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.