From 09d5bc296bbf00eec638385f6ab408037fb7b826 Mon Sep 17 00:00:00 2001 From: Antonoff <35700168+memearchivarius@users.noreply.github.com> Date: Wed, 26 Feb 2025 13:51:20 +0300 Subject: [PATCH 01/23] Refactor and improve get methods documentation for clarity and precision --- .../guidelines/smart-contracts/get-methods.md | 271 +++++++++--------- 1 file changed, 135 insertions(+), 136 deletions(-) diff --git a/docs/v3/guidelines/smart-contracts/get-methods.md b/docs/v3/guidelines/smart-contracts/get-methods.md index 594b08290e6..4909f272d3d 100644 --- a/docs/v3/guidelines/smart-contracts/get-methods.md +++ b/docs/v3/guidelines/smart-contracts/get-methods.md @@ -1,62 +1,61 @@ # Get Methods :::note -Before proceeding, it is recommended that readers have a basic understanding of the [FunC programming language](/v3/documentation/smart-contracts/func/overview) on TON Blockchain. This will help you grasp the information provided here more effectively. +To fully benefit from this content, readers must understand the [FunC programming language](/v3/documentation/smart-contracts/func/overview) on the TON Blockchain. This knowledge is crucial for grasping the information presented here. ::: ## Introduction -Get methods are special functions in smart contracts that are made for querying specific data from them. Their execution doesn't cost any fees and happens outside of the blockchain. +Get methods are special functions in smart contracts that allow you to query specific data. Their execution doesn't cost any fees and happens outside of the blockchain. -These functions are very common in most smart contracts. For example, the default [Wallet contract](/v3/documentation/smart-contracts/contracts-specs/wallet-contracts) has several get methods, such as `seqno()`, `get_subwallet_id()` and `get_public_key()`. They are used by wallets, SDKs, and APIs to fetch data about wallets. +These functions are very common in most smart contracts. For example, the default [Wallet contract](/v3/documentation/smart-contracts/contracts-specs/wallet-contracts) has several get methods, such as `seqno()`, `get_subwallet_id()` and `get_public_key()`. Wallets, SDKs, and APIs use them to fetch data about wallets. ## Design patterns for get methods ### Basic get methods design patterns -1. **Single data point retrieval**: A basic design pattern is to create methods that return individual data points from the contract's state. These methods have no parameters and return a single value. +1. **Single data point retrieval**: A fundamental design pattern is to create methods that return individual data points from the contract's state. These methods have no parameters and return a single value. - Example: + Example: - ```func - int get_balance() method_id { - return get_data().begin_parse().preload_uint(64); - } - ``` + ```func + int get_balance() method_id { + return get_data().begin_parse().preload_uint(64); + } + ``` +2. **Aggregate Data Retrieval**: Another common method is to create methods that gather multiple pieces of data from a contract's state in one call. This is useful when specific data points are often used together. You can see this approach frequently in [Jetton](#jettons) and [NFT](#nfts) contracts. -2. **Aggregate data retrieval**: Another common pattern is to create methods that return multiple data points from the contract's state in a single call. This is often used when certain data points are commonly used together. These are commonly used in [Jetton](#jettons) and [NFT](#nfts) contracts. + Example: - Example: - - ```func - (int, slice, slice, cell) get_wallet_data() method_id { - return load_data(); - } - ``` + ```func + (int, slice, slice, cell) get_wallet_data() method_id { + return load_data(); + } + ``` ### Advanced get methods design patterns -1. **Computed data retrieval**: In some cases, the data that needs to be retrieved isn't stored directly in the contract's state, but instead is calculated based on the state and the input arguments. +1. **Computed data retrieval**: In some cases, the data that needs to be retrieved isn't stored directly in the contract's state but calculated based on the state and the input arguments. - Example: + Example: - ```func - slice get_wallet_address(slice owner_address) method_id { - (int total_supply, slice admin_address, cell content, cell jetton_wallet_code) = load_data(); - return calculate_user_jetton_wallet_address(owner_address, my_address(), jetton_wallet_code); - } - ``` + ```func + slice get_wallet_address(slice owner_address) method_id { + (int total_supply, slice admin_address, cell content, cell jetton_wallet_code) = load_data(); + return calculate_user_jetton_wallet_address(owner_address, my_address(), jetton_wallet_code); + } + ``` 2. **Conditional data retrieval**: Sometimes, the data that needs to be retrieved depends on certain conditions, such as the current time. - Example: + Example: - ```func - (int) get_ready_to_be_used() method_id { - int ready? = now() >= 1686459600; - return ready?; - } - ``` + ```func + (int) get_ready_to_be_used() method_id { + int ready? = now() >= 1686459600; + return ready?; + } + ``` ## Most common get methods @@ -66,32 +65,32 @@ These functions are very common in most smart contracts. For example, the defaul ```func int seqno() method_id { - return get_data().begin_parse().preload_uint(32); + return get_data().begin_parse().preload_uint(32); } ``` -Returns the sequence number of the transaction within a specific wallet. This method is primarily used for [replay protection](/v3/guidelines/smart-contracts/howto/wallet#replay-protection---seqno). +Returns the transaction's sequence number within a specific wallet. This method is primarily used for [replay protection](/v3/guidelines/smart-contracts/howto/wallet#replay-protection---seqno). #### get_subwallet_id() ```func int get_subwallet_id() method_id { - return get_data().begin_parse().skip_bits(32).preload_uint(32); + return get_data().begin_parse().skip_bits(32).preload_uint(32); } ``` -- [What is Subwallet ID?](/v3/guidelines/smart-contracts/howto/wallet#subwallet-ids) +- [What is Subwallet ID?](/v3/guidelines/smart-contracts/howto/wallet#subwallet-ids) #### get_public_key() ```func int get_public_key() method_id { - var cs = get_data().begin_parse().skip_bits(64); - return cs.preload_uint(256); + var cs = get_data().begin_parse().skip_bits(64); + return cs.preload_uint(256); } ``` -Retrieves the public key associated with the wallet. +This method retrieves the public key associated with the wallet. ### Jettons @@ -99,23 +98,23 @@ Retrieves the public key associated with the wallet. ```func (int, slice, slice, cell) get_wallet_data() method_id { - return load_data(); + return load_data(); } ``` This method returns the complete set of data associated with a jetton wallet: -- (int) balance -- (slice) owner_address -- (slice) jetton_master_address -- (cell) jetton_wallet_code +- (int) balance +- (slice) owner_address +- (slice) jetton_master_address +- (cell) jetton_wallet_code #### get_jetton_data() ```func (int, int, slice, cell, cell) get_jetton_data() method_id { - (int total_supply, slice admin_address, cell content, cell jetton_wallet_code) = load_data(); - return (total_supply, -1, admin_address, content, jetton_wallet_code); + (int total_supply, slice admin_address, cell content, cell jetton_wallet_code) = load_data(); + return (total_supply, -1, admin_address, content, jetton_wallet_code); } ``` @@ -125,12 +124,12 @@ Returns data of a jetton master, including its total supply, the address of its ```func slice get_wallet_address(slice owner_address) method_id { - (int total_supply, slice admin_address, cell content, cell jetton_wallet_code) = load_data(); - return calculate_user_jetton_wallet_address(owner_address, my_address(), jetton_wallet_code); + (int total_supply, slice admin_address, cell content, cell jetton_wallet_code) = load_data(); + return calculate_user_jetton_wallet_address(owner_address, my_address(), jetton_wallet_code); } ``` -Given the address of the owner, this method calculates and returns the address for the owner's jetton wallet contract. +Given the owner's address, this method calculates and returns the address for the owner's jetton wallet contract. ### NFTs @@ -138,66 +137,66 @@ Given the address of the owner, this method calculates and returns the address f ```func (int, int, slice, slice, cell) get_nft_data() method_id { - (int init?, int index, slice collection_address, slice owner_address, cell content) = load_data(); - return (init?, index, collection_address, owner_address, content); + (int init?, int index, slice collection_address, slice owner_address, cell content) = load_data(); + return (init?, index, collection_address, owner_address, content); } ``` -Returns the data associated with a non-fungible token, including whether it has been initialized, its index in a collection, the address of its collection, the owner's address, and its individual content. +Returns the data associated with a non-fungible token, including whether it has been initialized, its index in a collection, the address of its collection, the owner's address, and its content. #### get_collection_data() ```func (int, cell, slice) get_collection_data() method_id { - var (owner_address, next_item_index, content, _, _) = load_data(); - slice cs = content.begin_parse(); - return (next_item_index, cs~load_ref(), owner_address); + var (owner_address, next_item_index, content, _, _) = load_data(); + slice cs = content.begin_parse(); + return (next_item_index, cs~load_ref(), owner_address); } ``` -Returns the data of a NFT collection, including the index of the next item to mint, the content of the collection and the owner's address. +Returns the data of an NFT collection, including the index of the next item available for minting, the content of the collection, and the owner's address. #### get_nft_address_by_index(int index) ```func slice get_nft_address_by_index(int index) method_id { - var (_, _, _, nft_item_code, _) = load_data(); - cell state_init = calculate_nft_item_state_init(index, nft_item_code); - return calculate_nft_item_address(workchain(), state_init); + var (_, _, _, nft_item_code, _) = load_data(); + cell state_init = calculate_nft_item_state_init(index, nft_item_code); + return calculate_nft_item_address(workchain(), state_init); } ``` -Given an index, this method calculates and returns the address of the corresponding NFT item contract of this collection. +Given an index, this method calculates and returns the corresponding NFT item contract address within this collection. #### royalty_params() ```func (int, int, slice) royalty_params() method_id { - var (_, _, _, _, royalty) = load_data(); - slice rs = royalty.begin_parse(); - return (rs~load_uint(16), rs~load_uint(16), rs~load_msg_addr()); + var (_, _, _, _, royalty) = load_data(); + slice rs = royalty.begin_parse(); + return (rs~load_uint(16), rs~load_uint(16), rs~load_msg_addr()); } ``` -Fetches the royalty parameters for an NFT. These parameters include the royalty percentage, which is paid to the original creator whenever the NFT is sold. +This method fetches the royalty parameters for an NFT. These parameters include the royalty percentage paid to the original creator whenever the NFT is sold. #### get_nft_content(int index, cell individual_nft_content) ```func cell get_nft_content(int index, cell individual_nft_content) method_id { - var (_, _, content, _, _) = load_data(); - slice cs = content.begin_parse(); - cs~load_ref(); - slice common_content = cs~load_ref().begin_parse(); - return (begin_cell() - .store_uint(1, 8) ;; offchain tag - .store_slice(common_content) - .store_ref(individual_nft_content) - .end_cell()); + var (_, _, content, _, _) = load_data(); + slice cs = content.begin_parse(); + cs~load_ref(); + slice common_content = cs~load_ref().begin_parse(); + return (begin_cell() + .store_uint(1, 8) ;; offchain tag + .store_slice(common_content) + .store_ref(individual_nft_content) + .end_cell()); } ``` -Given an index and [individual NFT content](#get_nft_data), this method fetches and returns the combined common and individual content of the NFT. +Given an index and [individual NFT content](#get_nft_data), this method fetches and returns the NFT's combined common and individual content. ## How to work with get methods @@ -207,32 +206,32 @@ Given an index and [individual NFT content](#get_nft_data), this method fetches You can call get methods on the bottom of the page in the "Methods" tab. -- https://tonviewer.com/EQAWrNGl875lXA6Fff7nIOwTIYuwiJMq0SmtJ5Txhgnz4tXI?section=method +- https://tonviewer.com/EQAWrNGl875lXA6Fff7nIOwTIYuwiJMq0SmtJ5Txhgnz4tXI?section=method #### Ton.cx You can call get methods on the "Get methods" tab. -- https://ton.cx/address/EQAWrNGl875lXA6Fff7nIOwTIYuwiJMq0SmtJ5Txhgnz4tXI +- https://ton.cx/address/EQAWrNGl875lXA6Fff7nIOwTIYuwiJMq0SmtJ5Txhgnz4tXI ### Calling get methods from code We will use Javascript libraries and tools for the examples below: -- [ton](https://github.com/ton-org/ton) library -- [Blueprint](/v3/documentation/smart-contracts/getting-started/javascript) SDK +- [ton](https://github.com/ton-org/ton) library +- [Blueprint](/v3/documentation/smart-contracts/getting-started/javascript) SDK Let's say there is some contract with the following get method: ```func (int) get_total() method_id { - return get_data().begin_parse().preload_uint(32); ;; load and return the 32-bit number from the data + return get_data().begin_parse().preload_uint(32); ;; load and return the 32-bit number from the data } ``` This method returns a single number loaded from the contract data. -The code snippet below can be used to call this get method on some contract deployed at the known address: +You can use the code snippet below to call this get method on a contract deployed at a known address: ```ts import { TonClient } from '@ton/ton'; @@ -241,28 +240,28 @@ import { Address } from '@ton/core'; async function main() { // Create Client const client = new TonClient({ - endpoint: 'https://toncenter.com/api/v2/jsonRPC', - }); + endpoint: 'https://toncenter.com/api/v2/jsonRPC', + }); // Call get method const result = await client.runMethod( - Address.parse('EQD4eA1SdQOivBbTczzElFmfiKu4SXNL4S29TReQwzzr_70k'), + Address.parse('EQD4eA1SdQOivBbTczzElFmfiKu4SXNL4S29TReQwzzr_70k'), 'get_total' - ); + ); const total = result.stack.readNumber(); - console.log('Total:', total); + console.log('Total:', total); } main(); ``` -This code will result `Total: 123` output. The number can be different, this is just an example. +This code will produce an output in the format `Total: 123`. The number may vary, as this is just an example. ### Testing get methods -For testing smart contracts created we can use the [Sandbox](https://github.com/ton-community/sandbox) which is installed by default in new Blueprint projects. +For testing smart contracts, we can use the [Sandbox](https://github.com/ton-community/sandbox), which is installed by default in new Blueprint projects. -First, you need to add a special method in the contract wrapper that will execute the get method and return the typed result. Let's say your contract is called _Counter_ and you have already implemented the method that updates the stored number. Open `wrappers/Counter.ts` and add the following method: +First, you must add a special method in the contract wrapper to execute the get method and return the typed result. Let's say your contract is called _Counter_, and you have already implemented the method to update the stored number. Open `wrappers/Counter.ts` and add the following method: ```ts async getTotal(provider: ContractProvider) { @@ -271,7 +270,7 @@ async getTotal(provider: ContractProvider) { } ``` -It executed the get method and fetches the resulting stack. The stack in case with get methods is basically what it did return. In this snippet, we read a single number from it. In more complex cases with several values returned at once, you can just call the `readSomething` type of methods several times to parse the whole execution result from stack. +It executes the get method and retrieves the resulting stack. In this snippet, we read a single number from the stack. In more complex cases where multiple values are returned at once, you can simply call the `readSomething` type of method multiple times to parse the entire execution result from the stack. Finally, we can use this method in our tests. Navigate to the `tests/Counter.spec.ts` and add a new test: @@ -283,31 +282,31 @@ it('should return correct number from get method', async () => { }); ``` -Check it by running `npx blueprint test` in your terminal and if you did everything correct, this test should be marked as passed! +You can check it by running `npx blueprint test` in your terminal. If you did everything correctly, this test should be marked as passed! ## Invoking get methods from other contracts -Contrary to what might seem intuitive, invoking get methods from other contracts is not possible on-chain, primarily due to the nature of blockchain technology and the need for consensus. +Contrary to what might seem intuitive, invoking get methods from other contracts is impossible on-chain. This limitation stems primarily from the nature of blockchain technology and the need for consensus. -Firstly, acquiring data from another shardchain may require time. Such latency could easily disrupt contract execution flow, as blockchain operations are expected to execute in a deterministic and timely manner. +First, acquiring data from another shardchain may introduce significant latency. Such delays could disrupt the contract execution flow, as blockchain operations are designed to execute in a deterministic and timely manner. -Secondly, achieving consensus among validators would be problematic. In order for validators to verify the correctness of a transaction, they would also need to invoke the same get method. However, if the state of the target contract changes between these multiple invocations, validators could end up with differing versions of the transaction result. +Second, achieving consensus among validators would be problematic. Validators would also need to invoke the same get method to verify a transaction's correctness. However, if the state of the target contract changes between these multiple invocations, validators could end up with differing versions of the transaction result. -Lastly, smart contracts in TON are designed to be pure functions: for the same input, they will always produce the same output. This principle allows for straightforward consensus during message processing. Introducing runtime acquisition of arbitrary, dynamically changing data would break this deterministic property. +Lastly, smart contracts in TON are designed to be pure functions: they will always produce the same output for the same input. This principle allows for straightforward consensus during message processing. Introducing runtime acquisition of arbitrary, dynamically changing data would break this deterministic property. ### Implications for developers -These limitations imply that one contract cannot directly access the state of another contract via its get methods. The inability to incorporate real-time, external data in the deterministic flow of a contract might appear restrictive. However, it is these very constraints that ensure the integrity and reliability of blockchain technology. +These limitations mean that one contract cannot directly access the state of another contract via its get methods. While the inability to incorporate real-time, external data into a contract's deterministic flow might seem restrictive, it is precisely these constraints that ensure the integrity and reliability of blockchain technology. ### Solutions and Workarounds -In the TON Blockchain, smart contracts communicate via messages, instead of directly invoking methods from another contract. A message requesting execution of a specific method can be sent to a targeted contract. These requests typically start with special [operation codes](/v3/documentation/smart-contracts/message-management/internal-messages). +In the TON Blockchain, smart contracts communicate through messages rather than directly invoking methods from one another. One can send a message to another contract requesting the execution of a specific method. These requests usually begin with special [operation codes](/v3/documentation/smart-contracts/message-management/internal-messages). -A contract designed to accept these requests will execute the desired method and send the results back in a separate message. While this might seem complex, it actually streamlines communication between contracts, and enhances the blockchain network's scalability and performance. +A contract designed to handle such requests will execute the specified method and return the results in a separate message. While this approach may seem complex, it effectively streamlines communication between contracts, enhancing the scalability and performance of the blockchain network. -This message-passing mechanism is integral to the TON Blockchain's operation, paving the way for scalable network growth without the need for extensive synchronization between shards. +This message-passing mechanism is integral to the TON Blockchain's operation, paving the way for scalable network growth without requiring extensive synchronization between shards. -For effective inter-contract communication, it's essential that your contracts are designed to correctly accept and respond to requests. This includes specifying methods that can be invoked on-chain to return responses. +For effective inter-contract communication, it is crucial to design your contracts so that they can properly accept and respond to requests. This involves implementing methods that can be invoked on-chain to return responses. Let's consider a simple example: @@ -315,58 +314,58 @@ Let's consider a simple example: #include "imports/stdlib.fc"; int get_total() method_id { - return get_data().begin_parse().preload_uint(32); + return get_data().begin_parse().preload_uint(32); } () recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure { - if (in_msg_body.slice_bits() < 32) { - return (); - } - - slice cs = in_msg_full.begin_parse(); - cs~skip_bits(4); - slice sender = cs~load_msg_addr(); - - int op = in_msg_body~load_uint(32); ;; load the operation code - - if (op == 1) { ;; increase and update the number - int number = in_msg_body~load_uint(32); - int total = get_total(); - total += number; - set_data(begin_cell().store_uint(total, 32).end_cell()); - } - elseif (op == 2) { ;; query the number - int total = get_total(); - send_raw_message(begin_cell() - .store_uint(0x18, 6) - .store_slice(sender) - .store_coins(0) - .store_uint(0, 107) ;; default message headers (see sending messages page) - .store_uint(3, 32) ;; response operation code - .store_uint(total, 32) ;; the requested number - .end_cell(), 64); - } + if (in_msg_body.slice_bits() < 32) { + return (); + } + + slice cs = in_msg_full.begin_parse(); + cs~skip_bits(4); + slice sender = cs~load_msg_addr(); + + int op = in_msg_body~load_uint(32); ;; load the operation code + + if (op == 1) { ;; increase and update the number + int number = in_msg_body~load_uint(32); + int total = get_total(); + total += number; + set_data(begin_cell().store_uint(total, 32).end_cell()); + } + elseif (op == 2) { ;; query the number + int total = get_total(); + send_raw_message(begin_cell() + .store_uint(0x18, 6) + .store_slice(sender) + .store_coins(0) + .store_uint(0, 107) ;; default message headers (see sending messages page) + .store_uint(3, 32) ;; response operation code + .store_uint(total, 32) ;; the requested number + .end_cell(), 64); + } } ``` In this example, the contract receives and processes internal messages by interpreting operation codes, executing specific methods, and returning responses appropriately: -- Op-code `1` denotes a request to update the number in the contract's data. -- Op-code `2` signifies a request to query the number from the contract's data. -- Op-code `3` is used in the response message, which the calling smart contract must handle in order to receive the result. +- Op-code `1` denotes a request to update the number in the contract's data. +- Op-code `2` signifies a request to query the number from the contract's data. +- Op-code `3` is used in the response message, which the calling smart contract must handle to receive the result. For simplicity, we used just simple little numbers 1, 2, and 3 for the operation codes. But for real projects, consider setting them according to the standard: -- [CRC32 Hashes for op-codes](/v3/documentation/data-formats/tlb/crc32) +- [CRC32 Hashes for op-codes](/v3/documentation/data-formats/tlb/crc32) ## Common pitfalls and how to avoid them -1. **Misuse of get methods**: As mentioned earlier, get methods are designed to return data from the contract's state and are not meant to change the state of the contract. Attempting to alter the contract's state within a get method won't actually do it. +1. **Misuse of get methods**: As mentioned earlier, get methods are designed to return data from the contract's state and are not meant to change the contract's state. Attempting to alter the contract's state within a get method will not actually do it. -2. **Ignoring return types**: Every get method should have a clearly defined return type that matches the data being retrieved. If a method is expected to return a specific type of data, ensure that all paths within the method return this type. Avoid using inconsistent return types, as this can lead to errors and difficulties when interacting with the contract. +2. **Ignoring return types**: Every get method must have a clearly defined return type that matches the retrieved data. If a method is expected to return a specific type of data, ensure that all execution paths within the method return this type. Inconsistent return types should be avoided, as they can lead to errors and complications when interacting with the contract. -3. **Assuming cross-contract calls**: A common misconception is that get methods can be called from other contracts on-chain. However, as we've discussed, this is not possible due to the nature of blockchain technology and the need for consensus. Always remember that get methods are intended to be used off-chain, and on-chain interactions between contracts are done through internal messages. +3. **Assuming cross-contract calls**: A common misconception is that get methods can be called directly from other contracts on-chain. However, as previously discussed, this is not possible due to the inherent nature of blockchain technology and the requirement for consensus. Always keep in mind that get methods are designed for off-chain use, while on-chain interactions between contracts are facilitated through internal messages. ## Conclusion -Get methods are an essential tool for querying data from smart contracts in the TON Blockchain. Although they have their limitations, understanding these restrictions and knowing how to work around them is key to effectively using get methods in your smart contracts. +Get methods are vital for querying data from smart contracts on the TON Blockchain. While they have certain limitations, understanding these constraints and learning how to work around them is crucial for effectively utilizing get methods in your smart contracts. From e032b04242f620730808bfb4b73ca5867a6db7b9 Mon Sep 17 00:00:00 2001 From: Antonoff <35700168+memearchivarius@users.noreply.github.com> Date: Wed, 26 Feb 2025 17:54:12 +0300 Subject: [PATCH 02/23] Improve fee calculation documentation with enhanced clarity and precision --- .../smart-contracts/fee-calculation.md | 159 +++++++++--------- .../guidelines/smart-contracts/get-methods.md | 2 +- 2 files changed, 81 insertions(+), 80 deletions(-) diff --git a/docs/v3/guidelines/smart-contracts/fee-calculation.md b/docs/v3/guidelines/smart-contracts/fee-calculation.md index cd4ca1c2be8..42e77cdf56d 100644 --- a/docs/v3/guidelines/smart-contracts/fee-calculation.md +++ b/docs/v3/guidelines/smart-contracts/fee-calculation.md @@ -1,18 +1,18 @@ -# Fees Calculation +# Fees calculation -When your contract starts processing an incoming message, you should check the amount of TONs attached to the message to ensure they are enough to cover [all types of fees](/v3/documentation/smart-contracts/transaction-fees/fees#elements-of-transaction-fee). To do this, you need to calculate (or predict) the fee for the current transaction. +When your contract begins processing an incoming message, you should verify the amount of TONs attached to the message to ensure it is sufficient to cover [all types of fees](/v3/documentation/smart-contracts/transaction-fees/fees#elements-of-transaction-fee). To achieve this, you need to calculate (or predict) the fee for the current transaction. -This document describes how to calculate fees in FunC contracts using the new TVM opcodes. +This document explains how to calculate fees in FunC contracts using the latest TVM opcodes. :::info More information on opcodes -For a comprehensive list of TVM opcodes, including those mentioned below, check the [TVM instruction page](/v3/documentation/tvm/instructions). +For a comprehensive list of TVM opcodes, including those mentioned below, refer to the [TVM instruction page](/v3/documentation/tvm/instructions). ::: -## Storage Fee +## Storage fee ### Overview -In brief, `storage fees` are the amounts you pay for storing a smart contract on the blockchain. You pay for every second the smart contract is stored on the blockchain. +In short, `storage fees` are the costs of storing a smart contract on the blockchain. You pay for every second the smart contract remains stored on the blockchain. Use the `GETSTORAGEFEE` opcode with the following parameters: @@ -22,76 +22,76 @@ Use the `GETSTORAGEFEE` opcode with the following parameters: | bits | Number of contract bits | | is_mc | True if the source or destination is in the masterchain | -:::info Only unique hash cells are counted for storage and fwd fees i.e. 3 identical hash cells are counted as one. +:::info Only unique hash cells are counted for storage and forward fees. For example, three identical hash cells are counted as one. -In particular, it deduplicates data: if there are several equivalent sub-cells referenced in different branches, their content is only stored once. +This mechanism deduplicates data: if multiple equivalent sub-cells are referenced across different branches, their content is stored only once. [Read more about deduplication](/v3/documentation/data-formats/tlb/library-cells). ::: -### Calculation Flow +### Calculation flow -Each contract has its balance. You can calculate how many TONs your contract requires to remain valid for a specified `seconds` time using the function: +Each contract has its balance. You can calculate how many TONs your contract requires to remain valid for a specified `seconds` duration using the function: ```func int get_storage_fee(int workchain, int seconds, int bits, int cells) asm(cells bits seconds workchain) "GETSTORAGEFEE"; ``` -You can then hardcode that value into the contract and calculate the current storage fee using: +You can then hardcode this value into the contract and calculate the current storage fee using: ```func -;; functions from func stdlib (not presented on mainnet) +;; functions from func stdlib (not available on mainnet) () raw_reserve(int amount, int mode) impure asm "RAWRESERVE"; int get_storage_fee(int workchain, int seconds, int bits, int cells) asm(cells bits seconds workchain) "GETSTORAGEFEE"; int my_storage_due() asm "DUEPAYMENT"; ;; constants from stdlib -;;; Creates an output action which would reserve exactly x nanograms (if y = 0). +;;; Creates an output action which reserves exactly x nanograms (if y = 0). const int RESERVE_REGULAR = 0; -;;; Creates an output action which would reserve at most x nanograms (if y = 2). +;;; Creates an output action which reserves at most x nanograms (if y = 2). ;;; Bit +2 in y means that the external action does not fail if the specified amount cannot be reserved; instead, all remaining balance is reserved. const int RESERVE_AT_MOST = 2; -;;; in the case of action fail - bounce transaction. No effect if RESERVE_AT_MOST (+2) is used. TVM UPGRADE 2023-07. v3/documentation/tvm/changelog/tvm-upgrade-2023-07#sending-messages +;;; In the case of action failure, the transaction is bounced. No effect if RESERVE_AT_MOST (+2) is used. TVM UPGRADE 2023-07. [v3/documentation/tvm/changelog/tvm-upgrade-2023-07#sending-messages](https://ton.org/docs/#/tvm/changelog/tvm-upgrade-2023-07#sending-messages) const int RESERVE_BOUNCE_ON_ACTION_FAIL = 16; () calculate_and_reserve_at_most_storage_fee(int balance, int msg_value, int workchain, int seconds, int bits, int cells) inline { - int on_balance_before_msg = my_ton_balance - msg_value; - int min_storage_fee = get_storage_fee(workchain, seconds, bits, cells); ;; can be hardcoded IF CODE OF THE CONTRACT WILL NOT BE UPDATED - raw_reserve(max(on_balance_before_msg, min_storage_fee + my_storage_due()), RESERVE_AT_MOST); + int on_balance_before_msg = my_ton_balance - msg_value; + int min_storage_fee = get_storage_fee(workchain, seconds, bits, cells); ;; can be hardcoded IF THE CONTRACT CODE WILL NOT BE UPDATED + raw_reserve(max(on_balance_before_msg, min_storage_fee + my_storage_due()), RESERVE_AT_MOST); } ``` -If `storage_fee` is hardcoded, **remember to update it** during contract update process. Not all contracts support updating, so this is an optional requirement. +If `storage_fee` is hardcoded, **remember to update it** during the contract update process. Not all contracts support updates, so this is an optional requirement. -## Computation Fee +## Computation fee ### Overview -In most cases use the `GETGASFEE` opcode with the following parameters: +In most cases, use the `GETGASFEE` opcode with the following parameters: | Param | Description | |:-----------|:--------------------------------------------------------| | `gas_used` | Gas amount, calculated in tests and hardcoded | -| `is_mc` | True if the source or destination is in the masterchain | +| `is_mc` | True if the source or destination is in the masterchain | -### Calculation Flow +### Calculation flow ```func int get_compute_fee(int workchain, int gas_used) asm(gas_used workchain) "GETGASFEE"; ``` -But how do you get `gas_used`? Through tests! +But how do you determine `gas_used`? Through testing! To calculate `gas_used`, you should write a test for your contract that: -1. Makes a transfer. -2. Checks if it's successful and retrieves the transfer info. -3. Checks the actual amount of gas used by that transfer for computation. +1. Executes a transfer. +2. Verifies its success and retrieves the transfer details. +3. Checks the amount of gas the transfer uses for computation. -Contract computation flow can depend on input data. You should run contract in that way to use as much gas as possible. Make sure that you are using the most expensive compute way to compute contract +The contract's computation flow can depend on input data. You should run the contract in a way that maximizes gas usage. Ensure you are using the most computationally expensive path to test the contract. ```ts -// Just Init code +// Initialization code const deployerJettonWallet = await userWallet(deployer.address); let initialJettonBalance = await deployerJettonWallet.getJettonBalance(); const notDeployerJettonWallet = await userWallet(notDeployer.address); @@ -99,45 +99,45 @@ let initialJettonBalance2 = await notDeployerJettonWallet.getJettonBalance(); let sentAmount = toNano('0.5'); let forwardAmount = toNano('0.05'); let forwardPayload = beginCell().storeUint(0x1234567890abcdefn, 128).endCell(); -// Make sure payload is different, so cell load is charged for each individual payload. +// Ensure the payload is unique to charge cell loading for each payload. let customPayload = beginCell().storeUint(0xfedcba0987654321n, 128).endCell(); // Let's use this case for fees calculation -// Put the forward payload into custom payload, to make sure maximum possible gas is used during computation +// Embed the forward payload into the custom payload to ensure maximum gas usage during computation const sendResult = await deployerJettonWallet.sendTransfer(deployer.getSender(), toNano('0.17'), // tons sentAmount, notDeployer.address, - deployer.address, customPayload, forwardAmount, forwardPayload); -expect(sendResult.transactions).toHaveTransaction({ //excesses - from: notDeployerJettonWallet.address, - to: deployer.address, + deployer.address, customPayload, forwardAmount, forwardPayload); +expect(sendResult.transactions).toHaveTransaction({ // excesses + from: notDeployerJettonWallet.address, + to: deployer.address, }); /* transfer_notification#7362d09c query_id:uint64 amount:(VarUInteger 16) - sender:MsgAddress forward_payload:(Either Cell ^Cell) - = InternalMsgBody; + sender:MsgAddress forward_payload:(Either Cell ^Cell) + = InternalMsgBody; */ expect(sendResult.transactions).toHaveTransaction({ // notification - from: notDeployerJettonWallet.address, - to: notDeployer.address, - value: forwardAmount, - body: beginCell().storeUint(Op.transfer_notification, 32).storeUint(0, 64) // default queryId - .storeCoins(sentAmount) - .storeAddress(deployer.address) - .storeUint(1, 1) - .storeRef(forwardPayload) - .endCell() + from: notDeployerJettonWallet.address, + to: notDeployer.address, + value: forwardAmount, + body: beginCell().storeUint(Op.transfer_notification, 32).storeUint(0, 64) // default queryId + .storeCoins(sentAmount) + .storeAddress(deployer.address) + .storeUint(1, 1) + .storeRef(forwardPayload) + .endCell() }); const transferTx = findTransactionRequired(sendResult.transactions, { - on: deployerJettonWallet.address, - from: deployer.address, - op: Op.transfer, - success: true + on: deployerJettonWallet.address, + from: deployer.address, + op: Op.transfer, + success: true }); let computedGeneric: (transaction: Transaction) => TransactionComputeVm; computedGeneric = (transaction) => { if(transaction.description.type !== "generic") - throw("Expected generic transactionaction"); + throw("Expected generic transaction"); if(transaction.description.computePhase.type !== "vm") throw("Compute phase expected") return transaction.description.computePhase; @@ -146,27 +146,27 @@ computedGeneric = (transaction) => { let printTxGasStats: (name: string, trans: Transaction) => bigint; printTxGasStats = (name, transaction) => { const txComputed = computedGeneric(transaction); - console.log(`${name} used ${txComputed.gasUsed} gas`); - console.log(`${name} gas cost: ${txComputed.gasFees}`); + console.log(`${name} used ${txComputed.gasUsed} gas`); + console.log(`${name} gas cost: ${txComputed.gasFees}`); return txComputed.gasFees; } send_gas_fee = printTxGasStats("Jetton transfer", transferTx); ``` -## Forward Fee +## Forward fee ### Overview -The forward fee is taken for outgoing messages. +The forward fee is charged for outgoing messages. -Generally, there are three cases of forward fee processing: +Generally, there are three scenarios for forward fee processing: -1. The message structure is deterministic and you can predict the fee. -2. The message structure depends a lot on the incoming message structure. -3. You can't predict the outgoing message structure at all. +1. The message structure is deterministic, and you can predict the fee. +2. The message structure depends heavily on the incoming message structure. +3. You cannot predict the outgoing message structure at all. -### Calculation Flow +### Calculation flow If the message structure is deterministic, use the `GETFORWARDFEE` opcode with the following parameters: @@ -176,42 +176,43 @@ If the message structure is deterministic, use the `GETFORWARDFEE` opcode with t | bits | Number of bits | | is_mc | True if the source or destination is in the masterchain | -:::info Only unique hash cells are counted for storage and fwd fees i.e. 3 identical hash cells are counted as one. +:::info Only unique hash cells are counted for storage and forward fees. For example, three identical hash cells are counted as one. -In particular, it deduplicates data: if there are several equivalent sub-cells referenced in different branches, their content is only stored once. +This mechanism deduplicates data: if multiple equivalent sub-cells are referenced across different branches, their content is stored only once. [Read more about deduplication](/v3/documentation/data-formats/tlb/library-cells). ::: -However, sometimes the outgoing message depends significantly on the incoming structure, and in that case, you can't fully predict the fee. Try to use the `GETORIGINALFWDFEE` opcode with the following parameters: +However, if the outgoing message depends significantly on the incoming structure, you may not be able to predict the fee fully. In such cases, try using the `GETORIGINALFWDFEE` opcode with the following parameters: | Param name | Description | |:-----------|:----------------------------------------------------| | fwd_fee | Parsed from the incoming message | | is_mc | True if the source or destination is in the masterchain | -:::caution Be careful with `SENDMSG` opcode -Next opcode, `SENDMSG`, **is the least optimal way** to calculate fee, but **better than not checking**. +:::caution Be careful with the `SENDMSG` opcode +The `SENDMSG` opcode is the least optimal way to calculate fees, but it is better than not checking. It uses an **unpredictable amount** of gas. -Do not use it unless necessary. +Avoid using it unless absolutely necessary. ::: -If even `GETORIGINALFWDFEE` can't be used, there is one more option. Use the `SENDMSG` opcode with the following parameters: +If even `GETORIGINALFWDFEE` cannot be used, one more option exists. Use the `SENDMSG` opcode with the following parameters: | Param name | Description | |:-----------|:-------------| | cells | Number of cells | | mode | Message mode | -Modes affect the fee calculation as follows: -- `+1024` do not create action, only estimate fee. Other modes will send a message in action phase. -- `+128` substitutes the value of the entire balance of the contract before the start of the computation phase (slightly inaccurate, since gas expenses that cannot be estimated before the completion of the computation phase are not taken into account). -- `+64` substitutes the entire balance of the incoming message as an outcoming value (slightly inaccurate, gas expenses that cannot be estimated before the computation is completed are not taken into account). -- Other modes can be found [on message modes page](/v3/documentation/smart-contracts/message-management/sending-messages#message-modes). +Modes influence the fee calculation in the following ways: +- **`+1024`**: This mode does not create an action but only estimates the fee. Other modes will send a message during the action phase. +- **`+128`**: This mode substitutes the value of the entire contract balance before the computation phase begins. This is slightly inaccurate because gas expenses, which cannot be estimated before the computation phase, are excluded. +- **`+64`**: This mode substitutes the entire balance of the incoming message as the outgoing value. This is also slightly inaccurate, as gas expenses that cannot be estimated until the computation is completed are excluded. +- Refer to the [message modes page](/v3/documentation/smart-contracts/message-management/sending-messages#message-modes) for additional modes. -It creates an output action and returns a fee for creating a message. However, it uses an unpredictable amount of gas, which can't be calculated using formulas, so how can it be calculated? Use `GASCONSUMED`: + +It creates an output action and returns the fee for creating a message. However, it uses an unpredictable amount of gas, which cannot be calculated using formulas. To measure gas usage, use `GASCONSUMED`: ```func int send_message(cell msg, int mode) impure asm "SENDMSG"; @@ -219,14 +220,14 @@ int gas_consumed() asm "GASCONSUMED"; ;; ... some code ... () calculate_forward_fee(cell msg, int mode) inline { - int gas_before = gas_consumed(); - int forward_fee = send_message(msg, mode); - int gas_usage = gas_consumed() - gas_before; + int gas_before = gas_consumed(); + int forward_fee = send_message(msg, mode); + int gas_usage = gas_consumed() - gas_before; - ;; forward fee -- fee value - ;; gas_usage -- amount of gas, used to send msg + ;; forward fee -- fee value + ;; gas_usage -- the amount of gas used to send the message } ``` -## See Also +## See also - [Stablecoin contract with fees calculation](https://github.com/ton-blockchain/stablecoin-contract) diff --git a/docs/v3/guidelines/smart-contracts/get-methods.md b/docs/v3/guidelines/smart-contracts/get-methods.md index 4909f272d3d..aa5c6cc130a 100644 --- a/docs/v3/guidelines/smart-contracts/get-methods.md +++ b/docs/v3/guidelines/smart-contracts/get-methods.md @@ -1,4 +1,4 @@ -# Get Methods +# Get methods :::note To fully benefit from this content, readers must understand the [FunC programming language](/v3/documentation/smart-contracts/func/overview) on the TON Blockchain. This knowledge is crucial for grasping the information presented here. From 4200552d4dc3a9b80626c9d36c155e70f6d43f92 Mon Sep 17 00:00:00 2001 From: Antonoff <35700168+memearchivarius@users.noreply.github.com> Date: Thu, 27 Feb 2025 12:57:05 +0300 Subject: [PATCH 03/23] mid-upd --- .../smart-contracts/fee-calculation.md | 122 ++++++++++-------- .../guidelines/smart-contracts/get-methods.md | 109 ++++++++-------- 2 files changed, 122 insertions(+), 109 deletions(-) diff --git a/docs/v3/guidelines/smart-contracts/fee-calculation.md b/docs/v3/guidelines/smart-contracts/fee-calculation.md index 42e77cdf56d..3cec20b295c 100644 --- a/docs/v3/guidelines/smart-contracts/fee-calculation.md +++ b/docs/v3/guidelines/smart-contracts/fee-calculation.md @@ -17,7 +17,7 @@ In short, `storage fees` are the costs of storing a smart contract on the blockc Use the `GETSTORAGEFEE` opcode with the following parameters: | Param name | Description | -|:-----------|:--------------------------------------------------------| +| :--------- | :------------------------------------------------------ | | cells | Number of contract cells | | bits | Number of contract bits | | is_mc | True if the source or destination is in the masterchain | @@ -46,9 +46,9 @@ int get_storage_fee(int workchain, int seconds, int bits, int cells) asm(cells b int my_storage_due() asm "DUEPAYMENT"; ;; constants from stdlib -;;; Creates an output action which reserves exactly x nanograms (if y = 0). +;;; Creates an output action which reserves exactly x nanoTONs (if y = 0). const int RESERVE_REGULAR = 0; -;;; Creates an output action which reserves at most x nanograms (if y = 2). +;;; Creates an output action which reserves at most x nanoTONs (if y = 2). ;;; Bit +2 in y means that the external action does not fail if the specified amount cannot be reserved; instead, all remaining balance is reserved. const int RESERVE_AT_MOST = 2; ;;; In the case of action failure, the transaction is bounced. No effect if RESERVE_AT_MOST (+2) is used. TVM UPGRADE 2023-07. [v3/documentation/tvm/changelog/tvm-upgrade-2023-07#sending-messages](https://ton.org/docs/#/tvm/changelog/tvm-upgrade-2023-07#sending-messages) @@ -56,7 +56,7 @@ const int RESERVE_BOUNCE_ON_ACTION_FAIL = 16; () calculate_and_reserve_at_most_storage_fee(int balance, int msg_value, int workchain, int seconds, int bits, int cells) inline { int on_balance_before_msg = my_ton_balance - msg_value; - int min_storage_fee = get_storage_fee(workchain, seconds, bits, cells); ;; can be hardcoded IF THE CONTRACT CODE WILL NOT BE UPDATED + int min_storage_fee = get_storage_fee(workchain, seconds, bits, cells); ;; can be hardcoded if the contract code will not be updated. raw_reserve(max(on_balance_before_msg, min_storage_fee + my_storage_due()), RESERVE_AT_MOST); } ``` @@ -70,9 +70,9 @@ If `storage_fee` is hardcoded, **remember to update it** during the contract upd In most cases, use the `GETGASFEE` opcode with the following parameters: | Param | Description | -|:-----------|:--------------------------------------------------------| +| :--------- | :------------------------------------------------------ | | `gas_used` | Gas amount, calculated in tests and hardcoded | -| `is_mc` | True if the source or destination is in the masterchain | +| `is_mc` | True if the source or destination is in the masterchain | ### Calculation flow @@ -96,60 +96,71 @@ const deployerJettonWallet = await userWallet(deployer.address); let initialJettonBalance = await deployerJettonWallet.getJettonBalance(); const notDeployerJettonWallet = await userWallet(notDeployer.address); let initialJettonBalance2 = await notDeployerJettonWallet.getJettonBalance(); -let sentAmount = toNano('0.5'); -let forwardAmount = toNano('0.05'); +let sentAmount = toNano("0.5"); +let forwardAmount = toNano("0.05"); let forwardPayload = beginCell().storeUint(0x1234567890abcdefn, 128).endCell(); // Ensure the payload is unique to charge cell loading for each payload. let customPayload = beginCell().storeUint(0xfedcba0987654321n, 128).endCell(); // Let's use this case for fees calculation // Embed the forward payload into the custom payload to ensure maximum gas usage during computation -const sendResult = await deployerJettonWallet.sendTransfer(deployer.getSender(), toNano('0.17'), // tons - sentAmount, notDeployer.address, - deployer.address, customPayload, forwardAmount, forwardPayload); -expect(sendResult.transactions).toHaveTransaction({ // excesses - from: notDeployerJettonWallet.address, - to: deployer.address, +const sendResult = await deployerJettonWallet.sendTransfer( + deployer.getSender(), + toNano("0.17"), // tons + sentAmount, + notDeployer.address, + deployer.address, + customPayload, + forwardAmount, + forwardPayload +); +expect(sendResult.transactions).toHaveTransaction({ + // excesses + from: notDeployerJettonWallet.address, + to: deployer.address, }); /* transfer_notification#7362d09c query_id:uint64 amount:(VarUInteger 16) sender:MsgAddress forward_payload:(Either Cell ^Cell) = InternalMsgBody; */ -expect(sendResult.transactions).toHaveTransaction({ // notification - from: notDeployerJettonWallet.address, - to: notDeployer.address, - value: forwardAmount, - body: beginCell().storeUint(Op.transfer_notification, 32).storeUint(0, 64) // default queryId - .storeCoins(sentAmount) - .storeAddress(deployer.address) - .storeUint(1, 1) - .storeRef(forwardPayload) - .endCell() +expect(sendResult.transactions).toHaveTransaction({ + // notification + from: notDeployerJettonWallet.address, + to: notDeployer.address, + value: forwardAmount, + body: beginCell() + .storeUint(Op.transfer_notification, 32) + .storeUint(0, 64) // default queryId + .storeCoins(sentAmount) + .storeAddress(deployer.address) + .storeUint(1, 1) + .storeRef(forwardPayload) + .endCell(), }); const transferTx = findTransactionRequired(sendResult.transactions, { - on: deployerJettonWallet.address, - from: deployer.address, - op: Op.transfer, - success: true + on: deployerJettonWallet.address, + from: deployer.address, + op: Op.transfer, + success: true, }); let computedGeneric: (transaction: Transaction) => TransactionComputeVm; computedGeneric = (transaction) => { - if(transaction.description.type !== "generic") - throw("Expected generic transaction"); - if(transaction.description.computePhase.type !== "vm") - throw("Compute phase expected") + if (transaction.description.type !== "generic") + throw "Expected generic transaction"; + if (transaction.description.computePhase.type !== "vm") + throw "Compute phase expected"; return transaction.description.computePhase; -} +}; let printTxGasStats: (name: string, trans: Transaction) => bigint; printTxGasStats = (name, transaction) => { - const txComputed = computedGeneric(transaction); - console.log(`${name} used ${txComputed.gasUsed} gas`); - console.log(`${name} gas cost: ${txComputed.gasFees}`); - return txComputed.gasFees; -} + const txComputed = computedGeneric(transaction); + console.log(`${name} used ${txComputed.gasUsed} gas`); + console.log(`${name} gas cost: ${txComputed.gasFees}`); + return txComputed.gasFees; +}; send_gas_fee = printTxGasStats("Jetton transfer", transferTx); ``` @@ -170,11 +181,11 @@ Generally, there are three scenarios for forward fee processing: If the message structure is deterministic, use the `GETFORWARDFEE` opcode with the following parameters: -| Param name | Description | -|:-----------|:---------------------------------------------------------------------------------------| -| cells | Number of cells | -| bits | Number of bits | -| is_mc | True if the source or destination is in the masterchain | +| Param name | Description | +| :--------- | :------------------------------------------------------ | +| cells | Number of cells | +| bits | Number of bits | +| is_mc | True if the source or destination is in the masterchain | :::info Only unique hash cells are counted for storage and forward fees. For example, three identical hash cells are counted as one. @@ -185,9 +196,9 @@ This mechanism deduplicates data: if multiple equivalent sub-cells are reference However, if the outgoing message depends significantly on the incoming structure, you may not be able to predict the fee fully. In such cases, try using the `GETORIGINALFWDFEE` opcode with the following parameters: -| Param name | Description | -|:-----------|:----------------------------------------------------| -| fwd_fee | Parsed from the incoming message | +| Param name | Description | +| :--------- | :------------------------------------------------------ | +| fwd_fee | Parsed from the incoming message | | is_mc | True if the source or destination is in the masterchain | :::caution Be careful with the `SENDMSG` opcode @@ -200,17 +211,17 @@ Avoid using it unless absolutely necessary. If even `GETORIGINALFWDFEE` cannot be used, one more option exists. Use the `SENDMSG` opcode with the following parameters: -| Param name | Description | -|:-----------|:-------------| +| Param name | Description | +| :--------- | :-------------- | | cells | Number of cells | -| mode | Message mode | +| mode | Message mode | -Modes influence the fee calculation in the following ways: -- **`+1024`**: This mode does not create an action but only estimates the fee. Other modes will send a message during the action phase. -- **`+128`**: This mode substitutes the value of the entire contract balance before the computation phase begins. This is slightly inaccurate because gas expenses, which cannot be estimated before the computation phase, are excluded. -- **`+64`**: This mode substitutes the entire balance of the incoming message as the outgoing value. This is also slightly inaccurate, as gas expenses that cannot be estimated until the computation is completed are excluded. -- Refer to the [message modes page](/v3/documentation/smart-contracts/message-management/sending-messages#message-modes) for additional modes. +Modes influence the fee calculation in the following ways: +- **`+1024`**: This mode does not create an action but only estimates the fee. Other modes will send a message during the action phase. +- **`+128`**: This mode substitutes the value of the entire contract balance before the computation phase begins. This is slightly inaccurate because gas expenses, which cannot be estimated before the computation phase, are excluded. +- **`+64`**: This mode substitutes the entire balance of the incoming message as the outgoing value. This is also slightly inaccurate, as gas expenses that cannot be estimated until the computation is completed are excluded. +- Refer to the [message modes page](/v3/documentation/smart-contracts/message-management/sending-messages#message-modes) for additional modes. It creates an output action and returns the fee for creating a message. However, it uses an unpredictable amount of gas, which cannot be calculated using formulas. To measure gas usage, use `GASCONSUMED`: @@ -223,11 +234,12 @@ int gas_consumed() asm "GASCONSUMED"; int gas_before = gas_consumed(); int forward_fee = send_message(msg, mode); int gas_usage = gas_consumed() - gas_before; - + ;; forward fee -- fee value ;; gas_usage -- the amount of gas used to send the message } ``` ## See also + - [Stablecoin contract with fees calculation](https://github.com/ton-blockchain/stablecoin-contract) diff --git a/docs/v3/guidelines/smart-contracts/get-methods.md b/docs/v3/guidelines/smart-contracts/get-methods.md index aa5c6cc130a..87099175ab1 100644 --- a/docs/v3/guidelines/smart-contracts/get-methods.md +++ b/docs/v3/guidelines/smart-contracts/get-methods.md @@ -1,14 +1,14 @@ # Get methods :::note -To fully benefit from this content, readers must understand the [FunC programming language](/v3/documentation/smart-contracts/func/overview) on the TON Blockchain. This knowledge is crucial for grasping the information presented here. +To fully benefit from this content, readers must understand the [FunC programming language](/v3/documentation/smart-contracts/func/overview/) on the TON Blockchain. This knowledge is crucial for grasping the information presented here. ::: ## Introduction Get methods are special functions in smart contracts that allow you to query specific data. Their execution doesn't cost any fees and happens outside of the blockchain. -These functions are very common in most smart contracts. For example, the default [Wallet contract](/v3/documentation/smart-contracts/contracts-specs/wallet-contracts) has several get methods, such as `seqno()`, `get_subwallet_id()` and `get_public_key()`. Wallets, SDKs, and APIs use them to fetch data about wallets. +These functions are very common in most smart contracts. For example, the default [Wallet contract](/v3/documentation/smart-contracts/contracts-specs/wallet-contracts/) has several get methods, such as `seqno()`, `get_subwallet_id()` and `get_public_key()`. Wallets, SDKs, and APIs use them to fetch data about wallets. ## Design patterns for get methods @@ -16,46 +16,47 @@ These functions are very common in most smart contracts. For example, the defaul 1. **Single data point retrieval**: A fundamental design pattern is to create methods that return individual data points from the contract's state. These methods have no parameters and return a single value. - Example: +Example: + +```func +int get_balance() method_id { +return get_data().begin_parse().preload_uint(64); +} +``` - ```func - int get_balance() method_id { - return get_data().begin_parse().preload_uint(64); - } - ``` 2. **Aggregate Data Retrieval**: Another common method is to create methods that gather multiple pieces of data from a contract's state in one call. This is useful when specific data points are often used together. You can see this approach frequently in [Jetton](#jettons) and [NFT](#nfts) contracts. - Example: +Example: - ```func - (int, slice, slice, cell) get_wallet_data() method_id { - return load_data(); - } - ``` +```func +(int, slice, slice, cell) get_wallet_data() method_id { +return load_data(); +} +``` ### Advanced get methods design patterns 1. **Computed data retrieval**: In some cases, the data that needs to be retrieved isn't stored directly in the contract's state but calculated based on the state and the input arguments. - Example: +Example: - ```func - slice get_wallet_address(slice owner_address) method_id { - (int total_supply, slice admin_address, cell content, cell jetton_wallet_code) = load_data(); - return calculate_user_jetton_wallet_address(owner_address, my_address(), jetton_wallet_code); - } - ``` +```func +slice get_wallet_address(slice owner_address) method_id { +(int total_supply, slice admin_address, cell content, cell jetton_wallet_code) = load_data(); +return calculate_user_jetton_wallet_address(owner_address, my_address(), jetton_wallet_code); +} +``` 2. **Conditional data retrieval**: Sometimes, the data that needs to be retrieved depends on certain conditions, such as the current time. - Example: +Example: - ```func - (int) get_ready_to_be_used() method_id { - int ready? = now() >= 1686459600; - return ready?; - } - ``` +```func +(int) get_ready_to_be_used() method_id { +int ready? = now() >= 1686459600; +return ready?; +} +``` ## Most common get methods @@ -69,7 +70,7 @@ int seqno() method_id { } ``` -Returns the transaction's sequence number within a specific wallet. This method is primarily used for [replay protection](/v3/guidelines/smart-contracts/howto/wallet#replay-protection---seqno). +Returns the transaction's sequence number within a specific wallet. This method is primarily used for [replay protection](/v3/guidelines/smart-contracts/howto/wallet#replay-protection---seqno/). #### get_subwallet_id() @@ -79,7 +80,7 @@ int get_subwallet_id() method_id { } ``` -- [What is Subwallet ID?](/v3/guidelines/smart-contracts/howto/wallet#subwallet-ids) +- [What is Subwallet ID?](/v3/guidelines/smart-contracts/howto/wallet#subwallet-ids/) #### get_public_key() @@ -218,8 +219,8 @@ You can call get methods on the "Get methods" tab. We will use Javascript libraries and tools for the examples below: -- [ton](https://github.com/ton-org/ton) library -- [Blueprint](/v3/documentation/smart-contracts/getting-started/javascript) SDK +- [ton](https://github.com/ton-org/ton/) library +- [Blueprint](/v3/documentation/smart-contracts/getting-started/javascript/) SDK Let's say there is some contract with the following get method: @@ -234,22 +235,22 @@ This method returns a single number loaded from the contract data. You can use the code snippet below to call this get method on a contract deployed at a known address: ```ts -import { TonClient } from '@ton/ton'; -import { Address } from '@ton/core'; +import { TonClient } from "@ton/ton"; +import { Address } from "@ton/core"; async function main() { - // Create Client - const client = new TonClient({ - endpoint: 'https://toncenter.com/api/v2/jsonRPC', - }); - - // Call get method - const result = await client.runMethod( - Address.parse('EQD4eA1SdQOivBbTczzElFmfiKu4SXNL4S29TReQwzzr_70k'), - 'get_total' - ); - const total = result.stack.readNumber(); - console.log('Total:', total); + // Create Client + const client = new TonClient({ + endpoint: "https://toncenter.com/api/v2/jsonRPC", + }); + + // Call get method + const result = await client.runMethod( + Address.parse("EQD4eA1SdQOivBbTczzElFmfiKu4SXNL4S29TReQwzzr_70k"), + "get_total" + ); + const total = result.stack.readNumber(); + console.log("Total:", total); } main(); @@ -259,7 +260,7 @@ This code will produce an output in the format `Total: 123`. The number may vary ### Testing get methods -For testing smart contracts, we can use the [Sandbox](https://github.com/ton-community/sandbox), which is installed by default in new Blueprint projects. +For testing smart contracts, we can use the [Sandbox](https://github.com/ton-community/sandbox/), which is installed by default in new Blueprint projects. First, you must add a special method in the contract wrapper to execute the get method and return the typed result. Let's say your contract is called _Counter_, and you have already implemented the method to update the stored number. Open `wrappers/Counter.ts` and add the following method: @@ -275,10 +276,10 @@ It executes the get method and retrieves the resulting stack. In this snippet, w Finally, we can use this method in our tests. Navigate to the `tests/Counter.spec.ts` and add a new test: ```ts -it('should return correct number from get method', async () => { - const caller = await blockchain.treasury('caller'); - await counter.sendNumber(caller.getSender(), toNano('0.01'), 123); - expect(await counter.getTotal()).toEqual(123); +it("should return correct number from get method", async () => { + const caller = await blockchain.treasury("caller"); + await counter.sendNumber(caller.getSender(), toNano("0.01"), 123); + expect(await counter.getTotal()).toEqual(123); }); ``` @@ -298,9 +299,9 @@ Lastly, smart contracts in TON are designed to be pure functions: they will alwa These limitations mean that one contract cannot directly access the state of another contract via its get methods. While the inability to incorporate real-time, external data into a contract's deterministic flow might seem restrictive, it is precisely these constraints that ensure the integrity and reliability of blockchain technology. -### Solutions and Workarounds +### Solutions and workarounds -In the TON Blockchain, smart contracts communicate through messages rather than directly invoking methods from one another. One can send a message to another contract requesting the execution of a specific method. These requests usually begin with special [operation codes](/v3/documentation/smart-contracts/message-management/internal-messages). +In the TON Blockchain, smart contracts communicate through messages rather than directly invoking methods from one another. One can send a message to another contract requesting the execution of a specific method. These requests usually begin with special [operation codes](/v3/documentation/smart-contracts/message-management/internal-messages/). A contract designed to handle such requests will execute the specified method and return the results in a separate message. While this approach may seem complex, it effectively streamlines communication between contracts, enhancing the scalability and performance of the blockchain network. @@ -356,7 +357,7 @@ In this example, the contract receives and processes internal messages by interp For simplicity, we used just simple little numbers 1, 2, and 3 for the operation codes. But for real projects, consider setting them according to the standard: -- [CRC32 Hashes for op-codes](/v3/documentation/data-formats/tlb/crc32) +- [CRC32 Hashes for op-codes](/v3/documentation/data-formats/tlb/crc32/) ## Common pitfalls and how to avoid them From bd6e4e4120392c6a92886be3f8bbc275e609c084 Mon Sep 17 00:00:00 2001 From: Antonoff <35700168+memearchivarius@users.noreply.github.com> Date: Tue, 4 Mar 2025 11:36:38 +0300 Subject: [PATCH 04/23] small_upd1 --- .../smart-contracts/fee-calculation.md | 2 +- .../smart-contracts/howto/wallet.md | 947 ++++++++++-------- 2 files changed, 506 insertions(+), 443 deletions(-) diff --git a/docs/v3/guidelines/smart-contracts/fee-calculation.md b/docs/v3/guidelines/smart-contracts/fee-calculation.md index 3cec20b295c..cc79147e926 100644 --- a/docs/v3/guidelines/smart-contracts/fee-calculation.md +++ b/docs/v3/guidelines/smart-contracts/fee-calculation.md @@ -194,7 +194,7 @@ This mechanism deduplicates data: if multiple equivalent sub-cells are reference [Read more about deduplication](/v3/documentation/data-formats/tlb/library-cells). ::: -However, if the outgoing message depends significantly on the incoming structure, you may not be able to predict the fee fully. In such cases, try using the `GETORIGINALFWDFEE` opcode with the following parameters: +However, if the outgoing message depends significantly on the incoming structure, you may not be able to fully predict the fee. In such cases, try using the `GETORIGINALFWDFEE` opcode with the following parameters: | Param name | Description | | :--------- | :------------------------------------------------------ | diff --git a/docs/v3/guidelines/smart-contracts/howto/wallet.md b/docs/v3/guidelines/smart-contracts/howto/wallet.md index de06ed2a491..f9ab24a0d95 100644 --- a/docs/v3/guidelines/smart-contracts/howto/wallet.md +++ b/docs/v3/guidelines/smart-contracts/howto/wallet.md @@ -5,37 +5,36 @@ description: In this tutorial, you will learn how to fully work with wallets, me import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -# Working With Wallet Smart Contracts +# Working with wallet smart contracts ## 👋 Introduction Learning how wallets and transactions work on TON before beginning smart contracts development is essential. This knowledge will help developers understand the interaction between wallets, messages, and smart contracts to implement specific development tasks. :::tip -It's recommended to get acquainted with [Types of Wallet Contracts](/v3/documentation/smart-contracts/contracts-specs/wallet-contracts) article before reading this tutorial. +Before starting this tutorial, we recommend reviewing the [Types of Wallet Contracts](/v3/documentation/smart-contracts/contracts-specs/wallet-contracts) article. ::: -In this section we’ll learn to create operations without using pre-configured functions to understand development workflows. All references necessary for the analysis of this tutorial are located in the references chapter. +This section will teach us to create operations without using pre-configured functions to understand development workflows. The references chapter contains all the necessary references for analyzing this tutorial. ## 💡 Prerequisites -This tutorial requires basic knowledge of JavaScript and TypeScript or Golang. It is also necessary to hold at least 3 TON (which can be stored in an exchange account, a non-custodial wallet, or by using the Telegram bot wallet). It is necessary to have a basic understanding of [cell](/v3/concepts/dive-into-ton/ton-blockchain/cells-as-data-storage), [addresses in TON](/v3/documentation/smart-contracts/addresses), [blockchain of blockchains](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains) to understand this tutorial. +This tutorial requires basic knowledge of JavaScript and TypeScript or Golang. It is also necessary to hold at least 3 TON (which can be stored in an exchange account, a non-custodial wallet, or the Telegram bot wallet). It is necessary to have a basic understanding of [cell](/v3/concepts/dive-into-ton/ton-blockchain/cells-as-data-storage), [addresses in TON](/v3/documentation/smart-contracts/addresses), [blockchain of blockchains](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains) to understand this tutorial. -:::info MAINNET DEVELOPMENT IS ESSENTIAL -Working with the TON Testnet often leads to deployment errors, difficulty tracking transactions, and unstable network functionality. Therefore, it could be beneficial to complete most development on the TON Mainnet to potentially avoid these issues, which might be necessary to reduce the number of transactions and thereby possibly minimize fees. +:::info MAINNET DEVELOPMENT IS ESSENTIAL +Working with the TON Testnet often leads to deployment errors, difficulty tracking transactions, and unstable network functionality. Completing most of the development on the TON Mainnet could help avoid potential issues. This may reduce the number of transactions and minimize fees. ::: -## 💿 Source Code -All code examples used in this tutorial can be found in the following [GitHub repository](https://github.com/aSpite/wallet-tutorial). +## 💿 Source code +All code examples used in this tutorial can be found in the following [GitHub repository](https://github.com/aSpite/wallet-tutorial). -## ✍️ What You Need To Get Started +## ✍️ What you need to get started - Ensure NodeJS is installed. - Specific Ton libraries are required and include: @ton/ton 13.5.1+, @ton/core 0.49.2+ and @ton/crypto 3.2.0+. -**OPTIONAL**: If you prefer to use Go instead of JS, it is necessary to install the [tonutils-go](https://github.com/xssnick/tonutils-go) library and the GoLand IDE to conduct development on TON. This library will be used in this tutorial for the GO version. - +**OPTIONAL**: If you prefer Go instead of JS, install the [tonutils-go](https://github.com/xssnick/tonutils-go) library and the GoLand IDE to develop on TON. This library will be used in this tutorial for the GO version. @@ -56,28 +55,33 @@ go get github.com/xssnick/tonutils-go/address -## ⚙ Set Your Environment +## ⚙ Set your environment + +To create a TypeScript project, you need to follow these steps in order: -In order to create a TypeScript project it's necessary to conduct the following steps in order: 1. Create an empty folder (which we’ll name WalletsTutorial). 2. Open the project folder using the CLI. 3. Use the following commands to set up your project: + ```bash npm init -y npm install typescript @types/node ts-node nodemon --save-dev npx tsc --init --rootDir src --outDir build \ --esModuleInterop --target es2020 --resolveJsonModule --lib es6 \ --module commonjs --allowJs true --noImplicitAny false --allowSyntheticDefaultImports true --strict false ``` + :::info -To help us carry out the next process a `ts-node` is used to execute TypeScript code directly without precompiling. `nodemon` is used to restart the node application automatically when file changes in the directory are detected. -::: -4. Next, remove these lines from `tsconfig.json`: +To help us carry out the following process, a `ts-node` executes TypeScript code directly without precompiling. `nodemon` restarts the node application automatically when file changes in the directory are detected. +::: 4. Next, remove these lines from `tsconfig.json`: + ```json "files": [ "\\", "\\" - ] + ] ``` + 5. Then, create a `nodemon.json` config in your project root with the following content: + ```json { "watch": ["src"], @@ -86,12 +90,16 @@ To help us carry out the next process a `ts-node` is used to execute TypeScript "exec": "npx ts-node ./src/index.ts" } ``` -6. Add this script to `package.json` instead of "test", which is added when the project is created: + +6. Add this script to `package.json` instead of "test", which is included when the project is created. + ```json "start:dev": "npx nodemon" ``` -7. Create `src` folder in the project root and `index.ts` file in this folder. + +7. Create a `src` folder in the project root and an `index.ts` file in this folder. 8. Next, the following code should be added: + ```ts async function main() { console.log("Hello, TON!"); @@ -99,44 +107,53 @@ async function main() { main().finally(() => console.log("Exiting...")); ``` -9. Run the code using terminal: + +9. Run the code using the terminal: + ```bash npm run start:dev ``` + 10. Finally, the console output will appear. ![](/img/docs/how-to-wallet/wallet_1.png) :::tip Blueprint -The TON Community created an excellent tool for automating all development processes (deployment, contract writing, testing) called [Blueprint](https://github.com/ton-org/blueprint). However, we will not be needing such a powerful tool, so it is suggested that the instructions above are followed. +The TON Community created an excellent tool for automating all development processes (deployment, contract writing, testing) called [Blueprint](https://github.com/ton-org/blueprint). However, we will not need such a powerful tool, so the instructions above should be followed. ::: -**OPTIONAL: ** When using Golang, follow these instructions:: +**OPTIONAL: ** When using Golang, follow these instructions: 1. Install the GoLand IDE. -2. Create a project folder and `go.mod` file using the following content (the **version of Go** may need to be changed to conduct this process if the current version being used is outdated): +2. Create a project folder and a `go.mod` file with the following content. If the current version of Go is outdated, update it to the required version to proceed with this process: + ``` module main go 1.20 ``` + 3. Type the following command into the terminal: + ```bash go get github.com/xssnick/tonutils-go ``` -4. Create the `main.go` file in the root of your project with following content: + +4. Create the `main.go` file in the root of your project with the following content: + ```go package main import ( - "log" + "log" ) func main() { - log.Println("Hello, TON!") + log.Println("Hello, TON!") } ``` -5. Change the name of the module in the `go.mod` to `main`. + +5. Change the module's name in the `go.mod` to `main`. 6. Run the code above until the output in the terminal is displayed. :::info @@ -144,53 +161,53 @@ It is also possible to use another IDE since GoLand isn’t free, but it is pref ::: :::warning IMPORTANT -All coding components should be added to the `main` function that was created in the [⚙ Set Your Environment](/v3/guidelines/smart-contracts/howto/wallet#-set-your-environment) section. +Add all coding components to the `main` function created in the [⚙ Set Your Environment](/v3/guidelines/smart-contracts/howto/wallet#-set-your-environment) section. -Additionally, only the imports required for a specific code section will be specified in each new section and new imports will need to be added and combined with old ones. +Only the imports required for that specific code section are specified in each new section. Combine new imports with the existing ones as needed. ::: +## 🚀 Let's get started! +In this tutorial, we’ll learn which wallets (versions 3 and 4) are most often used on TON Blockchain and get acquainted with how their smart contracts work. This will allow developers to better understand the different message types on the TON platform to make it simpler to create messages, send them to the blockchain, deploy wallets, and eventually, be able to work with high-load wallets. -## 🚀 Let's Get Started! - -In this tutorial we’ll learn which wallets (version’s 3 and 4) are most often used on TON Blockchain and get acquainted with how their smart contracts work. This will allow developers to better understand the different messages types on the TON platform to make it simpler to create messages, send them to the blockchain, deploy wallets, and eventually, be able to work with high-load wallets. - -Our main task is to build messages using various objects and functions for @ton/ton, @ton/core, @ton/crypto (ExternalMessage, InternalMessage, Signing etc.) to understand what messages look like on a bigger scale. To carry out this process we'll make use of two main wallet versions (v3 and v4) because of the fact that exchanges, non-custodial wallets, and most users only used these specific versions. +Our main task is to build messages using various objects and functions for @ton/ton, @ton/core, and @ton/crypto (ExternalMessage, InternalMessage, Signing, etc.) to understand what messages look like on a bigger scale. To carry out this process, we'll use two main wallet versions (v3 and v4) because exchanges, non-custodial wallets, and most users only use these specific versions. :::note -There may be occasions in this tutorial when there is no explanation for particular details. In these cases, more details will be provided in later stages of this tutorial. +This tutorial may not explain particular details on occasion. In these cases, more details will be provided later. -**IMPORTANT:** Throughout this tutorial [wallet v3 code](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/wallet3-code.fc) is used to better understand the wallet development process. It should be noted that version v3 has two sub-versions: r1 and r2. Currently, only the second version is being used, this means that when we refer to v3 in this document it means v3r2. +** IMPORTANT: ** Throughout this tutorial, the [wallet v3 code] (https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/wallet3-code.fc) is used to understand the wallet development process better. Version v3 has two sub-versions: r1 and r2. Currently, only the second version is being used, which means that when we refer to v3 in this document, it implies v3r2. ::: -## 💎 TON Blockchain Wallets +## 💎 TON blockchain wallets -All wallets that operate and run on TON Blockchain are actually smart contracts, in the same way, everything operating on TON is a smart contract. Like most blockchains, it is possible to deploy smart contracts on the network and customize them for different uses. Thanks to this feature, **full wallet customization is possible**. -On TON wallet smart contracts help the platform communicate with other smart contract types. However, it is important to consider how wallet communication takes place. +All wallets operating on the TON Blockchain are smart contracts, and everything running on TON functions as a smart contract. Like most blockchains, TON allows users to deploy and customize smart contracts for various purposes, enabling full wallet customization. +Wallet smart contracts on TON facilitate communication between the platform and other types of smart contracts. However, it’s essential to understand how wallet communication works. ### Wallet Communication -Generally, there are two message types on TON Blockchain: `internal` and `external`. External messages allow for the ability to send messages to the blockchain from the outside world, thus allowing for the communication with smart contracts that accept such messages. The function responsible for carrying out this process is as follows: + +Generally, TON Blockchain has two message types: `internal` and `external`. External messages allow sending messages to the blockchain from the outside world, thus allowing communication with smart contracts that accept such messages. The function responsible for carrying out this process is as follows: ```func () recv_external(slice in_msg) impure { - ;; some code + ;; some code } ``` -Before we dive into more details concerning wallets, let’s look at how wallets accept external messages. On TON, all wallets hold the owner’s `public key`, `seqno`, and `subwallet_id`. When receiving an external message, the wallet uses the `get_data()` method to retrieve data from the storage portion of the wallet. It then conducts several verification procedures and decides whether to accept the message or not. This process is conducted as follows: + +Before exploring wallets in more detail, let’s examine how wallets accept external messages. On TON, every wallet stores the owner’s `public key`, `seqno`, and `subwallet_id`. When a wallet receives an external message, it uses the `get_data()` method to retrieve data from its storage. The wallet then performs several verification checks to determine whether to accept the message. This process works as follows: ```func () recv_external(slice in_msg) impure { - var signature = in_msg~load_bits(512); ;; get signature from the message body - var cs = in_msg; - var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32)); ;; get rest values from the message body - throw_if(35, valid_until <= now()); ;; check the relevance of the message - var ds = get_data().begin_parse(); ;; get data from storage and convert it into a slice to be able to read values - var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256)); ;; read values from storage - ds.end_parse(); ;; make sure we do not have anything in ds variable - throw_unless(33, msg_seqno == stored_seqno); - throw_unless(34, subwallet_id == stored_subwallet); - throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key)); - accept_message(); + var signature = in_msg~load_bits(512); ;; get signature from the message body + var cs = in_msg; + var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32)); ;; get rest values from the message body + throw_if(35, valid_until <= now()); ;; check the relevance of the message + var ds = get_data().begin_parse(); ;; get data from storage and convert it into a slice to be able to read values + var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256)); ;; read values from storage + ds.end_parse(); ;; make sure we do not have anything in ds variable + throw_unless(33, msg_seqno == stored_seqno); + throw_unless(34, subwallet_id == stored_subwallet); + throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key)); + accept_message(); ``` > 💡 Useful links: @@ -213,27 +230,27 @@ Before we dive into more details concerning wallets, let’s look at how wallets > > ["accept_message()" in docs](/v3/documentation/smart-contracts/transaction-fees/accept-message-effects) -Now let’s take a closer look. +Now, let’s take a closer look. -### Replay Protection - Seqno +### Replay protection - seqno -Message replay protection in the wallet smart contract is directly related to the message seqno (Sequence Number) which keeps track of which messages are sent in which order. It is very important that a single message is not repeated from a wallet because it throws off the integrity of the system entirely. If we further examine smart contract code within a wallet, the `seqno` is typically handled as follows: +Message replay protection in the wallet smart contract relies on the `seqno` (Sequence Number), which tracks the order of sent messages. Preventing message repetition is critical, as duplicate messages can compromise the system’s integrity. When analyzing wallet smart contract code, the `seqno` is typically managed as follows: ```func throw_unless(33, msg_seqno == stored_seqno); ``` -This line of code above checks the `seqno`, which comes in the message and checks it with `seqno`, which is stored in a smart contract. The contract returns an error with `33 exit code` if they do not match. So if the sender passed invalid seqno, it means that he made some mistake in the message sequence, and the contract protects against such cases. +The code above compares the `seqno` from the incoming message with the `seqno` stored in the smart contract. If the values do not match, the contract returns an error with the `33 exit code`. This ensures that if the sender provides an invalid `seqno`, indicating a mistake in the message sequence, the contract prevents further processing and safeguards against such errors. :::note -It's also essential to consider that external messages can be sent by anyone. This means that if you send 1 TON to someone, someone else can repeat this message. However, when the seqno increases, the previous external message becomes invalid, and no one will be able to repeat it, thus preventing the possibility of stealing your funds. +It's also essential to consider that anyone can send external messages. If you send 1 TON to someone, someone else can repeat this message. However, when the seqno increases, the previous external message becomes invalid, and no one will be able to repeat it, thus preventing the possibility of stealing your funds. ::: ### Signature -As mentioned earlier, wallet smart contracts accept external messages. However, these messages come from the outside world and that data cannot be 100% trusted. Therefore, each wallet stores the owner's public key. The smart contract uses a public key to verify the legitimacy of the message signature when receiving an external message that the owner signed with the private key. This verifies that the message is actually from the contract owner. +As mentioned earlier, wallet smart contracts accept external messages. However, since these messages originate from the outside world, their data cannot be fully trusted. Therefore, each wallet stores the owner's public key. When the wallet receives an external message signed with the owner’s private key, the smart contract uses the public key to verify the message’s signature. This ensures the message genuinely comes from the contract owner. -To carry out this process, the wallet must first obtain the signature from the incoming message which loads the public key from storage and validates the signature using the following process: +The wallet first extracts the signature from the incoming message to perform this verification. It then loads the public key from storage and validates the signature using the following process: ```func var signature = in_msg~load_bits(512); @@ -242,17 +259,17 @@ var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_ui throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key)); ``` -And if all verification processes are completed correctly, the smart contract accepts the message and processes it: +If all verification steps succeed, the smart contract accepts and processes the message: ```func accept_message(); ``` :::info accept_message() -Because the message comes from the outside world, it does not contain the Toncoin required to pay the transaction fee. When sending TON using the accept_message() function, a gas_credit (at the time of writing its value is 10,000 gas units) is applied which allows the necessary calculations to be carried out for free if the gas does not exceed the gas_credit value. After the accept_message() function is used, all the gas spent (in TON) is taken from the balance of the smart contract. More can be read about this process [here](/v3/documentation/smart-contracts/transaction-fees/accept-message-effects). +Since external messages do not include the Toncoin required to pay transaction fees, the `accept_message()` function applies a `gas_credit` (currently valued at 10,000 gas units). This allows the contract to perform necessary calculations for free, provided the gas usage does not exceed the `gas_credit` limit. After invoking `accept_message()`, the smart contract deducts all gas costs (in TON) from its balance. You can read more about this process [here](/v3/documentation/smart-contracts/transaction-fees/accept-message-effects). ::: -### Transaction Expiration +### Transaction expiration Another step used to check the validity of external messages is the `valid_until` field. As you can see from the variable name, this is the time in UNIX before the message is valid. If this verification process fails, the contract completes the processing of the transaction and returns the 35 exit code follows: @@ -263,17 +280,16 @@ throw_if(35, valid_until <= now()); This algorithm works to protect against the susceptibility of various errors when the message is no longer valid but was still sent to the blockchain for an unknown reason. -### Wallet v3 and Wallet v4 Differences +### Wallet v3 and wallet v4 differences The only difference between Wallet v3 and Wallet v4 is that Wallet v4 makes use of `plugins` that can be installed and deleted. These plugins are special smart contracts which are able to request a specific number of TON at a specific time from a wallet smart contract. Wallet smart contracts, in turn, will send the required amount of TON in response without the need for the owner to participate. This is similar to the **subscription model** for which plugins are created. We will not learn these details, because this is out of the scope of this tutorial. -### How Wallets facilitate communication with Smart Contracts +### How wallets facilitate communication with smart contracts As we discussed earlier, a wallet smart contract accepts external messages, validates them and accepts them if all checks are passed. The contract then starts the loop of retrieving messages from the body of external messages then creates internal messages and sends them to the blockchain as follows: - ```func cs~touch(); while (cs.slice_refs()) { @@ -296,15 +312,16 @@ Since a **maximum of 4 references** can be stored in one cell, we can send a max > > ["load_ref()" in docs](/v3/documentation/smart-contracts/func/docs/stdlib/#load_ref) -## 📬 External and Internal Messages +## 📬 External and internal messages In this section, we’ll learn more about `internal` and `external` messages and we’ll create messages and send them to the network to minimize the use of pre-cooked functions. To carry out this process it is necessary to make use of a ready-made wallet to make the task easier. To accomplish this: -1. Install the [wallet app](/v3/concepts/dive-into-ton/ton-ecosystem/wallet-apps) (e.g., Tonkeeper is used by the author) + +1. Install the [wallet app](/v3/concepts/dive-into-ton/ton-ecosystem/wallet-apps) (e.g., Tonkeeper is used by the author) 2. Switch wallet app to v3r2 address version -3. Deposit 1 TON into the wallet -4. Send the message to another address (you can send to yourself, to the same wallet). +3. Deposit 1 TON into the wallet +4. Send the message to another address (you can send to yourself, to the same wallet). This way, the Tonkeeper wallet app will deploy the wallet contract and we can use it for the following steps. @@ -328,7 +345,6 @@ Initially, each message must first store `CommonMsgInfo` ([TL-B](https://github. By reading `block.tlb` file, we can notice three types of CommonMsgInfo: `int_msg_info$0`, `ext_in_msg_info$10`, `ext_out_msg_info$11`. We will not go into specific details detailing the specificities of the `ext_out_msg_info` TL-B structure. That said, it is an external message type that a smart contract can send for using as external logs. For examples of this format, consider having a closer look at the [Elector](https://tonscan.org/address/Ef8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM0vF) contract. - [Looking at TL-B](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L127-L128), you’ll notice that **only the CommonMsgInfo is available when used with the ext_in_msg_info type**. This is because message fields such as `src`, `created_lt`, `created_at`, and others are rewritten by validators during transaction handling. In this case, the `src` field in message is most important because when messages are sent, the sender is unknown, and is written by validators during verification. This ensures that the address in the `src` field is correct and cannot be manipulated. However, the `CommonMsgInfo` structure only supports the `MsgAddress` specification, but the sender’s address is typically unknown and it is required to write the `addr_none` (two zero bits `00`). In this case, the `CommonMsgInfoRelaxed` structure is used, which supports the `addr_none` address. For the `ext_in_msg_info` (used for incoming external messages), the `CommonMsgInfo` structure is used because these message types don’t make use of a sender and always use the [MsgAddressExt](https://hub.com/ton/ton.blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L100) structure (the `addr_none$00` meaning two zero bits), which means there is no need to overwrite the data. @@ -354,7 +370,7 @@ Let’s first consider `0x18` and `0x10` (x - hexadecimal), which are hexadecima ```func var msg = begin_cell() - .store_uint(0, 1) ;; this bit indicates that we send an internal message according to int_msg_info$0 + .store_uint(0, 1) ;; this bit indicates that we send an internal message according to int_msg_info$0 .store_uint(1, 1) ;; IHR Disabled .store_uint(1, 1) ;; or .store_uint(0, 1) for 0x10 | bounce .store_uint(0, 1) ;; bounced @@ -364,22 +380,25 @@ var msg = begin_cell() .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) ;; store something as a body ``` + Now let’s go through each option in detail: -Option | Explanation -:---: | :---: -IHR Disabled | Currently, this option is disabled (which means we store 1) because Instant Hypercube Routing is not fully implemented. In addition, this will be needed when a large number of [Shardchains](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains#many-accountchains-shards) are live on the network. More can be read about the IHR Disabled option in the [tblkch.pdf](https://ton.org/tblkch.pdf) (chapter 2). -Bounce | While sending messages, a variety of errors can occur during smart contract processing. To avoid losing TON, it is necessary to set the Bounce option to 1 (true). In this case, if any contract errors occur during transaction processing, the message will be returned to the sender, and the same amount of TON will be received minus fees. More can be read about non-bounceable messages [here](/v3/documentation/smart-contracts/message-management/non-bounceable-messages). -Bounced | Bounced messages are messages that are returned to the sender because an error occurred while processing the transaction with a smart contract. This option tells you whether the message received is bounced or not. -Src | The Src is the sender address. In this case, two zero bits are written to indicate the `addr_none` address. +| Option | Explanation | +| :----------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| IHR Disabled | Currently, this option is disabled (which means we store 1) because Instant Hypercube Routing is not fully implemented. In addition, this will be needed when a large number of [Shardchains](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains#many-accountchains-shards) are live on the network. More can be read about the IHR Disabled option in the [tblkch.pdf](https://ton.org/tblkch.pdf) (chapter 2). | +| Bounce | While sending messages, a variety of errors can occur during smart contract processing. To avoid losing TON, it is necessary to set the Bounce option to 1 (true). In this case, if any contract errors occur during transaction processing, the message will be returned to the sender, and the same amount of TON will be received minus fees. More can be read about non-bounceable messages [here](/v3/documentation/smart-contracts/message-management/non-bounceable-messages). | +| Bounced | Bounced messages are messages that are returned to the sender because an error occurred while processing the transaction with a smart contract. This option tells you whether the message received is bounced or not. | +| Src | The Src is the sender address. In this case, two zero bits are written to indicate the `addr_none` address. | The next two lines of code: + ```func ... .store_slice(to_address) .store_coins(amount) ... ``` + - we specify the recipient and the number of TON to be sent. Finally, let’s look at the remaining lines of code: @@ -395,15 +414,16 @@ Finally, let’s look at the remaining lines of code: .store_uint(0, 1) ;; Message body ;; store something as a body ``` -Option | Explanation -:---: | :---: -Extra currency | This is a native implementation of existing jettons and is not currently in use. -IHR fee | As mentioned, the IHR is not currently in use, so this fee is always zero. More can be read about this in the [tblkch.pdf](https://ton.org/tblkch.pdf) (3.1.8). -Forwarding fee | A forwarding message fee. More can be read about this in the [fees documentation](/v3/documentation/smart-contracts/transaction-fees/fees-low-level#transactions-and-phases). -Logical time of creation | The time used to create the correct messages queue. -UNIX time of creation | The time the message was created in UNIX. -State Init | Code and source data for deploying a smart contract. If the bit is set to `0`, it means that we do not have a State Init. But if it is set to `1`, then another bit needs to be written which indicates whether the State Init is stored in the same cell (0) or written as a reference (1). -Message body | This part defines how the message body is stored. At times the message body is too large to fit into the message itself. In this case, it should be stored as a **reference** whereby the bit is set to `1` to show that the body is used as a reference. If the bit is `0`, the body is in the same cell as the message. + +| Option | Explanation | +| :----------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| Extra currency | This is a native implementation of existing jettons and is not currently in use. | +| IHR fee | As mentioned, the IHR is not currently in use, so this fee is always zero. More can be read about this in the [tblkch.pdf](https://ton.org/tblkch.pdf) (3.1.8). | +| Forwarding fee | A forwarding message fee. More can be read about this in the [fees documentation](/v3/documentation/smart-contracts/transaction-fees/fees-low-level#transactions-and-phases). | +| Logical time of creation | The time used to create the correct messages queue. | +| UNIX time of creation | The time the message was created in UNIX. | +| State Init | Code and source data for deploying a smart contract. If the bit is set to `0`, it means that we do not have a State Init. But if it is set to `1`, then another bit needs to be written which indicates whether the State Init is stored in the same cell (0) or written as a reference (1). | +| Message body | This part defines how the message body is stored. At times the message body is too large to fit into the message itself. In this case, it should be stored as a **reference** whereby the bit is set to `1` to show that the body is used as a reference. If the bit is `0`, the body is in the same cell as the message. | The values outlined above (including src) excluding the State Init and the Message Body bits, are rewritten by validators. @@ -418,7 +438,7 @@ First, let’s say a user wants to send 0.5 TON to themselves with the text "**H ```js -import { beginCell } from '@ton/core'; +import { beginCell } from "@ton/core"; let internalMessageBody = beginCell() .storeUint(0, 32) // write 32 zero bits to indicate that a text comment will follow @@ -451,9 +471,9 @@ Next, the `InternalMessage` is created according to the information we have stud ```js -import { toNano, Address } from '@ton/ton'; +import { toNano, Address } from "@ton/ton"; -const walletAddress = Address.parse('put your wallet address'); +const walletAddress = Address.parse("put your wallet address"); let internalMessage = beginCell() .storeUint(0, 1) // indicate that it is an internal message -> int_msg_info$0 @@ -515,20 +535,20 @@ It is necessary to retrieve the `seqno` (sequence number) of our wallet smart co ```js -import { TonClient } from '@ton/ton'; -import { mnemonicToWalletKey } from '@ton/crypto'; +import { TonClient } from "@ton/ton"; +import { mnemonicToWalletKey } from "@ton/crypto"; const client = new TonClient({ endpoint: "https://toncenter.com/api/v2/jsonRPC", // you can replace it on https://testnet.toncenter.com/api/v2/jsonRPC for testnet - apiKey: "put your api key" // you can get an api key from @tonapibot bot in Telegram + apiKey: "put your api key", // you can get an api key from @tonapibot bot in Telegram }); -const mnemonic = 'put your mnemonic'; // word1 word2 word3 +const mnemonic = "put your mnemonic"; // word1 word2 word3 let getMethodResult = await client.runMethod(walletAddress, "seqno"); // run "seqno" GET method from your wallet contract let seqno = getMethodResult.stack.readNumber(); // get seqno from response -const mnemonicArray = mnemonic.split(' '); // get array from string -const keyPair = await mnemonicToWalletKey(mnemonicArray); // get Secret and Public keys from mnemonic +const mnemonicArray = mnemonic.split(" "); // get array from string +const keyPair = await mnemonicToWalletKey(mnemonicArray); // get Secret and Public keys from mnemonic ``` @@ -587,7 +607,7 @@ Therefore, the `seqno`, `keys`, and `internal message` need to be sent. Now we n ```js -import { sign } from '@ton/crypto'; +import { sign } from "@ton/crypto"; let toSign = beginCell() .storeUint(698983191, 32) // subwallet_id | We consider this further @@ -632,7 +652,6 @@ body := cell.BeginCell(). Note that here no `.endCell()` was used in the definition of the `toSign`. The fact is that in this case it is necessary **to transfer toSign content directly to the message body**. If writing a cell was required, it would have to be stored as a reference. - :::tip Wallet V4 In addition to basic verification process we learned bellow for the Wallet V3, Wallet V4 smart contracts [extracts the opcode to determine whether a simple translation or a message associated with the plugin](https://github.com/ton-blockchain/wallet-contract/blob/4111fd9e3313ec17d99ca9b5b1656445b5b49d8f/func/wallet-v4-code.fc#L94-L100) is required. To match this version, it is necessary to add the `storeUint(0, 8).` (JS/TS), `MustStoreUInt(0, 8).` (Golang) functions after writing the seqno (sequence number) and before specifying the transaction mode. ::: @@ -674,12 +693,12 @@ externalMessage := cell.BeginCell(). -Option | Explanation -:---: | :---: -Src | The sender address. Since an incoming external message cannot have a sender, there will always be 2 zero bits (an addr_none [TL-B](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L100)). -Import Fee | The fee used to pay for importing incoming external messages. -State Init | Unlike the Internal Message, the State Init within the external message is needed **to deploy a contract from the outside world**. The State Init used in conjunction with the Internal Message allows one contract to deploy another. -Message Body | The message that must be sent to the contract for processing. +| Option | Explanation | +| :----------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| Src | The sender address. Since an incoming external message cannot have a sender, there will always be 2 zero bits (an addr_none [TL-B](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L100)). | +| Import Fee | The fee used to pay for importing incoming external messages. | +| State Init | Unlike the Internal Message, the State Init within the external message is needed **to deploy a contract from the outside world**. The State Init used in conjunction with the Internal Message allows one contract to deploy another. | +| Message Body | The message that must be sent to the contract for processing. | :::tip 0b10 0b10 (b - binary) denotes a binary record. In this process, two bits are stored: `1` and `0`. Thus we specify that it's `ext_in_msg_info$10`. @@ -691,7 +710,7 @@ Now we have a completed message that is ready to be sent to our contract. To acc ```js -console.log(externalMessage.toBoc().toString("base64")) +console.log(externalMessage.toBoc().toString("base64")); client.sendFile(externalMessage.toBoc()); ``` @@ -737,17 +756,16 @@ The first thing needed to correctly create a wallet is to retrieve a `private` a This is accomplished as follows: - ```js -import { mnemonicToWalletKey, mnemonicNew } from '@ton/crypto'; +import { mnemonicToWalletKey, mnemonicNew } from "@ton/crypto"; // const mnemonicArray = 'put your mnemonic'.split(' ') // get our mnemonic as array const mnemonicArray = await mnemonicNew(24); // 24 is the number of words in a seed phrase const keyPair = await mnemonicToWalletKey(mnemonicArray); // extract private and public keys from mnemonic -console.log(mnemonicArray) // if we want, we can print our mnemonic +console.log(mnemonicArray); // if we want, we can print our mnemonic ``` @@ -768,10 +786,10 @@ import ( mnemonic := wallet.NewSeed() // get new mnemonic // The following three lines will extract the private key using the mnemonic phrase. We will not go into cryptographic details. It has all been implemented in the tonutils-go library, but it immediately returns the finished object of the wallet with the address and ready methods. So we’ll have to write the lines to get the key separately. Goland IDE will automatically import all required libraries (crypto, pbkdf2 and others). -mac := hmac.New(sha512.New, []byte(strings.Join(mnemonic, " "))) +mac := hmac.New(sha512.New, []byte(strings.Join(mnemonic, " "))) hash := mac.Sum(nil) k := pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries "TON default seed" is used as salt when getting keys -// 32 is a key len +// 32 is a key len privateKey := ed25519.NewKeyFromSeed(k) // get private key publicKey := privateKey.Public().(ed25519.PublicKey) // get public key from private key @@ -792,7 +810,7 @@ It is necessary to output the generated mnemonic seed phrase to the console then One of the most notable benefits of wallets being smart contracts is the ability to create **a vast number of wallets** using just one private key. This is because the addresses of smart contracts on TON Blockchain are computed using several factors including the `stateInit`. The stateInit contains the `code` and `initial data`, which is stored in the blockchain’s smart contract storage. -By changing just one bit within the stateInit, a different address can be generated. That is why the `subwallet_id` was initially created. The `subwallet_id` is stored in the contract storage and it can be used to create many different wallets (with different subwallet IDs) with one private key. This functionality can be very useful when integrating various wallet types with centralized service such as exchanges. +By changing just one bit within the stateInit, a different address can be generated. That is why the `subwallet_id` was initially created. The `subwallet_id` is stored in the contract storage and it can be used to create many different wallets (with different subwallet IDs) with one private key. This functionality can be very useful when integrating various wallet types with centralized service such as exchanges. The default subwallet_id value is `698983191` according to the [line of code](https://github.com/ton-blockchain/ton/blob/4b940f8bad9c2d3bf44f196f6995963c7cee9cc3/tonlib/tonlib/TonlibClient.cpp#L2420) below taken from the TON Blockchain’s source code: @@ -843,7 +861,7 @@ npm i --save @ton-community/func-js We’ll only use JavaScript to compile code, as the libraries for compiling code are JavaScript based. However, after compiling is finalized, as long as we have the **base64 output** of our cell, it is possible to use this compiled code in languages such as Go and others. -First, we need to create two files: `wallet_v3.fc` and `stdlib.fc`. The compiler works with the stdlib.fc library. All necessary and basic functions, which correspond with the `asm` instructions were created in the library. The stdlib.fc file can be downloaded [here](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/stdlib.fc). In the `wallet_v3.fc` file it is necessary to copy the code above. +First, we need to create two files: `wallet_v3.fc` and `stdlib.fc`. The compiler works with the stdlib.fc library. All necessary and basic functions, which correspond with the `asm` instructions were created in the library. The stdlib.fc file can be downloaded [here](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/stdlib.fc). In the `wallet_v3.fc` file it is necessary to copy the code above. Now we have the following structure for the project we are creating: @@ -872,28 +890,30 @@ Remember to add the following line to the beginning of the `wallet_v3.fc` file t Now let’s write code to compile our smart contract and run it using the `npm run start:dev`: ```js -import { compileFunc } from '@ton-community/func-js'; -import fs from 'fs'; // we use fs for reading content of files -import { Cell } from '@ton/core'; +import { compileFunc } from "@ton-community/func-js"; +import fs from "fs"; // we use fs for reading content of files +import { Cell } from "@ton/core"; const result = await compileFunc({ -targets: ['wallet_v3.fc'], // targets of your project -sources: { - "stdlib.fc": fs.readFileSync('./src/stdlib.fc', { encoding: 'utf-8' }), - "wallet_v3.fc": fs.readFileSync('./src/wallet_v3.fc', { encoding: 'utf-8' }), -} + targets: ["wallet_v3.fc"], // targets of your project + sources: { + "stdlib.fc": fs.readFileSync("./src/stdlib.fc", { encoding: "utf-8" }), + "wallet_v3.fc": fs.readFileSync("./src/wallet_v3.fc", { + encoding: "utf-8", + }), + }, }); -if (result.status === 'error') { -console.error(result.message) -return; +if (result.status === "error") { + console.error(result.message); + return; } const codeCell = Cell.fromBoc(Buffer.from(result.codeBoc, "base64"))[0]; // get buffer from base64 encoded BOC and get cell from this buffer // now we have base64 encoded BOC with compiled code in result.codeBoc -console.log('Code BOC: ' + result.codeBoc); -console.log('\nHash: ' + codeCell.hash().toString('base64')); // get the hash of cell and convert in to base64 encoded string. We will need it further +console.log("Code BOC: " + result.codeBoc); +console.log("\nHash: " + codeCell.hash().toString("base64")); // get the hash of cell and convert in to base64 encoded string. We will need it further ``` The result will be the following output in the terminal: @@ -919,7 +939,7 @@ base64BOC := "te6ccgEBCAEAhgABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQCW8oMI1xgg0x/TH9MfAv codeCellBytes, _ := base64.StdEncoding.DecodeString(base64BOC) // decode base64 in order to get byte array codeCell, err := cell.FromBOC(codeCellBytes) // get cell with code from byte array if err != nil { // check if there are any error - panic(err) + panic(err) } log.Println("Hash:", base64.StdEncoding.EncodeToString(codeCell.Hash())) // get the hash of our cell, encode it to base64 because it has []byte type and output to the terminal @@ -928,8 +948,6 @@ log.Println("Hash:", base64.StdEncoding.EncodeToString(codeCell.Hash())) // get - - The result will be the following output in the terminal: ```text @@ -942,13 +960,13 @@ After the above processes are complete it is confirmed that the correct code is Before building a message it is important to understand what a State Init is. First let’s go through the [TL-B scheme](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L141-L143): -Option | Explanation -:---: | :---: -split_depth | This option is intended for highly loaded smart contracts that can be split and located on several [shardchains](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains#many-accountchains-shards). More information detailing how this works can be found in the [tblkch.pdf](https://ton.org/tblkch.pdf) (4.1.6). Only a `0` bit is stored since it is being used only within a wallet smart contract. -special | Used for TicTok. These smart contracts are automatically called for each block and are not needed for regular smart contracts. Information about this can be found in [this section](/v3/documentation/data-formats/tlb/transaction-layout#tick-tock) or in [tblkch.pdf](https://ton.org/tblkch.pdf) (4.1.6). Only a `0` bit is stored within this specification because we do not need such a function. -code | `1` bit means the presence of the smart contract code as a reference. -data | `1` bit means the presence of the smart contract data as a reference. -library | A library that operates on the [masterchain](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains#masterchain-blockchain-of-blockchains) and can be used by different smart contracts. This will not be used for wallet, so its bit is set to `0`. Information about this can be found in [tblkch.pdf](https://ton.org/tblkch.pdf) (1.8.4). +| Option | Explanation | +| :---------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| split_depth | This option is intended for highly loaded smart contracts that can be split and located on several [shardchains](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains#many-accountchains-shards). More information detailing how this works can be found in the [tblkch.pdf](https://ton.org/tblkch.pdf) (4.1.6). Only a `0` bit is stored since it is being used only within a wallet smart contract. | +| special | Used for TicTok. These smart contracts are automatically called for each block and are not needed for regular smart contracts. Information about this can be found in [this section](/v3/documentation/data-formats/tlb/transaction-layout#tick-tock) or in [tblkch.pdf](https://ton.org/tblkch.pdf) (4.1.6). Only a `0` bit is stored within this specification because we do not need such a function. | +| code | `1` bit means the presence of the smart contract code as a reference. | +| data | `1` bit means the presence of the smart contract data as a reference. | +| library | A library that operates on the [masterchain](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains#masterchain-blockchain-of-blockchains) and can be used by different smart contracts. This will not be used for wallet, so its bit is set to `0`. Information about this can be found in [tblkch.pdf](https://ton.org/tblkch.pdf) (1.8.4). | Next we’ll prepare the `initial data`, which will be present in our contract’s storage immediately after deployment: @@ -956,7 +974,7 @@ Next we’ll prepare the `initial data`, which will be present in our contract ```js -import { beginCell } from '@ton/core'; +import { beginCell } from "@ton/core"; const dataCell = beginCell() .storeUint(0, 32) // Seqno @@ -985,7 +1003,7 @@ At this stage, both the contract `code` and its `initial data` is present. With ```js -import { Address } from '@ton/core'; +import { Address } from "@ton/core"; const stateInit = beginCell() .storeBit(0) // No split_depth @@ -1029,9 +1047,9 @@ log.Println("Contract address:", contractAddress.String()) // Output contract ad Using the State Init, we can now build the message and send it to the blockchain. :::warning - To carry out this process, **a minimum wallet balance of 0.1 TON** is required (the balance can be less, but this amount is guaranteed to be sufficient). To accomplish this, we’ll need to run the code mentioned earlier in the tutorial, obtain the correct wallet address, and send 0.1 TON to this address. Alternatively, you can send this sum manually via your wallet app before sending the deployment message itself. +To carry out this process, **a minimum wallet balance of 0.1 TON** is required (the balance can be less, but this amount is guaranteed to be sufficient). To accomplish this, we’ll need to run the code mentioned earlier in the tutorial, obtain the correct wallet address, and send 0.1 TON to this address. Alternatively, you can send this sum manually via your wallet app before sending the deployment message itself. - Deployment by external messages is presented here mostly for educational purposes; in practice, it's much more convenient to [deploy smart contracts via Wallets](/v3/guidelines/smart-contracts/howto/wallet#contract-deployment-via-wallet), which will be described later. +Deployment by external messages is presented here mostly for educational purposes; in practice, it's much more convenient to [deploy smart contracts via Wallets](/v3/guidelines/smart-contracts/howto/wallet#contract-deployment-via-wallet), which will be described later. ::: Let’s start with building the message similar to the one we built **in the previous section**: @@ -1040,8 +1058,8 @@ Let’s start with building the message similar to the one we built **in the pre ```js -import { sign } from '@ton/crypto'; -import { toNano } from '@ton/core'; +import { sign } from "@ton/crypto"; +import { toNano } from "@ton/core"; const internalMessageBody = beginCell() .storeUint(0, 32) @@ -1050,7 +1068,9 @@ const internalMessageBody = beginCell() const internalMessage = beginCell() .storeUint(0x10, 6) // no bounce - .storeAddress(Address.parse("put your first wallet address from were you sent 0.1 TON")) + .storeAddress( + Address.parse("put your first wallet address from were you sent 0.1 TON") + ) .storeCoins(toNano("0.03")) .storeUint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1) // We store 1 that means we have body as a reference .storeRef(internalMessageBody) @@ -1065,10 +1085,7 @@ const toSign = beginCell() .storeRef(internalMessage); const signature = sign(toSign.endCell().hash(), keyPair.secretKey); -const body = beginCell() - .storeBuffer(signature) - .storeBuilder(toSign) - .endCell(); +const body = beginCell().storeBuffer(signature).storeBuilder(toSign).endCell(); ``` @@ -1160,11 +1177,11 @@ Finally, we can send our message to the blockchain to deploy our wallet and use ```js -import { TonClient } from '@ton/ton'; +import { TonClient } from "@ton/ton"; const client = new TonClient({ endpoint: "https://toncenter.com/api/v2/jsonRPC", - apiKey: "put your api key" // you can get an api key from @tonapibot bot in Telegram + apiKey: "put your api key", // you can get an api key from @tonapibot bot in Telegram }); client.sendFile(externalMessage.toBoc()); @@ -1222,23 +1239,23 @@ To accomplish this, it is necessary to create 4 different internal messages. We ```js -import { Cell } from '@ton/core'; +import { Cell } from "@ton/core"; const internalMessagesAmount = ["0.01", "0.02", "0.03", "0.04"]; const internalMessagesComment = [ "Hello, TON! #1", "Hello, TON! #2", "", // Let's leave the third message without comment - "Hello, TON! #4" -] + "Hello, TON! #4", +]; const destinationAddresses = [ "Put any address that belongs to you", "Put any address that belongs to you", "Put any address that belongs to you", - "Put any address that belongs to you" -] // All 4 addresses can be the same + "Put any address that belongs to you", +]; // All 4 addresses can be the same -let internalMessages:Cell[] = []; // array for our internal messages +let internalMessages: Cell[] = []; // array for our internal messages ``` @@ -1269,23 +1286,23 @@ var internalMessages [len(internalMessagesAmount)]*cell.Cell // array for our in -[Sending mode](/v3/documentation/smart-contracts/message-management/sending-messages#message-modes) for all messages is set to `mode 3`. However, if different modes are required an array can be created to fulfill different purposes. +[Sending mode](/v3/documentation/smart-contracts/message-management/sending-messages#message-modes) for all messages is set to `mode 3`. However, if different modes are required an array can be created to fulfill different purposes. ```js -import { Address, beginCell, toNano } from '@ton/core'; +import { Address, beginCell, toNano } from "@ton/core"; for (let index = 0; index < internalMessagesAmount.length; index++) { const amount = internalMessagesAmount[index]; - + let internalMessage = beginCell() - .storeUint(0x18, 6) // bounce - .storeAddress(Address.parse(destinationAddresses[index])) - .storeCoins(toNano(amount)) - .storeUint(0, 1 + 4 + 4 + 64 + 32 + 1); - + .storeUint(0x18, 6) // bounce + .storeAddress(Address.parse(destinationAddresses[index])) + .storeCoins(toNano(amount)) + .storeUint(0, 1 + 4 + 4 + 64 + 32 + 1); + /* At this stage, it is not clear if we will have a message body. So put a bit only for stateInit, and if we have a comment, in means @@ -1293,8 +1310,8 @@ for (let index = 0; index < internalMessagesAmount.length; index++) { body as a reference. */ - if(internalMessagesComment[index] != "") { - internalMessage.storeBit(1) // we store Message Body as a reference + if (internalMessagesComment[index] != "") { + internalMessage.storeBit(1); // we store Message Body as a reference let internalMessageBody = beginCell() .storeUint(0, 32) @@ -1302,15 +1319,13 @@ for (let index = 0; index < internalMessagesAmount.length; index++) { .endCell(); internalMessage.storeRef(internalMessageBody); - } - else - /* + } else internalMessage.storeBit(0); + /* Since we do not have a message body, we indicate that the message body is in this message, but do not write it, which means it is absent. In that case, just set the bit to 0. */ - internalMessage.storeBit(0); - + internalMessages.push(internalMessage.endCell()); } ``` @@ -1334,9 +1349,9 @@ for i := 0; i < len(internalMessagesAmount); i++ { MustStoreUInt(0, 1+4+4+64+32+1) /* - At this stage, it is not clear if we will have a message body. - So put a bit only for stateInit, and if we have a comment, in means - we have a body message. In that case, set the bit to 1 and store the + At this stage, it is not clear if we will have a message body. + So put a bit only for stateInit, and if we have a comment, in means + we have a body message. In that case, set the bit to 1 and store the body as a reference. */ @@ -1370,27 +1385,27 @@ Now let's use our knowledge from [chapter two](/v3/guidelines/smart-contracts/ho ```js -import { TonClient } from '@ton/ton'; -import { mnemonicToWalletKey } from '@ton/crypto'; +import { TonClient } from "@ton/ton"; +import { mnemonicToWalletKey } from "@ton/crypto"; -const walletAddress = Address.parse('put your wallet address'); +const walletAddress = Address.parse("put your wallet address"); const client = new TonClient({ endpoint: "https://toncenter.com/api/v2/jsonRPC", - apiKey: "put your api key" // you can get an api key from @tonapibot bot in Telegram + apiKey: "put your api key", // you can get an api key from @tonapibot bot in Telegram }); -const mnemonic = 'put your mnemonic'; // word1 word2 word3 +const mnemonic = "put your mnemonic"; // word1 word2 word3 let getMethodResult = await client.runMethod(walletAddress, "seqno"); // run "seqno" GET method from your wallet contract let seqno = getMethodResult.stack.readNumber(); // get seqno from response -const mnemonicArray = mnemonic.split(' '); // get array from string -const keyPair = await mnemonicToWalletKey(mnemonicArray); // get Secret and Public keys from mnemonic +const mnemonicArray = mnemonic.split(" "); // get array from string +const keyPair = await mnemonicToWalletKey(mnemonicArray); // get Secret and Public keys from mnemonic let toSign = beginCell() .storeUint(698983191, 32) // subwallet_id .storeUint(Math.floor(Date.now() / 1e3) + 60, 32) // Message expiration time, +60 = 1 minute .storeUint(seqno, 32); // store seqno - // Do not forget that if we use Wallet V4, we need to add .storeUint(0, 8) +// Do not forget that if we use Wallet V4, we need to add .storeUint(0, 8) ``` @@ -1449,7 +1464,7 @@ toSign := cell.BeginCell(). MustStoreUInt(698983191, 32). // subwallet_id | We consider this further MustStoreUInt(uint64(time.Now().UTC().Unix()+60), 32). // message expiration time, +60 = 1 minute MustStoreUInt(seqno.Uint64(), 32) // store seqno - // Do not forget that if we use Wallet V4, we need to add MustStoreUInt(0, 8). + // Do not forget that if we use Wallet V4, we need to add MustStoreUInt(0, 8). ``` @@ -1463,8 +1478,8 @@ Next, we’ll add our messages that we built earlier in the loop: ```js for (let index = 0; index < internalMessages.length; index++) { const internalMessage = internalMessages[index]; - toSign.storeUint(3, 8) // store mode of our internal message - toSign.storeRef(internalMessage) // store our internalMessage as a reference + toSign.storeUint(3, 8); // store mode of our internal message + toSign.storeRef(internalMessage); // store our internalMessage as a reference } ``` @@ -1488,24 +1503,24 @@ Now that the above processes are complete, let’s **sign** our message, **build ```js -import { sign } from '@ton/crypto'; +import { sign } from "@ton/crypto"; let signature = sign(toSign.endCell().hash(), keyPair.secretKey); // get the hash of our message to wallet smart contract and sign it to get signature let body = beginCell() - .storeBuffer(signature) // store signature - .storeBuilder(toSign) // store our message - .endCell(); + .storeBuffer(signature) // store signature + .storeBuilder(toSign) // store our message + .endCell(); let externalMessage = beginCell() - .storeUint(0b10, 2) // ext_in_msg_info$10 - .storeUint(0, 2) // src -> addr_none - .storeAddress(walletAddress) // Destination address - .storeCoins(0) // Import Fee - .storeBit(0) // No State Init - .storeBit(1) // We store Message Body as a reference - .storeRef(body) // Store Message Body as a reference - .endCell(); + .storeUint(0b10, 2) // ext_in_msg_info$10 + .storeUint(0, 2) // src -> addr_none + .storeAddress(walletAddress) // Destination address + .storeCoins(0) // Import Fee + .storeBit(0) // No State Init + .storeBit(1) // We store Message Body as a reference + .storeRef(body) // Store Message Body as a reference + .endCell(); client.sendFile(externalMessage.toBoc()); ``` @@ -1557,7 +1572,7 @@ After this process is completed it is possible to use a TON blockchain explorer In addition to regular messages, users often send NFTs to each other. Unfortunately, not all libraries contain methods that are tailored for use with this type of smart contract. Therefore, it is necessary to create code that will allow us to build a message for sending NFTs. First, let's become more familiar with the TON NFT [standard](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md). -Especially, we need to understand TL-B for [NFT Transfers](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md#1-transfer) in details. +Especially, we need to understand TL-B for [NFT Transfers](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md#1-transfer) in details. - `query_id`: Query ID has no value in terms of message processing. The NFT contract doesn't validate it; it only reads it. This value can be useful when a service wants to assign a specific query ID to each of its messages for identification purposes. Therefore, we will set it to 0. @@ -1575,13 +1590,17 @@ Now let's build the message itself: ```js -import { Address, beginCell, toNano } from '@ton/core'; +import { Address, beginCell, toNano } from "@ton/core"; -const destinationAddress = Address.parse("put your wallet where you want to send NFT"); -const walletAddress = Address.parse("put your wallet which is the owner of NFT") +const destinationAddress = Address.parse( + "put your wallet where you want to send NFT" +); +const walletAddress = Address.parse( + "put your wallet which is the owner of NFT" +); const nftAddress = Address.parse("put your nft address"); -// We can add a comment, but it will not be displayed in the explorers, +// We can add a comment, but it will not be displayed in the explorers, // as it is not supported by them at the time of writing the tutorial. const forwardPayload = beginCell() .storeUint(0, 32) @@ -1599,13 +1618,13 @@ const transferNftBody = beginCell() .storeRef(forwardPayload) // store forward_payload as a .reference .endCell(); -const internalMessage = beginCell(). - storeUint(0x18, 6). // bounce - storeAddress(nftAddress). - storeCoins(toNano("0.05")). - storeUint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1). // We store 1 that means we have body as a reference - storeRef(transferNftBody). - endCell(); +const internalMessage = beginCell() + .storeUint(0x18, 6) // bounce + .storeAddress(nftAddress) + .storeCoins(toNano("0.05")) + .storeUint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1) // We store 1 that means we have body as a reference + .storeRef(transferNftBody) + .endCell(); ``` @@ -1663,18 +1682,18 @@ Smart contracts often make use of [GET methods](/v3/guidelines/smart-contracts/g Below we’ll learn more about the basics of GET methods used with [V3](https://github.com/ton-blockchain/ton/blob/e37583e5e6e8cd0aebf5142ef7d8db282f10692b/crypto/smartcont/wallet3-code.fc#L31-L41) and [V4](https://github.com/ton-blockchain/wallet-contract/blob/4111fd9e3313ec17d99ca9b5b1656445b5b49d8f/func/wallet-v4-code.fc#L164-L198). Let’s start with the methods that are the same for both wallet versions: -Method | Explanation -:---: | :---: -int seqno() | This method is needed to receive the current seqno and send messages with the correct value. In previous sections of this tutorial, this method was called often. -int get_public_key() | This method is used to retrive a public key. The get_public_key() is not broadly used, and can be used by different services. For example, some API services allow for the retrieval of numerous wallets with the same public key +| Method | Explanation | +| :------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| int seqno() | This method is needed to receive the current seqno and send messages with the correct value. In previous sections of this tutorial, this method was called often. | +| int get_public_key() | This method is used to retrive a public key. The get_public_key() is not broadly used, and can be used by different services. For example, some API services allow for the retrieval of numerous wallets with the same public key | Now let’s move to the methods that only the V4 wallet makes use of: -Method | Explanation -:---: | :---: -int get_subwallet_id() | Earlier in the tutorial we considered this. This method allows you to retrive subwallet_id. -int is_plugin_installed(int wc, int addr_hash) | Let’s us know if the plugin has been installed. To call this method it’s necessary to pass the [workchain](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains#workchain-blockchain-with-your-own-rules) and the plugin address hash. -tuple get_plugin_list() | This method returns the address of the plugins that are installed. +| Method | Explanation | +| :--------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| int get_subwallet_id() | Earlier in the tutorial we considered this. This method allows you to retrive subwallet_id. | +| int is_plugin_installed(int wc, int addr_hash) | Let’s us know if the plugin has been installed. To call this method it’s necessary to pass the [workchain](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains#workchain-blockchain-with-your-own-rules) and the plugin address hash. | +| tuple get_plugin_list() | This method returns the address of the plugins that are installed. | Let’s consider the `get_public_key` and the `is_plugin_installed` methods. These two methods were chosen because at first we would have to get a public key from 256 bits of data, and after that we would have to learn how to pass a slice and different types of data to GET methods. This is very useful to help us learn how to properly make use of these methods. @@ -1684,15 +1703,17 @@ First we need a client that is capable of sending requests. Therefore, we’ll u ```js -import { TonClient } from '@ton/ton'; -import { Address } from '@ton/core'; +import { TonClient } from "@ton/ton"; +import { Address } from "@ton/core"; const client = new TonClient({ - endpoint: "https://toncenter.com/api/v2/jsonRPC", - apiKey: "put your api key" // you can get an api key from @tonapibot bot in Telegram + endpoint: "https://toncenter.com/api/v2/jsonRPC", + apiKey: "put your api key", // you can get an api key from @tonapibot bot in Telegram }); -const walletAddress = Address.parse("EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF"); // my wallet address as an example +const walletAddress = Address.parse( + "EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF" +); // my wallet address as an example ``` @@ -1733,11 +1754,14 @@ Now we need to call the GET method wallet. ```js -// I always call runMethodWithError instead of runMethod to be able to check the exit_code of the called method. -let getResult = await client.runMethodWithError(walletAddress, "get_public_key"); // run get_public_key GET Method +// I always call runMethodWithError instead of runMethod to be able to check the exit_code of the called method. +let getResult = await client.runMethodWithError( + walletAddress, + "get_public_key" +); // run get_public_key GET Method const publicKeyUInt = getResult.stack.readBigNumber(); // read answer that contains uint256 const publicKey = publicKeyUInt.toString(16); // get hex string from bigint (uint256) -console.log(publicKey) +console.log(publicKey); ``` @@ -1769,8 +1793,12 @@ Then we switch to the `is_plugin_installed` method. As an example, we’ll again ```js -const oldWalletAddress = Address.parse("EQAM7M--HGyfxlErAIUODrxBA3yj5roBeYiTuy6BHgJ3Sx8k"); // my old wallet address -const subscriptionAddress = Address.parseFriendly("EQBTKTis-SWYdupy99ozeOvnEBu8LRrQP_N9qwOTSAy3sQSZ"); // subscription plugin address which is already installed on the wallet +const oldWalletAddress = Address.parse( + "EQAM7M--HGyfxlErAIUODrxBA3yj5roBeYiTuy6BHgJ3Sx8k" +); // my old wallet address +const subscriptionAddress = Address.parseFriendly( + "EQBTKTis-SWYdupy99ozeOvnEBu8LRrQP_N9qwOTSAy3sQSZ" +); // subscription plugin address which is already installed on the wallet ``` @@ -1790,13 +1818,16 @@ Now we need to retrieve the plugin’s hash address so the address can be transl ```js -const hash = BigInt(`0x${subscriptionAddress.address.hash.toString("hex")}`) ; +const hash = BigInt(`0x${subscriptionAddress.address.hash.toString("hex")}`); -getResult = await client.runMethodWithError(oldWalletAddress, "is_plugin_installed", -[ - {type: "int", value: BigInt("0")}, // pass workchain as int - {type: "int", value: hash} // pass plugin address hash as int -]); +getResult = await client.runMethodWithError( + oldWalletAddress, + "is_plugin_installed", + [ + { type: "int", value: BigInt("0") }, // pass workchain as int + { type: "int", value: hash }, // pass plugin address hash as int + ] +); console.log(getResult.stack.readNumber()); // -1 ``` @@ -1838,28 +1869,30 @@ In this case, we’ll set the `subwallet_id` to `3` or any other number needed t ```js -import { beginCell, Cell } from '@ton/core'; -import { mnemonicToWalletKey } from '@ton/crypto'; +import { beginCell, Cell } from "@ton/core"; +import { mnemonicToWalletKey } from "@ton/crypto"; -const mnemonicArray = 'put your mnemonic'.split(" "); +const mnemonicArray = "put your mnemonic".split(" "); const keyPair = await mnemonicToWalletKey(mnemonicArray); // extract private and public keys from mnemonic -const codeCell = Cell.fromBase64('te6ccgEBCAEAhgABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQCW8oMI1xgg0x/TH9MfAvgju/Jj7UTQ0x/TH9P/0VEyuvKhUUS68qIE+QFUEFX5EPKj+ACTINdKltMH1AL7AOgwAaTIyx/LH8v/ye1UAATQMAIBSAYHABe7Oc7UTQ0z8x1wv/gAEbjJftRNDXCx+A=='); +const codeCell = Cell.fromBase64( + "te6ccgEBCAEAhgABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQCW8oMI1xgg0x/TH9MfAvgju/Jj7UTQ0x/TH9P/0VEyuvKhUUS68qIE+QFUEFX5EPKj+ACTINdKltMH1AL7AOgwAaTIyx/LH8v/ye1UAATQMAIBSAYHABe7Oc7UTQ0z8x1wv/gAEbjJftRNDXCx+A==" +); const dataCell = beginCell() - .storeUint(0, 32) // Seqno - .storeUint(3, 32) // Subwallet ID - .storeBuffer(keyPair.publicKey) // Public Key - .endCell(); + .storeUint(0, 32) // Seqno + .storeUint(3, 32) // Subwallet ID + .storeBuffer(keyPair.publicKey) // Public Key + .endCell(); const stateInit = beginCell() - .storeBit(0) // No split_depth - .storeBit(0) // No special - .storeBit(1) // We have code - .storeRef(codeCell) - .storeBit(1) // We have data - .storeRef(dataCell) - .storeBit(0) // No library - .endCell(); + .storeBit(0) // No split_depth + .storeBit(0) // No special + .storeBit(1) // We have code + .storeRef(codeCell) + .storeBit(1) // We have data + .storeRef(dataCell) + .storeBit(0) // No library + .endCell(); ``` @@ -1917,27 +1950,27 @@ Next we’ll retrieve the address from our contract and build the InternalMessag ```js -import { Address, toNano } from '@ton/core'; +import { Address, toNano } from "@ton/core"; const contractAddress = new Address(0, stateInit.hash()); // get the hash of stateInit to get the address of our smart contract in workchain with ID 0 console.log(`Contract address: ${contractAddress.toString()}`); // Output contract address to console const internalMessageBody = beginCell() - .storeUint(0, 32) - .storeStringTail('Deploying...') - .endCell(); + .storeUint(0, 32) + .storeStringTail("Deploying...") + .endCell(); const internalMessage = beginCell() - .storeUint(0x10, 6) // no bounce - .storeAddress(contractAddress) - .storeCoins(toNano('0.01')) - .storeUint(0, 1 + 4 + 4 + 64 + 32) - .storeBit(1) // We have State Init - .storeBit(1) // We store State Init as a reference - .storeRef(stateInit) // Store State Init as a reference - .storeBit(1) // We store Message Body as a reference - .storeRef(internalMessageBody) // Store Message Body Init as a reference - .endCell(); + .storeUint(0x10, 6) // no bounce + .storeAddress(contractAddress) + .storeCoins(toNano("0.01")) + .storeUint(0, 1 + 4 + 4 + 64 + 32) + .storeBit(1) // We have State Init + .storeBit(1) // We store State Init as a reference + .storeRef(stateInit) // Store State Init as a reference + .storeBit(1) // We store Message Body as a reference + .storeRef(internalMessageBody) // Store Message Body Init as a reference + .endCell(); ``` @@ -1984,46 +2017,48 @@ Next, we’ll prepare a message for our wallet and send it: ```js -import { TonClient } from '@ton/ton'; -import { sign } from '@ton/crypto'; +import { TonClient } from "@ton/ton"; +import { sign } from "@ton/crypto"; const client = new TonClient({ - endpoint: 'https://toncenter.com/api/v2/jsonRPC', - apiKey: 'put your api key' // you can get an api key from @tonapibot bot in Telegram + endpoint: "https://toncenter.com/api/v2/jsonRPC", + apiKey: "put your api key", // you can get an api key from @tonapibot bot in Telegram }); -const walletMnemonicArray = 'put your mnemonic'.split(' '); +const walletMnemonicArray = "put your mnemonic".split(" "); const walletKeyPair = await mnemonicToWalletKey(walletMnemonicArray); // extract private and public keys from mnemonic -const walletAddress = Address.parse('put your wallet address with which you will deploy'); -const getMethodResult = await client.runMethod(walletAddress, 'seqno'); // run "seqno" GET method from your wallet contract +const walletAddress = Address.parse( + "put your wallet address with which you will deploy" +); +const getMethodResult = await client.runMethod(walletAddress, "seqno"); // run "seqno" GET method from your wallet contract const seqno = getMethodResult.stack.readNumber(); // get seqno from response // message for our wallet const toSign = beginCell() - .storeUint(698983191, 32) // subwallet_id - .storeUint(Math.floor(Date.now() / 1e3) + 60, 32) // Message expiration time, +60 = 1 minute - .storeUint(seqno, 32) // store seqno - // Do not forget that if we use Wallet V4, we need to add .storeUint(0, 8) - .storeUint(3, 8) - .storeRef(internalMessage); + .storeUint(698983191, 32) // subwallet_id + .storeUint(Math.floor(Date.now() / 1e3) + 60, 32) // Message expiration time, +60 = 1 minute + .storeUint(seqno, 32) // store seqno + // Do not forget that if we use Wallet V4, we need to add .storeUint(0, 8) + .storeUint(3, 8) + .storeRef(internalMessage); const signature = sign(toSign.endCell().hash(), walletKeyPair.secretKey); // get the hash of our message to wallet smart contract and sign it to get signature const body = beginCell() - .storeBuffer(signature) // store signature - .storeBuilder(toSign) // store our message - .endCell(); + .storeBuffer(signature) // store signature + .storeBuilder(toSign) // store our message + .endCell(); const external = beginCell() - .storeUint(0b10, 2) // indicate that it is an incoming external message - .storeUint(0, 2) // src -> addr_none - .storeAddress(walletAddress) - .storeCoins(0) // Import fee - .storeBit(0) // We do not have State Init - .storeBit(1) // We store Message Body as a reference - .storeRef(body) // Store Message Body as a reference - .endCell(); + .storeUint(0b10, 2) // indicate that it is an incoming external message + .storeUint(0, 2) // src -> addr_none + .storeAddress(walletAddress) + .storeCoins(0) // Import fee + .storeBit(0) // We do not have State Init + .storeBit(1) // We store Message Body as a reference + .storeRef(body) // Store Message Body as a reference + .endCell(); -console.log(external.toBoc().toString('base64')); +console.log(external.toBoc().toString("base64")); client.sendFile(external.toBoc()); ``` @@ -2109,7 +2144,7 @@ This concludes our work with ordinary wallets. At this stage, you should have a ## 🔥 High-Load Wallet V3 -When working with many messages in a short period, there is a need for special wallet called High-Load Wallet. High-Load Wallet V2 was the main wallet on TON for a long time, but you had to be very careful with it. Otherwise, you could [lock all funds](https://t.me/tonstatus/88). +When working with many messages in a short period, there is a need for special wallet called High-Load Wallet. High-Load Wallet V2 was the main wallet on TON for a long time, but you had to be very careful with it. Otherwise, you could [lock all funds](https://t.me/tonstatus/88). [With the advent of High-Load Wallet V3](https://github.com/ton-blockchain/highload-wallet-contract-v3), this problem has been solved at the contract architecture level and consumes less gas. This chapter will cover the basics of High-Load Wallet V3 and important nuances to remember. @@ -2117,7 +2152,6 @@ When working with many messages in a short period, there is a need for special w We will work [with a slightly modified version of Wrapper](https://github.com/aSpite/highload-wallet-contract-v3/blob/main/wrappers/HighloadWalletV3.ts) for the contract, as it protects against some non-obvious mistakes. ::: - ### Storage Structure First of all, [TL-B schema](https://github.com/ton-blockchain/highload-wallet-contract-v3/blob/d58c31e82315c34b4db55942851dd8d4153975c5/contracts/scheme.tlb#L1C1-L3C21) will help us in learning the structure of the contract storage: @@ -2134,14 +2168,14 @@ You can read more about TL-B [here](/v3/documentation/data-formats/tlb/tl-b-lang In the contract storage, we can find the following fields: -| Field | Description | -| :---: | :---: | -| public_key | Public key of the contract. | -| subwallet_id | [Wallet ID](#subwallet-ids). It allows you to create many wallets using the same public key. | -| old_queries | Old queries that have already been processed and are outdated. They are moved here after each timeout. | -| queries | Queries that have been processed but are not yet outdated. | +| Field | Description | +| :-------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| public_key | Public key of the contract. | +| subwallet_id | [Wallet ID](#subwallet-ids). It allows you to create many wallets using the same public key. | +| old_queries | Old queries that have already been processed and are outdated. They are moved here after each timeout. | +| queries | Queries that have been processed but are not yet outdated. | | last_clean_time | The time of the last cleanup. If `last_clean_time < (now() - timeout)`, old queries are moved to `old_queries`. If `last_clean_time < (now() - 2 * timeout)`, both `old_queries` and `queries` are cleared. | -| timeout | The time after which queries are moved to `old_queries`. | +| timeout | The time after which queries are moved to `old_queries`. | We will discuss more about working with processed queries in [Replay Protection](#replay-protection). @@ -2252,7 +2286,7 @@ commit(); This is done so that when executing further code if there is an error in the message the user is trying to send, the contract does not return to its previous state. Otherwise, the external will remain valid and can be accepted several times, resulting in wasted balance. -However, another issue must be addressed - possible errors during the **Action Phase**. Although we have a flag to ignore the mistakes (2) when sending a message, it doesn't work in all cases, so we need to ensure that no errors occur during this phase, which could cause the state to roll back and make `commit()` meaningless. +However, another issue must be addressed - possible errors during the **Action Phase**. Although we have a flag to ignore the mistakes (2) when sending a message, it doesn't work in all cases, so we need to ensure that no errors occur during this phase, which could cause the state to roll back and make `commit()` meaningless. For this reason, instead of sending all messages directly, the contract sends itself a message with the `internal_transfer` opcode. This message is parsed in detail by the contract to ensure that no Action Phase error occurs: @@ -2302,7 +2336,7 @@ if (op == op::internal_transfer) { } ``` -When dealing with `internal_transfer` there is one important nuance. As we have discussed above, the contract sends a message to itself, but that message is entirely collected on the user side. The problem is that you need to correctly count how much TON will be attached to the message. +When dealing with `internal_transfer` there is one important nuance. As we have discussed above, the contract sends a message to itself, but that message is entirely collected on the user side. The problem is that you need to correctly count how much TON will be attached to the message. In the wrapper in the official repository this field is optional and if the user does not specify it, [mode becomes 128](https://github.com/ton-blockchain/highload-wallet-contract-v3/blob/d58c31e82315c34b4db55942851dd8d4153975c5/wrappers/HighloadWalletV3.ts#L115), which means that the entire balance is sent. The problem is that in such a case there is a **edge case**. @@ -2326,23 +2360,22 @@ Although the external message limit is 64KB, the larger the external, the more l High-Load Wallet V3 supports the 5 GET methods: -Method | Explanation -:---: | :---: -int get_public_key() | Returns the public key of the contract. -int get_subwallet_id() | Returns the subwallet ID. -int get_last_clean_time() | Returns the time of the last cleaning. -int get_timeout() | Returns the timeout value. -int processed?(int query_id, int need_clean) | Returns whether the query_id has been processed. If need_clean is set to 1, then will first do the cleanup based on `last_clean_time` and `timeout` and then check for query_id in `old_queries` and `queries`. +| Method | Explanation | +| :------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| int get_public_key() | Returns the public key of the contract. | +| int get_subwallet_id() | Returns the subwallet ID. | +| int get_last_clean_time() | Returns the time of the last cleaning. | +| int get_timeout() | Returns the timeout value. | +| int processed?(int query_id, int need_clean) | Returns whether the query_id has been processed. If need_clean is set to 1, then will first do the cleanup based on `last_clean_time` and `timeout` and then check for query_id in `old_queries` and `queries`. | :::tip It is recommended to pass `true` for `need_clean` unless the situation requires otherwise since the most current dictionary states will then be returned. ::: -Due to how the Query ID is organized in High-Load Wallet V3, we can send a message with the same Query ID again if it does not arrive without fear of the request being processed twice. +Due to how the Query ID is organized in High-Load Wallet V3, we can send a message with the same Query ID again if it does not arrive without fear of the request being processed twice. However, in such a case, we must consider that no more than `timeout` time has elapsed since the first sending attempt. Otherwise, the request may have been processed but already deleted from the dictionaries. Therefore, it is recommended to set `timeout` to no less than an hour and no more than 24 hours. - ### Deploying High-Load Wallet V3 To deploy a contract, we need 2 cells: `code` and `date`. For the code, we will use the following cell: @@ -2353,11 +2386,16 @@ To deploy a contract, we need 2 cells: `code` and `date`. For the code, we will ```js import { Cell } from "@ton/core"; -const HIGHLOAD_V3_CODE = Cell.fromBoc(Buffer.from('b5ee9c7241021001000228000114ff00f4a413f4bcf2c80b01020120020d02014803040078d020d74bc00101c060b0915be101d0d3030171b0915be0fa4030f828c705b39130e0d31f018210ae42e5a4ba9d8040d721d74cf82a01ed55fb04e030020120050a02027306070011adce76a2686b85ffc00201200809001aabb6ed44d0810122d721d70b3f0018aa3bed44d08307d721d70b1f0201200b0c001bb9a6eed44d0810162d721d70b15800e5b8bf2eda2edfb21ab09028409b0ed44d0810120d721f404f404d33fd315d1058e1bf82325a15210b99f326df82305aa0015a112b992306dde923033e2923033e25230800df40f6fa19ed021d721d70a00955f037fdb31e09130e259800df40f6fa19cd001d721d70a00937fdb31e0915be270801f6f2d48308d718d121f900ed44d0d3ffd31ff404f404d33fd315d1f82321a15220b98e12336df82324aa00a112b9926d32de58f82301de541675f910f2a106d0d31fd4d307d30cd309d33fd315d15168baf2a2515abaf2a6f8232aa15250bcf2a304f823bbf2a35304800df40f6fa199d024d721d70a00f2649130e20e01fe5309800df40f6fa18e13d05004d718d20001f264c858cf16cf8301cf168e1030c824cf40cf8384095005a1a514cf40e2f800c94039800df41704c8cbff13cb1ff40012f40012cb3f12cb15c9ed54f80f21d0d30001f265d3020171b0925f03e0fa4001d70b01c000f2a5fa4031fa0031f401fa0031fa00318060d721d300010f0020f265d2000193d431d19130e272b1fb00b585bf03', 'hex'))[0]; +const HIGHLOAD_V3_CODE = Cell.fromBoc( + Buffer.from( + "b5ee9c7241021001000228000114ff00f4a413f4bcf2c80b01020120020d02014803040078d020d74bc00101c060b0915be101d0d3030171b0915be0fa4030f828c705b39130e0d31f018210ae42e5a4ba9d8040d721d74cf82a01ed55fb04e030020120050a02027306070011adce76a2686b85ffc00201200809001aabb6ed44d0810122d721d70b3f0018aa3bed44d08307d721d70b1f0201200b0c001bb9a6eed44d0810162d721d70b15800e5b8bf2eda2edfb21ab09028409b0ed44d0810120d721f404f404d33fd315d1058e1bf82325a15210b99f326df82305aa0015a112b992306dde923033e2923033e25230800df40f6fa19ed021d721d70a00955f037fdb31e09130e259800df40f6fa19cd001d721d70a00937fdb31e0915be270801f6f2d48308d718d121f900ed44d0d3ffd31ff404f404d33fd315d1f82321a15220b98e12336df82324aa00a112b9926d32de58f82301de541675f910f2a106d0d31fd4d307d30cd309d33fd315d15168baf2a2515abaf2a6f8232aa15250bcf2a304f823bbf2a35304800df40f6fa199d024d721d70a00f2649130e20e01fe5309800df40f6fa18e13d05004d718d20001f264c858cf16cf8301cf168e1030c824cf40cf8384095005a1a514cf40e2f800c94039800df41704c8cbff13cb1ff40012f40012cb3f12cb15c9ed54f80f21d0d30001f265d3020171b0925f03e0fa4001d70b01c000f2a5fa4031fa0031f401fa0031fa00318060d721d300010f0020f265d2000193d431d19130e272b1fb00b585bf03", + "hex" + ) +)[0]; ``` - + Unlike the other examples, here we will work [with a ready-made wrapper](https://github.com/aSpite/highload-wallet-contract-v3/blob/main/wrappers/HighloadWalletV3.ts), as it will be quite difficult and time-consuming to build each message manually. To create an instance of the HighloadWalletV3 class, we pass `publicKey`, `subwalletId` and `timeout` and also the code: @@ -2366,27 +2404,32 @@ Unlike the other examples, here we will work [with a ready-made wrapper](https:/ ```js import { TonClient } from "@ton/ton"; -import { HighloadWalletV3 } from "./wrappers/HighloadWalletV3"; +import { HighloadWalletV3 } from "./wrappers/HighloadWalletV3"; import { mnemonicToWalletKey } from "@ton/crypto"; const client = new TonClient({ - endpoint: 'https://toncenter.com/api/v2/jsonRPC', - apiKey: 'put your api key' // you can get an api key from @tonapibot bot in Telegram + endpoint: "https://toncenter.com/api/v2/jsonRPC", + apiKey: "put your api key", // you can get an api key from @tonapibot bot in Telegram }); -const walletMnemonicArray = 'put your mnemonic'.split(' '); +const walletMnemonicArray = "put your mnemonic".split(" "); const walletKeyPair = await mnemonicToWalletKey(walletMnemonicArray); // extract private and public keys from mnemonic -const wallet = client.open(HighloadWalletV3.createFromConfig({ - publicKey: walletKeyPair.publicKey, - subwalletId: 0x10ad, - timeout: 60 * 60, // 1 hour -}, HIGHLOAD_V3_CODE)); +const wallet = client.open( + HighloadWalletV3.createFromConfig( + { + publicKey: walletKeyPair.publicKey, + subwalletId: 0x10ad, + timeout: 60 * 60, // 1 hour + }, + HIGHLOAD_V3_CODE + ) +); console.log(`Wallet address: ${wallet.address.toString()}`); ``` - + Now we need a regular wallet, from which we will deploy the contract: @@ -2396,17 +2439,21 @@ Now we need a regular wallet, from which we will deploy the contract: ```js import { WalletContractV3R2 } from "@ton/ton"; -const deployerWalletMnemonicArray = 'put your mnemonic'.split(' '); -const deployerWalletKeyPair = await mnemonicToWalletKey(deployerWalletMnemonicArray); // extract private and public keys from mnemonic -const deployerWallet = client.open(WalletContractV3R2.create({ +const deployerWalletMnemonicArray = "put your mnemonic".split(" "); +const deployerWalletKeyPair = await mnemonicToWalletKey( + deployerWalletMnemonicArray +); // extract private and public keys from mnemonic +const deployerWallet = client.open( + WalletContractV3R2.create({ publicKey: deployerWalletKeyPair.publicKey, - workchain: 0 -})); + workchain: 0, + }) +); console.log(`Deployer wallet address: ${deployerWallet.address.toString()}`); ``` - + If you have a V4 version wallet, you can use the `WalletContractV4` class. Now, all we have to do is to deploy the contract: @@ -2414,11 +2461,14 @@ If you have a V4 version wallet, you can use the `WalletContractV4` class. Now, ```js -await wallet.sendDeploy(deployerWallet.sender(deployerWalletKeyPair.secretKey), toNano(0.05)); +await wallet.sendDeploy( + deployerWallet.sender(deployerWalletKeyPair.secretKey), + toNano(0.05) +); ``` - + By viewing the address that was output to the console in explorer, we can verify that our wallet is deployed. @@ -2436,18 +2486,22 @@ import { HighloadWalletV3 } from "./wrappers/HighloadWalletV3"; import { mnemonicToWalletKey } from "@ton/crypto"; const client = new TonClient({ - endpoint: 'https://toncenter.com/api/v2/jsonRPC', - apiKey: 'put your api key' // you can get an api key from @tonapibot bot in Telegram + endpoint: "https://toncenter.com/api/v2/jsonRPC", + apiKey: "put your api key", // you can get an api key from @tonapibot bot in Telegram }); -const walletMnemonicArray = 'put your mnemonic'.split(' '); +const walletMnemonicArray = "put your mnemonic".split(" "); const walletKeyPair = await mnemonicToWalletKey(walletMnemonicArray); // extract private and public keys from mnemonic -const wallet = client.open(HighloadWalletV3.createFromAddress(Address.parse('put your high-load wallet address'))); +const wallet = client.open( + HighloadWalletV3.createFromAddress( + Address.parse("put your high-load wallet address") + ) +); console.log(`Wallet address: ${wallet.address.toString()}`); ``` - + Now we need to create an instance of the `HighloadQueryId` class. This class makes it easy to work with `shift` and `bit_number`. To create it, we use the `fromShiftAndBitNumber` method: @@ -2461,7 +2515,7 @@ const queryHandler = HighloadQueryId.fromShiftAndBitNumber(0n, 0n); ``` - + We put zeros here since this is the first request. However, if you've sent any messages before, you'll need to pick an unused combination of these values. Now let's create an array where we will store all our actions and add one action to it to get our TONs back: @@ -2469,25 +2523,28 @@ We put zeros here since this is the first request. However, if you've sent any m ```js -import { beginCell, internal, OutActionSendMsg, SendMode, toNano } from "@ton/core"; +import { + beginCell, + internal, + OutActionSendMsg, + SendMode, + toNano, +} from "@ton/core"; const actions: OutActionSendMsg[] = []; actions.push({ - type: 'sendMsg', - mode: SendMode.CARRY_ALL_REMAINING_BALANCE, - outMsg: internal({ - to: Address.parse('put address of deployer wallet'), - value: toNano(0), - body: beginCell() - .storeUint(0, 32) - .storeStringTail('Hello, TON!') - .endCell() - }) + type: "sendMsg", + mode: SendMode.CARRY_ALL_REMAINING_BALANCE, + outMsg: internal({ + to: Address.parse("put address of deployer wallet"), + value: toNano(0), + body: beginCell().storeUint(0, 32).storeStringTail("Hello, TON!").endCell(), + }), }); ``` - + Next we just need to fill in the `subwalletId`, `timeout`, `internalMessageValue` and `createdAt` fields to send the message: @@ -2500,19 +2557,19 @@ const timeout = 60 * 60; // must be same as in the contract const internalMessageValue = toNano(0.01); // in real case it is recommended to set the value to 1 TON const createdAt = Math.floor(Date.now() / 1000) - 60; // LiteServers have some delay in time await wallet.sendBatch( - walletKeyPair.secretKey, - actions, - subwalletId, - queryHandler, - timeout, - internalMessageValue, - SendMode.PAY_GAS_SEPARATELY, - createdAt + walletKeyPair.secretKey, + actions, + subwalletId, + queryHandler, + timeout, + internalMessageValue, + SendMode.PAY_GAS_SEPARATELY, + createdAt ); ``` - + After submitting, we should use the `getNext` method in `queryHandler` and save the current value. In a real case, this value should be stored in the database and reset after the `timeout * 2` time. @@ -2524,7 +2581,7 @@ queryHandler.getNext(); ``` - + ## 🔥 High-Load Wallet V2 (Outdated) @@ -2534,7 +2591,7 @@ Exchanges are probably the best example of where high-load wallets are used on a ### High-load wallet FunC code -First, let’s examine [the code structure of high-load wallet smart contract](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/new-highload-wallet-v2.fif): +First, let’s examine [the code structure of high-load wallet smart contract](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/new-highload-wallet-v2.fif): ```func () recv_external(slice in_msg) impure { @@ -2606,7 +2663,6 @@ Note that if a value is found, `f` is always equal to -1 (true). The `~ -1` oper Typically, [smart contracts on TON pay for their own storage](/v3/documentation/smart-contracts/transaction-fees/fees-low-level#storage-fee). This means that the amount of data smart contracts can store is limited to prevent high network loading. To allow the system to be more efficient, messages that are more than 64 seconds old are removed from the storage. This is conducted as follows: - ```func bound -= (64 << 32); ;; clean up records that have expired more than 64 seconds ago old_queries~udict_set_builder(64, query_id, begin_cell()); ;; add current query to dictionary @@ -2617,7 +2673,7 @@ do { if (f) { ;; check if any value was found f = (i < bound); ;; check if more than 64 seconds have elapsed after expiration } - if (f) { + if (f) { old_queries = old_queries'; ;; if yes save changes in our dictionary last_cleaned = i; ;; save last removed query } @@ -2628,7 +2684,7 @@ do { > > ["udict_delete_get_min()" in docs](/v3/documentation/smart-contracts/func/docs/stdlib/#dict_delete_get_min) -Note that it is necessary to interact with the `f` variable several times. Since the [TVM is a stack machine](/v3/documentation/tvm/tvm-overview#tvm-is-a-stack-machine), during each interaction with the `f` variable it is necessary to pop all values to get the desired variable. The `f~touch()` operation places the f variable at the top of the stack to optimize code execution. +Note that it is necessary to interact with the `f` variable several times. Since the [TVM is a stack machine](/v3/documentation/tvm/tvm-overview#tvm-is-a-stack-machine), during each interaction with the `f` variable it is necessary to pop all values to get the desired variable. The `f~touch()` operation places the f variable at the top of the stack to optimize code execution. ### Bitwise Operations @@ -2637,6 +2693,7 @@ This section may seem a bit complicated for those who have not previously worked ```func var bound = (now() << 32); ;; bitwise left shift operation ``` + As a result 32 bits are added to the number on the right side. This means that **existing values are moved 32 bits to the left**. For example, let’s consider the number 3 and translate it into a binary form with a result of 11. Applying the `3 << 2` operation, 11 is moved 2 bit places. This means that two bits are added to the right of the string. In the end, we have 1100, which is 12. The first thing to understand about this process is to remember that the `now()` function returns a result of uint32, meaning that the resulting value will be 32 bits. By shifting 32 bits to the left, space is opened up for another uint32, resulting in the correct query_id. This way, the **timestamp and query_id can be combined** within one variable for optimization. @@ -2654,6 +2711,7 @@ if (f) { ;; check if any value has been found f = (i < bound); ;; check if more than 64 seconds have elapsed after expiration } ``` + To understand this better, let’s use the number `1625918400` as an example of a timestamp. Its binary representation (with the left-handed addition of zeros for 32 bits) is 01100000111010011000101111000000. By performing a 32 bit bitwise left shift, the result is 32 zeros at the end of the binary representation of our number. After this is completed, **it is possible to add any query_id (uint32)**. Then by subtracting `64 << 32` the result is a timestamp that 64 seconds ago had the same query_id. This fact can be verified by performing the following calculations `((1625918400 << 32) - (64 << 32)) >> 32`. This way we can compare the necessary portions of our number (the timestamp) and at the same time the query_id does not interfere. @@ -2676,10 +2734,10 @@ After all operations are complete, the only task remaining is to save the new va The last thing we have to consider before we dive into wallet deployment and message creation is high-load wallet GET methods: -Method | Explanation -:---: | :---: -int processed?(int query_id) | Notifies the user if a particular request has been processed. This means it returns `-1` if the request has been processed and `0` if it has not. Also, this method may return `1` if the answer is unknown since the request is old and no longer stored in the contract. -int get_public_key() | Rerive a public key. We have considered this method before. +| Method | Explanation | +| :--------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| int processed?(int query_id) | Notifies the user if a particular request has been processed. This means it returns `-1` if the request has been processed and `0` if it has not. Also, this method may return `1` if the answer is unknown since the request is old and no longer stored in the contract. | +| int get_public_key() | Rerive a public key. We have considered this method before. | Let’s look at the `int processed?(int query_id)` method closely to help us to understand why we need to make use of the last_cleaned: @@ -2692,6 +2750,7 @@ int processed?(int query_id) method_id { return found ? true : - (query_id <= last_cleaned); } ``` + The `last_cleaned` is retrieved from the storage of the contract and a dictionary of old queries. If the query is found, it is to be returned true, and if not, the expression `- (query_id <= last_cleaned)`. The last_cleaned contains the last removed request **with the highest timestamp**, as we started with the minimum timestamp when deleting the requests. This means that if the query_id passed to the method is smaller than the last last_cleaned value, it is impossible to determine whether it was ever in the contract or not. Therefore the `query_id <= last_cleaned` returns -1 while the minus before this expression changes the answer to 1. If query_id is larger than last_cleaned method, then it has not yet been processed. @@ -2706,29 +2765,30 @@ To begin the process required to deploy a high-load wallet it's necessary to cop ```js -import { compileFunc } from '@ton-community/func-js'; -import fs from 'fs' -import { Cell } from '@ton/core'; +import { compileFunc } from "@ton-community/func-js"; +import fs from "fs"; +import { Cell } from "@ton/core"; const result = await compileFunc({ - targets: ['highload_wallet.fc'], // targets of your project - sources: { - 'stdlib.fc': fs.readFileSync('./src/stdlib.fc', { encoding: 'utf-8' }), - 'highload_wallet.fc': fs.readFileSync('./src/highload_wallet.fc', { encoding: 'utf-8' }), - } + targets: ["highload_wallet.fc"], // targets of your project + sources: { + "stdlib.fc": fs.readFileSync("./src/stdlib.fc", { encoding: "utf-8" }), + "highload_wallet.fc": fs.readFileSync("./src/highload_wallet.fc", { + encoding: "utf-8", + }), + }, }); -if (result.status === 'error') { -console.error(result.message) -return; +if (result.status === "error") { + console.error(result.message); + return; } -const codeCell = Cell.fromBoc(Buffer.from(result.codeBoc, 'base64'))[0]; +const codeCell = Cell.fromBoc(Buffer.from(result.codeBoc, "base64"))[0]; // now we have base64 encoded BOC with compiled code in result.codeBoc -console.log('Code BOC: ' + result.codeBoc); -console.log('\nHash: ' + codeCell.hash().toString('base64')); // get the hash of cell and convert in to base64 encoded string - +console.log("Code BOC: " + result.codeBoc); +console.log("\nHash: " + codeCell.hash().toString("base64")); // get the hash of cell and convert in to base64 encoded string ``` @@ -2758,7 +2818,7 @@ base64BOC := "te6ccgEBCQEA5QABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQHq8oMI1xgg0x/TP/gjqh codeCellBytes, _ := base64.StdEncoding.DecodeString(base64BOC) // decode base64 in order to get byte array codeCell, err := cell.FromBOC(codeCellBytes) // get cell with code from byte array if err != nil { // check if there is any error - panic(err) + panic(err) } log.Println("Hash:", base64.StdEncoding.EncodeToString(codeCell.Hash())) // get the hash of our cell, encode it to base64 because it has []byte type and output to the terminal @@ -2773,28 +2833,29 @@ Now we need to retrieve a cell composed of its initial data, build a State Init, ```js -import { Address, beginCell } from '@ton/core'; -import { mnemonicToWalletKey } from '@ton/crypto'; +import { Address, beginCell } from "@ton/core"; +import { mnemonicToWalletKey } from "@ton/crypto"; -const highloadMnemonicArray = 'put your mnemonic that you have generated and saved before'.split(' '); +const highloadMnemonicArray = + "put your mnemonic that you have generated and saved before".split(" "); const highloadKeyPair = await mnemonicToWalletKey(highloadMnemonicArray); // extract private and public keys from mnemonic const dataCell = beginCell() - .storeUint(698983191, 32) // Subwallet ID - .storeUint(0, 64) // Last cleaned - .storeBuffer(highloadKeyPair.publicKey) // Public Key - .storeBit(0) // indicate that the dictionary is empty - .endCell(); + .storeUint(698983191, 32) // Subwallet ID + .storeUint(0, 64) // Last cleaned + .storeBuffer(highloadKeyPair.publicKey) // Public Key + .storeBit(0) // indicate that the dictionary is empty + .endCell(); const stateInit = beginCell() - .storeBit(0) // No split_depth - .storeBit(0) // No special - .storeBit(1) // We have code - .storeRef(codeCell) - .storeBit(1) // We have data - .storeRef(dataCell) - .storeBit(0) // No library - .endCell(); + .storeBit(0) // No split_depth + .storeBit(0) // No special + .storeBit(1) // We have code + .storeRef(codeCell) + .storeBit(1) // We have data + .storeRef(dataCell) + .storeBit(0) // No library + .endCell(); const contractAddress = new Address(0, stateInit.hash()); // get the hash of stateInit to get the address of our smart contract in workchain with ID 0 console.log(`Contract address: ${contractAddress.toString()}`); // Output contract address to console @@ -2843,7 +2904,7 @@ log.Println("Contract address:", contractAddress.String()) // Output contract ``` - + :::caution Everything we have detailed above follows the same steps as the contract [deployment via wallet](/v3/guidelines/smart-contracts/howto/wallet#contract-deployment-via-wallet) section. To better understanding, read the entire [GitHub source code](https://github.com/aSpite/wallet-tutorial). @@ -2851,7 +2912,7 @@ Everything we have detailed above follows the same steps as the contract [deploy ### Sending High-Load Wallet V2 Messages -Now let’s program a high-load wallet to send several messages at the same time. For example, let's take 12 messages per transaction so that the gas fees are small. +Now let’s program a high-load wallet to send several messages at the same time. For example, let's take 12 messages per transaction so that the gas fees are small. :::info High-load balance To complete the transaction, the balance of the contract must be at least 0.5 TON. @@ -2863,28 +2924,30 @@ Each message carry its own comment with code and the destination address will be ```js -import { Address, beginCell, Cell, toNano } from '@ton/core'; +import { Address, beginCell, Cell, toNano } from "@ton/core"; -let internalMessages:Cell[] = []; -const walletAddress = Address.parse('put your wallet address from which you deployed high-load wallet'); +let internalMessages: Cell[] = []; +const walletAddress = Address.parse( + "put your wallet address from which you deployed high-load wallet" +); for (let i = 0; i < 12; i++) { - const internalMessageBody = beginCell() - .storeUint(0, 32) - .storeStringTail(`Hello, TON! #${i}`) - .endCell(); - - const internalMessage = beginCell() - .storeUint(0x18, 6) // bounce - .storeAddress(walletAddress) - .storeCoins(toNano('0.01')) - .storeUint(0, 1 + 4 + 4 + 64 + 32) - .storeBit(0) // We do not have State Init - .storeBit(1) // We store Message Body as a reference - .storeRef(internalMessageBody) // Store Message Body Init as a reference - .endCell(); - - internalMessages.push(internalMessage); + const internalMessageBody = beginCell() + .storeUint(0, 32) + .storeStringTail(`Hello, TON! #${i}`) + .endCell(); + + const internalMessage = beginCell() + .storeUint(0x18, 6) // bounce + .storeAddress(walletAddress) + .storeCoins(toNano("0.01")) + .storeUint(0, 1 + 4 + 4 + 64 + 32) + .storeBit(0) // We do not have State Init + .storeBit(1) // We store Message Body as a reference + .storeRef(internalMessageBody) // Store Message Body Init as a reference + .endCell(); + + internalMessages.push(internalMessage); } ``` @@ -2956,14 +3019,14 @@ console.log(finalQueryID); // print query_id. With this query_id we can call GET const toSign = beginCell() .storeUint(698983191, 32) // subwallet_id .storeUint(finalQueryID, 64) - // Here we create our own method that will save the + // Here we create our own method that will save the // message mode and a reference to the message .storeDict(dictionary, Dictionary.Keys.Int(16), { serialize: (src, buidler) => { buidler.storeUint(3, 8); // save message mode, mode = 3 buidler.storeRef(src); // save message as reference }, - // We won't actually use this, but this method + // We won't actually use this, but this method // will help to read our dictionary that we saved parse: (src) => { let cell = beginCell() @@ -3042,27 +3105,27 @@ Next we’ll create an external message and send it to the blockchain using the ```js -import { TonClient } from '@ton/ton'; +import { TonClient } from "@ton/ton"; const body = beginCell() - .storeBuffer(signature) // store signature - .storeBuilder(toSign) // store our message - .endCell(); + .storeBuffer(signature) // store signature + .storeBuilder(toSign) // store our message + .endCell(); const externalMessage = beginCell() - .storeUint(0b10, 2) // indicate that it is an incoming external message - .storeUint(0, 2) // src -> addr_none - .storeAddress(highloadWalletAddress) - .storeCoins(0) // Import fee - .storeBit(0) // We do not have State Init - .storeBit(1) // We store Message Body as a reference - .storeRef(body) // Store Message Body as a reference - .endCell(); + .storeUint(0b10, 2) // indicate that it is an incoming external message + .storeUint(0, 2) // src -> addr_none + .storeAddress(highloadWalletAddress) + .storeCoins(0) // Import fee + .storeBit(0) // We do not have State Init + .storeBit(1) // We store Message Body as a reference + .storeRef(body) // Store Message Body as a reference + .endCell(); // We do not need a key here as we will be sending 1 request per second const client = new TonClient({ - endpoint: 'https://toncenter.com/api/v2/jsonRPC', - // apiKey: 'put your api key' // you can get an api key from @tonapibot bot in Telegram + endpoint: "https://toncenter.com/api/v2/jsonRPC", + // apiKey: 'put your api key' // you can get an api key from @tonapibot bot in Telegram }); client.sendFile(externalMessage.toBoc()); @@ -3140,22 +3203,22 @@ If you have any questions, comments, or suggestions please reach out to the auth The main sources of code: - - [@ton/ton (JS/TS)](https://github.com/ton-org/ton) - - [@ton/core (JS/TS)](https://github.com/ton-org/ton-core) - - [@ton/crypto (JS/TS)](https://github.com/ton-org/ton-crypto) - - [tonutils-go (GO)](https://github.com/xssnick/tonutils-go). +- [@ton/ton (JS/TS)](https://github.com/ton-org/ton) +- [@ton/core (JS/TS)](https://github.com/ton-org/ton-core) +- [@ton/crypto (JS/TS)](https://github.com/ton-org/ton-crypto) +- [tonutils-go (GO)](https://github.com/xssnick/tonutils-go). Official documentation: - - [Internal messages](/v3/documentation/smart-contracts/message-management/internal-messages) +- [Internal messages](/v3/documentation/smart-contracts/message-management/internal-messages) + +- [External messages](/v3/documentation/smart-contracts/message-management/external-messages) - - [External messages](/v3/documentation/smart-contracts/message-management/external-messages) +- [Types of Wallet Contracts](/v3/documentation/smart-contracts/contracts-specs/wallet-contracts#wallet-v4) - - [Types of Wallet Contracts](/v3/documentation/smart-contracts/contracts-specs/wallet-contracts#wallet-v4) - - - [TL-B](/v3/documentation/data-formats/tlb/tl-b-language) +- [TL-B](/v3/documentation/data-formats/tlb/tl-b-language) - - [Blockchain of Blockchains](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains) +- [Blockchain of Blockchains](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains) External references: From b8bb3ecdaae8ac0f5b0ef49566faff0014ca7a40 Mon Sep 17 00:00:00 2001 From: Antonoff <35700168+memearchivarius@users.noreply.github.com> Date: Fri, 7 Mar 2025 17:34:36 +0300 Subject: [PATCH 05/23] Update wallet.md --- .../smart-contracts/howto/wallet.md | 221 +++++++++--------- 1 file changed, 110 insertions(+), 111 deletions(-) diff --git a/docs/v3/guidelines/smart-contracts/howto/wallet.md b/docs/v3/guidelines/smart-contracts/howto/wallet.md index f9ab24a0d95..225b5cb3179 100644 --- a/docs/v3/guidelines/smart-contracts/howto/wallet.md +++ b/docs/v3/guidelines/smart-contracts/howto/wallet.md @@ -271,38 +271,38 @@ Since external messages do not include the Toncoin required to pay transaction f ### Transaction expiration -Another step used to check the validity of external messages is the `valid_until` field. As you can see from the variable name, this is the time in UNIX before the message is valid. If this verification process fails, the contract completes the processing of the transaction and returns the 35 exit code follows: +Another step used to check the validity of external messages is the `valid_until` field. As you can see from the variable name, this is the time in UNIX before the message is valid. If this verification process fails, the contract completes the processing of the transaction and returns the 35 exit code as follows: ```func var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32)); throw_if(35, valid_until <= now()); ``` -This algorithm works to protect against the susceptibility of various errors when the message is no longer valid but was still sent to the blockchain for an unknown reason. +This algorithm safeguards against potential errors, such as when a message is no longer valid but is still sent to the blockchain for an unknown reason. ### Wallet v3 and wallet v4 differences -The only difference between Wallet v3 and Wallet v4 is that Wallet v4 makes use of `plugins` that can be installed and deleted. These plugins are special smart contracts which are able to request a specific number of TON at a specific time from a wallet smart contract. +The key difference between Wallet v3 and Wallet v4 lies in Wallet v4’s support for `plugins`. Users can install or delete these plugins, which are specialized smart contracts capable of requesting a specific amount of TON from the wallet smart contract at a designated time. -Wallet smart contracts, in turn, will send the required amount of TON in response without the need for the owner to participate. This is similar to the **subscription model** for which plugins are created. We will not learn these details, because this is out of the scope of this tutorial. +Wallet smart contracts automatically send the required amount of TON in response to plugin requests without requiring the owner’s involvement. This functionality mirrors a **subscription model**, which is the primary purpose of plugins. We won’t delve into these details further as they fall outside the scope of this tutorial. ### How wallets facilitate communication with smart contracts -As we discussed earlier, a wallet smart contract accepts external messages, validates them and accepts them if all checks are passed. The contract then starts the loop of retrieving messages from the body of external messages then creates internal messages and sends them to the blockchain as follows: +As mentioned, a wallet smart contract accepts external messages, validates them, and processes them if all checks pass. Once the contract accepts a message, it begins a loop to extract messages from the body of the external message, creates internal messages, and sends them to the blockchain as shown below: ```func cs~touch(); while (cs.slice_refs()) { - var mode = cs~load_uint(8); ;; load message mode - send_raw_message(cs~load_ref(), mode); ;; get each new internal message as a cell with the help of load_ref() and send it + var mode = cs~load_uint(8); ;; load message mode + send_raw_message(cs~load_ref(), mode); ;; get each new internal message as a cell with the help of load_ref() and send it } ``` :::tip touch() -On TON, all smart contracts run on the stack-based TON Virtual Machine (TVM). ~ touch() places the variable `cs` on top of the stack to optimize the running of code for less gas. +On TON, all smart contracts run on the stack-based TON Virtual Machine (TVM). ~ touch() places the variable `cs` on top of the stack to optimize code running for less gas. ::: -Since a **maximum of 4 references** can be stored in one cell, we can send a maximum of 4 internal messages per external message. +Since a single cell can store **a maximum of 4 references**, we can send up to 4 internal messages per external message. > 💡 Useful links: > @@ -314,83 +314,83 @@ Since a **maximum of 4 references** can be stored in one cell, we can send a max ## 📬 External and internal messages -In this section, we’ll learn more about `internal` and `external` messages and we’ll create messages and send them to the network to minimize the use of pre-cooked functions. +This section will explore `internal` and `external` messages in more detail. We’ll create and send these messages to the network, minimizing reliance on pre-built functions. -To carry out this process it is necessary to make use of a ready-made wallet to make the task easier. To accomplish this: +To simplify this process, we’ll use a pre-built wallet. Here’s how to proceed: 1. Install the [wallet app](/v3/concepts/dive-into-ton/ton-ecosystem/wallet-apps) (e.g., Tonkeeper is used by the author) -2. Switch wallet app to v3r2 address version +2. Switch the wallet app to v3r2 address version 3. Deposit 1 TON into the wallet -4. Send the message to another address (you can send to yourself, to the same wallet). +4. Send the message to another address (you can send it to yourself, to the same wallet). -This way, the Tonkeeper wallet app will deploy the wallet contract and we can use it for the following steps. +This way, the Tonkeeper wallet app will deploy the wallet contract, which we can use for the following steps. :::note -At the time of writing, most wallet apps on TON by default use the wallet v4 version. Plugins are not required in this tutorial and we’ll make use of the functionality provided by wallet v3. During use, Tonkeeper allows the user to choose the version of the wallet they want. Therefore, it is recommended to deploy wallet version 3 (wallet v3). +At the time of writing, most wallet apps on TON default to wallet v4. However, since plugins are not required for this tutorial, we’ll use the functionality provided by wallet v3. Tonkeeper allows users to select their preferred wallet version, so it’s recommended to deploy wallet v3. ::: ### TL-B -As noted, everything in TON Blockchain is a smart contract consisting of cells. To properly serialize and deserialize the data we need standards. To accomplish the serialization and deserialization process, `TL-B` was created as a universal tool to describe different data types in different ways with different sequences inside cells. +As mentioned earlier, everything in the TON Blockchain is a smart contract composed of cells. Standards are essential to ensure proper serialization and deserialization of data. For this purpose, `TL-B` was developed as a universal tool to describe various data types, structures, and sequences within cells. -In this section, we’ll examine [block.tlb](https://github.com/ton-blockchain/ton/blob/master/crypto/block/block.tlb). This file will be very useful during future development, as it describes how different cells should be assembled. In our case specifically, it details the intricacies of internal and external messages. +In this section, we’ll explore [block.tlb](https://github.com/ton-blockchain/ton/blob/master/crypto/block/block.tlb). This file will be invaluable for future development as it outlines how to assemble different types of cells. Specifically for our purposes, it provides detailed information about the structure and behavior of internal and external messages. :::info -Basic information will be provided within this guide. For further details, please refer to our TL-B [documentation](/v3/documentation/data-formats/tlb/tl-b-language) to learn more about TL-B. +This guide provides basic information. For further details, please refer to our TL-B [documentation](/v3/documentation/data-formats/tlb/tl-b-language) to learn more about TL-B. ::: ### CommonMsgInfo Initially, each message must first store `CommonMsgInfo` ([TL-B](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L123-L130)) or `CommonMsgInfoRelaxed` ([TL-B](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L132-L137)). This allows us to define technical details that relate to the message type, message time, recipient address, technical flags, and fees. -By reading `block.tlb` file, we can notice three types of CommonMsgInfo: `int_msg_info$0`, `ext_in_msg_info$10`, `ext_out_msg_info$11`. We will not go into specific details detailing the specificities of the `ext_out_msg_info` TL-B structure. That said, it is an external message type that a smart contract can send for using as external logs. For examples of this format, consider having a closer look at the [Elector](https://tonscan.org/address/Ef8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM0vF) contract. +By reading the `block.tlb` file, we can notice three types of CommonMsgInfo: `int_msg_info$0`, `ext_in_msg_info$10`, `ext_out_msg_info$11`. We will not go into specific details detailing the specificities of the `ext_out_msg_info` TL-B structure. That said, it is an external message type that a smart contract can send to use as an external log. For examples of this format, consider having a closer look at the [Elector](https://tonscan.org/address/Ef8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM0vF) contract. -[Looking at TL-B](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L127-L128), you’ll notice that **only the CommonMsgInfo is available when used with the ext_in_msg_info type**. This is because message fields such as `src`, `created_lt`, `created_at`, and others are rewritten by validators during transaction handling. In this case, the `src` field in message is most important because when messages are sent, the sender is unknown, and is written by validators during verification. This ensures that the address in the `src` field is correct and cannot be manipulated. +When examining [TL-B](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L127-L128), you’ll notice that **only `CommonMsgInfo` is available when using the `ext_in_msg_info` type**. It is because fields like `src`, `created_lt`, `created_at`, and others are overwritten by validators during transaction processing. Among these, the `src` field is particularly important. Since the sender’s address is unknown when the message is sent, validators populate this field during verification. This ensures the `src` address is accurate and cannot be tampered with. -However, the `CommonMsgInfo` structure only supports the `MsgAddress` specification, but the sender’s address is typically unknown and it is required to write the `addr_none` (two zero bits `00`). In this case, the `CommonMsgInfoRelaxed` structure is used, which supports the `addr_none` address. For the `ext_in_msg_info` (used for incoming external messages), the `CommonMsgInfo` structure is used because these message types don’t make use of a sender and always use the [MsgAddressExt](https://hub.com/ton/ton.blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L100) structure (the `addr_none$00` meaning two zero bits), which means there is no need to overwrite the data. +However, the `CommonMsgInfo` structure only supports the `MsgAddress` specification. Since the sender’s address is typically unknown, it’s necessary to use `addr_none` (represented by two zero bits `00`). The `CommonMsgInfoRelaxed` structure is used in such cases, as it supports the `addr_none` address. For `ext_in_msg_info` (used for incoming external messages), the `CommonMsgInfo` structure is sufficient because these messages don’t require a sender and always use the [MsgAddressExt](https://hub.com/ton/ton.blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L100) structure (represented by `addr_none$00`, meaning two zero bits). This eliminates the need to overwrite the data. :::note -The numbers after `$` symbol are the bits that are required to store at the beginning of a certain structure, for further identification of these structures during reading (deserialization). +The numbers after the `$` symbol are the bits that are required to be stored at the beginning of a specific structure for further identification of these structures during reading (deserialization). ::: ### Internal Message Creation -Internal messages are used to send messages between contracts. When analyzing various contract types (such as [NFTs](https://github.com/ton-blockchain/token-contract/blob/f2253cb0f0e1ae0974d7dc0cef3a62cb6e19f806/nft/nft-item.fc#L51-L56) and [Jetons](https://github.com/ton-blockchain/token-contract/blob/f2253cb0f0e1ae0974d7dc0cef3a62cb6e19f806/ft/jetton-wallet.fc#L139-L144)) that send messages where the writing of contracts is considered, the following lines of code are often used: +Internal messages facilitate communication between contracts. When examining various contract types, such as [NFTs](https://github.com/ton-blockchain/token-contract/blob/f2253cb0f0e1ae0974d7dc0cef3a62cb6e19f806/nft/nft-item.fc#L51-L56) and [Jettons](https://github.com/ton-blockchain/token-contract/blob/f2253cb0f0e1ae0974d7dc0cef3a62cb6e19f806/ft/jetton-wallet.fc#L139-L144), you’ll often encounter the following lines of code, which are commonly used when writing contracts that send messages: ```func var msg = begin_cell() - .store_uint(0x18, 6) ;; or 0x10 for non-bounce - .store_slice(to_address) - .store_coins(amount) - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) - ;; store something as a body + .store_uint(0x18, 6) ;; or 0x10 for non-bounce + .store_slice(to_address) + .store_coins(amount) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) + ;; store something as a body ``` -Let’s first consider `0x18` and `0x10` (x - hexadecimal), which are hexadecimal numbers laid out in the following manner (given that we allocate 6 bits): `011000` and `010000`. This means that the code above can be overwritten as follows: +Let’s examine `0x18` and `0x10` (where `x` denotes hexadecimal). These numbers can be represented in binary as `011000` and `010000`, respectively, assuming we allocate 6 bits. This means the code above can be rewritten as follows: ```func var msg = begin_cell() - .store_uint(0, 1) ;; this bit indicates that we send an internal message according to int_msg_info$0 - .store_uint(1, 1) ;; IHR Disabled - .store_uint(1, 1) ;; or .store_uint(0, 1) for 0x10 | bounce - .store_uint(0, 1) ;; bounced - .store_uint(0, 2) ;; src -> two zero bits for addr_none - .store_slice(to_address) - .store_coins(amount) - .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) - ;; store something as a body + .store_uint(0, 1) ;; this bit indicates that we send an internal message according to int_msg_info$0 + .store_uint(1, 1) ;; IHR Disabled + .store_uint(1, 1) ;; or .store_uint(0, 1) for 0x10 | bounce + .store_uint(0, 1) ;; bounced + .store_uint(0, 2) ;; src -> two zero bits for addr_none + .store_slice(to_address) + .store_coins(amount) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) + ;; store something as a body ``` -Now let’s go through each option in detail: +Now, let’s go through each option in detail: | Option | Explanation | | :----------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -| IHR Disabled | Currently, this option is disabled (which means we store 1) because Instant Hypercube Routing is not fully implemented. In addition, this will be needed when a large number of [Shardchains](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains#many-accountchains-shards) are live on the network. More can be read about the IHR Disabled option in the [tblkch.pdf](https://ton.org/tblkch.pdf) (chapter 2). | -| Bounce | While sending messages, a variety of errors can occur during smart contract processing. To avoid losing TON, it is necessary to set the Bounce option to 1 (true). In this case, if any contract errors occur during transaction processing, the message will be returned to the sender, and the same amount of TON will be received minus fees. More can be read about non-bounceable messages [here](/v3/documentation/smart-contracts/message-management/non-bounceable-messages). | -| Bounced | Bounced messages are messages that are returned to the sender because an error occurred while processing the transaction with a smart contract. This option tells you whether the message received is bounced or not. | -| Src | The Src is the sender address. In this case, two zero bits are written to indicate the `addr_none` address. | +| IHR Disabled | Currently, this option is disabled (meaning we store `1`) because Instant Hypercube Routing (IHR) is not yet fully implemented. This option will become relevant once many [Shardchains](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains#many-accountchains-shards) are active on the network. For more details about the IHR Disabled option, refer to [tblkch.pdf](https://ton.org/tblkch.pdf) (chapter 2). | +| Bounce | When sending messages, errors can occur during smart contract processing. Setting the `Bounce` option to `1` (true) is essential to prevent TON loss. If any errors arise during transaction processing, the message will be returned to the sender, and the same amount of TON (minus fees) will be refunded. Refer to [this guide](/v3/documentation/smart-contracts/message-management/non-bounceable-messages) for more details on non-bounceable messages. | +| Bounced | Bounced messages are those returned to the sender due to an error during transaction processing with a smart contract. This option indicates whether the received message is bounced or not. | +| Src | The Src is the sender's address. In this case, two zero bits are written to indicate the `addr_none` address. | -The next two lines of code: +The following two lines of code: ```func ... @@ -405,34 +405,33 @@ Finally, let’s look at the remaining lines of code: ```func ... - .store_uint(0, 1) ;; Extra currency - .store_uint(0, 4) ;; IHR fee - .store_uint(0, 4) ;; Forwarding fee - .store_uint(0, 64) ;; Logical time of creation - .store_uint(0, 32) ;; UNIX time of creation - .store_uint(0, 1) ;; State Init - .store_uint(0, 1) ;; Message body - ;; store something as a body + .store_uint(0, 1) ;; Extra currency + .store_uint(0, 4) ;; IHR fee + .store_uint(0, 4) ;; Forwarding fee + .store_uint(0, 64) ;; Logical time of creation + .store_uint(0, 32) ;; UNIX time of creation + .store_uint(0, 1) ;; State Init + .store_uint(0, 1) ;; Message body + ;; store something as a body ``` | Option | Explanation | | :----------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | | Extra currency | This is a native implementation of existing jettons and is not currently in use. | -| IHR fee | As mentioned, the IHR is not currently in use, so this fee is always zero. More can be read about this in the [tblkch.pdf](https://ton.org/tblkch.pdf) (3.1.8). | -| Forwarding fee | A forwarding message fee. More can be read about this in the [fees documentation](/v3/documentation/smart-contracts/transaction-fees/fees-low-level#transactions-and-phases). | +| IHR fee | As mentioned, IHR is not currently used, so this fee is always zero. For more information, refer to [tblkch.pdf](https://ton.org/tblkch.pdf) (section 3.1.8). | +| Forwarding fee | A forwarding message fee. For more information, refer to [fees documentation](/v3/documentation/smart-contracts/transaction-fees/fees-low-level#transactions-and-phases). | | Logical time of creation | The time used to create the correct messages queue. | | UNIX time of creation | The time the message was created in UNIX. | -| State Init | Code and source data for deploying a smart contract. If the bit is set to `0`, it means that we do not have a State Init. But if it is set to `1`, then another bit needs to be written which indicates whether the State Init is stored in the same cell (0) or written as a reference (1). | -| Message body | This part defines how the message body is stored. At times the message body is too large to fit into the message itself. In this case, it should be stored as a **reference** whereby the bit is set to `1` to show that the body is used as a reference. If the bit is `0`, the body is in the same cell as the message. | +| State Init | The code and source data for deploying a smart contract. If the bit is set to `0`, there is no State Init. However, if it’s set to `1`, an additional bit is required to indicate whether the State Init is stored in the same cell (`0`) or written as a reference (`1`). | +| Message body | This section determines how the message body is stored. If the message body is too large to fit directly into the message, it is stored as a **reference**. In this case, the bit is set to `1` to indicate that the body is stored as a reference. If the bit is `0`, the body resides in the same cell as the message. | -The values outlined above (including src) excluding the State Init and the Message Body bits, are rewritten by validators. +Validators rewrite the above values (including src), excluding the State Init and the Message Body bits. :::note -If the number value fits within fewer bits than is specified, then the missing zeros are added to the left side of the value. For example, 0x18 fits within 5 bits -> `11000`. However, since 6 bits were specified, the end result becomes `011000`. +If the number value fits within fewer bits than is specified, then the missing zeros are added to the left side of the value. For example, 0x18 fits within 5 bits -> `11000`. However, since 6 bits were specified, the result becomes `011000`. ::: -Next, we’ll begin preparing a message, which will be sent Toncoins to another wallet v3. -First, let’s say a user wants to send 0.5 TON to themselves with the text "**Hello, TON!**", refer to this section of our documentation to learn ([How to send message with a comment](/v3/documentation/smart-contracts/func/cookbook#how-to-send-a-simple-message)). +Next, we’ll prepare a message to send Toncoins to another wallet v3. For example, let’s say a user wants to send 0.5 TON to themselves with the comment "**Hello, TON!**". To learn how to send a message with a comment, refer to this documentation section: [How to Send a Simple Message](/v3/documentation/smart-contracts/func/cookbook#how-to-send-a-simple-message). @@ -441,9 +440,9 @@ First, let’s say a user wants to send 0.5 TON to themselves with the text "**H import { beginCell } from "@ton/core"; let internalMessageBody = beginCell() - .storeUint(0, 32) // write 32 zero bits to indicate that a text comment will follow - .storeStringTail("Hello, TON!") // write our text comment - .endCell(); + .storeUint(0, 32) // write 32 zero bits to indicate that a text comment will follow + .storeStringTail("Hello, TON!") // write our text comment + .endCell(); ``` @@ -451,7 +450,7 @@ let internalMessageBody = beginCell() ```go import ( - "github.com/xssnick/tonutils-go/tvm/cell" + "github.com/xssnick/tonutils-go/tvm/cell" ) internalMessageBody := cell.BeginCell(). @@ -463,7 +462,7 @@ internalMessageBody := cell.BeginCell(). -Above we created an `InternalMessageBody` in which the body of our message is stored. Note that when storing text that does not fit into a single Cell (1023 bits), it is necessary **to split the data into several cells** according to [the following documentation](/v3/documentation/smart-contracts/message-management/internal-messages). However, in this case the high-level libraries creates cells according to requirements, so at this stage there is no need to worry about it. +Above, we created an `InternalMessageBody` to store the body of our message. Note that if the text exceeds the capacity of a single Cell (1023 bits), it’s necessary to **split the data into multiple cells**, as outlined in [this documentation](/v3/documentation/smart-contracts/message-management/internal-messages). However, high-level libraries handle cell creation according to the requirements in this case, so there’s no need to worry about it at this stage. Next, the `InternalMessage` is created according to the information we have studied earlier as follows: @@ -476,22 +475,22 @@ import { toNano, Address } from "@ton/ton"; const walletAddress = Address.parse("put your wallet address"); let internalMessage = beginCell() - .storeUint(0, 1) // indicate that it is an internal message -> int_msg_info$0 - .storeBit(1) // IHR Disabled - .storeBit(1) // bounce - .storeBit(0) // bounced - .storeUint(0, 2) // src -> addr_none - .storeAddress(walletAddress) - .storeCoins(toNano("0.2")) // amount - .storeBit(0) // Extra currency - .storeCoins(0) // IHR Fee - .storeCoins(0) // Forwarding Fee - .storeUint(0, 64) // Logical time of creation - .storeUint(0, 32) // UNIX time of creation - .storeBit(0) // No State Init - .storeBit(1) // We store Message Body as a reference - .storeRef(internalMessageBody) // Store Message Body as a reference - .endCell(); + .storeUint(0, 1) // indicate that it is an internal message -> int_msg_info$0 + .storeBit(1) // IHR Disabled + .storeBit(1) // bounce + .storeBit(0) // bounced + .storeUint(0, 2) // src -> addr_none + .storeAddress(walletAddress) + .storeCoins(toNano("0.2")) // amount + .storeBit(0) // Extra currency + .storeCoins(0) // IHR Fee + .storeCoins(0) // Forwarding Fee + .storeUint(0, 64) // Logical time of creation + .storeUint(0, 32) // UNIX time of creation + .storeBit(0) // No State Init + .storeBit(1) // We store Message Body as a reference + .storeRef(internalMessageBody) // Store Message Body as a reference + .endCell(); ``` @@ -529,7 +528,7 @@ internalMessage := cell.BeginCell(). ### Creating a Message -It is necessary to retrieve the `seqno` (sequence number) of our wallet smart contract. To accomplish this, a `Client` is created which will be used to send a request to run the Get method "seqno" of our wallet. It is also necessary to add a seed phrase (which you saved during creating a wallet [here](#--external-and-internal-messages)) to sign our message via the following steps: +We need to create a ' Client ' to retrieve our wallet smart contract's `seqno` (sequence number). This client will send a request to execute the Get method `seqno` on our wallet. Additionally, we must include the seed phrase (saved during wallet creation [here](#--external-and-internal-messages)) to sign our message. Follow these steps to proceed: @@ -577,7 +576,7 @@ if err != nil { } client := ton.NewAPIClient(connection) // create client -block, err := client.CurrentMasterchainInfo(context.Background()) // get current block, we will need it in requests to LiteServer +block, err := client.CurrentMasterchainInfo(context.Background()) // get the current block, we will need it in requests to LiteServer if err != nil { log.Fatalln("CurrentMasterchainInfo err:", err.Error()) return @@ -593,7 +592,7 @@ seqno := getMethodResult.MustInt(0) // get seqno from response // The next three lines will extract the private key using the mnemonic phrase. We will not go into cryptographic details. With the tonutils-go library, this is all implemented, but we’re doing it again to get a full understanding. mac := hmac.New(sha512.New, []byte(strings.Join(mnemonic, " "))) hash := mac.Sum(nil) -k := pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries "TON default seed" is used as salt when getting keys +k := pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries, "TON default seed" is used as salt when getting keys privateKey := ed25519.NewKeyFromSeed(k) ``` @@ -601,7 +600,7 @@ privateKey := ed25519.NewKeyFromSeed(k) -Therefore, the `seqno`, `keys`, and `internal message` need to be sent. Now we need to create a [message](/v3/documentation/smart-contracts/message-management/sending-messages) for our wallet and store the data in this message in the sequence used at the beginning of the tutorial. This is accomplished as follows: +To proceed, we need to send the `seqno`, `keys`, and `internal message`. Next, we’ll create a [message](/v3/documentation/smart-contracts/message-management/sending-messages) for our wallet and store the data in the sequence outlined at the beginning of the tutorial. This is achieved as follows: @@ -610,18 +609,18 @@ Therefore, the `seqno`, `keys`, and `internal message` need to be sent. Now we n import { sign } from "@ton/crypto"; let toSign = beginCell() - .storeUint(698983191, 32) // subwallet_id | We consider this further - .storeUint(Math.floor(Date.now() / 1e3) + 60, 32) // Message expiration time, +60 = 1 minute - .storeUint(seqno, 32) // store seqno - .storeUint(3, 8) // store mode of our internal message - .storeRef(internalMessage); // store our internalMessage as a reference + .storeUint(698983191, 32) // subwallet_id | We consider this further + .storeUint(Math.floor(Date.now() / 1e3) + 60, 32) // Message expiration time, +60 = 1 minute + .storeUint(seqno, 32) // store seqno + .storeUint(3, 8) // store mode of our internal message + .storeRef(internalMessage); // store our internalMessage as a reference -let signature = sign(toSign.endCell().hash(), keyPair.secretKey); // get the hash of our message to wallet smart contract and sign it to get signature +let signature = sign(toSign.endCell().hash(), keyPair.secretKey); // get the hash of our message to the wallet smart contract and sign it to get signature let body = beginCell() - .storeBuffer(signature) // store signature - .storeBuilder(toSign) // store our message - .endCell(); + .storeBuffer(signature) // store signature + .storeBuilder(toSign) // store our message + .endCell(); ``` @@ -639,7 +638,7 @@ toSign := cell.BeginCell(). MustStoreUInt(uint64(3), 8). // store mode of our internal message MustStoreRef(internalMessage) // store our internalMessage as a reference -signature := ed25519.Sign(privateKey, toSign.EndCell().Hash()) // get the hash of our message to wallet smart contract and sign it to get signature +signature := ed25519.Sign(privateKey, toSign.EndCell().Hash()) // get the hash of our message to the wallet smart contract and sign it to get the signature body := cell.BeginCell(). MustStoreSlice(signature, 512). // store signature @@ -650,29 +649,29 @@ body := cell.BeginCell(). -Note that here no `.endCell()` was used in the definition of the `toSign`. The fact is that in this case it is necessary **to transfer toSign content directly to the message body**. If writing a cell was required, it would have to be stored as a reference. +Note that no `.endCell()` was used in the definition of the `toSign` here. In this case, it is necessary **to transfer toSign content directly to the message body**. If writing a cell was required, it would have to be stored as a reference. :::tip Wallet V4 -In addition to basic verification process we learned bellow for the Wallet V3, Wallet V4 smart contracts [extracts the opcode to determine whether a simple translation or a message associated with the plugin](https://github.com/ton-blockchain/wallet-contract/blob/4111fd9e3313ec17d99ca9b5b1656445b5b49d8f/func/wallet-v4-code.fc#L94-L100) is required. To match this version, it is necessary to add the `storeUint(0, 8).` (JS/TS), `MustStoreUInt(0, 8).` (Golang) functions after writing the seqno (sequence number) and before specifying the transaction mode. +In addition to the basic verification process we learned above for the Wallet V3, Wallet V4 smart contracts [extract the opcode to determine whether a simple translation or a message associated with the plugin](https://github.com/ton-blockchain/wallet-contract/blob/4111fd9e3313ec17d99ca9b5b1656445b5b49d8f/func/wallet-v4-code.fc#L94-L100) is required. To match this version, it is necessary to add the `storeUint(0, 8).` (JS/TS), `MustStoreUInt(0, 8).` (Golang) functions after writing the seqno (sequence number) and before specifying the transaction mode. ::: ### External Message Creation -To deliver any internal message to a blockchain from the outside world, it is necessary to send it within an external message. As we have previously considered, it is necessary to only make use of the `ext_in_msg_info$10` structure, as the goal is to send an external message to our contract. Now, let's create an external message that will be sent to our wallet: +To deliver an internal message to the blockchain from the outside world, it must be sent within an external message. As previously discussed, we’ll use the `ext_in_msg_info$10` structure since our goal is to send an external message to our contract. Now, let’s create the external message that will be sent to our wallet: ```js let externalMessage = beginCell() - .storeUint(0b10, 2) // 0b10 -> 10 in binary - .storeUint(0, 2) // src -> addr_none - .storeAddress(walletAddress) // Destination address - .storeCoins(0) // Import Fee - .storeBit(0) // No State Init - .storeBit(1) // We store Message Body as a reference - .storeRef(body) // Store Message Body as a reference - .endCell(); + .storeUint(0b10, 2) // 0b10 -> 10 in binary + .storeUint(0, 2) // src -> addr_none + .storeAddress(walletAddress) // Destination address + .storeCoins(0) // Import Fee + .storeBit(0) // No State Init + .storeBit(1) // We store Message Body as a reference + .storeRef(body) // Store Message Body as a reference + .endCell(); ``` @@ -696,15 +695,15 @@ externalMessage := cell.BeginCell(). | Option | Explanation | | :----------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | | Src | The sender address. Since an incoming external message cannot have a sender, there will always be 2 zero bits (an addr_none [TL-B](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L100)). | -| Import Fee | The fee used to pay for importing incoming external messages. | -| State Init | Unlike the Internal Message, the State Init within the external message is needed **to deploy a contract from the outside world**. The State Init used in conjunction with the Internal Message allows one contract to deploy another. | +| Import Fee | The fee for importing incoming external messages. | +| State Init | Unlike the Internal Message, the State Init within the external message is needed **to deploy a contract from the outside world**. The State Init used with the Internal Message allows one contract to deploy another. | | Message Body | The message that must be sent to the contract for processing. | :::tip 0b10 -0b10 (b - binary) denotes a binary record. In this process, two bits are stored: `1` and `0`. Thus we specify that it's `ext_in_msg_info$10`. +0b10 (b - binary) denotes a binary record. Two bits are stored in this process: `1` and `0`. Thus, we specify that it's `ext_in_msg_info$10`. ::: -Now we have a completed message that is ready to be sent to our contract. To accomplish this, it should first be serialized to a `BOC` ([Bag of Cells](/v3/documentation/data-formats/tlb/cell-boc#bag-of-cells)), then be sent using the following code: +Now that we have a completed message ready to send to our contract, the next step is to serialize it into a `BOC` ([Bag of Cells](/v3/documentation/data-formats/tlb/cell-boc#bag-of-cells)). Once serialized, we can send it using the following code: @@ -742,7 +741,7 @@ if err != nil { > > [More about Bag of Cells](/v3/documentation/data-formats/tlb/cell-boc#bag-of-cells) -As a result, we got the output of our BOC in the console and the message sent to our wallet. By copying the base64 encoded string, it is possible to [manually send our message and retrieve the hash using toncenter](https://toncenter.com/api/v2/#/send/send_boc_return_hash_sendBocReturnHash_post). +As a result, we got the output of our BOC in the console, and the message was sent to our wallet. By copying the base64 encoded string, it is possible to [manually send our message and retrieve the hash using toncenter](https://toncenter.com/api/v2/#/send/send_boc_return_hash_sendBocReturnHash_post). ## 👛 Deploying a Wallet From c26e7c5a47fccb0b971e83dd859eb37f137dc044 Mon Sep 17 00:00:00 2001 From: AlexG <39581753+reveloper@users.noreply.github.com> Date: Sat, 8 Mar 2025 17:41:40 +0900 Subject: [PATCH 06/23] Update wallet.md style update --- .../smart-contracts/howto/wallet.md | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/v3/guidelines/smart-contracts/howto/wallet.md b/docs/v3/guidelines/smart-contracts/howto/wallet.md index 225b5cb3179..30f0f5eee5a 100644 --- a/docs/v3/guidelines/smart-contracts/howto/wallet.md +++ b/docs/v3/guidelines/smart-contracts/howto/wallet.md @@ -183,7 +183,7 @@ This tutorial may not explain particular details on occasion. In these cases, mo All wallets operating on the TON Blockchain are smart contracts, and everything running on TON functions as a smart contract. Like most blockchains, TON allows users to deploy and customize smart contracts for various purposes, enabling full wallet customization. Wallet smart contracts on TON facilitate communication between the platform and other types of smart contracts. However, it’s essential to understand how wallet communication works. -### Wallet Communication +### Wallet сommunication Generally, TON Blockchain has two message types: `internal` and `external`. External messages allow sending messages to the blockchain from the outside world, thus allowing communication with smart contracts that accept such messages. The function responsible for carrying out this process is as follows: @@ -282,7 +282,7 @@ This algorithm safeguards against potential errors, such as when a message is no ### Wallet v3 and wallet v4 differences -The key difference between Wallet v3 and Wallet v4 lies in Wallet v4’s support for `plugins`. Users can install or delete these plugins, which are specialized smart contracts capable of requesting a specific amount of TON from the wallet smart contract at a designated time. +The key difference between wallet v3 and wallet v4 lies in wallet v4’s support for `plugins`. Users can install or delete these plugins, which are specialized smart contracts capable of requesting a specific amount of TON from the wallet smart contract at a designated time. Wallet smart contracts automatically send the required amount of TON in response to plugin requests without requiring the owner’s involvement. This functionality mirrors a **subscription model**, which is the primary purpose of plugins. We won’t delve into these details further as they fall outside the scope of this tutorial. @@ -333,7 +333,7 @@ At the time of writing, most wallet apps on TON default to wallet v4. However, s As mentioned earlier, everything in the TON Blockchain is a smart contract composed of cells. Standards are essential to ensure proper serialization and deserialization of data. For this purpose, `TL-B` was developed as a universal tool to describe various data types, structures, and sequences within cells. -In this section, we’ll explore [block.tlb](https://github.com/ton-blockchain/ton/blob/master/crypto/block/block.tlb). This file will be invaluable for future development as it outlines how to assemble different types of cells. Specifically for our purposes, it provides detailed information about the structure and behavior of internal and external messages. +This section will explore [block.tlb](https://github.com/ton-blockchain/ton/blob/master/crypto/block/block.tlb). This file will be invaluable for future development as it outlines how to assemble different types of cells. Specifically for our purposes, it provides detailed information about the structure and behavior of internal and external messages. :::info This guide provides basic information. For further details, please refer to our TL-B [documentation](/v3/documentation/data-formats/tlb/tl-b-language) to learn more about TL-B. @@ -350,10 +350,10 @@ When examining [TL-B](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9 However, the `CommonMsgInfo` structure only supports the `MsgAddress` specification. Since the sender’s address is typically unknown, it’s necessary to use `addr_none` (represented by two zero bits `00`). The `CommonMsgInfoRelaxed` structure is used in such cases, as it supports the `addr_none` address. For `ext_in_msg_info` (used for incoming external messages), the `CommonMsgInfo` structure is sufficient because these messages don’t require a sender and always use the [MsgAddressExt](https://hub.com/ton/ton.blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L100) structure (represented by `addr_none$00`, meaning two zero bits). This eliminates the need to overwrite the data. :::note -The numbers after the `$` symbol are the bits that are required to be stored at the beginning of a specific structure for further identification of these structures during reading (deserialization). +The numbers after the `$` symbol are the bits that must be stored at the beginning of a specific structure for further identification of these structures during reading (deserialization). ::: -### Internal Message Creation +### Internal message creation Internal messages facilitate communication between contracts. When examining various contract types, such as [NFTs](https://github.com/ton-blockchain/token-contract/blob/f2253cb0f0e1ae0974d7dc0cef3a62cb6e19f806/nft/nft-item.fc#L51-L56) and [Jettons](https://github.com/ton-blockchain/token-contract/blob/f2253cb0f0e1ae0974d7dc0cef3a62cb6e19f806/ft/jetton-wallet.fc#L139-L144), you’ll often encounter the following lines of code, which are commonly used when writing contracts that send messages: @@ -366,7 +366,7 @@ var msg = begin_cell() ;; store something as a body ``` -Let’s examine `0x18` and `0x10` (where `x` denotes hexadecimal). These numbers can be represented in binary as `011000` and `010000`, respectively, assuming we allocate 6 bits. This means the code above can be rewritten as follows: +Let’s examine `0x18` and `0x10` (where `x` denotes hexadecimal). These numbers can be represented in binary as `011000` and `010000`, assuming we allocate 6 bits. This means the code above can be rewritten as follows: ```func var msg = begin_cell() @@ -388,7 +388,7 @@ Now, let’s go through each option in detail: | IHR Disabled | Currently, this option is disabled (meaning we store `1`) because Instant Hypercube Routing (IHR) is not yet fully implemented. This option will become relevant once many [Shardchains](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains#many-accountchains-shards) are active on the network. For more details about the IHR Disabled option, refer to [tblkch.pdf](https://ton.org/tblkch.pdf) (chapter 2). | | Bounce | When sending messages, errors can occur during smart contract processing. Setting the `Bounce` option to `1` (true) is essential to prevent TON loss. If any errors arise during transaction processing, the message will be returned to the sender, and the same amount of TON (minus fees) will be refunded. Refer to [this guide](/v3/documentation/smart-contracts/message-management/non-bounceable-messages) for more details on non-bounceable messages. | | Bounced | Bounced messages are those returned to the sender due to an error during transaction processing with a smart contract. This option indicates whether the received message is bounced or not. | -| Src | The Src is the sender's address. In this case, two zero bits are written to indicate the `addr_none` address. | +| Src | The Src is the sender's address. In this case, two zero bits indicate the `addr_none` address. | The following two lines of code: @@ -526,9 +526,9 @@ internalMessage := cell.BeginCell(). -### Creating a Message +### Creating a message -We need to create a ' Client ' to retrieve our wallet smart contract's `seqno` (sequence number). This client will send a request to execute the Get method `seqno` on our wallet. Additionally, we must include the seed phrase (saved during wallet creation [here](#--external-and-internal-messages)) to sign our message. Follow these steps to proceed: +We must create a `client` to retrieve our wallet smart contract's `seqno` (sequence number). This client will send a request to execute the Get method `seqno` on our wallet. Additionally, we must include the seed phrase (saved during wallet creation [here](#--external-and-internal-messages)) to sign our message. Follow these steps to proceed: @@ -649,13 +649,13 @@ body := cell.BeginCell(). -Note that no `.endCell()` was used in the definition of the `toSign` here. In this case, it is necessary **to transfer toSign content directly to the message body**. If writing a cell was required, it would have to be stored as a reference. +Note that no `.endCell()` was used in defining the `toSign` here. In this case, it is necessary **to transfer toSign content directly to the message body**. If writing a cell was required, it would have to be stored as a reference. :::tip Wallet V4 -In addition to the basic verification process we learned above for the Wallet V3, Wallet V4 smart contracts [extract the opcode to determine whether a simple translation or a message associated with the plugin](https://github.com/ton-blockchain/wallet-contract/blob/4111fd9e3313ec17d99ca9b5b1656445b5b49d8f/func/wallet-v4-code.fc#L94-L100) is required. To match this version, it is necessary to add the `storeUint(0, 8).` (JS/TS), `MustStoreUInt(0, 8).` (Golang) functions after writing the seqno (sequence number) and before specifying the transaction mode. +In addition to the basic verification process we learned above for the Wallet V3, Wallet V4 smart contracts [extract the opcode to determine whether a simple translation or a message associated with the plugin](https://github.com/ton-blockchain/wallet-contract/blob/4111fd9e3313ec17d99ca9b5b1656445b5b49d8f/func/wallet-v4-code.fc#L94-L100) is required. To match this version, it is necessary to add the `storeUint(0, 8).` (JS/TS), `MustStoreUInt(0, 8).` (Golang) functions after writing the **sequence number (seqno)** and before specifying the transaction mode. ::: -### External Message Creation +### External message creation To deliver an internal message to the blockchain from the outside world, it must be sent within an external message. As previously discussed, we’ll use the `ext_in_msg_info$10` structure since our goal is to send an external message to our contract. Now, let’s create the external message that will be sent to our wallet: @@ -697,7 +697,7 @@ externalMessage := cell.BeginCell(). | Src | The sender address. Since an incoming external message cannot have a sender, there will always be 2 zero bits (an addr_none [TL-B](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L100)). | | Import Fee | The fee for importing incoming external messages. | | State Init | Unlike the Internal Message, the State Init within the external message is needed **to deploy a contract from the outside world**. The State Init used with the Internal Message allows one contract to deploy another. | -| Message Body | The message that must be sent to the contract for processing. | +| Message Body | The message must be sent to the contract for processing. | :::tip 0b10 0b10 (b - binary) denotes a binary record. Two bits are stored in this process: `1` and `0`. Thus, we specify that it's `ext_in_msg_info$10`. @@ -743,15 +743,15 @@ if err != nil { As a result, we got the output of our BOC in the console, and the message was sent to our wallet. By copying the base64 encoded string, it is possible to [manually send our message and retrieve the hash using toncenter](https://toncenter.com/api/v2/#/send/send_boc_return_hash_sendBocReturnHash_post). -## 👛 Deploying a Wallet +## 👛 Deploying a wallet -We have learned the basics of creating messages, which will now be helpful for deploying the wallet. In the past, we have deployed wallet via wallet app, but in this case we’ll need to deploy our wallet manually. +We have learned the basics of creating messages, which will now help deploy the wallet. In the past, we have deployed wallet via the wallet app, but in this case, we’ll need to deploy our wallet manually. -In this section we’ll go over how to create a wallet (wallet v3) from scratch. You’ll learn how to compile the code for a wallet smart contract, generate a mnemonic phrase, receive a wallet address, and deploy a wallet using external messages and State Init (state initialization). +In this section, we’ll review how to create a wallet (wallet v3) from scratch. You’ll learn how to compile the code for a wallet smart contract, generate a mnemonic phrase, receive a wallet address, and deploy a wallet using external messages and State Init (state initialization). ### Generating a Mnemonic -The first thing needed to correctly create a wallet is to retrieve a `private` and `public` key. To accomplish this task, it is necessary to generate a mnemonic seed phrase and then extract private and public keys using cryptographic libraries. +The first thing needed to create a wallet correctly is to retrieve a `private` and `public` key. To accomplish this task, generating a mnemonic seed phrase and extracting private and public keys using cryptographic libraries is necessary. This is accomplished as follows: @@ -846,7 +846,7 @@ var subWallet uint64 = 698983191 -### Compiling Wallet Code +### Compiling wallet code Now that we have the private and public keys and the subwallet_id clearly defined we need to compile the wallet code. To accomplish this, we’ll use the [wallet v3 code](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/wallet3-code.fc) from the official repository. From dea46f2610ded7e1004cafa4ac2761827bbaaf41 Mon Sep 17 00:00:00 2001 From: AlexG <39581753+Reveloper@users.noreply.github.com> Date: Sun, 9 Mar 2025 21:42:48 +0900 Subject: [PATCH 07/23] refactor_guidelines_sidebar --- sidebars/guidelines.js | 407 ++++++++++++++++++++--------------------- 1 file changed, 199 insertions(+), 208 deletions(-) diff --git a/sidebars/guidelines.js b/sidebars/guidelines.js index 1c30dc31897..43402c02b87 100644 --- a/sidebars/guidelines.js +++ b/sidebars/guidelines.js @@ -29,83 +29,77 @@ module.exports = [ }, ], }, - { - 'type': 'html', - 'value': '
', - }, - { - 'type': 'html', - 'value': ' Smart Contracts Guidelines ', - }, - 'v3/guidelines/smart-contracts/guidelines', - 'v3/guidelines/smart-contracts/get-methods', - { - type: 'doc', - label: 'Transaction fees calculation', - id: 'v3/guidelines/smart-contracts/fee-calculation', - }, - { - type: 'category', - label: 'Testing', - items: [ - 'v3/guidelines/smart-contracts/testing/overview', - 'v3/guidelines/smart-contracts/testing/writing-test-examples', - ], - }, - { - type: 'category', - label: 'Security Measures', - items: [ - 'v3/guidelines/smart-contracts/security/overview', - 'v3/guidelines/smart-contracts/security/secure-programming', - 'v3/guidelines/smart-contracts/security/things-to-focus', - 'v3/guidelines/smart-contracts/security/ton-hack-challenge-1', - 'v3/guidelines/smart-contracts/security/random-number-generation', - 'v3/guidelines/smart-contracts/security/random', - ], - }, { type: 'category', - label: 'How to', + label: ' Smart contracts guidelines', items: [ + 'v3/guidelines/smart-contracts/guidelines', + 'v3/guidelines/smart-contracts/get-methods', + { + type: 'doc', + label: 'Transaction fees calculation', + id: 'v3/guidelines/smart-contracts/fee-calculation', + }, + { + type: 'category', + label: 'Testing', + items: [ + 'v3/guidelines/smart-contracts/testing/overview', + 'v3/guidelines/smart-contracts/testing/writing-test-examples', + ], + }, { type: 'category', - label: 'Compile from Sources', + label: 'Security measures', + items: [ + 'v3/guidelines/smart-contracts/security/overview', + 'v3/guidelines/smart-contracts/security/secure-programming', + 'v3/guidelines/smart-contracts/security/things-to-focus', + 'v3/guidelines/smart-contracts/security/ton-hack-challenge-1', + 'v3/guidelines/smart-contracts/security/random-number-generation', + 'v3/guidelines/smart-contracts/security/random', + ], + }, + { + type: 'category', + label: 'How to', items: [ { - type: 'doc', - label: 'Compilation Instructions', - id: 'v3/guidelines/smart-contracts/howto/compile/compilation-instructions', + type: 'category', + label: 'Compile from sources', + items: [ + { + type: 'doc', + label: 'Compilation instructions', + id: 'v3/guidelines/smart-contracts/howto/compile/compilation-instructions', + }, + { + type: 'doc', + label: 'Instructions for low-memory machines', + id: 'v3/guidelines/smart-contracts/howto/compile/instructions-low-memory', + }, + ], }, + 'v3/guidelines/smart-contracts/howto/multisig', + 'v3/guidelines/smart-contracts/howto/multisig-js', + 'v3/guidelines/smart-contracts/howto/airdrop-claim-best-practice', + 'v3/guidelines/smart-contracts/howto/shard-optimization', + 'v3/guidelines/smart-contracts/howto/wallet', + 'v3/guidelines/smart-contracts/howto/nominator-pool', + 'v3/guidelines/smart-contracts/howto/single-nominator-pool', { - type: 'doc', - label: 'Instructions for low-memory machines', - id: 'v3/guidelines/smart-contracts/howto/compile/instructions-low-memory', + type: 'link', + label: 'How to shard your TON smart contract and why', + href: 'https://blog.ton.org/how-to-shard-your-ton-smart-contract-and-why-studying-the-anatomy-of-tons-jettons', }, ], }, - 'v3/guidelines/smart-contracts/howto/multisig', - 'v3/guidelines/smart-contracts/howto/multisig-js', - 'v3/guidelines/smart-contracts/howto/airdrop-claim-best-practice', - 'v3/guidelines/smart-contracts/howto/shard-optimization', - 'v3/guidelines/smart-contracts/howto/wallet', - 'v3/guidelines/smart-contracts/howto/nominator-pool', - 'v3/guidelines/smart-contracts/howto/single-nominator-pool', - { - type: 'link', - label: 'How to shard your TON smart contract and why', - href: 'https://blog.ton.org/how-to-shard-your-ton-smart-contract-and-why-studying-the-anatomy-of-tons-jettons', - }, - ], - }, - { - 'type': 'html', - 'value': '
', + ] }, { - 'type': 'html', - 'value': ' DApps Guidelines ', - }, + type: 'category', + label: ' DApps guidelines', + items: [ 'v3/guidelines/dapps/overview', 'v3/guidelines/dapps/cookbook', { @@ -121,33 +115,33 @@ module.exports = [ }, { type: 'category', - label: 'Tutorials & Examples', + label: 'Tutorials & examples', items: [ 'v3/guidelines/dapps/apis-sdks/api-keys', 'v3/guidelines/dapps/apis-sdks/getblock-ton-api', { type: 'doc', id: 'v3/guidelines/dapps/tutorials/nft-minting-guide', - label: 'NFT Minting Guide', + label: 'NFT minting guide', }, { type: 'doc', id: 'v3/guidelines/dapps/tutorials/mint-your-first-token', - label: 'Mint Your First Token', + label: 'Mint your first token', }, { type: 'doc', id: 'v3/guidelines/dapps/tutorials/zero-knowledge-proofs', - label: 'Zero-Knowledge Proofs', + label: 'Zero-Knowledge proofs', }, { type: 'doc', id: 'v3/guidelines/dapps/tutorials/web3-game-example', - label: 'Web3 Game Example', + label: 'Web3 game example', }, { type: 'category', - label: 'Telegram Bot Examples', + label: 'Telegram bot examples', items: [ 'v3/guidelines/dapps/tutorials/telegram-bot-examples/accept-payments-in-a-telegram-bot', 'v3/guidelines/dapps/tutorials/telegram-bot-examples/accept-payments-in-a-telegram-bot-2', @@ -173,7 +167,7 @@ module.exports = [ }, { type: 'category', - label: 'Tutorials & Examples', + label: 'Tutorials & examples', items: [ 'v3/guidelines/dapps/tma/tutorials/step-by-step-guide', 'v3/guidelines/dapps/tma/tutorials/app-examples', @@ -186,171 +180,168 @@ module.exports = [ }, { type: 'category', - label: 'Advanced Asset Processing', + label: 'Advanced asset processing', items: [ - 'v3/guidelines/dapps/asset-processing/payments-processing', // TODO: divide - 'v3/guidelines/dapps/asset-processing/jettons', // TODO: divide - 'v3/guidelines/dapps/asset-processing/mintless-jettons', // TODO: divide + 'v3/guidelines/dapps/asset-processing/payments-processing', + 'v3/guidelines/dapps/asset-processing/jettons', + 'v3/guidelines/dapps/asset-processing/mintless-jettons', 'v3/guidelines/dapps/asset-processing/compressed-nfts', 'v3/guidelines/dapps/asset-processing/mass-mint-tools', { type: 'category', - label: 'NFT Processing', + label: 'NFT processing', items: [ - 'v3/guidelines/dapps/asset-processing/nft-processing/nfts', // TODO: divide + 'v3/guidelines/dapps/asset-processing/nft-processing/nfts', 'v3/guidelines/dapps/asset-processing/nft-processing/metadata-parsing', ], }, ], }, - { - 'type': 'html', - 'value': '
', - }, - { - 'type': 'html', - 'value': ' Blockchain Nodes Guidelines ', - }, - 'v3/guidelines/nodes/overview', - { - type: 'category', - label: 'Running Nodes', - items: [ - 'v3/guidelines/nodes/running-nodes/archive-node', - 'v3/guidelines/nodes/running-nodes/full-node', - 'v3/guidelines/nodes/running-nodes/liteserver-node', - 'v3/guidelines/nodes/running-nodes/validator-node', - 'v3/guidelines/nodes/running-nodes/staking-with-nominator-pools', - 'v3/guidelines/nodes/running-nodes/run-mytonctrl-docker', - 'v3/guidelines/nodes/running-nodes/running-a-local-ton', - 'v3/guidelines/nodes/running-nodes/secure-guidelines', - ], - }, - { - type: 'category', - label: 'Maintenance Guidelines', - items: [ - 'v3/guidelines/nodes/maintenance-guidelines/mytonctrl-backup-restore', - 'v3/guidelines/nodes/maintenance-guidelines/mytonctrl-validator-standby', - 'v3/guidelines/nodes/maintenance-guidelines/mytonctrl-private-alerting', - 'v3/guidelines/nodes/maintenance-guidelines/mytonctrl-prometheus', - 'v3/guidelines/nodes/maintenance-guidelines/mytonctrl-remote-controller' - ], - }, - 'v3/guidelines/nodes/custom-overlays', - 'v3/guidelines/nodes/nodes-troubleshooting', - 'v3/guidelines/nodes/node-maintenance-and-security', - 'v3/guidelines/nodes/monitoring/performance-monitoring', - 'v3/guidelines/nodes/persistent-states', - 'v3/guidelines/nodes/faq', - { - 'type': 'html', - 'value': '
', - }, - { - 'type': 'html', - 'value': ' Integrate with TON ', - }, - 'v3/guidelines/ton-connect/overview', - { - type: 'doc', - id: 'v3/guidelines/ton-connect/wallet', + ] }, { type: 'category', - label: 'Frameworks', + label: 'Blockchain nodes guidelines', items: [ + 'v3/guidelines/nodes/overview', + { + type: 'category', + label: 'Running nodes', + items: [ + 'v3/guidelines/nodes/running-nodes/archive-node', + 'v3/guidelines/nodes/running-nodes/full-node', + 'v3/guidelines/nodes/running-nodes/liteserver-node', + 'v3/guidelines/nodes/running-nodes/validator-node', + 'v3/guidelines/nodes/running-nodes/staking-with-nominator-pools', + 'v3/guidelines/nodes/running-nodes/run-mytonctrl-docker', + 'v3/guidelines/nodes/running-nodes/running-a-local-ton', + 'v3/guidelines/nodes/running-nodes/secure-guidelines', + ], + }, + { + type: 'category', + label: 'Maintenance guidelines', + items: [ + 'v3/guidelines/nodes/maintenance-guidelines/mytonctrl-backup-restore', + 'v3/guidelines/nodes/maintenance-guidelines/mytonctrl-validator-standby', + 'v3/guidelines/nodes/maintenance-guidelines/mytonctrl-private-alerting', + 'v3/guidelines/nodes/maintenance-guidelines/mytonctrl-prometheus', + 'v3/guidelines/nodes/maintenance-guidelines/mytonctrl-remote-controller' + ], + }, + 'v3/guidelines/nodes/custom-overlays', + 'v3/guidelines/nodes/nodes-troubleshooting', + 'v3/guidelines/nodes/node-maintenance-and-security', + 'v3/guidelines/nodes/monitoring/performance-monitoring', + 'v3/guidelines/nodes/persistent-states', + 'v3/guidelines/nodes/faq', + ] + }, + { + type: 'category', + label: 'Integrate with TON', + items: [ + 'v3/guidelines/ton-connect/overview', { type: 'doc', - id: 'v3/guidelines/ton-connect/frameworks/react', - label: 'React Apps', - }, - { - type: 'doc', - id: 'v3/guidelines/ton-connect/frameworks/vue', - label: 'Vue Apps', + id: 'v3/guidelines/ton-connect/wallet', }, { - type: 'doc', - id: 'v3/guidelines/ton-connect/frameworks/web', - label: 'HTML/JS Apps', + type: 'category', + label: 'Frameworks', + items: [ + { + type: 'doc', + id: 'v3/guidelines/ton-connect/frameworks/react', + label: 'React Apps', + }, + { + type: 'doc', + id: 'v3/guidelines/ton-connect/frameworks/vue', + label: 'Vue Apps', + }, + { + type: 'doc', + id: 'v3/guidelines/ton-connect/frameworks/web', + label: 'HTML/JS Apps', + }, + ], }, - ], - }, - { - type: 'category', - label: 'Guidelines', - items: [ - 'v3/guidelines/ton-connect/guidelines/how-ton-connect-works', - 'v3/guidelines/ton-connect/guidelines/developers', - 'v3/guidelines/ton-connect/guidelines/creating-manifest', - 'v3/guidelines/ton-connect/guidelines/preparing-messages', - 'v3/guidelines/ton-connect/guidelines/sending-messages', - 'v3/guidelines/ton-connect/guidelines/verifying-signed-in-users', - 'v3/guidelines/ton-connect/guidelines/integration-with-javascript-sdk', - ], - }, - { - type: 'category', - label: 'Advanced', - items: [ { - type: 'link', - label: 'Protocol specification', - href: 'https://github.com/ton-blockchain/ton-connect', + type: 'category', + label: 'Guidelines', + items: [ + 'v3/guidelines/ton-connect/guidelines/how-ton-connect-works', + 'v3/guidelines/ton-connect/guidelines/developers', + 'v3/guidelines/ton-connect/guidelines/creating-manifest', + 'v3/guidelines/ton-connect/guidelines/preparing-messages', + 'v3/guidelines/ton-connect/guidelines/sending-messages', + 'v3/guidelines/ton-connect/guidelines/verifying-signed-in-users', + 'v3/guidelines/ton-connect/guidelines/integration-with-javascript-sdk', + ], }, { - type: 'link', - label: 'Wallets List', - href: 'https://github.com/ton-blockchain/wallets-list', + type: 'category', + label: 'Advanced', + items: [ + { + type: 'link', + label: 'Protocol specification', + href: 'https://github.com/ton-blockchain/ton-connect', + }, + { + type: 'link', + label: 'Wallets List', + href: 'https://github.com/ton-blockchain/wallets-list', + }, + ], }, - ], - }, - { - type: 'category', - label: 'Business', - items: [ - 'v3/guidelines/ton-connect/business/ton-connect-for-business', - 'v3/guidelines/ton-connect/business/ton-connect-for-security', - 'v3/guidelines/ton-connect/business/ton-connect-comparison', - ], - }, - { - 'type': 'html', - 'value': '
', - }, - { - 'type': 'html', - 'value': ' Web3 Guidelines ', - }, - 'v3/guidelines/web3/overview', - { - 'type': 'category', - 'label': 'TON DNS', - 'items': [ - 'v3/guidelines/web3/ton-dns/dns', - 'v3/guidelines/web3/ton-dns/subresolvers', - ], - }, - { - 'type': 'category', - 'label': 'TON Proxy & Sites', - 'items': [ - 'v3/guidelines/web3/ton-proxy-sites/how-to-run-ton-site', - 'v3/guidelines/web3/ton-proxy-sites/ton-sites-for-applications', - 'v3/guidelines/web3/ton-proxy-sites/connect-with-ton-proxy', - 'v3/guidelines/web3/ton-proxy-sites/how-to-open-any-ton-site', - 'v3/guidelines/web3/ton-proxy-sites/site-and-domain-management', - 'v3/guidelines/web3/ton-proxy-sites/running-your-own-ton-proxy', - ], + { + type: 'category', + label: 'Business', + items: [ + 'v3/guidelines/ton-connect/business/ton-connect-for-business', + 'v3/guidelines/ton-connect/business/ton-connect-for-security', + 'v3/guidelines/ton-connect/business/ton-connect-comparison', + ], + } + ] }, + { 'type': 'category', - 'label': 'TON Storage', + 'label': 'Web3 guidelines', 'items': [ - 'v3/guidelines/web3/ton-storage/storage-daemon', - 'v3/guidelines/web3/ton-storage/storage-provider', - 'v3/guidelines/web3/ton-storage/storage-faq', - ], + 'v3/guidelines/web3/overview', + { + 'type': 'category', + 'label': 'TON DNS', + 'items': [ + 'v3/guidelines/web3/ton-dns/dns', + 'v3/guidelines/web3/ton-dns/subresolvers', + ], + }, + { + 'type': 'category', + 'label': 'Proxy & sites', + 'items': [ + 'v3/guidelines/web3/ton-proxy-sites/how-to-run-ton-site', + 'v3/guidelines/web3/ton-proxy-sites/ton-sites-for-applications', + 'v3/guidelines/web3/ton-proxy-sites/connect-with-ton-proxy', + 'v3/guidelines/web3/ton-proxy-sites/how-to-open-any-ton-site', + 'v3/guidelines/web3/ton-proxy-sites/site-and-domain-management', + 'v3/guidelines/web3/ton-proxy-sites/running-your-own-ton-proxy', + ], + }, + { + 'type': 'category', + 'label': 'TON Storage', + 'items': [ + 'v3/guidelines/web3/ton-storage/storage-daemon', + 'v3/guidelines/web3/ton-storage/storage-provider', + 'v3/guidelines/web3/ton-storage/storage-faq', + ], + }, + ] }, ]; From d58db0dfd721a0bb0b8e975e7cf9570a8ac5ce3e Mon Sep 17 00:00:00 2001 From: AlexG <39581753+Reveloper@users.noreply.github.com> Date: Sun, 9 Mar 2025 21:43:52 +0900 Subject: [PATCH 08/23] refactor_guideline_overview --- .../guidelines/smart-contracts/guidelines.mdx | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/docs/v3/guidelines/smart-contracts/guidelines.mdx b/docs/v3/guidelines/smart-contracts/guidelines.mdx index e26646ec957..36b559ddf05 100644 --- a/docs/v3/guidelines/smart-contracts/guidelines.mdx +++ b/docs/v3/guidelines/smart-contracts/guidelines.mdx @@ -2,18 +2,28 @@ import Button from '@site/src/components/button' # Overview -This page collects some recommendations and best practices that could be followed when developing new smart contracts on TON Blockchain. - -* [Internal messages](/v3/documentation/smart-contracts/message-management/internal-messages) -* [External messages](/v3/documentation/smart-contracts/message-management/external-messages) -* [Using non-bounceable messages](/v3/documentation/smart-contracts/message-management/non-bounceable-messages) -* [Get-methods](/v3/guidelines/smart-contracts/get-methods) -* ["accept_message" effects](/v3/documentation/smart-contracts/transaction-fees/accept-message-effects) -* [Paying for processing queries and sending responses](/v3/documentation/smart-contracts/transaction-fees/forward-fees) -* [How and why to shard your TON smart contract. Studying the anatomy of TON's Jettons](https://blog.ton.org/how-to-shard-your-ton-smart-contract-and-why-studying-the-anatomy-of-tons-jettons) -* [TON Keeper founders Oleg Andreev and Oleg Illarionov on TON jettons](https://www.youtube.com/watch?v=oEO29KmOpv4) - -Also there is a useful Smart Contract [documentation](/v3/documentation/smart-contracts/overview). +This page contents table of content for TON smart contracts guidelines. + +## Guide navigator + +| Guide | Stack | Description | +|--------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------|----------------------------------------------------------------------------------| +| [TON Hello world](https://tonhelloworld.com/01-wallet/) | TS, @ton/ton | Write and deploy your first contract. | +| [Writing tests with Blueprint](/v3/guidelines/smart-contracts/testing/overview/) | TS, @ton/ton | Learn how to write and invoke local tests for contract. | +| [Writing tests examples](/v3/guidelines/smart-contracts/testing/writing-test-examples/) | TS, @ton/ton | Learn how to write various test suites for edgecases. | +| [Things to focus on while working with TON blockchain](/v3/guidelines/smart-contracts/security/things-to-focus/) | FunC | Best practies for DApps development in TON. | +| [Secure smart contract programming](/v3/guidelines/smart-contracts/security/secure-programming/) | FunC | Best practices for secure development of smart contracts on FunC. | +| [Drawing conclusions from TON Hack Challenge](/v3/guidelines/smart-contracts/security/ton-hack-challenge-1/) | FunC | Best practices for secure development | +| [Random number generation](/v3/guidelines/smart-contracts/security/random-number-generation/) | FunC | Generating random numbers in TON for various projects. | +| [Generation of block random seed](/v3/guidelines/smart-contracts/security/random/) | C++, Core | Explanation on random in TON. | +| [Compilation instructions](/v3/guidelines/smart-contracts/howto/compile/compilation-instructions/) | C++, cmake, Core | Compile executables from source for deep native integration. | +| [Instructions for low-memory machines](/v3/guidelines/smart-contracts/howto/compile/instructions-low-memory/) | С++ | Extnension for compilation on low-memary machines. | +| [Working with wallet smart contracts](/v3/guidelines/smart-contracts/howto/wallet/) | FunC; TS, @ton/ton | Learn in detail interaction with various wallets smartcontracts. | +| [Make a simple multisig contract with fift](/v3/guidelines/smart-contracts/howto/multisig/) | Fift, Lite Client | This tutorial help you learn how to deploy your multisig contract. | +| [Interact with multisig wallets using TypeScript](/v3/guidelines/smart-contracts/howto/multisig-js) | TS, @ton/ton | Learn how to interact to multisig wallet in TON. | +| [Airdrop claiming guidelines](/v3/guidelines/smart-contracts/howto/airdrop-claim-best-practice/) | Design and arthictecture | Learn concepts on contract interactions and their impact on overall performance. | +| [Shard optimizations on TON](/v3/guidelines/smart-contracts/howto/shard-optimization/) | TS, @ton/ton | Learn best practice for conract shard optimization. | +| [How to shard your TON smart contract and why](https://blog.ton.org/how-to-shard-your-ton-smart-contract-and-why-studying-the-anatomy-of-tons-jettons) | Desing and architecture | Learn concept of contract system with jetton standard | ## TON Course: Contract Development @@ -46,3 +56,6 @@ RU + +## See Also +- [Smart contract documentation](/v3/documentation/smart-contracts/overview/) \ No newline at end of file From de0daa5a2fc9bcbf022631d694293ea07682e7d1 Mon Sep 17 00:00:00 2001 From: AlexG <39581753+Reveloper@users.noreply.github.com> Date: Mon, 10 Mar 2025 19:17:34 +0900 Subject: [PATCH 09/23] cut_leagacy_pages --- docs/develop/dapps/apis/sdk.mdx | 1 - .../develop/dapps/asset-processing/jettons.md | 1 - docs/develop/dapps/cookbook.mdx | 1 - docs/develop/dapps/telegram-apps/README.mdx | 1 - .../dapps/telegram-apps/app-examples.mdx | 1 - .../telegram-apps/step-by-step-guide.mdx | 1 - docs/develop/dapps/ton-connect/developers.md | 1 - docs/develop/dapps/ton-connect/overview.mdx | 1 - docs/develop/dapps/ton-connect/react.mdx | 1 - docs/develop/dapps/ton-connect/wallet.mdx | 1 - docs/develop/dapps/ton-connect/web.mdx | 1 - .../dapps/tutorials/how-to-run-ton-site.md | 1 - docs/develop/get-started-with-ton.mdx | 1 - docs/develop/overview.mdx | 1 - docs/develop/smart-contracts/fees.md | 1 - docs/learn/introduction.mdx | 1 - docs/learn/overviews/addresses.md | 1 - docs/learn/tvm-instructions/generate_md.py | 93 -- docs/learn/tvm-instructions/instructions.csv | 1026 ----------------- docs/participate/run-nodes/full-node.mdx | 1 - docs/participate/wallets/contracts.md | 1 - 21 files changed, 1138 deletions(-) delete mode 120000 docs/develop/dapps/apis/sdk.mdx delete mode 120000 docs/develop/dapps/asset-processing/jettons.md delete mode 120000 docs/develop/dapps/cookbook.mdx delete mode 120000 docs/develop/dapps/telegram-apps/README.mdx delete mode 120000 docs/develop/dapps/telegram-apps/app-examples.mdx delete mode 120000 docs/develop/dapps/telegram-apps/step-by-step-guide.mdx delete mode 120000 docs/develop/dapps/ton-connect/developers.md delete mode 120000 docs/develop/dapps/ton-connect/overview.mdx delete mode 120000 docs/develop/dapps/ton-connect/react.mdx delete mode 120000 docs/develop/dapps/ton-connect/wallet.mdx delete mode 120000 docs/develop/dapps/ton-connect/web.mdx delete mode 120000 docs/develop/dapps/tutorials/how-to-run-ton-site.md delete mode 120000 docs/develop/get-started-with-ton.mdx delete mode 120000 docs/develop/overview.mdx delete mode 120000 docs/develop/smart-contracts/fees.md delete mode 120000 docs/learn/introduction.mdx delete mode 120000 docs/learn/overviews/addresses.md delete mode 100644 docs/learn/tvm-instructions/generate_md.py delete mode 100644 docs/learn/tvm-instructions/instructions.csv delete mode 120000 docs/participate/run-nodes/full-node.mdx delete mode 120000 docs/participate/wallets/contracts.md diff --git a/docs/develop/dapps/apis/sdk.mdx b/docs/develop/dapps/apis/sdk.mdx deleted file mode 120000 index e639a787de0..00000000000 --- a/docs/develop/dapps/apis/sdk.mdx +++ /dev/null @@ -1 +0,0 @@ -../../../v3/guidelines/dapps/apis-sdks/sdk.mdx \ No newline at end of file diff --git a/docs/develop/dapps/asset-processing/jettons.md b/docs/develop/dapps/asset-processing/jettons.md deleted file mode 120000 index 3ed49d7c5e3..00000000000 --- a/docs/develop/dapps/asset-processing/jettons.md +++ /dev/null @@ -1 +0,0 @@ -../../../v3/guidelines/dapps/asset-processing/jettons.md \ No newline at end of file diff --git a/docs/develop/dapps/cookbook.mdx b/docs/develop/dapps/cookbook.mdx deleted file mode 120000 index a3122ddf36a..00000000000 --- a/docs/develop/dapps/cookbook.mdx +++ /dev/null @@ -1 +0,0 @@ -../../v3/guidelines/dapps/cookbook.mdx \ No newline at end of file diff --git a/docs/develop/dapps/telegram-apps/README.mdx b/docs/develop/dapps/telegram-apps/README.mdx deleted file mode 120000 index 86eaa940948..00000000000 --- a/docs/develop/dapps/telegram-apps/README.mdx +++ /dev/null @@ -1 +0,0 @@ -../../../v3/guidelines/dapps/tma/overview.mdx \ No newline at end of file diff --git a/docs/develop/dapps/telegram-apps/app-examples.mdx b/docs/develop/dapps/telegram-apps/app-examples.mdx deleted file mode 120000 index 12a685c981e..00000000000 --- a/docs/develop/dapps/telegram-apps/app-examples.mdx +++ /dev/null @@ -1 +0,0 @@ -../../../v3/guidelines/dapps/tma/tutorials/app-examples.mdx \ No newline at end of file diff --git a/docs/develop/dapps/telegram-apps/step-by-step-guide.mdx b/docs/develop/dapps/telegram-apps/step-by-step-guide.mdx deleted file mode 120000 index aceb0ab96ba..00000000000 --- a/docs/develop/dapps/telegram-apps/step-by-step-guide.mdx +++ /dev/null @@ -1 +0,0 @@ -../../../v3/guidelines/dapps/tma/tutorials/step-by-step-guide.mdx \ No newline at end of file diff --git a/docs/develop/dapps/ton-connect/developers.md b/docs/develop/dapps/ton-connect/developers.md deleted file mode 120000 index a40840749d9..00000000000 --- a/docs/develop/dapps/ton-connect/developers.md +++ /dev/null @@ -1 +0,0 @@ -../../../v3/guidelines/ton-connect/guidelines/developers.md \ No newline at end of file diff --git a/docs/develop/dapps/ton-connect/overview.mdx b/docs/develop/dapps/ton-connect/overview.mdx deleted file mode 120000 index 2aa3f0e201b..00000000000 --- a/docs/develop/dapps/ton-connect/overview.mdx +++ /dev/null @@ -1 +0,0 @@ -../../../v3/guidelines/ton-connect/overview.mdx \ No newline at end of file diff --git a/docs/develop/dapps/ton-connect/react.mdx b/docs/develop/dapps/ton-connect/react.mdx deleted file mode 120000 index ccc585b43dd..00000000000 --- a/docs/develop/dapps/ton-connect/react.mdx +++ /dev/null @@ -1 +0,0 @@ -../../../v3/guidelines/ton-connect/frameworks/react.mdx \ No newline at end of file diff --git a/docs/develop/dapps/ton-connect/wallet.mdx b/docs/develop/dapps/ton-connect/wallet.mdx deleted file mode 120000 index 9aa00a000ef..00000000000 --- a/docs/develop/dapps/ton-connect/wallet.mdx +++ /dev/null @@ -1 +0,0 @@ -../../../v3/guidelines/ton-connect/wallet.mdx \ No newline at end of file diff --git a/docs/develop/dapps/ton-connect/web.mdx b/docs/develop/dapps/ton-connect/web.mdx deleted file mode 120000 index d50e1234cc8..00000000000 --- a/docs/develop/dapps/ton-connect/web.mdx +++ /dev/null @@ -1 +0,0 @@ -../../../v3/guidelines/ton-connect/frameworks/web.mdx \ No newline at end of file diff --git a/docs/develop/dapps/tutorials/how-to-run-ton-site.md b/docs/develop/dapps/tutorials/how-to-run-ton-site.md deleted file mode 120000 index 384bd018865..00000000000 --- a/docs/develop/dapps/tutorials/how-to-run-ton-site.md +++ /dev/null @@ -1 +0,0 @@ -../../../v3/guidelines/web3/ton-proxy-sites/how-to-run-ton-site.md \ No newline at end of file diff --git a/docs/develop/get-started-with-ton.mdx b/docs/develop/get-started-with-ton.mdx deleted file mode 120000 index d86064f835d..00000000000 --- a/docs/develop/get-started-with-ton.mdx +++ /dev/null @@ -1 +0,0 @@ -../v3/guidelines/get-started-with-ton.mdx \ No newline at end of file diff --git a/docs/develop/overview.mdx b/docs/develop/overview.mdx deleted file mode 120000 index fd7f89b4f1c..00000000000 --- a/docs/develop/overview.mdx +++ /dev/null @@ -1 +0,0 @@ -../v3/documentation/ton-documentation.mdx \ No newline at end of file diff --git a/docs/develop/smart-contracts/fees.md b/docs/develop/smart-contracts/fees.md deleted file mode 120000 index c310385f0d2..00000000000 --- a/docs/develop/smart-contracts/fees.md +++ /dev/null @@ -1 +0,0 @@ -../../v3/documentation/smart-contracts/transaction-fees/fees.md \ No newline at end of file diff --git a/docs/learn/introduction.mdx b/docs/learn/introduction.mdx deleted file mode 120000 index 845a86a6690..00000000000 --- a/docs/learn/introduction.mdx +++ /dev/null @@ -1 +0,0 @@ -../v3/concepts/dive-into-ton/introduction.mdx \ No newline at end of file diff --git a/docs/learn/overviews/addresses.md b/docs/learn/overviews/addresses.md deleted file mode 120000 index 0c37e49083c..00000000000 --- a/docs/learn/overviews/addresses.md +++ /dev/null @@ -1 +0,0 @@ -../../v3/documentation/smart-contracts/addresses.md \ No newline at end of file diff --git a/docs/learn/tvm-instructions/generate_md.py b/docs/learn/tvm-instructions/generate_md.py deleted file mode 100644 index 84fa7fc0ccf..00000000000 --- a/docs/learn/tvm-instructions/generate_md.py +++ /dev/null @@ -1,93 +0,0 @@ -import argparse -import csv -import re -import sys - -parser = argparse.ArgumentParser(description="Generate TVM instruction reference document") -parser.add_argument("instructions_csv", type=str, help="csv file with the instructions") -parser.add_argument("doc_template", type=str, help="template for the document") -parser.add_argument("out_file", type=str, help="output file") -args = parser.parse_args() - -TABLE_HEADER = \ - "| xxxxxxx
Opcode " +\ - "| xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax " +\ - "| xxxxxxxxxxxxxxxxx
Stack " +\ - "| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description " +\ - "| xxxx
Gas |\n" +\ - "|:-|:-|:-|:-|:-|" - -categories = dict() -cmd_to_name = dict() - -with open(args.instructions_csv, "r") as f: - reader = csv.DictReader(f) - for row in reader: - cat = row["doc_category"] - if cat not in categories: - categories[cat] = [] - categories[cat].append(row) - if row["name"] != "": - for s in row["doc_fift"].split("\n"): - s = s.strip() - if s != "": - s = s.split()[-1] - if s not in cmd_to_name: - cmd_to_name[s] = row["name"] - -def name_to_id(s): - return "instr-" + s.lower().replace("_", "-").replace("#", "SHARP") - -def make_link(text, cmd): - if cmd not in cmd_to_name: - return text - name = cmd_to_name[cmd] - return "[%s](#%s)" % (text, name_to_id(name)) - -def gen_links(text): - return re.sub("`([^ `][^`]* )?([A-Z0-9#-]+)`", lambda m: make_link(m.group(0), m.group(2)), text) - -def make_table(cat): - if cat not in categories: - print("No such category", cat, file=sys.stderr) - return "" - table = [TABLE_HEADER] - for row in categories[cat]: - opcode = row["doc_opcode"] - fift = row["doc_fift"] - stack = row["doc_stack"] - desc = row["doc_description"] - gas = row["doc_gas"] - - if opcode != "": - opcode = "**`%s`**" % opcode - - if fift != "": - fift = "
".join("`%s`" % s.strip() for s in fift.split("\n")) - - if stack != "": - stack = "_`%s`_" % stack - stack = stack.replace("|", "\\|") - stack = stack.strip() - - desc = desc.replace("|", "\\|") - desc = desc.replace("\n", "
") - - if gas != "": - gas = gas.replace("|", "\\|") - gas = "`" + gas + "`" - - desc = gen_links(desc) - desc = "
" % name_to_id(row["name"]) + desc - - table.append("| %s | %s | %s | %s | %s |" % (opcode, fift, stack, desc, gas)) - - return "\n".join(table) - -templ = open(args.doc_template, "r").read() - -templ = gen_links(templ) - -doc = re.sub("{{ *Table: *([a-zA-Z0-9_-]+) *}}", lambda m: make_table(m.group(1)), templ) -with open(args.out_file, "w") as f: - print(doc, file=f) diff --git a/docs/learn/tvm-instructions/instructions.csv b/docs/learn/tvm-instructions/instructions.csv deleted file mode 100644 index 3ff1b1b7d73..00000000000 --- a/docs/learn/tvm-instructions/instructions.csv +++ /dev/null @@ -1,1026 +0,0 @@ -name,alias_of,tlb,doc_category,doc_opcode,doc_fift,doc_stack,doc_gas,doc_description -NOP,,#00,stack_basic,00,NOP,-,18,Does nothing. -SWAP,XCHG_0I,#01,stack_basic,01,SWAP,x y - y x,18,Same as `s1 XCHG0`. -XCHG_0I,,#0 i:(## 4) {1 <= i},stack_basic,0i,s[i] XCHG0,,18,"Interchanges `s0` with `s[i]`, `1 <= i <= 15`." -XCHG_IJ,,#10 i:(## 4) j:(## 4) {1 <= i} {i + 1 <= j},stack_basic,10ij,s[i] s[j] XCHG,,26,"Interchanges `s[i]` with `s[j]`, `1 <= i < j <= 15`." -XCHG_0I_LONG,,#11 ii:uint8,stack_basic,11ii,s0 [ii] s() XCHG,,26,"Interchanges `s0` with `s[ii]`, `0 <= ii <= 255`." -XCHG_1I,,#1 i:(## 4) {2 <= i},stack_basic,1i,s1 s[i] XCHG,,18,"Interchanges `s1` with `s[i]`, `2 <= i <= 15`." -PUSH,,#2 i:uint4,stack_basic,2i,s[i] PUSH,,18,Pushes a copy of the old `s[i]` into the stack. -DUP,PUSH,#20,stack_basic,20,DUP,x - x x,18,Same as `s0 PUSH`. -OVER,PUSH,#21,stack_basic,21,OVER,x y - x y x,18,Same as `s1 PUSH`. -POP,,#3 i:uint4,stack_basic,3i,s[i] POP,,18,Pops the old `s0` value into the old `s[i]`. -DROP,POP,#30,stack_basic,30,DROP,x -,18,"Same as `s0 POP`, discards the top-of-stack value." -NIP,POP,#31,stack_basic,31,NIP,x y - y,18,Same as `s1 POP`. -XCHG3,,#4 i:uint4 j:uint4 k:uint4,stack_complex,4ijk,s[i] s[j] s[k] XCHG3,,26,Equivalent to `s2 s[i] XCHG` `s1 s[j] XCHG` `s[k] XCHG0`. -XCHG2,,#50 i:uint4 j:uint4,stack_complex,50ij,s[i] s[j] XCHG2,,26,Equivalent to `s1 s[i] XCHG` `s[j] XCHG0`. -XCPU,,#51 i:uint4 j:uint4,stack_complex,51ij,s[i] s[j] XCPU,,26,Equivalent to `s[i] XCHG0` `s[j] PUSH`. -PUXC,,#52 i:uint4 j:uint4,stack_complex,52ij,s[i] s[j-1] PUXC,,26,Equivalent to `s[i] PUSH` `SWAP` `s[j] XCHG0`. -PUSH2,,#53 i:uint4 j:uint4,stack_complex,53ij,s[i] s[j] PUSH2,,26,Equivalent to `s[i] PUSH` `s[j+1] PUSH`. -XCHG3_ALT,,#540 i:uint4 j:uint4 k:uint4,stack_complex,540ijk,s[i] s[j] s[k] XCHG3_l,,34,Long form of `XCHG3`. -XC2PU,,#541 i:uint4 j:uint4 k:uint4,stack_complex,541ijk,s[i] s[j] s[k] XC2PU,,34,Equivalent to `s[i] s[j] XCHG2` `s[k] PUSH`. -XCPUXC,,#542 i:uint4 j:uint4 k:uint4,stack_complex,542ijk,s[i] s[j] s[k-1] XCPUXC,,34,Equivalent to `s1 s[i] XCHG` `s[j] s[k-1] PUXC`. -XCPU2,,#543 i:uint4 j:uint4 k:uint4,stack_complex,543ijk,s[i] s[j] s[k] XCPU2,,34,Equivalent to `s[i] XCHG0` `s[j] s[k] PUSH2`. -PUXC2,,#544 i:uint4 j:uint4 k:uint4,stack_complex,544ijk,s[i] s[j-1] s[k-1] PUXC2,,34,Equivalent to `s[i] PUSH` `s2 XCHG0` `s[j] s[k] XCHG2`. -PUXCPU,,#545 i:uint4 j:uint4 k:uint4,stack_complex,545ijk,s[i] s[j-1] s[k-1] PUXCPU,,34,Equivalent to `s[i] s[j-1] PUXC` `s[k] PUSH`. -PU2XC,,#546 i:uint4 j:uint4 k:uint4,stack_complex,546ijk,s[i] s[j-1] s[k-2] PU2XC,,34,Equivalent to `s[i] PUSH` `SWAP` `s[j] s[k-1] PUXC`. -PUSH3,,#547 i:uint4 j:uint4 k:uint4,stack_complex,547ijk,s[i] s[j] s[k] PUSH3,,34,Equivalent to `s[i] PUSH` `s[j+1] s[k+1] PUSH2`. -BLKSWAP,,#55 i:uint4 j:uint4,stack_complex,55ij,[i+1] [j+1] BLKSWAP,,26,"Permutes two blocks `s[j+i+1] … s[j+1]` and `s[j] … s0`. -`0 <= i,j <= 15` -Equivalent to `[i+1] [j+1] REVERSE` `[j+1] 0 REVERSE` `[i+j+2] 0 REVERSE`." -ROT2,BLKSWAP,#5513,stack_complex,5513,"ROT2 -2ROT",a b c d e f - c d e f a b,26,Rotates the three topmost pairs of stack entries. -ROLL,BLKSWAP,#550 i:uint4,stack_complex,550i,[i+1] ROLL,,26,"Rotates the top `i+1` stack entries. -Equivalent to `1 [i+1] BLKSWAP`." -ROLLREV,BLKSWAP,#55 i:uint4 zero:(## 4) {zero = 0},stack_complex,55i0,"[i+1] -ROLL -[i+1] ROLLREV",,26,"Rotates the top `i+1` stack entries in the other direction. -Equivalent to `[i+1] 1 BLKSWAP`." -PUSH_LONG,,#56 ii:uint8,stack_complex,56ii,[ii] s() PUSH,,26,"Pushes a copy of the old `s[ii]` into the stack. -`0 <= ii <= 255`" -POP_LONG,,#57 ii:uint8,stack_complex,57ii,[ii] s() POP,,26,"Pops the old `s0` value into the old `s[ii]`. -`0 <= ii <= 255`" -ROT,,#58,stack_complex,58,ROT,a b c - b c a,18,Equivalent to `1 2 BLKSWAP` or to `s2 s1 XCHG2`. -ROTREV,,#59,stack_complex,59,"ROTREV --ROT",a b c - c a b,18,Equivalent to `2 1 BLKSWAP` or to `s2 s2 XCHG2`. -SWAP2,,#5A,stack_complex,5A,"SWAP2 -2SWAP",a b c d - c d a b,18,Equivalent to `2 2 BLKSWAP` or to `s3 s2 XCHG2`. -DROP2,,#5B,stack_complex,5B,"DROP2 -2DROP",a b - ,18,Equivalent to `DROP` `DROP`. -DUP2,,#5C,stack_complex,5C,"DUP2 -2DUP",a b - a b a b,18,Equivalent to `s1 s0 PUSH2`. -OVER2,,#5D,stack_complex,5D,"OVER2 -2OVER",a b c d - a b c d a b,18,Equivalent to `s3 s2 PUSH2`. -REVERSE,,#5E i:uint4 j:uint4,stack_complex,5Eij,[i+2] [j] REVERSE,,26,Reverses the order of `s[j+i+1] … s[j]`. -BLKDROP,,#5F0 i:uint4,stack_complex,5F0i,[i] BLKDROP,,26,Equivalent to `DROP` performed `i` times. -BLKPUSH,,#5F i:(## 4) j:uint4 {1 <= i},stack_complex,5Fij,[i] [j] BLKPUSH,,26,"Equivalent to `PUSH s(j)` performed `i` times. -`1 <= i <= 15`, `0 <= j <= 15`." -PICK,,#60,stack_complex,60,"PICK -PUSHX",,18,"Pops integer `i` from the stack, then performs `s[i] PUSH`." -ROLLX,,#61,stack_complex,61,ROLLX,,18,"Pops integer `i` from the stack, then performs `1 [i] BLKSWAP`." --ROLLX,,#62,stack_complex,62,"-ROLLX -ROLLREVX",,18,"Pops integer `i` from the stack, then performs `[i] 1 BLKSWAP`." -BLKSWX,,#63,stack_complex,63,BLKSWX,,18,"Pops integers `i`,`j` from the stack, then performs `[i] [j] BLKSWAP`." -REVX,,#64,stack_complex,64,REVX,,18,"Pops integers `i`,`j` from the stack, then performs `[i] [j] REVERSE`." -DROPX,,#65,stack_complex,65,DROPX,,18,"Pops integer `i` from the stack, then performs `[i] BLKDROP`." -TUCK,,#66,stack_complex,66,TUCK,a b - b a b,18,Equivalent to `SWAP` `OVER` or to `s1 s1 XCPU`. -XCHGX,,#67,stack_complex,67,XCHGX,,18,"Pops integer `i` from the stack, then performs `s[i] XCHG`." -DEPTH,,#68,stack_complex,68,DEPTH,- depth,18,Pushes the current depth of the stack. -CHKDEPTH,,#69,stack_complex,69,CHKDEPTH,i -,18/58,"Pops integer `i` from the stack, then checks whether there are at least `i` elements, generating a stack underflow exception otherwise." -ONLYTOPX,,#6A,stack_complex,6A,ONLYTOPX,,18,"Pops integer `i` from the stack, then removes all but the top `i` elements." -ONLYX,,#6B,stack_complex,6B,ONLYX,,18,"Pops integer `i` from the stack, then leaves only the bottom `i` elements. Approximately equivalent to `DEPTH` `SWAP` `SUB` `DROPX`." -BLKDROP2,,#6C i:(## 4) j:uint4 {1 <= i},stack_complex,6Cij,[i] [j] BLKDROP2,,26,"Drops `i` stack elements under the top `j` elements. -`1 <= i <= 15`, `0 <= j <= 15` -Equivalent to `[i+j] 0 REVERSE` `[i] BLKDROP` `[j] 0 REVERSE`." -NULL,,#6D,tuple,6D,"NULL -PUSHNULL", - null,18,Pushes the only value of type _Null_. -ISNULL,,#6E,tuple,6E,ISNULL,x - ?,18,"Checks whether `x` is a _Null_, and returns `-1` or `0` accordingly." -TUPLE,,#6F0 n:uint4,tuple,6F0n,[n] TUPLE,x_1 ... x_n - t,26+n,"Creates a new _Tuple_ `t=(x_1, … ,x_n)` containing `n` values `x_1`,..., `x_n`. -`0 <= n <= 15`" -NIL,TUPLE,#6F00,tuple,6F00,NIL,- t,26,Pushes the only _Tuple_ `t=()` of length zero. -SINGLE,TUPLE,#6F01,tuple,6F01,SINGLE,x - t,27,"Creates a singleton `t:=(x)`, i.e., a _Tuple_ of length one." -PAIR,TUPLE,#6F02,tuple,6F02,"PAIR -CONS",x y - t,28,"Creates pair `t:=(x,y)`." -TRIPLE,TUPLE,#6F03,tuple,6F03,TRIPLE,x y z - t,29,"Creates triple `t:=(x,y,z)`." -INDEX,,#6F1 k:uint4,tuple,6F1k,[k] INDEX,t - x,26,"Returns the `k`-th element of a _Tuple_ `t`. -`0 <= k <= 15`." -FIRST,INDEX,#6F10,tuple,6F10,"FIRST -CAR",t - x,26,Returns the first element of a _Tuple_. -SECOND,INDEX,#6F11,tuple,6F11,"SECOND -CDR",t - y,26,Returns the second element of a _Tuple_. -THIRD,INDEX,#6F12,tuple,6F12,THIRD,t - z,26,Returns the third element of a _Tuple_. -UNTUPLE,,#6F2 n:uint4,tuple,6F2n,[n] UNTUPLE,t - x_1 ... x_n,26+n,"Unpacks a _Tuple_ `t=(x_1,...,x_n)` of length equal to `0 <= n <= 15`. -If `t` is not a _Tuple_, or if `|t| != n`, a type check exception is thrown." -UNSINGLE,UNTUPLE,#6F21,tuple,6F21,UNSINGLE,t - x,27,Unpacks a singleton `t=(x)`. -UNPAIR,UNTUPLE,#6F22,tuple,6F22,"UNPAIR -UNCONS",t - x y,28,"Unpacks a pair `t=(x,y)`." -UNTRIPLE,UNTUPLE,#6F23,tuple,6F23,UNTRIPLE,t - x y z,29,"Unpacks a triple `t=(x,y,z)`." -UNPACKFIRST,,#6F3 k:uint4,tuple,6F3k,[k] UNPACKFIRST,t - x_1 ... x_k,26+k,"Unpacks first `0 <= k <= 15` elements of a _Tuple_ `t`. -If `|t|= |t|`, throws a range check exception." -SETFIRST,SETINDEX,#6F50,tuple,6F50,SETFIRST,t x - t',26+|t|,Sets the first component of _Tuple_ `t` to `x` and returns the resulting _Tuple_ `t'`. -SETSECOND,SETINDEX,#6F51,tuple,6F51,SETSECOND,t x - t',26+|t|,Sets the second component of _Tuple_ `t` to `x` and returns the resulting _Tuple_ `t'`. -SETTHIRD,SETINDEX,#6F52,tuple,6F52,SETTHIRD,t x - t',26+|t|,Sets the third component of _Tuple_ `t` to `x` and returns the resulting _Tuple_ `t'`. -INDEXQ,,#6F6 k:uint4,tuple,6F6k,[k] INDEXQ,t - x,26,"Returns the `k`-th element of a _Tuple_ `t`, where `0 <= k <= 15`. In other words, returns `x_{k+1}` if `t=(x_1,...,x_n)`. If `k>=n`, or if `t` is _Null_, returns a _Null_ instead of `x`." -FIRSTQ,INDEXQ,#6F60,tuple,6F60,"FIRSTQ -CARQ",t - x,26,Returns the first element of a _Tuple_. -SECONDQ,INDEXQ,#6F61,tuple,6F61,"SECONDQ -CDRQ",t - y,26,Returns the second element of a _Tuple_. -THIRDQ,INDEXQ,#6F62,tuple,6F62,THIRDQ,t - z,26,Returns the third element of a _Tuple_. -SETINDEXQ,,#6F7 k:uint4,tuple,6F7k,[k] SETINDEXQ,t x - t',26+|t’|,"Sets the `k`-th component of _Tuple_ `t` to `x`, where `0 <= k < 16`, and returns the resulting _Tuple_ `t'`. -If `|t| <= k`, first extends the original _Tuple_ to length `n’=k+1` by setting all new components to _Null_. If the original value of `t` is _Null_, treats it as an empty _Tuple_. If `t` is not _Null_ or _Tuple_, throws an exception. If `x` is _Null_ and either `|t| <= k` or `t` is _Null_, then always returns `t'=t` (and does not consume tuple creation gas)." -SETFIRSTQ,SETINDEXQ,#6F70,tuple,6F70,SETFIRSTQ,t x - t',26+|t’|,Sets the first component of _Tuple_ `t` to `x` and returns the resulting _Tuple_ `t'`. -SETSECONDQ,SETINDEXQ,#6F71,tuple,6F71,SETSECONDQ,t x - t',26+|t’|,Sets the second component of _Tuple_ `t` to `x` and returns the resulting _Tuple_ `t'`. -SETTHIRDQ,SETINDEXQ,#6F72,tuple,6F72,SETTHIRDQ,t x - t',26+|t’|,Sets the third component of _Tuple_ `t` to `x` and returns the resulting _Tuple_ `t'`. -TUPLEVAR,,#6F80,tuple,6F80,TUPLEVAR,x_1 ... x_n n - t,26+n,"Creates a new _Tuple_ `t` of length `n` similarly to `TUPLE`, but with `0 <= n <= 255` taken from the stack." -INDEXVAR,,#6F81,tuple,6F81,INDEXVAR,t k - x,26,"Similar to `k INDEX`, but with `0 <= k <= 254` taken from the stack." -UNTUPLEVAR,,#6F82,tuple,6F82,UNTUPLEVAR,t n - x_1 ... x_n,26+n,"Similar to `n UNTUPLE`, but with `0 <= n <= 255` taken from the stack." -UNPACKFIRSTVAR,,#6F83,tuple,6F83,UNPACKFIRSTVAR,t n - x_1 ... x_n,26+n,"Similar to `n UNPACKFIRST`, but with `0 <= n <= 255` taken from the stack." -EXPLODEVAR,,#6F84,tuple,6F84,EXPLODEVAR,t n - x_1 ... x_m m,26+m,"Similar to `n EXPLODE`, but with `0 <= n <= 255` taken from the stack." -SETINDEXVAR,,#6F85,tuple,6F85,SETINDEXVAR,t x k - t',26+|t’|,"Similar to `k SETINDEX`, but with `0 <= k <= 254` taken from the stack." -INDEXVARQ,,#6F86,tuple,6F86,INDEXVARQ,t k - x,26,"Similar to `n INDEXQ`, but with `0 <= k <= 254` taken from the stack." -SETINDEXVARQ,,#6F87,tuple,6F87,SETINDEXVARQ,t x k - t',26+|t’|,"Similar to `k SETINDEXQ`, but with `0 <= k <= 254` taken from the stack." -TLEN,,#6F88,tuple,6F88,TLEN,t - n,26,Returns the length of a _Tuple_. -QTLEN,,#6F89,tuple,6F89,QTLEN,t - n or -1,26,"Similar to `TLEN`, but returns `-1` if `t` is not a _Tuple_." -ISTUPLE,,#6F8A,tuple,6F8A,ISTUPLE,t - ?,26,Returns `-1` or `0` depending on whether `t` is a _Tuple_. -LAST,,#6F8B,tuple,6F8B,LAST,t - x,26,Returns the last element of a non-empty _Tuple_ `t`. -TPUSH,,#6F8C,tuple,6F8C,"TPUSH -COMMA",t x - t',26+|t’|,"Appends a value `x` to a _Tuple_ `t=(x_1,...,x_n)`, but only if the resulting _Tuple_ `t'=(x_1,...,x_n,x)` is of length at most 255. Otherwise throws a type check exception." -TPOP,,#6F8D,tuple,6F8D,TPOP,t - t' x,26+|t’|,"Detaches the last element `x=x_n` from a non-empty _Tuple_ `t=(x_1,...,x_n)`, and returns both the resulting _Tuple_ `t'=(x_1,...,x_{n-1})` and the original last element `x`." -NULLSWAPIF,,#6FA0,tuple,6FA0,NULLSWAPIF,x - x or null x,26,"Pushes a _Null_ under the topmost _Integer_ `x`, but only if `x!=0`." -NULLSWAPIFNOT,,#6FA1,tuple,6FA1,NULLSWAPIFNOT,x - x or null x,26,"Pushes a _Null_ under the topmost _Integer_ `x`, but only if `x=0`. May be used for stack alignment after quiet primitives such as `PLDUXQ`." -NULLROTRIF,,#6FA2,tuple,6FA2,NULLROTRIF,x y - x y or null x y,26,"Pushes a _Null_ under the second stack entry from the top, but only if the topmost _Integer_ `y` is non-zero." -NULLROTRIFNOT,,#6FA3,tuple,6FA3,NULLROTRIFNOT,x y - x y or null x y,26,"Pushes a _Null_ under the second stack entry from the top, but only if the topmost _Integer_ `y` is zero. May be used for stack alignment after quiet primitives such as `LDUXQ`." -NULLSWAPIF2,,#6FA4,tuple,6FA4,NULLSWAPIF2,x - x or null null x,26,"Pushes two nulls under the topmost _Integer_ `x`, but only if `x!=0`. -Equivalent to `NULLSWAPIF` `NULLSWAPIF`." -NULLSWAPIFNOT2,,#6FA5,tuple,6FA5,NULLSWAPIFNOT2,x - x or null null x,26,"Pushes two nulls under the topmost _Integer_ `x`, but only if `x=0`. -Equivalent to `NULLSWAPIFNOT` `NULLSWAPIFNOT`." -NULLROTRIF2,,#6FA6,tuple,6FA6,NULLROTRIF2,x y - x y or null null x y,26,"Pushes two nulls under the second stack entry from the top, but only if the topmost _Integer_ `y` is non-zero. -Equivalent to `NULLROTRIF` `NULLROTRIF`." -NULLROTRIFNOT2,,#6FA7,tuple,6FA7,NULLROTRIFNOT2,x y - x y or null null x y,26,"Pushes two nulls under the second stack entry from the top, but only if the topmost _Integer_ `y` is zero. -Equivalent to `NULLROTRIFNOT` `NULLROTRIFNOT`." -INDEX2,,#6FB i:uint2 j:uint2,tuple,6FBij,[i] [j] INDEX2,t - x,26,"Recovers `x=(t_{i+1})_{j+1}` for `0 <= i,j <= 3`. -Equivalent to `[i] INDEX` `[j] INDEX`." -CADR,INDEX2,#6FB4,tuple,6FB4,CADR,t - x,26,Recovers `x=(t_2)_1`. -CDDR,INDEX2,#6FB5,tuple,6FB5,CDDR,t - x,26,Recovers `x=(t_2)_2`. -INDEX3,,#6FE_ i:uint2 j:uint2 k:uint2,tuple,6FE_ijk,[i] [j] [k] INDEX3,t - x,26,"Recovers `x=t_{i+1}_{j+1}_{k+1}`. -`0 <= i,j,k <= 3` -Equivalent to `[i] [j] INDEX2` `[k] INDEX`." -CADDR,INDEX3,#6FD4,tuple,6FD4,CADDR,t - x,26,Recovers `x=t_2_2_1`. -CDDDR,INDEX3,#6FD5,tuple,6FD5,CDDDR,t - x,26,Recovers `x=t_2_2_2`. -PUSHINT_4,,#7 i:uint4,const_int,7i,"[x] PUSHINT -[x] INT",- x,18,"Pushes integer `x` into the stack. `-5 <= x <= 10`. -Here `i` equals four lower-order bits of `x` (`i=x mod 16`)." -ZERO,PUSHINT_4,#70,const_int,70,"ZERO -FALSE",- 0,18, -ONE,PUSHINT_4,#71,const_int,71,ONE,- 1,18, -TWO,PUSHINT_4,#72,const_int,72,TWO,- 2,18, -TEN,PUSHINT_4,#7A,const_int,7A,TEN,- 10,18, -TRUE,PUSHINT_4,#7F,const_int,7F,TRUE,- -1,18, -PUSHINT_8,,#80 xx:int8,const_int,80xx,"[xx] PUSHINT -[xx] INT",- xx,26,Pushes integer `xx`. `-128 <= xx <= 127`. -PUSHINT_16,,#81 xxxx:int16,const_int,81xxxx,"[xxxx] PUSHINT -[xxxx] INT",- xxxx,34,Pushes integer `xxxx`. `-2^15 <= xx < 2^15`. -PUSHINT_LONG,,#82 l:(## 5) xxx:(int (8 * l + 19)),const_int,82lxxx,"[xxx] PUSHINT -[xxx] INT",- xxx,23,"Pushes integer `xxx`. -_Details:_ 5-bit `0 <= l <= 30` determines the length `n=8l+19` of signed big-endian integer `xxx`. -The total length of this instruction is `l+4` bytes or `n+13=8l+32` bits." -PUSHPOW2,,#83 xx:uint8,const_int,83xx,[xx+1] PUSHPOW2,- 2^(xx+1),26,"(Quietly) pushes `2^(xx+1)` for `0 <= xx <= 255`. -`2^256` is a `NaN`." -PUSHNAN,PUSHPOW2,#83FF,const_int,83FF,PUSHNAN,- NaN,26,Pushes a `NaN`. -PUSHPOW2DEC,,#84 xx:uint8,const_int,84xx,[xx+1] PUSHPOW2DEC,- 2^(xx+1)-1,26,Pushes `2^(xx+1)-1` for `0 <= xx <= 255`. -PUSHNEGPOW2,,#85 xx:uint8,const_int,85xx,[xx+1] PUSHNEGPOW2,- -2^(xx+1),26,Pushes `-2^(xx+1)` for `0 <= xx <= 255`. -PUSHREF,,#88 c:^Cell,const_data,88,[ref] PUSHREF,- c,18,"Pushes the reference `ref` into the stack. -_Details:_ Pushes the first reference of `cc.code` into the stack as a _Cell_ (and removes this reference from the current continuation)." -PUSHREFSLICE,,#89 c:^Cell,const_data,89,[ref] PUSHREFSLICE,- s,118/43,"Similar to `PUSHREF`, but converts the cell into a _Slice_." -PUSHREFCONT,,#8A c:^Cell,const_data,8A,[ref] PUSHREFCONT,- cont,118/43,"Similar to `PUSHREFSLICE`, but makes a simple ordinary _Continuation_ out of the cell." -PUSHSLICE,,#8B x:(## 4) sss:((8 * x + 4) * Bit),const_data,8Bxsss,"[slice] PUSHSLICE -[slice] SLICE",- s,22,"Pushes the slice `slice` into the stack. -_Details:_ Pushes the (prefix) subslice of `cc.code` consisting of its first `8x+4` bits and no references (i.e., essentially a bitstring), where `0 <= x <= 15`. -A completion tag is assumed, meaning that all trailing zeroes and the last binary one (if present) are removed from this bitstring. -If the original bitstring consists only of zeroes, an empty slice will be pushed." -PUSHSLICE_REFS,,#8C r:(## 2) xx:(## 5) c:((r + 1) * ^Cell) ssss:((8 * xx + 1) * Bit),const_data,8Crxxssss,"[slice] PUSHSLICE -[slice] SLICE",- s,25,"Pushes the slice `slice` into the stack. -_Details:_ Pushes the (prefix) subslice of `cc.code` consisting of its first `1 <= r+1 <= 4` references and up to first `8xx+1` bits of data, with `0 <= xx <= 31`. -A completion tag is also assumed." -PUSHSLICE_LONG,,#8D r:(#<= 4) xx:(## 7) c:(r * ^Cell) ssss:((8 * xx + 6) * Bit),const_data,8Drxxsssss,"[slice] PUSHSLICE -[slice] SLICE",- s,28,"Pushes the slice `slice` into the stack. -_Details:_ Pushes the subslice of `cc.code` consisting of `0 <= r <= 4` references and up to `8xx+6` bits of data, with `0 <= xx <= 127`. -A completion tag is assumed." -,,,const_data,,"x{} PUSHSLICE -x{ABCD1234} PUSHSLICE -b{01101} PUSHSLICE",- s,,"Examples of `PUSHSLICE`. -`x{}` is an empty slice. `x{...}` is a hexadecimal literal. `b{...}` is a binary literal. -More on slice literals [here](https://github.com/Piterden/TON-docs/blob/master/Fift.%20A%20Brief%20Introduction.md#user-content-51-slice-literals). -Note that the assembler can replace `PUSHSLICE` with `PUSHREFSLICE` in certain situations (e.g. if there’s not enough space in the current continuation)." -,,,const_data,," PUSHREF - PUSHREFSLICE",- c/s,,"Examples of `PUSHREF` and `PUSHREFSLICE`. -More on building cells in fift [here](https://github.com/Piterden/TON-docs/blob/master/Fift.%20A%20Brief%20Introduction.md#user-content-52-builder-primitives)." -PUSHCONT,,#8F_ r:(## 2) xx:(## 7) c:(r * ^Cell) ssss:((8 * xx) * Bit),const_data,8F_rxxcccc,"[builder] PUSHCONT -[builder] CONT",- c,26,"Pushes a continuation made from `builder`. -_Details:_ Pushes the simple ordinary continuation `cccc` made from the first `0 <= r <= 3` references and the first `0 <= xx <= 127` bytes of `cc.code`." -PUSHCONT_SHORT,,#9 x:(## 4) ssss:((8 * x) * Bit),const_data,9xccc,"[builder] PUSHCONT -[builder] CONT",- c,18,"Pushes a continuation made from `builder`. -_Details:_ Pushes an `x`-byte continuation for `0 <= x <= 15`." -,,,const_data,,"<{ code }> PUSHCONT -<{ code }> CONT -CONT:<{ code }>",- c,,"Pushes a continuation with code `code`. -Note that the assembler can replace `PUSHCONT` with `PUSHREFCONT` in certain situations (e.g. if there’s not enough space in the current continuation)." -ADD,,#A0,arithm_basic,A0,ADD,x y - x+y,18, -SUB,,#A1,arithm_basic,A1,SUB,x y - x-y,18, -SUBR,,#A2,arithm_basic,A2,SUBR,x y - y-x,18,Equivalent to `SWAP` `SUB`. -NEGATE,,#A3,arithm_basic,A3,NEGATE,x - -x,18,"Equivalent to `-1 MULCONST` or to `ZERO SUBR`. -Notice that it triggers an integer overflow exception if `x=-2^256`." -INC,,#A4,arithm_basic,A4,INC,x - x+1,18,Equivalent to `1 ADDCONST`. -DEC,,#A5,arithm_basic,A5,DEC,x - x-1,18,Equivalent to `-1 ADDCONST`. -ADDCONST,,#A6 cc:int8,arithm_basic,A6cc,"[cc] ADDCONST -[cc] ADDINT -[-cc] SUBCONST -[-cc] SUBINT",x - x+cc,26,`-128 <= cc <= 127`. -MULCONST,,#A7 cc:int8,arithm_basic,A7cc,"[cc] MULCONST -[cc] MULINT",x - x*cc,26,`-128 <= cc <= 127`. -MUL,,#A8,arithm_basic,A8,MUL,x y - x*y,18, -DIV_BASE,,#A9 m:uint1 s:uint2 cdft:(Either [ d:uint2 f:uint2 ] [ d:uint2 f:uint2 tt:uint8 ]),arithm_div,A9mscdf,,,26,"This is the general encoding of division, with an optional pre-multiplication and an optional replacement of the division or multiplication by a shift. Variable fields are as follows: -`0 <= m <= 1` - Indicates whether there is pre-multiplication (`MULDIV` and its variants), possibly replaced by a left shift. -`0 <= s <= 2` - Indicates whether either the multiplication or the division have been replaced by shifts: `s=0` - no replacement, `s=1` - division replaced by a right shift, `s=2` - multiplication replaced by a left shift (possible only for `m=1`). -`0 <= c <= 1` - Indicates whether there is a constant one-byte argument `tt` for the shift operator (if `s!=0`). For `s=0`, `c=0`. If `c=1`, then `0 <= tt <= 255`, and the shift is performed by `tt+1` bits. If `s!=0` and `c=0`, then the shift amount is provided to the instruction as a top-of-stack _Integer_ in range `0...256`. -`1 <= d <= 3` - Indicates which results of division are required: `1` - only the quotient, `2` - only the remainder, `3` - both. -`0 <= f <= 2` - Rounding mode: `0` - floor, `1` - nearest integer, `2` - ceiling. -All instructions below are variants of this." -DIV,DIV_BASE,#A904,arithm_div,A904,DIV,x y - q,26,"`q=floor(x/y)`, `r=x-y*q`" -DIVR,DIV_BASE,#A905,arithm_div,A905,DIVR,x y - q’,26,"`q’=round(x/y)`, `r’=x-y*q’`" -DIVC,DIV_BASE,#A906,arithm_div,A906,DIVC,x y - q'',26,"`q’’=ceil(x/y)`, `r’’=x-y*q’’`" -MOD,DIV_BASE,#A908,arithm_div,A908,MOD,x y - r,26, -DIVMOD,DIV_BASE,#A90C,arithm_div,A90C,DIVMOD,x y - q r,26, -DIVMODR,DIV_BASE,#A90D,arithm_div,A90D,DIVMODR,x y - q' r',26, -DIVMODC,DIV_BASE,#A90E,arithm_div,A90E,DIVMODC,x y - q'' r'',26, -RSHIFTR_VAR,DIV_BASE,#A925,arithm_div,A925,RSHIFTR,x y - round(x/2^y),26, -RSHIFTC_VAR,DIV_BASE,#A926,arithm_div,A926,RSHIFTC,x y - ceil(x/2^y),34, -RSHIFTR,DIV_BASE,#A935 tt:uint8,arithm_div,A935tt,[tt+1] RSHIFTR#,x y - round(x/2^(tt+1)),34, -RSHIFTC,DIV_BASE,#A936 tt:uint8,arithm_div,A936tt,[tt+1] RSHIFTC#,x y - ceil(x/2^(tt+1)),34, -MODPOW2,DIV_BASE,#A938 tt:uint8,arithm_div,A938tt,[tt+1] MODPOW2#,x - x mod 2^(tt+1),26, -MULDIV,DIV_BASE,#A984,arithm_div,A98,MULDIV,x y z - q,26,`q=floor(x*y/z)` -MULDIVR,DIV_BASE,#A985,arithm_div,A985,MULDIVR,x y z - q',26,`q'=round(x*y/z)` -MULDIVMOD,DIV_BASE,#A98C,arithm_div,A98C,MULDIVMOD,x y z - q r,26,"`q=floor(x*y/z)`, `r=x*y-z*q`" -MULRSHIFT_VAR,DIV_BASE,#A9A4,arithm_div,A9A4,MULRSHIFT,x y z - floor(x*y/2^z),26,`0 <= z <= 256` -MULRSHIFTR_VAR,DIV_BASE,#A9A5,arithm_div,A9A5,MULRSHIFTR,x y z - round(x*y/2^z),26,`0 <= z <= 256` -MULRSHIFTC_VAR,DIV_BASE,#A9A6,arithm_div,A9A6,MULRSHIFTC,x y z - ceil(x*y/2^z),34,`0 <= z <= 256` -MULRSHIFT,DIV_BASE,#A9B4 tt:uint8,arithm_div,A9B4tt,[tt+1] MULRSHIFT#,x y - floor(x*y/2^(tt+1)),34, -MULRSHIFTR,DIV_BASE,#A9B5 tt:uint8,arithm_div,A9B5tt,[tt+1] MULRSHIFTR#,x y - round(x*y/2^(tt+1)),34, -MULRSHIFTC,DIV_BASE,#A9B6 tt:uint8,arithm_div,A9B6tt,[tt+1] MULRSHIFTC#,x y - ceil(x*y/2^(tt+1)),26, -LSHIFTDIV_VAR,DIV_BASE,#A9C4,arithm_div,A9C4,LSHIFTDIV,x y z - floor(2^z*x/y),26,`0 <= z <= 256` -LSHIFTDIVR_VAR,DIV_BASE,#A9C5,arithm_div,A9C5,LSHIFTDIVR,x y z - round(2^z*x/y),26,`0 <= z <= 256` -LSHIFTDIVC_VAR,DIV_BASE,#A9C6,arithm_div,A9C6,LSHIFTDIVC,x y z - ceil(2^z*x/y),34,`0 <= z <= 256` -LSHIFTDIV,DIV_BASE,#A9D4 tt:uint8,arithm_div,A9D4tt,[tt+1] LSHIFT#DIV,x y - floor(2^(tt+1)*x/y),34, -LSHIFTDIVR,DIV_BASE,#A9D5 tt:uint8,arithm_div,A9D5tt,[tt+1] LSHIFT#DIVR,x y - round(2^(tt+1)*x/y),34, -LSHIFTDIVC,DIV_BASE,#A9D6 tt:uint8,arithm_div,A9D6tt,[tt+1] LSHIFT#DIVC,x y - ceil(2^(tt+1)*x/y),26, -LSHIFT,,#AA cc:uint8,arithm_logical,AAcc,[cc+1] LSHIFT#,x - x*2^(cc+1),26,`0 <= cc <= 255` -RSHIFT,,#AB cc:uint8,arithm_logical,ABcc,[cc+1] RSHIFT#,x - floor(x/2^(cc+1)),18,`0 <= cc <= 255` -LSHIFT_VAR,,#AC,arithm_logical,AC,LSHIFT,x y - x*2^y,18,`0 <= y <= 1023` -RSHIFT_VAR,,#AD,arithm_logical,AD,RSHIFT,x y - floor(x/2^y),18,`0 <= y <= 1023` -POW2,,#AE,arithm_logical,AE,POW2,y - 2^y,18,"`0 <= y <= 1023` -Equivalent to `ONE` `SWAP` `LSHIFT`." -AND,,#B0,arithm_logical,B0,AND,x y - x&y,18,"Bitwise and of two signed integers `x` and `y`, sign-extended to infinity." -OR,,#B1,arithm_logical,B1,OR,x y - x|y,18,Bitwise or of two integers. -XOR,,#B2,arithm_logical,B2,XOR,x y - x xor y,18,Bitwise xor of two integers. -NOT,,#B3,arithm_logical,B3,NOT,x - ~x,26,Bitwise not of an integer. -FITS,,#B4 cc:uint8,arithm_logical,B4cc,[cc+1] FITS,x - x,26/76,"Checks whether `x` is a `cc+1`-bit signed integer for `0 <= cc <= 255` (i.e., whether `-2^cc <= x < 2^cc`). -If not, either triggers an integer overflow exception, or replaces `x` with a `NaN` (quiet version)." -CHKBOOL,FITS,#B400,arithm_logical,B400,CHKBOOL,x - x,26/76,"Checks whether `x` is a “boolean value'' (i.e., either 0 or -1)." -UFITS,,#B5 cc:uint8,arithm_logical,B5cc,[cc+1] UFITS,x - x,26/76,"Checks whether `x` is a `cc+1`-bit unsigned integer for `0 <= cc <= 255` (i.e., whether `0 <= x < 2^(cc+1)`)." -CHKBIT,UFITS,#B500,arithm_logical,B500,CHKBIT,x - x,26/76,"Checks whether `x` is a binary digit (i.e., zero or one)." -FITSX,,#B600,arithm_logical,B600,FITSX,x c - x,26/76,Checks whether `x` is a `c`-bit signed integer for `0 <= c <= 1023`. -UFITSX,,#B601,arithm_logical,B601,UFITSX,x c - x,26/76,Checks whether `x` is a `c`-bit unsigned integer for `0 <= c <= 1023`. -BITSIZE,,#B602,arithm_logical,B602,BITSIZE,x - c,26,Computes smallest `c >= 0` such that `x` fits into a `c`-bit signed integer (`-2^(c-1) <= c < 2^(c-1)`). -UBITSIZE,,#B603,arithm_logical,B603,UBITSIZE,x - c,26,"Computes smallest `c >= 0` such that `x` fits into a `c`-bit unsigned integer (`0 <= x < 2^c`), or throws a range check exception." -MIN,,#B608,arithm_logical,B608,MIN,x y - x or y,26,Computes the minimum of two integers `x` and `y`. -MAX,,#B609,arithm_logical,B609,MAX,x y - x or y,26,Computes the maximum of two integers `x` and `y`. -MINMAX,,#B60A,arithm_logical,B60A,"MINMAX -INTSORT2",x y - x y or y x,26,Sorts two integers. Quiet version of this operation returns two `NaN`s if any of the arguments are `NaN`s. -ABS,,#B60B,arithm_logical,B60B,ABS,x - |x|,26,Computes the absolute value of an integer `x`. -QADD,,#B7A0,arithm_quiet,B7A0,QADD,x y - x+y,26, -QSUB,,#B7A1,arithm_quiet,B7A1,QSUB,x y - x-y,26, -QSUBR,,#B7A2,arithm_quiet,B7A2,QSUBR,x y - y-x,26, -QNEGATE,,#B7A3,arithm_quiet,B7A3,QNEGATE,x - -x,26, -QINC,,#B7A4,arithm_quiet,B7A4,QINC,x - x+1,26, -QDEC,,#B7A5,arithm_quiet,B7A5,QDEC,x - x-1,26, -QMUL,,#B7A8,arithm_quiet,B7A8,QMUL,x y - x*y,26, -QDIV,,#B7A904,arithm_quiet,B7A904,QDIV,x y - q,34,Division returns `NaN` if `y=0`. -QDIVR,,#B7A905,arithm_quiet,B7A905,QDIVR,x y - q’,34, -QDIVC,,#B7A906,arithm_quiet,B7A906,QDIVC,x y - q'',34, -QMOD,,#B7A908,arithm_quiet,B7A908,QMOD,x y - r,34, -QDIVMOD,,#B7A90C,arithm_quiet,B7A90C,QDIVMOD,x y - q r,34, -QDIVMODR,,#B7A90D,arithm_quiet,B7A90D,QDIVMODR,x y - q' r',34, -QDIVMODC,,#B7A90E,arithm_quiet,B7A90E,QDIVMODC,x y - q'' r'',34, -QMULDIVR,,#B7A985,arithm_quiet,B7A985,QMULDIVR,x y z - q',34, -QMULDIVMOD,,#B7A98C,arithm_quiet,B7A98C,QMULDIVMOD,x y z - q r,34, -QLSHIFT,,#B7AC,arithm_quiet,B7AC,QLSHIFT,x y - x*2^y,26, -QRSHIFT,,#B7AD,arithm_quiet,B7AD,QRSHIFT,x y - floor(x/2^y),26, -QPOW2,,#B7AE,arithm_quiet,B7AE,QPOW2,y - 2^y,26, -QAND,,#B7B0,arithm_quiet,B7B0,QAND,x y - x&y,26, -QOR,,#B7B1,arithm_quiet,B7B1,QOR,x y - x|y,26, -QXOR,,#B7B2,arithm_quiet,B7B2,QXOR,x y - x xor y,26, -QNOT,,#B7B3,arithm_quiet,B7B3,QNOT,x - ~x,26, -QFITS,,#B7B4 cc:uint8,arithm_quiet,B7B4cc,[cc+1] QFITS,x - x,34,"Replaces `x` with a `NaN` if x is not a `cc+1`-bit signed integer, leaves it intact otherwise." -QUFITS,,#B7B5 cc:uint8,arithm_quiet,B7B5cc,[cc+1] QUFITS,x - x,34,"Replaces `x` with a `NaN` if x is not a `cc+1`-bit unsigned integer, leaves it intact otherwise." -QFITSX,,#B7B600,arithm_quiet,B7B600,QFITSX,x c - x,34,"Replaces `x` with a `NaN` if x is not a c-bit signed integer, leaves it intact otherwise." -QUFITSX,,#B7B601,arithm_quiet,B7B601,QUFITSX,x c - x,34,"Replaces `x` with a `NaN` if x is not a c-bit unsigned integer, leaves it intact otherwise." -SGN,,#B8,compare_int,B8,SGN,x - sgn(x),18,"Computes the sign of an integer `x`: -`-1` if `x<0`, `0` if `x=0`, `1` if `x>0`." -LESS,,#B9,compare_int,B9,LESS,x y - xy,18, -NEQ,,#BD,compare_int,BD,NEQ,x y - x!=y,18,Equivalent to `EQUAL` `NOT`. -GEQ,,#BE,compare_int,BE,GEQ,x y - x>=y,18,Equivalent to `LESS` `NOT`. -CMP,,#BF,compare_int,BF,CMP,x y - sgn(x-y),18,"Computes the sign of `x-y`: -`-1` if `xy`. -No integer overflow can occur here unless `x` or `y` is a `NaN`." -EQINT,,#C0 yy:int8,compare_int,C0yy,[yy] EQINT,x - x=yy,26,"Returns `-1` if `x=yy`, `0` otherwise. -`-2^7 <= yy < 2^7`." -ISZERO,EQINT,#C000,compare_int,C000,ISZERO,x - x=0,26,Checks whether an integer is zero. Corresponds to Forth's `0=`. -LESSINT,,#C1 yy:int8,compare_int,C1yy,"[yy] LESSINT -[yy-1] LEQINT",x - xyy,26,"Returns `-1` if `x>yy`, `0` otherwise. -`-2^7 <= yy < 2^7`." -ISPOS,GTINT,#C200,compare_int,C200,ISPOS,x - x>0,26,Checks whether an integer is positive. Corresponds to Forth's `0>`. -ISNNEG,GTINT,#C2FF,compare_int,C2FF,ISNNEG,x - x >=0,26,Checks whether an integer is non-negative. -NEQINT,,#C3 yy:int8,compare_int,C3yy,[yy] NEQINT,x - x!=yy,26,"Returns `-1` if `x!=yy`, `0` otherwise. -`-2^7 <= yy < 2^7`." -ISNAN,,#C4,compare_int,C4,ISNAN,x - x=NaN,18,Checks whether `x` is a `NaN`. -CHKNAN,,#C5,compare_int,C5,CHKNAN,x - x,18/68,Throws an arithmetic overflow exception if `x` is a `NaN`. -SEMPTY,,#C700,compare_other,C700,SEMPTY,s - ?,26,"Checks whether a _Slice_ `s` is empty (i.e., contains no bits of data and no cell references)." -SDEMPTY,,#C701,compare_other,C701,SDEMPTY,s - ?,26,Checks whether _Slice_ `s` has no bits of data. -SREMPTY,,#C702,compare_other,C702,SREMPTY,s - ?,26,Checks whether _Slice_ `s` has no references. -SDFIRST,,#C703,compare_other,C703,SDFIRST,s - ?,26,Checks whether the first bit of _Slice_ `s` is a one. -SDLEXCMP,,#C704,compare_other,C704,SDLEXCMP,s s' - x,26,"Compares the data of `s` lexicographically with the data of `s'`, returning `-1`, 0, or 1 depending on the result." -SDEQ,,#C705,compare_other,C705,SDEQ,s s' - ?,26,"Checks whether the data parts of `s` and `s'` coincide, equivalent to `SDLEXCMP` `ISZERO`." -SDPFX,,#C708,compare_other,C708,SDPFX,s s' - ?,26,Checks whether `s` is a prefix of `s'`. -SDPFXREV,,#C709,compare_other,C709,SDPFXREV,s s' - ?,26,"Checks whether `s'` is a prefix of `s`, equivalent to `SWAP` `SDPFX`." -SDPPFX,,#C70A,compare_other,C70A,SDPPFX,s s' - ?,26,"Checks whether `s` is a proper prefix of `s'` (i.e., a prefix distinct from `s'`)." -SDPPFXREV,,#C70B,compare_other,C70B,SDPPFXREV,s s' - ?,26,Checks whether `s'` is a proper prefix of `s`. -SDSFX,,#C70C,compare_other,C70C,SDSFX,s s' - ?,26,Checks whether `s` is a suffix of `s'`. -SDSFXREV,,#C70D,compare_other,C70D,SDSFXREV,s s' - ?,26,Checks whether `s'` is a suffix of `s`. -SDPSFX,,#C70E,compare_other,C70E,SDPSFX,s s' - ?,26,Checks whether `s` is a proper suffix of `s'`. -SDPSFXREV,,#C70F,compare_other,C70F,SDPSFXREV,s s' - ?,26,Checks whether `s'` is a proper suffix of `s`. -SDCNTLEAD0,,#C710,compare_other,C710,SDCNTLEAD0,s - n,26,Returns the number of leading zeroes in `s`. -SDCNTLEAD1,,#C711,compare_other,C711,SDCNTLEAD1,s - n,26,Returns the number of leading ones in `s`. -SDCNTTRAIL0,,#C712,compare_other,C712,SDCNTTRAIL0,s - n,26,Returns the number of trailing zeroes in `s`. -SDCNTTRAIL1,,#C713,compare_other,C713,SDCNTTRAIL1,s - n,26,Returns the number of trailing ones in `s`. -NEWC,,#C8,cell_build,C8,NEWC,- b,18,Creates a new empty _Builder_. -ENDC,,#C9,cell_build,C9,ENDC,b - c,518,Converts a _Builder_ into an ordinary _Cell_. -STI,,#CA cc:uint8,cell_build,CAcc,[cc+1] STI,x b - b',26,"Stores a signed `cc+1`-bit integer `x` into _Builder_ `b` for `0 <= cc <= 255`, throws a range check exception if `x` does not fit into `cc+1` bits." -STU,,#CB cc:uint8,cell_build,CBcc,[cc+1] STU,x b - b',26,Stores an unsigned `cc+1`-bit integer `x` into _Builder_ `b`. In all other respects it is similar to `STI`. -STREF,,#CC,cell_build,CC,STREF,c b - b',18,Stores a reference to _Cell_ `c` into _Builder_ `b`. -STBREFR,,#CD,cell_build,CD,"STBREFR -ENDCST",b b'' - b,518,Equivalent to `ENDC` `SWAP` `STREF`. -STSLICE,,#CE,cell_build,CE,STSLICE,s b - b',18,Stores _Slice_ `s` into _Builder_ `b`. -STIX,,#CF00,cell_build,CF00,STIX,x b l - b',26,Stores a signed `l`-bit integer `x` into `b` for `0 <= l <= 257`. -STUX,,#CF01,cell_build,CF01,STUX,x b l - b',26,Stores an unsigned `l`-bit integer `x` into `b` for `0 <= l <= 256`. -STIXR,,#CF02,cell_build,CF02,STIXR,b x l - b',26,"Similar to `STIX`, but with arguments in a different order." -STUXR,,#CF03,cell_build,CF03,STUXR,b x l - b',26,"Similar to `STUX`, but with arguments in a different order." -STIXQ,,#CF04,cell_build,CF04,STIXQ,x b l - x b f or b' 0,26,"A quiet version of `STIX`. If there is no space in `b`, sets `b'=b` and `f=-1`. -If `x` does not fit into `l` bits, sets `b'=b` and `f=1`. -If the operation succeeds, `b'` is the new _Builder_ and `f=0`. -However, `0 <= l <= 257`, with a range check exception if this is not so." -STUXQ,,#CF05,cell_build,CF05,STUXQ,x b l - x b f or b' 0,26,A quiet version of `STUX`. -STIXRQ,,#CF06,cell_build,CF06,STIXRQ,b x l - b x f or b' 0,26,A quiet version of `STIXR`. -STUXRQ,,#CF07,cell_build,CF07,STUXRQ,b x l - b x f or b' 0,26,A quiet version of `STUXR`. -STI_ALT,,#CF08 cc:uint8,cell_build,CF08cc,[cc+1] STI_l,x b - b',34,A longer version of `[cc+1] STI`. -STU_ALT,,#CF09 cc:uint8,cell_build,CF09cc,[cc+1] STU_l,x b - b',34,A longer version of `[cc+1] STU`. -STIR,,#CF0A cc:uint8,cell_build,CF0Acc,[cc+1] STIR,b x - b',34,Equivalent to `SWAP` `[cc+1] STI`. -STUR,,#CF0B cc:uint8,cell_build,CF0Bcc,[cc+1] STUR,b x - b',34,Equivalent to `SWAP` `[cc+1] STU`. -STIQ,,#CF0C cc:uint8,cell_build,CF0Ccc,[cc+1] STIQ,x b - x b f or b' 0,34,A quiet version of `STI`. -STUQ,,#CF0D cc:uint8,cell_build,CF0Dcc,[cc+1] STUQ,x b - x b f or b' 0,34,A quiet version of `STU`. -STIRQ,,#CF0E cc:uint8,cell_build,CF0Ecc,[cc+1] STIRQ,b x - b x f or b' 0,34,A quiet version of `STIR`. -STURQ,,#CF0F cc:uint8,cell_build,CF0Fcc,[cc+1] STURQ,b x - b x f or b' 0,34,A quiet version of `STUR`. -STREF_ALT,,#CF10,cell_build,CF10,STREF_l,c b - b',26,A longer version of `STREF`. -STBREF,,#CF11,cell_build,CF11,STBREF,b' b - b'',526,Equivalent to `SWAP` `STBREFR`. -STSLICE_ALT,,#CF12,cell_build,CF12,STSLICE_l,s b - b',26,A longer version of `STSLICE`. -STB,,#CF13,cell_build,CF13,STB,b' b - b'',26,Appends all data from _Builder_ `b'` to _Builder_ `b`. -STREFR,,#CF14,cell_build,CF14,STREFR,b c - b',26,Equivalent to `SWAP` `STREF`. -STBREFR_ALT,,#CF15,cell_build,CF15,STBREFR_l,b b' - b'',526,A longer encoding of `STBREFR`. -STSLICER,,#CF16,cell_build,CF16,STSLICER,b s - b',26,Equivalent to `SWAP` `STSLICE`. -STBR,,#CF17,cell_build,CF17,"STBR -BCONCAT",b b' - b'',26,"Concatenates two builders. -Equivalent to `SWAP` `STB`." -STREFQ,,#CF18,cell_build,CF18,STREFQ,c b - c b -1 or b' 0,26,Quiet version of `STREF`. -STBREFQ,,#CF19,cell_build,CF19,STBREFQ,b' b - b' b -1 or b'' 0,526,Quiet version of `STBREF`. -STSLICEQ,,#CF1A,cell_build,CF1A,STSLICEQ,s b - s b -1 or b' 0,26,Quiet version of `STSLICE`. -STBQ,,#CF1B,cell_build,CF1B,STBQ,b' b - b' b -1 or b'' 0,26,Quiet version of `STB`. -STREFRQ,,#CF1C,cell_build,CF1C,STREFRQ,b c - b c -1 or b' 0,26,Quiet version of `STREFR`. -STBREFRQ,,#CF1D,cell_build,CF1D,STBREFRQ,b b' - b b' -1 or b'' 0,526,Quiet version of `STBREFR`. -STSLICERQ,,#CF1E,cell_build,CF1E,STSLICERQ,b s - b s -1 or b'' 0,26,Quiet version of `STSLICER`. -STBRQ,,#CF1F,cell_build,CF1F,"STBRQ -BCONCATQ",b b' - b b' -1 or b'' 0,26,Quiet version of `STBR`. -STREFCONST,,#CF20 c:^Cell,cell_build,CF20,[ref] STREFCONST,b - b’,26,Equivalent to `PUSHREF` `STREFR`. -STREF2CONST,,#CF21 c1:^Cell c2:^Cell,cell_build,CF21,[ref] [ref] STREF2CONST,b - b’,26,Equivalent to `STREFCONST` `STREFCONST`. -ENDXC,,#CF23,cell_build,CF23,,b x - c,526,"If `x!=0`, creates a _special_ or _exotic_ cell from _Builder_ `b`. -The type of the exotic cell must be stored in the first 8 bits of `b`. -If `x=0`, it is equivalent to `ENDC`. Otherwise some validity checks on the data and references of `b` are performed before creating the exotic cell." -STILE4,,#CF28,cell_build,CF28,STILE4,x b - b',26,Stores a little-endian signed 32-bit integer. -STULE4,,#CF29,cell_build,CF29,STULE4,x b - b',26,Stores a little-endian unsigned 32-bit integer. -STILE8,,#CF2A,cell_build,CF2A,STILE8,x b - b',26,Stores a little-endian signed 64-bit integer. -STULE8,,#CF2B,cell_build,CF2B,STULE8,x b - b',26,Stores a little-endian unsigned 64-bit integer. -BDEPTH,,#CF30,cell_build,CF30,BDEPTH,b - x,26,"Returns the depth of _Builder_ `b`. If no cell references are stored in `b`, then `x=0`; otherwise `x` is one plus the maximum of depths of cells referred to from `b`." -BBITS,,#CF31,cell_build,CF31,BBITS,b - x,26,Returns the number of data bits already stored in _Builder_ `b`. -BREFS,,#CF32,cell_build,CF32,BREFS,b - y,26,Returns the number of cell references already stored in `b`. -BBITREFS,,#CF33,cell_build,CF33,BBITREFS,b - x y,26,Returns the numbers of both data bits and cell references in `b`. -BREMBITS,,#CF35,cell_build,CF35,BREMBITS,b - x',26,Returns the number of data bits that can still be stored in `b`. -BREMREFS,,#CF36,cell_build,CF36,BREMREFS,b - y',26,Returns the number of references that can still be stored in `b`. -BREMBITREFS,,#CF37,cell_build,CF37,BREMBITREFS,b - x' y',26,Returns the numbers of both data bits and references that can still be stored in `b`. -BCHKBITS,,#CF38 cc:uint8,cell_build,CF38cc,[cc+1] BCHKBITS#,b -,34/84,"Checks whether `cc+1` bits can be stored into `b`, where `0 <= cc <= 255`." -BCHKBITS_VAR,,#CF39,cell_build,CF39,BCHKBITS,b x - ,26/76,"Checks whether `x` bits can be stored into `b`, `0 <= x <= 1023`. If there is no space for `x` more bits in `b`, or if `x` is not within the range `0...1023`, throws an exception." -BCHKREFS,,#CF3A,cell_build,CF3A,BCHKREFS,b y - ,26/76,"Checks whether `y` references can be stored into `b`, `0 <= y <= 7`." -BCHKBITREFS,,#CF3B,cell_build,CF3B,BCHKBITREFS,b x y - ,26/76,"Checks whether `x` bits and `y` references can be stored into `b`, `0 <= x <= 1023`, `0 <= y <= 7`." -BCHKBITSQ,,#CF3C cc:uint8,cell_build,CF3Ccc,[cc+1] BCHKBITSQ#,b - ?,34,"Checks whether `cc+1` bits can be stored into `b`, where `0 <= cc <= 255`." -BCHKBITSQ_VAR,,#CF3D,cell_build,CF3D,BCHKBITSQ,b x - ?,26,"Checks whether `x` bits can be stored into `b`, `0 <= x <= 1023`." -BCHKREFSQ,,#CF3E,cell_build,CF3E,BCHKREFSQ,b y - ?,26,"Checks whether `y` references can be stored into `b`, `0 <= y <= 7`." -BCHKBITREFSQ,,#CF3F,cell_build,CF3F,BCHKBITREFSQ,b x y - ?,26,"Checks whether `x` bits and `y` references can be stored into `b`, `0 <= x <= 1023`, `0 <= y <= 7`." -STZEROES,,#CF40,cell_build,CF40,STZEROES,b n - b',26,Stores `n` binary zeroes into _Builder_ `b`. -STONES,,#CF41,cell_build,CF41,STONES,b n - b',26,Stores `n` binary ones into _Builder_ `b`. -STSAME,,#CF42,cell_build,CF42,STSAME,b n x - b',26,Stores `n` binary `x`es (`0 <= x <= 1`) into _Builder_ `b`. -STSLICECONST,,#CFC0_ x:(## 2) y:(## 3) c:(x * ^Cell) sss:((8 * y + 2) * Bit),cell_build,CFC0_xysss,[slice] STSLICECONST,b - b',24,"Stores a constant subslice `sss`. -_Details:_ `sss` consists of `0 <= x <= 3` references and up to `8y+2` data bits, with `0 <= y <= 7`. Completion bit is assumed. -Note that the assembler can replace `STSLICECONST` with `PUSHSLICE` `STSLICER` if the slice is too big." -STZERO,STSLICECONST,#CF81,cell_build,CF81,STZERO,b - b',24,Stores one binary zero. -STONE,STSLICECONST,#CF83,cell_build,CF83,STONE,b - b',24,Stores one binary one. -CTOS,,#D0,cell_parse,D0,CTOS,c - s,118/43,"Converts a _Cell_ into a _Slice_. Notice that `c` must be either an ordinary cell, or an exotic cell which is automatically _loaded_ to yield an ordinary cell `c'`, converted into a _Slice_ afterwards." -ENDS,,#D1,cell_parse,D1,ENDS,s - ,18/68,"Removes a _Slice_ `s` from the stack, and throws an exception if it is not empty." -LDI,,#D2 cc:uint8,cell_parse,D2cc,[cc+1] LDI,s - x s',26,"Loads (i.e., parses) a signed `cc+1`-bit integer `x` from _Slice_ `s`, and returns the remainder of `s` as `s'`." -LDU,,#D3 cc:uint8,cell_parse,D3cc,[cc+1] LDU,s - x s',26,Loads an unsigned `cc+1`-bit integer `x` from _Slice_ `s`. -LDREF,,#D4,cell_parse,D4,LDREF,s - c s',18,Loads a cell reference `c` from `s`. -LDREFRTOS,,#D5,cell_parse,D5,LDREFRTOS,s - s' s'',118/43,Equivalent to `LDREF` `SWAP` `CTOS`. -LDSLICE,,#D6 cc:uint8,cell_parse,D6cc,[cc+1] LDSLICE,s - s'' s',26,Cuts the next `cc+1` bits of `s` into a separate _Slice_ `s''`. -LDIX,,#D700,cell_parse,D700,LDIX,s l - x s',26,"Loads a signed `l`-bit (`0 <= l <= 257`) integer `x` from _Slice_ `s`, and returns the remainder of `s` as `s'`." -LDUX,,#D701,cell_parse,D701,LDUX,s l - x s',26,"Loads an unsigned `l`-bit integer `x` from (the first `l` bits of) `s`, with `0 <= l <= 256`." -PLDIX,,#D702,cell_parse,D702,PLDIX,s l - x,26,"Preloads a signed `l`-bit integer from _Slice_ `s`, for `0 <= l <= 257`." -PLDUX,,#D703,cell_parse,D703,PLDUX,s l - x,26,"Preloads an unsigned `l`-bit integer from `s`, for `0 <= l <= 256`." -LDIXQ,,#D704,cell_parse,D704,LDIXQ,s l - x s' -1 or s 0,26,"Quiet version of `LDIX`: loads a signed `l`-bit integer from `s` similarly to `LDIX`, but returns a success flag, equal to `-1` on success or to `0` on failure (if `s` does not have `l` bits), instead of throwing a cell underflow exception." -LDUXQ,,#D705,cell_parse,D705,LDUXQ,s l - x s' -1 or s 0,26,Quiet version of `LDUX`. -PLDIXQ,,#D706,cell_parse,D706,PLDIXQ,s l - x -1 or 0,26,Quiet version of `PLDIX`. -PLDUXQ,,#D707,cell_parse,D707,PLDUXQ,s l - x -1 or 0,26,Quiet version of `PLDUX`. -LDI_ALT,,#D708 cc:uint8,cell_parse,D708cc,[cc+1] LDI_l,s - x s',34,A longer encoding for `LDI`. -LDU_ALT,,#D709 cc:uint8,cell_parse,D709cc,[cc+1] LDU_l,s - x s',34,A longer encoding for `LDU`. -PLDI,,#D70A cc:uint8,cell_parse,D70Acc,[cc+1] PLDI,s - x,34,Preloads a signed `cc+1`-bit integer from _Slice_ `s`. -PLDU,,#D70B cc:uint8,cell_parse,D70Bcc,[cc+1] PLDU,s - x,34,Preloads an unsigned `cc+1`-bit integer from `s`. -LDIQ,,#D70C cc:uint8,cell_parse,D70Ccc,[cc+1] LDIQ,s - x s' -1 or s 0,34,A quiet version of `LDI`. -LDUQ,,#D70D cc:uint8,cell_parse,D70Dcc,[cc+1] LDUQ,s - x s' -1 or s 0,34,A quiet version of `LDU`. -PLDIQ,,#D70E cc:uint8,cell_parse,D70Ecc,[cc+1] PLDIQ,s - x -1 or 0,34,A quiet version of `PLDI`. -PLDUQ,,#D70F cc:uint8,cell_parse,D70Fcc,[cc+1] PLDUQ,s - x -1 or 0,34,A quiet version of `PLDU`. -PLDUZ,,#D714_ c:uint3,cell_parse,D714_c,[32(c+1)] PLDUZ,s - s x,26,"Preloads the first `32(c+1)` bits of _Slice_ `s` into an unsigned integer `x`, for `0 <= c <= 7`. If `s` is shorter than necessary, missing bits are assumed to be zero. This operation is intended to be used along with `IFBITJMP` and similar instructions." -LDSLICEX,,#D718,cell_parse,D718,LDSLICEX,s l - s'' s',26,"Loads the first `0 <= l <= 1023` bits from _Slice_ `s` into a separate _Slice_ `s''`, returning the remainder of `s` as `s'`." -PLDSLICEX,,#D719,cell_parse,D719,PLDSLICEX,s l - s'',26,Returns the first `0 <= l <= 1023` bits of `s` as `s''`. -LDSLICEXQ,,#D71A,cell_parse,D71A,LDSLICEXQ,s l - s'' s' -1 or s 0,26,A quiet version of `LDSLICEX`. -PLDSLICEXQ,,#D71B,cell_parse,D71B,PLDSLICEXQ,s l - s' -1 or 0,26,A quiet version of `LDSLICEXQ`. -LDSLICE_ALT,,#D71C cc:uint8,cell_parse,D71Ccc,[cc+1] LDSLICE_l,s - s'' s',34,A longer encoding for `LDSLICE`. -PLDSLICE,,#D71D cc:uint8,cell_parse,D71Dcc,[cc+1] PLDSLICE,s - s'',34,Returns the first `0 < cc+1 <= 256` bits of `s` as `s''`. -LDSLICEQ,,#D71E cc:uint8,cell_parse,D71Ecc,[cc+1] LDSLICEQ,s - s'' s' -1 or s 0,34,A quiet version of `LDSLICE`. -PLDSLICEQ,,#D71F cc:uint8,cell_parse,D71Fcc,[cc+1] PLDSLICEQ,s - s'' -1 or 0,34,A quiet version of `PLDSLICE`. -SDCUTFIRST,,#D720,cell_parse,D720,SDCUTFIRST,s l - s',26,Returns the first `0 <= l <= 1023` bits of `s`. It is equivalent to `PLDSLICEX`. -SDSKIPFIRST,,#D721,cell_parse,D721,SDSKIPFIRST,s l - s',26,Returns all but the first `0 <= l <= 1023` bits of `s`. It is equivalent to `LDSLICEX` `NIP`. -SDCUTLAST,,#D722,cell_parse,D722,SDCUTLAST,s l - s',26,Returns the last `0 <= l <= 1023` bits of `s`. -SDSKIPLAST,,#D723,cell_parse,D723,SDSKIPLAST,s l - s',26,Returns all but the last `0 <= l <= 1023` bits of `s`. -SDSUBSTR,,#D724,cell_parse,D724,SDSUBSTR,s l l' - s',26,"Returns `0 <= l' <= 1023` bits of `s` starting from offset `0 <= l <= 1023`, thus extracting a bit substring out of the data of `s`." -SDBEGINSX,,#D726,cell_parse,D726,SDBEGINSX,s s' - s'',26,"Checks whether `s` begins with (the data bits of) `s'`, and removes `s'` from `s` on success. On failure throws a cell deserialization exception. Primitive `SDPFXREV` can be considered a quiet version of `SDBEGINSX`." -SDBEGINSXQ,,#D727,cell_parse,D727,SDBEGINSXQ,s s' - s'' -1 or s 0,26,A quiet version of `SDBEGINSX`. -SDBEGINS,,#D72A_ x:(## 7) sss:((8 * x + 3) * Bit),cell_parse,D72A_xsss,[slice] SDBEGINS,s - s'',31,"Checks whether `s` begins with constant bitstring `sss` of length `8x+3` (with continuation bit assumed), where `0 <= x <= 127`, and removes `sss` from `s` on success." -SDBEGINSQ,,#D72E_ x:(## 7) sss:((8 * x + 3) * Bit),cell_parse,D72E_xsss,[slice] SDBEGINSQ,s - s'' -1 or s 0,31,A quiet version of `SDBEGINS`. -SCUTFIRST,,#D730,cell_parse,D730,SCUTFIRST,s l r - s',26,Returns the first `0 <= l <= 1023` bits and first `0 <= r <= 4` references of `s`. -SSKIPFIRST,,#D731,cell_parse,D731,SSKIPFIRST,s l r - s',26,Returns all but the first `l` bits of `s` and `r` references of `s`. -SCUTLAST,,#D732,cell_parse,D732,SCUTLAST,s l r - s',26,Returns the last `0 <= l <= 1023` data bits and last `0 <= r <= 4` references of `s`. -SSKIPLAST,,#D733,cell_parse,D733,SSKIPLAST,s l r - s',26,Returns all but the last `l` bits of `s` and `r` references of `s`. -SUBSLICE,,#D734,cell_parse,D734,SUBSLICE,s l r l' r' - s',26,"Returns `0 <= l' <= 1023` bits and `0 <= r' <= 4` references from _Slice_ `s`, after skipping the first `0 <= l <= 1023` bits and first `0 <= r <= 4` references." -SPLIT,,#D736,cell_parse,D736,SPLIT,s l r - s' s'',26,"Splits the first `0 <= l <= 1023` data bits and first `0 <= r <= 4` references from `s` into `s'`, returning the remainder of `s` as `s''`." -SPLITQ,,#D737,cell_parse,D737,SPLITQ,s l r - s' s'' -1 or s 0,26,A quiet version of `SPLIT`. -XCTOS,,#D739,cell_parse,D739,,c - s ?,,"Transforms an ordinary or exotic cell into a _Slice_, as if it were an ordinary cell. A flag is returned indicating whether `c` is exotic. If that be the case, its type can later be deserialized from the first eight bits of `s`." -XLOAD,,#D73A,cell_parse,D73A,,c - c',,"Loads an exotic cell `c` and returns an ordinary cell `c'`. If `c` is already ordinary, does nothing. If `c` cannot be loaded, throws an exception." -XLOADQ,,#D73B,cell_parse,D73B,,c - c' -1 or c 0,,"Loads an exotic cell `c` and returns an ordinary cell `c'`. If `c` is already ordinary, does nothing. If `c` cannot be loaded, returns 0." -SCHKBITS,,#D741,cell_parse,D741,SCHKBITS,s l - ,26/76,"Checks whether there are at least `l` data bits in _Slice_ `s`. If this is not the case, throws a cell deserialisation (i.e., cell underflow) exception." -SCHKREFS,,#D742,cell_parse,D742,SCHKREFS,s r - ,26/76,Checks whether there are at least `r` references in _Slice_ `s`. -SCHKBITREFS,,#D743,cell_parse,D743,SCHKBITREFS,s l r - ,26/76,Checks whether there are at least `l` data bits and `r` references in _Slice_ `s`. -SCHKBITSQ,,#D745,cell_parse,D745,SCHKBITSQ,s l - ?,26,Checks whether there are at least `l` data bits in _Slice_ `s`. -SCHKREFSQ,,#D746,cell_parse,D746,SCHKREFSQ,s r - ?,26,Checks whether there are at least `r` references in _Slice_ `s`. -SCHKBITREFSQ,,#D747,cell_parse,D747,SCHKBITREFSQ,s l r - ?,26,Checks whether there are at least `l` data bits and `r` references in _Slice_ `s`. -PLDREFVAR,,#D748,cell_parse,D748,PLDREFVAR,s n - c,26,Returns the `n`-th cell reference of _Slice_ `s` for `0 <= n <= 3`. -SBITS,,#D749,cell_parse,D749,SBITS,s - l,26,Returns the number of data bits in _Slice_ `s`. -SREFS,,#D74A,cell_parse,D74A,SREFS,s - r,26,Returns the number of references in _Slice_ `s`. -SBITREFS,,#D74B,cell_parse,D74B,SBITREFS,s - l r,26,Returns both the number of data bits and the number of references in `s`. -PLDREFIDX,,#D74E_ n:uint2,cell_parse,D74E_n,[n] PLDREFIDX,s - c,26,"Returns the `n`-th cell reference of _Slice_ `s`, where `0 <= n <= 3`." -PLDREF,PLDREFIDX,#D74C,cell_parse,D74C,PLDREF,s - c,26,Preloads the first cell reference of a _Slice_. -LDILE4,,#D750,cell_parse,D750,LDILE4,s - x s',26,Loads a little-endian signed 32-bit integer. -LDULE4,,#D751,cell_parse,D751,LDULE4,s - x s',26,Loads a little-endian unsigned 32-bit integer. -LDILE8,,#D752,cell_parse,D752,LDILE8,s - x s',26,Loads a little-endian signed 64-bit integer. -LDULE8,,#D753,cell_parse,D753,LDULE8,s - x s',26,Loads a little-endian unsigned 64-bit integer. -PLDILE4,,#D754,cell_parse,D754,PLDILE4,s - x,26,Preloads a little-endian signed 32-bit integer. -PLDULE4,,#D755,cell_parse,D755,PLDULE4,s - x,26,Preloads a little-endian unsigned 32-bit integer. -PLDILE8,,#D756,cell_parse,D756,PLDILE8,s - x,26,Preloads a little-endian signed 64-bit integer. -PLDULE8,,#D757,cell_parse,D757,PLDULE8,s - x,26,Preloads a little-endian unsigned 64-bit integer. -LDILE4Q,,#D758,cell_parse,D758,LDILE4Q,s - x s' -1 or s 0,26,Quietly loads a little-endian signed 32-bit integer. -LDULE4Q,,#D759,cell_parse,D759,LDULE4Q,s - x s' -1 or s 0,26,Quietly loads a little-endian unsigned 32-bit integer. -LDILE8Q,,#D75A,cell_parse,D75A,LDILE8Q,s - x s' -1 or s 0,26,Quietly loads a little-endian signed 64-bit integer. -LDULE8Q,,#D75B,cell_parse,D75B,LDULE8Q,s - x s' -1 or s 0,26,Quietly loads a little-endian unsigned 64-bit integer. -PLDILE4Q,,#D75C,cell_parse,D75C,PLDILE4Q,s - x -1 or 0,26,Quietly preloads a little-endian signed 32-bit integer. -PLDULE4Q,,#D75D,cell_parse,D75D,PLDULE4Q,s - x -1 or 0,26,Quietly preloads a little-endian unsigned 32-bit integer. -PLDILE8Q,,#D75E,cell_parse,D75E,PLDILE8Q,s - x -1 or 0,26,Quietly preloads a little-endian signed 64-bit integer. -PLDULE8Q,,#D75F,cell_parse,D75F,PLDULE8Q,s - x -1 or 0,26,Quietly preloads a little-endian unsigned 64-bit integer. -LDZEROES,,#D760,cell_parse,D760,LDZEROES,s - n s',26,"Returns the count `n` of leading zero bits in `s`, and removes these bits from `s`." -LDONES,,#D761,cell_parse,D761,LDONES,s - n s',26,"Returns the count `n` of leading one bits in `s`, and removes these bits from `s`." -LDSAME,,#D762,cell_parse,D762,LDSAME,s x - n s',26,"Returns the count `n` of leading bits equal to `0 <= x <= 1` in `s`, and removes these bits from `s`." -SDEPTH,,#D764,cell_parse,D764,SDEPTH,s - x,26,"Returns the depth of _Slice_ `s`. If `s` has no references, then `x=0`; otherwise `x` is one plus the maximum of depths of cells referred to from `s`." -CDEPTH,,#D765,cell_parse,D765,CDEPTH,c - x,26,"Returns the depth of _Cell_ `c`. If `c` has no references, then `x=0`; otherwise `x` is one plus the maximum of depths of cells referred to from `c`. If `c` is a _Null_ instead of a _Cell_, returns zero." -EXECUTE,,#D8,cont_basic,D8,"EXECUTE -CALLX",c - ,18,"_Calls_, or _executes_, continuation `c`." -JMPX,,#D9,cont_basic,D9,JMPX,c - ,18,"_Jumps_, or transfers control, to continuation `c`. -The remainder of the previous current continuation `cc` is discarded." -CALLXARGS,,#DA p:uint4 r:uint4,cont_basic,DApr,[p] [r] CALLXARGS,c - ,26,"_Calls_ continuation `c` with `p` parameters and expecting `r` return values -`0 <= p <= 15`, `0 <= r <= 15`" -CALLXARGS_VAR,,#DB0 p:uint4,cont_basic,DB0p,[p] -1 CALLXARGS,c - ,26,"_Calls_ continuation `c` with `0 <= p <= 15` parameters, expecting an arbitrary number of return values." -JMPXARGS,,#DB1 p:uint4,cont_basic,DB1p,[p] JMPXARGS,c - ,26,"_Jumps_ to continuation `c`, passing only the top `0 <= p <= 15` values from the current stack to it (the remainder of the current stack is discarded)." -RETARGS,,#DB2 r:uint4,cont_basic,DB2r,[r] RETARGS,,26,"_Returns_ to `c0`, with `0 <= r <= 15` return values taken from the current stack." -RET,,#DB30,cont_basic,DB30,"RET -RETTRUE",,26,"_Returns_ to the continuation at `c0`. The remainder of the current continuation `cc` is discarded. -Approximately equivalent to `c0 PUSHCTR` `JMPX`." -RETALT,,#DB31,cont_basic,DB31,"RETALT -RETFALSE",,26,"_Returns_ to the continuation at `c1`. -Approximately equivalent to `c1 PUSHCTR` `JMPX`." -BRANCH,,#DB32,cont_basic,DB32,"BRANCH -RETBOOL",f - ,26,"Performs `RETTRUE` if integer `f!=0`, or `RETFALSE` if `f=0`." -CALLCC,,#DB34,cont_basic,DB34,CALLCC,c - ,26,"_Call with current continuation_, transfers control to `c`, pushing the old value of `cc` into `c`'s stack (instead of discarding it or writing it into new `c0`)." -JMPXDATA,,#DB35,cont_basic,DB35,JMPXDATA,c - ,26,"Similar to `CALLCC`, but the remainder of the current continuation (the old value of `cc`) is converted into a _Slice_ before pushing it into the stack of `c`." -CALLCCARGS,,#DB36 p:uint4 r:uint4,cont_basic,DB36pr,[p] [r] CALLCCARGS,c - ,34,"Similar to `CALLXARGS`, but pushes the old value of `cc` (along with the top `0 <= p <= 15` values from the original stack) into the stack of newly-invoked continuation `c`, setting `cc.nargs` to `-1 <= r <= 14`." -CALLXVARARGS,,#DB38,cont_basic,DB38,CALLXVARARGS,c p r - ,26,"Similar to `CALLXARGS`, but takes `-1 <= p,r <= 254` from the stack. The next three operations also take `p` and `r` from the stack, both in the range `-1...254`." -RETVARARGS,,#DB39,cont_basic,DB39,RETVARARGS,p r - ,26,Similar to `RETARGS`. -JMPXVARARGS,,#DB3A,cont_basic,DB3A,JMPXVARARGS,c p r - ,26,Similar to `JMPXARGS`. -CALLCCVARARGS,,#DB3B,cont_basic,DB3B,CALLCCVARARGS,c p r - ,26,Similar to `CALLCCARGS`. -CALLREF,,#DB3C c:^Cell,cont_basic,DB3C,[ref] CALLREF,,126/51,Equivalent to `PUSHREFCONT` `CALLX`. -JMPREF,,#DB3D c:^Cell,cont_basic,DB3D,[ref] JMPREF,,126/51,Equivalent to `PUSHREFCONT` `JMPX`. -JMPREFDATA,,#DB3E c:^Cell,cont_basic,DB3E,[ref] JMPREFDATA,,126/51,Equivalent to `PUSHREFCONT` `JMPXDATA`. -RETDATA,,#DB3F,cont_basic,DB3F,RETDATA,,26,"Equivalent to `c0 PUSHCTR` `JMPXDATA`. In this way, the remainder of the current continuation is converted into a _Slice_ and returned to the caller." -IFRET,,#DC,cont_conditional,DC,"IFRET -IFNOT:",f - ,18,"Performs a `RET`, but only if integer `f` is non-zero. If `f` is a `NaN`, throws an integer overflow exception." -IFNOTRET,,#DD,cont_conditional,DD,"IFNOTRET -IF:",f - ,18,"Performs a `RET`, but only if integer `f` is zero." -IF,,#DE,cont_conditional,DE,IF,f c - ,18,"Performs `EXECUTE` for `c` (i.e., _executes_ `c`), but only if integer `f` is non-zero. Otherwise simply discards both values." -,,,cont_conditional,DE,"IF:<{ code }> -<{ code }>IF",f -,,Equivalent to `<{ code }> CONT` `IF`. -IFNOT,,#DF,cont_conditional,DF,IFNOT,f c - ,18,"Executes continuation `c`, but only if integer `f` is zero. Otherwise simply discards both values." -,,,cont_conditional,DF,"IFNOT:<{ code }> -<{ code }>IFNOT",f -,,Equivalent to `<{ code }> CONT` `IFNOT`. -IFJMP,,#E0,cont_conditional,E0,IFJMP,f c - ,18,"Jumps to `c` (similarly to `JMPX`), but only if `f` is non-zero." -,,,cont_conditional,E0,IFJMP:<{ code }>,f -,,Equivalent to `<{ code }> CONT` `IFJMP`. -IFNOTJMP,,#E1,cont_conditional,E1,IFNOTJMP,f c - ,18,"Jumps to `c` (similarly to `JMPX`), but only if `f` is zero." -,,,cont_conditional,E1,IFNOTJMP:<{ code }>,f -,,Equivalent to `<{ code }> CONT` `IFNOTJMP`. -IFELSE,,#E2,cont_conditional,E2,IFELSE,f c c' - ,18,"If integer `f` is non-zero, executes `c`, otherwise executes `c'`. Equivalent to `CONDSELCHK` `EXECUTE`." -,,,cont_conditional,E2,IF:<{ code1 }>ELSE<{ code2 }>,f -,,Equivalent to `<{ code1 }> CONT` `<{ code2 }> CONT` `IFELSE`. -IFREF,,#E300 c:^Cell,cont_conditional,E300,[ref] IFREF,f - ,26/126/51,"Equivalent to `PUSHREFCONT` `IF`, with the optimization that the cell reference is not actually loaded into a _Slice_ and then converted into an ordinary _Continuation_ if `f=0`. -Gas consumption of this primitive depends on whether `f=0` and whether the reference was loaded before. -Similar remarks apply other primitives that accept a continuation as a reference." -IFNOTREF,,#E301 c:^Cell,cont_conditional,E301,[ref] IFNOTREF,f - ,26/126/51,Equivalent to `PUSHREFCONT` `IFNOT`. -IFJMPREF,,#E302 c:^Cell,cont_conditional,E302,[ref] IFJMPREF,f - ,26/126/51,Equivalent to `PUSHREFCONT` `IFJMP`. -IFNOTJMPREF,,#E303 c:^Cell,cont_conditional,E303,[ref] IFNOTJMPREF,f - ,26/126/51,Equivalent to `PUSHREFCONT` `IFNOTJMP`. -CONDSEL,,#E304,cont_conditional,E304,CONDSEL,f x y - x or y,26,"If integer `f` is non-zero, returns `x`, otherwise returns `y`. Notice that no type checks are performed on `x` and `y`; as such, it is more like a conditional stack operation. Roughly equivalent to `ROT` `ISZERO` `INC` `ROLLX` `NIP`." -CONDSELCHK,,#E305,cont_conditional,E305,CONDSELCHK,f x y - x or y,26,"Same as `CONDSEL`, but first checks whether `x` and `y` have the same type." -IFRETALT,,#E308,cont_conditional,E308,IFRETALT,f -,26,Performs `RETALT` if integer `f!=0`. -IFNOTRETALT,,#E309,cont_conditional,E309,IFNOTRETALT,f -,26,Performs `RETALT` if integer `f=0`. -IFREFELSE,,#E30D c:^Cell,cont_conditional,E30D,[ref] IFREFELSE,f c -,26/126/51,"Equivalent to `PUSHREFCONT` `SWAP` `IFELSE`, with the optimization that the cell reference is not actually loaded into a _Slice_ and then converted into an ordinary _Continuation_ if `f=0`. Similar remarks apply to the next two primitives: cells are converted into continuations only when necessary." -IFELSEREF,,#E30E c:^Cell,cont_conditional,E30E,[ref] IFELSEREF,f c -,26/126/51,Equivalent to `PUSHREFCONT` `IFELSE`. -IFREFELSEREF,,#E30F c1:^Cell c2:^Cell,cont_conditional,E30F,[ref] [ref] IFREFELSEREF,f -,126/51,Equivalent to `PUSHREFCONT` `PUSHREFCONT` `IFELSE`. -IFBITJMP,,#E39_ n:uint5,cont_conditional,E39_n,[n] IFBITJMP,x c - x,26,"Checks whether bit `0 <= n <= 31` is set in integer `x`, and if so, performs `JMPX` to continuation `c`. Value `x` is left in the stack." -IFNBITJMP,,#E3B_ n:uint5,cont_conditional,E3B_n,[n] IFNBITJMP,x c - x,26,Jumps to `c` if bit `0 <= n <= 31` is not set in integer `x`. -IFBITJMPREF,,#E3D_ n:uint5 c:^Cell,cont_conditional,E3D_n,[ref] [n] IFBITJMPREF,x - x,126/51,Performs a `JMPREF` if bit `0 <= n <= 31` is set in integer `x`. -IFNBITJMPREF,,#E3F_ n:uint5 c:^Cell,cont_conditional,E3F_n,[ref] [n] IFNBITJMPREF,x - x,126/51,Performs a `JMPREF` if bit `0 <= n <= 31` is not set in integer `x`. -REPEAT,,#E4,cont_loops,E4,REPEAT,n c - ,18,"Executes continuation `c` `n` times, if integer `n` is non-negative. If `n>=2^31` or `n<-2^31`, generates a range check exception. -Notice that a `RET` inside the code of `c` works as a `continue`, not as a `break`. One should use either alternative (experimental) loops or alternative `RETALT` (along with a `SETEXITALT` before the loop) to `break` out of a loop." -,,,cont_loops,E4,"REPEAT:<{ code }> -<{ code }>REPEAT",n -,,Equivalent to `<{ code }> CONT` `REPEAT`. -REPEATEND,,#E5,cont_loops,E5,"REPEATEND -REPEAT:",n - ,18,"Similar to `REPEAT`, but it is applied to the current continuation `cc`." -UNTIL,,#E6,cont_loops,E6,UNTIL,c - ,18,"Executes continuation `c`, then pops an integer `x` from the resulting stack. If `x` is zero, performs another iteration of this loop. The actual implementation of this primitive involves an extraordinary continuation `ec_until` with its arguments set to the body of the loop (continuation `c`) and the original current continuation `cc`. This extraordinary continuation is then saved into the savelist of `c` as `c.c0` and the modified `c` is then executed. The other loop primitives are implemented similarly with the aid of suitable extraordinary continuations." -,,,cont_loops,E6,"UNTIL:<{ code }> -<{ code }>UNTIL",-,,Equivalent to `<{ code }> CONT` `UNTIL`. -UNTILEND,,#E7,cont_loops,E7,"UNTILEND -UNTIL:",-,18,"Similar to `UNTIL`, but executes the current continuation `cc` in a loop. When the loop exit condition is satisfied, performs a `RET`." -WHILE,,#E8,cont_loops,E8,WHILE,c' c - ,18,"Executes `c'` and pops an integer `x` from the resulting stack. If `x` is zero, exists the loop and transfers control to the original `cc`. If `x` is non-zero, executes `c`, and then begins a new iteration." -,,,cont_loops,E8,WHILE:<{ cond }>DO<{ code }>,-,,Equivalent to `<{ cond }> CONT` `<{ code }> CONT` `WHILE`. -WHILEEND,,#E9,cont_loops,E9,WHILEEND,c' - ,18,"Similar to `WHILE`, but uses the current continuation `cc` as the loop body." -AGAIN,,#EA,cont_loops,EA,AGAIN,c - ,18,"Similar to `REPEAT`, but executes `c` infinitely many times. A `RET` only begins a new iteration of the infinite loop, which can be exited only by an exception, or a `RETALT` (or an explicit `JMPX`)." -,,,cont_loops,EA,"AGAIN:<{ code }> -<{ code }>AGAIN",-,,Equivalent to `<{ code }> CONT` `AGAIN`. -AGAINEND,,#EB,cont_loops,EB,"AGAINEND -AGAIN:",-,18,"Similar to `AGAIN`, but performed with respect to the current continuation `cc`." -REPEATBRK,,#E314,cont_loops,E314,REPEATBRK,n c -,26,"Similar to `REPEAT`, but also sets `c1` to the original `cc` after saving the old value of `c1` into the savelist of the original `cc`. In this way `RETALT` could be used to break out of the loop body." -,,,cont_loops,E314,"REPEATBRK:<{ code }> -<{ code }>REPEATBRK",n -,,Equivalent to `<{ code }> CONT` `REPEATBRK`. -REPEATENDBRK,,#E315,cont_loops,E315,REPEATENDBRK,n -,26,"Similar to `REPEATEND`, but also sets `c1` to the original `c0` after saving the old value of `c1` into the savelist of the original `c0`. Equivalent to `SAMEALTSAVE` `REPEATEND`." -UNTILBRK,,#E316,cont_loops,E316,UNTILBRK,c -,26,"Similar to `UNTIL`, but also modifies `c1` in the same way as `REPEATBRK`." -,,,cont_loops,E316,UNTILBRK:<{ code }>,-,,Equivalent to `<{ code }> CONT` `UNTILBRK`. -UNTILENDBRK,,#E317,cont_loops,E317,"UNTILENDBRK -UNTILBRK:",-,26,Equivalent to `SAMEALTSAVE` `UNTILEND`. -WHILEBRK,,#E318,cont_loops,E318,WHILEBRK,c' c -,26,"Similar to `WHILE`, but also modifies `c1` in the same way as `REPEATBRK`." -,,,cont_loops,E318,WHILEBRK:<{ cond }>DO<{ code }>,-,,Equivalent to `<{ cond }> CONT` `<{ code }> CONT` `WHILEBRK`. -WHILEENDBRK,,#E319,cont_loops,E319,WHILEENDBRK,c -,26,Equivalent to `SAMEALTSAVE` `WHILEEND`. -AGAINBRK,,#E31A,cont_loops,E31A,AGAINBRK,c -,26,"Similar to `AGAIN`, but also modifies `c1` in the same way as `REPEATBRK`." -,,,cont_loops,E31A,AGAINBRK:<{ code }>,-,,Equivalent to `<{ code }> CONT` `AGAINBRK`. -AGAINENDBRK,,#E31B,cont_loops,E31B,"AGAINENDBRK -AGAINBRK:",-,26,Equivalent to `SAMEALTSAVE` `AGAINEND`. -SETCONTARGS_N,,#EC r:uint4 n:(#<= 14),cont_stack,ECrn,[r] [n] SETCONTARGS,x_1 x_2...x_r c - c',26+s”,"Similar to `[r] -1 SETCONTARGS`, but sets `c.nargs` to the final size of the stack of `c'` plus `n`. In other words, transforms `c` into a _closure_ or a _partially applied function_, with `0 <= n <= 14` arguments missing." -SETNUMARGS,SETCONTARGS_N,#EC0 n:(#<= 14),cont_stack,EC0n,[n] SETNUMARGS,c - c',26,"Sets `c.nargs` to `n` plus the current depth of `c`'s stack, where `0 <= n <= 14`. If `c.nargs` is already set to a non-negative value, does nothing." -SETCONTARGS,,#EC r:uint4 n:(## 4) {n = 15},cont_stack,ECrF,[r] -1 SETCONTARGS,x_1 x_2...x_r c - c',26+s”,"Pushes `0 <= r <= 15` values `x_1...x_r` into the stack of (a copy of) the continuation `c`, starting with `x_1`. If the final depth of `c`'s stack turns out to be greater than `c.nargs`, a stack overflow exception is generated." -RETURNARGS,,#ED0 p:uint4,cont_stack,ED0p,[p] RETURNARGS,-,26+s”,"Leaves only the top `0 <= p <= 15` values in the current stack (somewhat similarly to `ONLYTOPX`), with all the unused bottom values not discarded, but saved into continuation `c0` in the same way as `SETCONTARGS` does." -RETURNVARARGS,,#ED10,cont_stack,ED10,RETURNVARARGS,p -,26+s”,"Similar to `RETURNARGS`, but with Integer `0 <= p <= 255` taken from the stack." -SETCONTVARARGS,,#ED11,cont_stack,ED11,SETCONTVARARGS,x_1 x_2...x_r c r n - c',26+s”,"Similar to `SETCONTARGS`, but with `0 <= r <= 255` and `-1 <= n <= 255` taken from the stack." -SETNUMVARARGS,,#ED12,cont_stack,ED12,SETNUMVARARGS,c n - c',26,"`-1 <= n <= 255` -If `n=-1`, this operation does nothing (`c'=c`). -Otherwise its action is similar to `[n] SETNUMARGS`, but with `n` taken from the stack." -BLESS,,#ED1E,cont_create,ED1E,BLESS,s - c,26,"Transforms a _Slice_ `s` into a simple ordinary continuation `c`, with `c.code=s` and an empty stack and savelist." -BLESSVARARGS,,#ED1F,cont_create,ED1F,BLESSVARARGS,x_1...x_r s r n - c,26+s”,Equivalent to `ROT` `BLESS` `ROTREV` `SETCONTVARARGS`. -BLESSARGS,,#EE r:uint4 n:uint4,cont_create,EErn,[r] [n] BLESSARGS,x_1...x_r s - c,26,"`0 <= r <= 15`, `-1 <= n <= 14` -Equivalent to `BLESS` `[r] [n] SETCONTARGS`. -The value of `n` is represented inside the instruction by the 4-bit integer `n mod 16`." -BLESSNUMARGS,BLESSARGS,#EE0 n:uint4,cont_create,EE0n,[n] BLESSNUMARGS,s - c,26,"Also transforms a _Slice_ `s` into a _Continuation_ `c`, but sets `c.nargs` to `0 <= n <= 14`." -PUSHCTR,,#ED4 i:uint4,cont_registers,ED4i,"c[i] PUSHCTR -c[i] PUSH",- x,26,"Pushes the current value of control register `c(i)`. If the control register is not supported in the current codepage, or if it does not have a value, an exception is triggered." -PUSHROOT,PUSHCTR,#ED44,cont_registers,ED44,"c4 PUSHCTR -c4 PUSH",- x,26,"Pushes the “global data root'' cell reference, thus enabling access to persistent smart-contract data." -POPCTR,,#ED5 i:uint4,cont_registers,ED5i,"c[i] POPCTR -c[i] POP",x - ,26,"Pops a value `x` from the stack and stores it into control register `c(i)`, if supported in the current codepage. Notice that if a control register accepts only values of a specific type, a type-checking exception may occur." -POPROOT,POPCTR,#ED54,cont_registers,ED54,"c4 POPCTR -c4 POP",x -,26,"Sets the “global data root'' cell reference, thus allowing modification of persistent smart-contract data." -SETCONTCTR,,#ED6 i:uint4,cont_registers,ED6i,"c[i] SETCONT -c[i] SETCONTCTR",x c - c',26,"Stores `x` into the savelist of continuation `c` as `c(i)`, and returns the resulting continuation `c'`. Almost all operations with continuations may be expressed in terms of `SETCONTCTR`, `POPCTR`, and `PUSHCTR`." -SETRETCTR,,#ED7 i:uint4,cont_registers,ED7i,c[i] SETRETCTR,x - ,26,Equivalent to `c0 PUSHCTR` `c[i] SETCONTCTR` `c0 POPCTR`. -SETALTCTR,,#ED8 i:uint4,cont_registers,ED8i,c[i] SETALTCTR,x - ,26,Equivalent to `c1 PUSHCTR` `c[i] SETCONTCTR` `c1 POPCTR`. -POPSAVE,,#ED9 i:uint4,cont_registers,ED9i,"c[i] POPSAVE -c[i] POPCTRSAVE",x -,26,"Similar to `c[i] POPCTR`, but also saves the old value of `c[i]` into continuation `c0`. -Equivalent (up to exceptions) to `c[i] SAVECTR` `c[i] POPCTR`." -SAVE,,#EDA i:uint4,cont_registers,EDAi,"c[i] SAVE -c[i] SAVECTR",,26,"Saves the current value of `c(i)` into the savelist of continuation `c0`. If an entry for `c[i]` is already present in the savelist of `c0`, nothing is done. Equivalent to `c[i] PUSHCTR` `c[i] SETRETCTR`." -SAVEALT,,#EDB i:uint4,cont_registers,EDBi,"c[i] SAVEALT -c[i] SAVEALTCTR",,26,"Similar to `c[i] SAVE`, but saves the current value of `c[i]` into the savelist of `c1`, not `c0`." -SAVEBOTH,,#EDC i:uint4,cont_registers,EDCi,"c[i] SAVEBOTH -c[i] SAVEBOTHCTR",,26,Equivalent to `c[i] SAVE` `c[i] SAVEALT`. -PUSHCTRX,,#EDE0,cont_registers,EDE0,PUSHCTRX,i - x,26,"Similar to `c[i] PUSHCTR`, but with `i`, `0 <= i <= 255`, taken from the stack. -Notice that this primitive is one of the few “exotic'' primitives, which are not polymorphic like stack manipulation primitives, and at the same time do not have well-defined types of parameters and return values, because the type of `x` depends on `i`." -POPCTRX,,#EDE1,cont_registers,EDE1,POPCTRX,x i - ,26,"Similar to `c[i] POPCTR`, but with `0 <= i <= 255` from the stack." -SETCONTCTRX,,#EDE2,cont_registers,EDE2,SETCONTCTRX,x c i - c',26,"Similar to `c[i] SETCONTCTR`, but with `0 <= i <= 255` from the stack." -COMPOS,,#EDF0,cont_registers,EDF0,"COMPOS -BOOLAND",c c' - c'',26,"Computes the composition `compose0(c, c’)`, which has the meaning of “perform `c`, and, if successful, perform `c'`'' (if `c` is a boolean circuit) or simply “perform `c`, then `c'`''. Equivalent to `SWAP` `c0 SETCONT`." -COMPOSALT,,#EDF1,cont_registers,EDF1,"COMPOSALT -BOOLOR",c c' - c'',26,"Computes the alternative composition `compose1(c, c’)`, which has the meaning of “perform `c`, and, if not successful, perform `c'`'' (if `c` is a boolean circuit). Equivalent to `SWAP` `c1 SETCONT`." -COMPOSBOTH,,#EDF2,cont_registers,EDF2,COMPOSBOTH,c c' - c'',26,"Computes composition `compose1(compose0(c, c’), c’)`, which has the meaning of “compute boolean circuit `c`, then compute `c'`, regardless of the result of `c`''." -ATEXIT,,#EDF3,cont_registers,EDF3,ATEXIT,c - ,26,"Sets `c0` to `compose0(c, c0)`. In other words, `c` will be executed before exiting current subroutine." -,,,cont_registers,EDF3,"ATEXIT:<{ code }> -<{ code }>ATEXIT",-,,Equivalent to `<{ code }> CONT` `ATEXIT`. -ATEXITALT,,#EDF4,cont_registers,EDF4,ATEXITALT,c - ,26,"Sets `c1` to `compose1(c, c1)`. In other words, `c` will be executed before exiting current subroutine by its alternative return path." -,,,cont_registers,EDF4,"ATEXITALT:<{ code }> -<{ code }>ATEXITALT",-,,Equivalent to `<{ code }> CONT` `ATEXITALT`. -SETEXITALT,,#EDF5,cont_registers,EDF5,SETEXITALT,c - ,26,"Sets `c1` to `compose1(compose0(c, c0), c1)`, -In this way, a subsequent `RETALT` will first execute `c`, then transfer control to the original `c0`. This can be used, for instance, to exit from nested loops." -THENRET,,#EDF6,cont_registers,EDF6,THENRET,c - c',26,"Computes `compose0(c, c0)`." -THENRETALT,,#EDF7,cont_registers,EDF7,THENRETALT,c - c',26,"Computes `compose0(c, c1)`" -INVERT,,#EDF8,cont_registers,EDF8,INVERT,-,26,Interchanges `c0` and `c1`. -BOOLEVAL,,#EDF9,cont_registers,EDF9,BOOLEVAL,c - ?,26,"Performs `cc:=compose1(compose0(c, compose0(-1 PUSHINT, cc)), compose0(0 PUSHINT, cc))`. If `c` represents a boolean circuit, the net effect is to evaluate it and push either `-1` or `0` into the stack before continuing." -SAMEALT,,#EDFA,cont_registers,EDFA,SAMEALT,-,26,Sets `c1` to `c0`. Equivalent to `c0 PUSHCTR` `c1 POPCTR`. -SAMEALTSAVE,,#EDFB,cont_registers,EDFB,SAMEALTSAVE,-,26,"Sets `c1` to `c0`, but first saves the old value of `c1` into the savelist of `c0`. -Equivalent to `c1 SAVE` `SAMEALT`." -CALLDICT,,#F0 n:uint8,cont_dict,F0nn,"[nn] CALL -[nn] CALLDICT",- nn,,"Calls the continuation in `c3`, pushing integer `0 <= nn <= 255` into its stack as an argument. -Approximately equivalent to `[nn] PUSHINT` `c3 PUSHCTR` `EXECUTE`." -CALLDICT_LONG,,#F12_ n:uint14,cont_dict,F12_n,"[n] CALL -[n] CALLDICT",- n,,"For `0 <= n < 2^14`, an encoding of `[n] CALL` for larger values of `n`." -JMPDICT,,#F16_ n:uint14,cont_dict,F16_n,[n] JMP, - n,,"Jumps to the continuation in `c3`, pushing integer `0 <= n < 2^14` as its argument. -Approximately equivalent to `n PUSHINT` `c3 PUSHCTR` `JMPX`." -PREPAREDICT,,#F1A_ n:uint14,cont_dict,F1A_n,"[n] PREPARE -[n] PREPAREDICT", - n c,,"Equivalent to `n PUSHINT` `c3 PUSHCTR`, for `0 <= n < 2^14`. -In this way, `[n] CALL` is approximately equivalent to `[n] PREPARE` `EXECUTE`, and `[n] JMP` is approximately equivalent to `[n] PREPARE` `JMPX`. -One might use, for instance, `CALLXARGS` or `CALLCC` instead of `EXECUTE` here." -THROW_SHORT,,#F22_ n:uint6,exceptions,F22_n,[n] THROW, - 0 n,76,"Throws exception `0 <= n <= 63` with parameter zero. -In other words, it transfers control to the continuation in `c2`, pushing `0` and `n` into its stack, and discarding the old stack altogether." -THROWIF_SHORT,,#F26_ n:uint6,exceptions,F26_n,[n] THROWIF,f - ,26/76,Throws exception `0 <= n <= 63` with parameter zero only if integer `f!=0`. -THROWIFNOT_SHORT,,#F2A_ n:uint6,exceptions,F2A_n,[n] THROWIFNOT,f - ,26/76,Throws exception `0 <= n <= 63` with parameter zero only if integer `f=0`. -THROW,,#F2C4_ n:uint11,exceptions,F2C4_n,[n] THROW,- 0 nn,84,"For `0 <= n < 2^11`, an encoding of `[n] THROW` for larger values of `n`." -THROWARG,,#F2CC_ n:uint11,exceptions,F2CC_n,[n] THROWARG,x - x nn,84,"Throws exception `0 <= n < 2^11` with parameter `x`, by copying `x` and `n` into the stack of `c2` and transferring control to `c2`." -THROWIF,,#F2D4_ n:uint11,exceptions,F2D4_n,[n] THROWIF,f - ,34/84,"For `0 <= n < 2^11`, an encoding of `[n] THROWIF` for larger values of `n`." -THROWARGIF,,#F2DC_ n:uint11,exceptions,F2DC_n,[n] THROWARGIF,x f - ,34/84,Throws exception `0 <= nn < 2^11` with parameter `x` only if integer `f!=0`. -THROWIFNOT,,#F2E4_ n:uint11,exceptions,F2E4_n,[n] THROWIFNOT,f - ,34/84,"For `0 <= n < 2^11`, an encoding of `[n] THROWIFNOT` for larger values of `n`." -THROWARGIFNOT,,#F2EC_ n:uint11,exceptions,F2EC_n,[n] THROWARGIFNOT,x f - ,34/84,Throws exception `0 <= n < 2^11` with parameter `x` only if integer `f=0`. -THROWANY,,#F2F0,exceptions,F2F0,THROWANY,n - 0 n,76,"Throws exception `0 <= n < 2^16` with parameter zero. -Approximately equivalent to `ZERO` `SWAP` `THROWARGANY`." -THROWARGANY,,#F2F1,exceptions,F2F1,THROWARGANY,x n - x n,76,"Throws exception `0 <= n < 2^16` with parameter `x`, transferring control to the continuation in `c2`. -Approximately equivalent to `c2 PUSHCTR` `2 JMPXARGS`." -THROWANYIF,,#F2F2,exceptions,F2F2,THROWANYIF,n f - ,26/76,Throws exception `0 <= n < 2^16` with parameter zero only if `f!=0`. -THROWARGANYIF,,#F2F3,exceptions,F2F3,THROWARGANYIF,x n f - ,26/76,Throws exception `0 <= n<2^16` with parameter `x` only if `f!=0`. -THROWANYIFNOT,,#F2F4,exceptions,F2F4,THROWANYIFNOT,n f - ,26/76,Throws exception `0 <= n<2^16` with parameter zero only if `f=0`. -THROWARGANYIFNOT,,#F2F5,exceptions,F2F5,THROWARGANYIFNOT,x n f - ,26/76,Throws exception `0 <= n<2^16` with parameter `x` only if `f=0`. -TRY,,#F2FF,exceptions,F2FF,TRY,c c' - ,26,"Sets `c2` to `c'`, first saving the old value of `c2` both into the savelist of `c'` and into the savelist of the current continuation, which is stored into `c.c0` and `c'.c0`. Then runs `c` similarly to `EXECUTE`. If `c` does not throw any exceptions, the original value of `c2` is automatically restored on return from `c`. If an exception occurs, the execution is transferred to `c'`, but the original value of `c2` is restored in the process, so that `c'` can re-throw the exception by `THROWANY` if it cannot handle it by itself." -,,,exceptions,F2FF,TRY:<{ code1 }>CATCH<{ code2 }>,-,,Equivalent to `<{ code1 }> CONT` `<{ code2 }> CONT` `TRY`. -TRYARGS,,#F3 p:uint4 r:uint4,exceptions,F3pr,[p] [r] TRYARGS,c c' - ,26,"Similar to `TRY`, but with `[p] [r] CALLXARGS` internally used instead of `EXECUTE`. -In this way, all but the top `0 <= p <= 15` stack elements will be saved into current continuation's stack, and then restored upon return from either `c` or `c'`, with the top `0 <= r <= 15` values of the resulting stack of `c` or `c'` copied as return values." -NEWDICT,NULL,#6D,dict_create,6D,NEWDICT, - D,18,"Returns a new empty dictionary. -It is an alternative mnemonics for `PUSHNULL`." -DICTEMPTY,ISNULL,#6E,dict_create,6E,DICTEMPTY,D - ?,18,"Checks whether dictionary `D` is empty, and returns `-1` or `0` accordingly. -It is an alternative mnemonics for `ISNULL`." -STDICTS,STSLICE,#CE,dict_serial,CE,"STDICTS -",s b - b',18,"Stores a _Slice_-represented dictionary `s` into _Builder_ `b`. -It is actually a synonym for `STSLICE`." -STDICT,,#F400,dict_serial,F400,"STDICT -STOPTREF",D b - b',26,"Stores dictionary `D` into _Builder_ `b`, returing the resulting _Builder_ `b'`. -In other words, if `D` is a cell, performs `STONE` and `STREF`; if `D` is _Null_, performs `NIP` and `STZERO`; otherwise throws a type checking exception." -SKIPDICT,,#F401,dict_serial,F401,"SKIPDICT -SKIPOPTREF",s - s',26,Equivalent to `LDDICT` `NIP`. -LDDICTS,,#F402,dict_serial,F402,LDDICTS,s - s' s'',26,"Loads (parses) a (_Slice_-represented) dictionary `s'` from _Slice_ `s`, and returns the remainder of `s` as `s''`. -This is a “split function'' for all `HashmapE(n,X)` dictionary types." -PLDDICTS,,#F403,dict_serial,F403,PLDDICTS,s - s',26,"Preloads a (_Slice_-represented) dictionary `s'` from _Slice_ `s`. -Approximately equivalent to `LDDICTS` `DROP`." -LDDICT,,#F404,dict_serial,F404,"LDDICT -LDOPTREF",s - D s',26,"Loads (parses) a dictionary `D` from _Slice_ `s`, and returns the remainder of `s` as `s'`. May be applied to dictionaries or to values of arbitrary `(^Y)?` types." -PLDDICT,,#F405,dict_serial,F405,"PLDDICT -PLDOPTREF",s - D,26,"Preloads a dictionary `D` from _Slice_ `s`. -Approximately equivalent to `LDDICT` `DROP`." -LDDICTQ,,#F406,dict_serial,F406,LDDICTQ,s - D s' -1 or s 0,26,A quiet version of `LDDICT`. -PLDDICTQ,,#F407,dict_serial,F407,PLDDICTQ,s - D -1 or 0,26,A quiet version of `PLDDICT`. -DICTGET,,#F40A,dict_get,F40A,DICTGET,k D n - x -1 or 0,,"Looks up key `k` (represented by a _Slice_, the first `0 <= n <= 1023` data bits of which are used as a key) in dictionary `D` of type `HashmapE(n,X)` with `n`-bit keys. -On success, returns the value found as a _Slice_ `x`." -DICTGETREF,,#F40B,dict_get,F40B,DICTGETREF,k D n - c -1 or 0,,"Similar to `DICTGET`, but with a `LDREF` `ENDS` applied to `x` on success. -This operation is useful for dictionaries of type `HashmapE(n,^Y)`." -DICTIGET,,#F40C,dict_get,F40C,DICTIGET,i D n - x -1 or 0,,"Similar to `DICTGET`, but with a signed (big-endian) `n`-bit _Integer_ `i` as a key. If `i` does not fit into `n` bits, returns `0`. If `i` is a `NaN`, throws an integer overflow exception." -DICTIGETREF,,#F40D,dict_get,F40D,DICTIGETREF,i D n - c -1 or 0,,Combines `DICTIGET` with `DICTGETREF`: it uses signed `n`-bit _Integer_ `i` as a key and returns a _Cell_ instead of a _Slice_ on success. -DICTUGET,,#F40E,dict_get,F40E,DICTUGET,i D n - x -1 or 0,,"Similar to `DICTIGET`, but with _unsigned_ (big-endian) `n`-bit _Integer_ `i` used as a key." -DICTUGETREF,,#F40F,dict_get,F40F,DICTUGETREF,i D n - c -1 or 0,,"Similar to `DICTIGETREF`, but with an unsigned `n`-bit _Integer_ key `i`." -DICTSET,,#F412,dict_set,F412,DICTSET,x k D n - D',,"Sets the value associated with `n`-bit key `k` (represented by a _Slice_ as in `DICTGET`) in dictionary `D` (also represented by a _Slice_) to value `x` (again a _Slice_), and returns the resulting dictionary as `D'`." -DICTSETREF,,#F413,dict_set,F413,DICTSETREF,c k D n - D',,"Similar to `DICTSET`, but with the value set to a reference to _Cell_ `c`." -DICTISET,,#F414,dict_set,F414,DICTISET,x i D n - D',,"Similar to `DICTSET`, but with the key represented by a (big-endian) signed `n`-bit integer `i`. If `i` does not fit into `n` bits, a range check exception is generated." -DICTISETREF,,#F415,dict_set,F415,DICTISETREF,c i D n - D',,"Similar to `DICTSETREF`, but with the key a signed `n`-bit integer as in `DICTISET`." -DICTUSET,,#F416,dict_set,F416,DICTUSET,x i D n - D',,"Similar to `DICTISET`, but with `i` an _unsigned_ `n`-bit integer." -DICTUSETREF,,#F417,dict_set,F417,DICTUSETREF,c i D n - D',,"Similar to `DICTISETREF`, but with `i` unsigned." -DICTSETGET,,#F41A,dict_set,F41A,DICTSETGET,x k D n - D' y -1 or D' 0,,"Combines `DICTSET` with `DICTGET`: it sets the value corresponding to key `k` to `x`, but also returns the old value `y` associated with the key in question, if present." -DICTSETGETREF,,#F41B,dict_set,F41B,DICTSETGETREF,c k D n - D' c' -1 or D' 0,,Combines `DICTSETREF` with `DICTGETREF` similarly to `DICTSETGET`. -DICTISETGET,,#F41C,dict_set,F41C,DICTISETGET,x i D n - D' y -1 or D' 0,,"`DICTISETGET`, but with `i` a signed `n`-bit integer." -DICTISETGETREF,,#F41D,dict_set,F41D,DICTISETGETREF,c i D n - D' c' -1 or D' 0,,"`DICTISETGETREF`, but with `i` a signed `n`-bit integer." -DICTUSETGET,,#F41E,dict_set,F41E,DICTUSETGET,x i D n - D' y -1 or D' 0,,"`DICTISETGET`, but with `i` an unsigned `n`-bit integer." -DICTUSETGETREF,,#F41F,dict_set,F41F,DICTUSETGETREF,c i D n - D' c' -1 or D' 0,,"`DICTISETGETREF`, but with `i` an unsigned `n`-bit integer." -DICTREPLACE,,#F422,dict_set,F422,DICTREPLACE,x k D n - D' -1 or D 0,,"A _Replace_ operation, which is similar to `DICTSET`, but sets the value of key `k` in dictionary `D` to `x` only if the key `k` was already present in `D`." -DICTREPLACEREF,,#F423,dict_set,F423,DICTREPLACEREF,c k D n - D' -1 or D 0,,A _Replace_ counterpart of `DICTSETREF`. -DICTIREPLACE,,#F424,dict_set,F424,DICTIREPLACE,x i D n - D' -1 or D 0,,"`DICTREPLACE`, but with `i` a signed `n`-bit integer." -DICTIREPLACEREF,,#F425,dict_set,F425,DICTIREPLACEREF,c i D n - D' -1 or D 0,,"`DICTREPLACEREF`, but with `i` a signed `n`-bit integer." -DICTUREPLACE,,#F426,dict_set,F426,DICTUREPLACE,x i D n - D' -1 or D 0,,"`DICTREPLACE`, but with `i` an unsigned `n`-bit integer." -DICTUREPLACEREF,,#F427,dict_set,F427,DICTUREPLACEREF,c i D n - D' -1 or D 0,,"`DICTREPLACEREF`, but with `i` an unsigned `n`-bit integer." -DICTREPLACEGET,,#F42A,dict_set,F42A,DICTREPLACEGET,x k D n - D' y -1 or D 0,,"A _Replace_ counterpart of `DICTSETGET`: on success, also returns the old value associated with the key in question." -DICTREPLACEGETREF,,#F42B,dict_set,F42B,DICTREPLACEGETREF,c k D n - D' c' -1 or D 0,,A _Replace_ counterpart of `DICTSETGETREF`. -DICTIREPLACEGET,,#F42C,dict_set,F42C,DICTIREPLACEGET,x i D n - D' y -1 or D 0,,"`DICTREPLACEGET`, but with `i` a signed `n`-bit integer." -DICTIREPLACEGETREF,,#F42D,dict_set,F42D,DICTIREPLACEGETREF,c i D n - D' c' -1 or D 0,,"`DICTREPLACEGETREF`, but with `i` a signed `n`-bit integer." -DICTUREPLACEGET,,#F42E,dict_set,F42E,DICTUREPLACEGET,x i D n - D' y -1 or D 0,,"`DICTREPLACEGET`, but with `i` an unsigned `n`-bit integer." -DICTUREPLACEGETREF,,#F42F,dict_set,F42F,DICTUREPLACEGETREF,c i D n - D' c' -1 or D 0,,"`DICTREPLACEGETREF`, but with `i` an unsigned `n`-bit integer." -DICTADD,,#F432,dict_set,F432,DICTADD,x k D n - D' -1 or D 0,,"An _Add_ counterpart of `DICTSET`: sets the value associated with key `k` in dictionary `D` to `x`, but only if it is not already present in `D`." -DICTADDREF,,#F433,dict_set,F433,DICTADDREF,c k D n - D' -1 or D 0,,An _Add_ counterpart of `DICTSETREF`. -DICTIADD,,#F434,dict_set,F434,DICTIADD,x i D n - D' -1 or D 0,,"`DICTADD`, but with `i` a signed `n`-bit integer." -DICTIADDREF,,#F435,dict_set,F435,DICTIADDREF,c i D n - D' -1 or D 0,,"`DICTADDREF`, but with `i` a signed `n`-bit integer." -DICTUADD,,#F436,dict_set,F436,DICTUADD,x i D n - D' -1 or D 0,,"`DICTADD`, but with `i` an unsigned `n`-bit integer." -DICTUADDREF,,#F437,dict_set,F437,DICTUADDREF,c i D n - D' -1 or D 0,,"`DICTADDREF`, but with `i` an unsigned `n`-bit integer." -DICTADDGET,,#F43A,dict_set,F43A,DICTADDGET,x k D n - D' -1 or D y 0,,"An _Add_ counterpart of `DICTSETGET`: sets the value associated with key `k` in dictionary `D` to `x`, but only if key `k` is not already present in `D`. Otherwise, just returns the old value `y` without changing the dictionary." -DICTADDGETREF,,#F43B,dict_set,F43B,DICTADDGETREF,c k D n - D' -1 or D c' 0,,An _Add_ counterpart of `DICTSETGETREF`. -DICTIADDGET,,#F43C,dict_set,F43C,DICTIADDGET,x i D n - D' -1 or D y 0,,"`DICTADDGET`, but with `i` a signed `n`-bit integer." -DICTIADDGETREF,,#F43D,dict_set,F43D,DICTIADDGETREF,c i D n - D' -1 or D c' 0,,"`DICTADDGETREF`, but with `i` a signed `n`-bit integer." -DICTUADDGET,,#F43E,dict_set,F43E,DICTUADDGET,x i D n - D' -1 or D y 0,,"`DICTADDGET`, but with `i` an unsigned `n`-bit integer." -DICTUADDGETREF,,#F43F,dict_set,F43F,DICTUADDGETREF,c i D n - D' -1 or D c' 0,,"`DICTADDGETREF`, but with `i` an unsigned `n`-bit integer." -DICTSETB,,#F441,dict_set_builder,F441,DICTSETB,b k D n - D',, -DICTISETB,,#F442,dict_set_builder,F442,DICTISETB,b i D n - D',, -DICTUSETB,,#F443,dict_set_builder,F443,DICTUSETB,b i D n - D',, -DICTSETGETB,,#F445,dict_set_builder,F445,DICTSETGETB,b k D n - D' y -1 or D' 0,, -DICTISETGETB,,#F446,dict_set_builder,F446,DICTISETGETB,b i D n - D' y -1 or D' 0,, -DICTUSETGETB,,#F447,dict_set_builder,F447,DICTUSETGETB,b i D n - D' y -1 or D' 0,, -DICTREPLACEB,,#F449,dict_set_builder,F449,DICTREPLACEB,b k D n - D' -1 or D 0,, -DICTIREPLACEB,,#F44A,dict_set_builder,F44A,DICTIREPLACEB,b i D n - D' -1 or D 0,, -DICTUREPLACEB,,#F44B,dict_set_builder,F44B,DICTUREPLACEB,b i D n - D' -1 or D 0,, -DICTREPLACEGETB,,#F44D,dict_set_builder,F44D,DICTREPLACEGETB,b k D n - D' y -1 or D 0,, -DICTIREPLACEGETB,,#F44E,dict_set_builder,F44E,DICTIREPLACEGETB,b i D n - D' y -1 or D 0,, -DICTUREPLACEGETB,,#F44F,dict_set_builder,F44F,DICTUREPLACEGETB,b i D n - D' y -1 or D 0,, -DICTADDB,,#F451,dict_set_builder,F451,DICTADDB,b k D n - D' -1 or D 0,, -DICTIADDB,,#F452,dict_set_builder,F452,DICTIADDB,b i D n - D' -1 or D 0,, -DICTUADDB,,#F453,dict_set_builder,F453,DICTUADDB,b i D n - D' -1 or D 0,, -DICTADDGETB,,#F455,dict_set_builder,F455,DICTADDGETB,b k D n - D' -1 or D y 0,, -DICTIADDGETB,,#F456,dict_set_builder,F456,DICTIADDGETB,b i D n - D' -1 or D y 0,, -DICTUADDGETB,,#F457,dict_set_builder,F457,DICTUADDGETB,b i D n - D' -1 or D y 0,, -DICTDEL,,#F459,dict_delete,F459,DICTDEL,k D n - D' -1 or D 0,,"Deletes `n`-bit key, represented by a _Slice_ `k`, from dictionary `D`. If the key is present, returns the modified dictionary `D'` and the success flag `-1`. Otherwise, returns the original dictionary `D` and `0`." -DICTIDEL,,#F45A,dict_delete,F45A,DICTIDEL,i D n - D' ?,,"A version of `DICTDEL` with the key represented by a signed `n`-bit _Integer_ `i`. If `i` does not fit into `n` bits, simply returns `D` `0` (“key not found, dictionary unmodified'')." -DICTUDEL,,#F45B,dict_delete,F45B,DICTUDEL,i D n - D' ?,,"Similar to `DICTIDEL`, but with `i` an unsigned `n`-bit integer." -DICTDELGET,,#F462,dict_delete,F462,DICTDELGET,k D n - D' x -1 or D 0,,"Deletes `n`-bit key, represented by a _Slice_ `k`, from dictionary `D`. If the key is present, returns the modified dictionary `D'`, the original value `x` associated with the key `k` (represented by a _Slice_), and the success flag `-1`. Otherwise, returns the original dictionary `D` and `0`." -DICTDELGETREF,,#F463,dict_delete,F463,DICTDELGETREF,k D n - D' c -1 or D 0,,"Similar to `DICTDELGET`, but with `LDREF` `ENDS` applied to `x` on success, so that the value returned `c` is a _Cell_." -DICTIDELGET,,#F464,dict_delete,F464,DICTIDELGET,i D n - D' x -1 or D 0,,"`DICTDELGET`, but with `i` a signed `n`-bit integer." -DICTIDELGETREF,,#F465,dict_delete,F465,DICTIDELGETREF,i D n - D' c -1 or D 0,,"`DICTDELGETREF`, but with `i` a signed `n`-bit integer." -DICTUDELGET,,#F466,dict_delete,F466,DICTUDELGET,i D n - D' x -1 or D 0,,"`DICTDELGET`, but with `i` an unsigned `n`-bit integer." -DICTUDELGETREF,,#F467,dict_delete,F467,DICTUDELGETREF,i D n - D' c -1 or D 0,,"`DICTDELGETREF`, but with `i` an unsigned `n`-bit integer." -DICTGETOPTREF,,#F469,dict_mayberef,F469,DICTGETOPTREF,k D n - c^?,,A variant of `DICTGETREF` that returns _Null_ instead of the value `c^?` if the key `k` is absent from dictionary `D`. -DICTIGETOPTREF,,#F46A,dict_mayberef,F46A,DICTIGETOPTREF,i D n - c^?,,"`DICTGETOPTREF`, but with `i` a signed `n`-bit integer. If the key `i` is out of range, also returns _Null_." -DICTUGETOPTREF,,#F46B,dict_mayberef,F46B,DICTUGETOPTREF,i D n - c^?,,"`DICTGETOPTREF`, but with `i` an unsigned `n`-bit integer. If the key `i` is out of range, also returns _Null_." -DICTSETGETOPTREF,,#F46D,dict_mayberef,F46D,DICTSETGETOPTREF,c^? k D n - D' ~c^?,,"A variant of both `DICTGETOPTREF` and `DICTSETGETREF` that sets the value corresponding to key `k` in dictionary `D` to `c^?` (if `c^?` is _Null_, then the key is deleted instead), and returns the old value `~c^?` (if the key `k` was absent before, returns _Null_ instead)." -DICTISETGETOPTREF,,#F46E,dict_mayberef,F46E,DICTISETGETOPTREF,c^? i D n - D' ~c^?,,"Similar to primitive `DICTSETGETOPTREF`, but using signed `n`-bit _Integer_ `i` as a key. If `i` does not fit into `n` bits, throws a range checking exception." -DICTUSETGETOPTREF,,#F46F,dict_mayberef,F46F,DICTUSETGETOPTREF,c^? i D n - D' ~c^?,,"Similar to primitive `DICTSETGETOPTREF`, but using unsigned `n`-bit _Integer_ `i` as a key." -PFXDICTSET,,#F470,dict_prefix,F470,PFXDICTSET,x k D n - D' -1 or D 0,, -PFXDICTREPLACE,,#F471,dict_prefix,F471,PFXDICTREPLACE,x k D n - D' -1 or D 0,, -PFXDICTADD,,#F472,dict_prefix,F472,PFXDICTADD,x k D n - D' -1 or D 0,, -PFXDICTDEL,,#F473,dict_prefix,F473,PFXDICTDEL,k D n - D' -1 or D 0,, -DICTGETNEXT,,#F474,dict_next,F474,DICTGETNEXT,k D n - x' k' -1 or 0,,"Computes the minimal key `k'` in dictionary `D` that is lexicographically greater than `k`, and returns `k'` (represented by a _Slice_) along with associated value `x'` (also represented by a _Slice_)." -DICTGETNEXTEQ,,#F475,dict_next,F475,DICTGETNEXTEQ,k D n - x' k' -1 or 0,,"Similar to `DICTGETNEXT`, but computes the minimal key `k'` that is lexicographically greater than or equal to `k`." -DICTGETPREV,,#F476,dict_next,F476,DICTGETPREV,k D n - x' k' -1 or 0,,"Similar to `DICTGETNEXT`, but computes the maximal key `k'` lexicographically smaller than `k`." -DICTGETPREVEQ,,#F477,dict_next,F477,DICTGETPREVEQ,k D n - x' k' -1 or 0,,"Similar to `DICTGETPREV`, but computes the maximal key `k'` lexicographically smaller than or equal to `k`." -DICTIGETNEXT,,#F478,dict_next,F478,DICTIGETNEXT,i D n - x' i' -1 or 0,,"Similar to `DICTGETNEXT`, but interprets all keys in dictionary `D` as big-endian signed `n`-bit integers, and computes the minimal key `i'` that is larger than _Integer_ `i` (which does not necessarily fit into `n` bits)." -DICTIGETNEXTEQ,,#F479,dict_next,F479,DICTIGETNEXTEQ,i D n - x' i' -1 or 0,,"Similar to `DICTGETNEXTEQ`, but interprets keys as signed `n`-bit integers." -DICTIGETPREV,,#F47A,dict_next,F47A,DICTIGETPREV,i D n - x' i' -1 or 0,,"Similar to `DICTGETPREV`, but interprets keys as signed `n`-bit integers." -DICTIGETPREVEQ,,#F47B,dict_next,F47B,DICTIGETPREVEQ,i D n - x' i' -1 or 0,,"Similar to `DICTGETPREVEQ`, but interprets keys as signed `n`-bit integers." -DICTUGETNEXT,,#F47C,dict_next,F47C,DICTUGETNEXT,i D n - x' i' -1 or 0,,"Similar to `DICTGETNEXT`, but interprets all keys in dictionary `D` as big-endian unsigned `n`-bit integers, and computes the minimal key `i'` that is larger than _Integer_ `i` (which does not necessarily fit into `n` bits, and is not necessarily non-negative)." -DICTUGETNEXTEQ,,#F47D,dict_next,F47D,DICTUGETNEXTEQ,i D n - x' i' -1 or 0,,"Similar to `DICTGETNEXTEQ`, but interprets keys as unsigned `n`-bit integers." -DICTUGETPREV,,#F47E,dict_next,F47E,DICTUGETPREV,i D n - x' i' -1 or 0,,"Similar to `DICTGETPREV`, but interprets keys as unsigned `n`-bit integers." -DICTUGETPREVEQ,,#F47F,dict_next,F47F,DICTUGETPREVEQ,i D n - x' i' -1 or 0,,"Similar to `DICTGETPREVEQ`, but interprets keys a unsigned `n`-bit integers." -DICTMIN,,#F482,dict_min,F482,DICTMIN,D n - x k -1 or 0,,"Computes the minimal key `k` (represented by a _Slice_ with `n` data bits) in dictionary `D`, and returns `k` along with the associated value `x`." -DICTMINREF,,#F483,dict_min,F483,DICTMINREF,D n - c k -1 or 0,,"Similar to `DICTMIN`, but returns the only reference in the value as a _Cell_ `c`." -DICTIMIN,,#F484,dict_min,F484,DICTIMIN,D n - x i -1 or 0,,"Similar to `DICTMIN`, but computes the minimal key `i` under the assumption that all keys are big-endian signed `n`-bit integers. Notice that the key and value returned may differ from those computed by `DICTMIN` and `DICTUMIN`." -DICTIMINREF,,#F485,dict_min,F485,DICTIMINREF,D n - c i -1 or 0,,"Similar to `DICTIMIN`, but returns the only reference in the value." -DICTUMIN,,#F486,dict_min,F486,DICTUMIN,D n - x i -1 or 0,,"Similar to `DICTMIN`, but returns the key as an unsigned `n`-bit _Integer_ `i`." -DICTUMINREF,,#F487,dict_min,F487,DICTUMINREF,D n - c i -1 or 0,,"Similar to `DICTUMIN`, but returns the only reference in the value." -DICTMAX,,#F48A,dict_min,F48A,DICTMAX,D n - x k -1 or 0,,"Computes the maximal key `k` (represented by a _Slice_ with `n` data bits) in dictionary `D`, and returns `k` along with the associated value `x`." -DICTMAXREF,,#F48B,dict_min,F48B,DICTMAXREF,D n - c k -1 or 0,,"Similar to `DICTMAX`, but returns the only reference in the value." -DICTIMAX,,#F48C,dict_min,F48C,DICTIMAX,D n - x i -1 or 0,,"Similar to `DICTMAX`, but computes the maximal key `i` under the assumption that all keys are big-endian signed `n`-bit integers. Notice that the key and value returned may differ from those computed by `DICTMAX` and `DICTUMAX`." -DICTIMAXREF,,#F48D,dict_min,F48D,DICTIMAXREF,D n - c i -1 or 0,,"Similar to `DICTIMAX`, but returns the only reference in the value." -DICTUMAX,,#F48E,dict_min,F48E,DICTUMAX,D n - x i -1 or 0,,"Similar to `DICTMAX`, but returns the key as an unsigned `n`-bit _Integer_ `i`." -DICTUMAXREF,,#F48F,dict_min,F48F,DICTUMAXREF,D n - c i -1 or 0,,"Similar to `DICTUMAX`, but returns the only reference in the value." -DICTREMMIN,,#F492,dict_min,F492,DICTREMMIN,D n - D' x k -1 or D 0,,"Computes the minimal key `k` (represented by a _Slice_ with `n` data bits) in dictionary `D`, removes `k` from the dictionary, and returns `k` along with the associated value `x` and the modified dictionary `D'`." -DICTREMMINREF,,#F493,dict_min,F493,DICTREMMINREF,D n - D' c k -1 or D 0,,"Similar to `DICTREMMIN`, but returns the only reference in the value as a _Cell_ `c`." -DICTIREMMIN,,#F494,dict_min,F494,DICTIREMMIN,D n - D' x i -1 or D 0,,"Similar to `DICTREMMIN`, but computes the minimal key `i` under the assumption that all keys are big-endian signed `n`-bit integers. Notice that the key and value returned may differ from those computed by `DICTREMMIN` and `DICTUREMMIN`." -DICTIREMMINREF,,#F495,dict_min,F495,DICTIREMMINREF,D n - D' c i -1 or D 0,,"Similar to `DICTIREMMIN`, but returns the only reference in the value." -DICTUREMMIN,,#F496,dict_min,F496,DICTUREMMIN,D n - D' x i -1 or D 0,,"Similar to `DICTREMMIN`, but returns the key as an unsigned `n`-bit _Integer_ `i`." -DICTUREMMINREF,,#F497,dict_min,F497,DICTUREMMINREF,D n - D' c i -1 or D 0,,"Similar to `DICTUREMMIN`, but returns the only reference in the value." -DICTREMMAX,,#F49A,dict_min,F49A,DICTREMMAX,D n - D' x k -1 or D 0,,"Computes the maximal key `k` (represented by a _Slice_ with `n` data bits) in dictionary `D`, removes `k` from the dictionary, and returns `k` along with the associated value `x` and the modified dictionary `D'`." -DICTREMMAXREF,,#F49B,dict_min,F49B,DICTREMMAXREF,D n - D' c k -1 or D 0,,"Similar to `DICTREMMAX`, but returns the only reference in the value as a _Cell_ `c`." -DICTIREMMAX,,#F49C,dict_min,F49C,DICTIREMMAX,D n - D' x i -1 or D 0,,"Similar to `DICTREMMAX`, but computes the minimal key `i` under the assumption that all keys are big-endian signed `n`-bit integers. Notice that the key and value returned may differ from those computed by `DICTREMMAX` and `DICTUREMMAX`." -DICTIREMMAXREF,,#F49D,dict_min,F49D,DICTIREMMAXREF,D n - D' c i -1 or D 0,,"Similar to `DICTIREMMAX`, but returns the only reference in the value." -DICTUREMMAX,,#F49E,dict_min,F49E,DICTUREMMAX,D n - D' x i -1 or D 0,,"Similar to `DICTREMMAX`, but returns the key as an unsigned `n`-bit _Integer_ `i`." -DICTUREMMAXREF,,#F49F,dict_min,F49F,DICTUREMMAXREF,D n - D' c i -1 or D 0,,"Similar to `DICTUREMMAX`, but returns the only reference in the value." -DICTIGETJMP,,#F4A0,dict_special,F4A0,DICTIGETJMP,i D n - ,,"Similar to `DICTIGET`, but with `x` `BLESS`ed into a continuation with a subsequent `JMPX` to it on success. On failure, does nothing. This is useful for implementing `switch`/`case` constructions." -DICTUGETJMP,,#F4A1,dict_special,F4A1,DICTUGETJMP,i D n - ,,"Similar to `DICTIGETJMP`, but performs `DICTUGET` instead of `DICTIGET`." -DICTIGETEXEC,,#F4A2,dict_special,F4A2,DICTIGETEXEC,i D n - ,,"Similar to `DICTIGETJMP`, but with `EXECUTE` instead of `JMPX`." -DICTUGETEXEC,,#F4A3,dict_special,F4A3,DICTUGETEXEC,i D n - ,,"Similar to `DICTUGETJMP`, but with `EXECUTE` instead of `JMPX`." -DICTPUSHCONST,,#F4A6_ d:^Cell n:uint10,dict_special,F4A6_n,[ref] [n] DICTPUSHCONST, - D n,34,"Pushes a non-empty constant dictionary `D` (as a `Cell^?`) along with its key length `0 <= n <= 1023`, stored as a part of the instruction. The dictionary itself is created from the first of remaining references of the current continuation. In this way, the complete `DICTPUSHCONST` instruction can be obtained by first serializing `xF4A4_`, then the non-empty dictionary itself (one `1` bit and a cell reference), and then the unsigned 10-bit integer `n` (as if by a `STU 10` instruction). An empty dictionary can be pushed by a `NEWDICT` primitive instead." -PFXDICTGETQ,,#F4A8,dict_special,F4A8,PFXDICTGETQ,s D n - s' x s'' -1 or s 0,,"Looks up the unique prefix of _Slice_ `s` present in the prefix code dictionary represented by `Cell^?` `D` and `0 <= n <= 1023`. If found, the prefix of `s` is returned as `s'`, and the corresponding value (also a _Slice_) as `x`. The remainder of `s` is returned as a _Slice_ `s''`. If no prefix of `s` is a key in prefix code dictionary `D`, returns the unchanged `s` and a zero flag to indicate failure." -PFXDICTGET,,#F4A9,dict_special,F4A9,PFXDICTGET,s D n - s' x s'',,"Similar to `PFXDICTGET`, but throws a cell deserialization failure exception on failure." -PFXDICTGETJMP,,#F4AA,dict_special,F4AA,PFXDICTGETJMP,s D n - s' s'' or s,,"Similar to `PFXDICTGETQ`, but on success `BLESS`es the value `x` into a _Continuation_ and transfers control to it as if by a `JMPX`. On failure, returns `s` unchanged and continues execution." -PFXDICTGETEXEC,,#F4AB,dict_special,F4AB,PFXDICTGETEXEC,s D n - s' s'',,"Similar to `PFXDICTGETJMP`, but `EXEC`utes the continuation found instead of jumping to it. On failure, throws a cell deserialization exception." -PFXDICTCONSTGETJMP,,#F4AE_ d:^Cell n:uint10,dict_special,F4AE_n,"[ref] [n] PFXDICTCONSTGETJMP -[ref] [n] PFXDICTSWITCH",s - s' s'' or s,,Combines `[n] DICTPUSHCONST` for `0 <= n <= 1023` with `PFXDICTGETJMP`. -DICTIGETJMPZ,,#F4BC,dict_special,F4BC,DICTIGETJMPZ,i D n - i or nothing,,A variant of `DICTIGETJMP` that returns index `i` on failure. -DICTUGETJMPZ,,#F4BD,dict_special,F4BD,DICTUGETJMPZ,i D n - i or nothing,,A variant of `DICTUGETJMP` that returns index `i` on failure. -DICTIGETEXECZ,,#F4BE,dict_special,F4BE,DICTIGETEXECZ,i D n - i or nothing,,A variant of `DICTIGETEXEC` that returns index `i` on failure. -DICTUGETEXECZ,,#F4BF,dict_special,F4BF,DICTUGETEXECZ,i D n - i or nothing,,A variant of `DICTUGETEXEC` that returns index `i` on failure. -SUBDICTGET,,#F4B1,dict_sub,F4B1,SUBDICTGET,k l D n - D',,"Constructs a subdictionary consisting of all keys beginning with prefix `k` (represented by a _Slice_, the first `0 <= l <= n <= 1023` data bits of which are used as a key) of length `l` in dictionary `D` of type `HashmapE(n,X)` with `n`-bit keys. On success, returns the new subdictionary of the same type `HashmapE(n,X)` as a _Slice_ `D'`." -SUBDICTIGET,,#F4B2,dict_sub,F4B2,SUBDICTIGET,x l D n - D',,"Variant of `SUBDICTGET` with the prefix represented by a signed big-endian `l`-bit _Integer_ `x`, where necessarily `l <= 257`." -SUBDICTUGET,,#F4B3,dict_sub,F4B3,SUBDICTUGET,x l D n - D',,"Variant of `SUBDICTGET` with the prefix represented by an unsigned big-endian `l`-bit _Integer_ `x`, where necessarily `l <= 256`." -SUBDICTRPGET,,#F4B5,dict_sub,F4B5,SUBDICTRPGET,k l D n - D',,"Similar to `SUBDICTGET`, but removes the common prefix `k` from all keys of the new dictionary `D'`, which becomes of type `HashmapE(n-l,X)`." -SUBDICTIRPGET,,#F4B6,dict_sub,F4B6,SUBDICTIRPGET,x l D n - D',,"Variant of `SUBDICTRPGET` with the prefix represented by a signed big-endian `l`-bit _Integer_ `x`, where necessarily `l <= 257`." -SUBDICTURPGET,,#F4B7,dict_sub,F4B7,SUBDICTURPGET,x l D n - D',,"Variant of `SUBDICTRPGET` with the prefix represented by an unsigned big-endian `l`-bit _Integer_ `x`, where necessarily `l <= 256`." -ACCEPT,,#F800,app_gas,F800,ACCEPT,-,26,"Sets current gas limit `g_l` to its maximal allowed value `g_m`, and resets the gas credit `g_c` to zero, decreasing the value of `g_r` by `g_c` in the process. -In other words, the current smart contract agrees to buy some gas to finish the current transaction. This action is required to process external messages, which bring no value (hence no gas) with themselves." -SETGASLIMIT,,#F801,app_gas,F801,SETGASLIMIT,g - ,26,"Sets current gas limit `g_l` to the minimum of `g` and `g_m`, and resets the gas credit `g_c` to zero. If the gas consumed so far (including the present instruction) exceeds the resulting value of `g_l`, an (unhandled) out of gas exception is thrown before setting new gas limits. Notice that `SETGASLIMIT` with an argument `g >= 2^63-1` is equivalent to `ACCEPT`." -COMMIT,,#F80F,app_gas,F80F,COMMIT,-,26,Commits the current state of registers `c4` (“persistent data'') and `c5` (“actions'') so that the current execution is considered “successful'' with the saved values even if an exception is thrown later. -RANDU256,,#F810,app_rnd,F810,RANDU256,- x,26+|c7|+|c1_1|,"Generates a new pseudo-random unsigned 256-bit _Integer_ `x`. The algorithm is as follows: if `r` is the old value of the random seed, considered as a 32-byte array (by constructing the big-endian representation of an unsigned 256-bit integer), then its `sha512(r)` is computed; the first 32 bytes of this hash are stored as the new value `r'` of the random seed, and the remaining 32 bytes are returned as the next random value `x`." -RAND,,#F811,app_rnd,F811,RAND,y - z,26+|c7|+|c1_1|,"Generates a new pseudo-random integer `z` in the range `0...y-1` (or `y...-1`, if `y<0`). More precisely, an unsigned random value `x` is generated as in `RAND256U`; then `z:=floor(x*y/2^256)` is computed. -Equivalent to `RANDU256` `256 MULRSHIFT`." -SETRAND,,#F814,app_rnd,F814,SETRAND,x - ,26+|c7|+|c1_1|,Sets the random seed to unsigned 256-bit _Integer_ `x`. -ADDRAND,,#F815,app_rnd,F815,"ADDRAND -RANDOMIZE",x - ,26,"Mixes unsigned 256-bit _Integer_ `x` into the random seed `r` by setting the random seed to `Sha` of the concatenation of two 32-byte strings: the first with the big-endian representation of the old seed `r`, and the second with the big-endian representation of `x`." -GETPARAM,,#F82 i:uint4,app_config,F82i,[i] GETPARAM, - x,26,"Returns the `i`-th parameter from the _Tuple_ provided at `c7` for `0 <= i <= 15`. Equivalent to `c7 PUSHCTR` `FIRST` `[i] INDEX`. -If one of these internal operations fails, throws an appropriate type checking or range checking exception." -NOW,GETPARAM,#F823,app_config,F823,NOW, - x,26,"Returns the current Unix time as an _Integer_. If it is impossible to recover the requested value starting from `c7`, throws a type checking or range checking exception as appropriate. -Equivalent to `3 GETPARAM`." -BLOCKLT,GETPARAM,#F824,app_config,F824,BLOCKLT, - x,26,"Returns the starting logical time of the current block. -Equivalent to `4 GETPARAM`." -LTIME,GETPARAM,#F825,app_config,F825,LTIME, - x,26,"Returns the logical time of the current transaction. -Equivalent to `5 GETPARAM`." -RANDSEED,GETPARAM,#F826,app_config,F826,RANDSEED, - x,26,"Returns the current random seed as an unsigned 256-bit _Integer_. -Equivalent to `6 GETPARAM`." -BALANCE,GETPARAM,#F827,app_config,F827,BALANCE, - t,26,"Returns the remaining balance of the smart contract as a _Tuple_ consisting of an _Integer_ (the remaining Gram balance in nanograms) and a _Maybe Cell_ (a dictionary with 32-bit keys representing the balance of “extra currencies''). -Equivalent to `7 GETPARAM`. -Note that `RAW` primitives such as `SENDRAWMSG` do not update this field." -MYADDR,GETPARAM,#F828,app_config,F828,MYADDR, - s,26,"Returns the internal address of the current smart contract as a _Slice_ with a `MsgAddressInt`. If necessary, it can be parsed further using primitives such as `PARSEMSGADDR` or `REWRITESTDADDR`. -Equivalent to `8 GETPARAM`." -CONFIGROOT,GETPARAM,#F829,app_config,F829,CONFIGROOT, - D,26,Returns the _Maybe Cell_ `D` with the current global configuration dictionary. Equivalent to `9 GETPARAM `. -CONFIGDICT,,#F830,app_config,F830,CONFIGDICT, - D 32,26,"Returns the global configuration dictionary along with its key length (32). -Equivalent to `CONFIGROOT` `32 PUSHINT`." -CONFIGPARAM,,#F832,app_config,F832,CONFIGPARAM,i - c -1 or 0,,"Returns the value of the global configuration parameter with integer index `i` as a _Cell_ `c`, and a flag to indicate success. -Equivalent to `CONFIGDICT` `DICTIGETREF`." -CONFIGOPTPARAM,,#F833,app_config,F833,CONFIGOPTPARAM,i - c^?,,"Returns the value of the global configuration parameter with integer index `i` as a _Maybe Cell_ `c^?`. -Equivalent to `CONFIGDICT` `DICTIGETOPTREF`." -GETGLOBVAR,,#F840,app_global,F840,GETGLOBVAR,k - x,26,"Returns the `k`-th global variable for `0 <= k < 255`. -Equivalent to `c7 PUSHCTR` `SWAP` `INDEXVARQ`." -GETGLOB,,#F85_ k:(## 5) {1 <= k},app_global,F85_k,[k] GETGLOB, - x,26,"Returns the `k`-th global variable for `1 <= k <= 31`. -Equivalent to `c7 PUSHCTR` `[k] INDEXQ`." -SETGLOBVAR,,#F860,app_global,F860,SETGLOBVAR,x k - ,26+|c7’|,"Assigns `x` to the `k`-th global variable for `0 <= k < 255`. -Equivalent to `c7 PUSHCTR` `ROTREV` `SETINDEXVARQ` `c7 POPCTR`." -SETGLOB,,#F87_ k:(## 5) {1 <= k},app_global,F87_k,[k] SETGLOB,x - ,26+|c7’|,"Assigns `x` to the `k`-th global variable for `1 <= k <= 31`. -Equivalent to `c7 PUSHCTR` `SWAP` `k SETINDEXQ` `c7 POPCTR`." -HASHCU,,#F900,app_crypto,F900,HASHCU,c - x,26,Computes the representation hash of a _Cell_ `c` and returns it as a 256-bit unsigned integer `x`. Useful for signing and checking signatures of arbitrary entities represented by a tree of cells. -HASHSU,,#F901,app_crypto,F901,HASHSU,s - x,526,Computes the hash of a _Slice_ `s` and returns it as a 256-bit unsigned integer `x`. The result is the same as if an ordinary cell containing only data and references from `s` had been created and its hash computed by `HASHCU`. -SHA256U,,#F902,app_crypto,F902,SHA256U,s - x,26,"Computes `Sha` of the data bits of _Slice_ `s`. If the bit length of `s` is not divisible by eight, throws a cell underflow exception. The hash value is returned as a 256-bit unsigned integer `x`." -CHKSIGNU,,#F910,app_crypto,F910,CHKSIGNU,h s k - ?,26,"Checks the Ed25519-signature `s` of a hash `h` (a 256-bit unsigned integer, usually computed as the hash of some data) using public key `k` (also represented by a 256-bit unsigned integer). -The signature `s` must be a _Slice_ containing at least 512 data bits; only the first 512 bits are used. The result is `-1` if the signature is valid, `0` otherwise. -Notice that `CHKSIGNU` is equivalent to `ROT` `NEWC` `256 STU` `ENDC` `ROTREV` `CHKSIGNS`, i.e., to `CHKSIGNS` with the first argument `d` set to 256-bit _Slice_ containing `h`. Therefore, if `h` is computed as the hash of some data, these data are hashed _twice_, the second hashing occurring inside `CHKSIGNS`." -CHKSIGNS,,#F911,app_crypto,F911,CHKSIGNS,d s k - ?,26,"Checks whether `s` is a valid Ed25519-signature of the data portion of _Slice_ `d` using public key `k`, similarly to `CHKSIGNU`. If the bit length of _Slice_ `d` is not divisible by eight, throws a cell underflow exception. The verification of Ed25519 signatures is the standard one, with `Sha` used to reduce `d` to the 256-bit number that is actually signed." -CDATASIZEQ,,#F940,app_misc,F940,CDATASIZEQ,c n - x y z -1 or 0,,"Recursively computes the count of distinct cells `x`, data bits `y`, and cell references `z` in the dag rooted at _Cell_ `c`, effectively returning the total storage used by this dag taking into account the identification of equal cells. The values of `x`, `y`, and `z` are computed by a depth-first traversal of this dag, with a hash table of visited cell hashes used to prevent visits of already-visited cells. The total count of visited cells `x` cannot exceed non-negative _Integer_ `n`; otherwise the computation is aborted before visiting the `(n+1)`-st cell and a zero is returned to indicate failure. If `c` is _Null_, returns `x=y=z=0`." -CDATASIZE,,#F941,app_misc,F941,CDATASIZE,c n - x y z,,A non-quiet version of `CDATASIZEQ` that throws a cell overflow exception (8) on failure. -SDATASIZEQ,,#F942,app_misc,F942,SDATASIZEQ,s n - x y z -1 or 0,,"Similar to `CDATASIZEQ`, but accepting a _Slice_ `s` instead of a _Cell_. The returned value of `x` does not take into account the cell that contains the slice `s` itself; however, the data bits and the cell references of `s` are accounted for in `y` and `z`." -SDATASIZE,,#F943,app_misc,F943,SDATASIZE,s n - x y z,,A non-quiet version of `SDATASIZEQ` that throws a cell overflow exception (8) on failure. -LDGRAMS,,#FA00,app_currency,FA00,"LDGRAMS -LDVARUINT16",s - x s',26,"Loads (deserializes) a `Gram` or `VarUInteger 16` amount from _Slice_ `s`, and returns the amount as _Integer_ `x` along with the remainder `s'` of `s`. The expected serialization of `x` consists of a 4-bit unsigned big-endian integer `l`, followed by an `8l`-bit unsigned big-endian representation of `x`. -The net effect is approximately equivalent to `4 LDU` `SWAP` `3 LSHIFT#` `LDUX`." -LDVARINT16,,#FA01,app_currency,FA01,LDVARINT16,s - x s',26,"Similar to `LDVARUINT16`, but loads a _signed_ _Integer_ `x`. -Approximately equivalent to `4 LDU` `SWAP` `3 LSHIFT#` `LDIX`." -STGRAMS,,#FA02,app_currency,FA02,"STGRAMS -STVARUINT16",b x - b',26,"Stores (serializes) an _Integer_ `x` in the range `0...2^120-1` into _Builder_ `b`, and returns the resulting _Builder_ `b'`. The serialization of `x` consists of a 4-bit unsigned big-endian integer `l`, which is the smallest integer `l>=0`, such that `x<2^(8l)`, followed by an `8l`-bit unsigned big-endian representation of `x`. If `x` does not belong to the supported range, a range check exception is thrown." -STVARINT16,,#FA03,app_currency,FA03,STVARINT16,b x - b',26,"Similar to `STVARUINT16`, but serializes a _signed_ _Integer_ `x` in the range `-2^119...2^119-1`." -LDMSGADDR,,#FA40,app_addr,FA40,LDMSGADDR,s - s' s'',26,"Loads from _Slice_ `s` the only prefix that is a valid `MsgAddress`, and returns both this prefix `s'` and the remainder `s''` of `s` as slices." -LDMSGADDRQ,,#FA41,app_addr,FA41,LDMSGADDRQ,s - s' s'' -1 or s 0,26,"A quiet version of `LDMSGADDR`: on success, pushes an extra `-1`; on failure, pushes the original `s` and a zero." -PARSEMSGADDR,,#FA42,app_addr,FA42,PARSEMSGADDR,s - t,26,"Decomposes _Slice_ `s` containing a valid `MsgAddress` into a _Tuple_ `t` with separate fields of this `MsgAddress`. If `s` is not a valid `MsgAddress`, a cell deserialization exception is thrown." -PARSEMSGADDRQ,,#FA43,app_addr,FA43,PARSEMSGADDRQ,s - t -1 or 0,26,A quiet version of `PARSEMSGADDR`: returns a zero on error instead of throwing an exception. -REWRITESTDADDR,,#FA44,app_addr,FA44,REWRITESTDADDR,s - x y,26,"Parses _Slice_ `s` containing a valid `MsgAddressInt` (usually a `msg_addr_std`), applies rewriting from the `anycast` (if present) to the same-length prefix of the address, and returns both the workchain `x` and the 256-bit address `y` as integers. If the address is not 256-bit, or if `s` is not a valid serialization of `MsgAddressInt`, throws a cell deserialization exception." -REWRITESTDADDRQ,,#FA45,app_addr,FA45,REWRITESTDADDRQ,s - x y -1 or 0,26,A quiet version of primitive `REWRITESTDADDR`. -REWRITEVARADDR,,#FA46,app_addr,FA46,REWRITEVARADDR,s - x s',26,"A variant of `REWRITESTDADDR` that returns the (rewritten) address as a _Slice_ `s`, even if it is not exactly 256 bit long (represented by a `msg_addr_var`)." -REWRITEVARADDRQ,,#FA47,app_addr,FA47,REWRITEVARADDRQ,s - x s' -1 or 0,26,A quiet version of primitive `REWRITEVARADDR`. -SENDRAWMSG,,#FB00,app_actions,FB00,SENDRAWMSG,c x - ,526,"Sends a raw message contained in _Cell `c`_, which should contain a correctly serialized object `Message X`, with the only exception that the source address is allowed to have dummy value `addr_none` (to be automatically replaced with the current smart-contract address), and `ihr_fee`, `fwd_fee`, `created_lt` and `created_at` fields can have arbitrary values (to be rewritten with correct values during the action phase of the current transaction). Integer parameter `x` contains the flags. Currently `x=0` is used for ordinary messages; `x=128` is used for messages that are to carry all the remaining balance of the current smart contract (instead of the value originally indicated in the message); `x=64` is used for messages that carry all the remaining value of the inbound message in addition to the value initially indicated in the new message (if bit 0 is not set, the gas fees are deducted from this amount); `x'=x+1` means that the sender wants to pay transfer fees separately; `x'=x+2` means that any errors arising while processing this message during the action phase should be ignored. Finally, `x'=x+32` means that the current account must be destroyed if its resulting balance is zero. This flag is usually employed together with `+128`." -RAWRESERVE,,#FB02,app_actions,FB02,RAWRESERVE,x y - ,526,"Creates an output action which would reserve exactly `x` nanograms (if `y=0`), at most `x` nanograms (if `y=2`), or all but `x` nanograms (if `y=1` or `y=3`), from the remaining balance of the account. It is roughly equivalent to creating an outbound message carrying `x` nanograms (or `b-x` nanograms, where `b` is the remaining balance) to oneself, so that the subsequent output actions would not be able to spend more money than the remainder. Bit `+2` in `y` means that the external action does not fail if the specified amount cannot be reserved; instead, all remaining balance is reserved. Bit `+8` in `y` means `x:=-x` before performing any further actions. Bit `+4` in `y` means that `x` is increased by the original balance of the current account (before the compute phase), including all extra currencies, before performing any other checks and actions. Currently `x` must be a non-negative integer, and `y` must be in the range `0...15`." -RAWRESERVEX,,#FB03,app_actions,FB03,RAWRESERVEX,x D y - ,526,"Similar to `RAWRESERVE`, but also accepts a dictionary `D` (represented by a _Cell_ or _Null_) with extra currencies. In this way currencies other than Grams can be reserved." -SETCODE,,#FB04,app_actions,FB04,SETCODE,c - ,526,Creates an output action that would change this smart contract code to that given by _Cell_ `c`. Notice that this change will take effect only after the successful termination of the current run of the smart contract. -SETLIBCODE,,#FB06,app_actions,FB06,SETLIBCODE,c x - ,526,"Creates an output action that would modify the collection of this smart contract libraries by adding or removing library with code given in _Cell_ `c`. If `x=0`, the library is actually removed if it was previously present in the collection (if not, this action does nothing). If `x=1`, the library is added as a private library, and if `x=2`, the library is added as a public library (and becomes available to all smart contracts if the current smart contract resides in the masterchain); if the library was present in the collection before, its public/private status is changed according to `x`. Also, `16` can be added to `x` to enable bounce transaction on failure. Values of `x` other than `0...2 (+16 possible)` are invalid." -CHANGELIB,,#FB07,app_actions,FB07,CHANGELIB,h x - ,526,"Creates an output action similarly to `SETLIBCODE`, but instead of the library code accepts its hash as an unsigned 256-bit integer `h`. If `x!=0` and the library with hash `h` is absent from the library collection of this smart contract, this output action will fail." -DEBUG,,#FE nn:(#<= 239),debug,FEnn,{nn} DEBUG,-,26,`0 <= nn < 240` -DEBUGSTR,,#FEF n:(## 4) ssss:((n * 8 + 8) * Bit),debug,FEFnssss,"{string} DEBUGSTR -{string} {x} DEBUGSTRI",-,26,"`0 <= n < 16`. Length of `ssss` is `n+1` bytes. -`{string}` is a [string literal](https://github.com/Piterden/TON-docs/blob/master/Fift.%20A%20Brief%20Introduction.md#user-content-29-string-literals). -`DEBUGSTR`: `ssss` is the given string. -`DEBUGSTRI`: `ssss` is one-byte integer `0 <= x <= 255` followed by the given string." -DUMPSTK,DEBUG,#FE00,debug,FE00,DUMPSTK,-,26,Dumps the stack (at most the top 255 values) and shows the total stack depth. -DUMP,DEBUG,#FE2 i:uint4,debug,FE2i,s[i] DUMP,-,26,Dumps `s[i]`. -SETCP,,#FF nn:(#<= 239),codepage,FFnn,[nn] SETCP,-,26,"Selects TVM codepage `0 <= nn < 240`. If the codepage is not supported, throws an invalid opcode exception." -SETCP0,SETCP,#FF00,codepage,FF00,SETCP0,-,26,Selects TVM (test) codepage zero as described in this document. -SETCP_SPECIAL,,#FFF z:(## 4) {1 <= z},codepage,FFFz,[z-16] SETCP,-,26,"Selects TVM codepage `z-16` for `1 <= z <= 15`. Negative codepages `-13...-1` are reserved for restricted versions of TVM needed to validate runs of TVM in other codepages. Negative codepage `-14` is reserved for experimental codepages, not necessarily compatible between different TVM implementations, and should be disabled in the production versions of TVM." -SETCPX,,#FFF0,codepage,FFF0,SETCPX,c - ,26,Selects codepage `c` with `-2^15 <= c < 2^15` passed in the top of the stack. -MYCODE,,#F8210,app_config,F8210,MYCODE,- c,26,Retrieves code of smart-contract from c7. Equivalent to `10 GETPARAM`. -INCOMINGVALUE,,#F8211,app_config,F8211,INCOMINGVALUE,- t,26,Retrieves value of incoming message from c7. Equivalent to `11 GETPARAM`. -STORAGEFEES,,#F8212,app_config,F8212,STORAGEFEES,- i,26,Retrieves value of storage phase fees from c7. Equivalent to `12 GETPARAM`. -PREVBLOCKSINFOTUPLE,,#F8213,app_config,F8213,PREVBLOCKSINFOTUPLE,- t,26,Retrives PrevBlocksInfo: `[last_mc_blocks, prev_key_block]` from c7. Equivalent to `13 GETPARAM`. -PREVMCBLOCKS,,#F83400,app_config,F83400,PREVMCBLOCKS,- t,34,Retrives `last_mc_blocks` part of PrevBlocksInfo from c7 (parameter 13). -PREVKEYBLOCK,,#F83401,app_config,F83401,PREVKEYBLOCK,- t,34,Retrives `prev_key_block` part of PrevBlocksInfo from c7 (parameter 13). -GLOBALID,,#F835,app_config,F835,GLOBALID,- i,26,Retrieves global_id from 19 network config. -GASCONSUMED,,#F807,app_gas,F807,GASCONSUMED,- g_c,26,Returns gas consumed by VM so far (including this instruction). -MULADDDIVMOD,,#A980,arithm_div,A980,MULADDDIVMOD,x y w z - q=floor((xy+w)/z) r=(xy+w)-zq,26,Performs multiplication, addition, division, and modulo in one step. Calculates q as floor((xy+w)/z) and r as (xy+w)-zq. -MULADDDIVMODR,,#A981,arithm_div,A981,MULADDDIVMODR,x y w z - q=round((xy+w)/z) r=(xy+w)-zq,26,Similar to MULADDDIVMOD but calculates q as round((xy+w)/z). -MULADDDIVMODC,,#A982,arithm_div,A982,MULADDDIVMODC,x y w z - q=ceil((xy+w)/z) r=(xy+w)-zq,26,Similar to MULADDDIVMOD but calculates q as ceil((xy+w)/z). -ADDDIVMOD,,#A900,arithm_div,A900,ADDDIVMOD,x w z - q=floor((x+w)/z) r=(x+w)-zq,26,Performs addition, division, and modulo in one step. Calculates q as floor((x+w)/z) and r as (x+w)-zq. -ADDDIVMODR,,#A901,arithm_div,A901,ADDDIVMODR,x w z - q=round((x+w)/z) r=(x+w)-zq,26,Similar to ADDDIVMOD but calculates q as round((x+w)/z). -ADDDIVMODC,,#A902,arithm_div,A902,ADDDIVMODC,x w y - q=ceil((x+w)/z) r=(x+w)-zq,26,Similar to ADDDIVMOD but calculates q as ceil((x+w)/z). Incorrect stack description in the provided data; assumed typo for 'z' instead of 'y' in the input stack. -ADDRSHIFTMOD,,#A920,arithm_div,A920,ADDRSHIFTMOD,x w z - q=floor((x+w)/2^z) r=(x+w)-q*2^z,26,Performs addition, right shift, and modulo in one step. Calculates q as floor((x+w)/2^z) and r as (x+w)-q*2^z. -ADDRSHIFTMODR,,#A921,arithm_div,A921,ADDRSHIFTMODR,x w z - q=round((x+w)/2^z) r=(x+w)-q*2^z,26,Similar to ADDRSHIFTMOD but calculates q as round((x+w)/2^z). -ADDRSHIFTMODC,,#A922,arithm_div,A922,ADDRSHIFTMODC,x w z - q=ceil((x+w)/2^z) r=(x+w)-q*2^z,26,Similar to ADDRSHIFTMOD but calculates q as ceil((x+w)/2^z). -MULADDRSHIFTMOD,,#A9A0,arithm_div,A9A0,MULADDRSHIFTMOD,x y w z - q=floor((xy+w)/2^z) r=(xy+w)-q*2^z,26,Combines multiplication, addition, right shift, and modulo. Calculates q as floor((xy+w)/2^z) and r as (xy+w)-q*2^z. -MULADDRSHIFTRMOD,,#A9A1,arithm_div,A9A1,MULADDRSHIFTRMOD,x y w z - q=round((xy+w)/2^z) r=(xy+w)-q*2^z,26,Similar to MULADDRSHIFTMOD but calculates q as round((xy+w)/2^z). -MULADDRSHIFTCMOD,,#A9A2,arithm_div,A9A2,MULADDRSHIFTCMOD,x y w z - q=ceil((xy+w)/2^z) r=(xy+w)-q*2^z,26,Similar to MULADDRSHIFTMOD but calculates q as ceil((xy+w)/2^z). -LSHIFTADDDIVMOD,,#A9D0 tt:uint8,arithm_div,A9D0tt,[tt+1] LSHIFT#ADDDIVMOD,x w z - q=floor((x*2^y+w)/z) r=(x*2^y+w)-zq,34,Performs left shift on x, adds w, then divides by z, rounding down for q and calculates remainder r. -LSHIFTADDDIVMODR,,#A9D1 tt:uint8,arithm_div,A9D1tt,[tt+1] LSHIFT#ADDDIVMODR,x w z - q=round((x*2^y+w)/z) r=(x*2^y+w)-zq,34,Similar to LSHIFTADDDIVMOD but rounds q to the nearest integer. -LSHIFTADDDIVMODC,,#A9D2 tt:uint8,arithm_div,A9D2tt,[tt+1] LSHIFT#ADDDIVMODC,x w z - q=ceil((x*2^y+w)/z) r=(x*2^y+w)-zq,34,Similar to LSHIFTADDDIVMOD but rounds q up to the nearest integer. -HASHEXT_SHA256,,#F90400,app_crypto,F90400,HASHEXT_SHA256,s_1 ... s_n n - h,1/33 gas per byte,Calculates and returns hash of the concatenation of slices (or builders) `s_1...s_n`. -HASHEXT_SHA512,,#F90401,app_crypto,F90401,HASHEXT_SHA512,s_1 ... s_n n - h,1/16 gas per byte,Calculates and returns hash of the concatenation of slices (or builders) `s_1...s_n`. -HASHEXT_BLAKE2B,,#F90402,app_crypto,F90402,HASHEXT_BLAKE2B,s_1 ... s_n n - h,1/19 gas per byte,Calculates and returns hash of the concatenation of slices (or builders) `s_1...s_n`. -HASHEXT_KECCAK256,,#F90403,app_crypto,F90403,HASHEXT_KECCAK256,s_1 ... s_n n - h,1/11 gas per byte,Calculates and returns hash of the concatenation of slices (or builders) `s_1...s_n`. -HASHEXT_KECCAK512,,#F90404,app_crypto,F90404,HASHEXT_KECCAK512,s_1 ... s_n n - h,1/19 gas per byte,Calculates and returns hash of the concatenation of slices (or builders) `s_1...s_n`. -HASHEXTR_SHA256,,#F90500,app_crypto,F90500,HASHEXTR_SHA256,s_n ... s_1 n - h,1/33 gas per byte,Same as `HASHEXT_`, but arguments are given in reverse order. -HASHEXTR_SHA512,,#F90501,app_crypto,F90501,HASHEXTR_SHA512,s_n ... s_1 n - h,1/16 gas per byte,Same as `HASHEXT_`, but arguments are given in reverse order. -HASHEXTR_BLAKE2B,,#F90502,app_crypto,F90502,HASHEXTR_BLAKE2B,s_n ... s_1 n - h,1/19 gas per byte,Same as `HASHEXT_`, but arguments are given in reverse order. -HASHEXTR_KECCAK256,,#F90503,app_crypto,F90503,HASHEXTR_SHA256,s_n ... s_1 n - h,1/11 gas per byte,Same as `HASHEXT_`, but arguments are given in reverse order. -HASHEXTR_KECCAK512,,#F90504,app_crypto,F90504,HASHEXTR_KECCAK512,s_n ... s_1 n - h,1/19 gas per byte,Same as `HASHEXT_`, but arguments are given in reverse order. -HASHEXTA_SHA256,,#F90600,app_crypto,F90600,HASHEXTA_SHA256,b s_1 ... s_n n - b',1/33 gas per byte,Appends the resulting hash to a builder `b` instead of pushing it to the stack. -HASHEXTA_SHA512,,#F90601,app_crypto,F90601,HASHEXTA_SHA512,b s_1 ... s_n n - b',1/16 gas per byte,Appends the resulting hash to a builder `b` instead of pushing it to the stack. -HASHEXTA_BLAKE2B,,#F90602,app_crypto,F90602,HASHEXTA_BLAKE2B,b s_1 ... s_n n - b',1/19 gas per byte,Appends the resulting hash to a builder `b` instead of pushing it to the stack. -HASHEXTA_KECCAK256,,#F90603,app_crypto,F90603,HASHEXTA_KECCAK256,b s_1 ... s_n n - b',1/11 gas per byte,Appends the resulting hash to a builder `b` instead of pushing it to the stack. -HASHEXTA_KECCAK512,,#F90604,app_crypto,F90604,HASHEXTA_KECCAK512,b s_1 ... s_n n - b',1/6 gas per byte,Appends the resulting hash to a builder `b` instead of pushing it to the stack. -HASHEXTAR_SHA256,,#F90700,app_crypto,F90700,HASHEXTAR_SHA256,b s_n ... s_1 n - b',1/33 gas per byte,Arguments are given in reverse order, appends hash to builder. -HASHEXTAR_SHA512,,#F90701,app_crypto,F90701,HASHEXTAR_SHA512,b s_n ... s_1 n - b',1/16 gas per byte,Arguments are given in reverse order, appends hash to builder. -HASHEXTAR_BLAKE2B,,#F90702,app_crypto,F90702,HASHEXTAR_BLAKE2B,b s_n ... s_1 n - b',1/19 gas per byte,Arguments are given in reverse order, appends hash to builder. -HASHEXTAR_KECCAK256,,#F90703,app_crypto,F90703,HASHEXTAR_KECCAK256,b s_n ... s_1 n - b',1/11 gas per byte,Arguments are given in reverse order, appends hash to builder. -HASHEXTAR_KECCAK512,,#F90704,app_crypto,F90704,HASHEXTAR_KECCAK512,b s_n ... s_1 n - b',1/6 gas per byte,Arguments are given in reverse order, appends hash to builder. -ECRECOVER,,#F912,app_crypto,F912,ECRECOVER,hash v r s - 0 or h x1 x2 -1,1526,Recovers public key from signature, identical to Bitcoin/Ethereum operations. -P256_CHKSIGNS,,#F915,app_crypto,F915,P256_CHKSIGNS,d sig k - ?,3526,Checks seck256r1-signature `sig` of data portion of slice `d` and public key `k`. Returns -1 on success, 0 on failure. -P256_CHKSIGNU,,#F914,app_crypto,F914,P256_CHKSIGNU,h sig k - ?,3526,Same as P256_CHKSIGNS, but the signed data is 32-byte encoding of 256-bit unsigned integer h. -RIST255_FROMHASH,,#F920,app_crypto,F920,RIST255_FROMHASH,h1 h2 - x,626,Deterministically generates a valid point `x` from a 512-bit hash (given as two 256-bit integers). -RIST255_VALIDATE,,#F921,app_crypto,F921,RIST255_VALIDATE,x - ,226,Checks that integer `x` is a valid representation of some curve point. Throws `range_chk` on error. -RIST255_ADD,,#F922,app_crypto,F922,RIST255_ADD,x y - x+y,626,Addition of two points on a curve. -RIST255_SUB,,#F923,app_crypto,F923,RIST255_SUB,x y - x-y,626,Subtraction of two points on curve. -RIST255_MUL,,#F924,app_crypto,F924,RIST255_MUL,x n - x*n,2026,Multiplies point `x` by a scalar `n`. Any `n` is valid, including negative. -RIST255_MULBASE,,#F925,app_crypto,F925,RIST255_MULBASE,n - g*n,776,Multiplies the generator point `g` by a scalar `n`. Any `n` is valid, including negative. -RIST255_PUSHL,,#F926,app_crypto,F926,RIST255_PUSHL,- l,26,Pushes integer `l=2^252+27742317777372353535851937790883648493`, which is the order of the group. -RIST255_QVALIDATE,,#B7F921,app_crypto,B7F921,RIST255_QVALIDATE,x - 0 or -1,234,Quiet version of `RIST255_VALIDATE`. -RIST255_QADD,,#B7F922,app_crypto,B7F922,RIST255_QADD,x y - 0 or x+y -1,634,Quiet version of `RIST255_ADD`. -RIST255_QSUB,,#B7F923,app_crypto,B7F923,RIST255_QSUB,x y - 0 or x-y -1,634,Quiet version of `RIST255_SUB`. -RIST255_QMUL,,#B7F924,app_crypto,B7F924,RIST255_QMUL,x n - 0 or x*n -1,2034,Quiet version of `RIST255_MUL`. -RIST255_QMULBASE,,#B7F925,app_crypto,B7F925,RIST255_QMULBASE,n - 0 or g*n -1,784,Quiet version of `RIST255_MULBASE` -RUNVM,,#DB4 flags:(## 12),cont_basic,DB4fff,RUNVM,x_1 ... x_n n code [r] [c4] [c7] [g_l] [g_m] - x'_1 ... x'_m exitcode [data'] [c4'] [c5] [g_c],66+x,Runs child VM with code `code` and stack `x_1...x_n`. Returns the resulting stack `x'_1...x'_m` and exitcode. Other arguments and return values are enabled by flags. -RUNVMX,,#DB50,cont_basic,DB50,RUNVMX,x_1 ... x_n n code [r] [c4] [c7] [g_l] [g_m] flags - x'_1 ... x'_m exitcode [data'] [c4'] [c5] [g_c],66+x,Same as `RUNVM`, but pops flags from stack. -GETGASFEE,,#F836,app_config,F836,GETGASFEE,gas_used is_mc - price,,Calculates gas fee -GETSTORAGEFEE,,#F837,app_config,F837,GETSTORAGEFEE,cells bits seconds is_mc - price,,Calculates storage fees in nanotons for contract based on current storage prices. `cells` and `bits` are the size of the [AccountState](https://github.com/ton-blockchain/ton/blob/8a9ff339927b22b72819c5125428b70c406da631/crypto/block/block.tlb#L247) (with deduplication, including root cell). -GETFORWARDFEE,,#F838,app_config,F838,GETFORWARDFEE,cells bits is_mc - price,,Calculates forward fees in nanotons for outgoing message. `is_mc` is true if the source or the destination is in masterchain, false if both are in basechain. Note, cells and bits in Message should be counted with account for deduplication and root-is-not-counted rules. -GETPRECOMPILEDGAS,,#F839,app_config,F839,GETPRECOMPILEDGAS,- x,,reserved, currently returns null. Will return cost of contract execution in gas units if this contract is precompiled -GETORIGINALFWDFEE,,#F83A,app_config,F83A,GETORIGINALFWDFEE,fwd_fee is_mc - orig_fwd_fee,,calculate `fwd_fee * 2^16 / first_frac`. Can be used to get the original `fwd_fee` of the message (as replacement for hardcoded values like [this](https://github.com/ton-blockchain/token-contract/blob/21e7844fa6dbed34e0f4c70eb5f0824409640a30/ft/jetton-wallet.fc#L224C17-L224C46)) from `fwd_fee` parsed from incoming message. `is_mc` is true if the source or the destination is in masterchain, false if both are in basechain. -GETGASFEESIMPLE,,#F83B,app_config,F83B,GETGASFEESIMPLE,gas_used is_mc - price,,Same as `GETGASFEE`, but without flat price (just `(gas_used * price) / 2^16)`. -GETFORWARDFEESIMPLE,,#F83C,app_config,F83C,GETFORWARDFEESIMPLE,cells bits is_mc - price,,Calculates additional forward cost in nanotons for message that contains additional `cells` and `bits`. In other words, same as `GETFORWARDFEE`, but without lump price (just `(bits*bit_price + cells*cell_price) / 2^16)`. -UNPACKEDCONFIGTUPLE,,#F82E,app_config,F82E,UNPACKEDCONFIGTUPLE,- c,26,Retrieves tuple of configs slices from c7 -DUEPAYMENT,,#F82F,app_config,F82F,DUEPAYMENT,- i,26,Retrieves value of due payment from c7 -GLOBALID,,#F835,app_config,F835,GLOBALID,- i,26,Now retrieves `ConfigParam 19` from from c7, ton form config dict. -SENDMSG,,#FB08,app_config,FB08,SENDMSG,msg mode - i,,Now retrieves `ConfigParam 24/25` (message prices) and `ConfigParam 43` (`max_msg_cells`) from c7, not from config dict. -CLEVEL,,#D766,cell_parse,D766,CLEVEL,cell - level,26,Returns level of the cell -CLEVELMASK,,#D767,cell_parse,D767,CLEVELMASK,cell - level_mask,26,Returns level mask of the cell -CHASHIX,,#D770,cell_parse,D770,CHASHIX,cell i - depth,26,Returns ith hash of the cell (i is in range 0..3) -CDEPTHIX,,#D771,cell_parse,D771,CDEPTHIX,cell i - depth,26,Returns ith depth of the cell (i is in range 0..3) diff --git a/docs/participate/run-nodes/full-node.mdx b/docs/participate/run-nodes/full-node.mdx deleted file mode 120000 index 72f25b81c54..00000000000 --- a/docs/participate/run-nodes/full-node.mdx +++ /dev/null @@ -1 +0,0 @@ -../../v3/guidelines/nodes/running-nodes/full-node.mdx \ No newline at end of file diff --git a/docs/participate/wallets/contracts.md b/docs/participate/wallets/contracts.md deleted file mode 120000 index 4c977596eb9..00000000000 --- a/docs/participate/wallets/contracts.md +++ /dev/null @@ -1 +0,0 @@ -../../v3/documentation/smart-contracts/contracts-specs/wallet-contracts.md \ No newline at end of file From d0e464658e8b25739b0fce3db64cf528b4ba1e6f Mon Sep 17 00:00:00 2001 From: AlexG <39581753+reveloper@users.noreply.github.com> Date: Mon, 10 Mar 2025 19:25:53 +0900 Subject: [PATCH 10/23] Update guidelines.mdx Proofread. --- docs/v3/guidelines/smart-contracts/guidelines.mdx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/v3/guidelines/smart-contracts/guidelines.mdx b/docs/v3/guidelines/smart-contracts/guidelines.mdx index 36b559ddf05..5a160d5d322 100644 --- a/docs/v3/guidelines/smart-contracts/guidelines.mdx +++ b/docs/v3/guidelines/smart-contracts/guidelines.mdx @@ -2,9 +2,9 @@ import Button from '@site/src/components/button' # Overview -This page contents table of content for TON smart contracts guidelines. +This page contains a table of contents for TON smart contracts guidelines. -## Guide navigator +## Guides overview | Guide | Stack | Description | |--------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------|----------------------------------------------------------------------------------| @@ -19,15 +19,15 @@ This page contents table of content for TON smart contracts guidelines. | [Compilation instructions](/v3/guidelines/smart-contracts/howto/compile/compilation-instructions/) | C++, cmake, Core | Compile executables from source for deep native integration. | | [Instructions for low-memory machines](/v3/guidelines/smart-contracts/howto/compile/instructions-low-memory/) | С++ | Extnension for compilation on low-memary machines. | | [Working with wallet smart contracts](/v3/guidelines/smart-contracts/howto/wallet/) | FunC; TS, @ton/ton | Learn in detail interaction with various wallets smartcontracts. | -| [Make a simple multisig contract with fift](/v3/guidelines/smart-contracts/howto/multisig/) | Fift, Lite Client | This tutorial help you learn how to deploy your multisig contract. | +| [Make a simple multisig contract with fift](/v3/guidelines/smart-contracts/howto/multisig/) | Fift, Lite Client | This tutorial will help you learn how to deploy your multisig contract. | | [Interact with multisig wallets using TypeScript](/v3/guidelines/smart-contracts/howto/multisig-js) | TS, @ton/ton | Learn how to interact to multisig wallet in TON. | -| [Airdrop claiming guidelines](/v3/guidelines/smart-contracts/howto/airdrop-claim-best-practice/) | Design and arthictecture | Learn concepts on contract interactions and their impact on overall performance. | +| [Airdrop claiming guidelines](/v3/guidelines/smart-contracts/howto/airdrop-claim-best-practice/) | Design and architecture | Learn concepts on contract interactions and their impact on overall performance. | | [Shard optimizations on TON](/v3/guidelines/smart-contracts/howto/shard-optimization/) | TS, @ton/ton | Learn best practice for conract shard optimization. | | [How to shard your TON smart contract and why](https://blog.ton.org/how-to-shard-your-ton-smart-contract-and-why-studying-the-anatomy-of-tons-jettons) | Desing and architecture | Learn concept of contract system with jetton standard | ## TON Course: Contract Development -The [TON Blockchain Course](https://stepik.org/course/176754/) is a comprehensive guide to TON Blockchain development. +The [TON Blockchain Course](https://stepik.org/course/176754/) is a comprehensive guide to blockchain development. - Module 2 is dedicated to __TVM, transactions, scalability and business cases__. - Module 3 is dedicated to __smart contract development lifecycle__. @@ -36,7 +36,7 @@ The [TON Blockchain Course](https://stepik.org/course/176754/) is a comprehensiv @@ -58,4 +58,4 @@ RU ## See Also -- [Smart contract documentation](/v3/documentation/smart-contracts/overview/) \ No newline at end of file +- [Smart contract documentation](/v3/documentation/smart-contracts/overview/) From d630a55adfe044e7b692b8d8a303f26e76fffa98 Mon Sep 17 00:00:00 2001 From: AlexG <39581753+Reveloper@users.noreply.github.com> Date: Mon, 10 Mar 2025 19:57:42 +0900 Subject: [PATCH 11/23] split_tables --- .../guidelines/smart-contracts/guidelines.mdx | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/docs/v3/guidelines/smart-contracts/guidelines.mdx b/docs/v3/guidelines/smart-contracts/guidelines.mdx index 5a160d5d322..028603f0569 100644 --- a/docs/v3/guidelines/smart-contracts/guidelines.mdx +++ b/docs/v3/guidelines/smart-contracts/guidelines.mdx @@ -6,28 +6,42 @@ This page contains a table of contents for TON smart contracts guidelines. ## Guides overview +### Smart contract development + +| Guide | Stack | Description | +|--------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------|----------------------------------------------------------------------------------| +| [TON Hello world](https://tonhelloworld.com/01-wallet/) | FunC; TS, @ton/ton | Write and deploy your first contract. | +| [Working with wallet smart contracts](/v3/guidelines/smart-contracts/howto/wallet/) | FunC; TS, @ton/ton | Learn in detail interaction with various wallets smartcontracts. | +| [Writing tests with Blueprint](/v3/guidelines/smart-contracts/testing/overview/) | TS, @ton/ton | Learn how to write and invoke local tests for contract. | +| [Writing tests examples](/v3/guidelines/smart-contracts/testing/writing-test-examples/) | TS, @ton/ton | Learn how to write various test suites for edgecases. | +| [Interact with multisig wallets using TypeScript](/v3/guidelines/smart-contracts/howto/multisig-js) | TS, @ton/ton | Learn how to interact to multisig wallet in TON. | +| [Shard optimizations on TON](/v3/guidelines/smart-contracts/howto/shard-optimization/) | TS, @ton/ton | Learn best practice for conract shard optimization. | + + +### Scalability and security + | Guide | Stack | Description | |--------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------|----------------------------------------------------------------------------------| -| [TON Hello world](https://tonhelloworld.com/01-wallet/) | TS, @ton/ton | Write and deploy your first contract. | -| [Writing tests with Blueprint](/v3/guidelines/smart-contracts/testing/overview/) | TS, @ton/ton | Learn how to write and invoke local tests for contract. | -| [Writing tests examples](/v3/guidelines/smart-contracts/testing/writing-test-examples/) | TS, @ton/ton | Learn how to write various test suites for edgecases. | -| [Things to focus on while working with TON blockchain](/v3/guidelines/smart-contracts/security/things-to-focus/) | FunC | Best practies for DApps development in TON. | -| [Secure smart contract programming](/v3/guidelines/smart-contracts/security/secure-programming/) | FunC | Best practices for secure development of smart contracts on FunC. | -| [Drawing conclusions from TON Hack Challenge](/v3/guidelines/smart-contracts/security/ton-hack-challenge-1/) | FunC | Best practices for secure development | -| [Random number generation](/v3/guidelines/smart-contracts/security/random-number-generation/) | FunC | Generating random numbers in TON for various projects. | -| [Generation of block random seed](/v3/guidelines/smart-contracts/security/random/) | C++, Core | Explanation on random in TON. | -| [Compilation instructions](/v3/guidelines/smart-contracts/howto/compile/compilation-instructions/) | C++, cmake, Core | Compile executables from source for deep native integration. | -| [Instructions for low-memory machines](/v3/guidelines/smart-contracts/howto/compile/instructions-low-memory/) | С++ | Extnension for compilation on low-memary machines. | -| [Working with wallet smart contracts](/v3/guidelines/smart-contracts/howto/wallet/) | FunC; TS, @ton/ton | Learn in detail interaction with various wallets smartcontracts. | -| [Make a simple multisig contract with fift](/v3/guidelines/smart-contracts/howto/multisig/) | Fift, Lite Client | This tutorial will help you learn how to deploy your multisig contract. | -| [Interact with multisig wallets using TypeScript](/v3/guidelines/smart-contracts/howto/multisig-js) | TS, @ton/ton | Learn how to interact to multisig wallet in TON. | +| [How to shard your TON smart contract and why](https://blog.ton.org/how-to-shard-your-ton-smart-contract-and-why-studying-the-anatomy-of-tons-jettons) | Desing and architecture | Learn concept of contract system with jetton standard | | [Airdrop claiming guidelines](/v3/guidelines/smart-contracts/howto/airdrop-claim-best-practice/) | Design and architecture | Learn concepts on contract interactions and their impact on overall performance. | -| [Shard optimizations on TON](/v3/guidelines/smart-contracts/howto/shard-optimization/) | TS, @ton/ton | Learn best practice for conract shard optimization. | -| [How to shard your TON smart contract and why](https://blog.ton.org/how-to-shard-your-ton-smart-contract-and-why-studying-the-anatomy-of-tons-jettons) | Desing and architecture | Learn concept of contract system with jetton standard | +| [Things to focus on while working with TON blockchain](/v3/guidelines/smart-contracts/security/things-to-focus/) | FunC | Best practies for DApps development in TON. | +| [Secure smart contract programming](/v3/guidelines/smart-contracts/security/secure-programming/) | FunC | Best practices for secure development of smart contracts on FunC. | +| [Drawing conclusions from TON Hack Challenge](/v3/guidelines/smart-contracts/security/ton-hack-challenge-1/) | FunC | Best practices for secure development | +| [Random number generation](/v3/guidelines/smart-contracts/security/random-number-generation/) | FunC | Generating random numbers in TON for various projects. | + + +### Advanced + +| Guide | Stack | Description | +|--------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------|------------------------------------------------------------------------------------------| +| [Generation of block random seed](/v3/guidelines/smart-contracts/security/random/) | C++, Core | Explanation on random in TON. | +| [Compilation instructions](/v3/guidelines/smart-contracts/howto/compile/compilation-instructions/) | C++, cmake, Core | Compile executables from source for deep native integration. | +| [Instructions for low-memory machines](/v3/guidelines/smart-contracts/howto/compile/instructions-low-memory/) | С++ | Extnension for compilation on low-memary machines. | +| [Make a simple multisig contract with fift](/v3/guidelines/smart-contracts/howto/multisig/) | Fift, Lite Client | This tutorial will help you learn how to deploy your multisig contract with Lite Client. | -## TON Course: Contract Development +## TON course: contract development -The [TON Blockchain Course](https://stepik.org/course/176754/) is a comprehensive guide to blockchain development. +The [TON blockchain course](https://stepik.org/course/176754/) is a comprehensive guide to blockchain development. - Module 2 is dedicated to __TVM, transactions, scalability and business cases__. - Module 3 is dedicated to __smart contract development lifecycle__. From ccb3cc0433164d14ccd9d2bd2b2f730046414de7 Mon Sep 17 00:00:00 2001 From: AlexG <39581753+Reveloper@users.noreply.github.com> Date: Mon, 10 Mar 2025 20:37:33 +0900 Subject: [PATCH 12/23] update_wordlist --- redirects.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/redirects.js b/redirects.js index cf133e474da..827e000145d 100644 --- a/redirects.js +++ b/redirects.js @@ -11,10 +11,6 @@ module.exports = [ to: '/v3/documentation/tvm/tvm-exit-codes', from: '/learn/tvm-instructions/tvm_exit_codes', }, - { - to: '/develop/dapps/telegram-apps', - from: '/develop/dapps/twa', - }, { to: '/v3/documentation/tvm/tvm-overview', from: '/learn/tvm-instructions/tvm_overview', From a3c381f12f2a8e4fdecd71a8ce02516d1a922276 Mon Sep 17 00:00:00 2001 From: Antonoff <35700168+memearchivarius@users.noreply.github.com> Date: Mon, 10 Mar 2025 22:58:43 +0300 Subject: [PATCH 13/23] Update wallet.md hopefully final changes --- .../smart-contracts/howto/wallet.md | 819 +++++++++--------- 1 file changed, 404 insertions(+), 415 deletions(-) diff --git a/docs/v3/guidelines/smart-contracts/howto/wallet.md b/docs/v3/guidelines/smart-contracts/howto/wallet.md index 30f0f5eee5a..5c388c2b729 100644 --- a/docs/v3/guidelines/smart-contracts/howto/wallet.md +++ b/docs/v3/guidelines/smart-contracts/howto/wallet.md @@ -383,12 +383,12 @@ var msg = begin_cell() Now, let’s go through each option in detail: -| Option | Explanation | -| :----------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -| IHR Disabled | Currently, this option is disabled (meaning we store `1`) because Instant Hypercube Routing (IHR) is not yet fully implemented. This option will become relevant once many [Shardchains](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains#many-accountchains-shards) are active on the network. For more details about the IHR Disabled option, refer to [tblkch.pdf](https://ton.org/tblkch.pdf) (chapter 2). | +| Option | Explanation | +| :----------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| IHR Disabled | Currently, this option is disabled (meaning we store `1`) because Instant Hypercube Routing (IHR) is not yet fully implemented. This option will become relevant once many [Shardchains](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains#many-accountchains-shards) are active on the network. For more details about the IHR Disabled option, refer to [tblkch.pdf](https://ton.org/tblkch.pdf) (chapter 2). | | Bounce | When sending messages, errors can occur during smart contract processing. Setting the `Bounce` option to `1` (true) is essential to prevent TON loss. If any errors arise during transaction processing, the message will be returned to the sender, and the same amount of TON (minus fees) will be refunded. Refer to [this guide](/v3/documentation/smart-contracts/message-management/non-bounceable-messages) for more details on non-bounceable messages. | -| Bounced | Bounced messages are those returned to the sender due to an error during transaction processing with a smart contract. This option indicates whether the received message is bounced or not. | -| Src | The Src is the sender's address. In this case, two zero bits indicate the `addr_none` address. | +| Bounced | Bounced messages are those returned to the sender due to an error during transaction processing with a smart contract. This option indicates whether the received message is bounced or not. | +| Src | The Src is the sender's address. In this case, two zero bits indicate the `addr_none` address. | The following two lines of code: @@ -415,14 +415,14 @@ Finally, let’s look at the remaining lines of code: ;; store something as a body ``` -| Option | Explanation | -| :----------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -| Extra currency | This is a native implementation of existing jettons and is not currently in use. | -| IHR fee | As mentioned, IHR is not currently used, so this fee is always zero. For more information, refer to [tblkch.pdf](https://ton.org/tblkch.pdf) (section 3.1.8). | -| Forwarding fee | A forwarding message fee. For more information, refer to [fees documentation](/v3/documentation/smart-contracts/transaction-fees/fees-low-level#transactions-and-phases). | -| Logical time of creation | The time used to create the correct messages queue. | -| UNIX time of creation | The time the message was created in UNIX. | -| State Init | The code and source data for deploying a smart contract. If the bit is set to `0`, there is no State Init. However, if it’s set to `1`, an additional bit is required to indicate whether the State Init is stored in the same cell (`0`) or written as a reference (`1`). | +| Option | Explanation | +| :----------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| Extra currency | This is a native implementation of existing jettons and is not currently in use. | +| IHR fee | As mentioned, IHR is not currently used, so this fee is always zero. For more information, refer to [tblkch.pdf](https://ton.org/tblkch.pdf) (section 3.1.8). | +| Forwarding fee | A forwarding message fee. For more information, refer to [fees documentation](/v3/documentation/smart-contracts/transaction-fees/fees-low-level#transactions-and-phases). | +| Logical time of creation | The time used to create the correct messages queue. | +| UNIX time of creation | The time the message was created in UNIX. | +| State Init | The code and source data for deploying a smart contract. If the bit is set to `0`, there is no State Init. However, if it’s set to `1`, an additional bit is required to indicate whether the State Init is stored in the same cell (`0`) or written as a reference (`1`). | | Message body | This section determines how the message body is stored. If the message body is too large to fit directly into the message, it is stored as a **reference**. In this case, the bit is set to `1` to indicate that the body is stored as a reference. If the bit is `0`, the body resides in the same cell as the message. | Validators rewrite the above values (including src), excluding the State Init and the Message Body bits. @@ -440,9 +440,9 @@ Next, we’ll prepare a message to send Toncoins to another wallet v3. For examp import { beginCell } from "@ton/core"; let internalMessageBody = beginCell() - .storeUint(0, 32) // write 32 zero bits to indicate that a text comment will follow - .storeStringTail("Hello, TON!") // write our text comment - .endCell(); + .storeUint(0, 32) // write 32 zero bits to indicate that a text comment will follow + .storeStringTail("Hello, TON!") // write our text comment + .endCell(); ``` @@ -475,22 +475,22 @@ import { toNano, Address } from "@ton/ton"; const walletAddress = Address.parse("put your wallet address"); let internalMessage = beginCell() - .storeUint(0, 1) // indicate that it is an internal message -> int_msg_info$0 - .storeBit(1) // IHR Disabled - .storeBit(1) // bounce - .storeBit(0) // bounced - .storeUint(0, 2) // src -> addr_none - .storeAddress(walletAddress) - .storeCoins(toNano("0.2")) // amount - .storeBit(0) // Extra currency - .storeCoins(0) // IHR Fee - .storeCoins(0) // Forwarding Fee - .storeUint(0, 64) // Logical time of creation - .storeUint(0, 32) // UNIX time of creation - .storeBit(0) // No State Init - .storeBit(1) // We store Message Body as a reference - .storeRef(internalMessageBody) // Store Message Body as a reference - .endCell(); + .storeUint(0, 1) // indicate that it is an internal message -> int_msg_info$0 + .storeBit(1) // IHR Disabled + .storeBit(1) // bounce + .storeBit(0) // bounced + .storeUint(0, 2) // src -> addr_none + .storeAddress(walletAddress) + .storeCoins(toNano("0.2")) // amount + .storeBit(0) // Extra currency + .storeCoins(0) // IHR Fee + .storeCoins(0) // Forwarding Fee + .storeUint(0, 64) // Logical time of creation + .storeUint(0, 32) // UNIX time of creation + .storeBit(0) // No State Init + .storeBit(1) // We store Message Body as a reference + .storeRef(internalMessageBody) // Store Message Body as a reference + .endCell(); ``` @@ -609,18 +609,18 @@ To proceed, we need to send the `seqno`, `keys`, and `internal message`. Next, w import { sign } from "@ton/crypto"; let toSign = beginCell() - .storeUint(698983191, 32) // subwallet_id | We consider this further - .storeUint(Math.floor(Date.now() / 1e3) + 60, 32) // Message expiration time, +60 = 1 minute - .storeUint(seqno, 32) // store seqno - .storeUint(3, 8) // store mode of our internal message - .storeRef(internalMessage); // store our internalMessage as a reference + .storeUint(698983191, 32) // subwallet_id | We consider this further + .storeUint(Math.floor(Date.now() / 1e3) + 60, 32) // Message expiration time, +60 = 1 minute + .storeUint(seqno, 32) // store seqno + .storeUint(3, 8) // store mode of our internal message + .storeRef(internalMessage); // store our internalMessage as a reference let signature = sign(toSign.endCell().hash(), keyPair.secretKey); // get the hash of our message to the wallet smart contract and sign it to get signature let body = beginCell() - .storeBuffer(signature) // store signature - .storeBuilder(toSign) // store our message - .endCell(); + .storeBuffer(signature) // store signature + .storeBuilder(toSign) // store our message + .endCell(); ``` @@ -664,14 +664,14 @@ To deliver an internal message to the blockchain from the outside world, it must ```js let externalMessage = beginCell() - .storeUint(0b10, 2) // 0b10 -> 10 in binary - .storeUint(0, 2) // src -> addr_none - .storeAddress(walletAddress) // Destination address - .storeCoins(0) // Import Fee - .storeBit(0) // No State Init - .storeBit(1) // We store Message Body as a reference - .storeRef(body) // Store Message Body as a reference - .endCell(); + .storeUint(0b10, 2) // 0b10 -> 10 in binary + .storeUint(0, 2) // src -> addr_none + .storeAddress(walletAddress) // Destination address + .storeCoins(0) // Import Fee + .storeBit(0) // No State Init + .storeBit(1) // We store Message Body as a reference + .storeRef(body) // Store Message Body as a reference + .endCell(); ``` @@ -695,9 +695,9 @@ externalMessage := cell.BeginCell(). | Option | Explanation | | :----------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | | Src | The sender address. Since an incoming external message cannot have a sender, there will always be 2 zero bits (an addr_none [TL-B](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L100)). | -| Import Fee | The fee for importing incoming external messages. | -| State Init | Unlike the Internal Message, the State Init within the external message is needed **to deploy a contract from the outside world**. The State Init used with the Internal Message allows one contract to deploy another. | -| Message Body | The message must be sent to the contract for processing. | +| Import Fee | The fee for importing incoming external messages. | +| State Init | Unlike the Internal Message, the State Init within the external message is needed **to deploy a contract from the outside world**. The State Init used with the Internal Message allows one contract to deploy another. | +| Message Body | The message must be sent to the contract for processing. | :::tip 0b10 0b10 (b - binary) denotes a binary record. Two bits are stored in this process: `1` and `0`. Thus, we specify that it's `ext_in_msg_info$10`. @@ -745,15 +745,15 @@ As a result, we got the output of our BOC in the console, and the message was se ## 👛 Deploying a wallet -We have learned the basics of creating messages, which will now help deploy the wallet. In the past, we have deployed wallet via the wallet app, but in this case, we’ll need to deploy our wallet manually. +We’ve covered the basics of creating messages to help us deploy a wallet. Previously, we deployed wallets using wallet apps, but we’ll deploy our wallet manually this time. -In this section, we’ll review how to create a wallet (wallet v3) from scratch. You’ll learn how to compile the code for a wallet smart contract, generate a mnemonic phrase, receive a wallet address, and deploy a wallet using external messages and State Init (state initialization). +In this section, we’ll walk through creating a wallet (wallet v3) from scratch. You’ll learn how to compile the wallet smart contract code, generate a mnemonic phrase, obtain a wallet address, and deploy the wallet using external messages and State Init (state initialization). -### Generating a Mnemonic +### Generating a mnemonic -The first thing needed to create a wallet correctly is to retrieve a `private` and `public` key. To accomplish this task, generating a mnemonic seed phrase and extracting private and public keys using cryptographic libraries is necessary. +The first step in creating a wallet is generating a `private` and `public` key. We’ll generate a mnemonic seed phrase and extract the keys using cryptographic libraries. -This is accomplished as follows: +Here’s how to accomplish this: @@ -784,42 +784,42 @@ import ( // mnemonic := strings.Split("put your mnemonic", " ") // get our mnemonic as array mnemonic := wallet.NewSeed() // get new mnemonic -// The following three lines will extract the private key using the mnemonic phrase. We will not go into cryptographic details. It has all been implemented in the tonutils-go library, but it immediately returns the finished object of the wallet with the address and ready methods. So we’ll have to write the lines to get the key separately. Goland IDE will automatically import all required libraries (crypto, pbkdf2 and others). +// The following three lines will extract the private key using the mnemonic phrase. We will not go into cryptographic details. It has all been implemented in the tonutils-go library, but it immediately returns the finished wallet object with the address and ready methods. So we’ll have to write the lines to get the key separately. Goland IDE will automatically import all required libraries (crypto, pbkdf2, and others). mac := hmac.New(sha512.New, []byte(strings.Join(mnemonic, " "))) hash := mac.Sum(nil) -k := pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries "TON default seed" is used as salt when getting keys +k := pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries, "TON default seed" is used as salt when getting keys // 32 is a key len privateKey := ed25519.NewKeyFromSeed(k) // get private key publicKey := privateKey.Public().(ed25519.PublicKey) // get public key from private key -log.Println(publicKey) // print publicKey so that at this stage the compiler does not complain that we do not use our variable +log.Println(publicKey) // print publicKey so that at this stage, the compiler does not complain that we do not use our variable log.Println(mnemonic) // if we want, we can print our mnemonic ``` -The private key is needed to sign messages and the public key is stored in the wallet’s smart contract. +The private key is needed to sign messages, and the public key is stored in the wallet’s smart contract. :::danger IMPORTANT -It is necessary to output the generated mnemonic seed phrase to the console then save and use it (as detailed in the previous section) in order to use the same key pair each time the wallet’s code is run. +Make sure to output the generated mnemonic seed phrase to the console, save it, and use it (as detailed in the previous section) to ensure the same key pair is used each time the wallet’s code is run. ::: ### Subwallet IDs -One of the most notable benefits of wallets being smart contracts is the ability to create **a vast number of wallets** using just one private key. This is because the addresses of smart contracts on TON Blockchain are computed using several factors including the `stateInit`. The stateInit contains the `code` and `initial data`, which is stored in the blockchain’s smart contract storage. +One of the most notable benefits of wallets being smart contracts is the ability to create **a vast number of wallets** using just one private key. This is because the addresses of smart contracts on TON Blockchain are computed using several factors, including the `stateInit`. The stateInit contains the `code` and `initial data`, which is stored in the blockchain’s smart contract storage. -By changing just one bit within the stateInit, a different address can be generated. That is why the `subwallet_id` was initially created. The `subwallet_id` is stored in the contract storage and it can be used to create many different wallets (with different subwallet IDs) with one private key. This functionality can be very useful when integrating various wallet types with centralized service such as exchanges. +Changing just one bit within the stateInit can generate a different address. That is why the `subwallet_id` was initially created. The `subwallet_id` is stored in the contract storage and can be used to create many different wallets (with different subwallet IDs) with one private key. This functionality can be handy when integrating various wallet types with centralized services such as exchanges. -The default subwallet_id value is `698983191` according to the [line of code](https://github.com/ton-blockchain/ton/blob/4b940f8bad9c2d3bf44f196f6995963c7cee9cc3/tonlib/tonlib/TonlibClient.cpp#L2420) below taken from the TON Blockchain’s source code: +The default `subwallet_id` value is `698983191`, as per the [line of code](https://github.com/ton-blockchain/ton/blob/4b940f8bad9c2d3bf44f196f6995963c7cee9cc3/tonlib/tonlib/TonlibClient.cpp#L2420) below taken from the TON Blockchain’s source code: ```cpp res.wallet_id = td::as(res.config.zero_state_id.root_hash.as_slice().data()); ``` -It is possible to retrieve genesis block information (zero_state) from the [configuration file](https://ton.org/global-config.json). Understanding the complexities and details of this is not necessary but it's important to remember that the default value of the `subwallet_id` is `698983191`. +It is possible to retrieve genesis block information (zero_state) from the [configuration file](https://ton.org/global-config.json). Understanding the complexities and details of this is not necessary, but it's important to remember that the default value of the `subwallet_id` is `698983191`. -Each wallet contract checks the subwallet_id field for external messages to avoid instances when requests were sent to wallet with another ID: +Each wallet contract checks the `subwallet_id` field for external messages to avoid instances where requests are sent to a wallet with another ID: ```func var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32)); @@ -848,21 +848,19 @@ var subWallet uint64 = 698983191 ### Compiling wallet code -Now that we have the private and public keys and the subwallet_id clearly defined we need to compile the wallet code. To accomplish this, we’ll use the [wallet v3 code](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/wallet3-code.fc) from the official repository. +Now that the private and public keys and the `subwallet_id` are clearly defined, we need to compile the wallet code. We’ll use the [wallet v3 code](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/wallet3-code.fc) from the official repository. -To compile wallet code it is necessary to use the [@ton-community/func-js](https://github.com/ton-community/func-js) library. -Using this library it allows us to compile FunC code and retrieve a cell containing the code. To get started, it is necessary to install the library and save (--save) it to the `package.json` as follows: +The [@ton-community/func-js](https://github.com/ton-community/func-js) library is necessary to compile wallet code. This library allows us to compile FunC code and retrieve a cell containing the code. To get started, install the library and save it to the `package.json` as follows: ```bash npm i --save @ton-community/func-js ``` -We’ll only use JavaScript to compile code, as the libraries for compiling code are JavaScript based. -However, after compiling is finalized, as long as we have the **base64 output** of our cell, it is possible to use this compiled code in languages such as Go and others. +We’ll only use JavaScript to compile code, as the libraries for compiling code are JavaScript-based. However, after compiling is finalized, as long as we have our cell's **base64 output**, it is possible to use this compiled code in languages such as Go and others. -First, we need to create two files: `wallet_v3.fc` and `stdlib.fc`. The compiler works with the stdlib.fc library. All necessary and basic functions, which correspond with the `asm` instructions were created in the library. The stdlib.fc file can be downloaded [here](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/stdlib.fc). In the `wallet_v3.fc` file it is necessary to copy the code above. +First, we need to create two files: `wallet_v3.fc` and `stdlib.fc`. The compiler relies on the `stdlib.fc` library, which contains all the necessary basic functions corresponding to `asm` instructions. You can download the `stdlib.fc` file [here](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/stdlib.fc). For the `wallet_v3.fc` file, copy the code from the repository. -Now we have the following structure for the project we are creating: +Now, we have the following structure for the project we are creating: ``` . @@ -877,7 +875,7 @@ Now we have the following structure for the project we are creating: ``` :::info -It’s fine if your IDE plugin conflicts with the `() set_seed(int) impure asm "SETRAND";` in the `stdlib.fc` file. +It’s OK if your IDE plugin conflicts with the `() set_seed(int) impure asm "SETRAND";` in the `stdlib.fc` file. ::: Remember to add the following line to the beginning of the `wallet_v3.fc` file to indicate that the functions from the stdlib will be used below: @@ -886,7 +884,7 @@ Remember to add the following line to the beginning of the `wallet_v3.fc` file t #include "stdlib.fc"; ``` -Now let’s write code to compile our smart contract and run it using the `npm run start:dev`: +Now let’s write code to compile our smart contract and run it using `npm run start:dev`: ```js import { compileFunc } from "@ton-community/func-js"; @@ -910,9 +908,9 @@ if (result.status === "error") { const codeCell = Cell.fromBoc(Buffer.from(result.codeBoc, "base64"))[0]; // get buffer from base64 encoded BOC and get cell from this buffer -// now we have base64 encoded BOC with compiled code in result.codeBoc +// now we have base64 encoded BOC with compiled code in the result.codeBoc console.log("Code BOC: " + result.codeBoc); -console.log("\nHash: " + codeCell.hash().toString("base64")); // get the hash of cell and convert in to base64 encoded string. We will need it further +console.log("\nHash: " + codeCell.hash().toString("base64")); // get the hash of cell and convert it to base64 encoded string. We will need it further ``` The result will be the following output in the terminal: @@ -923,7 +921,7 @@ Code BOC: te6ccgEBCAEAhgABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQCW8oMI1xgg0x/TH9MfAvgju/ Hash: idlku00WfSC36ujyK2JVT92sMBEpCNRUXOGO4sJVBPA= ``` -Once this is completed it is possible to retrieve the same cell (using the base64 encoded output) with our wallet code using other libraries and languages: +Once this process is complete, you can retrieve the same cell (using the base64 encoded output) containing our wallet code using other libraries and languages: @@ -941,7 +939,7 @@ if err != nil { // check if there are any error panic(err) } -log.Println("Hash:", base64.StdEncoding.EncodeToString(codeCell.Hash())) // get the hash of our cell, encode it to base64 because it has []byte type and output to the terminal +log.Println("Hash:", base64.StdEncoding.EncodeToString(codeCell.Hash())) // get the hash of our cell, encode it to base64 because it has []byte type, and output to the terminal ``` @@ -953,21 +951,22 @@ The result will be the following output in the terminal: idlku00WfSC36ujyK2JVT92sMBEpCNRUXOGO4sJVBPA= ``` -After the above processes are complete it is confirmed that the correct code is being used within our cell because the hashes match. +After the above processes are complete, the hashes match, confirming that the correct code is used within our cell. -### Creating the State Init for Deployment +### Creating the state init for deployment -Before building a message it is important to understand what a State Init is. First let’s go through the [TL-B scheme](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L141-L143): +Before building a message, it is essential to understand what a State Init is. First, let’s go through the [TL-B scheme](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L141-L143): -| Option | Explanation | -| :---------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -| split_depth | This option is intended for highly loaded smart contracts that can be split and located on several [shardchains](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains#many-accountchains-shards). More information detailing how this works can be found in the [tblkch.pdf](https://ton.org/tblkch.pdf) (4.1.6). Only a `0` bit is stored since it is being used only within a wallet smart contract. | -| special | Used for TicTok. These smart contracts are automatically called for each block and are not needed for regular smart contracts. Information about this can be found in [this section](/v3/documentation/data-formats/tlb/transaction-layout#tick-tock) or in [tblkch.pdf](https://ton.org/tblkch.pdf) (4.1.6). Only a `0` bit is stored within this specification because we do not need such a function. | -| code | `1` bit means the presence of the smart contract code as a reference. | -| data | `1` bit means the presence of the smart contract data as a reference. | -| library | A library that operates on the [masterchain](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains#masterchain-blockchain-of-blockchains) and can be used by different smart contracts. This will not be used for wallet, so its bit is set to `0`. Information about this can be found in [tblkch.pdf](https://ton.org/tblkch.pdf) (1.8.4). | +| Option | Explanation | +| :---------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| split_depth | This option is designed for highly loaded smart contracts that can be split and distributed across multiple [shardchains](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains#many-accountchains-shards). For more details on how this works, refer to the [tblkch.pdf](https://ton.org/tblkch.pdf) (section 4.1.6). Since this feature is not needed for wallet smart contracts, only a `0` bit is stored. | +| special | This option is used for **TicTok** smart contracts, which are automatically triggered for each block. Regular smart contracts, such as wallets, do not require this functionality. For more details, refer to [this section](/v3/documentation/data-formats/tlb/transaction-layout#tick-tock) or the [tblkch.pdf](https://ton.org/tblkch.pdf) (section 4.1.6). Since this feature is unnecessary for our use case, only a `0` bit is stored. | +| | +| code | `1` bit means the presence of the smart contract code as a reference. | +| data | `1` bit means the presence of the smart contract data as a reference. | +| library | This option refers to a library that operates on the [masterchain](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains#masterchain-blockchain-of-blockchains) and can be shared across multiple smart contracts. Since wallets do not require this functionality, its bit is set to `0`. For more information, refer to [tblkch.pdf](https://ton.org/tblkch.pdf) (section 1.8.4). | -Next we’ll prepare the `initial data`, which will be present in our contract’s storage immediately after deployment: +Next, we’ll prepare the `initial data`, which will be present in our contract’s storage immediately after deployment: @@ -996,7 +995,7 @@ dataCell := cell.BeginCell(). -At this stage, both the contract `code` and its `initial data` is present. With this data, we can produce our **wallet address**. The address of the wallet depends on the State Init, which includes the code and initial data. +The contract `code` and its `initial data` are present at this stage. With this data, we can produce our **wallet address**. The wallet's address depends on the State Init, which includes the code and initial data. @@ -1043,15 +1042,15 @@ log.Println("Contract address:", contractAddress.String()) // Output contract ad -Using the State Init, we can now build the message and send it to the blockchain. +We can build and send the message to the blockchain using the State Init. :::warning -To carry out this process, **a minimum wallet balance of 0.1 TON** is required (the balance can be less, but this amount is guaranteed to be sufficient). To accomplish this, we’ll need to run the code mentioned earlier in the tutorial, obtain the correct wallet address, and send 0.1 TON to this address. Alternatively, you can send this sum manually via your wallet app before sending the deployment message itself. +To carry out this process, **a minimum wallet balance of 0.1 TON** is required (the balance can be less, but this amount is guaranteed sufficient). To accomplish this, we’ll need to run the code mentioned earlier in the tutorial, obtain the correct wallet address, and send 0.1 TON to this address. Alternatively, you can send this sum manually via your wallet app before sending the deployment message. -Deployment by external messages is presented here mostly for educational purposes; in practice, it's much more convenient to [deploy smart contracts via Wallets](/v3/guidelines/smart-contracts/howto/wallet#contract-deployment-via-wallet), which will be described later. +Deployment by external messages is presented here primarily for educational purposes; in practice, it's much more convenient to [deploy smart contracts via Wallets](/v3/guidelines/smart-contracts/howto/wallet#contract-deployment-via-wallet), which will be described later. ::: -Let’s start with building the message similar to the one we built **in the previous section**: +Let’s start with building a message similar to the one we built **in the previous section**: @@ -1071,7 +1070,7 @@ const internalMessage = beginCell() Address.parse("put your first wallet address from were you sent 0.1 TON") ) .storeCoins(toNano("0.03")) - .storeUint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1) // We store 1 that means we have body as a reference + .storeUint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1) // We store 1, which means we have a body as a reference .storeRef(internalMessageBody) .endCell(); @@ -1079,7 +1078,7 @@ const internalMessage = beginCell() const toSign = beginCell() .storeUint(subWallet, 32) .storeUint(Math.floor(Date.now() / 1e3) + 60, 32) - .storeUint(0, 32) // We put seqno = 0, because after deploying wallet will store 0 as seqno + .storeUint(0, 32) // We put seqno = 0 because after deploying wallet will store 0 as seqno .storeUint(3, 8) .storeRef(internalMessage); @@ -1103,9 +1102,9 @@ internalMessageBody := cell.BeginCell(). internalMessage := cell.BeginCell(). MustStoreUInt(0x10, 6). // no bounce - MustStoreAddr(address.MustParseAddr("put your first wallet address from were you sent 0.1 TON")). + MustStoreAddr(address.MustParseAddr("put your first wallet address from where you sent 0.1 TON")). MustStoreBigCoins(tlb.MustFromTON("0.03").NanoTON()). - MustStoreUInt(1, 1 + 4 + 4 + 64 + 32 + 1 + 1). // We store 1 that means we have body as a reference + MustStoreUInt(1, 1 + 4 + 4 + 64 + 32 + 1 + 1). // We store 1, which means we have a body as a reference MustStoreRef(internalMessageBody). EndCell() @@ -1113,7 +1112,7 @@ internalMessage := cell.BeginCell(). toSign := cell.BeginCell(). MustStoreUInt(subWallet, 32). MustStoreUInt(uint64(time.Now().UTC().Unix()+60), 32). - MustStoreUInt(0, 32). // We put seqno = 0, because after deploying wallet will store 0 as seqno + MustStoreUInt(0, 32). // We put seqno = 0 because after deploying, the wallet will store 0 as seqno MustStoreUInt(3, 8). MustStoreRef(internalMessage) @@ -1121,24 +1120,24 @@ signature := ed25519.Sign(privateKey, toSign.EndCell().Hash()) body := cell.BeginCell(). MustStoreSlice(signature, 512). MustStoreBuilder(toSign). - EndCell() + EndCell() ``` -After this is completed the result is the correct State Init and Message Body. +Once this process is complete, the result is a properly constructed State Init and Message Body. -### Sending An External Message +### Sending an external message -The **main difference** will be in the presence of the external message, because the State Init is stored to help carry out correct contract deployment. Since the contract does not have its own code yet, it cannot process any internal messages. Therefore, next we send its code and the initial data **after it is successfully deployed so it can process our message** with "Hello, TON!" comment: +The **main difference** lies in including the external message, as the State Init is stored to ensure proper contract deployment. Since the contract doesn’t yet have its code, it cannot process internal messages. Therefore, we send its code and initial data, enabling it to process our message with the "Hello, TON!" comment **after successful deployment**. ```js const externalMessage = beginCell() - .storeUint(0b10, 2) // indicate that it is an incoming external message + .storeUint(0b10, 2) // indicates that it is an incoming external message .storeUint(0, 2) // src -> addr_none .storeAddress(contractAddress) .storeCoins(0) // Import fee @@ -1155,7 +1154,7 @@ const externalMessage = beginCell() ```go externalMessage := cell.BeginCell(). - MustStoreUInt(0b10, 2). // indicate that it is an incoming external message + MustStoreUInt(0b10, 2). // indicates that it is an incoming external message MustStoreUInt(0, 2). // src -> addr_none MustStoreAddr(contractAddress). MustStoreCoins(0). // Import fee @@ -1216,23 +1215,23 @@ if err != nil { -Note that we have sent an internal message using mode `3`. If it is necessary to repeat the deployment of the same wallet, **the smart contract can be destroyed**. To accomplish this, set the mode correctly by adding 128 (take the entire balance of the smart contract) + 32 (destroy the smart contract) which will = `160` to retrieve the remaining TON balance and deploy the wallet again. +Note that we sent an internal message using mode `3`. If you need to redeploy the same wallet, **the smart contract can be destroyed**. To do this, set the mode to `160` by adding 128 (take the entire balance of the smart contract) + 32 (destroy the smart contract). This will retrieve the remaining TON balance and allow you to deploy the wallet again. -It's important to note that for each new transaction the **seqno will need to be increased by one**. +Remember that for each new transaction, the **seqno must be incremented by one**. :::info The contract code we used is [verified](https://tonscan.org/tx/BL9T1i5DjX1JRLUn4z9JOgOWRKWQ80pSNevis26hGvc=), so you can see an example [here](https://tonscan.org/address/EQDBjzo_iQCZh3bZSxFnK9ue4hLTOKgsCNKfC8LOUM4SlSCX#source). ::: -## 💸 Working With Wallet Smart Contracts +## 💸 Working with wallet smart contracts -After completing the first half of this tutorial we’re now much more familiar with wallet smart contracts and how they are developed and used. We learned how to deploy and destroy them and send messages without depending on pre-configured library functions. To apply more of what we learned above, in the next section, we’ll focus on building and sending more complex messages. +After completing the first half of this tutorial, we’ve gained a deeper understanding of wallet smart contracts, including how they are developed and used. We’ve also learned how to deploy and destroy them and how to send messages without relying on pre-configured library functions. The next section will focus on building and sending more complex messages to apply what we've learned further. -### Sending Multiple Messages Simultaneously +### Sending multiple messages simultaneously -As you may already know, [one cell can store up to 1023 bits of data and up to 4 references](/v3/documentation/data-formats/tlb/cell-boc#cell) to other cells. In the first section of this tutorial we detailed how internal messages are delivered in a ‘whole’ loop as a link and sent. This means it is possible to **store up to 4 internal messages inside the external** message. This allows four messages to be sent at the same time. +As you already know, [a single cell can store up to 1023 bits of data and up to 4 references](/v3/documentation/data-formats/tlb/cell-boc#cell) to other cells. In the first section of this tutorial, we explained how internal messages are delivered in a ‘whole’ loop as a link and sent. This means it’s possible to **store up to 4 internal messages within an external message**, allowing four messages to be sent simultaneously. -To accomplish this, it is necessary to create 4 different internal messages. We can do this manually or through a `loop`. We need to define 3 arrays: array of TON amount, array of comments, array of messages. For messages, we need to prepare another one array - internalMessages. +To accomplish this, we need to create four different internal messages. We can do this manually or through a `loop`. We need to define three arrays: an array of TON amount, an array of comments, and an array of messages. For messages, we need to prepare another array - internalMessages. @@ -1285,7 +1284,7 @@ var internalMessages [len(internalMessagesAmount)]*cell.Cell // array for our in -[Sending mode](/v3/documentation/smart-contracts/message-management/sending-messages#message-modes) for all messages is set to `mode 3`. However, if different modes are required an array can be created to fulfill different purposes. +[Sending mode](/v3/documentation/smart-contracts/message-management/sending-messages#message-modes) for all messages is set to `mode 3`. However, an array can be created to fulfill different purposes if different modes are required. @@ -1303,11 +1302,8 @@ for (let index = 0; index < internalMessagesAmount.length; index++) { .storeUint(0, 1 + 4 + 4 + 64 + 32 + 1); /* - At this stage, it is not clear if we will have a message body. - So put a bit only for stateInit, and if we have a comment, in means - we have a body message. In that case, set the bit to 1 and store the - body as a reference. - */ +It’s unclear whether we’ll have a message body at this stage. Therefore, we’ll only set a bit for the `stateInit`. If we include a comment, it means we have a message body. In that case, set the bit to `1` and store the body as a reference. + */ if (internalMessagesComment[index] != "") { internalMessage.storeBit(1); // we store Message Body as a reference @@ -1320,10 +1316,10 @@ for (let index = 0; index < internalMessagesAmount.length; index++) { internalMessage.storeRef(internalMessageBody); } else internalMessage.storeBit(0); /* - Since we do not have a message body, we indicate that - the message body is in this message, but do not write it, - which means it is absent. In that case, just set the bit to 0. - */ + Since we do not have a message body, we indicate that + the message body is in this message but do not write it, + which means it is absent. In that case, just set the bit to 0. + */ internalMessages.push(internalMessage.endCell()); } @@ -1348,11 +1344,8 @@ for i := 0; i < len(internalMessagesAmount); i++ { MustStoreUInt(0, 1+4+4+64+32+1) /* - At this stage, it is not clear if we will have a message body. - So put a bit only for stateInit, and if we have a comment, in means - we have a body message. In that case, set the bit to 1 and store the - body as a reference. - */ +It’s unclear whether we’ll have a message body at this stage. Therefore, we’ll only set a bit for the `stateInit`. If we include a comment, it means we have a message body. In that case, set the bit to `1` and store the body as a reference. + */ if internalMessagesComment[i] != "" { internalMessage.MustStoreBoolBit(true) // we store Message Body as a reference @@ -1363,14 +1356,14 @@ for i := 0; i < len(internalMessagesAmount); i++ { EndCell() internalMessage.MustStoreRef(internalMessageBody) - } else { + } else { /* - Since we do not have a message body, we indicate that - the message body is in this message, but do not write it, - which means it is absent. In that case, just set the bit to 0. - */ + Since we do not have a message body, we indicate that + the message body is in this message but do not write it, + which means it is absent. In that case, just set the bit to 0. + */ internalMessage.MustStoreBoolBit(false) - } + } internalMessages[i] = internalMessage.EndCell() } ``` @@ -1378,7 +1371,7 @@ for i := 0; i < len(internalMessagesAmount); i++ { -Now let's use our knowledge from [chapter two](/v3/guidelines/smart-contracts/howto/wallet#-deploying-a-wallet) to build a message for our wallet that can send 4 messages simultaneously: +Now let's use our knowledge from [chapter two](/v3/guidelines/smart-contracts/howto/wallet#-deploying-a-wallet) to build a message for our wallet that can send four messages simultaneously: @@ -1412,16 +1405,16 @@ let toSign = beginCell() ```go import ( - "context" - "crypto/ed25519" - "crypto/hmac" - "crypto/sha512" - "github.com/xssnick/tonutils-go/liteclient" - "github.com/xssnick/tonutils-go/ton" - "golang.org/x/crypto/pbkdf2" - "log" - "strings" - "time" + "context" + "crypto/ed25519" + "crypto/hmac" + "crypto/sha512" + "github.com/xssnick/tonutils-go/liteclient" + "github.com/xssnick/tonutils-go/ton" + "golang.org/x/crypto/pbkdf2" + "log" + "strings" + "time" ) walletAddress := address.MustParseAddr("put your wallet address") @@ -1442,11 +1435,11 @@ mnemonic := strings.Split("put your mnemonic", " ") // word1 word2 word3 // all required libraries (crypto, pbkdf2 and others). mac := hmac.New(sha512.New, []byte(strings.Join(mnemonic, " "))) hash := mac.Sum(nil) -k := pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries "TON default seed" is used as salt when getting keys +k := pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries, "TON default seed" is used as salt when getting keys // 32 is a key len privateKey := ed25519.NewKeyFromSeed(k) // get private key -block, err := client.CurrentMasterchainInfo(context.Background()) // get current block, we will need it in requests to LiteServer +block, err := client.CurrentMasterchainInfo(context.Background()) // get the current block, we will need it in requests to LiteServer if err != nil { log.Fatalln("CurrentMasterchainInfo err:", err.Error()) return @@ -1469,7 +1462,7 @@ toSign := cell.BeginCell(). -Next, we’ll add our messages that we built earlier in the loop: +Next, we’ll add the messages that we built earlier in the loop: @@ -1487,16 +1480,16 @@ for (let index = 0; index < internalMessages.length; index++) { ```go for i := 0; i < len(internalMessages); i++ { - internalMessage := internalMessages[i] - toSign.MustStoreUInt(3, 8) // store mode of our internal message - toSign.MustStoreRef(internalMessage) // store our internalMessage as a reference + internalMessage := internalMessages[i] + toSign.MustStoreUInt(3, 8) // store mode of our internal message + toSign.MustStoreRef(internalMessage) // store our internalMessage as a reference } ``` -Now that the above processes are complete, let’s **sign** our message, **build an external message** (as outlined in previous sections of this tutorial) and **send it** to the blockchain: +Now that the above processes are complete, let’s **sign** our message, **build an external message** (as outlined in previous sections of this tutorial), and **send it** to the blockchain: @@ -1504,7 +1497,7 @@ Now that the above processes are complete, let’s **sign** our message, **build ```js import { sign } from "@ton/crypto"; -let signature = sign(toSign.endCell().hash(), keyPair.secretKey); // get the hash of our message to wallet smart contract and sign it to get signature +let signature = sign(toSign.endCell().hash(), keyPair.secretKey); // get the hash of our message to the wallet smart contract and sign it to get signature let body = beginCell() .storeBuffer(signature) // store signature @@ -1532,7 +1525,7 @@ import ( "github.com/xssnick/tonutils-go/tl" ) -signature := ed25519.Sign(privateKey, toSign.EndCell().Hash()) // get the hash of our message to wallet smart contract and sign it to get signature +signature := ed25519.Sign(privateKey, toSign.EndCell().Hash()) // get the hash of our message to the wallet smart contract and sign it to get the signature body := cell.BeginCell(). MustStoreSlice(signature, 512). // store signature @@ -1562,28 +1555,28 @@ if err != nil { :::info Connection error -If an error related to the lite-server connection (Golang) occurs, the code must be run until the message can be sent. This is because the tonutils-go library uses several different lite-servers through the global configuration that have been specified in the code. However, not all lite-servers can accept our connection. +If an error related to the lite-server connection (in Golang) occurs, you may need to run the code repeatedly until the message is successfully sent. This happens because the `tonutils-go` library uses multiple lite-servers from the global configuration specified in the code. However, not all lite-servers may accept the connection. ::: -After this process is completed it is possible to use a TON blockchain explorer to verify that the wallet sent four messages to the addresses previously specified. +After completing this process, you can use a TON blockchain explorer to verify that the wallet sent four messages to the specified addresses. -### NFT Transfers +### NFT transfers -In addition to regular messages, users often send NFTs to each other. Unfortunately, not all libraries contain methods that are tailored for use with this type of smart contract. Therefore, it is necessary to create code that will allow us to build a message for sending NFTs. First, let's become more familiar with the TON NFT [standard](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md). +In addition to regular messages, users often send NFTs to each other. Unfortunately, not all libraries include methods specifically designed for interacting with this type of smart contract. As a result, we need to write code that allows us to construct messages for sending NFTs. First, let’s familiarize ourselves with the TON NFT [standard](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md). -Especially, we need to understand TL-B for [NFT Transfers](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md#1-transfer) in details. +Specifically, we need to thoroughly understand the TL-B schema for [NFT Transfers](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md#1-transfer). -- `query_id`: Query ID has no value in terms of message processing. The NFT contract doesn't validate it; it only reads it. This value can be useful when a service wants to assign a specific query ID to each of its messages for identification purposes. Therefore, we will set it to 0. +- `query_id`: Query ID has no value in message processing. The NFT contract doesn't validate it; it only reads it. This value can be helpful when a service wants to assign a specific query ID to each message for identification purposes. Therefore, we will set it to 0. -- `response_destination`: After processing the ownership change message there will be extra TON. They will be sent to this address, if specified, otherwise remain on the NFT balance. +- `response_destination`: After processing the ownership change message, there will be extra TONs. If specified, they will be sent to this address; otherwise, they will remain on the NFT balance. -- `custom_payload`: The custom_payload is needed to carry out specific tasks and is not used with ordinary NFTs. +- `custom_payload`: The custom_payload is used for specific tasks and is not typically required for ordinary NFTs. -- `forward_amount`: If the forward_amount isn’t zero, the specified TON amount will be sent to the new owner. That way the new owner will be notified that they received something. +- `forward_amount`: If the forward_amount isn’t zero, the specified TON amount will be sent to the new owner, who will then be notified that they received something. -- `forward_payload`: The forward_payload is additional data that can be sent to the new owner together with the forward_amount. For example, using forward_payload allows users to [add a comment during the transfer of the NFT](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md#forward_payload-format), as shown in the tutorial earlier. However, although the forward_payload is written within TON’s NFT standard, blockchain explorers do not fully support displaying various details. The same problem also exists when displaying Jettons. +- `forward_payload`: The forward_payload is additional data that can be sent to the new owner along with the `forward_amount`. For example, the forward_payload allows users to [add a comment during the transfer of an NFT](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md#forward_payload-format), as demonstrated earlier in the tutorial. However, despite being part of TON’s NFT standard, blockchain explorers do not fully support displaying these details. A similar issue exists when displaying Jettons. -Now let's build the message itself: +Now, let's build the message itself: @@ -1595,12 +1588,12 @@ const destinationAddress = Address.parse( "put your wallet where you want to send NFT" ); const walletAddress = Address.parse( - "put your wallet which is the owner of NFT" + "put your wallet, which is the owner of NFT." ); const nftAddress = Address.parse("put your nft address"); // We can add a comment, but it will not be displayed in the explorers, -// as it is not supported by them at the time of writing the tutorial. +// as they do not support it at the time of writing the tutorial. const forwardPayload = beginCell() .storeUint(0, 32) .storeStringTail("Hello, TON!") @@ -1621,7 +1614,7 @@ const internalMessage = beginCell() .storeUint(0x18, 6) // bounce .storeAddress(nftAddress) .storeCoins(toNano("0.05")) - .storeUint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1) // We store 1 that means we have body as a reference + .storeUint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1) // We store 1, which means we have the body as a reference .storeRef(transferNftBody) .endCell(); ``` @@ -1641,7 +1634,7 @@ walletAddress := address.MustParseAddr("put your wallet which is the owner of NF nftAddress := address.MustParseAddr("put your nft address") // We can add a comment, but it will not be displayed in the explorers, -// as it is not supported by them at the time of writing the tutorial. +// as they do not support it at the time of writing the tutorial. forwardPayload := cell.BeginCell(). MustStoreUInt(0, 32). MustStoreStringSnake("Hello, TON!"). @@ -1662,7 +1655,7 @@ internalMessage := cell.BeginCell(). MustStoreUInt(0x18, 6). // bounce MustStoreAddr(nftAddress). MustStoreBigCoins(tlb.MustFromTON("0.05").NanoTON()). - MustStoreUInt(1, 1 + 4 + 4 + 64 + 32 + 1 + 1). // We store 1 that means we have body as a reference + MustStoreUInt(1, 1 + 4 + 4 + 64 + 32 + 1 + 1). // We store 1, which means we have the body as a reference MustStoreRef(transferNftBody). EndCell() ``` @@ -1671,32 +1664,32 @@ internalMessage := cell.BeginCell(). The NFT transfer opcode comes from [the same standard](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md#tl-b-schema). -Now let's complete the message, as is laid out in the previous sections of this tutorial. The correct code needed to complete the message is found in the [GitHub repository](/v3/guidelines/smart-contracts/howto/wallet#-source-code). +Now, let's complete the message as laid out in this tutorial's previous sections. The correct code to complete the message is in the [GitHub repository](/v3/guidelines/smart-contracts/howto/wallet#-source-code). -The same procedure can be completed with Jettons. To conduct this process, read the TL-B [standart](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md) for jettons transfer. To this point specifically, a small difference between NFT and Jettons transfers exists. +The same procedure can also be applied to Jettons. To carry out this process, refer to the TL-B [standard](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md) for Jettons transfers. It’s important to note that a slight difference exists between NFT and Jettons transfers. -### Wallet v3 and Wallet v4 Get Methods +### Wallet v3 and wallet v4 get methods -Smart contracts often make use of [GET methods](/v3/guidelines/smart-contracts/get-methods), however, they don’t run inside the blockchain but instead on the client side. GET methods have many uses and provide accessibility to different data types for smart contracts. For example, the [get_nft_data() method in NFT smart contracts](https://github.com/ton-blockchain/token-contract/blob/991bdb4925653c51b0b53ab212c53143f71f5476/nft/nft-item.fc#L142-L145) allows users to retrieve specific content, owner, and NFT collection information. +Smart contracts often use [GET methods](/v3/guidelines/smart-contracts/get-methods). However, they don’t run inside the blockchain but on the client side. GET methods have many uses and provide accessibility to different data types for smart contracts. For example, the [get_nft_data() method in NFT smart contracts](https://github.com/ton-blockchain/token-contract/blob/991bdb4925653c51b0b53ab212c53143f71f5476/nft/nft-item.fc#L142-L145) allows users to retrieve specific content, owner, and NFT collection information. Below we’ll learn more about the basics of GET methods used with [V3](https://github.com/ton-blockchain/ton/blob/e37583e5e6e8cd0aebf5142ef7d8db282f10692b/crypto/smartcont/wallet3-code.fc#L31-L41) and [V4](https://github.com/ton-blockchain/wallet-contract/blob/4111fd9e3313ec17d99ca9b5b1656445b5b49d8f/func/wallet-v4-code.fc#L164-L198). Let’s start with the methods that are the same for both wallet versions: -| Method | Explanation | -| :------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -| int seqno() | This method is needed to receive the current seqno and send messages with the correct value. In previous sections of this tutorial, this method was called often. | -| int get_public_key() | This method is used to retrive a public key. The get_public_key() is not broadly used, and can be used by different services. For example, some API services allow for the retrieval of numerous wallets with the same public key | +| Method | Explanation | +| :------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| int seqno() | This method is essential for retrieving the current seqno and sending messages with the correct value. In previous sections of this tutorial, we frequently called this method. | +| int get_public_key() | This method retrieves the public key. While get_public_key() is not widely used, various services can utilize it. For example, some API services allow retrieving multiple wallets associated with the same public key. | -Now let’s move to the methods that only the V4 wallet makes use of: +Now, let’s move to the methods that only the V4 wallet makes use of: -| Method | Explanation | -| :--------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -| int get_subwallet_id() | Earlier in the tutorial we considered this. This method allows you to retrive subwallet_id. | -| int is_plugin_installed(int wc, int addr_hash) | Let’s us know if the plugin has been installed. To call this method it’s necessary to pass the [workchain](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains#workchain-blockchain-with-your-own-rules) and the plugin address hash. | -| tuple get_plugin_list() | This method returns the address of the plugins that are installed. | +| Method | Explanation | +| :--------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| int get_subwallet_id() | Earlier in the tutorial, we considered this. This method allows you to retrive subwallet_id. | +| int is_plugin_installed(int wc, int addr_hash) | Let us know if the plugin has been installed. To call this method, you need to pass the [workchain](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains#workchain-blockchain-with-your-own-rules) and the plugin address hash. | +| tuple get_plugin_list() | This method returns the address of the installed plugins. | -Let’s consider the `get_public_key` and the `is_plugin_installed` methods. These two methods were chosen because at first we would have to get a public key from 256 bits of data, and after that we would have to learn how to pass a slice and different types of data to GET methods. This is very useful to help us learn how to properly make use of these methods. +Let’s consider the `get_public_key` and the `is_plugin_installed` methods. These two methods were chosen because we would first have to get a public key from 256 bits of data, and then we would have to learn how to pass a slice and different types of data to GET methods. This is very useful to help us learn how to properly use these methods. -First we need a client that is capable of sending requests. Therefore, we’ll use a specific wallet address ([EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF](https://tonscan.org/address/EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF)) as an example: +First, we need a client who is capable of sending requests. Therefore, we’ll use a specific wallet address ([EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF](https://tonscan.org/address/EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF)) as an example: @@ -1735,7 +1728,7 @@ if err != nil { } client := ton.NewAPIClient(connection) -block, err := client.CurrentMasterchainInfo(context.Background()) // get current block, we will need it in requests to LiteServer +block, err := client.CurrentMasterchainInfo(context.Background()) // get the current block, we will need it in requests to LiteServer if err != nil { log.Fatalln("CurrentMasterchainInfo err:", err.Error()) return @@ -1747,7 +1740,7 @@ walletAddress := address.MustParseAddr("EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72 -Now we need to call the GET method wallet. +Now, we need to call the GET method wallet. @@ -1769,8 +1762,8 @@ console.log(publicKey); ```go getResult, err := client.RunGetMethod(context.Background(), block, walletAddress, "get_public_key") // run get_public_key GET Method if err != nil { - log.Fatalln("RunGetMethod err:", err.Error()) - return + log.Fatalln("RunGetMethod err:", err.Error()) + return } // We have a response as an array with values and should specify the index when reading it @@ -1783,10 +1776,10 @@ log.Println(publicKey) -After the call is successfully completed the end result is an extremely large 256 bit number which must be translated into a hex string. The resulting hex string for the wallet address we provided above is as follows: `430db39b13cf3cb76bfa818b6b13417b82be2c6c389170fbe06795c71996b1f8`. -Next, we leverage the [TonAPI](https://docs.tonconsole.com/tonapi/rest-api) (/v1/wallet/findByPubkey method), by inputting the obtained hex string into the system and it is immediately clear that the first element in the array within the answer will identify my wallet. +After the call is successfully completed, the end result is an extremely large 256-bit number that must be translated into a hex string. The resulting hex string for the wallet address we provided above is as follows: `430db39b13cf3cb76bfa818b6b13417b82be2c6c389170fbe06795c71996b1f8`. +Next, we leverage the [TonAPI](https://docs.tonconsole.com/tonapi/rest-api) (/v1/wallet/findByPubkey method) by inputting the obtained hex string into the system. It is immediately clear that the first element in the array within the answer will identify my wallet. -Then we switch to the `is_plugin_installed` method. As an example, we’ll again use the wallet we used earlier ([EQAM7M--HGyfxlErAIUODrxBA3yj5roBeYiTuy6BHgJ3Sx8k](https://tonscan.org/address/EQAM7M--HGyfxlErAIUODrxBA3yj5roBeYiTuy6BHgJ3Sx8k)) and the plugin ([EQBTKTis-SWYdupy99ozeOvnEBu8LRrQP_N9qwOTSAy3sQSZ](https://tonscan.org/address/EQBTKTis-SWYdupy99ozeOvnEBu8LRrQP_N9qwOTSAy3sQSZ)): +Then, we switch to the `is_plugin_installed` method. As an example, we’ll again use the wallet we used earlier ([EQAM7M--HGyfxlErAIUODrxBA3yj5roBeYiTuy6BHgJ3Sx8k](https://tonscan.org/address/EQAM7M--HGyfxlErAIUODrxBA3yj5roBeYiTuy6BHgJ3Sx8k)) and the plugin ([EQBTKTis-SWYdupy99ozeOvnEBu8LRrQP_N9qwOTSAy3sQSZ](https://tonscan.org/address/EQBTKTis-SWYdupy99ozeOvnEBu8LRrQP_N9qwOTSAy3sQSZ)): @@ -1797,7 +1790,7 @@ const oldWalletAddress = Address.parse( ); // my old wallet address const subscriptionAddress = Address.parseFriendly( "EQBTKTis-SWYdupy99ozeOvnEBu8LRrQP_N9qwOTSAy3sQSZ" -); // subscription plugin address which is already installed on the wallet +); // subscription plugin address, which is already installed on the wallet ``` @@ -1811,7 +1804,7 @@ subscriptionAddress := address.MustParseAddr("EQBTKTis-SWYdupy99ozeOvnEBu8LRrQP_ -Now we need to retrieve the plugin’s hash address so the address can be translated into a number and sent to the GET Method. +Now, we need to retrieve the plugin’s hash address so that it can be translated into a number and sent to the GET Method. @@ -1855,14 +1848,13 @@ log.Println(getResult.MustInt(0)) // -1 -The response must be `-1`, meaning the result is true. It is also possible to send a slice and a cell if required. It would be enough to create a Slice or Cell and transfer it instead of using the BigInt, specifying the appropriate type. +The response must be `-1`, meaning the result is true. If required, it is also possible to send a slice and a cell. It would be enough to create and transfer a Slice or Cell instead of using the BigInt, specifying the appropriate type. -### Contract Deployment via Wallet +### Contract deployment via wallet -In chapter three, we deployed a wallet. To accomplish this, we initially sent some TON and then a message from the wallet to deploy a smart contract. However, this process is not broadly used with external messages and is often primarily used for wallets only. While developing contracts, the deployment process is initialized by sending internal messages. +In chapter three, we deployed a wallet. To accomplish this, we initially sent some TON and a message from the wallet to deploy a smart contract. However, this process is not broadly used with external messages and is often used mainly for wallets. While developing contracts, the deployment process is initialized by sending internal messages. -To accomplish this, will use the V3R2 wallet smart contract that was used in [the third chapter](/v3/guidelines/smart-contracts/howto/wallet#compiling-wallet-code). -In this case, we’ll set the `subwallet_id` to `3` or any other number needed to retrieve another address when using the same private key (it's changeable): +We’ll use the V3R2 wallet smart contract introduced in [the third chapter](/v3/guidelines/smart-contracts/howto/wallet#compiling-wallet-code) to achieve this. In this case, we’ll set the `subwallet_id` to `3` or any other number required to generate a different address while using the same private key (this value is customizable): @@ -1916,7 +1908,7 @@ mnemonicArray := strings.Split("put your mnemonic", " ") // all required libraries (crypto, pbkdf2 and others). mac := hmac.New(sha512.New, []byte(strings.Join(mnemonicArray, " "))) hash := mac.Sum(nil) -k := pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries "TON default seed" is used as salt when getting keys +k := pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries, "TON default seed" is used as salt when getting keys // 32 is a key len privateKey := ed25519.NewKeyFromSeed(k) // get private key publicKey := privateKey.Public().(ed25519.PublicKey) // get public key from private key @@ -1943,7 +1935,7 @@ stateInit := cell.BeginCell(). -Next we’ll retrieve the address from our contract and build the InternalMessage. Also we add the "Deploying..." comment to our message. +Next, we’ll retrieve the address from our contract and build the Internal Message. We'll also add the "Deploying..." comment to our message. @@ -2007,7 +1999,7 @@ internalMessage := cell.BeginCell(). :::info -Note that above, the bits have been specified and that the stateInit and internalMessageBody have been saved as references. Since the links are stored separately, we could write 4 (0b100) + 2 (0b10) + 1 (0b1) -> (4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1) which means (0b111, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1) and then save two references. +Note that the bits have been specified above and that the stateInit and internalMessageBody have been saved as references. Since the links are stored separately, we could write 4 (0b100) + 2 (0b10) + 1 (0b1) -> (4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1), which means (0b111, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1) and then save two references. ::: Next, we’ll prepare a message for our wallet and send it: @@ -2026,9 +2018,7 @@ const client = new TonClient({ const walletMnemonicArray = "put your mnemonic".split(" "); const walletKeyPair = await mnemonicToWalletKey(walletMnemonicArray); // extract private and public keys from mnemonic -const walletAddress = Address.parse( - "put your wallet address with which you will deploy" -); +const walletAddress = Address.parse("put the wallet address you will deploy."); const getMethodResult = await client.runMethod(walletAddress, "seqno"); // run "seqno" GET method from your wallet contract const seqno = getMethodResult.stack.readNumber(); // get seqno from response @@ -2081,7 +2071,7 @@ if err != nil { } client := ton.NewAPIClient(connection) -block, err := client.CurrentMasterchainInfo(context.Background()) // get current block, we will need it in requests to LiteServer +block, err := client.CurrentMasterchainInfo(context.Background()) // get the current block, we will need it in requests to LiteServer if err != nil { log.Fatalln("CurrentMasterchainInfo err:", err.Error()) return @@ -2090,7 +2080,7 @@ if err != nil { walletMnemonicArray := strings.Split("put your mnemonic", " ") mac = hmac.New(sha512.New, []byte(strings.Join(walletMnemonicArray, " "))) hash = mac.Sum(nil) -k = pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries "TON default seed" is used as salt when getting keys +k = pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries, "TON default seed" is used as salt when getting keys // 32 is a key len walletPrivateKey := ed25519.NewKeyFromSeed(k) // get private key walletAddress := address.MustParseAddr("put your wallet address with which you will deploy") @@ -2110,7 +2100,7 @@ toSign := cell.BeginCell(). MustStoreUInt(3, 8). // store mode of our internal message MustStoreRef(internalMessage) // store our internalMessage as a reference -signature := ed25519.Sign(walletPrivateKey, toSign.EndCell().Hash()) // get the hash of our message to wallet smart contract and sign it to get signature +signature := ed25519.Sign(walletPrivateKey, toSign.EndCell().Hash()) // get the hash of our message to the wallet smart contract and sign it to get the signature body := cell.BeginCell(). MustStoreSlice(signature, 512). // store signature @@ -2141,24 +2131,24 @@ if err != nil { This concludes our work with ordinary wallets. At this stage, you should have a strong understanding of how to interact with wallet smart contracts, send messages, and be able to use various library types. -## 🔥 High-Load Wallet V3 +## 🔥 High-load wallet v3 -When working with many messages in a short period, there is a need for special wallet called High-Load Wallet. High-Load Wallet V2 was the main wallet on TON for a long time, but you had to be very careful with it. Otherwise, you could [lock all funds](https://t.me/tonstatus/88). +You’ll need a specialized wallet called a **High-Load Wallet** to handle many messages quickly. High-Load Wallet V2 was the primary wallet on TON for a long time, but it required careful handling. Otherwise, you risk [locking all funds](https://t.me/tonstatus/88). -[With the advent of High-Load Wallet V3](https://github.com/ton-blockchain/highload-wallet-contract-v3), this problem has been solved at the contract architecture level and consumes less gas. This chapter will cover the basics of High-Load Wallet V3 and important nuances to remember. +[With the introduction of High-Load Wallet V3](https://github.com/ton-blockchain/highload-wallet-contract-v3), this issue has been resolved at the contract architecture level, and it consumes less gas. This chapter will cover the basics of High-Load Wallet V3 and highlight important nuances to keep in mind. :::note -We will work [with a slightly modified version of Wrapper](https://github.com/aSpite/highload-wallet-contract-v3/blob/main/wrappers/HighloadWalletV3.ts) for the contract, as it protects against some non-obvious mistakes. +We will work [with a slightly modified version of wrapper](https://github.com/aSpite/highload-wallet-contract-v3/blob/main/wrappers/HighloadWalletV3.ts) for the contract, as it protects against some non-obvious mistakes. ::: -### Storage Structure +### Storage structure First of all, [TL-B schema](https://github.com/ton-blockchain/highload-wallet-contract-v3/blob/d58c31e82315c34b4db55942851dd8d4153975c5/contracts/scheme.tlb#L1C1-L3C21) will help us in learning the structure of the contract storage: ``` storage$_ public_key:bits256 subwallet_id:uint32 old_queries:(HashmapE 14 ^Cell) - queries:(HashmapE 14 ^Cell) last_clean_time:uint64 timeout:uint22 - = Storage; + queries:(HashmapE 14 ^Cell) last_clean_time:uint64 timeout:uint22 + = Storage; ``` :::tip TL-B @@ -2176,9 +2166,9 @@ In the contract storage, we can find the following fields: | last_clean_time | The time of the last cleanup. If `last_clean_time < (now() - timeout)`, old queries are moved to `old_queries`. If `last_clean_time < (now() - 2 * timeout)`, both `old_queries` and `queries` are cleared. | | timeout | The time after which queries are moved to `old_queries`. | -We will discuss more about working with processed queries in [Replay Protection](#replay-protection). +We’ll explore how to work with processed queries in more detail in the [Replay Protection](#replay-protection) section. -### Shifts and Bits Numbers as Query ID +### Shifts and bits numbers as query id The Query ID is a number that consists of two parts: shift and bit_number: @@ -2187,7 +2177,7 @@ int shift = msg_inner_slice~load_uint(KEY_SIZE); int bit_number = msg_inner_slice~load_uint(BIT_NUMBER_SIZE); ``` -The basic idea behind this is that each Query ID now only takes up 1 bit in the dictionary while not increasing gas consumption most of the time. +The core idea is that each Query ID now occupies only 1 bit in the dictionary while typically avoiding an increase in gas consumption. To start, the contract, using shift, tries to get the cell at that index in the `old_queries` dictionary: @@ -2195,13 +2185,13 @@ To start, the contract, using shift, tries to get the cell at that index in the (cell value, int found) = old_queries.udict_get_ref?(KEY_SIZE, shift); ``` -If such a cell is found, it skips `bit_number` bits to reach the bit with index `bit_number` (it is important to understand the difference between bit_number as a quantity and bit_number as an index). If such a bit is found, it means that a query with such a Query ID has already been processed, and an error is thrown: +If such a cell is found, it skips `bit_number` bits to reach the bit at index `bit_number` (it’s important to distinguish between `bit_number` as a quantity and `bit_number` as an index). If the bit is set, it means a query with that Query ID has already been processed, and the contract throws an error: ```func if (found) { - slice value_slice = value.begin_parse(); - value_slice~skip_bits(bit_number); - throw_if(error::already_executed, value_slice.preload_int(1)); + slice value_slice = value.begin_parse(); + value_slice~skip_bits(bit_number); + throw_if(error::already_executed, value_slice.preload_int(1)); } ``` @@ -2211,59 +2201,59 @@ The next step is to search the `queries` dictionary: (cell value, int found) = queries.udict_get_ref?(KEY_SIZE, shift); ``` -If such a cell is found, the contract cuts it into 2 parts: `0...bit_number-1` (head) and `bit_number...1023` (tail). Then, one bit is read from the beginning of the tail (the number of this bit is equal to the `bit_number` variable if you start counting from 0, i.e. it is the index of the required bit). If it is positive, the request with such a Query ID has already been processed, and an error is thrown. Otherwise, the bit is set to 1, and all the pieces are merged into one cell again and written back into the `queries` dictionary: +If such a cell is found, the contract splits it into two parts: `0...bit_number-1` (head) and `bit_number...1023` (tail). It then reads one bit from the beginning of the tail (this bit corresponds to the `bit_number` variable when counting from 0, i.e., it represents the index of the required bit). If the bit is set to `1`, the request with that Query ID has already been processed, and the contract throws an error. Otherwise, the contract sets the bit to `1`, merges the pieces back into a single cell, and writes it back into the `queries` dictionary: ```func builder new_value = null(); if (found) { - slice value_slice = value.begin_parse(); - (slice tail, slice head) = value_slice.load_bits(bit_number); - throw_if(error::already_executed, tail~load_int(1)); - new_value = begin_cell().store_slice(head).store_true().store_slice(tail); + slice value_slice = value.begin_parse(); + (slice tail, slice head) = value_slice.load_bits(bit_number); + throw_if(error::already_executed, tail~load_int(1)); + new_value = begin_cell().store_slice(head).store_true().store_slice(tail); } else { - new_value = begin_cell().store_zeroes(bit_number).store_true().store_zeroes(CELL_BITS_SIZE - bit_number - 1); + new_value = begin_cell().store_zeroes(bit_number).store_true().store_zeroes(CELL_BITS_SIZE - bit_number - 1); } ``` :::note -If you [familiarize yourself](/v3/documentation/tvm/instructions) with the operation of the `LDSLICEX` opcode (the load_bits function uses this opcode), you will notice that the read data is returned first (head) and only then the remaining data (tail), but they are in reverse order in the contract code. +If you [familiarize yourself](/v3/documentation/tvm/instructions) with the operation of the `LDSLICEX` opcode (used by the `load_bits` function), you’ll notice that the read data is returned first (head), followed by the remaining data (tail). However, in the contract code, they appear in reverse order. -In fact, they go in reverse order, because in stdlib in the function signature, the returned data [go in reverse order](https://github.com/ton-blockchain/highload-wallet-contract-v3/blob/d58c31e82315c34b4db55942851dd8d4153975c5/contracts/imports/stdlib.fc#L321): `(slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX";`. Here `-> 1 0` means to return the argument with index 1 (tail) first, and then 0 (head). +This happens because, in the stdlib function signature, the returned data [is ordered differently](https://github.com/ton-blockchain/highload-wallet-contract-v3/blob/d58c31e82315c34b4db55942851dd8d4153975c5/contracts/imports/stdlib.fc#L321): `(slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX";`. Here, `-> 1 0` indicates that the argument with index `1` (tail) is returned first, followed by the argument with index `0` (head). ::: -So in effect we are working with a matrix where `shift` is the row index and `bit_number` is the column index. This allows us to store up to 1023 queries in a single cell, which means that gas consumption will only increase every 1023 queries due to adding a new cell to the dictionary. It is important to realize that this will be done if the values grow sequentially, not randomly, so it is necessary to properly increase Query ID, [using a special class for this](https://github.com/aSpite/highload-wallet-contract-v3/blob/main/wrappers/HighloadQueryId.ts). +In practice, we’re working with a matrix where `shift` represents the row index and `bit_number` represents the column index. This structure allows us to store up to 1023 queries in a single cell, meaning gas consumption only increases every 1023 queries when a new cell is added to the dictionary. However, this efficiency depends on the sequential growth of values, not random ones. Therefore, it’s crucial to increment the Query ID properly [using a dedicated class for this purpose](https://github.com/aSpite/highload-wallet-contract-v3/blob/main/wrappers/HighloadQueryId.ts). -This approach allows storing a huge number of requests per timeout (1023 \* 8192 = 8,380,416), but you may notice that [the class HighloadQueryId supports 8,380,415](https://github.com/ton-blockchain/highload-wallet-contract-v3/blob/d58c31e82315c34b4db55942851dd8d4153975c5/wrappers/HighloadQueryId.ts#L32). This is to ensure that there will always be 1 bit left for one emergency timeout request if the entire limit is exhausted. This value is set because of the [limit on the maximum possible number of cells in an account stack](https://github.com/ton-blockchain/ton/blob/5c392e0f2d946877bb79a09ed35068f7b0bd333a/crypto/block/mc-config.h#L395) on the blockchain (as of this writing). +This approach allows storing massive requests per timeout (1023 \* 8192 = 8,380,416). Still, you may notice that [the class HighloadQueryId supports 8,380,415](https://github.com/ton-blockchain/highload-wallet-contract-v3/blob/d58c31e82315c34b4db55942851dd8d4153975c5/wrappers/HighloadQueryId.ts#L32). This is to ensure that there will always be 1 bit left for one emergency timeout request if the entire limit is exhausted. This value is set because of the [limit on the maximum possible number of cells in an account stack](https://github.com/ton-blockchain/ton/blob/5c392e0f2d946877bb79a09ed35068f7b0bd333a/crypto/block/mc-config.h#L395) on the blockchain (as of this writing). For every cell that can hold 1023 requests, 2 cells in the dictionary are spent (one to store the key, the other for the value). If we take the current maximum shift value, the theoretical maximum is 8192 \* 2 \* 2 (we have two dictionaries: queries and old_queries) = 32,768 cells. If you increase the key size by a bit, it will no longer fit within the current limits. :::info -Earlier in High-Load V2, each Query ID (64-bit) was stored in a separate cell in the dictionary and was a union of 32-bit fields `expire_at` and `query_id`. This led to a very fast growth in gas consumption when clearing old queries. +In High-Load V2, each Query ID (64-bit) was stored in a separate cell within the dictionary, combining two 32-bit fields: `expire_at` and `query_id`. This approach caused gas consumption to grow rapidly when clearing old queries. ::: -### Replay Protection +### Replay protection -As we know that external messages in TON [have no sender and can be sent by anyone in the network](#replay-protection---seqno), it is important to have a list of processed requests to avoid re-processing. For this purpose, High-Load Wallet V3 uses the `queries` and `old_queries` dictionaries and the `last_clean_time` and `timeout` values. +As we know that external messages in TON [have no sender and can be sent by anyone in the network](#replay-protection---seqno), it is essential to have a list of processed requests to avoid re-processing. For this purpose, High-Load Wallet V3 uses the `queries` and `old_queries` dictionaries and the `last_clean_time` and `timeout` values. -After the contract has completely retrieved all the data it needs from its storage, it checks to see when the last query dictionary cleanup occurred. If it was more than the `timeout` time ago, the contract moves all queries from queries to old_queries. If the last cleanup was more than `timeout * 2` times ago, the contract cleans up old_queries in addition: +After the contract has completely retrieved all the data it needs from its storage, it checks when the last query dictionary cleanup occurred. If it was more than the `timeout` time ago, the contract moves all queries from queries to old_queries. If the last cleanup was more than `timeout * 2` times ago, the contract cleans up old_queries in addition: ```func if (last_clean_time < (now() - timeout)) { - (old_queries, queries) = (queries, null()); - if (last_clean_time < (now() - (timeout * 2))) { - old_queries = null(); - } - last_clean_time = now(); + (old_queries, queries) = (queries, null()); + if (last_clean_time < (now() - (timeout * 2))) { + old_queries = null(); + } + last_clean_time = now(); } ``` -The reason for this is that the contract does not keep track of when exactly which request was executed. This means that if `timeout` is 3 hours, but the last request was executed one minute before reaching 3 hours, the request will be considered outdated one minute later, despite the 3-hour timeout. To solve this problem, the second dictionary stores the same queries for at least that much more time. +This happens because the contract doesn’t track the exact execution time of each request. For example, suppose the `timeout` is set to 3 hours, but the last request was executed one minute before the timeout. In that case, the request will be considered outdated just one minute later, even though the entire 3-hour period hasn’t elapsed. The second dictionary stores the same queries for at least the specified timeout duration to address this issue. Theoretically, a query has a lifetime from `timeout` to `timeout * 2`, which means that when tracking which queries are outdated, it is good practice to wait at least `timeout * 2` times to see if the query is obsolete. -### Guaranteed Error-Free Action Phase +### Guaranteed error-free action phase -Once all the checks and cleanups have been completed, the contract can accept the message, make changes to its storage, and call the commit function, which will consider the compute phase a success even if some error is thrown next: +Once all checks and cleanups are complete, the contract can accept the message, update its storage, and call the commit function. This ensures the compute phase is considered successful, even if an error occurs afterward. ```func accept_message(); @@ -2271,21 +2261,21 @@ accept_message(); queries~udict_set_ref(KEY_SIZE, shift, new_value.end_cell()); set_data(begin_cell() - .store_uint(public_key, PUBLIC_KEY_SIZE) - .store_uint(subwallet_id, SUBWALLET_ID_SIZE) - .store_dict(old_queries) - .store_dict(queries) - .store_uint(last_clean_time, TIMESTAMP_SIZE) - .store_uint(timeout, TIMEOUT_SIZE) - .end_cell()); + .store_uint(public_key, PUBLIC_KEY_SIZE) + .store_uint(subwallet_id, SUBWALLET_ID_SIZE) + .store_dict(old_queries) + .store_dict(queries) + .store_uint(last_clean_time, TIMESTAMP_SIZE) + .store_uint(timeout, TIMEOUT_SIZE) + .end_cell()); commit(); ``` -This is done so that when executing further code if there is an error in the message the user is trying to send, the contract does not return to its previous state. Otherwise, the external will remain valid and can be accepted several times, resulting in wasted balance. +This ensures that when executing further code, the contract doesn’t revert to its previous state if an error occurs in the message the user is trying to send. Without this, the external message would remain valid and could be accepted multiple times, leading to unnecessary balance depletion. -However, another issue must be addressed - possible errors during the **Action Phase**. Although we have a flag to ignore the mistakes (2) when sending a message, it doesn't work in all cases, so we need to ensure that no errors occur during this phase, which could cause the state to roll back and make `commit()` meaningless. +However, we must address another issue: potential errors during the **Action Phase**. While we have a flag to ignore errors (2) when sending a message, it doesn’t cover all cases. Therefore, we need to ensure no errors occur during this phase, as they could cause a state rollback, rendering `commit()` meaningless. For this reason, instead of sending all messages directly, the contract sends itself a message with the `internal_transfer` opcode. This message is parsed in detail by the contract to ensure that no Action Phase error occurs: @@ -2293,7 +2283,7 @@ For this reason, instead of sending all messages directly, the contract sends it throw_if(error::invalid_message_to_send, message_slice~load_uint(1)); ;; int_msg_info$0 int msg_flags = message_slice~load_uint(3); ;; ihr_disabled:Bool bounce:Bool bounced:Bool if (is_bounced(msg_flags)) { - return (); + return (); } slice message_source_adrress = message_slice~load_msg_addr(); ;; src throw_unless(error::invalid_message_to_send, is_address_none(message_source_adrress)); @@ -2307,75 +2297,75 @@ int maybe_state_init = message_slice~load_uint(1); throw_if(error::invalid_message_to_send, maybe_state_init); ;; throw if state-init included (state-init not supported) int either_body = message_slice~load_int(1); if (either_body) { - message_slice~load_ref(); - message_slice.end_parse(); + message_slice~load_ref(); + message_slice.end_parse(); } ``` -If any problem occurs while reading the data, it will still be Compute Phase. However, due to the presence of `commit()` this is not a problem and the transaction will still be considered a success. If all data has been read successfully, this is a guarantee that the Action Phase will pass without errors, as these checks cover all cases where the `IGNORE_ERRORS` (2) flag fails. The contract can then complete its work by sending a message: +If any issue arises while reading the data, it will still occur during the compute phase. However, thanks to `commit()`, this isn’t a problem, and the transaction will still be considered successful. If all data is read successfully, it guarantees that the Action Phase will proceed without errors, as these checks cover all cases where the `IGNORE_ERRORS` (2) flag might fail. The contract can then finalize its work by sending the message. ```func -;; send message with IGNORE_ERRORS flag to ignore errors in the action phase +;; send a message with the IGNORE_ERRORS flag to ignore errors in the action phase send_raw_message(message_to_send, send_mode | SEND_MODE_IGNORE_ERRORS); ``` -### Internal Transfer +### Internal transfer After `internal_transfer` reaches the contract, it loads the list of actions, sets them in the c5 register, and then applies `set_code` to protect against accidental code changes, which is also an action. Because of this, the number of messages that can be sent is 254 rather than 255, which is the limit on the blockchain. However, the contract can call itself to send more messages, which we will discuss later: ```func if (op == op::internal_transfer) { - in_msg_body~skip_query_id(); - cell actions = in_msg_body.preload_ref(); - cell old_code = my_code(); - set_actions(actions); - set_code(old_code); ;; prevent to change smart contract code - return (); + in_msg_body~skip_query_id(); + cell actions = in_msg_body.preload_ref(); + cell old_code = my_code(); + set_actions(actions); + set_code(old_code); ;; prevent to change smart contract code + return (); } ``` -When dealing with `internal_transfer` there is one important nuance. As we have discussed above, the contract sends a message to itself, but that message is entirely collected on the user side. The problem is that you need to correctly count how much TON will be attached to the message. +When working with `internal_transfer`, there’s an important nuance to consider. As mentioned earlier, the contract sends a message to itself, but it is entirely collected on the user's side. The challenge lies in accurately calculating the amount of TON to attach to the message. -In the wrapper in the official repository this field is optional and if the user does not specify it, [mode becomes 128](https://github.com/ton-blockchain/highload-wallet-contract-v3/blob/d58c31e82315c34b4db55942851dd8d4153975c5/wrappers/HighloadWalletV3.ts#L115), which means that the entire balance is sent. The problem is that in such a case there is a **edge case**. +In the wrapper in the official repository, this field is optional, and if the user does not specify it, [mode becomes 128](https://github.com/ton-blockchain/highload-wallet-contract-v3/blob/d58c31e82315c34b4db55942851dd8d4153975c5/wrappers/HighloadWalletV3.ts#L115), which means that the entire balance is sent. The problem is that there is **an edge case** in such a case. -Let's imagine that we want to send out a lot of tokens. After sending out the rest of the TON are returned to our wallet, since we set our address in the `response_destination` field. We start sending out multiple externals at the same time and the following situation occurs: +Imagine we want to send out a large number of tokens. After sending them, the remaining TONs are returned to our wallet because we set our address in the `response_destination` field. If we start sending multiple external messages simultaneously, the following situation can occur: -1. External message A is received, processed and sends the entire contract balance via `internal_transfer`. -2. Before external message B reaches, part of the commissions from the already completed token sent reaches. Hence, the non-empty contract balance allows the entire balance to be sent to internal message B again, but this time, a very small amount of TONs is sent. -3. Internal message A is received, processed. Token sending messages are sent. -4. Before internal message B reaches, external message C manages to reach and sends the entire balance again. -5. When receiving internal message B, the contract has little TON, even if some extra TON from sending tokens will reach and the request fails with exit code = 37 on Action Phase (Insufficient Funds). +1. External message A is received, processed, and sent the entire contract balance via `internal_transfer`. +2. Before external message B arrives, part of the commissions from the already completed token transfer reaches the contract. This replenishes the contract balance, allowing the entire balance to be sent again in internal message B, but this time with a tiny amount of TONs. +3. Internal message A is received and processed. Token transfer messages are sent. +4. External message C reaches the contract before internal message B arrives and sends the entire balance again. +5. When internal message B is received, the contract has very little TON left. Even if some additional TON from token transfers arrives, the request fails with exit code `37` during the Action Phase (Insufficient Funds). -Thus the contract displays that the request has been processed when in fact it has not. To avoid this scenario, it is **recommended to always put 1 TON** on `internal_transfer`. Therefore, [we are working with a modified wrapper](#-high-load-wallet-v3) that requires the user to specify the number of TONs. This value will suffice for all cases, since the external message size is limited to 64 KB and a message close to this limit will spend less than 1 TON. +As a result, the contract marks the request as processed, even though it wasn’t successfully executed. To avoid this scenario, it’s **recommended to always allocate 1 TON** for `internal_transfer`. Therefore, [we use a modified wrapper](#-high-load-wallet-v3) that requires the user to specify the amount of TONs. This value will suffice for all cases, as the external message size is limited to 64 KB, and a message close to this limit will consume less than 1 TON. High-Load Wallet V3 can send more than 254 messages, [putting the remaining messages into the 254th message](https://github.com/aSpite/highload-wallet-contract-v3/blob/d4c1752d00b5303782f121a87eb0620d403d9544/wrappers/HighloadWalletV3.ts#L169-L176). This way `internal_transfer` will be processed several times. The wrapper automatically does this, and we won't have to worry about it, but **recommended to take no more than 150 messages at a time** to ensure that even complex messages will fit into an external message. :::info -Although the external message limit is 64KB, the larger the external, the more likely it is to get lost in delivery, so 150 messages is the optimal solution. +Although the external message limit is 64KB, the larger the external message, the more likely it is to be lost in delivery, so 150 messages is the optimal solution. ::: -### GET Methods +### GET vethods High-Load Wallet V3 supports the 5 GET methods: -| Method | Explanation | -| :------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -| int get_public_key() | Returns the public key of the contract. | -| int get_subwallet_id() | Returns the subwallet ID. | -| int get_last_clean_time() | Returns the time of the last cleaning. | -| int get_timeout() | Returns the timeout value. | -| int processed?(int query_id, int need_clean) | Returns whether the query_id has been processed. If need_clean is set to 1, then will first do the cleanup based on `last_clean_time` and `timeout` and then check for query_id in `old_queries` and `queries`. | +| Method | Explanation | +| :------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| int get_public_key() | Returns the public key of the contract. | +| int get_subwallet_id() | Returns the subwallet ID. | +| int get_last_clean_time() | Returns the time of the last cleaning. | +| int get_timeout() | Returns the timeout value. | +| int processed?(int query_id, int need_clean) | Returns whether the query_id has been processed. If need_clean is set to 1, we will first do the cleanup based on `last_clean_time` and `timeout` and then check for query_id in `old_queries` and `queries`. | :::tip -It is recommended to pass `true` for `need_clean` unless the situation requires otherwise since the most current dictionary states will then be returned. +It’s recommended to pass `true` for `need_clean` unless the situation requires explicitly otherwise. This ensures the most current dictionary states are returned. ::: -Due to how the Query ID is organized in High-Load Wallet V3, we can send a message with the same Query ID again if it does not arrive without fear of the request being processed twice. +Thanks to how the Query ID is structured in High-Load Wallet V3, we can safely resend a message with the same Query ID if it doesn’t arrive initially without worrying about the request being processed twice. -However, in such a case, we must consider that no more than `timeout` time has elapsed since the first sending attempt. Otherwise, the request may have been processed but already deleted from the dictionaries. Therefore, it is recommended to set `timeout` to no less than an hour and no more than 24 hours. +However, in such cases, we must ensure that no more than `timeout` time has passed since the first sending attempt. Otherwise, the request might have already been processed and deleted from the dictionaries. Therefore, it’s recommended to set `timeout` to no less than an hour and no more than 24 hours. -### Deploying High-Load Wallet V3 +### Deploying high-load wallet v3 To deploy a contract, we need 2 cells: `code` and `date`. For the code, we will use the following cell: @@ -2396,7 +2386,7 @@ const HIGHLOAD_V3_CODE = Cell.fromBoc( -Unlike the other examples, here we will work [with a ready-made wrapper](https://github.com/aSpite/highload-wallet-contract-v3/blob/main/wrappers/HighloadWalletV3.ts), as it will be quite difficult and time-consuming to build each message manually. To create an instance of the HighloadWalletV3 class, we pass `publicKey`, `subwalletId` and `timeout` and also the code: +Unlike the other examples, here we will work [with a ready-made wrapper](https://github.com/aSpite/highload-wallet-contract-v3/blob/main/wrappers/HighloadWalletV3.ts), as it will be quite difficult and time-consuming to build each message manually. To create an instance of the HighloadWalletV3 class, we pass `publicKey`, `subwalletId`, and `timeout` and also the code: @@ -2430,7 +2420,7 @@ console.log(`Wallet address: ${wallet.address.toString()}`); -Now we need a regular wallet, from which we will deploy the contract: +Now, we need a regular wallet from which we will deploy the contract: @@ -2469,11 +2459,11 @@ await wallet.sendDeploy( -By viewing the address that was output to the console in explorer, we can verify that our wallet is deployed. +We can confirm that our wallet has been successfully deployed by checking the address output to the console in a blockchain explorer. -### Sending High-Load Wallet V3 Messages +### Sending high-load wallet v3 messages -Sending messages is also done through the wrapper, but in this case we will need to additionally keep the Query ID up to date. First, let's get an instance of our wallet class: +Sending messages is also done through the wrapper, but we will need to keep the Query ID up to date. First, let's get an instance of our wallet class: @@ -2502,7 +2492,7 @@ console.log(`Wallet address: ${wallet.address.toString()}`); -Now we need to create an instance of the `HighloadQueryId` class. This class makes it easy to work with `shift` and `bit_number`. To create it, we use the `fromShiftAndBitNumber` method: +Now, we need to create an instance of the `HighloadQueryId` class. This class makes working with `shift` and `bit_number` easy. To create it, we use the `fromShiftAndBitNumber` method: @@ -2545,7 +2535,7 @@ actions.push({ -Next we just need to fill in the `subwalletId`, `timeout`, `internalMessageValue` and `createdAt` fields to send the message: +Next, we need to fill in the `subwalletId`, `timeout`, `internalMessageValue`, and `createdAt` fields to send the message: @@ -2553,7 +2543,7 @@ Next we just need to fill in the `subwalletId`, `timeout`, `internalMessageValue ```js const subwalletId = 0x10ad; const timeout = 60 * 60; // must be same as in the contract -const internalMessageValue = toNano(0.01); // in real case it is recommended to set the value to 1 TON +const internalMessageValue = toNano(0.01); // in the real case, it is recommended to set the value to 1 TON const createdAt = Math.floor(Date.now() / 1000) - 60; // LiteServers have some delay in time await wallet.sendBatch( walletKeyPair.secretKey, @@ -2582,33 +2572,33 @@ queryHandler.getNext(); -## 🔥 High-Load Wallet V2 (Outdated) +## 🔥 High-load wallet v2 (Outdated) -In some situations, sending a large number of messages per transaction may be necessary. As previously mentioned, ordinary wallets support sending up to 4 messages at a time by storing [a maximum of 4 references](/v3/documentation/data-formats/tlb/cell-boc#cell) in a single cell. High-load wallets only allow 255 messages to be sent at once. This restriction exists because the maximum number of outgoing messages (actions) in the blockchain’s config settings is set to 255. +In some situations, sending a large number of messages per transaction may be necessary. As previously mentioned, ordinary wallets support sending up to 4 messages simultaneously by storing [a maximum of 4 references](/v3/documentation/data-formats/tlb/cell-boc#cell) in a single cell. High-load wallets only allow 255 messages to be sent at once. This restriction exists because the maximum number of outgoing messages (actions) in the blockchain’s config settings is set to 255. -Exchanges are probably the best example of where high-load wallets are used on a large scale. Established exchanges like Binance and others have extremely large user bases, this means that a large number of withdrawals messages are processed in short time periods. High-load wallets help address these withdrawal requests. +Exchanges are probably the best example of a large-scale use of high-load wallets. Established exchanges like Binance and others have extremely large user bases, which means that a large number of withdrawal messages are processed in short time periods. High-load wallets help address these withdrawal requests. ### High-load wallet FunC code -First, let’s examine [the code structure of high-load wallet smart contract](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/new-highload-wallet-v2.fif): +First, let’s examine [the code structure of a high-load wallet smart contract](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/new-highload-wallet-v2.fif): ```func () recv_external(slice in_msg) impure { - var signature = in_msg~load_bits(512); ;; get signature from the message body - var cs = in_msg; - var (subwallet_id, query_id) = (cs~load_uint(32), cs~load_uint(64)); ;; get rest values from the message body - var bound = (now() << 32); ;; bitwise left shift operation - throw_if(35, query_id < bound); ;; throw an error if message has expired - var ds = get_data().begin_parse(); - var (stored_subwallet, last_cleaned, public_key, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict()); ;; read values from storage - ds.end_parse(); ;; make sure we do not have anything in ds - (_, var found?) = old_queries.udict_get?(64, query_id); ;; check if we have already had such a request - throw_if(32, found?); ;; if yes throw an error - throw_unless(34, subwallet_id == stored_subwallet); - throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key)); - var dict = cs~load_dict(); ;; get dictionary with messages - cs.end_parse(); ;; make sure we do not have anything in cs - accept_message(); + var signature = in_msg~load_bits(512); ;; get signature from the message body + var cs = in_msg; + var (subwallet_id, query_id) = (cs~load_uint(32), cs~load_uint(64)); ;; get rest values from the message body + var bound = (now() << 32); ;; bitwise left shift operation + throw_if(35, query_id < bound); ;; throw an error if message has expired + var ds = get_data().begin_parse(); + var (stored_subwallet, last_cleaned, public_key, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict()); ;; read values from storage + ds.end_parse(); ;; make sure we do not have anything in ds + (_, var found?) = old_queries.udict_get?(64, query_id); ;; check if we have already had such a request + throw_if(32, found?); ;; if yes throw an error + throw_unless(34, subwallet_id == stored_subwallet); + throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key)); + var dict = cs~load_dict(); ;; get dictionary with messages + cs.end_parse(); ;; make sure we do not have anything in cs + accept_message(); ``` > 💡 Useful links: @@ -2619,12 +2609,11 @@ First, let’s examine [the code structure of high-load wallet smart contract](h > > ["udict_get?()" in docs](/v3/documentation/smart-contracts/func/docs/stdlib/#dict_get) -You notice some differences from ordinary wallets. Now let’s take a closer look at more details of how high-load wallets work on TON (except subwallets as we have gone over this previously). +You notice some differences from ordinary wallets. Now, let’s take a closer look at more details of how high-load wallets work on TON (except subwallets, as we have gone over this previously). -### Using a Query ID In Place Of a Seqno +### Using a query id in place of a seqno -As we previously discussed, ordinary wallet seqno increase by `1` after each transaction. While using a wallet sequence we had to wait until this value was updated, then retrieve it using the GET method and send a new message. -This process takes a significant amount of time which high-load wallets are not designed for (as discussed above, they are meant to send a large number of messages very quickly). Therefore, high-load wallets on TON make use of the `query_id`. +As mentioned, ordinary wallets increment their `seqno` by `1` after each transaction. When using a wallet sequence, we had to wait for this value to update, retrieve it using the GET method, and then send a new message. This process takes a significant amount of time, which high-load wallets are not designed for (as discussed earlier, they are built to send a large number of messages quickly). To address this, high-load wallets on TON use the `query_id`. If the same message request already exists, the contract won’t accept it, as it has already been processed: @@ -2637,18 +2626,18 @@ throw_if(32, found?); ;; if yes throw an error This way, we are **being protected from repeat messages**, which was the role of seqno in ordinary wallets. -### Sending Messages +### Sending messages -After the contract has accepted the external message, a loop starts, in which the `slices` stored in the dictionary are taken. These slices store messages' modes and the messages themselves. Sending new messages takes place until the dictionary is empty. +Once the contract accepts the external message, it initiates a loop. During this loop, the contract retrieves the `slices` stored in the dictionary, which contain the message modes and the messages themselves. The contract continues sending new messages until the dictionary is empty. ```func int i = -1; ;; we write -1 because it will be the smallest value among all dictionary keys do { - (i, var cs, var f) = dict.idict_get_next?(16, i); ;; get the key and its corresponding value with the smallest key, which is greater than i - if (f) { ;; check if any value was found - var mode = cs~load_uint(8); ;; load message mode - send_raw_message(cs~load_ref(), mode); ;; load message itself and send it - } + (i, var cs, var f) = dict.idict_get_next?(16, i); ;; get the key and its corresponding value with the smallest key, which is greater than i + if (f) { ;; check if any value was found + var mode = cs~load_uint(8); ;; load message mode + send_raw_message(cs~load_ref(), mode); ;; load message itself and send it + } } until (~ f); ;; if any value was found continue ``` @@ -2656,26 +2645,26 @@ do { > > ["idict_get_next()" in docs](/v3/documentation/smart-contracts/func/docs/stdlib/#dict_get_next) -Note that if a value is found, `f` is always equal to -1 (true). The `~ -1` operation (bitwise not) will always return a value of 0, meaning that the loop should be continued. At the same time, when a dictionary is filled with messages, it is necessary to start calculating those **with a value greater than -1** (e.g., 0) and continue increasing the value by 1 with each message. This structure allows messages to be sent in the correct sequential order. +Note that if a value is found, `f` always equals `-1` (true). The `~ -1` operation (bitwise NOT) will always return `0`, meaning the loop should continue. However, when filling the dictionary with messages, you must start calculating values **greater than `-1`** (e.g., `0`) and increment the value by `1` for each subsequent message. This structure ensures that messages are sent in the correct sequential order. -### Removing Expired Queries +### Removing expired queries -Typically, [smart contracts on TON pay for their own storage](/v3/documentation/smart-contracts/transaction-fees/fees-low-level#storage-fee). This means that the amount of data smart contracts can store is limited to prevent high network loading. To allow the system to be more efficient, messages that are more than 64 seconds old are removed from the storage. This is conducted as follows: +Typically, [smart contracts on TON pay for their storage](/v3/documentation/smart-contracts/transaction-fees/fees-low-level#storage-fee). This limits the amount of data smart contracts can store, preventing excessive network load. Messages older than 64 seconds are automatically removed from storage to improve system efficiency. This process works as follows: ```func bound -= (64 << 32); ;; clean up records that have expired more than 64 seconds ago old_queries~udict_set_builder(64, query_id, begin_cell()); ;; add current query to dictionary var queries = old_queries; ;; copy dictionary to another variable do { - var (old_queries', i, _, f) = old_queries.udict_delete_get_min(64); - f~touch(); - if (f) { ;; check if any value was found - f = (i < bound); ;; check if more than 64 seconds have elapsed after expiration - } - if (f) { - old_queries = old_queries'; ;; if yes save changes in our dictionary - last_cleaned = i; ;; save last removed query - } + var (old_queries', i, _, f) = old_queries.udict_delete_get_min(64); + f~touch(); + if (f) { ;; check if any value was found + f = (i < bound); ;; check if more than 64 seconds have elapsed after the expiration + } + if (f) { + old_queries = old_queries'; ;; if yes, save changes in our dictionary + last_cleaned = i; ;; save last removed query + } } until (~ f); ``` @@ -2683,19 +2672,19 @@ do { > > ["udict_delete_get_min()" in docs](/v3/documentation/smart-contracts/func/docs/stdlib/#dict_delete_get_min) -Note that it is necessary to interact with the `f` variable several times. Since the [TVM is a stack machine](/v3/documentation/tvm/tvm-overview#tvm-is-a-stack-machine), during each interaction with the `f` variable it is necessary to pop all values to get the desired variable. The `f~touch()` operation places the f variable at the top of the stack to optimize code execution. +It is necessary to interact with the `f` variable several times. Since the [TVM is a stack machine](/v3/documentation/tvm/tvm-overview#tvm-is-a-stack-machine), during each interaction with the `f` variable, it is necessary to pop all values to get the desired variable. The `f~touch()` operation places the f variable at the top of the stack to optimize code execution. -### Bitwise Operations +### Bitwise operations -This section may seem a bit complicated for those who have not previously worked with bitwise operations. The following line of code can be seen in the smart contract code: +This section might be challenging for those unfamiliar with bitwise operations. The following line of code appears in the smart contract: ```func var bound = (now() << 32); ;; bitwise left shift operation ``` -As a result 32 bits are added to the number on the right side. This means that **existing values are moved 32 bits to the left**. For example, let’s consider the number 3 and translate it into a binary form with a result of 11. Applying the `3 << 2` operation, 11 is moved 2 bit places. This means that two bits are added to the right of the string. In the end, we have 1100, which is 12. +As a result, 32 bits are added to the number on the right side. This means **existing values are shifted 32 bits to the left**. For example, let’s take the number `3` and convert it to binary, resulting in `11`. Applying the `3 << 2` operation shifts `11` two bit positions to the left, adding two `0`s to the right. This gives us `1100`, which equals `12`. -The first thing to understand about this process is to remember that the `now()` function returns a result of uint32, meaning that the resulting value will be 32 bits. By shifting 32 bits to the left, space is opened up for another uint32, resulting in the correct query_id. This way, the **timestamp and query_id can be combined** within one variable for optimization. +The first thing to understand about this process is to remember that the `now()` function returns a result of uint32, meaning that the resulting value will be 32 bits. Shifting 32 bits to the left opens space for another uint32, resulting in the correct query_id. This way, the **timestamp and query_id can be combined** within one variable for optimization. Next, let’s consider the following line of code: @@ -2703,62 +2692,62 @@ Next, let’s consider the following line of code: bound -= (64 << 32); ;; clean up the records that have expired more than 64 seconds ago ``` -Above we performed an operation to shift the number 64 by 32 bits to **subtract 64 seconds** from our timestamp. This way we'll be able to compare past query_ids and see if they are less than the received value. If so, they expired more than 64 seconds ago: +Above, we performed a bitwise shift operation on the number `64` by 32 bits to **subtract 64 seconds** from our timestamp. This allows us to compare past `query_id`s and determine if they are less than the calculated value. If they are, it means they expired more than 64 seconds ago: ```func if (f) { ;; check if any value has been found - f = (i < bound); ;; check if more than 64 seconds have elapsed after expiration + f = (i < bound); ;; check if more than 64 seconds have elapsed after the expiration } ``` -To understand this better, let’s use the number `1625918400` as an example of a timestamp. Its binary representation (with the left-handed addition of zeros for 32 bits) is 01100000111010011000101111000000. By performing a 32 bit bitwise left shift, the result is 32 zeros at the end of the binary representation of our number. +To better understand this, let’s use the number `1625918400` as an example of a timestamp. Its binary representation (with 32 bits, padded with zeros on the left) is `01100000111010011000101111000000`. By performing a 32-bit left shift, we add 32 zeros to the end of the binary representation of our number. -After this is completed, **it is possible to add any query_id (uint32)**. Then by subtracting `64 << 32` the result is a timestamp that 64 seconds ago had the same query_id. This fact can be verified by performing the following calculations `((1625918400 << 32) - (64 << 32)) >> 32`. This way we can compare the necessary portions of our number (the timestamp) and at the same time the query_id does not interfere. +After this operation, **we can add any `query_id` (uint32)**. By subtracting `64 << 32`, we obtain a timestamp representing the same `query_id` 64 seconds ago. This can be verified by performing the calculation `((1625918400 << 32) - (64 << 32)) >> 32`. This approach allows us to compare the relevant portions of our number (the timestamp) without interference from the `query_id`. -### Storage Updates +### Storage updates After all operations are complete, the only task remaining is to save the new values in the storage: ```func - set_data(begin_cell() - .store_uint(stored_subwallet, 32) - .store_uint(last_cleaned, 64) - .store_uint(public_key, 256) - .store_dict(old_queries) - .end_cell()); + set_data(begin_cell() + .store_uint(stored_subwallet, 32) + .store_uint(last_cleaned, 64) + .store_uint(public_key, 256) + .store_dict(old_queries) + .end_cell()); } ``` -### GET Methods +### GET methods The last thing we have to consider before we dive into wallet deployment and message creation is high-load wallet GET methods: -| Method | Explanation | -| :--------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -| int processed?(int query_id) | Notifies the user if a particular request has been processed. This means it returns `-1` if the request has been processed and `0` if it has not. Also, this method may return `1` if the answer is unknown since the request is old and no longer stored in the contract. | -| int get_public_key() | Rerive a public key. We have considered this method before. | +| Method | Explanation | +| :--------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| int processed?(int query_id) | Notifies the user if a request has been processed. This means it returns `-1` if the request has been processed and `0` if it has not. Also, this method may return `1` if the answer is unknown since the request is old and no longer stored in the contract. | +| int get_public_key() | Rerive a public key. We have considered this method before. | -Let’s look at the `int processed?(int query_id)` method closely to help us to understand why we need to make use of the last_cleaned: +Let’s look at the `int processed?(int query_id)` method closely to help us understand why we need to make use of the last_cleaned: ```func int processed?(int query_id) method_id { - var ds = get_data().begin_parse(); - var (_, last_cleaned, _, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict()); - ds.end_parse(); - (_, var found) = old_queries.udict_get?(64, query_id); - return found ? true : - (query_id <= last_cleaned); + var ds = get_data().begin_parse(); + var (_, last_cleaned, _, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict()); + ds.end_parse(); + (_, var found) = old_queries.udict_get?(64, query_id); + return found ? true : - (query_id <= last_cleaned); } ``` -The `last_cleaned` is retrieved from the storage of the contract and a dictionary of old queries. If the query is found, it is to be returned true, and if not, the expression `- (query_id <= last_cleaned)`. The last_cleaned contains the last removed request **with the highest timestamp**, as we started with the minimum timestamp when deleting the requests. +The `last_cleaned` value is retrieved from the contract storage and the dictionary of old queries. If the query is found, the method returns `true`. If not, it evaluates the expression `- (query_id <= last_cleaned)`. The `last_cleaned` value contains the last removed request **with the highest timestamp**, as we started deleting requests from the minimum timestamp. -This means that if the query_id passed to the method is smaller than the last last_cleaned value, it is impossible to determine whether it was ever in the contract or not. Therefore the `query_id <= last_cleaned` returns -1 while the minus before this expression changes the answer to 1. If query_id is larger than last_cleaned method, then it has not yet been processed. +If the `query_id` passed to the method is smaller than the `last_cleaned` value, it’s impossible to determine whether it was ever in the contract. Therefore, the expression `query_id <= last_cleaned` returns `-1`, and the minus before it changes the result to `1`. If the `query_id` is larger than `last_cleaned`, the method confirms that it hasn’t been processed yet. -### Deploying High-Load Wallet V2 +### Deploying high-load wallet v2 -In order to deploy a high-load wallet it is necessary to generate a mnemonic key in advance, which will be used by the user. It is possible to use the same key that was used in previous sections of this tutorial. +To deploy a high-load wallet, you need to generate a mnemonic key in advance, which the user will use. You can reuse the same key from previous sections of this tutorial. -To begin the process required to deploy a high-load wallet it's necessary to copy [the code of the smart contract](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/new-highload-wallet-v2.fif) to the same directory where the stdlib.fc and wallet_v3 are located and remember to add `#include "stdlib.fc";` to the beginning of the code. Next we’ll compile the high-load wallet code like we did in [section three](/v3/guidelines/smart-contracts/howto/wallet#compiling-wallet-code): +To begin the process required to deploy a high-load wallet it's necessary to copy [the code of the smart contract](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/new-highload-wallet-v2.fif) to the same directory where the stdlib.fc and wallet_v3 are located and remember to add `#include "stdlib.fc";` to the beginning of the code. Next, we’ll compile the high-load wallet code as we did in [section three](/v3/guidelines/smart-contracts/howto/wallet#compiling-wallet-code): @@ -2785,7 +2774,7 @@ if (result.status === "error") { const codeCell = Cell.fromBoc(Buffer.from(result.codeBoc, "base64"))[0]; -// now we have base64 encoded BOC with compiled code in result.codeBoc +// now we have base64 encoded BOC with compiled code in the result.codeBoc console.log("Code BOC: " + result.codeBoc); console.log("\nHash: " + codeCell.hash().toString("base64")); // get the hash of cell and convert in to base64 encoded string ``` @@ -2801,7 +2790,7 @@ Code BOC: te6ccgEBCQEA5QABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQHq8oMI1xgg0x/TP/gjqh9TIL Hash: lJTRzI7fEvBWcaGpugmSEJbrUIEeGSTsZcPGKfu4CBI= ``` -With the above result it is possible to use the base64 encoded output to retrieve the cell with our wallet code in other libraries and languages as follows: +With the above result, it is possible to use the base64 encoded output to retrieve the cell with our wallet code in other libraries and languages as follows: @@ -2820,13 +2809,13 @@ if err != nil { // check if there is any error panic(err) } -log.Println("Hash:", base64.StdEncoding.EncodeToString(codeCell.Hash())) // get the hash of our cell, encode it to base64 because it has []byte type and output to the terminal +log.Println("Hash:", base64.StdEncoding.EncodeToString(codeCell.Hash())) // get the hash of our cell, encode it to base64 because it has []byte type, and output to the terminal ``` -Now we need to retrieve a cell composed of its initial data, build a State Init, and calculate a high-load wallet address. After studying the smart contract code it became clear that the subwallet_id, last_cleaned, public_key and old_queries are sequentially stored in the storage: +Next, we need to retrieve a cell containing its initial data, build a State Init, and calculate the high-load wallet address. After analyzing the smart contract code, we determined that the `subwallet_id`, `last_cleaned`, `public_key`, and `old_queries` are stored sequentially in the storage: @@ -2843,7 +2832,7 @@ const dataCell = beginCell() .storeUint(698983191, 32) // Subwallet ID .storeUint(0, 64) // Last cleaned .storeBuffer(highloadKeyPair.publicKey) // Public Key - .storeBit(0) // indicate that the dictionary is empty + .storeBit(0) // indicates that the dictionary is empty .endCell(); const stateInit = beginCell() @@ -2876,7 +2865,7 @@ import ( highloadMnemonicArray := strings.Split("put your mnemonic that you have generated and saved before", " ") // word1 word2 word3 mac := hmac.New(sha512.New, []byte(strings.Join(highloadMnemonicArray, " "))) hash := mac.Sum(nil) -k := pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries "TON default seed" is used as salt when getting keys +k := pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries, "TON default seed" is used as salt when getting keys // 32 is a key len highloadPrivateKey := ed25519.NewKeyFromSeed(k) // get private key highloadPublicKey := highloadPrivateKey.Public().(ed25519.PublicKey) // get public key from private key @@ -2906,18 +2895,18 @@ log.Println("Contract address:", contractAddress.String()) // Output contract :::caution -Everything we have detailed above follows the same steps as the contract [deployment via wallet](/v3/guidelines/smart-contracts/howto/wallet#contract-deployment-via-wallet) section. To better understanding, read the entire [GitHub source code](https://github.com/aSpite/wallet-tutorial). +Everything we have detailed above follows the same steps as the contract [deployment via wallet](/v3/guidelines/smart-contracts/howto/wallet#contract-deployment-via-wallet) section. To better understand, read the entire [GitHub source code](https://github.com/aSpite/wallet-tutorial). ::: -### Sending High-Load Wallet V2 Messages +### Sending high-load wallet v2 messages -Now let’s program a high-load wallet to send several messages at the same time. For example, let's take 12 messages per transaction so that the gas fees are small. +Now, let’s program a high-load wallet to send several messages simultaneously. For example, let's take 12 messages per transaction so that the gas fees are small. :::info High-load balance -To complete the transaction, the balance of the contract must be at least 0.5 TON. +The contract balance must be at least 0.5 TON to complete the transaction. ::: -Each message carry its own comment with code and the destination address will be the wallet from which we deployed: +Each message carries its own comment with code, and the destination address will be the wallet from which we deployed: @@ -2927,7 +2916,7 @@ import { Address, beginCell, Cell, toNano } from "@ton/core"; let internalMessages: Cell[] = []; const walletAddress = Address.parse( - "put your wallet address from which you deployed high-load wallet" + "put your wallet address from which you deployed the high-load wallet" ); for (let i = 0; i < 12; i++) { @@ -2986,14 +2975,14 @@ for i := 0; i < 12; i++ { MustStoreRef(internalMessage). EndCell() - internalMessages = append(internalMessages, messageData) + internalMessages = append(internalMessages, messageData) } ``` -After completing the above process, the result is an array of internal messages. Next, it's necessary to create a dictionary for message storage and prepare and sign the message body. This is completed as follows: +After completing the above process, the result is an array of internal messages. Next, creating a dictionary for message storage and preparing and signing the message body is necessary. This is completed as follows: @@ -3003,45 +2992,45 @@ import { Dictionary } from '@ton/core'; import { mnemonicToWalletKey, sign } from '@ton/crypto'; import * as crypto from 'crypto'; -const dictionary = Dictionary.empty(); // create an empty dictionary with the key as a number and the value as a cell +const dictionary = Dictionary.empty(); // create an empty dictionary with the key as a number and the value as a cell for (let i = 0; i < internalMessages.length; i++) { const internalMessage = internalMessages[i]; // get our message from an array dictionary.set(i, internalMessage); // save the message in the dictionary } -const queryID = crypto.randomBytes(4).readUint32BE(); // create a random uint32 number, 4 bytes = 32 bits +const queryID = crypto.randomBytes(4).readUint32BE(); // Create a random uint32 number, 4 bytes = 32 bits const now = Math.floor(Date.now() / 1000); // get current timestamp const timeout = 120; // timeout for message expiration, 120 seconds = 2 minutes const finalQueryID = (BigInt(now + timeout) << 32n) + BigInt(queryID); // get our final query_id -console.log(finalQueryID); // print query_id. With this query_id we can call GET method to check if our request has been processed +console.log(finalQueryID); // print query_id. With this query_id, we can call the GET method to check if our request has been processed const toSign = beginCell() - .storeUint(698983191, 32) // subwallet_id - .storeUint(finalQueryID, 64) - // Here we create our own method that will save the + .storeUint(698983191, 32) // subwallet_id + .storeUint(finalQueryID, 64) + // Here, we create our own method that will save the // message mode and a reference to the message - .storeDict(dictionary, Dictionary.Keys.Int(16), { - serialize: (src, buidler) => { + .storeDict(dictionary, Dictionary.Keys.Int(16), { + serialize: (src, builder) => { buidler.storeUint(3, 8); // save message mode, mode = 3 - buidler.storeRef(src); // save message as reference - }, + builder.storeRef(src); // save message as reference + }, // We won't actually use this, but this method - // will help to read our dictionary that we saved + // will help to read the dictionary that we saved parse: (src) => { let cell = beginCell() - .storeUint(src.loadUint(8), 8) - .storeRef(src.loadRef()) - .endCell(); + .storeUint(src.loadUint(8), 8) + .storeRef(src.loadRef()) + .endCell(); return cell; - } - } + } + } ); const highloadMnemonicArray = 'put your high-load wallet mnemonic'.split(' '); const highloadKeyPair = await mnemonicToWalletKey(highloadMnemonicArray); // extract private and public keys from mnemonic const highloadWalletAddress = Address.parse('put your high-load wallet address'); -const signature = sign(toSign.endCell().hash(), highloadKeyPair.secretKey); // get the hash of our message to wallet smart contract and sign it to get signature +const signature = sign(toSign.endCell().hash(), highloadKeyPair.secretKey); // get the hash of our message to the wallet smart contract and sign it to get the signature ``` @@ -3066,14 +3055,14 @@ for i := 0; i < len(internalMessages); i++ { err := dictionary.SetIntKey(big.NewInt(int64(i)), internalMessage) // save the message in the dictionary if err != nil { return - } + } } queryID := rand.Uint32() timeout := 120 // timeout for message expiration, 120 seconds = 2 minutes now := time.Now().Add(time.Duration(timeout)*time.Second).UTC().Unix() << 32 // get current timestamp + timeout finalQueryID := uint64(now) + uint64(queryID) // get our final query_id -log.Println(finalQueryID) // print query_id. With this query_id we can call GET method to check if our request has been processed +log.Println(finalQueryID) // print query_id. With this query_id, we can call the GET method to check if our request has been processed toSign := cell.BeginCell(). MustStoreUInt(698983191, 32). // subwallet_id @@ -3083,7 +3072,7 @@ toSign := cell.BeginCell(). highloadMnemonicArray := strings.Split("put your high-load wallet mnemonic", " ") // word1 word2 word3 mac := hmac.New(sha512.New, []byte(strings.Join(highloadMnemonicArray, " "))) hash := mac.Sum(nil) -k := pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries "TON default seed" is used as salt when getting keys +k := pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries, "TON default seed" is used as salt when getting keys // 32 is a key len highloadPrivateKey := ed25519.NewKeyFromSeed(k) // get private key highloadWalletAddress := address.MustParseAddr("put your high-load wallet address") @@ -3095,10 +3084,10 @@ signature := ed25519.Sign(highloadPrivateKey, toSign.EndCell().Hash()) :::note IMPORTANT -Note that while using JavaScript and TypeScript that our messages were saved into an array without using a send mode. This occurs because during using @ton/ton library, it is expected that developer will implement process of serialization and deserialization by own hands. Therefore, a method is passed that first saves the message mode after it saves the message itself. If we make use of the `Dictionary.Values.Cell()` specification for the value method, it saves the entire message as a cell reference without saving the mode separately. +Note that when using JavaScript and TypeScript, our messages are saved into an array without a send mode. This happens because, when using the `@ton/ton` library, developers are expected to handle the serialization and deserialization process manually. As a result, the method first saves the message mode and then the message itself. Using the `Dictionary.Values.Cell()` specification for the value method saves the entire message as a cell reference without storing the mode separately. ::: -Next we’ll create an external message and send it to the blockchain using the following code: +Next, we’ll create an external message and send it to the blockchain using the following code: @@ -3176,31 +3165,31 @@ if err != nil { -After this process is completed it is possible to look up our wallet and verify that 12 outgoing messages were sent on our wallet. Is it also possible to call the `processed?` GET method using the query_id we initially used in the console. If this request has been processed correctly it provides a result of `-1` (true). +Once complete, you can look up your wallet and verify that 12 outgoing messages were sent. You can also call the `processed?` GET method using the `query_id` initially used in the console. If the request is processed correctly, it will return `-1` (true). ## 🏁 Conclusion -This tutorial provided us with a better understanding of how different wallet types operate on TON Blockchain. It also allowed us to learn how to create external and internal messages without using predefined library methods. +This tutorial helped us better understand how different wallet types operate on TON Blockchain. It also taught us how to create external and internal messages without using predefined library methods. -This helps us to be independent of using libraries and to understand the structure of TON Blockchain in a more in-depth way. We also learned how to use high-load wallets and analyzed many details to do with different data types and various operations. +This helps us be independent of libraries and to understand the structure of TON Blockchain more in-depth. We also learned how to use high-load wallets and analyzed many details related to different data types and various operations. -## 🧩 Next Steps +## 🧩 Next steps -Reading the documentation provided above is a complex undertaking and it’s difficult to understand the entirety of the TON platform. However, it is a good exercise for those passionate about building on the TON. Another suggestion is to begin learning how to write smart contracts on TON by consulting the following resources: [FunC Overview](/v3/documentation/smart-contracts/func/overview), [Best Practices](/v3/guidelines/smart-contracts/guidelines), [Examples of Smart Contracts](/v3/documentation/smart-contracts/contracts-specs/examples), [FunC Cookbook](/v3/documentation/smart-contracts/func/cookbook) +Reading the documentation provided above is a complex undertaking, and it’s difficult to understand the entirety of the TON platform. However, it is a good exercise for those passionate about building on the TON. Another suggestion is to begin learning how to write smart contracts on TON by consulting the following resources: [FunC Overview](/v3/documentation/smart-contracts/func/overview), [Best Practices](/v3/guidelines/smart-contracts/guidelines), [Examples of Smart Contracts](/v3/documentation/smart-contracts/contracts-specs/examples), [FunC Cookbook](/v3/documentation/smart-contracts/func/cookbook) Additionally, it is recommended that readers familiarize themselves with the following documents in more detail: [ton.pdf](https://docs.ton.org/ton.pdf) and [tblkch.pdf](https://ton.org/tblkch.pdf) documents. -## 📬 About the Author +## 📬 About the author -If you have any questions, comments, or suggestions please reach out to the author of this documentation section on [Telegram](https://t.me/aspite) (@aSpite or @SpiteMoriarty) or [GitHub](https://github.com/aSpite). +If you have any questions, comments, or suggestions, please contact the author of this documentation section on [Telegram](https://t.me/aspite) (@aSpite or @SpiteMoriarty) or [GitHub](https://github.com/aSpite). -## 📖 See Also +## 📖 See also - Wallets' source code: [V3](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/wallet3-code.fc), [V4](https://github.com/ton-blockchain/wallet-contract/blob/main/func/wallet-v4-code.fc), [High-load](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/new-highload-wallet-v2.fif) - Useful concept documents(may include outdated information): [ton.pdf](https://docs.ton.org/ton.pdf), [tblkch.pdf](https://ton.org/tblkch.pdf), [tvm.pdf](https://ton.org/tvm.pdf) -The main sources of code: +The primary sources of code: - [@ton/ton (JS/TS)](https://github.com/ton-org/ton) - [@ton/core (JS/TS)](https://github.com/ton-org/ton-core) From 13826c8c9672091d2a4bc69a2c3a27db9e40c527 Mon Sep 17 00:00:00 2001 From: AlexG <39581753+reveloper@users.noreply.github.com> Date: Tue, 11 Mar 2025 10:53:53 +0900 Subject: [PATCH 14/23] Update fee-calculation.md Improve readability. --- .../smart-contracts/fee-calculation.md | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/v3/guidelines/smart-contracts/fee-calculation.md b/docs/v3/guidelines/smart-contracts/fee-calculation.md index cc79147e926..03e451bd178 100644 --- a/docs/v3/guidelines/smart-contracts/fee-calculation.md +++ b/docs/v3/guidelines/smart-contracts/fee-calculation.md @@ -1,10 +1,12 @@ # Fees calculation -When your contract begins processing an incoming message, you should verify the amount of TONs attached to the message to ensure it is sufficient to cover [all types of fees](/v3/documentation/smart-contracts/transaction-fees/fees#elements-of-transaction-fee). To achieve this, you need to calculate (or predict) the fee for the current transaction. +## Introducation + +When your contract begins processing an incoming message, you should verify the number of TONs attached to the message to ensure it is sufficient to cover [all types of fees](/v3/documentation/smart-contracts/transaction-fees/fees#elements-of-transaction-fee). To achieve this, you need to calculate (or predict) the fee for the current transaction. This document explains how to calculate fees in FunC contracts using the latest TVM opcodes. -:::info More information on opcodes +:::info opcodes For a comprehensive list of TVM opcodes, including those mentioned below, refer to the [TVM instruction page](/v3/documentation/tvm/instructions). ::: @@ -20,7 +22,7 @@ Use the `GETSTORAGEFEE` opcode with the following parameters: | :--------- | :------------------------------------------------------ | | cells | Number of contract cells | | bits | Number of contract bits | -| is_mc | True if the source or destination is in the masterchain | +| is_mc | True if the source or destination is in the MasterChain | :::info Only unique hash cells are counted for storage and forward fees. For example, three identical hash cells are counted as one. @@ -72,7 +74,7 @@ In most cases, use the `GETGASFEE` opcode with the following parameters: | Param | Description | | :--------- | :------------------------------------------------------ | | `gas_used` | Gas amount, calculated in tests and hardcoded | -| `is_mc` | True if the source or destination is in the masterchain | +| `is_mc` | True if the source or destination is in the MasterChain | ### Calculation flow @@ -185,7 +187,7 @@ If the message structure is deterministic, use the `GETFORWARDFEE` opcode with t | :--------- | :------------------------------------------------------ | | cells | Number of cells | | bits | Number of bits | -| is_mc | True if the source or destination is in the masterchain | +| is_mc | True if the source or destination is in the MasterChain | :::info Only unique hash cells are counted for storage and forward fees. For example, three identical hash cells are counted as one. @@ -194,21 +196,19 @@ This mechanism deduplicates data: if multiple equivalent sub-cells are reference [Read more about deduplication](/v3/documentation/data-formats/tlb/library-cells). ::: -However, if the outgoing message depends significantly on the incoming structure, you may not be able to fully predict the fee. In such cases, try using the `GETORIGINALFWDFEE` opcode with the following parameters: +However, if the outgoing message depends significantly on the incoming structure, you may not be able to predict the fee fully. In such cases, try using the `GETORIGINALFWDFEE` opcode with the following parameters: | Param name | Description | | :--------- | :------------------------------------------------------ | | fwd_fee | Parsed from the incoming message | -| is_mc | True if the source or destination is in the masterchain | - -:::caution Be careful with the `SENDMSG` opcode -The `SENDMSG` opcode is the least optimal way to calculate fees, but it is better than not checking. +| is_mc | True if the source or destination is in the MasterChain | -It uses an **unpredictable amount** of gas. - -Avoid using it unless absolutely necessary. +:::caution +Be careful with the `SENDMSG` opcode because it uses an **unpredictable amount** of gas. Avoid using it unless necessary. ::: +The `SENDMSG` opcode is the least optimal way to calculate fees, but it is better than not checking. + If even `GETORIGINALFWDFEE` cannot be used, one more option exists. Use the `SENDMSG` opcode with the following parameters: | Param name | Description | From a066e779f582cf7783d3baadbad9db7d869bcae4 Mon Sep 17 00:00:00 2001 From: AlexG <39581753+reveloper@users.noreply.github.com> Date: Tue, 11 Mar 2025 11:06:18 +0900 Subject: [PATCH 15/23] Update get-methods.md Improve readability. --- .../guidelines/smart-contracts/get-methods.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/v3/guidelines/smart-contracts/get-methods.md b/docs/v3/guidelines/smart-contracts/get-methods.md index 87099175ab1..a81e1bd773d 100644 --- a/docs/v3/guidelines/smart-contracts/get-methods.md +++ b/docs/v3/guidelines/smart-contracts/get-methods.md @@ -8,7 +8,7 @@ To fully benefit from this content, readers must understand the [FunC programmin Get methods are special functions in smart contracts that allow you to query specific data. Their execution doesn't cost any fees and happens outside of the blockchain. -These functions are very common in most smart contracts. For example, the default [Wallet contract](/v3/documentation/smart-contracts/contracts-specs/wallet-contracts/) has several get methods, such as `seqno()`, `get_subwallet_id()` and `get_public_key()`. Wallets, SDKs, and APIs use them to fetch data about wallets. +These functions are widespread in most smart contracts. For example, the default [Wallet contract](/v3/documentation/smart-contracts/contracts-specs/wallet-contracts/) has several get methods, such as `seqno()`, `get_subwallet_id()` and `get_public_key()`. Wallets, SDKs, and APIs use them to fetch data about wallets. ## Design patterns for get methods @@ -24,7 +24,7 @@ return get_data().begin_parse().preload_uint(64); } ``` -2. **Aggregate Data Retrieval**: Another common method is to create methods that gather multiple pieces of data from a contract's state in one call. This is useful when specific data points are often used together. You can see this approach frequently in [Jetton](#jettons) and [NFT](#nfts) contracts. +2. **Aggregate data retrieval**: Another common method is to create methods that gather multiple pieces of data from a contract's state in one call. This is useful when specific data points are often used together. You can see this approach frequently in [Jetton](#jettons) and [NFT](#nfts) contracts. Example: @@ -80,7 +80,7 @@ int get_subwallet_id() method_id { } ``` -- [What is Subwallet ID?](/v3/guidelines/smart-contracts/howto/wallet#subwallet-ids/) +- [What is subwallet ID?](/v3/guidelines/smart-contracts/howto/wallet#subwallet-ids/) #### get_public_key() @@ -205,7 +205,7 @@ Given an index and [individual NFT content](#get_nft_data), this method fetches #### Tonviewer -You can call get methods on the bottom of the page in the "Methods" tab. +You can call get methods at the bottom of the page in the **Methods** tab. - https://tonviewer.com/EQAWrNGl875lXA6Fff7nIOwTIYuwiJMq0SmtJ5Txhgnz4tXI?section=method @@ -260,7 +260,7 @@ This code will produce an output in the format `Total: 123`. The number may vary ### Testing get methods -For testing smart contracts, we can use the [Sandbox](https://github.com/ton-community/sandbox/), which is installed by default in new Blueprint projects. +We can use the [Sandbox](https://github.com/ton-community/sandbox/) to test smart contracts, which is installed by default in new Blueprint projects. First, you must add a special method in the contract wrapper to execute the get method and return the typed result. Let's say your contract is called _Counter_, and you have already implemented the method to update the stored number. Open `wrappers/Counter.ts` and add the following method: @@ -289,7 +289,7 @@ You can check it by running `npx blueprint test` in your terminal. If you did ev Contrary to what might seem intuitive, invoking get methods from other contracts is impossible on-chain. This limitation stems primarily from the nature of blockchain technology and the need for consensus. -First, acquiring data from another shardchain may introduce significant latency. Such delays could disrupt the contract execution flow, as blockchain operations are designed to execute in a deterministic and timely manner. +First, acquiring data from another ShardChain may introduce significant latency. Such delays could disrupt the contract execution flow, as blockchain operations are designed to execute in a deterministic and timely manner. Second, achieving consensus among validators would be problematic. Validators would also need to invoke the same get method to verify a transaction's correctness. However, if the state of the target contract changes between these multiple invocations, validators could end up with differing versions of the transaction result. @@ -361,12 +361,15 @@ For simplicity, we used just simple little numbers 1, 2, and 3 for the operation ## Common pitfalls and how to avoid them -1. **Misuse of get methods**: As mentioned earlier, get methods are designed to return data from the contract's state and are not meant to change the contract's state. Attempting to alter the contract's state within a get method will not actually do it. +1. **Misuse of get methods**: As mentioned earlier, get methods are designed to return data from the contract's state and are not meant to change the contract's state. Attempting to alter the contract's state within a get method will not do it. 2. **Ignoring return types**: Every get method must have a clearly defined return type that matches the retrieved data. If a method is expected to return a specific type of data, ensure that all execution paths within the method return this type. Inconsistent return types should be avoided, as they can lead to errors and complications when interacting with the contract. -3. **Assuming cross-contract calls**: A common misconception is that get methods can be called directly from other contracts on-chain. However, as previously discussed, this is not possible due to the inherent nature of blockchain technology and the requirement for consensus. Always keep in mind that get methods are designed for off-chain use, while on-chain interactions between contracts are facilitated through internal messages. +3. **Assuming cross-contract calls**: A common misconception is that get methods can be called directly from other contracts on-chain. However, as previously discussed, this is not possible due to the inherent nature of blockchain technology and the requirement for consensus. Always remember that get methods are designed for off-chain use, while on-chain interactions between contracts are facilitated through internal messages. ## Conclusion Get methods are vital for querying data from smart contracts on the TON Blockchain. While they have certain limitations, understanding these constraints and learning how to work around them is crucial for effectively utilizing get methods in your smart contracts. + +## See also +- [Writing tests examples](/v3/guidelines/smart-contracts/testing/writing-test-examples/) From 2cf22d176a7db319f8098fdae35e08d2413c46c3 Mon Sep 17 00:00:00 2001 From: AlexG <39581753+reveloper@users.noreply.github.com> Date: Tue, 11 Mar 2025 11:32:01 +0900 Subject: [PATCH 16/23] Update wallet.md Improve readability. --- .../smart-contracts/howto/wallet.md | 75 ++++++++++++------- 1 file changed, 48 insertions(+), 27 deletions(-) diff --git a/docs/v3/guidelines/smart-contracts/howto/wallet.md b/docs/v3/guidelines/smart-contracts/howto/wallet.md index 5c388c2b729..c62fbb61c6a 100644 --- a/docs/v3/guidelines/smart-contracts/howto/wallet.md +++ b/docs/v3/guidelines/smart-contracts/howto/wallet.md @@ -600,7 +600,7 @@ privateKey := ed25519.NewKeyFromSeed(k) -To proceed, we need to send the `seqno`, `keys`, and `internal message`. Next, we’ll create a [message](/v3/documentation/smart-contracts/message-management/sending-messages) for our wallet and store the data in the sequence outlined at the beginning of the tutorial. This is achieved as follows: +To proceed, we must send the `seqno`, `keys`, and `internal message`. Next, we’ll create a [message](/v3/documentation/smart-contracts/message-management/sending-messages) for our wallet and store the data in the sequence outlined at the beginning of the tutorial. This is achieved as follows: @@ -739,7 +739,7 @@ if err != nil { > 💡 Useful link: > -> [More about Bag of Cells](/v3/documentation/data-formats/tlb/cell-boc#bag-of-cells) +> More about [Bag of cells](/v3/documentation/data-formats/tlb/cell-boc#bag-of-cells) As a result, we got the output of our BOC in the console, and the message was sent to our wallet. By copying the base64 encoded string, it is possible to [manually send our message and retrieve the hash using toncenter](https://toncenter.com/api/v2/#/send/send_boc_return_hash_sendBocReturnHash_post). @@ -848,7 +848,7 @@ var subWallet uint64 = 698983191 ### Compiling wallet code -Now that the private and public keys and the `subwallet_id` are clearly defined, we need to compile the wallet code. We’ll use the [wallet v3 code](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/wallet3-code.fc) from the official repository. +Now that the private and public keys and the `subwallet_id` are clearly defined, we must compile the wallet code. We’ll use the [wallet v3 code](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/wallet3-code.fc) from the official repository. The [@ton-community/func-js](https://github.com/ton-community/func-js) library is necessary to compile wallet code. This library allows us to compile FunC code and retrieve a cell containing the code. To get started, install the library and save it to the `package.json` as follows: @@ -858,7 +858,7 @@ npm i --save @ton-community/func-js We’ll only use JavaScript to compile code, as the libraries for compiling code are JavaScript-based. However, after compiling is finalized, as long as we have our cell's **base64 output**, it is possible to use this compiled code in languages such as Go and others. -First, we need to create two files: `wallet_v3.fc` and `stdlib.fc`. The compiler relies on the `stdlib.fc` library, which contains all the necessary basic functions corresponding to `asm` instructions. You can download the `stdlib.fc` file [here](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/stdlib.fc). For the `wallet_v3.fc` file, copy the code from the repository. +First, we must create two files: `wallet_v3.fc` and `stdlib.fc`. The compiler relies on the `stdlib.fc` library, which contains all the necessary basic functions corresponding to `asm` instructions. You can download the `stdlib.fc` file [here](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/stdlib.fc). For the `wallet_v3.fc` file, copy the code from the repository. Now, we have the following structure for the project we are creating: @@ -960,7 +960,7 @@ Before building a message, it is essential to understand what a State Init is. F | Option | Explanation | | :---------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | | split_depth | This option is designed for highly loaded smart contracts that can be split and distributed across multiple [shardchains](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains#many-accountchains-shards). For more details on how this works, refer to the [tblkch.pdf](https://ton.org/tblkch.pdf) (section 4.1.6). Since this feature is not needed for wallet smart contracts, only a `0` bit is stored. | -| special | This option is used for **TicTok** smart contracts, which are automatically triggered for each block. Regular smart contracts, such as wallets, do not require this functionality. For more details, refer to [this section](/v3/documentation/data-formats/tlb/transaction-layout#tick-tock) or the [tblkch.pdf](https://ton.org/tblkch.pdf) (section 4.1.6). Since this feature is unnecessary for our use case, only a `0` bit is stored. | +| special | This option is used for **TicTok** smart contracts that are automatically triggered for each block. Regular smart contracts, such as wallets, do not require this functionality. For more details, refer to [this section](/v3/documentation/data-formats/tlb/transaction-layout#tick-tock) or the [tblkch.pdf](https://ton.org/tblkch.pdf) (section 4.1.6). Since this feature is unnecessary for our use case, only a `0` bit is stored. | | | | code | `1` bit means the presence of the smart contract code as a reference. | | data | `1` bit means the presence of the smart contract data as a reference. | @@ -1045,10 +1045,12 @@ log.Println("Contract address:", contractAddress.String()) // Output contract ad We can build and send the message to the blockchain using the State Init. :::warning +Keep in mind this concept for your services +::: + To carry out this process, **a minimum wallet balance of 0.1 TON** is required (the balance can be less, but this amount is guaranteed sufficient). To accomplish this, we’ll need to run the code mentioned earlier in the tutorial, obtain the correct wallet address, and send 0.1 TON to this address. Alternatively, you can send this sum manually via your wallet app before sending the deployment message. -Deployment by external messages is presented here primarily for educational purposes; in practice, it's much more convenient to [deploy smart contracts via Wallets](/v3/guidelines/smart-contracts/howto/wallet#contract-deployment-via-wallet), which will be described later. -::: +Deployment by external messages is presented here primarily for educational purposes; in practice, it's much more convenient to [deploy smart contracts via wallets](/v3/guidelines/smart-contracts/howto/wallet#contract-deployment-via-wallet), which will be described later. Let’s start with building a message similar to the one we built **in the previous section**: @@ -1215,9 +1217,9 @@ if err != nil { -Note that we sent an internal message using mode `3`. If you need to redeploy the same wallet, **the smart contract can be destroyed**. To do this, set the mode to `160` by adding 128 (take the entire balance of the smart contract) + 32 (destroy the smart contract). This will retrieve the remaining TON balance and allow you to deploy the wallet again. +Note that we sent an internal message using mode `3`. If you must redeploy the same wallet, **the smart contract can be destroyed**. To do this, set the [mode](/v3/documentation/smart-contracts/message-management/message-modes-cookbook#mode160/) to `160` by adding `128` (take the entire balance of the smart contract) + `32` (destroy the smart contract). This will retrieve the remaining TON balance and allow you to deploy the wallet again. -Remember that for each new transaction, the **seqno must be incremented by one**. +Remember that for each new transaction, the `seqno` must be incremented by one. :::info The contract code we used is [verified](https://tonscan.org/tx/BL9T1i5DjX1JRLUn4z9JOgOWRKWQ80pSNevis26hGvc=), so you can see an example [here](https://tonscan.org/address/EQDBjzo_iQCZh3bZSxFnK9ue4hLTOKgsCNKfC8LOUM4SlSCX#source). @@ -1562,7 +1564,7 @@ After completing this process, you can use a TON blockchain explorer to verify t ### NFT transfers -In addition to regular messages, users often send NFTs to each other. Unfortunately, not all libraries include methods specifically designed for interacting with this type of smart contract. As a result, we need to write code that allows us to construct messages for sending NFTs. First, let’s familiarize ourselves with the TON NFT [standard](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md). +In addition to regular messages, users often send NFTs to each other. Unfortunately, not all libraries specifically use methods for interacting with this type of smart contract. As a result, we need to write code that allows us to construct messages for sending NFTs. First, let’s familiarize ourselves with the TON NFT [standard](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md). Specifically, we need to thoroughly understand the TL-B schema for [NFT Transfers](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md#1-transfer). @@ -1804,7 +1806,7 @@ subscriptionAddress := address.MustParseAddr("EQBTKTis-SWYdupy99ozeOvnEBu8LRrQP_ -Now, we need to retrieve the plugin’s hash address so that it can be translated into a number and sent to the GET Method. +Now, we need to retrieve the plugin’s hash address to translate it into a number and send it to the GET Method. @@ -1848,13 +1850,13 @@ log.Println(getResult.MustInt(0)) // -1 -The response must be `-1`, meaning the result is true. If required, it is also possible to send a slice and a cell. It would be enough to create and transfer a Slice or Cell instead of using the BigInt, specifying the appropriate type. +The response must be `-1`, meaning the result is `true`. It is also possible to send a slice and a cell if required. It would be enough to create and transfer a Slice or Cell instead of using the BigInt, specifying the appropriate type. ### Contract deployment via wallet In chapter three, we deployed a wallet. To accomplish this, we initially sent some TON and a message from the wallet to deploy a smart contract. However, this process is not broadly used with external messages and is often used mainly for wallets. While developing contracts, the deployment process is initialized by sending internal messages. -We’ll use the V3R2 wallet smart contract introduced in [the third chapter](/v3/guidelines/smart-contracts/howto/wallet#compiling-wallet-code) to achieve this. In this case, we’ll set the `subwallet_id` to `3` or any other number required to generate a different address while using the same private key (this value is customizable): +To achieve this, we’ll use the V3R2 wallet smart contract introduced in [the third chapter](/v3/guidelines/smart-contracts/howto/wallet#compiling-wallet-code). In this case, we’ll set the `subwallet_id` to `3` or any other number required to generate a different address while using the same private key (this value is customizable): @@ -1999,9 +2001,23 @@ internalMessage := cell.BeginCell(). :::info -Note that the bits have been specified above and that the stateInit and internalMessageBody have been saved as references. Since the links are stored separately, we could write 4 (0b100) + 2 (0b10) + 1 (0b1) -> (4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1), which means (0b111, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1) and then save two references. +Note that the bits have been specified above and that the stateInit and internalMessageBody have been saved as references. ::: +Since the links are stored separately, we could write: + +```tlb +4 (0b100) + 2 (0b10) + 1 (0b1) -> (4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1) +``` + +Tha also means: + +```tlb +(0b111, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1) +``` +Then, save two references. + + Next, we’ll prepare a message for our wallet and send it: @@ -2345,7 +2361,7 @@ High-Load Wallet V3 can send more than 254 messages, [putting the remaining mess Although the external message limit is 64KB, the larger the external message, the more likely it is to be lost in delivery, so 150 messages is the optimal solution. ::: -### GET vethods +### GET methods High-Load Wallet V3 supports the 5 GET methods: @@ -2572,7 +2588,11 @@ queryHandler.getNext(); -## 🔥 High-load wallet v2 (Outdated) +## 🔥 High-load wallet v2 + +::: warning +High-load wallet v2 is outdated. Do not use this for new projects. +::: In some situations, sending a large number of messages per transaction may be necessary. As previously mentioned, ordinary wallets support sending up to 4 messages simultaneously by storing [a maximum of 4 references](/v3/documentation/data-formats/tlb/cell-boc#cell) in a single cell. High-load wallets only allow 255 messages to be sent at once. This restriction exists because the maximum number of outgoing messages (actions) in the blockchain’s config settings is set to 255. @@ -2603,11 +2623,11 @@ First, let’s examine [the code structure of a high-load wallet smart contract] > 💡 Useful links: > -> ["Bitwise operations" in docs](/v3/documentation/smart-contracts/func/docs/stdlib/#dict_get) +> [Bitwise operations](/v3/documentation/smart-contracts/func/docs/stdlib/#dict_get) > -> ["load_dict()" in docs](/v3/documentation/smart-contracts/func/docs/stdlib/#load_dict) +> [load_dict()](/v3/documentation/smart-contracts/func/docs/stdlib/#load_dict) > -> ["udict_get?()" in docs](/v3/documentation/smart-contracts/func/docs/stdlib/#dict_get) +> [udict_get?()](/v3/documentation/smart-contracts/func/docs/stdlib/#dict_get) You notice some differences from ordinary wallets. Now, let’s take a closer look at more details of how high-load wallets work on TON (except subwallets, as we have gone over this previously). @@ -2670,7 +2690,7 @@ do { > 💡 Useful link: > -> ["udict_delete_get_min()" in docs](/v3/documentation/smart-contracts/func/docs/stdlib/#dict_delete_get_min) +> [udict_delete_get_min()](/v3/documentation/smart-contracts/func/docs/stdlib/#dict_delete_get_min) It is necessary to interact with the `f` variable several times. Since the [TVM is a stack machine](/v3/documentation/tvm/tvm-overview#tvm-is-a-stack-machine), during each interaction with the `f` variable, it is necessary to pop all values to get the desired variable. The `f~touch()` operation places the f variable at the top of the stack to optimize code execution. @@ -3185,9 +3205,7 @@ If you have any questions, comments, or suggestions, please contact the author o ## 📖 See also -- Wallets' source code: [V3](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/wallet3-code.fc), [V4](https://github.com/ton-blockchain/wallet-contract/blob/main/func/wallet-v4-code.fc), [High-load](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/new-highload-wallet-v2.fif) - -- Useful concept documents(may include outdated information): [ton.pdf](https://docs.ton.org/ton.pdf), [tblkch.pdf](https://ton.org/tblkch.pdf), [tvm.pdf](https://ton.org/tvm.pdf) +- Wallets' source code: [V3](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/wallet3-code.fc), [V4](https://github.com/ton-blockchain/wallet-contract/blob/main/func/wallet-v4-code.fc), [High-load](https://github.com/ton-blockchain/highload-wallet-contract-v3) The primary sources of code: @@ -3196,22 +3214,25 @@ The primary sources of code: - [@ton/crypto (JS/TS)](https://github.com/ton-org/ton-crypto) - [tonutils-go (GO)](https://github.com/xssnick/tonutils-go). -Official documentation: +TON documentation: - [Internal messages](/v3/documentation/smart-contracts/message-management/internal-messages) - [External messages](/v3/documentation/smart-contracts/message-management/external-messages) -- [Types of Wallet Contracts](/v3/documentation/smart-contracts/contracts-specs/wallet-contracts#wallet-v4) +- [Types of wallet contracts](/v3/documentation/smart-contracts/contracts-specs/wallet-contracts#wallet-v4) - [TL-B](/v3/documentation/data-formats/tlb/tl-b-language) -- [Blockchain of Blockchains](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains) +- [Blockchain of blockchains](/v3/concepts/dive-into-ton/ton-blockchain/blockchain-of-blockchains) External references: -- [Ton Deep](https://github.com/xssnick/ton-deep-doc) +- [Ton deep](https://github.com/xssnick/ton-deep-doc) - [Block.tlb](https://github.com/ton-blockchain/ton/blob/master/crypto/block/block.tlb) - [Standards in TON](https://github.com/ton-blockchain/TEPs) + +- Useful concept documents(may include outdated information): [ton.pdf](https://docs.ton.org/ton.pdf), [tblkch.pdf](https://ton.org/tblkch.pdf), [tvm.pdf](https://ton.org/tvm.pdf) + From d5e33c10741a39b2627e21c84111245b0d5257ae Mon Sep 17 00:00:00 2001 From: AlexG <39581753+Reveloper@users.noreply.github.com> Date: Tue, 11 Mar 2025 11:45:53 +0900 Subject: [PATCH 17/23] improve_readability --- .../guidelines/smart-contracts/guidelines.mdx | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/v3/guidelines/smart-contracts/guidelines.mdx b/docs/v3/guidelines/smart-contracts/guidelines.mdx index 028603f0569..babbd428628 100644 --- a/docs/v3/guidelines/smart-contracts/guidelines.mdx +++ b/docs/v3/guidelines/smart-contracts/guidelines.mdx @@ -2,20 +2,21 @@ import Button from '@site/src/components/button' # Overview -This page contains a table of contents for TON smart contracts guidelines. +This page navigates within the TON smart contracts guidelines. ## Guides overview ### Smart contract development -| Guide | Stack | Description | -|--------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------|----------------------------------------------------------------------------------| -| [TON Hello world](https://tonhelloworld.com/01-wallet/) | FunC; TS, @ton/ton | Write and deploy your first contract. | -| [Working with wallet smart contracts](/v3/guidelines/smart-contracts/howto/wallet/) | FunC; TS, @ton/ton | Learn in detail interaction with various wallets smartcontracts. | -| [Writing tests with Blueprint](/v3/guidelines/smart-contracts/testing/overview/) | TS, @ton/ton | Learn how to write and invoke local tests for contract. | -| [Writing tests examples](/v3/guidelines/smart-contracts/testing/writing-test-examples/) | TS, @ton/ton | Learn how to write various test suites for edgecases. | -| [Interact with multisig wallets using TypeScript](/v3/guidelines/smart-contracts/howto/multisig-js) | TS, @ton/ton | Learn how to interact to multisig wallet in TON. | -| [Shard optimizations on TON](/v3/guidelines/smart-contracts/howto/shard-optimization/) | TS, @ton/ton | Learn best practice for conract shard optimization. | +| Guide | Stack | Description | +|-------------------------------------------------------------------------------------------------|----------------------------------|----------------------------------------------------------------------------------| +| [TON Hello world](https://tonhelloworld.com/01-wallet/) | FunC; TS, @ton/ton | Write and deploy your first contract. | +| [Working with wallet smart contracts](/v3/guidelines/smart-contracts/howto/wallet/) | FunC; TS, @ton/ton | Learn in detail interaction with various wallets smartcontracts. | +| [Speedrun TON](https://tonspeedrun.tapps.ninja/) | FunC; TS, @ton/ton | Learn contract development with series of guidlines.| +| [Writing tests with Blueprint](/v3/guidelines/smart-contracts/testing/overview/) | TS, @ton/ton | Learn how to write and invoke local tests for contract. | +| [Shard optimizations on TON](/v3/guidelines/smart-contracts/howto/shard-optimization/) | TS, @ton/ton | Learn best practice for conract shard optimization. | +| [Writing tests examples](/v3/guidelines/smart-contracts/testing/writing-test-examples/) | TS, @ton/ton | Learn how to write various test suites for edgecases. | +| [Interact with multisig wallets using TypeScript](/v3/guidelines/smart-contracts/howto/multisig-js) | TS, @ton/ton | Learn how to interact to multisig wallet in TON. | ### Scalability and security From 01f6143be3aa0e5750ce36612bb6200a25196030 Mon Sep 17 00:00:00 2001 From: AlexG <39581753+Reveloper@users.noreply.github.com> Date: Tue, 11 Mar 2025 11:57:14 +0900 Subject: [PATCH 18/23] improve_readability --- .../guidelines/smart-contracts/guidelines.mdx | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/v3/guidelines/smart-contracts/guidelines.mdx b/docs/v3/guidelines/smart-contracts/guidelines.mdx index babbd428628..153533f1cbc 100644 --- a/docs/v3/guidelines/smart-contracts/guidelines.mdx +++ b/docs/v3/guidelines/smart-contracts/guidelines.mdx @@ -8,26 +8,26 @@ This page navigates within the TON smart contracts guidelines. ### Smart contract development -| Guide | Stack | Description | -|-------------------------------------------------------------------------------------------------|----------------------------------|----------------------------------------------------------------------------------| -| [TON Hello world](https://tonhelloworld.com/01-wallet/) | FunC; TS, @ton/ton | Write and deploy your first contract. | -| [Working with wallet smart contracts](/v3/guidelines/smart-contracts/howto/wallet/) | FunC; TS, @ton/ton | Learn in detail interaction with various wallets smartcontracts. | -| [Speedrun TON](https://tonspeedrun.tapps.ninja/) | FunC; TS, @ton/ton | Learn contract development with series of guidlines.| -| [Writing tests with Blueprint](/v3/guidelines/smart-contracts/testing/overview/) | TS, @ton/ton | Learn how to write and invoke local tests for contract. | -| [Shard optimizations on TON](/v3/guidelines/smart-contracts/howto/shard-optimization/) | TS, @ton/ton | Learn best practice for conract shard optimization. | -| [Writing tests examples](/v3/guidelines/smart-contracts/testing/writing-test-examples/) | TS, @ton/ton | Learn how to write various test suites for edgecases. | -| [Interact with multisig wallets using TypeScript](/v3/guidelines/smart-contracts/howto/multisig-js) | TS, @ton/ton | Learn how to interact to multisig wallet in TON. | +| Guide | Stack | Description | +|-------------------------------------------------------------------------------------------------|----------------------------------|-------------------------------------------------------------------| +| [TON Hello world](https://tonhelloworld.com/01-wallet/) | FunC; TS, @ton/ton | Write and deploy your first contract. | +| [Working with wallet smart contracts](/v3/guidelines/smart-contracts/howto/wallet/) | FunC; TS, @ton/ton | Learn in detail interaction with various wallets smart contracts. | +| [Speedrun TON](https://tonspeedrun.tapps.ninja/) | FunC; TS, @ton/ton | Learn contract development with series of guidelines. | +| [Writing tests with Blueprint](/v3/guidelines/smart-contracts/testing/overview/) | TS, @ton/ton | Learn how to write and invoke local tests for contract. | +| [Shard optimizations on TON](/v3/guidelines/smart-contracts/howto/shard-optimization/) | TS, @ton/ton | Learn best practice for contract shard optimization. | +| [Writing tests examples](/v3/guidelines/smart-contracts/testing/writing-test-examples/) | TS, @ton/ton | Learn how to write various test suites for edge cases. | +| [Interact with multisig wallets using TypeScript](/v3/guidelines/smart-contracts/howto/multisig-js) | TS, @ton/ton | Learn how to interact to multisig wallet in TON. | ### Scalability and security -| Guide | Stack | Description | -|--------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------|----------------------------------------------------------------------------------| -| [How to shard your TON smart contract and why](https://blog.ton.org/how-to-shard-your-ton-smart-contract-and-why-studying-the-anatomy-of-tons-jettons) | Desing and architecture | Learn concept of contract system with jetton standard | +| Guide | Stack | Description | +|--------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------|----------------------------------------------------------------------------------| +| [How to shard your TON smart contract and why](https://blog.ton.org/how-to-shard-your-ton-smart-contract-and-why-studying-the-anatomy-of-tons-jettons) | Design and architecture | Learn concept of contract system with jetton standard | | [Airdrop claiming guidelines](/v3/guidelines/smart-contracts/howto/airdrop-claim-best-practice/) | Design and architecture | Learn concepts on contract interactions and their impact on overall performance. | -| [Things to focus on while working with TON blockchain](/v3/guidelines/smart-contracts/security/things-to-focus/) | FunC | Best practies for DApps development in TON. | +| [Things to focus on while working with TON blockchain](/v3/guidelines/smart-contracts/security/things-to-focus/) | FunC | Best practices for DApps development in TON. | | [Secure smart contract programming](/v3/guidelines/smart-contracts/security/secure-programming/) | FunC | Best practices for secure development of smart contracts on FunC. | -| [Drawing conclusions from TON Hack Challenge](/v3/guidelines/smart-contracts/security/ton-hack-challenge-1/) | FunC | Best practices for secure development | +| [Drawing conclusions from TON hack challenge](/v3/guidelines/smart-contracts/security/ton-hack-challenge-1/) | FunC | Best practices for secure development | | [Random number generation](/v3/guidelines/smart-contracts/security/random-number-generation/) | FunC | Generating random numbers in TON for various projects. | @@ -37,7 +37,7 @@ This page navigates within the TON smart contracts guidelines. |--------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------|------------------------------------------------------------------------------------------| | [Generation of block random seed](/v3/guidelines/smart-contracts/security/random/) | C++, Core | Explanation on random in TON. | | [Compilation instructions](/v3/guidelines/smart-contracts/howto/compile/compilation-instructions/) | C++, cmake, Core | Compile executables from source for deep native integration. | -| [Instructions for low-memory machines](/v3/guidelines/smart-contracts/howto/compile/instructions-low-memory/) | С++ | Extnension for compilation on low-memary machines. | +| [Instructions for low-memory machines](/v3/guidelines/smart-contracts/howto/compile/instructions-low-memory/) | С++ | Extension for compilation on low-memory machines. | | [Make a simple multisig contract with fift](/v3/guidelines/smart-contracts/howto/multisig/) | Fift, Lite Client | This tutorial will help you learn how to deploy your multisig contract with Lite Client. | ## TON course: contract development From b2a96adead01a606bee0567fce820fa6271c1882 Mon Sep 17 00:00:00 2001 From: AlexG <39581753+Reveloper@users.noreply.github.com> Date: Tue, 11 Mar 2025 20:13:14 +0900 Subject: [PATCH 19/23] fix_jetton_redirect --- redirects/redirects.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/redirects/redirects.json b/redirects/redirects.json index 4322ffa9111..d8e3510194f 100644 --- a/redirects/redirects.json +++ b/redirects/redirects.json @@ -830,6 +830,10 @@ { "from": "/v3/concepts/qa-outsource/auditors", "to": "https://ton.org/talents?filterBy=Auditors" + }, + { + "from": "/develop/dapps/asset-processing/jettons", + "to": "/v3/guidelines/dapps/asset-processing/jettons" } ] \ No newline at end of file From 9b0e1b088a061334b0a33eb2c6d3d1da94adb22b Mon Sep 17 00:00:00 2001 From: AlexG <39581753+reveloper@users.noreply.github.com> Date: Tue, 11 Mar 2025 21:09:06 +0900 Subject: [PATCH 20/23] update_redirects (#1021) --- redirects/redirects.json | 73 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/redirects/redirects.json b/redirects/redirects.json index d8e3510194f..c7e3412a951 100644 --- a/redirects/redirects.json +++ b/redirects/redirects.json @@ -834,6 +834,77 @@ { "from": "/develop/dapps/asset-processing/jettons", "to": "/v3/guidelines/dapps/asset-processing/jettons" + }, + { + "from": "/develop/dapps/apis/sdk", + "to": "/v3/guidelines/dapps/apis-sdks/sdk" + }, + { + "from": "/develop/dapps/cookbook", + "to": "/v3/guidelines/dapps/cookbook" + }, + { + "from": "/develop/dapps/telegram-apps/README", + "to": "/v3/guidelines/dapps/tma/overview" + }, + { + "from": "/develop/dapps/telegram-apps/app-examples", + "to": "/v3/guidelines/dapps/tma/tutorials/app-examples" + }, + { + "from": "/develop/dapps/telegram-apps/step-by-step-guide", + "to": "/v3/guidelines/dapps/tma/tutorials/step-by-step-guide" + }, + { + "from": "/develop/dapps/ton-connect/developers", + "to": "/v3/guidelines/ton-connect/guidelines/developers" + }, + { + "from": "/develop/dapps/ton-connect/overview", + "to": "/v3/guidelines/ton-connect/guidelines/how-ton-connect-works" + }, + { + "from": "/develop/dapps/ton-connect/react", + "to": "/v3/guidelines/ton-connect/frameworks/react" + }, + { + "from": "/develop/dapps/ton-connect/wallet", + "to": "/v3/guidelines/ton-connect/wallet" + }, + { + "from": "/develop/dapps/ton-connect/web", + "to": "/v3/guidelines/ton-connect/frameworks/web" + }, + { + "from": "/develop/dapps/tutorials/how-to-run-ton-site", + "to": "/v3/guidelines/web3/ton-proxy-sites/how-to-run-ton-site" + }, + { + "from": "/develop/get-started-with-ton", + "to": "/v3/guidelines/get-started-with-ton" + }, + { + "from": "/develop/overview", + "to": "/v3/documentation/ton-documentation/README" + }, + { + "from": "/develop/smart-contracts/fees", + "to": "/v3/documentation/smart-contracts/transaction-fees/fees" + }, + { + "from": "/learn/introduction", + "to": "v3/concepts/dive-into-ton/introduction" + }, + { + "from": "/learn/overviews/addresses", + "to": "/v3/concepts/dive-into-ton/ton-blockchain/smart-contract-addresses" + }, + { + "from": "/participate/run-nodes/full-node", + "to": "/v3/guidelines/nodes/running-nodes/full-node" + }, + { + "from": "/participate/wallets/contracts", + "to": "/v3/documentation/smart-contracts/contracts-specs/wallet-contracts" } - ] \ No newline at end of file From 351109c1478c4628ac152950b4695392bd68a505 Mon Sep 17 00:00:00 2001 From: AlexG <39581753+Reveloper@users.noreply.github.com> Date: Tue, 11 Mar 2025 21:16:24 +0900 Subject: [PATCH 21/23] ton_documentation_redirect --- redirects/redirects.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redirects/redirects.json b/redirects/redirects.json index c7e3412a951..e4b3dbc7fbb 100644 --- a/redirects/redirects.json +++ b/redirects/redirects.json @@ -885,7 +885,7 @@ }, { "from": "/develop/overview", - "to": "/v3/documentation/ton-documentation/README" + "to": "/v3/documentation/ton-documentation" }, { "from": "/develop/smart-contracts/fees", From bb13415699d43b248eab3cb50bd0a80e24306411 Mon Sep 17 00:00:00 2001 From: D-Shestak Date: Thu, 13 Mar 2025 12:57:03 +0300 Subject: [PATCH 22/23] Update web3-game-example.md --- .../dapps/tutorials/web3-game-example.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/v3/guidelines/dapps/tutorials/web3-game-example.md b/docs/v3/guidelines/dapps/tutorials/web3-game-example.md index 387f13cf98b..45382deddfb 100644 --- a/docs/v3/guidelines/dapps/tutorials/web3-game-example.md +++ b/docs/v3/guidelines/dapps/tutorials/web3-game-example.md @@ -1,14 +1,14 @@ -# TON blockchain for games +# TON Blockchain for games ## What’s in the tutorial -In this tutorial we will consider how to add the TON blockchain to a game. For our example, we will use a Flappy Bird clone written in Phaser and will add GameFi features step by step. In the tutorial we will use short code pieces and pseudocode to make it more readable. Also, we will provide links to real code blocks to help you understand better. The whole implementation can be found in the [demo repo](https://github.com/ton-community/flappy-bird). +In this tutorial we will consider how to add TON Blockchain to a game. For our example, we will use a Flappy Bird clone written in Phaser and will add GameFi features step by step. In the tutorial we will use short code pieces and pseudocode to make it more readable. Also, we will provide links to real code blocks to help you understand better. The whole implementation can be found in the [demo repo](https://github.com/ton-community/flappy-bird). ![Flappy Bird game without GameFi features](/img/tutorials/gamefi-flappy/no-gamefi-yet.png) We are going to implement the following: -- Achievements. Let’s reward our users with [SBTs](/v3/concepts/glossary#sbt). The achievement system is a great tool to increase user engagement. -- Game currency. In TON blockchain it’s easy to launch your own token (jetton). The token can be used to create an in-game economy. Our users will be able to earn the game coins to spend them later. -- Game shop. We will provide users with the possibility to purchase in-game items using either in-game currency or the TON coin itself. +- **Achievements.** Let’s reward our users with [SBTs](/v3/concepts/glossary#sbt). The achievement system is a great tool to increase user engagement. +- **Game currency.** In TON Blockchain, it’s easy to launch your own token (jetton). The token can be used to create an in-game economy. Our users will be able to earn the game coins to spend them later. +- **Game shop.** We will provide users with the possibility to purchase in-game items using either in-game currency or the TON coin itself. ## Preparations @@ -199,7 +199,7 @@ const playedInfo = await submitPlayed('http://localhost:3001', wallet.account.ad > Read [submitPlayer function](https://github.com/ton-community/flappy-bird/blob/article-v1/workspaces/client/src/game-scene.ts#L10) code. -Let’s play the first time and ensure we will be rewarded with a FLAP token and SBT. Click the Play button, fly through a pipe or two and then hit into a tube. Alright, everything works! +Let’s play the first time and ensure we will be rewarded with a FLAP token and SBT. Click the `Play` button, fly through a pipe or two and then hit into a tube. Alright, everything works! ![Rewarded with token and SBT](/img/tutorials/gamefi-flappy/sbt-rewarded.png) @@ -224,11 +224,11 @@ To know when users make payments, we need to watch the master wallet transaction ### Client side for the shop -On the client side we have the Shop button. +On the client side we have the `Shop` button. ![Enter shop button](/img/tutorials/gamefi-flappy/shop-enter-button.png) -When a user clicks the button, the shop scene is opened. The shop scene contains the list of items a user can buy. Each item has a price and a Buy button. When a user clicks the Buy button, the purchase is made. +When a user clicks the button, the shop scene is opened. The shop scene contains the list of items a user can buy. Each item has a price and a `Buy` button. When a user clicks the `Buy` button, the purchase is made. Opening the Shop will trigger purchased items loading and updating it every 10 seconds: ```typescript From aa24a4897f4366344db97e0c0b4e0b37a0ff14ab Mon Sep 17 00:00:00 2001 From: D-Shestak Date: Fri, 14 Mar 2025 15:59:57 +0300 Subject: [PATCH 23/23] Update guidelines.mdx --- docs/v3/guidelines/smart-contracts/guidelines.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/v3/guidelines/smart-contracts/guidelines.mdx b/docs/v3/guidelines/smart-contracts/guidelines.mdx index 153533f1cbc..89dc81acab8 100644 --- a/docs/v3/guidelines/smart-contracts/guidelines.mdx +++ b/docs/v3/guidelines/smart-contracts/guidelines.mdx @@ -42,7 +42,7 @@ This page navigates within the TON smart contracts guidelines. ## TON course: contract development -The [TON blockchain course](https://stepik.org/course/176754/) is a comprehensive guide to blockchain development. +The [TON Blockchain course](https://stepik.org/course/176754/) is a comprehensive guide to blockchain development. - Module 2 is dedicated to __TVM, transactions, scalability and business cases__. - Module 3 is dedicated to __smart contract development lifecycle__. @@ -51,7 +51,7 @@ The [TON blockchain course](https://stepik.org/course/176754/) is a comprehensiv