From 1979ecb0b9268fff20d51f4f1ce6411a6b4ad134 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:09 +0800 Subject: [PATCH 001/219] New translations readme.md (Chinese Simplified) --- .../current/contribute/README.md | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/contribute/README.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/contribute/README.md b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/README.md new file mode 100644 index 0000000000..ac6ecb51f3 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/README.md @@ -0,0 +1,50 @@ +# How to Contribute + +## Identify area for contribution + +There are several ways to identify the area where you can contribute to TON Docs: + +- Join [TON Docs Club chat](https://t.me/+c-0fVO4XHQsyOWM8) in Telegram and get the latest tasks from maintainers. +- If you have a specific contribution in mind but are unsure about it, confirm whether + the contribution is appropriate by contacting one of the [Docs maintainers](/contribute/maintainers) directly. +- Get familiar with the most frequently asked questions in the [TON Developers](https://t.me/tondev_eng) chats. +- Please read over the [issues](https://github.com/ton-community/ton-docs/issues) in the GitHub repository. +- Learn available [footsteps](https://github.com/ton-society/ton-footsteps/issues?q=documentation) for the documentation. + +## TL;DR + +- If you need to add or edit something in TON Docs, create a pull request + against the `main` branch. +- The documentation team will review the pull request or reach out as needed. +- Repository: https://github.com/ton-community/ton-docs + +## Development + +### Online one-click contribution setup + +You can use Gitpod (a free, online, VS code-like IDE) for contributing. It will launch a workspace with a single click and will automatically: + +[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/ton-community/ton-docs) + +### Code Conventions + +- **Most important**: look around. Match the overall style of the project. This includes formatting, naming files, naming objects in code, naming things in documentation, and so on. +- **For documentation**: When editing documentation, do not wrap lines at 80 characters; instead, configure your editor to soft-wrap. + +Don't worry too much about styles in general; the maintainers will help you fix them as they review your code. + +### Pull Requests + +So you have decided to contribute code back to upstream by opening a pull request. You've put in a lot of effort, and we appreciate it. We will do our best to work with you and get the pull request reviewed. + +When submitting a pull request, please ensure the following: + +1. **Keep your pull request small**. Smaller pull requests (~300 lines of diff) are easier to review and more likely to get merged. Make sure the pull request does only one thing, otherwise please split it. +2. **Use descriptive titles**. It is recommended to follow the commit message style. +3. **Test your changes**. Describe your test plan in your pull request description. + +All pull requests should be opened against the `main` branch. + +## What Happens Next? + +The TON Docs team will be monitoring pull requests. Please help us by following the guidelines above to keep the pull requests consistent. From e0653009324af67510affdcc2660ef3f9f473711 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:10 +0800 Subject: [PATCH 002/219] New translations readme.mdx (Chinese Simplified) --- .../archive/hacktoberfest-2022/README.mdx | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/contribute/archive/hacktoberfest-2022/README.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/contribute/archive/hacktoberfest-2022/README.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/archive/hacktoberfest-2022/README.mdx new file mode 100644 index 0000000000..9bd67a36ba --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/archive/hacktoberfest-2022/README.mdx @@ -0,0 +1,51 @@ +import Button from '@site/src/components/button' + +# What is Hacktoberfest? + +
+ tlb structure +
+ +[Hacktoberfest](https://hacktoberfest.digitalocean.com/) is a month-long celebration of _open-source projects_, their _maintainers_, and the entire community of _contributors_. Each October, open source maintainers give new contributors extra attention as they guide developers through their first pull requests. + +For the TON Community it's time to help ecosystem growth together, so let's join the whole world with our **Hack-TON-berfest** party and become _#1 open-source ecosystem of the year_! + +## How to Participate? + +The Hacktoberfest rules for 2022 are as follows: + +- **Hacktoberfest is open to everyone**! +- Register anytime between September 26 and October 31 +- Pull requests can be made in any GITHUB or GITLAB project: + - [List of projects on TON Ecosystem](/hacktonberfest) + - [List of projects on GitHub](https://github.com/topics/hacktoberfest) +- Have **4** pull/merge requests accepted between October 1 and October 31 +- The first 40,000 participants (maintainers and contributors) who complete Hacktoberfest can choose between two prizes: a tree planted in their honor or a Hacktoberfest 2022 t-shirt. _(from the Hacktoberfest community)_ +- Every participant (maintainer and contributor) to any of the TON Ecosystem projects will receive a [**Limited Hack-TON-berfest NFT**](#what-the-rewards). _(from TON Foundation)_ + +For everyone in TON it's an opportunity to drive the growth of the entire ecosystem and receive cool rewards from TON Foundation. Let's do it together! + +## What are the rewards? + +To motivate the community to contribute to open source projects in the TON Ecosystem, you'll be able to receive a special reward from TON Foundation. Every participant will receive a **Limited Hack-TON-berfest NFT** achievement as a proof of participating: + +
+ +
+ +:::info IMPORTANT! +TON Foundation will mint a collection in November to all wallet addresses submitted to the [@toncontests_bot](https://t.me/toncontests_bot). It will happen after the calculation and validation of all contribution results. +::: + +You have plenty of time to participate in the event. Let's build decentralized Internet of the future with thousands of contributors from all over the world! + + + From 1e337e5880f9de4d8f64c29475bcd09c3f06f8e1 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:11 +0800 Subject: [PATCH 003/219] New translations as-contributor.md (Chinese Simplified) --- .../hacktoberfest-2022/as-contributor.md | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/contribute/archive/hacktoberfest-2022/as-contributor.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/contribute/archive/hacktoberfest-2022/as-contributor.md b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/archive/hacktoberfest-2022/as-contributor.md new file mode 100644 index 0000000000..d7a52d3f26 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/archive/hacktoberfest-2022/as-contributor.md @@ -0,0 +1,33 @@ +# Participate as a Contributor + +To become a contributor who receives a limited _Hack-TON-berfest NFT_, please set up your own [TON Wallet](https://ton.org/wallets) and verify your GitHub account. + +## Start your journey + +1. Set up any wallet from the [ton.org/wallets](https://ton.org/wallets) page. ([TON Wallet extension](https://chrome.google.com/webstore/detail/ton-wallet/nphplpgoakhhjchkkhmiggakijnkhfnd), for example.) +2. Please provide your wallet address to the [@toncontests_bot](https://t.me/toncontests_bot) in Telegram. +3. Validate your GitHub account in the same bot. + +After these steps you are ready to contribute and receive a [limited Hack-TON-berfest NFT](/contribute/hacktoberfest/#what-the-rewards). + +Welcome to the club, this is just the beginning! + +## New to contributing to open source? + +Hacktoberfest is a great place to start dipping your toes into open source contributions for the first time. There are plenty of streams, posts, guides, and discussions about to get started. You’ll be joining many folks who are also starting their journey this month! + +- [Basic information about Hacktoberfest for beginners](https://hacktoberfest.com/participation/#beginner-resources) +- [Guide to making your first contribution](https://dev.to/codesandboxio/how-to-make-your-first-open-source-contribution-2oim) by Ceora Ford +- [Practice the workflow to make your first contribution](https://github.com/firstcontributions/first-contributions) +- [Overcoming imposter syndrome in open source contribution](https://blackgirlbytes.dev/conquering-the-fear-of-contributing-to-open-source) + +## How can I contribute to TON? + +The TON Ecosystem has several organizations and repositories: + + + + 寻找贡献者的项目列表 + + From 80a2eba7ee7ea024be94a2a968e3aa204c7bee85 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:12 +0800 Subject: [PATCH 004/219] New translations as-maintainer.md (Chinese Simplified) --- .../hacktoberfest-2022/as-maintainer.md | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/contribute/archive/hacktoberfest-2022/as-maintainer.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/contribute/archive/hacktoberfest-2022/as-maintainer.md b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/archive/hacktoberfest-2022/as-maintainer.md new file mode 100644 index 0000000000..b02fb3b6aa --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/archive/hacktoberfest-2022/as-maintainer.md @@ -0,0 +1,39 @@ +# Participate as a Maintainer + +The Hacktoberfest event is the best time of year to receive support from the community! + +If your repository is relevant to the TON Ecosystem, many contributors will be interested in it. Let's help them dive right into your project! + +## Prepare to party + +To handle contributors in the right way, you need to have a repository in good standing. + +Follow these best practices to prepare your project for contributions: + +1. Add the “hacktoberfest” topic to your repository to **OPT-IN TO HACKTOBERFEST** and indicate you are looking for contributions. +2. Apply the “hacktoberfest” label to issues you want contributors to help you with in your GitHub or GitLab project. +3. Please read and use [the essential tips for new open source maintainers](https://blog.ton.org/essential-tips-for-new-open-source-maintainers) by TON Society. +4. Prepare to accept legitimate pull/merge requests by merging them, leaving an overall approving review, or adding the "hacktoberfest-accepted" label. +5. Reject any spam requests you receive by labeling them as “spam,” and close or label any other invalid contributions as "invalid." + +Here is an example of a full repository: [ton-community/ton-compiler](https://github.com/ton-community/ton-compiler) + +After that, feel free to add your repository to the list. + +## Rewards for Maintainers + +As a repository maintainer in the TON Ecosystem, you will be able to receive two types of rewards: + +1. [Hacktoberfest Reward Kit](https://hacktoberfest.com/participation/#maintainers) (_see REWARD FOR MAINTAINERS_) +2. [Limited Hack-TON-berfest NFT](/contribute/hacktoberfest/#what-the-rewards) (_please, register the wallet address in the [@toncontests_bot](https://t.me/toncontests_bot)_) + +## How to join and be listed? + +To participate in Hack-TON-berfest follow this link: + + + + Add a repository to the list + + From 791e0210211ae1c13ead89807ea7f7e1ee7959ce Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:12 +0800 Subject: [PATCH 005/219] New translations contribution-rules.md (Chinese Simplified) --- .../current/contribute/contribution-rules.md | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/contribute/contribution-rules.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/contribute/contribution-rules.md b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/contribution-rules.md new file mode 100644 index 0000000000..5a5f49d204 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/contribution-rules.md @@ -0,0 +1,44 @@ +# Contribution Guidelines + +Before contributing any docs.ton.org page, please review the following list of general and important requirements to guarantee a smooth experience. + +## Naming + +- It is essential to ensure the correct use of _THE_ in the TON documentation. _TON Blockchain_ and _TON Ecosystem_ are capitalized terms, and therefore, do not require _THE_ in their usage. +- We write _TON_ with regular nouns, and if it requires _THE_ according to English grammar, we use it. For instance: "_The_ TON Connect _protocol_ is a..." + +:::info +TON Blockchain... + +TON Ecosystem... + +The TON Connect protocol... +::: + +Please refer to the actual TON brand assets [here](https://ton.org/en/brand-assets). + +## Documentation References + +Every page in TON documentation should be finished with See Also section. Place there page, you think relates to current page without additional description. + +:::info + +``` +## See Also +* [TON Contribution Guidelines](/contribute/contribution-rules) +* [Tutorial Styling Guidelines](/contribute/tutorials/guidelines) +``` + +::: + +## English Helpful Sources + +The TON Ecosystem is being built for the entire world, so it's crucial that it's understandable for everyone on Earth. Here, we provide materials that are helpful for junior tech writers who want to improve their English skills. + +- [Plural Nouns](https://www.grammarly.com/blog/plural-nouns/) +- [Articles: A versus An](https://owl.purdue.edu/owl/general_writing/grammar/articles_a_versus_an.html) + +## See Also + +- [TON Contribution Guidelines](/contribute/contribution-rules) +- [Tutorial Styling Guidelines](/contribute/tutorials/guidelines) From 1fb42108b8843155424f3d7c6ac75407580780ac Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:13 +0800 Subject: [PATCH 006/219] New translations guidelines.md (Chinese Simplified) --- .../current/contribute/docs/guidelines.md | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/contribute/docs/guidelines.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/contribute/docs/guidelines.md b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/docs/guidelines.md new file mode 100644 index 0000000000..98949647c6 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/docs/guidelines.md @@ -0,0 +1,24 @@ +# Common Documentation Principals + +为了获得最佳的用户体验和清晰度,请在创建 docs.ton.org 上的新内容时,牢记我们旨在应用于所有文档的一般性和重要需求列表。 + +## 为从业者制作的文档 + +Documentations pages is primarily intended for documentation purposes and not as a tutorial, so it is important to minimize the use of personal examples or analogies in the text. It is crucial to ensure that the content is suitable for both professionals and non-professionals alike, while still providing valuable information. + +## Use a consistent format + +To make it easier for readers to navigate through the documentation, it is important to use a consistent format throughout the document. Use headings, subheadings, bullet points, and numbered lists to break up the text and make it easier to read. + +## Provide examples in special section + +Providing examples can help readers better understand the content and how to apply it. If you are writing documentation page and need refer several exmaples, please create special section Examples right before References and See Also sections. Do not mix description and examples in documentation pages. +Use code snippets, screenshots, or diagrams to illustrate your points and make the documentation more engaging. + +## Keep it up to date + +Tech documentation can quickly become outdated due to changes in technology or software updates. It is important to review and update the documentation regularly to ensure that it remains accurate and relevant to the current version of the software. + +## Get feedback + +Before publishing the documentation, it is a good idea to get feedback from other contributors or users. This can help identify areas that may be confusing or unclear, and allow you to make improvements before the documentation is released. From 4c45b8c50cb613e9ee7929bc39c838b604fdc2d2 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:14 +0800 Subject: [PATCH 007/219] New translations schemes-guidelines.mdx (Chinese Simplified) --- .../contribute/docs/schemes-guidelines.mdx | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/contribute/docs/schemes-guidelines.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/contribute/docs/schemes-guidelines.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/docs/schemes-guidelines.mdx new file mode 100644 index 0000000000..f3c9b7d09a --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/docs/schemes-guidelines.mdx @@ -0,0 +1,87 @@ +import ConceptImage from '@site/src/components/conceptImage'; +import ThemedImage from '@theme/ThemedImage'; + +# Graphic Explanations Guidelines + +Maintaining consistency in documentation is crucial, and to achieve this, a specific standard for visualizing processes in smart contracts has been developed. + +## Graphic Explanation Notation + +### Message Processing Graph + +To depict message processing, it's advisable to utilize a graphical representation resembling a smart contract graph, complete with labels for transactions and messages. + +If the order of transactions doesn't matter, you can omit their labels. This simplifies the diagram, making it easier to read and understand the details related to messages and contracts. + +#### Annotation Primitives + +| Figure | Description | +| ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | +| ![](/img/docs/scheme-templates/message-processing-graphs/circle_for_smart_contract.svg?raw=true) | Circle - Smart Contract entity | +| ![](/img/docs/scheme-templates/message-processing-graphs/rectangle_for_regular_message.svg?raw=true) | Rectangle - Message Entity | +| ![](/img/docs/scheme-templates/message-processing-graphs/dashed_rectgl_for_optional_message.svg?raw=true) | Dashed Rectangle - Optional Message Entity | +| ![](/img/docs/scheme-templates/message-processing-graphs/line_for_transaction.svg?raw=true) | Transactions (numeration optional) | +| ![](/img/docs/scheme-templates/message-processing-graphs/person_figure_for_actor.svg?raw=true) | Actor | + +- Avoid using big quantity different and bright colors. +- Use the modification of figures, such as using a dashed border line. +- For better comprehension, different transactions could be displayed with distinct line styles (solid and dashed). + +#### Message Processing Example + +

+ +

+ +Learn references directly from Visio [message-processing.vsdx](/static/schemes-visio/message-processing.vsdx). + +### Formats and Colors + +#### Fonts + +- **Inter** fonts family for all text within diagrams. + +#### Colors - Light Mode + +- Pencil Hand Drawn(default theme) + +#### Colors - Dark Mode + +- Font `#e3e3e3` +- Background `#232328` +- Light Highlight(arrows and scheme borders) `#058dd2` +- Dark Highlight(arrows and scheme borders) `#0088cc` +- InnerBackGround(for nested blocks) `#333337` + +#### Version Control Policy + +- Set diagrams in the documentation by SVG format for schemes to ensure readability on various devices. +- Store original files in the project's Git repository under the "/static/visio" directory, making them easier to modify in the future. + +### Sequence Diagram + +In the case of complex and repetitive communication schemes between 2-3 actors, it is advisable to use a sequence diagram. For messages, use the notation of a common synchronous message arrow. + +#### Example + +

+
+ +
+

+ +### Scheme References + +- [message-processing.vsdx](/schemes-visio/message_processing.vsdx) From 50782453a3af9b1b566d179a1801c84d63621f08 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:15 +0800 Subject: [PATCH 008/219] New translations maintainers.md (Chinese Simplified) --- .../current/contribute/maintainers.md | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/contribute/maintainers.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/contribute/maintainers.md b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/maintainers.md new file mode 100644 index 0000000000..78e26e3baf --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/maintainers.md @@ -0,0 +1,51 @@ +# Maintainers + +## Active Team + +Below is an alphabetical list of the current members on the TON Docs team. + +### Alex Golev + +TON Docs maintainer and DevRel at TON Foundation + +- Telegram: [@alexgton](https://t.me/alexgton) +- GitHub: [Reveloper](https://github.com/Reveloper) + +### Gusarich + +Web3 Developer, [TON Dev community](https://github.com/ton-community) contributor, [TON Footsteps](https://github.com/ton-society/ton-footsteps) and TON Docs maintainer + +- Telegram: [@Gusarich](https://t.me/Gusarich) +- GitHub: [Gusarich](https://github.com/Gusarich) + +### SwiftAdviser + +Developer Onboarding Manager at TON Foundation + +- Telegram: [@SwiftAdviser](https://t.me/SwiftAdviser) +- GitHub: [SwiftAdviser](https://github.com/SwiftAdviser) + +## Acknowledgements + +TON Docs was originally created by [tolya-yanot](https://github.com/tolya-yanot) and [EmelyanenkoK](https://github.com/EmelyanenkoK). + +Over time, TON Docs has benefitted from the intellect and dedication of [numerous external contributors](https://github.com/ton-community/ton-docs/graphs/contributors). We extend our heartfelt gratitude to each of them. + +However, we would like to specially acknowledge the substantial contributions made by the following individuals. Their respective contributions have greatly enriched the quality and depth of our documentation: + +- [akifoq](https://github.com/akifoq): early contributions +- [amnch1](https://github.com/amnch1): fixes +- [aSpite](https://github.com/aSpite): content +- [awesome-doge](https://github.com/awesome-doge): early contributions +- [coalus](https://github.com/coalus): content +- [delovoyhomie](https://github.com/delovoyhomie): content +- [krau5](https://github.com/krau5): improvements +- [LevZed](https://github.com/LevZed): content +- [ProgramCrafter](https://github.com/ProgramCrafter): content +- [siandreev](https://github.com/siandreev): content +- [SpyCheese](https://github.com/SpyCheese): early contributions +- [Tal Kol](https://github.com/talkol): early contributions +- [TrueCarry](https://github.com/TrueCarry): content +- [xssnick](https://github.com/xssnick): content + +Our sincere appreciation to each and every contributor who has aided in making TON Docs a rich and reliable resource. From 0003cfdfb61e8e8d4786943a501890233b35672d Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:15 +0800 Subject: [PATCH 009/219] New translations participate.md (Chinese Simplified) --- .../current/contribute/participate.md | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/contribute/participate.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/contribute/participate.md b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/participate.md new file mode 100644 index 0000000000..f79b75f39b --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/participate.md @@ -0,0 +1,49 @@ +# Contribution Guide + +Here is a step-by-step guide of contributing to TON Documentation with Tutorials. + +:::tip opportunity +You're lucky! It's a good opportunity to improve TON Ecosystem here. +::: + +If you decide to write tutorial, you can get some reward for outstanding materials: + +- **Special TON Bounty NFT** for the most valuable contributing to TON +- **Reward in TON** as payment for approved high-quality contribution like tutorial + +Let's see how you can participate in contributing process. + +## Decide what you want to write + +Find or write a material which you want to describe. + +1. Check a [list of issues on TON Docs GitHub](https://github.com/ton-community/ton-docs/issues) with `tutorial` label. +2. _OR_ write your own idea on TON Docs GitHub with [tutorial template](https://github.com/ton-community/ton-docs/issues/new?assignees=\&labels=feature+%3Asparkles%3A%2Ccontent+%3Afountain_pen%3A\&template=suggest_tutorial.yaml\&title=Suggest+a+tutorial). + +## Describe a problem to get a reward + +Write a _ton-footstep_ to receive a funding for your contributing. + +1. Read about [TON Bounties](https://github.com/ton-society/grants-and-bounties/blob/main/bounties/BOUNTIES_PROGRAM_GUIDELINES.md) program more detailed. + 1. **TLDR:** Use [Improve TVM Instructions article](https://github.com/ton-society/grants-and-bounties/issues/361) as an example. +2. Write [your own bounty](https://github.com/ton-society/grants-and-bounties/issues/new/choose) to participate and wait for approve. [TON Bounties Creator Bot](https://t.me/footsteps_helper_bot) will help you. +3. After received `approved` label start to write your tutorial. + +## Writing a tutorial + +**Preparations**. Minimize future amount of requested changes, _save your time_: + +1. Follow [Tutorial Guidelines](/contribute/guidelines) and check them with [Sample Tutorial Structure](/contribute/sample-tutorial) +2. Read [Principles of a Good Tutorial](/contribute/principles-of-a-good-tutorial) to write amazing tutorial :) +3. Inspire with [Mint your first Jetton](/develop/dapps/tutorials/jetton-minter) example in sources. +4. **Setup environment**. [Check the tutorial](/contribute#online-one-click-contribution-setup) running your fork locally or using Gitpod. +5. **Write tutorial**. Using the environment, see how tutorial looks like on your fork. +6. **Make a Pull Request**. Open PR to get some feedback from maintainers. +7. Get merged! + +## Receiving a reward + +1. After your PR in TON Docs got merged, please write in your ton-footsteps task. +2. Follow a guide [How to complete ton-bounty?](https://github.com/ton-society/grants-and-bounties/blob/main/bounties/BOUNTIES_PROGRAM_GUIDELINES.md#got-assigned-submit-a-questbook-proposal) to complete bounty and get reward. +3. In your task, you will be asked for a wallet to send a reward. +4. Get rewarded! From 6923bae1ca3fb9e8ce105f7eb1b3615ecded187e Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:16 +0800 Subject: [PATCH 010/219] New translations guidelines.md (Chinese Simplified) --- .../contribute/tutorials/guidelines.md | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/contribute/tutorials/guidelines.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/contribute/tutorials/guidelines.md b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/tutorials/guidelines.md new file mode 100644 index 0000000000..0adfa8dc9c --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/tutorials/guidelines.md @@ -0,0 +1,135 @@ +# Tutorial Styling Guidelines + +So you've decided to write a tutorial for TON Documentation? + +We're excited to have you among our contributors! Please review the guidelines below to make sure your tutorial follows the style and quality of the pre-existing content on TON Docs. + +It is important that you take some time to become familiar with the tutorial structure and how headings should be used. Please read through some of our pre-existing tutorials and also have a look at [previous Pull Requests](https://github.com/ton-community/ton-docs/pulls?q=is%3Apr+is%3Aclosed) before submitting your own. + +## Process + +:::info IMPORTANT +Before you start writing, _read the guidelines below_! They will help you ensure the level of standardization and quality that will make the review process much faster. +::: + +Also, be sure to refer to the [**sample tutorial structure**](/contribute/tutorials/sample-tutorial) we have provided. + +1. To begin, fork and then clone the [ton-docs](https://github.com/ton-community/ton-docs/) repository on GitHub and create a new branch in your local repository. +2. Write your tutorial keeping quality and readability in mind! Have a look at the existing tutorials to see what you should aim for. +3. When you're ready to submit it for review, [open a Pull Request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) from your branch. We will be notified, and the review process will begin: + 1. **Please make every effort to submit only the final draft of your tutorial**. Some typos and grammar fixes are acceptable, but if there are significant changes to be made before we can publish the tutorial, it will take much longer to review and have you make the necessary changes. +4. Once we've reviewed your submission and you've made all necessary changes, we'll merge the Pull Request and publish the tutorial on TON Documentation. We'll contact you shortly after this to arrange your payment! +5. Once it is published, remember to **promote** your tutorial on social media! The [documentation maintainers](/contribute/maintainers) can help to amplify this promotion as long as you cooperate with us. + +To summarize, the workflow is as follows: + +1. _**Fork and Clone**_ the **`ton-docs`** repository +2. _**Write and polish**_ your tutorial +3. _**Submit a Pull Request**_ for review +4. _**Make any necessary changes**_ +5. The tutorial is _**merged and published**_ +6. _**Promote your tutorial**_ on social media! + +## Context + +The primary issue with adding "THE" before "TON" is that during the development of TON documentation and editorial policy, various departments such as marketing, vendors, and developers joined the discussion to capitalize words such as "Blockchain," "Ecosystem," and others in conjunction with "TON" to create a strong image of a single system, network, and brand. After lengthy discussions, we concluded that for a strong brand image, we should create a glossary of words and phrases that can be written without "THE" and capitalized. If it can be capitalized, the article is unnecessary. Currently, there are two such word combinations: TON Blockchain and TON Ecosystem. + +For other TON module names such as TON Connect, TON SDK, TON Grants, etc., it depends on the context. We apply the capitalization rule but are flexible with the article rule. If the component name stands alone, it is better without the article. However, if it is combined with a common noun, such as the TON Connect protocol, the article is needed because it refers to the entity protocol. + +Regarding other word combinations like "TON + noun" (e.g., "the TON world," "the TON community," etc.), we do not restrict the use of the article because we naturally expect to see the article in combination with a noun. + +## General tips + +- **Do not copy and paste pre-existing content**. Plagiarism is a serious issue and will not be tolerated. If the tutorial is inspired by some existing content, reference it and link to it. When linking to other tutorials/resources, use TON Docs resources if possible. +- **Include any walkthrough videos or video content** in the PR by uploading it to Google Drive. +- **Account funding from faucets must be clearly explained**, including which account is being funded, from where, and why. Do not assume learners can accomplish this task on their own! +- **Display sample outputs** in the form of Terminal snippets or screenshots to help learners understand what to expect. Trim long outputs. +- **Take an error-driven approach** where you bump into errors on purpose to teach learners how to debug them. For example, if you need to fund an account to be able to deploy a contract, first try and deploy without funding, observe the error that is returned, then fix the error (by funding the account) and try again. +- **Add potential errors and troubleshooting.** Of course, the tutorial shouldn't list every possible error, but it should make an effort to catch the important or most common ones. +- **Use React or Vue** for the client-side. +- **Before making the PR, run the code by yourself first** to avoid any obvious errors and make sure it works as expected. +- **Avoid including external/cross-links** to different sources between tutorials. If your tutorial is longer, we can discuss how to turn it into a longer course or Pathway. +- **Provide** **pictures or screenshots** to illustrate the complicated processes where needed. +- Upload your images to the `static` directory of the learn-tutorials repository — **DO NOT** use hotlinks to external sites, as this can result in broken images. +- **Image links must** **be in markdown format** and you must **ONLY** use the raw GitHub URL of the static directory in the repository: `![name of your image](https://raw.githubusercontent.com/ton-community/ton-docs/main/static/img/tutorials/.png?raw=true)` + - Remember to add `?raw=true` at the end of the URL. + +## How to structure your tutorial + +:::info Sample tutorial structure +Feel free to check out the [sample tutorial structure](/contribute/tutorials/sample-tutorial) to see it with your own eyes. +::: + +- The **Title** should be direct and clear, summarizing the tutorial's goal. Do not add the tutorial title as a heading inside the document; instead, use the markdown document filename. + - _For example_: If your tutorial was titled "_Step by step guide for writing your first smart contract in FunC_," the filename should be:\ + `step-by-step-guide-for-writing-your-first-smart-contract-in-func.md` +- Include an **Introduction** section explaining _why_ this tutorial matters and what the context of the tutorial is. Don't assume that it is obvious. +- Include a **Prerequisites** section explaining any _prior knowledge_ required or any existing tutorials that need to be completed first, any tokens that are needed, etc. +- Include a **Requirements** section explaining any _technologies that must be installed_ **prior** to starting the tutorial and that the tutorial will not cover, such as the TON Wallet extension, Node.js, etc. Do not list packages that will be installed during the tutorial. +- Use **subheadings** (H2: ##) to break down your explanations within the body of the tutorial. Keep the Table of Contents in mind when using subheadings, and try to keep them on point. + - If the content below a subheading is short (for example, only a single paragraph and a code block), consider using bold text instead of a subheading. +- Include a **Conclusion** section that summarizes what was learned, reinforces key points, and also congratulates the learner for completing the tutorial. +- (_**Optional**_) Include a **What's Next** section pointing to good follow-up tutorials or other resources (projects, articles, etc.). +- (_**Optional**_) Include an **About The** **Author** section at the end. Your bio should include a link to your GitHub profile (which will have your name, website, etc.) and a link to your Telegram profile (so that users can contact/tag you for help and questions). +- A **References** section **must** be present if you have taken any help in writing this tutorial from other documents, GitHub repos, or other tutorials. Credit sources by adding their name and a link to the document when possible (if it is not a digital document, include an ISBN or other means of reference). + +## Style Guide + +- **Writing Tone -** Tutorials are written by community contributors for their peers. + - Given this, we recommend creating a tone of inclusion and interaction throughout the tutorial. Use words such as “we”, “us”, “our”. + - _For example_: "We have successfully deployed our contract." + - When providing direct instructions, feel free to use “you”, “your”, etc. + - _For example_: “_Your file should look like this:_”. + +- **Use Markdown properly** throughout your tutorial. Refer to [GitHub's markdown guide](https://guides.github.com/features/mastering-markdown/) as well as the [sample tutorial structure](/contribute/tutorials/sample-tutorial). + +- **Do not use pre-formatted text for emphasis**, _for example_: + - ❌ "TON counter `smart contract` named `counter.fc`" is incorrect. + - ✅ "TON counter **smart contract** named `counter.fc`" is correct. + +- **Do not use any markdown formatting in a section heading**, _for example_: + - ❌ # **Introduction** is incorrect. + - ✅ # Introduction is correct. + +- **Explain your code!** Don't just ask learners to blindly copy and paste. + - Function names, variables, and constants **must** be consistent across the entire document. + - Use a comment at the beginning of a code block to show the path and filename where the code exists. _For example_: + + ```jsx + // test-application/src/filename.jsx + + import { useEffect, useState } from 'react'; + + ... + ``` + +- **Select the appropriate language** for code block syntax highlighting! + - All code blocks _must_ have syntax highlighting. Use **\`\`\`text** if you are not sure what kind of syntax highlighting to apply. + +- **Do not use code block syntax for pre-formatted text,** _for example_: + - ❌ \`filename.jsx\` is incorrect. + - ✅ \`filename.jsx\` is correct. + +- **Your code blocks should be well commented**. Comments should be short (usually two or three lines at a time) and effective. If you need more space to explain a piece of code, do it outside of the code block. + +- **Remember to leave a blank line** before and after all code blocks.\ + _For example_: + +```jsx + +// test-application/src/filename.jsx + +import { useEffect, useState } from 'react'; + +``` + +- **Use a linter & prettifier** before pasting your code into the code blocks. We recommend `eslint` for JavaScript/React. Use `prettier` for code formatting. +- **Avoid the overuse of bullet points**, numbered lists, or complicated text formatting. The use of **bold** or _italic_ emphasis is allowed but should be kept to a minimum. + +# **App setup** + +- Web3 projects will typically include several existing code libraries. Be sure to account for this when writing your tutorial. Where possible, provide a GitHub repository as a starting point to make it easier for learners to get started. +- If you are _not_ using a GitHub repo to contain the code used in your tutorial, remember to explain to readers how to create a folder to keep the code organized. + _For example_: `mkdir example && cd example` +- If you use `npm init` to initialize a project directory, explain the prompts or use the `-y` flag. +- If you use `npm install` use the `-save` flag. From b2179c16ed75a5378a56703ceed6b172c995d3f8 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:17 +0800 Subject: [PATCH 011/219] New translations principles-of-a-good-tutorial.md (Chinese Simplified) --- .../principles-of-a-good-tutorial.md | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/contribute/tutorials/principles-of-a-good-tutorial.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/contribute/tutorials/principles-of-a-good-tutorial.md b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/tutorials/principles-of-a-good-tutorial.md new file mode 100644 index 0000000000..c6d904c67c --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/tutorials/principles-of-a-good-tutorial.md @@ -0,0 +1,27 @@ +# Principles of a good tutorial + +Original comment with these principles by [talkol](https://github.com/talkol): + +- [Original comment on TON Footstep #7](https://github.com/ton-society/ton-footsteps/issues/7#issuecomment-1187581181) + +Here is a summary of these points for new contributors. + +## Principles + +1. The full flow should run on the user's client. There shouldn't be any third-party services involved. You need to do everything so that the user can simply clone the repository and immediately run it. + +2. The README should be VERY detailed. Do not assume the users know anything. If the tutorial requires it, it should also explain how to install the FunC compiler or Lite-client on your device. You can copy these parts from other tutorials in this documentation. + +3. It would be good if the repository included the whole source code for the contracts used, so that users could make minor changes to the standard code. For example, the Jetton smart contract allows users to experiment with custom behavior. + +4. If it is possible, create a user-friendly interface that will allow users to deploy or run the project without having to download the code or configure anything. Notice that this should still be standalone and served from GitHub Pages to run 100% client-side on the user's device. Example: https://minter.ton.org/ + +5. Explain to users what every field choice means and explain best practices. + +6. Explain everything there is to know about security. You must explain enough that creators do not make mistakes and create dangerous smart contracts/bots/websites—you are teaching them the best security practices. + +7. Ideally, the repository should include well-written tests that show the reader how to best implement them in the context of your tutorial. + +8. The repository should have its own easy-to-understand compilation/deployment scripts. A user should be able to just `npm install` and use them. + +9. Sometimes a GitHub repository is enough and there is no need to write a full article. Just a README with all the code you need in the repository. In this case, the code should be well-commented so that the user can easily read and understand it. From 8270a6a29a4e3910804c04a8c5c2539e403b591c Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:18 +0800 Subject: [PATCH 012/219] New translations sample-tutorial.md (Chinese Simplified) --- .../contribute/tutorials/sample-tutorial.md | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/contribute/tutorials/sample-tutorial.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/contribute/tutorials/sample-tutorial.md b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/tutorials/sample-tutorial.md new file mode 100644 index 0000000000..93b3b2255e --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/contribute/tutorials/sample-tutorial.md @@ -0,0 +1,130 @@ +# Sample tutorial structure + +## Introduction + +The Introduction heading **must** be H2: `## Introduction` + +This section is for you to explain the context of this tutorial and why it is important, what we're going to build and learn in this tutorial. + +- Explain this section like you're explaining it to a 5-year-old (**[ELI5](https://www.dictionary.com/e/slang/eli5/)**) +- Explain everything in 5–6 lines maximum. + +_For example:_ + +> A smart contract is just a computer program that runs on TON Blockchain, or more specifically on its [TVM](/learn/tvm-instructions/tvm-overview) (_TON Virtual Machine_). The contract is made of code (_compiled TVM instructions_) and data (_persistent state_) that are stored at some address on TON. + +## Prerequisites + +The Prerequisites heading **must** be H2: `## Prerequisites` + +This section is for you to explain any prior knowledge needed or any existing tutorials that need to be completed first. Any tokens that are needed—mention them here. + +_For example:_ + +> In this tutorial, we're going to mint Jetton on testnet. Before we continue, make sure that your [testnet](/develop/smart-contracts/environment/testnet) wallet has sufficient balance. + +## Requirements + +The Requirements heading **must** be H2: `## Requirements` + +**OPTIONAL :** Embed any video content in this section if your tutorial has any. + +Any technology that needs to be installed **prior** to starting the tutorial and that the tutorial will not cover (`TON Wallet Extension`, `node`, etc.). Do not list packages that will be installed during the tutorial. + +_For example:_ + +- We'll need the TON Wallet extension in this tutorial; install it from [HERE](https://chrome.google.com/webstore/detail/ton-wallet/nphplpgoakhhjchkkhmiggakijnkhfnd). +- Make sure to have NodeJS 12.0.1+ installed. + +## Body of the Tutorial + +- Please do not use "Body of the Tutorial" as a heading, use your own heading that is relevant to the material. + - "Getting started" is acceptable if you can't think of anything else 😉 +- Add any text content necessary to guide readers through your tutorial, and _**remember to proofread your content**_ for spelling and grammar before you submit your tutorial. + - [Grammarly](http://grammarly.com) is a good free program that can help you avoid grammar mistakes. + +### Key points + +- Do not use "Body of the Tutorial" as a heading! + +- **Keep all subheadings at H3,** don't go for H4 or any lower. + - In Markdown syntax, two hashmarks are used for H2 headings: ## + - Three hashmarks are used for H3 headings: ### + +- Add only necessary comments to code blocks. _**Do not**_ add # style comments to terminal input code blocks. + +- Add all relevant code blocks: + - ## Markdown syntax for code blocks consists of three backticks at the beginning and end of the code block. Also, make sure that there is a newline before and after the backticks in all code blocks. _For example_: + + + - ALL code blocks _**must**_ have a syntax highlight type. Use \`\`\`text if you are not sure. + + - \\`\`\`text must be used for terminal output, terminal commands, and plaintext. + + - \`javascript *or* `js can be used for any JavaScript code. + + - \`typescript or `ts can be used for any TypeScript code. + + - \\`\`\`jsx is for ReactJS code. + + - \\`\`\`cpp is for Func code. + + - Use \\`\`\`graphql when highlighting GraphQL syntax. + + - Use \`json when highlighting valid JSON. (For invalid JSON examples use \`text instead.) + + - \\`\`\`bash should _only_ be used in code blocks where you need to have # style comments. This must be done carefully because in many situations the # character will render as a markdown heading. Typically, the Table of Contents will be affected if this occurs. + +- Do not use `pre-formatted text` for emphasis; instead, use only **bold** or _italic_ text. + +- Add images or code blocks to reflect the expected terminal output. + +- Take an error-driven approach when writing your tutorial. Add common errors and troubleshooting steps. _For example:_ + +> **Unable to connect to Testnet due to an error when executing the +> `node deploy:testnet` command.** +> +> Let's look at some common causes: + +- Make sure you have enough funds in your generated testnet wallet in `.env`. If not, please add some testnet coins from the faucet giver. +- If you're still experiencing the same issue, reach out to the devs in the [Dev Chat](https://t.me/TonDev_eng/) for help. + +> + +## Conclusion + +The Conclusion heading **must** be H2: `## Conclusion` + +This section should summarize what was learned in the tutorial, reinforce key points, and congratulate the learner on completing the tutorial. Use a maximum of 5–6 lines. +_For example_: + +> We created a simple new FunC contract with counter functionality. We then built and deployed it on-chain, and finally interacted with it by calling a getter and sending a message. + +Please remember that this code is not meant for production; there are still a few other things to consider if you wanted to deploy this to mainnet, such as disabling the transfer method if the token is listed on the market, and so on. + +> + +## See Also + +The Next Steps heading **must** be H2: `## See Also` + +Use this section to explain what can be done next after this tutorial to continue learning. +Feel free to add recommended projects and articles relating to this tutorial. +If you're working on any other advanced tutorials, you can briefly mention them here. +Typically, only related pages from docs.ton.org are placed here. + +## About the Author _(Optional)_ + +The About the Author heading **must** be H2: `## About the Author` + +Keep it short. One or two lines at most. You can include a link to your GitHub profile + Telegram profile. Please refrain from adding your LinkedIn or Twitter here. + +## References _(Optional)_ + +The References heading **must** be H2: `## References` + +This section _**must**_ be present if you have taken any help in writing this tutorial from other documents, GitHub repos or pre-existing tutorials. + +Credit sources by adding their name and a link to the document when possible. + +If it is not a digital document, include an ISBN or other form of reference. From 50d58abe9e2ca513a4cbb125de3d6242207b3072 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:18 +0800 Subject: [PATCH 013/219] New translations mining.md (Chinese Simplified) --- .../current/develop/archive/mining.md | 199 ++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/archive/mining.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/archive/mining.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/archive/mining.md new file mode 100644 index 0000000000..d6f96c4b54 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/archive/mining.md @@ -0,0 +1,199 @@ +# TON mining guide + +:::warning deprecated +This information may be out of date and no longer useful. Feel free to omit it. +::: + +## Introduction + +This document provides an introduction to the process of mining Toncoin using PoW givers. Please visit [ton.org/mining](https://ton.org/mining) for up-to-date status of TON mining. + +## Quick start + +To start mining right away: + +1. Get a [computer suitable for mining](#hardware). +2. Install [Ubuntu](https://ubuntu.com) 20.04 desktop or server distribution. +3. Install [mytonctrl](https://github.com/igroman787/mytonctrl#installation-ubuntu) in `lite` mode. +4. Check your hardware and [expected mining income](#faq-emi) by running `emi` command within `mytonctrl`. +5. If you do not yet have one, create `wallet address` using one of the [wallets](https://www.ton.org/wallets). +6. Define your `wallet address` as a mining target by executing `set minerAddr "..."` in `mytonctrl`. +7. Chose a giver contract from the list available on [ton.org/mining](https://ton.org/mining) and set your miner to mine it by executing `set powAddr "..."` in `mytonctrl`. +8. Start mining by executing `mon` in `mytonctrl` +9. Check the CPU load on your computer; the process called `pow-miner` should use most of your CPU. +10. Wait to get lucky; the output of step 4 should have told you approximately what your chances are to mine a block. + +## Basics + +Toncoin are distributed by so-called `PoW Givers` which are smart contracts with certain amounts of TONs assigned to them. Currently, there are 10 active PoW givers on the TON Network. Givers hand out coins in blocks of 100 TON each. In order to receive such a block, your computer needs to solve a complex mathematical challenge issued by a giver and do that as fast as possible; you will compete against other miners for the reward of 100 TON. If someone manages to solve the problem before you, all the work your machine has done is in vain, and a new round/race begins. + +It is important to understand that profits from mining do not "trickle in" as your machine does the works, they come in batches of 100 TON for every successful solution of giver challenge. This means that if your machine has a 10% chance to calculate a block within 24 hours (see step 4 of [Quick start](#quickStart)) then you will probably need to wait for ~10 days before you will get a 100 TON reward. + +The process of mining is largely automated by `mytonctrl`. Detailed information about the mining process can be found in [PoW givers](https://www.ton.org/#/howto/pow-givers) document. + +## Advanced + +If you are serious about mining and wish to operate more than one machine/mining farm, then you really need to learn TON and how mining works; please see the [HOWTO](https://ton.org/#/howto/) section for in-depth information. Here is some general advice: + +- **DO** run your own node / lite server on a separate machine; this will ensure that your mining farm does not depend on external lite servers that can go down or not process your queries in a timely fashion. +- **DO NOT** bombard public lite servers with `get_pow_params` queries, if you have custom scripts that poll givers status in high frequency you **must** use your own lite server. Clients that violate this rule risk having their IPs blacklisted on public lite servers. +- **DO** try to understand how [mining process](https://www.ton.org/#/howto/pow-givers) works; most larger miners use their own scripts that offer many advantages over `mytonctrl` in environments with multiple mining machines. + +## Miner hardware + +The total network hashrate of TON mining is very high; miners need high-performance machines if they wish to succeed. Mining on standard home computers and notebooks is futile, and we advise against such attempts. + +#### CPU + +Modern CPU that supports [Intel SHA Extension](https://en.wikipedia.org/wiki/Intel_SHA_extensions) is a **must**. Most miners use AMD EPYC or Threadripper-based machines with at least 32 cores and 64 threads. + +#### GPU + +Yes! You can mine TON using GPU. There is a version of a PoW miner that is capable to use both Nvidia and AMD GPUs; you can find the code and instructions on how to use it in the [POW Miner GPU](https://github.com/tontechio/pow-miner-gpu/blob/main/crypto/util/pow-miner-howto.md) repository. + +As for now, one needs to be tech-savvy to use this, but we are working on a more user-friendly solution. + +#### Memory + +Almost the entire mining process happens in the L2 cache of the CPU. That means that memory speed and size play no role in mining performance. A dual AMD EPYC system with a single DIMM on one memory channel will mine just as fast as one with 16 DIMMs occupying all channels. + +Please do note that this applies to the plain mining process **only**, if your machine also runs full node or other processes, then things change! But this is outside the scope of this guide. + +#### Storage + +Plain miner run in lite mode uses minimum space and does not store any data in storage. + +#### Network + +Plain miner needs the ability to open outgoing connections to the Internet. + +#### FPGA / ASIC + +See [can I use FPGA / ASICs?](#faq-hw-asic) + +### Cloud machines + +Many people mine using AWS or Google compute cloud machines. As outlined in the specs above, what really matters is CPU. Therefore, we advise AWS [c5a.24xlarge](https://aws.amazon.com/ec2/instance-types/c5/) or Google [n2d-highcpu-224](https://cloud.google.com/compute/vm-instance-pricing) instances. + +### Income estimates + +The formula for calculating the income is quite simple: `($total_bleed / $total_hashrate) * $your_hashrate`. This will give you **current** estimate. You can find out the variables on [ton.org/mining](https://ton.org/mining) or use the estimated mining income calculator (`emi` command) in `mytonctrl`. Here is sample output made on August 7th, 2021 using i5-11400F CPU: + +``` +Mining income estimations +----------------------------------------------------------------- +Total network 24h earnings: 171635.79 TON +Average network 24h hashrate: 805276100000 HPS +Your machine hashrate: 68465900 HPS +Est. 24h chance to mine a block: 15% +Est. monthly income: 437.7 TON +``` + +**Important**: Please do note that the information provided is based on _network hashrate at the moment of execution_. Your actual income over time will depend on many factors, such as changing network hashrate, the chosen giver, and a good portion of luck. + +## FAQ + +### General + +#### Is TON PoS or PoW network? + +TON Blockchain uses the Proof-of-Stake consensus. Mining is not required to generate new blocks. + +#### So how come TON is Proof-of-Work? + +Well, the reason is that the initial issue of 5 billion Toncoins were transferred to ad hoc Proof-of-Work Giver smart contracts. +Mining is used to obtain Toncoins from this smart contract. + +#### How many coins are left for mining? + +The most actual information is available on [ton.org/mining](https://ton.org/mining), see `bleed` graphs. PoW Giver contracts have their limits and will dry out once users mine all the available Toncoins. + +#### How many coins have been mined already? + +As of August 2021, about 4.9BN Toncoins have been mined. + +#### Who has mined those coins? + +Coins have been mined to over 70'000 wallets, owners of those wallets are not known. + +#### Is it difficult to start mining? + +Not at all. All you need is [adequate hardware](#hardware) and to follow the steps outlined in the [quick start](#quickStart) section. + +#### Is there another way to mine? + +Yes, there is a third-party app—[TON Miner Bot](https://t.me/TonMinerBot). + +#### Where can I see mining statistics? + +[ton.org/mining](https://ton.org/mining) + +#### How many miners are out there? + +We cannot say this. All we know is the total hashrate of all miners on the network. However, there are graphs on [ton.org/mining](https://ton.org/mining) that attempt to estimate quantity of machines of certan type needed to provide aproximate total hashrate. + +#### Do I need Toncoin to start mining? + +No, you do not. Anyone can start mining without owning a single Toncoin. + +#### I mine for hours, why my wallet total does not increase, not even by 1 TON? + +TON are mined in blocks of 100, you either guess a block and receive 100 TON or receive nothing. Please see [basics](#basics). + +#### I've been mining for days and I see no results, why? + +Did you check your current [Income estimates](#hardware-estimates)? If field `Est. 24h chance to mine a block` is less than 100%, then you need to be patient. Also, please note that a 50% chance to mine a block within 24 hours does not automatically mean that you will mine one within 2 days; 50% applies to each day separately. + +#### Are there mining pools? + +No, as of now there are no implementations of mining pools, everyone mines for themselves. + +#### Which giver should I mine? + +It does not really matter which giver you choose. The difficulty tends to fluctuate on each giver, so the current easiest giver on [ton.org/mining](https://ton.org/mining) might become the most complex within an hour. The same applies in the opposite direction. + +### Hardware + +#### Will a faster machine always win? + +No, all miners take different roads to find the solution. A faster machine has a higher probability of success, but it doesn't guarantee victory! + +#### How much income will my machine generate? + +Please see [Income estimates](#hardware-estimates). + +#### Can I use my BTC/ETH rig to mine TON? + +No, TON uses a single SHA256 hashing method which is different from BTC, ETH, and others. ASICS or FPGAs wbich are built for mining other cryptos will not help. + +#### What is better, a single fast machine or several slow ones? + +This is controversial. See: miner software launches threads for each core on the system, and each core gets its own set of keys to process, so if you have one machine capable to run 64 threads and 4 x machines capable to run 16 threads each, then they will be exactly as successful assuming that the speed of each thread is the same. + +In the real world, however, CPUs with lower core count are usually clocked higher, so you will probably have better success with multiple machines. + +#### If I run many machines, will they cooperate? + +No, they will not. Each machine mines on its own, but the solution finding process is random: no machine, not even a single thread (see above) will take the same path. Thus, their hashrates add up in your favor without direct cooperation. + +#### Can I mine using ARM CPUs? + +Depending on the CPU, AWS Graviton2 instances are indeed very capable miners and are able to hold price/performance ratio alongside AMD EPYC-based instances. + +### Software + +#### Can I mine using Windows/xBSD/some other OS? + +Of course, [TON source code](https://github.com/ton-blockchain/ton) has been known to be built on Windows, xBSD and other OSes. However, there is no comfortable automated installation, as under Linux with `mytonctrl`, you will need to install the software manually and create your own scripts. For FreeBSD, there is a [port](https://github.com/sonofmom/freebsd_ton_port) source code that allows quick installation. + +#### Will my mining become faster if I run mytonctrl in full node mode? + +Calculation process by itself will not be faster, but you will gain some stability and, most importantly, flexibility if you operate your own full node/lite server. + +#### What do I need to / how can I operate a full node? + +This is out of scope of this guide, please consult [Full node howto](https://ton.org/#/howto/full-node) and/or [mytonctrl instructions](https://github.com/igroman787/mytonctrl). + +#### Can you help me to build software on my OS? + +This is out of scope of this guide, please consult [Full node howto](https://ton.org/#/howto/full-node) as well as [Mytonctrl installation scripts](https://github.com/igroman787/mytonctrl/blob/master/scripts/toninstaller.sh#L44) for information about dependencies and process. From 5943f4455a670e7f234fdf19e8213abb604ea56b Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:19 +0800 Subject: [PATCH 014/219] New translations pow-givers.md (Chinese Simplified) --- .../current/develop/archive/pow-givers.md | 199 ++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/archive/pow-givers.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/archive/pow-givers.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/archive/pow-givers.md new file mode 100644 index 0000000000..34d6512650 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/archive/pow-givers.md @@ -0,0 +1,199 @@ +# POW Givers + +:::warning deprecated +此信息可能已过时,不再有用。您可以随意忽略它。 +::: + +本文旨在描述如何与POW Giver智能合约互动,以获得Toncoin。我们假设您已熟悉TON区块链轻客户端,如`入门`中所述,并熟悉编译轻客户端和其他软件的程序。为了获得运行验证者所需的更多Toncoin,我们还假设您熟悉`完整节点`和`验证者`页面。为了获得更多的Toncoin,您还需要一台足够强大的专用服务器来运行完整节点。获取少量的Toncoin不需要专用服务器,在家用电脑上几分钟内即可完成。 + +> 请注意,目前由于矿工数量众多,任何挖矿都需要大量资源。 + +## 1. Proof-of-Work Giver智能合约 + +为了防止少数恶意方收集所有Toncoin,网络的主链上部署了一种特殊的“工作量证明赠予者”智能合约。这些智能合约的地址如下: + +小额赠予者(每几分钟提供10至100 Toncoin): + +- kf-kkdY_B7p-77TLn2hUhM6QidWrrsl8FYWCIvBMpZKprBtN +- kf8SYc83pm5JkGt0p3TQRkuiM58O9Cr3waUtR9OoFq716lN- +- kf-FV4QTxLl-7Ct3E6MqOtMt-RGXMxi27g4I645lw6MTWraV +- kf_NSzfDJI1A3rOM0GQm7xsoUXHTgmdhN5-OrGD8uwL2JMvQ +- kf8gf1PQy4u2kURl-Gz4LbS29eaN4sVdrVQkPO-JL80VhOe6 +- kf8kO6K6Qh6YM4ddjRYYlvVAK7IgyW8Zet-4ZvNrVsmQ4EOF +- kf-P_TOdwcCh0AXHhBpICDMxStxHenWdLCDLNH5QcNpwMHJ8 +- kf91o4NNTryJ-Cw3sDGt9OTiafmETdVFUMvylQdFPoOxIsLm +- kf9iWhwk9GwAXjtwKG-vN7rmXT3hLIT23RBY6KhVaynRrIK7 +- kf8JfFUEJhhpRW80_jqD7zzQteH6EBHOzxiOhygRhBdt4z2N + +大额赠予者(每天至少提供10,000 Toncoin): + +- kf8guqdIbY6kpMykR8WFeVGbZcP2iuBagXfnQuq0rGrxgE04 +- kf9CxReRyaGj0vpSH0gRZkOAitm_yDHvgiMGtmvG-ZTirrMC +- kf-WXA4CX4lqyVlN4qItlQSWPFIy00NvO2BAydgC4CTeIUme +- kf8yF4oXfIj7BZgkqXM6VsmDEgCqWVSKECO1pC0LXWl399Vx +- kf9nNY69S3_heBBSUtpHRhIzjjqY0ChugeqbWcQGtGj-gQxO +- kf_wUXx-l1Ehw0kfQRgFtWKO07B6WhSqcUQZNyh4Jmj8R4zL +- kf_6keW5RniwNQYeq3DNWGcohKOwI85p-V2MsPk4v23tyO3I +- kf_NSPpF4ZQ7mrPylwk-8XQQ1qFD5evLnx5_oZVNywzOjSfh +- kf-uNWj4JmTJefr7IfjBSYQhFbd3JqtQ6cxuNIsJqDQ8SiEA +- kf8mO4l6ZB_eaMn1OqjLRrrkiBcSt7kYTvJC_dzJLdpEDKxn + +> 请注意,目前所有大额赠予者已被耗尽。 + +前十个智能合约使愿意获取少量Toncoin的用户能够在不花费太多计算功率的情况下获得一些(通常情况下,家用电脑上几分钟的工作应该就足够了)。其余智能合约用于获取网络中运行验证者所需的更多Toncoin;通常,一天在足够强大的专用服务器上的工作应该足以获得所需金额。 + +> 请注意,目前由于矿工数量众多,挖掘小额赠予者也需要大量资源。 + +您应该随机选择这些“proof-of-work giver”智能合约中的一个(根据您的目的从这两个列表中选择),并通过类似于挖矿的程序从该智能合约中获得Toncoin。基本上,您需要呈现一个包含工作量证明和您钱包地址的外部消息给所选的“proof-of-work giver”智能合约,然后金额将被发送给您。 + +## 2. 挖矿过程 + +为了创建一个包含“工作量证明(proof-of-work)”的外部消息,您应该运行一个特殊的挖矿实用程序,从GitHub库中的TON源代码编译而成。该实用程序位于构建目录的`./crypto/pow-miner`文件中,可以通过在构建目录中输入`make pow-miner`来编译。 + +然而,在运行`pow-miner`之前,您需要知道所选“proof-of-work giver”智能合约的`seed`和`complexity`参数的实际值。这可以通过调用该智能合约的get方法`get_pow_params`来完成。例如,如果您使用 giver 智能合约,`kf-kkdY_B7p-77TLn2hUhM6QidWrrsl8FYWCIvBMpZKprBtN`,您可以简单地键入: + +``` +> runmethod kf-kkdY_B7p-77TLn2hUhM6QidWrrsl8FYWCIvBMpZKprBtN get_pow_params +``` + +在轻客户端控制台中,并获得像这样的输出: + +```... + arguments: [ 101616 ] + result: [ 229760179690128740373110445116482216837 53919893334301279589334030174039261347274288845081144962207220498432 100000000000 256 ] + remote result (not to be trusted): [ 229760179690128740373110445116482216837 53919893334301279589334030174039261347274288845081144962207220498432 100000000000 256 ] +``` + +“result:”行中的前两个大数字分别是这个智能合约的`seed`和`complexity`。在此例中,seed是`229760179690128740373110445116482216837`,而复杂度是`53919893334301279589334030174039261347274288845081144962207220498432`。 + +接下来,您按如下方式调用`pow-miner`实用程序: + +``` +$ crypto/pow-miner -vv -w -t +``` + +这里: + +- ``是您希望用于挖矿的CPU核心数量。 +- ``是矿工运行失败前的最长秒数。 +- ``是您的钱包地址(可能尚未初始化)。它要么在主链上,要么在工作链上(请注意,您需要一个主链钱包来控制验证者)。 +- ``和``是通过运行get方法`get-pow-params`获得的最新值。 +- ``是所选proof-of-work giver智能合约的地址。 +- ``是成功时保存工作量证明的外部消息的输出文件的文件名。 + +例如,如果您的钱包地址是`kQBWkNKqzCAwA9vjMwRmg7aY75Rf8lByPA9zKXoqGkHi8SM7`,您可能会运行: + +``` +$ crypto/pow-miner -vv -w7 -t100 kQBWkNKqzCAwA9vjMwRmg7aY75Rf8lByPA9zKXoqGkHi8SM7 229760179690128740373110445116482216837 53919893334301279589334030174039261347274288845081144962207220498432 100000000000 kf-kkdY_B7p-77TLn2hUhM6QidWrrsl8FYWCIvBMpZKprBtN mined.boc +``` + +程序将运行一段时间(在这种情况下最多100秒),并且要么成功终止(zero exit code)并将所需的工作量证明保存在文件`mined.boc`中,要么以非零 exit code 终止,如果没有找到工作量证明。 + +在失败的情况下,您会看到像这样的内容: + +``` + [ expected required hashes for success: 2147483648 ] + [ hashes computed: 1192230912 ] +``` + +程序将以非零 exit code 终止。然后您必须再次获取`seed`和`complexity`(因为它们可能已经在此期间改变,因为更成功的矿工的请求已经被处理),并重新运行`pow-miner`,使用新参数重复过程,直到成功。 + +在成功的情况下,您会看到类似于: + +``` + [ expected required hashes for success: 2147483648 ] + 4D696E65005EFE49705690D2AACC203003DBE333046683B698EF945FF250723C0F73297A2A1A41E2F1A1F533B3BC4F5664D6C743C1C5C74BB3342F3A7314364B3D0DA698E6C80C1EA4ACDA33755876665780BAE9BE8A4D6385A1F533B3BC4F5664D6C743C1C5C74BB3342F3A7314364B3D0DA698E6C80C1EA4 + Saving 176 bytes of serialized external message into file `mined.boc` + [ hashes computed: 1122036095 ] +``` + +然后,您可以使用轻客户端将外部消息从文件`mined.boc`发送到 proof-of-work giver 智能合约(您必须尽快这样做): + +``` +> sendfile mined.boc +... external message status is 1 +``` + +您可以等待几秒钟,然后检查您的钱包状态: + +:::info +请注意,在此处和以下的代码、注释和/或文档中可能包含“gram”、“nanogram”等参数、方法和定义。这是原始TON代码的遗产,由Telegram开发。Gram加密货币从未发行。TON的货币是Toncoin,TON测试网的代币是Test Toncoin。 +::: + +``` +> last +> getaccount kQBWkNKqzCAwA9vjMwRmg7aY75Rf8lByPA9zKXoqGkHi8SM7 +... +account state is (account + addr:(addr_std + anycast:nothing workchain_id:0 address:x5690D2AACC203003DBE333046683B698EF945FF250723C0F73297A2A1A41E2F1) + storage_stat:(storage_info + used:(storage_used + cells:(var_uint len:1 value:1) + bits:(var_uint len:1 value:111) + public_cells:(var_uint len:0 value:0)) last_paid:1593722498 + due_payment:nothing) + storage:(account_storage last_trans_lt:7720869000002 + balance:(currencies + grams:(nanograms + amount:(var_uint len:5 value:100000000000)) + other:(extra_currencies + dict:hme_empty)) + state:account_uninit)) +x{C005690D2AACC203003DBE333046683B698EF945FF250723C0F73297A2A1A41E2F12025BC2F7F2341000001C169E9DCD0945D21DBA0004_} +last transaction lt = 7720869000001 hash = 83C15CDED025970FEF7521206E82D2396B462AADB962C7E1F4283D88A0FAB7D4 +account balance is 100000000000ng +``` + +如果在您之前没有人发送具有此`seed`和`complexity`的有效工作量证明,proof-of-work giver 将接受您的工作量证明,这将反映在您钱包的余额中(发送外部消息后可能需要10或20秒钟才会发生;请确保多次尝试并在每次检查钱包余额之前输入`last`以刷新轻客户端状态)。如果成功,您会看到余额增加(如果之前不存在,甚至您的钱包也会以未初始化的状态被创建)。如果失败,您将不得不获得新的`seed`和`complexity`,并从头开始重复挖矿过程。 + +如果您幸运并且钱包的余额增加了,如果之前没有初始化,您可能想初始化钱包(有关创建钱包的更多信息可以在`逐步操作`中找到): + +``` +> sendfile new-wallet-query.boc +... external message status is 1 +> last +> getaccount kQBWkNKqzCAwA9vjMwRmg7aY75Rf8lByPA9zKXoqGkHi8SM7 +... +account state is (account + addr:(addr_std + anycast:nothing workchain_id:0 address:x5690D2AACC203003DBE333046683B698EF945FF250723C0F73297A2A1A41E2F1) + storage_stat:(storage_info + used:(storage_used + cells:(var_uint len:1 value:3) + bits:(var_uint len:2 value:1147) + public_cells:(var_uint len:0 value:0)) last_paid:1593722691 + due_payment:nothing) + storage:(account_storage last_trans_lt:7720945000002 + balance:(currencies + grams:(nanograms + amount:(var_uint len:5 value:99995640998)) + other:(extra_currencies + dict:hme_empty)) + state:(account_active + ( + split_depth:nothing + special:nothing + code:(just + value:(raw@^Cell + x{} + x{FF0020DD2082014C97BA218201339CBAB19C71B0ED44D0D31FD70BFFE304E0A4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED54} + )) + data:(just + value:(raw@^Cell + x{} + x{00000001CE6A50A6E9467C32671667F8C00C5086FC8D62E5645652BED7A80DF634487715} + )) + library:hme_empty)))) +x{C005690D2AACC203003DBE333046683B698EF945FF250723C0F73297A2A1A41E2F1206811EC2F7F23A1800001C16B0BC790945D20D1929934_} + x{FF0020DD2082014C97BA218201339CBAB19C71B0ED44D0D31FD70BFFE304E0A4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED54} + x{00000001CE6A50A6E9467C32671667F8C00C5086FC8D62E5645652BED7A80DF634487715} +last transaction lt = 7720945000001 hash = 73353151859661AB0202EA5D92FF409747F201D10F1E52BD0CBB93E1201676BF +account balance is 99995640998ng +``` + +现在您是100 Toncoin的幸运拥有者。祝贺您! + +## 3. 在失败的情况下自动化挖矿过程 + +如果您长时间无法获得Toncoin,这可能是因为太多其他用户同时从同一个 proof-of-work giver 智能合约进行挖矿。也许您应该从上面给出的列表中选择另一个 proof-of-work giver 智能合约。或者,您可以编写一个简单的脚本,自动运行`pow-miner`,使用正确的参数一遍又一遍地运行,直到成功(通过检查`pow-miner`的 exit code 来检测),并调用带有参数`-c 'sendfile mined.boc'`的轻客户端,以便在找到后立即给他发送外部消息。 From 6c40fc52d55f27b68e6a23f19a7392d6b5808837 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:20 +0800 Subject: [PATCH 015/219] New translations sharding-lifecycle.mdx (Chinese Simplified) --- .../develop/blockchain/sharding-lifecycle.mdx | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/blockchain/sharding-lifecycle.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/blockchain/sharding-lifecycle.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/blockchain/sharding-lifecycle.mdx new file mode 100644 index 0000000000..8a1e77f43c --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/blockchain/sharding-lifecycle.mdx @@ -0,0 +1,68 @@ +# Infinity Sharding Paradigm + +## Understanding Split Merge in TON Blockchain + +The TON (Telegram Open Network) Blockchain introduces innovative concepts for blockchain scalability and efficiency. One such concept is the Split Merge functionality, integral to its blockchain architecture. This short article explores the key aspects of Split Merge in the TON Blockchain, focusing on its role within the Infinite Sharding Paradigm (ISP). + +#### Infinite Sharding Paradigm (ISP) and its Application + +ISP underpins the TON Blockchain's design, treating each account as part of its separate "accountchain." These accountchains are then aggregated into shardchain blocks for efficiency. The state of a shardchain comprises the states of all its accountchains. Thus, a shardchain block essentially is a collection of virtual blocks of accounts assigned to it. + +- **ShardState**: Approximated as Hashmap(n, AccountState), where n is the bit length of the account_id. +- **ShardBlock**: Approximated as Hashmap(n, AccountBlock). + +Each shardchain, or more precisely, each shardchain block, is identified by a combination of `workchain_id` and a binary prefix `s` of the account_id. + +## Sharding Example + +![](/img/docs/blockchain-fundamentals/split-merge.svg) + +In the provided graphic scheme: + +- The black line represents the masterchain. +- Shards of a workchain are divided by time and denoted in black dashed line. +- Blocks 101, 102, 103, and 80 relate to the masterchain block with seqno=29. Here, 101, 102, and 103 are in one shard, while 80 is in another. +- If a split or merge event happens, the affected shards pause until the next masterchain block. + +In summary, Split Merge in TON Blockchain is a complex yet efficient mechanism that enhances scalability and interaction within the blockchain network. It exemplifies TON's approach to resolving common blockchain challenges, emphasizing efficiency and global consistency. + +## Sharding Details + +#### Split and Non-Split Parts of Shardchain + +A shardchain block and state are divided into two parts: + +1. **Split Part**: Complies with the ISP form, containing account-specific data. +2. **Non-Split Part**: Involves data pertaining to the block's interaction with other blocks and the outside world. + +#### Interaction with Other Blocks + +The non-split parts are crucial for ensuring global consistency, reduced to internal and external local consistency conditions. They are significant for: + +- Message forwarding between shardchains. +- Transactions involving multiple shardchains. +- Delivery guarantees and validation of a block's initial state against its predecessor. + +#### Inbound and Outbound Messages + +Key components of the non-split part of a shardchain block include: + +- **InMsgDescr**: Descriptions of all messages imported into the block. +- **OutMsgDescr**: Descriptions of all messages exported or generated by the block. + +#### Block Header and Validator Signatures + +The block header, another non-split component, contains essential information like workchain_id, binary prefix of account_ids, and various hashes (e.g., of the immediate predecessor). Validator signatures are appended to the unsigned block, forming the signed block. + +#### Outbound Message Queue + +OutMsgQueue in the shardchain state is a critical non-split part. It holds undelivered messages from OutMsgDescr until they are processed or delivered to their destination. + +#### Shard Split and Merge Mechanics + +In the context of dynamic sharding, shard configurations may change due to split and merge events. These events are synchronized with the masterchain block. For instance, if a split or merge occurs, the affected shards wait for the next masterchain block before proceeding. + +## See Also + +- [Block Layout](/develop/data-formats/block-layout) +- [Whitepapers](/learn/docs) From 175b51024b700abeca47e20e3039f9be821cdb94 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:21 +0800 Subject: [PATCH 016/219] New translations shards.mdx (Chinese Simplified) --- .../current/develop/blockchain/shards.mdx | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/blockchain/shards.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/blockchain/shards.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/blockchain/shards.mdx new file mode 100644 index 0000000000..5f5f91e2e0 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/blockchain/shards.mdx @@ -0,0 +1,33 @@ +# Shards + +:::warning +Page is under development +::: + +Sharding is a mechanism in TON blockchain which allows to process a large number of transactions. The main idea of sharding in TON is that when account A sends a message to account B and account C sends a message to account D both of these operations can be performed asynchronously. + +By default in the Basechain (`workchain=0`) there is only one shard with a shard number `0x8000000000000000` (or `1000000000000000000000000000000000000000000000000000000000000000` in binary representation). The masterchain (`workchain=-1`) always has one and only one shard. + +## Splitting + +Each shard is responsible for some subset of accounts with some common binary prefix. This prefix appears in shard id which presented by a 64-bit integer and has the following structure: `100000...`. For example, shard with id `1011100000...` contains all accounts started with prefix `1011`. + +When number of transactions in some shard growth, it splits into two shards. New shards obtaining the following ids: `01000...` and `11000...` and responsible for accounts starting with `0` and `1` correspondingly. The seqnos of blocks in shards goes continuously starting from parent last seqno plus 1. After split shards go independently and may have different seqnos. + +Masterchain block contains information about shards in its header. After the block of shard appears in masterchain header it may be considered as finished (it cannot be rolled back). + +Example: + +- Masterchain block `seqno=34607821` has 2 shards: `(0,4000000000000000,40485798)` and `(0,c000000000000000,40485843)` (https://toncenter.com/api/v2/shards?seqno=34607821). +- Shard `shard=4000000000000000` was splitted into `shard=2000000000000000` and `shard=6000000000000000` and masterchain block `seqno=34607822` already has 3 shards: `(0,2000000000000000,40485799)` and `(0,6000000000000000,40485799)`. Note, that both new shards has the same seqnos but different shard IDs (https://toncenter.com/api/v2/shards?seqno=34607822). +- New shards go independently and after 100 masterchain blocks (in masterchain block `seqno=34607921`) one shard has last block `(0,2000000000000000,40485901)` and another one has `(0,6000000000000000,40485897)` (https://toncenter.com/api/v2/shards?seqno=34607921). + +## Merging + +When shards load goes down they can merge back as follow: + +- Two shards can merge if they have common parent and, therefore, their shard ids are `010...` and `10...` (for example `10010...` + `10110...` = `1010...`). The first block of merged shard will have `seqno=max(seqno1, seqno2) + 1`. + +Example: + +- In masterchain block `seqno=34626306` two of five shards with last blocks `(0,a000000000000000,40492030)` and `(0,e000000000000000,40492216)` merged into one with block `(0,c000000000000000,40492217)` (https://toncenter.com/api/v2/shards?seqno=34626306 and https://toncenter.com/api/v2/shards?seqno=34626307). From 4b66f1caf86cc77e6b2180465a18044daddcf63b Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:22 +0800 Subject: [PATCH 017/219] New translations auditors.mdx (Chinese Simplified) --- .../current/develop/companies/auditors.mdx | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/companies/auditors.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/companies/auditors.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/companies/auditors.mdx new file mode 100644 index 0000000000..2e03f21f6f --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/companies/auditors.mdx @@ -0,0 +1,26 @@ +import Button from '@site/src/components/button' + +# Security Assurance Providers(SAP) + +## TON SAP List + +Test your software with the following quality assurance providers: + +- [skynet.certik.com](https://skynet.certik.com/) +- [quantstamp.com](https://quantstamp.com/) +- [softstack.io formerly Chainsulting](https://softstack.io/) +- [slowmist.com](https://slowmist.com/) +- [hexens.io](https://hexens.io/) +- [vidma.io](https://vidma.io/) +- [scalebit](https://www.scalebit.xyz/) + +## Add New SAP + +If you are a new security provider for TON and want to be listed, please fill out the form. + + + +## See Also + +- [Outsource Development](/develop/companies/outsource) +- [Ton Jobs](https://jobs.ton.org/jobs) From 527bf338ed315a3959c2cdd0e07f96919e503866 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:22 +0800 Subject: [PATCH 018/219] New translations outsource.mdx (Chinese Simplified) --- .../current/develop/companies/outsource.mdx | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/companies/outsource.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/companies/outsource.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/companies/outsource.mdx new file mode 100644 index 0000000000..df9371e3c5 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/companies/outsource.mdx @@ -0,0 +1,157 @@ +import Button from '@site/src/components/button' + +# Outsource Development + +## Outsource Teams List + +Discover 3rd party development teams for your TON project + +- [Astralyx](#astralyx) +- [Blockczech R&D Lab](#blockczech-rd-lab) +- [EvaCodes](#evacodes) +- [Coinvent](#coinvent) +- [softstack](#softstack) + +### Astralyx + +#### Summary + +Company with big experience in TON and other chains development. You can ask us to create design, Telegram Mini Application (TMA), website, anything. + +#### Workstreams + +- TON smart-contracts development (including audits & tests) +- Web 2.0 & Web 3.0 development +- design, arts, renting leads for projects + +#### Projects + +- [t.me/xjetswapbot](http://t.me/xjetswapbot) (frontend, design), +- [github.com/astralyxdev/lockup-jettons-contract](http://github.com/astralyxdev/lockup-jettons-contract) (smartcontract, web interface, tests) +- [github.com/astralyxdev/ton-proxy](http://github.com/astralyxdev/ton-proxy) (TON Proxy extension, one of the first) +- [store.devdao.io](http://store.devdao.io) (front-end, design) +- [scaleton.io](http://scaleton.io) (landing, frontend, design) +- [burn.astralyx.dev](http://burn.astralyx.dev) (service for burning SBT NFT in TON, frontend, design) + +#### Contacts + +[astralyx.dev](http://astralyx.dev), contact@astralyx.dev + +### Blockczech R&D Lab + +#### Summary + +Web3.0 Software House & Startup Studio with a strong focus on blockchain-based solutions for games and eSports. + +#### Workstreams + +- dApps +- TMA Development +- blockchain games +- Integration + +#### Projects + +- [TCG.world](http://TCG.world) +- [cryptomeda.tech ](http://cryptomeda.tech) +- [liithos.com](http://liithos.com) +- [x.la/contracts/](http://x.la/contracts/) +- [About Blockczech R&D Lab](https://docs.google.com/presentation/d/1htMH1ihm31wQSn08ZziFfK6NpbPSHA3M/edit?usp=sharing&ouid=105247529013711719883&rtpof=true&sd=true) + +#### Contacts + +- http://blockczech.io +- [@blockczech](https://t.me/blockczech) + +### EvaCodes + +#### Summary + +EvaCodes is a top Eastern European blockchain development company with teams located in Ukraine, Armenia, and Poland. The company comprises over 50 skilled developers who have delivered 60+ web3 solutions, including web3 banking solutions, L1 blockchains, and web3 infrastructure. + +#### Workstreams + +- DeFi +- Crypto Wallets +- NFT-based solutions + +#### Projects + +- [alium.finance](https://alium.finance/) +- [trush.io](https://trush.io/) +- [konsta.network](https://konsta.network/) + +#### Contacts + +- ton@evacodes.com +- [evacodes.com](https://evacodes.com/) +- Telegram [@salesevacodes](https://t.me/salesevacodes) + +### Coinvent + +#### Summary + +Coinvent is a dedicated outsource development team, passionately committed to creating successful projects. Their expertise ranges from crafting simple bots to developing complex DeFi protocols. + +#### Workstreams + +- Smart-contracts +- DeFi, NFTs +- dApps, Telegram Mini Apps +- Regular Web2 +- Telegram Bots + +#### Projects + +- [Tonraffles Lock module](https://tonraffles.app/lock) (smart-contract, front-end) +- [Tonraffles NFT Launchpad](https://tonraffles.app/nft/launchpad) (smart-contracts) +- [OOIA shopping cart feature](https://testnet.ooia.art/) (smart-contract) +- [Monaki NFT Staking](https://www.monaki.life/) (smart-contracts) + +#### Contacts + +- [coinvent.dev](https://coinvent.dev) +- [@coinvent_dev](https://t.me/coinvent_dev) +- contact@coinvent.dev + +### softstack + +#### Summary + +Softstack formerly known as Chainsulting, is a leading provider of comprehensive Web3 solutions, with a focus on software development and smart contract auditing. + +#### Workstreams + +- Smart Contract & dApp Development +- Digital Asset Wallets +- Telegram Mini-Apps & Bots +- Cybersecurity (Smart Contract Audits, Penetration Testing) + +#### Projects + +- [DeGods](https://degods.com) +- [tixbase](https://tixbase.com) +- [TMRW Foundation](https://tmrw.com) +- [Bitcoin.com](https://bitcoin.com) +- [Coinlink Finance](https://coinlink.finance) + +#### Contacts + +- hello@softstack.io +- [softstack.io](https://softstack.io/) +- Telegram [@softstack](https://t.me/softstack) + +## Add your team + +If you are fully prepared to become an outsourced agent for TON Ecosystem, you can promote your company by filling out our form or submitting a pull request. + + + +



+ +## See Also + +- [Security Assurance Providers](/develop/companies/auditors) From 3f836b4a901416f2086507f0823ae1a35b1f6669 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:23 +0800 Subject: [PATCH 019/219] New translations readme.mdx (Chinese Simplified) --- .../current/develop/dapps/README.mdx | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/README.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/README.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/README.mdx new file mode 100644 index 0000000000..b6d435d3fa --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/README.mdx @@ -0,0 +1,92 @@ +import Button from '@site/src/components/button' + +# Getting Started + +Before diving into DApps, make sure you understand the underlying blockchain principles. You might find our [The Open Network](/learn/introduction) and [Blockchain of Blockchains](/learn/overviews/ton-blockchain) articles useful. + +TON DApps are applications without backend, which interact with the blockchain. In most cases, they interact with custom [smart contracts](/develop/smart-contracts/); this documentation provides ways to process standard assets available in TON, both as an example and for quicker DApps development. + +DApps generally can be written in any programming language having SDK for TON. Actually, the most frequent choice is a website, followed by Telegram mini-apps. + + + + +## TON Course: DApps + +The [TON Blockchain Course](https://stepik.org/course/176754/) is a comprehensive guide to TON Blockchain development. + +Module 5 and 6 completely covers DApps development. You'll learn how to create a DApp, how to work with TON Connect, how to use SDKs and how to work with the blockchain. + + + +## Basic Tools and Resources + +Here are some key resources that you'll need throughout your DApp development journey: + +1. [Developer Wallets](/participate/wallets/apps) +2. [Blockchain Explorers](/participate/explorers) +3. [API Documentation](/develop/dapps/apis/) +4. [SDKs for Various Languages](/develop/dapps/apis/sdk) +5. [Using the Testnet](/develop/smart-contracts/environment/testnet) +6. [TON Unfreezer](https://unfreezer.ton.org/) + +### Asset Management + +Working with assets? These guides cover the essentials: + +- [Payments Processing](/develop/dapps/asset-processing/) +- [Token (Jetton) Processing](/develop/dapps/asset-processing/jettons) +- [Handling NFTs](/develop/dapps/asset-processing/nft) +- [Parsing Metadata](/develop/dapps/asset-processing/metadata) + +### Introduction to DeFi + +Interested in decentralized finance (DeFi)? Here's how to handle different types of assets: + +- [Understanding Toncoin](/develop/dapps/defi/coins) +- [Tokens: Jettons & NFTs](/develop/dapps/defi/tokens) +- [TON Payments](/develop/dapps/defi/ton-payments) +- [Setting up Subscriptions](/develop/dapps/defi/subscriptions) + +## Tutorials and Examples + +### DeFi Basics + +- Create Your First Token: [Mint Your First Jetton](/develop/dapps/tutorials/jetton-minter) +- Step by Step: [NFT Collection Minting](/develop/dapps/tutorials/collection-minting) + +### Language-Specific Guides + +#### JavaScript + +- [Payment Process](https://github.com/toncenter/examples) +- [TON Bridge](https://github.com/ton-blockchain/bridge) +- [Web Wallet](https://github.com/toncenter/ton-wallet) +- [Dumpling Sales Bot](/develop/dapps/tutorials/accept-payments-in-a-telegram-bot-js) + +#### Python + +- [Example Projects](https://github.com/psylopunk/pytonlib/tree/main/examples) +- [Storefront Bot](/develop/dapps/tutorials/accept-payments-in-a-telegram-bot) + +#### Go + +- [Examples](https://github.com/xssnick/tonutils-go/tree/master/example) + +### Advanced Topics + +- [Zero-Knowledge Proofs](/develop/dapps/tutorials/simple-zk-on-ton) + +### Wallets Examples + +- [Desktop standard wallet (C++ and Qt)](https://github.com/ton-blockchain/wallet-desktop) +- [Android standard wallet (Java)](https://github.com/ton-blockchain/wallet-android) +- [iOS standard wallet (Swift)](https://github.com/ton-blockchain/wallet-ios) +- [TonLib CLI (C++)](https://github.com/ton-blockchain/ton/blob/master/tonlib/tonlib/tonlib-cli.cpp) + +## 👨‍💻 Contribution + +Missing some crucial material? You can either write a tutorial yourself or describe the problem for the community. + + + From 4be5f90df62b1e676bd6237597b55d092431d709 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:24 +0800 Subject: [PATCH 020/219] New translations readme.md (Chinese Simplified) --- .../current/develop/dapps/apis/README.md | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/apis/README.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/apis/README.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/apis/README.md new file mode 100644 index 0000000000..4633b1c497 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/apis/README.md @@ -0,0 +1,82 @@ +# API Types + +**High availability blockchain APIs are the core element of secure, convenient and fast development of useful applications on TON.** + +- [TON HTTP API](/develop/dapps/apis/toncenter) — API that allows to work with the _indexed blockchain information_. +- [TON ADNL API](/develop/dapps/apis/adnl) — secure API to communicate with TON, based on ADNL protocol. + +## Toncenter APIs + +- [TON Index](https://toncenter.com/api/v3/) - TON Index collects data from a full node to PostgreSQL database and provides convenient API to an indexed blockchain. +- [toncenter/v2](https://toncenter.com/) - This API enables HTTP access to TON blockchain - getting accounts and wallets information, looking up blocks and transactions, sending messages to the blockchain, calling get methods of smart contracts, and more. + +## Third party APIs + +- [tonapi.io](https://docs.tonconsole.com/tonapi/api-v2) - fast indexed API which provides basic data about accounts, transactions, blocks, application-specific data about NFT, Auctions, Jettons, TON DNS, Subscriptions. It also provides annotated data on transaction chains. +- [dton.io](https://dton.io/graphql/) - GraphQL API with that can provide data about accounts, transactions and blocks, as well as application-specific data about NFT, Auctions, Jettons and TON DNS. +- [ton-api-v4](https://mainnet-v4.tonhubapi.com) - another lite-api focused on speed via aggressive cashing in CDN. +- [docs.nftscan.com](https://docs.nftscan.com/reference/ton/model/asset-model) - NFT APIs for TON blockchain. +- [evercloud.dev](https://ton-mainnet.evercloud.dev/graphql) - GraphQL API for basic queries in TON. +- [everspace.center](https://everspace.center/toncoin) - Simple RPC API for accessing TON Blockchain. + +## Additional APIs + +### Toncoin rate APIs + +- https://tonapi.io/v2/rates?tokens=ton¤cies=ton%2Cusd%2Crub +- https://coinmarketcap.com/api/documentation/v1/ +- https://apiguide.coingecko.com/getting-started + +### Address Convert APIs + +:::info +It is preferable to convert address via local algorithm, read more in the [Addresses](/learn/overviews/addresses) section of documentation. +::: + +#### From Friendly to Raw form + +/api/v2/unpackAddress + +Curl + +```curl +curl -X 'GET' \ +'https://toncenter.com/api/v2/unpackAddress?address=EQApAj3rEnJJSxEjEHVKrH3QZgto_MQMOmk8l72azaXlY1zB' \ +-H 'accept: application/json' +``` + +Response body + +```curl +{ +"ok": true, +"result": "0:29023deb1272494b112310754aac7dd0660b68fcc40c3a693c97bd9acda5e563" +} +``` + +#### From Friendly to Raw form + +/api/v2/packAddress + +Curl + +```curl +curl -X 'GET' \ +'https://toncenter.com/api/v2/packAddress?address=0%3A29023deb1272494b112310754aac7dd0660b68fcc40c3a693c97bd9acda5e563' \ +-H 'accept: application/json' +``` + +Response body + +```json +{ + "ok": true, + "result": "EQApAj3rEnJJSxEjEHVKrH3QZgto/MQMOmk8l72azaXlY1zB" +} +``` + +## See Also + +- [TON HTTP API](/develop/dapps/apis/toncenter) +- [List of SDKs](/develop/dapps/apis/sdk) +- [TON Cookbook](/develop/dapps/cookbook) From 125a3c62c448f3da81667f29e9b8d919d263077b Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:25 +0800 Subject: [PATCH 021/219] New translations adnl.md (Chinese Simplified) --- .../current/develop/dapps/apis/adnl.md | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/apis/adnl.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/apis/adnl.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/apis/adnl.md new file mode 100644 index 0000000000..e8a10265a3 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/apis/adnl.md @@ -0,0 +1,47 @@ +# TON ADNL API + +:::tip + +There are different ways to connect to blockchain: + +1. RPC data provider or another API: in most cases, you have to _rely_ on its stability and security. +2. **ADNL connection**: you're connecting to a [liteserver](/participate/run-nodes/liteserver). They might be inaccessible, but with a certain level of validation (implemented in the library), cannot lie. +3. Tonlib binary: you're connecting to liteserver as well, so all benefits and downsides apply, but your application also contains a dynamic-loading library compiled outside. +4. Offchain-only. Such SDKs allow to create and serialize cells, which you can then send to APIs. + +::: + +Clients connect directly to Liteservers (nodes) using a binary protocol. + +The client downloads keyblocks, the current state of the account, and their **Merkle proofs**, which guarantees the validity of the received data. + +Read operations (like get-method calls) are made by launching a local TVM with a downloaded and verified state. It's worth noting that there is no need to download the full state of the blockchain, the client downloads only what is needed for the operation. + +You can connect to public liteservers from the global config ([Mainnet](https://ton.org/global-config.json) or [Testnet](https://ton.org/testnet-global.config.json)) or run your own [Liteserver](/participate/nodes/node-types) and handle this with [ADNL SDKs](/develop/dapps/apis/sdk#adnl-based-sdks). + +Read more about [Merkle proofs](/develop/data-formats/proofs) at [TON Whitepaper](https://ton.org/ton.pdf) 2.3.10, 2.3.11. + +Public liteservers (from the global config) exist to get you started with TON quickly. It can be used for learning to program in TON, or for applications and scripts that do not require 100% uptime. + +For building production infrastructure - it is suggested use well prepared infrastructure: + +- [set up own liteserver](https://docs.ton.org/participate/run-nodes/full-node#enable-liteserver-mode), +- use Liteserver premium providers [@liteserver_bot](https://t.me/liteserver_bot) + +## Pros & Cons + +- ✅ Reliable. Uses API with Merkle proof hashes to verify incoming binary data. + +- ✅ Secure. Since it checks Merkle proofs, you can even use untrusted liteservers. + +- ✅ Fast. Connects directly to TON Blockchain nodes, instead of using HTTP middleware. + +- ❌ Complicated. More time is required to figure things out. + +- ❌ Back-end first. Not compatible with web frontends (built for non-HTTP protocol), or requires HTTP-ADNL proxy. + +## API reference + +Requests and responses to the server are described by the [TL](/develop/data-formats/tl) schema that allows you to generate a typed interface for a certain programming language. + +[TonLib TL Schema](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl) From 717f2ec34c4072966ef16460091a74b786ff64b6 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:26 +0800 Subject: [PATCH 022/219] New translations getblock-ton-api.md (Chinese Simplified) --- .../develop/dapps/apis/getblock-ton-api.md | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/apis/getblock-ton-api.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/apis/getblock-ton-api.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/apis/getblock-ton-api.md new file mode 100644 index 0000000000..9dc11edf0d --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/apis/getblock-ton-api.md @@ -0,0 +1,111 @@ +# TON API by GetBlock + +This guide will cover essential steps in acquiring and using private RPC endpoints by GetBlock to access the TON blockchain. + +:::info +[GetBlock](https://getblock.io/) is a Web3 infrastructure provider, offers HTTP-based API endpoints for clients to interact with various blockchain networks, including TON. +::: + +## How to Access TON Blockchain Endpoints + +To start using GetBlock’s endpoints, users are required to log in to their account, retrieve a TON endpoint URL, and they're good to go. Follow along for more detailed guidance. + +### 1. Create a GetBlock Account + +Visit the GetBlock [website](https://getblock.io/?utm_source=external\&utm_medium=article\&utm_campaign=ton_docs) and locate the “Get Started for Free” button on the main page. Sign up for an account with your email address or by connecting a MetaMask wallet. + +![GetBlock.io\_main\_page](/img/docs/getblock-img/unnamed-2.png?=RAW) + +### 2. Select the TON Blockchain + +After signing in, you’ll be redirected to the dashboard. Locate the section titled "My Endpoints" and select "TON" under the "Protocols" dropdown menu. + +Choose the desired network and API type (JSON-RPC or JSON-RPC(v2)). + +![GetBlock\_account\_\_dashboard](/img/docs/getblock-img/unnamed-4.png) + +### 3. Generate Your Endpoint URL + +Click on the “Get” button to generate your TON blockchain endpoint URL. + +All endpoints within the GetBlock API follow a consistent structure: `https://go.getblock.io/[ACCESS TOKEN]/`. + +These access tokens serve as unique identifiers for each user or application and contain information necessary for routing the request to appropriate endpoints without revealing sensitive details. It essentially replaces the need for separate authorization headers or API keys. + +Users have the flexibility to generate multiple endpoints, roll tokens if compromised, or delete unused endpoints. + +![GetBlock\_account\_endpoints](/img/docs/getblock-img/unnamed-3.png) + +Now, you can use these URLs to interact with the TON blockchain, query data, send transactions, and build decentralized applications without the hassle of infrastructure setup and maintenance. + +### Free Requests and User Limits + +Please note that every registered GetBlock user receives 40,000 free requests capped at 60 RPS (Requests per Second). The request balance is renewed daily and can be utilized on any shared endpoints across supported blockchains. + +For enhanced features and capabilities, users have the option to explore paid options, which will be outlined below. + +GetBlock.io offers two types of plans: Shared Nodes and Dedicated Nodes. Clients can choose the tariff based on their requirements and budget. + +### Shared Nodes + +- Entry-level opportunity where same nodes are utilized by several clients simultaneously; +- Rate limit increased to 200 RPS; +- Well-suited for individual use or for applications that have lower transaction volumes and resource requirements compared to fully-scaled production applications; +- A more affordable option for individual developers or small teams with limited budgets. + +Shared nodes provide a cost-effective solution for accessing TON blockchain infrastructure without the need for significant upfront investment or commitment. + +As developers scale their applications and require additional resources, they can easily upgrade their subscription plans or transition to dedicated nodes if necessary. + +### Dedicated Nodes + +- One node is exclusively allocated to a single client; + No request limits; +- Opens access to archive nodes, a variety of server locations, and custom settings; +- Guarantees premium-level service and support to clients. + +This is a next-level solution for developers and decentralized applications (dApps) that require enhanced throughput, higher speed of node connection, and guaranteed resources as they scale. + +## How to use TON HTTP API by GetBlock + +In this section, we'll delve into the practical usage of the TON HTTP API provided by GetBlock. We'll explore examples to showcase how to effectively utilize the generated endpoints for your blockchain interactions. + +### Examples of common API calls + +Let's begin with a simple example using the ‘/getAddressBalance’ method to retrieve the balance for a specific address using the curl command. + +``` +curl --location --request GET 'https://go.getblock.io//getAddressBalance?address=EQDXZ2c5LnA12Eum-DlguTmfYkMOvNeFCh4rBD0tgmwjcFI-' \ + +--header 'Content-Type: application/json' +``` + +Make sure to replace `ACCESS-TOKEN` with your actual access token provided by GetBlock. + +This will output the balance in nanotons. + +![getAddressBalance\_response\_on\_TON\_blockchain](/img/docs/getblock-img/unnamed-2.png) + +Some other available methods to query the TON blockchain: + +| # | Method | Endpoint | Description | +| - | ------ | ------------------ | ----------------------------------------------------------------------------------------------------------------------------- | +| 1 | GET | getAddressState | returns the current status (uninitialized, active, or frozen) of a specified address on the TON blockchain | +| 2 | GET | getMasterchainInfo | Fetches information about the state of the masterchain | +| 3 | GET | getTokenData | Retrieves details about an NFT or Jetton associated with a specified TON account | +| 4 | GET | packAddress | Converts a TON address from its raw format to a human-readable format | +| 5 | POST | sendBoc | Sends serialized BOC files along with external messages to the blockchain for execution | + +Please refer to GetBlock's [documentation](https://getblock.io/docs/ton/json-rpc/ton_jsonrpc/) for a comprehensive API reference with examples and a list of additional methods. + +### Deploying smart contracts + +Developers can utilize the same endpoint URLs to seamlessly deploy contracts onto the TON blockchain using the TON library. + +The library will initialize a client to connect to the network via the GetBlock HTTP API endpoints. + +![Image from TON Blueprint IDE](/img/docs/getblock-img/unnamed-6.png) + +This tutorial should provide a comprehensive guide for developers looking to utilize GetBlock's API with the TON Blockchain effectively. + +Feel free to learn more from the [website](https://getblock.io/?utm_source=external\&utm_medium=article\&utm_campaign=ton_docs) or drop a line to GetBlock’s support via live chat, [Telegram](https://t.me/GetBlock_Support_Bot), or a website form. From dc7c42cc862c682aa60fae4a231c5843e9ea268b Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:26 +0800 Subject: [PATCH 023/219] New translations sdk.mdx (Chinese Simplified) --- .../current/develop/dapps/apis/sdk.mdx | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/apis/sdk.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/apis/sdk.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/apis/sdk.mdx new file mode 100644 index 0000000000..358a750c40 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/apis/sdk.mdx @@ -0,0 +1,77 @@ +# SDKs + +Instant navigate on preferred language with right sidebar. + +## Overview + +There are different ways to connect to blockchain: + +1. RPC data provider or other API: in most cases, you have to _rely_ on its stability and security. +2. ADNL connection: you're connecting to a [liteserver](/participate/run-nodes/liteserver). They might be inaccessible, but with a certain level of validation (implemented in the library), cannot lie. +3. Tonlib binary: you're connecting to liteserver as well, so all benefits and downsides apply, but your application also contains a dynamic-loading library compiled outside. +4. Offchain-only. Such SDKs allow to create and serialize cells, which you can then send to APIs. + +### TypeScript / JavaScript + +| Library | Blockchain connection | Description | +| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [ton](https://github.com/ton-org/ton) | via RPC ([Orbs](https://www.orbs.com/ton-access/) / [Toncenter](https://toncenter.com/api/v2/) / etc) | Convenient client library with wallet wrappers for development dApps on TON Blockchain. | +| [tonweb](https://github.com/toncenter/tonweb) | via RPC ([Orbs](https://www.orbs.com/ton-access/) / [Toncenter](https://toncenter.com/api/v2/) / etc) | Old-style TON JS SDK, with minimal external dependencies, extensively tested in production. | +| [tonkite/adnl](https://github.com/tonkite/adnl) | [ADNL](/develop/network/adnl-tcp) natively / via WebSocket | ADNL TypeScript implementation. | +| [tonutils](https://github.com/thekiba/tonutils) | Native [ADNL](/develop/network/adnl-tcp) | TypeScript-based interface for building and interacting with applications in TON Ecosystem. Due to native ADNL dependency, cannot be used for blockchain interaction in browser. | + +### Java + +| Library | Blockchain connection | Description | +| ------------------------------------------- | --------------------- | ------------------------------------------------------ | +| [ton4js](https://github.com/neodix42/ton4j) | Tonlib binary | Java SDK for The Open Network (TON) | + +### Python + + + +| Library | Blockchain connection | Description | +| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- | +| [pytoniq](https://github.com/yungwine/pytoniq) | Native ADNL | Python SDK with native LiteClient and other ADNL-based protocols implementations. | +| [pytoniq-core](https://github.com/yungwine/pytoniq-core) | _offchain-only_ | Python powerful transport-free SDK | +| [pytonlib](https://github.com/toncenter/pytonlib) | Tonlib binary | This is standalone Python library based on libtonlibjson, brought as a binary dependency from TON monorepo. | +| [mytonlib](https://github.com/igroman787/mytonlib) | Native ADNL | Native Python SDK library for working with The Open Network | +| [TonTools](https://github.com/yungwine/TonTools) | via RPC ([Orbs](https://www.orbs.com/ton-access/) / [Toncenter](https://toncenter.com/api/v2/) / etc) | TonTools is a high-level OOP library for Python, which can be used to interact with TON Blockchain. | +| [tonpy](https://github.com/disintar/tonpy) | Native ADNL | Python package that provides data structures and API to interact with TON blockchain. | +| [tvm_valuetypes](https://github.com/toncenter/tvm_valuetypes) | _offchain-only_ | library is collection of utilits for handling TVM types. | +| [pytvm](https://github.com/yungwine/pytvm) | _offchain_, Tonlib | Python TVM emulator using bindings to C++ standard emulator | + +### C# + +| Library | Blockchain connection | Description | +| ----------------------------------------------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------- | +| [TonSdk.NET](https://github.com/continuation-team/TonSdk.NET) | Native ADNL or RPC | Native C# SDK for The Open Network. | +| [justdmitry/TonLib.NET](https://github.com/justdmitry/TonLib.NET) | Tonlib binary | .NET SDK for The Open Network, connecting via libtonlibjson brought as a binary dependency from TON monorepo. | + +### Rust + +| Library | Blockchain connection | Description | +| ------------------------------------------------------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------- | +| [tonlib-rs](https://github.com/ston-fi/tonlib-rs) | Tonlib binary | Rust SDK for The Open Network, bringing binary dependency from TON monorepo. | +| [getgems-io/ton-grpc](https://github.com/getgems-io/ton-grpc) | Tonlib binary | Rust bindings for tonlibjson (thus, depending on binary from TON monorepo) and services built on top of it | + +### Go + +| Library | Blockchain connection | Description | +| -------------------------------------------------------- | --------------------- | -------------------------------------------------- | +| [tonutils-go](https://github.com/xssnick/tonutils-go) | Native ADNL | Golang library for interacting with TON blockchain | +| [tongo](https://github.com/tonkeeper/tongo) | Native ADNL | Go implementation of libraries for TON blockchain | +| [tonlib-go](https://github.com/ton-blockchain/tonlib-go) | Tonlib binary | Official bindings for libtonlibjson | + +### SDKs for other languages + +| Library | Language | Blockchain connection | Description | | +| --------------------------------------------------------------------------- | -------- | --------------------- | ------------------------------------------------------------------- | - | +| [ton-kotlin](https://github.com/ton-community/ton-kotlin) | Kotlin | Native ADNL | Kotlin/Multiplatform SDK for The Open Network. | | +| [tonlib-java](https://github.com/ton-blockchain/tonlib-java) | Java | Tonlib bin | JVM wrapper for TonLib that can be used with Java/Scala/Kotlin/etc. | | +| [ayrat555/ton](https://github.com/ayrat555/ton) | Elixir | _offchain-only_ | TON SDK for Elixir. | | +| [C++ Tonlib](https://github.com/ton-blockchain/ton/tree/master/example/cpp) | C++ | Tonlib binary | Official examples on smart contract interaction in TON monorepo | . | +| [Java Tonlib](https://github.com/ton-blockchain/tonlib-java) | Java | Tonlib binary | Official examples on smart contract interaction in TON monorepo. | | +| [labraburn/SwiftyTON](https://github.com/labraburn/SwiftyTON) | Swift | Tonlib binary | Native Swift wrapper for tonlib with async/await. | | +| [tonlib-xcframework](https://github.com/labraburn/tonlib-xcframework) | Swift | Tonlib binary | Tonlib build helper for iOS, all architectures. | | +| [labraburn/node-tonlib](https://github.com/labraburn/node-tonlib) | NodeJS | Tonlib binary | C++ addon for NodeJS to work with tonlibjson. | | From 839e943984fe47781d46096ce1fb5bda1131efee Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:27 +0800 Subject: [PATCH 024/219] New translations toncenter.md (Chinese Simplified) --- .../current/develop/dapps/apis/toncenter.md | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/apis/toncenter.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/apis/toncenter.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/apis/toncenter.md new file mode 100644 index 0000000000..77069981f5 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/apis/toncenter.md @@ -0,0 +1,52 @@ +# TON HTTP-based APIs + +:::tip + +There are different ways to connect to blockchain: + +1. **RPC data provider or another API**: in most cases, you have to _rely_ on its stability and security. +2. ADNL connection: you're connecting to a [liteserver](/participate/run-nodes/liteserver). They might be inaccessible, but with a certain level of validation (implemented in the library), cannot lie. +3. Tonlib binary: you're connecting to liteserver as well, so all benefits and downsides apply, but your application also contains a dynamic-loading library compiled outside. +4. Offchain-only. Such SDKs allow to create and serialize cells, which you can then send to APIs. + +::: + +## Pros & Cons + +- ✅ Habitual and suitable for a quick start, this is perfect for every newcomer looking to play with TON. + +- ✅ Web-oriented. Perfect to load data of TON smart contracts from Web, also allows to send messages there. + +- ❌ Simplified. It's not possible to receive information where you need an indexed TON API. + +- ❌ HTTP-Middleware. You can't fully trust server responses, unless server augments blockchain data with [Merkle proofs](/develop/data-formats/proofs) to allow validation that it is genuine. + +## RPC Nodes + +- [GetBlock Nodes](https://getblock.io/nodes/ton/) — connect and test your dApps using GetBlocks Nodes +- [TON Access](https://www.orbs.com/ton-access/) - HTTP API for The Open Network (TON). +- [Toncenter](https://toncenter.com/api/v2/) — community-hosted project for Quick Start with API. (Get an API key [@tonapibot](https://t.me/tonapibot)) +- [ton-node-docker](https://github.com/fmira21/ton-node-docker) - Docker Full Node and Toncenter API. +- [toncenter/ton-http-api](https://github.com/toncenter/ton-http-api) — run your own RPC node. +- [nownodes.io](https://nownodes.io/nodes) — NOWNodes full Nodes and blockbook Explorers via API. +- [Chainbase](https://chainbase.com/chainNetwork/TON) — Node API and data infrastructure for The Open Network. + +## Indexer + +### Toncenter TON Index + +Indexers allow to list jetton wallets, NFTs, transactions by certain filters, not only retrieve specific ones. + +- Public TON Index can be used: tests and development are for free, [premium](https://t.me/tonapibot) for production - [toncenter.com/api/v3/](https://toncenter.com/api/v3/). +- Run your own TON Index with [Worker](https://github.com/toncenter/ton-index-worker/tree/36134e7376986c5517ee65e6a1ddd54b1c76cdba) and [TON Index API wrapper](https://github.com/toncenter/ton-indexer). + +### GraphQL Nodes + +GraphQL nodes act as indexers as well. + +- [tvmlabs.io](https://ton-testnet.tvmlabs.dev/graphql) (for TON, testnet only at the moment of writing) - has wide variety of transaction/block data, ways to filter it, etc. +- [dton.io](https://dton.io/graphql) - as well as providing contracts data augmented with parsed "is jetton", "is NFT" flags, allows emulating transactions and receiving execution traces. + +## Other APIs + +- [TonAPI](https://docs.tonconsole.com/tonapi/api-v2) - API that is designed to provide users with a streamlined experience, not worrying about low-level details of smart contracts. From 07ef30447e761c71cf24b0928cc1ea53751837ee Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:28 +0800 Subject: [PATCH 025/219] New translations readme.md (Chinese Simplified) --- .../develop/dapps/asset-processing/README.md | 215 ++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/README.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/README.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/README.md new file mode 100644 index 0000000000..f57c8e9fe4 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/README.md @@ -0,0 +1,215 @@ +import Button from '@site/src/components/button' + +# Processing Global Overview + +This page contains an overview and specific details that explain how to process (send and accept) digital assets on the TON blockchain. + +:::info Transaction Confirmation +TON transactions are irreversible after just one confirmation. For the best user experience, it is suggested to avoid waiting on additional blocks once transactions are finalized on the TON Blockchain. Read more in the [Catchain.pdf](https://docs.ton.org/catchain.pdf#page=3). +::: + +## Best Practices + +### Toncoin + +#### Toncoin Deposits + +:::info +It is suggested to set several MEMO deposit wallets for better performance. +::: + +- [MEMO Deposits](https://github.com/toncenter/examples/blob/main/deposits.js) + +#### Toncoin Withdrawals + +- [Batched withdrawals](https://github.com/toncenter/examples/blob/main/withdrawals-highload-batch.js) + +- [Withdrawals](https://github.com/toncenter/examples/blob/main/withdrawals-highload.js) + +- [Detailed info](/develop/dapps/asset-processing#global-overview) + +### Jetton + +- [Read Jetton Proccesing](/develop/dapps/asset-processing/jettons) + +### Made by TON Community + +#### GO + +- [Gobicycle](https://github.com/gobicycle/bicycle) - service is focused on replenishing user balances and sending payments to blockchain accounts. Both TONs and Jettons are supported. The service is written with numerous pitfalls in mind that a developer might encounter (all checks for jettons, correct operations status check, resending messages, performance during high load when blockchain is splitted by shards). Provide simple HTTP API, rabbit and webhook notifications about new payments. +- [GO examples](https://github.com/xssnick/tonutils-go#how-to-use) + +#### JavaScript + +Using ton.js SDK (supported by TON Community): + +- [Create a key pair, a wallet and get a wallet address](https://github.com/toncenter/examples/blob/main/common.js) +- [Create a wallet, get its balance, make a transfer](https://github.com/ton-community/ton#usage) + +#### Python + +Using tonsdk library (similar to tonweb): + +- [Init wallet, create external message to deploy the wallet](https://github.com/tonfactory/tonsdk#create-mnemonic-init-wallet-class-create-external-message-to-deploy-the-wallet) + +## Global Overview + +Embodying a fully asynchronous approach, TON Blockchain involves a few concepts which are uncommon to traditional blockchains. Particularly, each interaction of any actor with the blockchain consists of a graph of asynchronously transferred messages between smart contracts and/or the external world. The common path of any interaction starts with an external message sent to a `wallet` smart contract, which authenticates the message sender using public-key cryptography, takes charge of fee payment, and sends inner blockchain messages. That way, transactions on the TON network are not synonymous with user interaction with the blockchain but merely nodes of the message graph: the result of accepting and processing a message by a smart contract, which may or may not lead to the emergence of new messages. The interaction may consist of an arbitrary number of messages and transactions and span a prolonged period of time. Technically, transactions with queues of messages are aggregated into blocks processed by validators. The asynchronous nature of the TON Blockchain **does not allow to predict the hash and lt (logical time) of a transaction** at the stage of sending a message. The transaction accepted to the block is final and cannot be modified. + +**Each inner blockchain message is a message from one smart contract to another, which bears some amount of digital assets, as well as an arbitrary portion of data.** + +Smart contract guidelines recommend treating the data payload, which begins with 32 binary zeros, as a human-readable text message. Most software, such as wallets and libraries, support this specification and allow to send text comments along with Toncoin as well as display comments in other messages. + +Smart contracts **pay fees for transactions** (usually from the balance of an incoming message) as well as a **storage fee for the contract's stored code and data**. Fees depend on workchain configs with maximal fees on `masterchain` and substantially lower fees on `basechain`. + +## Digital assets on TON + +TON has three types of digital assets. + +- Toncoin, the main token of the network. It is used for all basic operations on the blockchain, for example, paying gas fees or staking for validation. +- Native tokens, which are special kinds of assets that can be attached to any message on the network. These assets are currently not in use since the functionality for issuing new native tokens is closed. +- Contract assets, such as tokens and NFTs, which are analogous to the ERC-20/ERC-721 standards and are managed by arbitrary contracts and thus can require custom rules for processing. You can find more info on it's processin in [process NFTs](/develop/dapps/asset-processing/nfts) and [process Jettons](/develop/dapps/asset-processing/jettons) articles. + +### Simple Toncoin transfer + +To send Toncoin, the user needs to send a request via an external message, that is, a message from the outside world to the blockchain, to a special `wallet` smart contract (see below). Upon receiving this request, `wallet` will send an inner message with the desired amount of assets and optional data payload, for instance a text comment. + +## Wallet smart contract + +Wallet smart contracts are contracts on the TON Network which serve the task of allowing actors outside the blockchain to interact with blockchain entities. Generally, it solves three challenges: + +- authenticates owner: Rejects to process and pay fees for non-owners' requests. +- replays protection: Prohibits the repetitive execution of one request, for instance sending assets to some other smart contract. +- initiates arbitrary interaction with other smart contracts. + +Standard solution for the first challenge is public-key cryptography: `wallet` stores the public key and checks that an incoming message with a request is signed by the corresponding private key which is known only by the owner. The solution to the third challenge is common as well; generally, a request contains a fully formed inner message `wallet` sends to the network. However, for replay protection, there are a few different approaches. + +### Seqno-based wallets + +Seqno-based wallets follow the most simple approach to sequencing messages. Each message has a special `seqno` integer that must coincide with the counter stored in the `wallet` smart contract. `wallet` updates its counter on each request, thus ensuring that one request will not be processed twice. There are a few `wallet` versions that differ in publicly available methods: the ability to limit requests by expiration time, and the ability to have multiple wallets with the same public key. However, an inherent requirement of that approach is to send requests one by one, since any gap in `seqno` sequence will result in the inability to process all subsequent requests. + +### High-load wallets + +This `wallet` type follows an approach based on storing the identifier of the non-expired processed requests in smart-contract storage. In this approach, any request is checked for being a duplicate of an already processed request and, if a replay is detected, dropped. Due to expiration, the contract may not store all requests forever, but it will remove those that cannot be processed due to the expiration limit. Requests to this `wallet` may be sent in parallel without interfering with each other; however, this approach requires more sophisticated monitoring of request processing. + +## Interaction with blockchain + +Basic operations on TON Blockchain can be carried out via TonLib. It is a shared library which can be compiled along with a TON node and expose APIs for interaction with the blockchain via so-called lite servers (servers for lite clients). TonLib follows a trustless approach by checking proofs for all incoming data; thus, there is no necessity for a trusted data provider. Methods available to TonLib are listed [in the TL scheme](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L234). They can be used either as a shared library via wrappers like [pyTON](https://github.com/EmelyanenkoK/pyTON) or [tonlib-go](https://github.com/mercuryoio/tonlib-go/tree/master/v2) (technically those are the wrappers for `tonlibjson`) or through `tonlib-cli`. + +## Wallet deployment + +To deploy a wallet via TonLib one needs to: + +1. Generate a private/public key pair via [createNewKey](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L213) or its wrapper functions (example in [tonlib-go](https://github.com/mercuryoio/tonlib-go/tree/master/v2#create-new-private-key)). Note that the private key is generated locally and does not leave the host machine. +2. Form [InitialAccountWallet](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L60) structure corresponding to one of the enabled `wallets`. Currently `wallet.v3`, `wallet.highload.v1`, `wallet.highload.v2` are available. +3. Calculate the address of a new `wallet` smart contract via the [getAccountAddress](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L249) method. We recommend using a default revision `0` and also deploying wallets in the basechain `workchain=0` for lower processing and storage fees. +4. Send some Toncoin to the calculated address. Note that you need to send them in `non-bounce` mode since this address has no code yet and thus cannot process incoming messages. `non-bounce` flag indicates that even if processing fails, money should not be returned with a bounce message. We do not recommend using the `non-bounce` flag for other transactions, especially when carrying large sums, since the bounce mechanism provides some degree of protection against mistakes. +5. Form the desired [action](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L148), for instance `actionNoop` for deploy only. Then use [createQuery](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L255) and [sendQuery](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L260) to initiate interactions with the blockchain. +6. Check the contract in a few seconds with [getAccountState](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L254) method. + +:::tip +Read more in the [Wallet Tutorial](/develop/smart-contracts/tutorials/wallet#-deploying-a-wallet) +::: + +## Incoming message value + +To calculate the incoming value that the message brings to the contract, one needs to parse the transaction. It happens when the message hits the contract. A transaction can be obtained using [getTransactions](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L236). For an incoming wallet transaction, the correct data consists of one incoming message and zero outgoing messages. Otherwise, either an external message is sent to the wallet, in which case the owner spends Toncoin, or the wallet is not deployed and the incoming transaction bounces back. + +Anyway, in general, the amount that a message brings to the contract can be calculated as the value of the incoming message minus the sum of the values of the outgoing messages minus the fee: `value_{in_msg} - SUM(value_{out_msg}) - fee`. Technically, transaction representation contains three different fields with `fee` in name: `fee`, `storage_fee`, and `other_fee`, that is, a total fee, a part of the fee related to storage costs, and a part of the fee related to transaction processing. Only the first one should be used. + +## Checking contract's transactions + +A contract's transactions can be obtained using [getTransactions](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L236). This method allows to get 10 transactions from some `transactionId` and earlier. To process all incoming transactions, the following steps should be followed: + +1. The latest `last_transaction_id` can be obtained using [getAccountState](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L235) +2. List of 10 transactions should be loaded via the `getTransactions` method. +3. Unseen transactions from this list should be processed. +4. Incoming payments are transactions in which the incoming message has a source address; outgoing payments are transactions in which the incoming message has no source address and also presents the outgoing messages. These transactions should be processed accordingly. +5. If all of those 10 transactions are unseen, the next 10 transactions should be loaded and steps 2,3,4,5 should be repeated. + +## Accepting payments + +There are a few approaches to accepting payments that differ in their method of distinguishing users. + +### Invoice-based approach + +To accept payments based on attached comments, the service should + +1. Deploy the `wallet` contract. +2. Generate a unique `invoice` for each user. String representation of uuid32 will be enough. +3. Users should be instructed to send Toncoin to the service's `wallet` contract with an attached `invoice` as a comment. +4. Service should regularly poll the getTransactions method for the `wallet` contract. +5. For new transactions, the incoming message should be extracted, `comment` matched against the database, and the value (see **Incoming message value** paragraph) deposited to the user's account. + +## Invoices + +### Invoices with ton:// link + +If you need an easy integration for a simple user flow, it is suitable to use the ton:// link. +Best suited for one-time payments and invoices. + +```bash +ton://transfer/? + [nft=&] + [fee-amount=&] + [forward-amount=] +``` + +- ✅ Easy integration + +- ✅ No need to connect a wallet + +- ❌ Users need to scan a new QR code for each payment + +- ❌ It's not possible to track whether the user has signed the transaction or not + +- ❌ No information about the user's address + +- ❌ Workarounds are needed on platforms where such links are not clickable (e.g. messages from bots for Telegram desktop clients ) + +\ + +### Invoices with TON Connect + +Best suited for dApps that need to sign multiple payments/transactions within a session or need to maintain a connection to the wallet for some time. + +- ✅ There's a permanent communication channel with the wallet, information about the user's address + +- ✅ Users only need to scan a QR code once + +- ✅ It's possible to find out whether the user confirmed the transaction in the wallet, track the transaction by the returned BOC + +- ✅ Ready-made SDKs and UI kits are available for different platforms + +- ❌ If you only need to send one payment, the user needs to take two actions: connect the wallet and confirm the transaction + +- ❌ Integration is more complex than the ton:// link + +\ + +## Sending payments + +1. Service should deploy a `wallet` and keep it funded to prevent contract destruction due to storage fees. Note that storage fees are generally less than 1 Toncoin per year. +2. Service should get from the user `destination_address` and optional `comment`. Note that for the meantime, we recommend either prohibiting unfinished outgoing payments with the same (`destination_address`, `value`, `comment`) set or proper scheduling of those payments; that way, the next payment is initiated only after the previous one is confirmed. +3. Form [msg.dataText](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L98) with `comment` as text. +4. Form [msg.message](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L108) which contains `destination_address`, empty `public_key`, `amount` and `msg.dataText`. +5. Form [Action](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L149) which contains a set of outgoing messages. +6. Use [createQuery](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L255) and [sendQuery](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/tonlib_api.tl#L260) queries to send outgoing payments. +7. Service should regularly poll the getTransactions method for the `wallet` contract. Matching confirmed transactions with the outgoing payments by (`destination_address`, `value`, `comment`) allows to mark payments as finished; detect and show the user the corresponding transaction hash and lt (logical time). +8. Requests to `v3` of `high-load` wallets have an expiration time equal to 60 seconds by default. After that time unprocessed requests can be safely resent to the network (see steps 3-6). + +## Explorers + +The blockchain explorer is https://tonscan.org. + +To generate a transaction link in the explorer, the service needs to get the lt (logic time), transaction hash, and account address (account address for which lt and txhash were retrieved via the getTransactions method). https://tonscan.org and https://explorer.toncoin.org/ may then show the page for that tx in the following format: + +`https://tonviewer.com/transaction/{txhash as base64url}` + +`https://tonscan.org/tx/{lt as int}:{txhash as base64url}:{account address}` + +`https://explorer.toncoin.org/transaction?account={account address}<={lt as int}&hash={txhash as base64url}` From 791096127d7906c917aa75564dd7488902782393 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:29 +0800 Subject: [PATCH 026/219] New translations jetton_contracts.png (Chinese Simplified) --- .../dapps/asset-processing/jetton_contracts.png | Bin 0 -> 14771 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/jetton_contracts.png diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/jetton_contracts.png b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/jetton_contracts.png new file mode 100644 index 0000000000000000000000000000000000000000..f0ff9c0ea7946051c7e3c4a1982596e6b26b2f1b GIT binary patch literal 14771 zcmeIZby$>9_xB41B7#b{($dXPO2^RM9Yc3_BMc>tv?3`;Hwc1s3QDJhlr$0o^6nX* z_`T2Xyyu+jyyv>kKgU1tzGm)w-#gY`d#%s<4qRDL8ViF20|f;IO9m>Tih_cg0It8I zqk+Ho-6E+_Q0}m}Nou>uJ@B*EO8Tbo$AnAn3`tp726Ml8x^%1|$J zRS8pFPcw6NQB6&kzpXo%fDs^@Rt7W30(y^VghN+X)s$PtQ&PbU^jJcN&tAn!+e$;% z%ZJlchTle7g_cuH-bo9lBBkT#tf}egz{M-&X6dLV$;F|=E^c9hm?aKg4^42Jhntv* ziLSYpIJdVfkFKYhBae!gtgeZqgEyCyE9kSBjf^^k%he7hqiCU~EvYQ7Z30nKlZ2@8 zJDQo;Xqsv`S+aAct1zg!n)~(_$|2(%`R^ zuDO$yySbU1hm5GYypy^PzpT8Zs;8JH%+nNV>&zje3gc6Px^i+TIg0XFX?a^Z+H$Ch zYAK03x;dG6%Q&lPS<84in{ipl%G&UFf;Ev~x3+b)l-95WjY~lw(&8R!N>B}cZyi}R z@NQE%FC|Z19(FfN9ZgO*9VvEIJ_ltNNp%Swb~h_m6(uW9Nl{HVT^n0BJ8o%FJ8n}u zFD@-7FL`xoJ9Y_v1zj##4+Tjxb9*Tz6E#awWlL>ETP`mTB|dE}4rdEXMRR8>E-NK7 zO%n?@ei<$`4;Kwf2{UsQUN2<@2rt-1V!)U166kqdl=$t zaP8>fV8!c%xJ$`IR9ReJk&j=Q-@!-KjvsNOsIs}Gi4T`2*ql0pVo>r zUBt6U9|4SzP3&|41%(1dMnY8G%WyaQUc5&4c&~44WV10N{_P)?QMO$?$wcfS&cmjk zf^<`x=_CyN8H*dO@GZJ4(T2*MIm%ISg<^;%yE#oho#Rb~Ik*2-nD{Qpe8zgVx9dAP z)bv8b$bWP_M`+<@Fa1V-G(9IOE-vn2l?6Ip(5G)DYOt&G!}0NPst+GNj4v)4266k= zzD8Hq)RfiGNWmoIQ+|m_Ol4c%WWiySrc&Q6*H5{B`oI;QhC!X+uxk4#3Tj}G%?*JJ zRGNG?KUnTiH85cL=z9jSdoF3*6GO}i8^E)6a7cAt?dkvWRvh+ox{CJe>`*?h4dG_H^{ARY!Ze1f8z~fM5pD6 z>T!0=taL;3%~_#S(U3ViE@E!0_-|8FMT3=`wHDusRX9yL@1%2EtB#d5crlRF(h|<| z8^%b;?u8r}L1kAOmI7S29@}n-1i6`y=DdhboUQhfk&fy55ZN1@0R4TXryf0xx4YEd zE}sb*R@Ii2Y_9E;(Ky+i)1p(#DQli_I8@aYPTjUY1YKvq-)8V0ow3Yi;j$b_<}mBY z@1o+O7F(|Us+jcTg->5!0`-JQm6uBBxS*&?D-qmlgQOjws^Nvti4`%sanMp!hKDyb zyYq?!F^6f`_F`nW+@b^ek6?Px-4N&Wr%|Wd6N9Qc0gYk?(e#psUzLPQ`s@FrwfDuv zj|FqL8(v|IDbBUe9${1s=S)bW+>pRGS2Jwj5c>Stk9oUB_TF1k!q0bNBs!0ER!HXF z#5X>OdLL9DShtQp;f0&SdrOMSTioK&^qcrSqA#Zv^{sC2p60kqQGLH)c8;}IhR)xW z)DS_mA7#^ef0PuJI-!-a=ANB0)DzR{QcKUUdF;`s0CmEFUut6ygq~(EB#-e8;;oWA zi(wnFK?%^s_?g?FGlFQf*QG3M3!4#^*$GfDAvt|BQal__Q5AgTTk}X9JCCJ4L1`TO zexj1%jg#M={WFo8=5X@4W&}#}JeRiISr;vwrD1+tqb%Dp$J@N^;z!bimXQH#>w(ye z_eOS0*ZGHr#l(+ii+Vy=2cNtARWR=&-dI5d*`RVg9_IC&db)g5+->$fQI0jCz8=!_ z{Uxajc3Q>~5j?*qrGSfuCG-}J%MeFzqi@g6#m3X7-TJw;^VL^rPXo?Hf8+-KDGMTj zuMyf>N2Z-E`YI_3U6|`wy$xSG`Km-1B|E-Sh0<|#eYVt@y|(@>Mq;3UPPV?#L>VQD z4}P!|cy$=T6gt9FKFHcq*1}1Uc(K>AKflD*xO{8Ieac|rQ`M-@WuHUKE*)sPg1K=y zWY4ACzJ5mP{&(|k#d_KUrmXnP1Zdx)9Q2GE6C+zDjCieX%8;pH z(Wg|-`TBCdy?U>C2gB2Z%;^(q=Nl2ro6UEx)}qUD&-~2QQ$YcCd@&zqLlJ zdkKd{N^=j1ZC|Ei+tzrFZNW1gf4`K_{@igv^IrKRhw$0JEE&gRufNJ!SwEprlj zp9bnf=AFd(Pp8e+i{Hg1Y8m-!fa^yWYe`Dx!FLJh>vU}Pxo}iMtNreT3(-sDn9_EB z64{QDtA}ccP?bR_qJK>p8O_ek8HkFJFktS@E}shL6zKm3FWS*$e}1{!urvS`m)MU2 zIb|iN0ViY92Kw`0hW92Sp7<2&g-=dSTF_wAJ&8w$javs^|4NI-9?vhch-5hZZY3|J z-4W);`ziriLd|)1|AD`9WD>OYnV7Y&MH=QqH=91=o9i>rvqA~`FKtRih?uwvofINWzo5r+$^Q&LGE4(KR%54r&VlKX@RgGk4*&1XcMB1OhtuDi zrVM=*Y;0`I!NTOiIxE`$Y^$1G34ODm!Fj)d^G)zXloA&%5m;JU>KfW5^DKK!6AV93 zXSFcXf$`U`U)7x$OpWQ(Tnd%vz#0t7ufXc1brTvr7aiSf)kSPpx3VDx3?aG{NenJo ze(a}@QGG3p0xs6e*nZ``z}BS77kf8NDJ1mg+^peV=h8cTSqGBy8zyS(8~?TJmz0k% zoY@5(%Z~e5OR*p-ulQEbr@oI0>@f2me7e056gr;jex-ZrB?_Wu3N-s1X z3jSno`dK}=R@M8&)tBn|=cO6T)zDa6rRQV2A4J*TLZ}mzU!=WR1bxbup%Hl2xs+z; zV?DJs`r=H#p);~=#5BAkr}s67nK@Yfv?-&&h4pUFd1q;G?s&9+1S2m0!fnt?+^Xc)ML&{e>%%%|)HLsm26XAi`lFiW-_1fQ$ zX4n}f{K4W(M0SfTq67cSYcBc%={iM& zbUEzDi-p9fctoI5KngQ-ppeJDWBX2o#OmuBR* z+f9roba#D_d?^Fb6WUmzOl7c&p6goh*eH11{~g zZX(6t-#B%hyYUUSJQZ^-N%839UoWHiDw3(eQ)(#4F!U_5-PeGhz*DSjsI47($xs*X z^-?^t_TCdO5!SYIQ%`Lysu}B|?D?ge%cUEPb#8&Q9%X0cU@@ep7dh773S3%UPth`T zwy1U~pixp&OPU9JQHm;DU@7P9HtG^uc&0yKFuxZb7dJS&modcc?&AGQn1{JdN0I_)@Q2OHGKe>oIUPS&~#`ZX?1se$S4xTt^ac z75PLV)5fsZ`!jlfIP)U05+}{Ra?nS{V`-h#fsTl@k8CFtMB!;}i zixkVNZ$7On|KyDy$XkQd#WniJbAKCTYyYW@u?XuSQk<`$B|G8TCumo{j z<)CS{^*+#g$gcCfjfA91e7@SpLSt90rlQ90NQ$EQoR6Np)Y3zVac6+3!i@=4=riog z6MmQcOVZMKaKLtb5ThVcRkt&?(1kmx;HPNoNIFTQdt0lT_t9eUfS68NF=v zQP}h1u~tbn;pdhT%%MQ(sR9=*Uado^f*=0Q?TBq@y}ahz>Q+Cd`S0P2XsC~?0jCwg zR5Sc$sXZ_-ZI-G?e#YK`dCh}=BuKeAyHo~ofauti6maI?!rMluuDcjlqnOH_a<{6N zI@<0{6;>zqeVdlAC@XVg$}O&v4H=>!)TB>FKRXPtx4*F&{JM!w*(EoxNR5qEk%{T} z_`q5D04)oT9vL4p1o3ImX$vrE>@ml3KS!T2WC_ zmG(8{BpX?!L!_t(ZhKQ3hl(x6-~EM*GwuL0zzqnLJ3hm+byx6&IJF_hxtZ!JP6B1)F+Tq&41jVugF_q zHmt=~Ff@D)QiH@Z>3EX(ogWUad%^VtkX!GhVsZAA#^xVv#efWAngeI5%@rgY`MnLa zbzyoJAgx-!3c|v|I<3=`8JqGZW`KYH9MR&tnVFH1uaqk!5W)qmLMBSMSuBV|=|_lL zTcrA3PvM%@^*@#lziLK7h{SUn)w7M4m$$`@<}8+wIy?mnHJUKcj5^7psSD&y+Csln z^&m>UV3)G6u#C^nZkGqu{djgue@;(BgZB2lhsl)UFN+=WHf*Z8<;tP7H!b>Lf@r*j z)6rfAQ5vJ)i3T}BWjR+j7OMEC$IvGr!C3P*CLy{7zJ1ILvXhSH5j@^In5g0u-ggjF z5NvIX4!#wsL??pZ*%45if6RUdGk6V~9Y<#IUSTk3lIT_kl`#!Y*^az3Xp;R-qBO`W zyq~`Q|6lljIi=<_2_soUE&TMI3oI=6^I z#<(no;=%a>b#<+YA>~zk`H-B^tT&D&eVc%dHdJ?Al7NkoJvBC7e+5PW|L@OJ=Dr8zPl$qukn$9r4xIEnj=cJP6f}JJq!b(Bc2y@2{ zv!N`COOzq@tF)-!zkkQ2b6F062vyZQFa6f7qHK?hlg%-4sW`F{v_1qtAYij`@}ccp z>1Oius*uNH!{_1QA%7?RDi!rMnpZ_F!|SKFaGxnAV^d2N@5NRUnjR+RJ{7yD>z1R> zIT9bGH)LdFViIIdS&vYwt&2>}623l3%Gfmg=%cf(hNaMcN2m3C{n1h#e5F84Vp{La z!02m5Cc3)1`tq`a(2EVT*`sfGT%wHVh#(;+DuGS{3-!vDCJ(JBH>6gus{XmK3X{piYnl@qz2QjVi}9lgf^}w`z^%5u2mUKCxVYkjjh3VjaRq;2XQc5_rj(C z;pNwNh=o~raSp$-z1Ow&G`07B-;+@pf{jf2L$J6}--B&j+__c6j%agK>Dz!76XL{T zG8#R%Zc2#E4N#eoZNar2RO6Wp!;kq3F6`diSC;$)mB3dN4o9{bkf+(Ei%d%|e0r1y zVhyf{`Ej4$@$j?KineBbukYd2$0LHC z7XqeM`ybCz!O31pWJAlUhUiA(E&8uSIIqIxSeqnfXJ^-5k~%--~HPgdYvP$w{OQ}Nz>8BC(-i{wxQ6o#8N+P+8Jx`==niV zCd@^N|17ora<9cfS@fA(z^@6d^=Y$L?mpDVAY4lZoTO*QqYGQQ4-q$_6@W)Ohh-l= zKln8B^EV(e3)jEmX^&XjuUt%0k9$ZR`@Tm!W!))nDfa`}_tDRq(f3_>9?vFIi%iXV z!@6X`dT-8GA8ja=U&A;*<9|+Hc@=Dy>wlWC;4!DL_wme7$EoA)0?eUHgye6Y73^Hk zK{F?NXPp12k9vV7<8Uuk&mjTiAUZ%WXK1!F3Y|jCJ%r;W1ro~A1`=8~yl`^jV1exNVSaBcgB7%$w;0SCN zzayjE1&R3)xC1@y1qdsR5fLuS(`e*$Sy<(Stk85~9?jZTq=4LSqM)%ks-3OvTnsW} zj!Q@_9`Vrq_8nZZ6-F!tgmKHpI%}Ch>2R9<{N5>OZk~;OaB}nVC7Sn-bjx|7v=94i zm7i`EK;(A)X(_Q$%qwAZN|)F)Qa|tG3^lHtPha zFJ1xaz;2@{b;d|h1~u}s+S^Rl>{fZx_>vsU)@?(JruEO}hV zWuDz>mkykCT7&b}c#b7FjD?Hssee*S4m)|{Ro2_bhlZK@M5 zT8Pq#BT3HWUf1jqx82V;OlVmD-BNke1h;2ZV$AONf#;c)aADIRe9X}RfFe32X#UB9 z8cI26aN+ES;SzoMU%=4W2&waBYJOeE@4GPGhqDLe`tCK=xU&l^0|VGcTa>2(=M)Vu zMK9b0V5YbkTN|5sj4)!#SU0t+_Eph(U@jPVv;nv!N$!>kN^aRN5{G#Weuz# zlB%3Ll2~JD(p+KrmN#!R5|Z^%$DiMDYf@j4hT`ti(Mpwb>%u7>U9UbRz52aV!P;Fv zscYMmOjg1?jC9`Cvl*|-&Sk?=sG0855j_?#k7xMCTknja1i2{(1|cUsD`ITW1eCz=a;1F+LX+km@L00g~#iDmO5pF zj!}}1!=W@deJwIFq8Rxfsc^oZ0idVfT$r3Jg!&37D{dd*KOo z^#TO(^HdltlIm>!HLC`W$uaz6G*~BTa6CM0I1|l5Kn~}w&YZhkJg2DVI4`kakntv) zlwm8?=lO0Z?UtiC4-aZa1P1NpKcDAEP9O9*H^rmZ_>QZoFf!eGl}vC+@+`|qY>}qZ zcSa-}Q+we6(BscGA^P4^6wjKx;P`W^plkf2om^nbllU5x zaZ~>uZW4M`w@n(~kK1s&!(L3>r`08GfG+lfZNW-t;Fh3=UGx{&GH~R~5mirU>J5uh z#oUhZJaLX=()XBwv8IgXKwz$ZD)5n!0W%{!B<_VWYj9vTnscU{k_3*L`DMt+r3$|lfY!}cr=T#3XBOFy(MtqQA|B6J!Ct+b!1X2PU2sdv4DQ<9UTA5kTw5Q+3h3SF zowjC5825O0&X2v=dNMB8qWDU9i)d_0Cwp}t@Lx}kPmD>g1kYKjMuZGVGEU!Auv8=y zjoM>U;xBpAb}mJd(=+M+Hap~s(86dHtdTV@3^V`9jPM(s~- zegP1EY%Xus;UU8h+k*jRexy~4r+L{|Cqz!|jj1Rij?S-DtATakbx4v{S)mP%*;PQI>+Dqw^I*J@*xdXCJ(yX69CRHM8OGbD(LNBK9%h=l`M z1*1DUTGbT!yYc&0_sP!loat7#ODjBJuS*8IQF{st={!}F2O2?LrI0BdrvgVlmigfW zRfe+-BeJjhXQb=Tgdg@%`sx_a^#p(55VWE&Va_Mp}#N1SqQqV0*<8$O+SFHK$9US?#vP zF**1VPYD}i@M~Q3*=fRI%-XX!DB@%Hyof2>N9g+rw7S@2cOJuMW5NpiT|y8Tf{<_%(brY7Dz7Q1WpFj+M=_;y4W z-QhsDCYf$N<8Ay5hVZI+m%*l}hh5Z%_v6$bjz~~7&3?e6s)3IomMl7rUxsE2F_V0# z51o|$6sRei&AIFgT8dvwxN7`oPz74SpdHVp%^=c`52~-j1%{W0i(%M%}Tm|k7wrfvePA{B$R=5U?B8zPNtnR-Q?9Pw#(`S zbS>;P)eGHw*E0oHkABY{eVJT=omN~V#t~M?gvGi>(T-nCYj3dL6QC%0$XJvZU5LpG zo1)AFL8LGfT>1A4Lj_(%NEvsv#zp}rTjl{K+Po^!f1vl54@Dn>?29Iij)wR&cCn;*Z?rqzfm zp(&kcEErvpe4x<2rv0xhJP&9sLZjZL<{d1+F183+c$y~Da87mYx4)H?i+unHF)yI6 z%Iom+qqkNFfB@tgam~#Fp1V7Z&Z}Zo9Vb~2Zhd}<opKqdT{t`mFLsiaVwZlBSL(XUk zDkd2XP?^9OOz8-{C+F^7haJ^R8Dv%@pH4MAJj_bo@4wUyHGzeGTIpV0?t7EbEvNlQ zR(B47orgH0))1wp!PbA{C2PH(*E)}j%gU19zi0Hc-;bvhSLYpby}PhNtCgR4lie+M zB!n&#s=q=@4G%NV-?BmAzaYwZGLXvmn>ac`kZ{looX8=bZ;AiHK^I_qG!Y*^&LH97 zGJu2k7sD+OypiZuKcz8EBH8Jw1riR1AmE@EVFoIaM$$w88tF1Umx-j2=m;9QY+!}< zzx_fDzogHG>%&dzGwR+dgkoh;j#b!`Nsfnk5iow}0IlIqp@dJ=lxaRU-P~h)xD0g7 zzpGgcPdF56x&SS8fXJ)dnO1OTaIv^A4}dLdAA?V~z9Xbou;%NkeG8Z1i#FzPS^~BL z5&jqZeO|o+4Dh9Z%VC2G8y0eOr;pAEv5SjFdAFQ^DUCyTx7=?(UHRyhL)9@*4Icyb zhIEL1#zKLi{5Lt~%mT}$Hhz2U3Le24uxN`BC=Ob(%;{|cPO0; zHuRc_E#v(E!G!{{2MlH8R|LX0u+Jf(-X6EU#+kpK)zna8vVqZOquxH0W6p}sPEvRGKAT{T?aeL|!9_jYM%E43D7hR6Ev zkxfrz4BK{UbsYfer*8Oh8|ju$(jD6_^yUAJx}t!{MDBFa*YV=u^P@WTL)kdQ?9tv$ z?Uu7xix#6g?67s9Ir@?5I6!QfMfpnjPb1TC6;K5XfP(%9oM+f@2>~ag0fY`>Tj;P& zwQg_vF6t=Yz>JJGh@*v3%0nDIw3DNkj9UC9rZaBNoI{I3obVs73qX42*QoWlaQYwF zMW>x82}Eh0hAqR<4({X9@%=vqTBVT>SQ|w=7TmScIbl&`KAUejy$+1MzlRg90m7Ha zRNg4&G4I?vlh?upKX|1ibdgU=--!FpRnM-fJB+mDiPthJ?;x3=M;SLS?;s##J81ZB zVjBoCOx%l8?IKS4$n-&`EfUU-95T6j|nioShVyz>;G6uxZdasB6VzcZZm zA6#n&(8YC7XO9OwK!4J})8OK0kZm1MjNys~i;FsB+Velbt;AmV~-+TeU-FgG_M4U)q!i%z?vMU{^+Y3OUuyUZm5O6LU#e$@}a8r#`| zXD+Fg4zV8)#<2eCHo(96b6Ii8;;Dtu5FIW0?l%RWzI!F)61opC!#>)1w#Tdt5E-#b zQ}x9(jL?8iwl|6fqf&icU;xB3AE^H3ub+l?!k^`Aq9uY%RiS>tLyz}hzy0P<^_UcC zQ=s5ly!hIP;C?b7s7e5kX0hZSQZQ;h?VE8OeX$U(oM5oo@P7cW_)Omo3EXJ`PO;<{ zJ|g5$8vgS9Zuh;rrtAO7t~v`O;R$cWBL;w!YK4yPso#NU4$1k2>)+Yn0ka{45HmT4 zvT%8mg!0z}^o8gI(%}R%Q$(GfdNkyUngy*p#As+4Obj2rJNg0Y3iWz#;|m8Qs8Ui= z6w&q#%wl=g00(iqX?G2kD1J4%y}4DC>%o)?1O;bYXnvZ|{Lk7l%m9V4E_5+Q&fV4t zZ#`d$jx(o71bVcQVH;~_mn-08v&eo@5h|I9G_NJfng1nJd6s}skxL=7)ygVCP9i*L zQS|@~0imuHr8HHSs@7pdyLak2HJWzE!B3#rVd_%eM1r}*&Z!kzQ5=&3r@f|)k;#tD zATZ-?q+VPJ=xOvE{~%Eg7Dj4ntC|!;=+^m}D>vlnv);5~Q6&Wh-5)53p8r1C;v!|l zM5*|Ano(9eE_*cZI@(`Sl(YROvj^iP{{6tuPaqpN0DgTZ&*%LIi#h`4W0Zzt#zyyF zX_U(_&s3kT?T2+$m;7q(Or-5v+X_{$aLH-LmC-;|L^r#{>d)uAR%2lZ`(uF+8hV)! z-YfWHO#Rf?_4)(CwXEAjobH1%3{Z_R5?NjT+Tpde=%u5*q{J*QLlJ2B4*~@Pijh46 zxWv9icqmz0CA>i&8upC@QXME7^^hvmjS2_8R+TF*i=i4+`xiT%pN+7P5);f4Bm8=+ z*zV5WaklGxUkQ+FwV8LzWSK{`<7;YnEZmBE)DfaVU#h5?1%z~7o)d>ZkaR%~gFZS9 zCm#K5x7yPgNSXS`J?Fol3iTZa0-a)<_H>PLVUG}sQD^1bAt?c^57op9Q4z@x$r}Jx za;SWuVE@Tue&a^EpU zjnUQ4{f!$Muxjo_hk$TW#rT(8F*0yK$h{EenI3T^_6gd>7KkTFicG=jr82)bP$T1% z1<247W{iJvBK*&T$OW1I@h?jBg~e#^u>H8i?*lm(qWWfexMI|rJlx|`Z_}LhbW1Tk zVNZQyV(*}-51B@C&U_LaD7fX;MuSt~mmPg`e2wgbPV1U9RY4>ol@88uC134*E)+hP0ZMD5mDy4PEjOoc?ZyfpaS)W3KV)m#32HiJqeA zTpp$rAIMWXcl!8i(v?=7Z((>@31+O=@HIgORp0NhyL=Bj;$pKh%V8sk5;nKWQ-&^~ zm>|yHNBN}&U;u7DTK3{S*`lIt)_a3Q+$$5_b%Hgi&yi~YFi0527c%++5+GWDE3BkM zf8eWyYM?Q4tHt^>t*5%;lSfLg;F6V)W&I}$QN)oEFl?p6Rtyp+`^K54LJ~esIkX2f zum%^8XMvXSJL6TgTc|{i&o;x1aVXY&yK{AxT2EH;t4F`D;&6NMB=TXUBXT8<1Cuq| zZ$zcC0)YNFH&!BwBMRP#n(C&Bj{k=8{y;%xBCKG@JWKp!FE-(FqO<&y%eb26ftPU=nkd&?~lujEtT|2Hb>Rs ztsQ{+S)&-dcC&q^_NP3R`hz&e3X9T_!J))q#Xg$Fm0<0dkuq9j?Qq9N8NJ{Zcj*H< z#^eSQiq+X$tV*l<$x3feu$XY^1i3YP@@t{eFa)cbj7q32lW^%~S@cXQCd#Qt05ppG zQA92sxLAo4{@pRJ^5Vvpno7e4TK{ijqV+%N%gVX2RQY&>u=S4X-F&0;p~~LgH2)cB znAqDU^|ndTKmb&e?$~=<*&cFUPWj37BJ8DyUu*LJ3==WeeN(N`_!^sp#T08d;axH` zNcTJxH6`%f9M(+OE3FL{sffw=62!JQG26s{GsC^1NTjNxpEhKOj{C@=i|ju{VsupU zra&LqNY9iuFm*20@)yVsX8BV6?T(gq8G9ecaRC9-W3;UEDV;0-(hv2CKovf;n`W*D zuN;rm`nmv1Gq8~J1t#%8MP>2Hi$2>&WPD%zvnAb^8(WJ_t2~NW)pHR3B*Oo#R*;3N zxK9+*WBNlSpA;{Oy6&~G#GCooePLk}Ot=(6UzR^Vq~lE=fv@RaG=DkYylO@GAD{^! zWHU^P^ti_$?TvIV{J*;_GZA!V)bi(m3NMfVB)W_u5i>)CFyAqP`LXedsFhbHKnKWU z{ysz8NI{Q_Bs%o$gCaWA37pQHmr}aJXm^3KD)}8!;)STo69XmdejVbZ*nm4t|#+|Lw_1-8w_MwP`YNs4BO{EjbYV${C$bdFOzX7{m!3&R9@m=YD*b z|2HTwFNCZ7<@m`%+#IqX;ipI4sv$z`<-dbKa(pEDN{Ad?R2f%5L`G_yU%Tl4I&WEv z3?kY)WN`3%7Z1cwKJZ`Gq_WB$y(hC!;fA6jo%a&7KUb`+`}YVZW$>w z%0ZXU;u~A25o@yjAeHV4RAt=PiDri@X~KR}h}8EV0+43TNdTQtx*1Dc4pi4#rF^U`b_ibXL$=0YhmXt6Gt8hdAF%iV>b<3ydOUByF?r2C}|6q+5kVH{h zKYm3hO+~L%p1EVy#QRxy8r>;@@Jz*OFWgP4YlZeF5!}}CF!6~|YG>jI0`3$l<+6h6 z>7G;_aP616rFe_V0r%wO%Q1f#?t@AJNF81sk^V8t>XsXO!;)?cG(e)+QG~GQG#wfo z9hvueK>VX!j!?1!2NYd2B)VE?K28m`LvsusyjajW)q-*mxe*6`YG4Af}%FLPWkA{f+6&^L&`bJ<9=QYD~m^KO6ny6B)Kzx^YCJH54 e!(qF*m-xx1jdN2*7W^0!MMhFlqFl^4 Date: Tue, 30 Apr 2024 17:58:29 +0800 Subject: [PATCH 027/219] New translations jettons.md (Chinese Simplified) --- .../develop/dapps/asset-processing/jettons.md | 907 ++++++++++++++++++ 1 file changed, 907 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/jettons.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/jettons.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/jettons.md new file mode 100644 index 0000000000..a91cbf5573 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/jettons.md @@ -0,0 +1,907 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import Button from '@site/src/components/button'; + +# Jetton Processing + +## Best Practices on Jettons Processing + +Jettons are tokens on TON Blockchain - one can consider them similarly to ERC-20 tokens on Ethereum. + +:::info Transaction Confirmation +TON transactions are irreversible after just one confirmation. For the best UX/UI avoid additional waiting. +::: + +#### Withdrawal + +[Highload Wallet v3](/participate/wallets/contracts#highload-wallet-v3) - this is TON Blockchain latest solution which is the gold standard for jetton withdrawals. It allows you to take advantage of batched withdrawals. + +[Batched withdrawals](https://github.com/toncenter/examples/blob/main/withdrawals-jettons-highload-batch.js) - Meaning that multiple withdrawals are sent in batches, allowing for quick and cheap withdrawals. + +#### Deposits + +:::info +It is suggested to set several MEMO deposit wallets for better performance. +::: + +[Memo Deposits](https://github.com/toncenter/examples/blob/main/deposits-jettons.js) - This allows you to keep one deposit wallet, and users add a memo in order to be identified by your system. This means that you don’t need to scan the entire blockchain, but is slightly less easy for users. + +[Memo-less deposits](https://github.com/gobicycle/bicycle) - This solution also exists, but is more difficult to integrate. However, we can assist with this, if you would prefer to take this route. Please notify us before deciding to implement this approach. + +### Additional Info + +:::caution Transaction Notification +if you will be allowing your users set a custom memo when withdrawing jettons - make sure to set forwardAmount to 0.000000001 TON (1 nanoton) whenever a memo (text comment) is attached, otherwise the transfer will not be standard compliant and will not be able to be processed by other CEXes and other such services. +::: + +- Please find the JS lib example - [tonweb](https://github.com/toncenter/tonweb) - which is the official JS library from the TON Foundation. + +- If you want to use Java, you can look into [ton4j](https://github.com/neodix42/ton4j/tree/main). + +- For Go, one should consider [tonutils-go](https://github.com/xssnick/tonutils-go). At the moment, we recommend the JS lib. + +## Content List + +:::tip +In following docs offers details about Jettons architecture generally, as well as core concepts of TON which may be different from EVM-like and other blockchains. This is crucial reading in order for one to grasp a good understanding of TON, and will greatly help you. +::: + +This document describes the following in order: + +1. Overview +2. Architecture +3. Jetton Master Contract (Token Minter) +4. Jetton Wallet Contract (User Wallet) +5. Message Layouts +6. Jetton Processing (off-chain) +7. Jetton Processing (on-chain) +8. Wallet processing +9. Best Practices + +## Overview + +:::info +TON transactions are irreversible after just one confirmation. +For clear understanding, the reader should be familiar with the basic principles of asset processing described in [this section of our documentation](/develop/dapps/asset-processing/). In particular, it is important to be familiar with [contracts](/learn/overviews/addresses#everything-is-a-smart-contract), [wallets](/develop/smart-contracts/tutorials/wallet), [messages](/develop/smart-contracts/guidelines/message-delivery-guarantees) and deployment process. +::: + +:::Info +For the best user experience, it is suggested to avoid waiting on additional blocks once transactions are finalized on the TON Blockchain. Read more in the [Catchain.pdf](https://docs.ton.org/catchain.pdf#page=3). +::: + +Quick jump to the core description of jetton processing: + +\ +\ + +



+ +TON Blockchain and its underlying ecosystem classifies fungible tokens (FTs) as jettons. Because sharding is applied on TON Blockchain, our implementation of fungible tokens is unique when compared to similar blockchain models. + +In this analysis, we take a deeper dive into the formal standards detailing jetton [behavior](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md) and [metadata](https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md). +A less formal sharding-focused overview of jetton architecture can be found in our +[anatomy of jettons blog post](https://blog.ton.org/how-to-shard-your-ton-smart-contract-and-why-studying-the-anatomy-of-tons-jettons). + +We have also provided specific details discussing our third-party open-source TON Payment Processor ([bicycle](https://github.com/gobicycle/bicycle)) which allows users to deposit and withdraw both Toncoin and jettons using a separate deposit address without using a text memo. + +## Jetton Architecture + +Standardized tokens on TON are implemented using a set of smart contracts, including: + +- [Jetton master](https://github.com/ton-blockchain/token-contract/blob/main/ft/jetton-minter.fc) smart contract +- [Jetton wallet](https://github.com/ton-blockchain/token-contract/blob/main/ft/jetton-wallet.fc) smart contracts + +

+
+ contracts scheme +
+

+ +## Jetton master smart contract + +The jetton master smart contract stores general information about the jetton ( + +including the total supply, a metadata link, or the metadata itself). + +It is possible for any user to create a counterfeit clone of a valuable jetton (using an arbitrary name, ticker, image, etc.) that is nearly identical to the original. Thankfully, counterfeit jettons are distinguishable by their addresses and can be identified quite easily. + +To eliminate the possibility of fraud for TON users, please look up the original jetton address (Jetton master contract) for specific jetton types or follow the project’s official social media channel or website to find the correct information. Check assets to eliminate the possibility of fraud with [Tonkeeper ton-assets list](https://github.com/tonkeeper/ton-assets). + +### Retrieving Jetton data + +To retrieve more specific Jetton data, the `get_jetton_data()` get method is used. + +This method returns the following data: + +| Name | Type | Description | +| -------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `total_supply` | `int` | the total number of issued jettons measured in indivisible units. | +| `mintable` | `int` | details whether new jettons can be minted or not. This value is either -1 (can be minted) or 0 (cannot be minted). | +| `admin_address` | `slice` | | +| `jetton_content` | `cell` | data in accordance with [TEP-64](https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md). | +| `jetton_wallet_code` | `cell` | | + +It is also possible to use the method `/jetton/masters` from the [Toncenter API](https://toncenter.com/api/v3/#/default/get_jetton_masters_api_v3_jetton_masters_get) to retrieve the already decoded Jetton data and metadata. We have also developed methods for (js) [tonweb](https://github.com/toncenter/tonweb/blob/master/src/contract/token/ft/JettonMinter.js#L85) and (js) [ton-core/ton](https://github.com/ton-core/ton/blob/master/src/jetton/JettonMaster.ts#L28), (go) [tongo](https://github.com/tonkeeper/tongo/blob/master/liteapi/jetton.go#L48) and (go) [tonutils-go](https://github.com/xssnick/tonutils-go/blob/33fd62d754d3a01329ed5c904db542ab4a11017b/ton/jetton/jetton.go#L79), (python) [pytonlib](https://github.com/toncenter/pytonlib/blob/d96276ec8a46546638cb939dea23612876a62881/pytonlib/client.py#L742) and many other SDKs. + +Example of using [Tonweb](https://github.com/toncenter/tonweb) to run a get method and get url for off-chain metadata: + +```js +import TonWeb from "tonweb"; +const tonweb = new TonWeb(); +const jettonMinter = new TonWeb.token.jetton.JettonMinter(tonweb.provider, {address: ""}); +const data = await jettonMinter.getJettonData(); +console.log('Total supply:', data.totalSupply.toString()); +console.log('URI to off-chain metadata:', data.jettonContentUri); +``` + +#### Jetton metadata + +More info on parsing metadata is provided [here](/develop/dapps/asset-processing/metadata). + +## Jetton Wallet smart contracts + +Jetton wallet contracts are used to send, receive, and burn jettons. Each _jetton wallet contract_ stores wallet balance information for specific users. +In specific instances, jetton wallets are used for individual jetton holders for each jetton type. + +Jetton wallets should not be confused with wallet’s meant for blockchain interaction and storing +only the Toncoin asset (e.g., v3R2 wallets, highload wallets, and others), +which is responsible for supporting and managing only a specific jetton type. + +Jetton wallets make use of smart contracts and are managed using internal messages between +the owner's wallet and the jetton wallet. For instance, say if Alice manages a wallet with jettons inside, +the scheme is as follows: Alice owns a wallet designed specifically for jetton use (such as wallet version v3r2). +When Alice initiates the sending of jettons in a wallet she manages, she sends external messages to her wallet, +and as a result, _her wallet_ sends an internal message to _her jetton wallet_ and +then the jetton wallet actually executes the token transfer. + +### Retrieving Jetton wallet addresses for a given user + +To retrieve a jetton wallet address using an owner address (a TON Wallet address), +the Jetton master contract provides the get method `get_wallet_address(slice owner_address)`. + +#### Retrieve using API + +The application serializes the owner’s address to a cell using +the `/runGetMethod` method from the [Toncenter API](https://toncenter.com/api/v3/#/default/run_get_method_api_v3_runGetMethod_post). + +#### Retrieve using SDK + +This process can also be initiated using ready to use methods present in our various SDKs, for instance,\ +using the Tonweb SDK, this process can be initiated by entering the following strings: + +```js +import TonWeb from "tonweb"; +const tonweb = new TonWeb(); +const jettonMinter = new TonWeb.token.jetton.JettonMinter(tonweb.provider, {address: ""}); +const address = await jettonMinter.getJettonWalletAddress(new TonWeb.utils.Address("")); +// It is important to always check that wallet indeed is attributed to desired Jetton Master: +const jettonWallet = new TonWeb.token.jetton.JettonWallet(tonweb.provider, { + address: jettonWalletAddress +}); +const jettonData = await jettonWallet.getData(); +if (jettonData.jettonMinterAddress.toString(false) !== new TonWeb.utils.Address(info.address).toString(false)) { + throw new Error('jetton minter address from jetton wallet doesnt match config'); +} + +console.log('Jetton wallet address:', address.toString(true, true, true)); +``` + +:::tip +For more examples read the [TON Cookbook](/develop/dapps/cookbook#how-to-calculate-users-jetton-wallet-address). +::: + +### Retrieving data for a specific Jetton wallet + +To retrieve the wallet’s account balance, owner identification information, and other info related to a specific jetton wallet contract, the `get_wallet_data()` get method is used within the jetton wallet contract. + +This method returns the following data: + +| Name | Type | +| ------------------------------------------------------------ | ----- | +| balance | int | +| owner | slice | +| jetton | slice | +| jetton_wallet_code | cell | + +It is also possible to use the `/jetton/wallets` get method using the [Toncenter API](https://toncenter.com/api/v3/#/default/get_jetton_wallets_api_v3_jetton_wallets_get) to retrieve previously decoded jetton wallet data (or methods within an SDK). For instance, using Tonweb: + +```js +import TonWeb from "tonweb"; +const tonweb = new TonWeb(); +const walletAddress = "EQBYc3DSi36qur7-DLDYd-AmRRb4-zk6VkzX0etv5Pa-Bq4Y"; +const jettonWallet = new TonWeb.token.jetton.JettonWallet(tonweb.provider,{address: walletAddress}); +const data = await jettonWallet.getData(); +console.log('Jetton balance:', data.balance.toString()); +console.log('Jetton owner address:', data.ownerAddress.toString(true, true, true)); +// It is important to always check that Jetton Master indeed recognize wallet +const jettonMinter = new TonWeb.token.jetton.JettonMinter(tonweb.provider, {address: data.jettonMinterAddress.toString(false)}); +const expectedJettonWalletAddress = await jettonMinter.getJettonWalletAddress(data.ownerAddress.toString(false)); +if (expectedJettonWalletAddress.toString(false) !== new TonWeb.utils.Address(walletAddress).toString(false)) { + throw new Error('jetton minter does not recognize the wallet'); +} + +console.log('Jetton master address:', data.jettonMinterAddress.toString(true, true, true)); +``` + +### Jetton Wallet Deployment + +When transferring jettons between wallets, transactions (messages) require a certain amount of TON +as payment for network gas fees and the execution of actions according to the Jetton wallet contract's code. +This means that the recipient does not need to deploy a jetton wallet prior to receiving jettons. +The recipient's jetton wallet will be deployed automatically as long as the sender holds enough TON +in the wallet to pay the required gas fees. + +## Message Layouts + +:::tip Messages +Read more about Messages [here](/develop/smart-contracts/guidelines/message-delivery-guarantees). +::: + +Communication between Jetton wallets and TON wallets occurs through the following communication sequence: + +![](/img/docs/asset-processing/jetton_transfer.svg) + +`Sender -> sender' jetton wallet` means the _transfer_ message body contains the following data: + +| Name | Type | +| ---------------------- | ------- | +| `query_id ` | uint64 | +| `amount ` | coins | +| `destination ` | address | +| `response_destination` | address | +| `custom_payload ` | cell | +| `forward_ton_amount` | coins | +| `forward_payload` | cell | + +`payee' jetton wallet -> payee` means the message notification body contains the following data: + +| Name | Type | +| -------------------------------------- | ------- | +| query_id \` | uint64 | +| amount \` | coins | +| sender \` | address | +| forward_payload\` | cell | + +`payee' jetton wallet -> Sender` means the excess message body contains the following data: + +| Name | Type | +| ---------- | ------ | +| `query_id` | uint64 | + +A detailed description of the jetton wallet contract fields can be found in the [TEP-74](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md) Jetton standard interface description. + +Messages using the `Transfer notification` and `Excesses` parameters are optional and depend on the amount of TON attached +to the `Transfer` message and the value of the `forward_ton_amount` field. + +The `query_id` identifier allows applications to link three messaging types `Transfer`, `Transfer notification` and `Excesses` to each other. +For this process to be carried out correctly it is recommended to always use a unique query id. + +### How to send Jetton transfers with comments and notifications + +In order to make a transfer with a notification (which is then used in-wallet for notification purposes), +a sufficient amount of TON must be attached to the message being sent by setting a non-zero `forward_ton_amount` +value and, if necessary, attaching a text comment to the `forward_payload`. +A text comment is encoded similarly to a text comment when sending Toncoin. + +[Fees for sending Jettons](https://docs.ton.org/develop/smart-contracts/fees#fees-for-sending-jettons) + +However, the commission depends on several factors including the Jetton code details and the need to deploy a new Jetton wallet for recipients. +Therefore, it is recommended to attach Toncoin with a margin and then set the address as the `response_destination` +to retrieve `Excesses` messages. For example, 0.05 TON can be attached to the message while setting the `forward_ton_amount` +value to 0.01 TON (this amount of TON will be attached to the `Transfer notification` message). + +[Jetton transfers with comment examples using the Tonweb SDK](https://github.com/toncenter/tonweb/blob/b550969d960235314974008d2c04d3d4e5d1f546/src/test-jetton.js#L128): + +```js +// first 4 bytes are tag of text comment +const comment = new Uint8Array([... new Uint8Array(4), ... new TextEncoder().encode('text comment')]); + +await wallet.methods.transfer({ + secretKey: keyPair.secretKey, + toAddress: JETTON_WALLET_ADDRESS, // address of Jetton wallet of Jetton sender + amount: TonWeb.utils.toNano('0.05'), // total amount of TONs attached to the transfer message + seqno: seqno, + payload: await jettonWallet.createTransferBody({ + jettonAmount: TonWeb.utils.toNano('500'), // Jetton amount (in basic indivisible units) + toAddress: new TonWeb.utils.Address(WALLET2_ADDRESS), // recepient user's wallet address (not Jetton wallet) + forwardAmount: TonWeb.utils.toNano('0.01'), // some amount of TONs to invoke Transfer notification message + forwardPayload: comment, // text comment for Transfer notification message + responseAddress: walletAddress // return the TONs after deducting commissions back to the sender's wallet address + }), + sendMode: 3, +}).send() +``` + +:::tip +For more examples read the [TON Cookbook](/develop/dapps/cookbook#how-to-construct-a-message-for-a-jetton-transfer-with-a-comment). +::: + +## Jetton off-chain processing + +:::info Transaction Confirmation +TON transactions are irreversible after just one confirmation. For the best user experience, it is suggested to avoid waiting on additional blocks once transactions are finalized on the TON Blockchain. Read more in the [Catchain.pdf](https://docs.ton.org/catchain.pdf#page=3). +::: + +Several scenarios that allow a user to accept Jettons are possible. Jettons can be accepted within a centralized hot wallet; as well, they can also be accepted using a wallet with a separate address for each individual user. + +To process Jettons, unlike individualized TON processing, a hot wallet is required (a v3R2, highload wallet) in addition +to a Jetton wallet or more than one Jetton wallet. Jetton hot wallet deployment is described in the [wallet deployment](/develop/dapps/asset-processing/#wallet-deployment) of our documentation. +That said, deployment of Jetton wallets according to the [Jetton wallet deployment](#jetton-wallet-deployment) criteria is not required. +However, when Jettons are received, Jetton wallets are deployed automatically, meaning that when Jettons are withdrawn, +it is assumed that they are already in the user’s possession. + +For security reasons it is preferable to be in possession of separate hot wallets for separate Jettons (many wallets for each asset type). + +When processing funds, it is also recommended to provide a cold wallet for storing excess funds which do not participate in the automatic deposit and withdrawal processes. + +### Adding new Jettons for asset processing and initial verification + +1. To find the correct smart contract token master address please see the following source: [How to find the right Jetton master contract](#jetton-master-smart-contract) +2. Additionally, to retrieve the metadata for a specific Jetton please see the following source: [How to receive Jetton metadata](#retrieving-jetton-data). + In order to correctly display new Jettons to users, the correct `decimals` and `symbol` are needed. + +For the safety of all of our users, it is critical to avoid Jettons that could be counterfeited (fake). For example, +Jettons with the `symbol`==`TON` or those that contain system notification messages, such as: +`ERROR`, `SYSTEM`, and others. Be sure to check that jettons are displayed in your interface in such a way that they cannot +be mixed with TON transfers, system notifications, etc.. At times, even the `symbol`,`name` and `image` +will be created to look nearly identical to the original with the hopes of misleading users. + +### Identification of an unknown Jetton when receiving a transfer notification message + +1. If a transfer notification message is received within your wallet regarding an unknown Jetton, then your wallet + has been created to hold the specific Jetton. Next, it is important to perform several verification processes. +2. The sender address of the internal message containing the `Transfer notification` body is the address of the new Jetton wallet. + Not to be confused with the `sender` field in the `Transfer notification` body, the address of the Jetton wallet + is the address from the source of the message. +3. Retrieving the Jetton master address for the new Jetton wallet: [How to retrieve data for the Jetton wallet](#retrieving-jetton-data). + To carry out this process, the `jetton` parameter is required and is the address that makes up the Jetton master contract. +4. Retrieving the Jetton wallet address for your wallet address (as an owner) using the Jetton master contract: [How to retrieve Jetton wallet address for a given user](#retrieving-jetton-wallet-addresses-for-a-given-user) +5. Compare the address returned by the master contract and the actual address of the wallet token. + If they match, it’s ideal. If not, then you likely received a scam token that is counterfeit. +6. Retrieving Jetton metadata: [How to receive Jetton metadata](#retrieving-jetton-data). +7. Check the `symbol` and `name` fields for signs of a scam. Warn the user if necessary. [Adding a new Jettons for processing and initial checks](#adding-new-jettons-for-asset-processing-and-initial-verification). + +### Accepting Jettons from users through a centralized wallet + +:::info +To prevent a bottleneck in incoming transactions to a single wallet, it is suggested to accept deposits across multiple wallets and to expand the number of these wallets as needed. +::: + +:::caution Transaction Notification +if you will be allowing your users set a custom memo when withdrawing jettons - make sure to set forwardAmount to 0.000000001 TON (1 nanoton) whenever a memo (text comment) is attached, otherwise the transfer will not be standard compliant and will not be able to be processed by other CEXes and other such services. +::: + +In this scenario, the payment service creates a unique memo identifier for each sender disclosing +the address of the centralized wallet and the amounts being sent. The sender sends the tokens +to the specified centralized address with the obligatory memo in the comment. + +**Pros of this method:** this method is very simple because there are no additional fees when accepting tokens and they are retrieved directly in the hot wallet. + +**Cons of this method:** this method requires that all users attach a comment to the transfer which can lead to a greater number of deposit mistakes (forgotten memos, incorrect memos, etc.), meaning a higher workload for support staff. + +Tonweb examples: + +1. [Accepting Jetton deposits to an individual HOT wallet with comments (memo)](https://github.com/toncenter/examples/blob/main/deposits-jettons-single-wallet.js) +2. [Jettons withdrawals example](https://github.com/toncenter/examples/blob/main/jettons-withdrawals.js) + +#### Preparations + +1. Prepare a list of accepted Jettons: [Adding new Jettons for processing and initial verification](#adding-new-jettons-for-asset-processing-and-initial-verification). +2. Hot wallet deployment (using v3R2 if no Jetton withdrawals are expected; highload v2 - if Jetton withdrawals are expected) [Wallet deployment](/develop/dapps/asset-processing/#wallet-deployment). +3. Perform a test Jetton transfer using the hot wallet address to initialize the wallet. + +#### Processing incoming Jettons + +1. Load the list of accepted Jettons +2. Retrieve a Jetton wallet address for your deployed hot wallet: [How to retrieve a Jetton wallet address for a given user](#retrieving-jetton-wallet-addresses-for-a-given-user) +3. Retrieve a Jetton master address for each Jetton wallet: [How to retrieve data for a Jetton wallet](#retrieving-data-for-a-specific-jetton-wallet). + To carry out this process, the `jetton` parameter is required (which is actually the address + of the Jetton master contract). +4. Compare the addresses of the Jetton master contracts from step 1. and step 3 (directly above). + If the addresses do not match, a Jetton address verification error must be reported. +5. Retrieve a list of the most recent unprocessed transactions using a hot wallet account and + iterate it (by sorting through each transaction one by one). See: [Checking contract's transactions](https://docs.ton.org/develop/dapps/asset-processing/#checking-contracts-transactions), + or use the [Tonweb example](https://github.com/toncenter/examples/blob/9f20f7104411771793dfbbdf07f0ca4860f12de2/deposits-single-wallet.js#L43) + or use Toncenter API `/getTransactions` method. +6. Check the input message (in_msg) for transactions and retrieve the source address from the input message. [Tonweb example](https://github.com/toncenter/examples/blob/9f20f7104411771793dfbbdf07f0ca4860f12de2/deposits-jettons-single-wallet.js#L84) +7. If the source address matches the address within a Jetton wallet, then it is necessary to continue processing the transaction. + If not, then skip processing the transaction and check the next transaction. +8. Ensure that the message body is not empty and that the first 32 bits of the message match the `transfer notification` op code `0x7362d09c`. + [Tonweb example](https://github.com/toncenter/examples/blob/9f20f7104411771793dfbbdf07f0ca4860f12de2/deposits-jettons-single-wallet.js#L91) + If the message body is empty or the op code is invalid - skip the transaction. +9. Read the message body’s other data, including the `query_id`, `amount`, `sender`, `forward_payload`. + [Jetton contracts message layouts](#jetton-contract-message-layouts), [Tonweb example](https://github.com/toncenter/examples/blob/9f20f7104411771793dfbbdf07f0ca4860f12de2/deposits-jettons-single-wallet.js#L105) +10. Try to retrieve text comments from the `forward_payload` data. The first 32 bits must match + the text comment op code `0x00000000` and the remaining - UTF-8 encoded text. + [Tonweb example](https://github.com/toncenter/examples/blob/9f20f7104411771793dfbbdf07f0ca4860f12de2/deposits-jettons-single-wallet.js#L110) +11. If the `forward_payload` data is empty or the op code is invalid - skip the transaction. +12. Compare the received comment with the saved memos. If there is a match (user identification is always possible) - deposit the transfer. +13. Restart from step 5 and repeat the process until you have walked through the entire list of transactions. + +### Accepting Jettons from user deposit addresses + +To accept Jettons from user deposit addresses, it is necessary that the payment service creates its +own individual address (deposit) for each participant sending funds. The service provision in this case involves +the execution of several parallel processes including creating new deposits, scanning blocks for transactions, +withdrawing funds from deposits to a hot wallet, and so on. + +Because a hot wallet can make use of one Jetton wallet for each Jetton type, it is necessary to create multiple +wallets to initiate deposits. In order to create a large number of wallets, but at the same time manage them with +one seed phrase (or private key), it is necessary to specify a different `subwallet_id` when creating a wallet. +On TON, the functionality required to create a subwallet is supported by version v3 wallets and higher. + +#### Creating a subwallet in Tonweb + +```Tonweb +const WalletClass = tonweb.wallet.all['v3R2']; +const wallet = new WalletClass(tonweb.provider, { + publicKey: keyPair.publicKey, + wc: 0, + walletId: , +}); +``` + +#### Preparation + +1. Prepare a list of accepted Jettons: [Adding new Jettons for processing and initial checks](#adding-new-jettons-for-asset-processing-and-initial-verification) +2. Hot wallet [Wallet Deployment](/develop/dapps/asset-processing/#wallet-deployment) + +#### Creating deposits + +1. Accept a request to create a new deposit for the user. +2. Generate a new subwallet (v3R2) address based on the hot wallet seed. [Creating a subwallet in Tonweb](#creating-a-subwallet-in-tonweb) +3. The receiving address can be given to the user as the address used for Jetton deposits (this is the address of + the owner of the deposit Jetton wallet). Wallet initialization is not required, this can be + accomplished when withdrawing Jettons from the deposit. +4. For this address, it is necessary to calculate the address of the Jetton wallet through the Jetton master contract. + [How to retrieve a Jetton wallet address for a given user](#retrieving-jetton-wallet-addresses-for-a-given-user). +5. Add the Jetton wallet address to the address pool for transaction monitoring and save the subwallet address. + +#### Processing transactions + +:::info Transaction Confirmation +TON transactions are irreversible after just one confirmation. For the best user experience, it is suggested to avoid waiting on additional blocks once transactions are finalized on the TON Blockchain. Read more in the [Catchain.pdf](https://docs.ton.org/catchain.pdf#page=3). +::: + +It is not always possible to determine the exact amount of Jettons received from the message, because Jetton +wallets may not send `transfer notification`, `excesses`, and `internal transfer` messages are not standardized. This means +that there is no guarantee that the `internal transfer` message can be decoded. + +Therefore, to determine the amount received in the wallet, balances need to be requested using the get method. +To retrieve key data when requesting balances, blocks are used according to the account’s state for a particular block on-chain. +[Preparation for block acceptance using Tonweb](https://github.com/toncenter/tonweb/blob/master/src/test-block-subscribe.js). + +This process is conducted as follows: + +1. Preparation for block acceptance (by readying the system to accept new blocks). +2. Retrieve a new block and save the previous block ID. +3. Receive transactions from blocks. +4. Filter transactions used only with addresses from the deposit Jetton wallet pool. +5. Decode messages using the `transfer notification` body to receive more detailed data including the + `sender` address, Jetton `amount` and comment. (See: [Processing incoming Jettons](#processing-incoming-jettons)) +6. If there is at least one transaction with non-decodable out messages (the message body does not contain op codes for + `transfer notification` and op codes for `excesses`) or without out messages present within the + account, then the Jetton balance must be requested using the get method for the current block, while the previous + block is used to calculate the difference in balances. Now the total balance deposit changes are revealed due + to the transactions being conducted within the block. +7. As an identifier for an unidentified transfer of Jettons (without a `transfer notification`), transaction data + can be used if there is one such transaction or block data present (if several are present within a block). +8. Now it’s necessary to check to ensure the deposit balance is correct. If the deposit balance is sufficient enough to initiate a transfer between a hot wallet and the existing Jetton wallet, Jettons need to be withdrawn to ensure the wallet balance has decreased. +9. Restart from step 2 and repeat the entire process. + +#### Withdrawals made from deposits + +Transfers should not be made from a deposit to a hot wallet with each deposit replenishment, +because of the fact that a commission in TON is taken for the transfer operation (paid in network gas fees). +It is important to determine a certain minimum amount of Jettons which are required to make a +transfer worthwhile (and thus deposit). + +By default, wallet owners of Jetton deposit wallets are not initialized. This is because there is no predetermined +requirement to pay storage fees. Jetton deposit wallets can be deployed when sending messages with a +`transfer` body which can then be destroyed immediately. To do this, the engineer must use a special +mechanism for sending messages: 128 + 32. + +1. Retrieve a list of deposits marked for withdrawal to a hot wallet +2. Retrieve saved owner addresses for each deposit +3. Messages are then sent to each owner address (by combining several such messages into a batch) from a highload + wallet with an attached TON Jetton amount. This is determined by adding the fees used for v3R2 wallet + initialization + the fees for sending a message with the `transfer` body + an arbitrary TON amount related to the `forward_ton_amount` + (if necessary). The attached TON amount is determined by adding the fees for v3R2 wallet initialization (value) + + the fees for sending a message with the `transfer` body (value) + an arbitrary TON amount + for `forward_ton_amount` (value) (if necessary). +4. When the balance on the address becomes non-zero, the account status changes. Wait a few seconds and check the status + of the account, it will soon change from the `nonexists` state to `uninit`. +5. For each owner address (with `uninit` status), it is necessary to send an external message with the v3R2 wallet + init and body with the `transfer` message for depositing into the Jetton wallet = 128 + 32. For the `transfer`, + the user must specify the address of the hot wallet as the `destination` and `response destination`. + A text comment can be added to make it simpler to identify the transfer. +6. It is possible to verify Jetton delivery using the deposit address to the hot wallet address by + taking into consideration the [processing of incoming Jettons info found here](#processing-incoming-jettons). + +### Jetton withdrawals + +To withdraw Jettons, the wallet sends messages with the `transfer` body to its corresponding Jetton wallet. +The Jetton wallet then sends the Jettons to the recipient. In good faith, it is important to attach some TON +as the `forward_ton_amount` (and optional comment to `forward_payload`) to trigger a `transfer notification`. +See: [Jetton contracts message layouts](#jetton-contract-message-layouts) + +#### Preparation + +1. Prepare a list of Jettons for withdrawals: [Adding new Jettons for processing and initial verification](#adding-new-jettons-for-asset-processing-and-initial-verification) +2. Hot wallet deployment is initiated. Highload v2 is recommended. [Wallet Deployment](/develop/dapps/asset-processing/#wallet-deployment) +3. Carry out a Jetton transfer using a hot wallet address to initialize the Jetton wallet and replenish its balance. + +#### Processing withdrawals + +1. Load a list of processed Jettons +2. Retrieve Jetton wallet addresses for the deployed hot wallet: [How to retrieve Jetton wallet addresses for a given user](#retrieving-jetton-wallet-addresses-for-a-given-user) +3. Retrieve Jetton master addresses for each Jetton wallet: [How to retrieve data for Jetton wallets](#retrieving-data-for-a-specific-jetton-wallet). + A `jetton` parameter is required (which is actually the address of Jetton master contract). +4. Compare the addresses from Jetton master contracts from step 1. and step 3. If the addresses do not match, then a Jetton address verification error should be reported. +5. Withdrawal requests are received which actually indicate the type of Jetton, the amount being transferred, and the recipient wallet address. +6. Check the balance of the Jetton wallet to ensure there are enough funds present to carry out withdrawal. +7. Generate a message using the Jetton `transfer` body by filling in the required fields, including: the query_id, amount being sent, + destination (the recipient's non-Jetton wallet address), response_destination (it is recommended to specify the user’s hot wallet), + forward_ton_amount (it is recommended to set this to at least 0.05 TON to invoke a `transfer notification`), `forward_payload` + (optional, if sending a comment is needed). [Jetton contracts message layouts](#jetton-contract-message-layouts), + [Tonweb example](https://github.com/toncenter/examples/blob/9f20f7104411771793dfbbdf07f0ca4860f12de2/jettons-withdrawals.js#L69) + In order to check the successful validation of the transaction, it is necessary to assign a unique value to the + `query_id` for each message. +8. When using a highload wallet, it is recommended that a batch of messages is collected and that one batch at a time is sent to optimize fees. +9. Save the expiration time for outgoing external messages (this is the time until the wallet successfully + processes the message, after this is completed, the wallet will no longer accept the message) +10. Send a single message or more than one message (batch messaging). +11. Retrieve the list of the latest unprocessed transactions within the hot wallet account and iterate it. + Learn more here: [Checking contract's transactions](/develop/dapps/asset-processing/#checking-contracts-transactions), + [Tonweb example](https://github.com/toncenter/examples/blob/9f20f7104411771793dfbbdf07f0ca4860f12de2/deposits-single-wallet.js#L43) or + use the Toncenter API `/getTransactions` method. +12. Look at outgoing messages in the account. +13. If a message exists with the `transfer` op code, then it should be decoded to retrieve the `query_id` value. + Retrieved `query_id`s need to be marked as successfully sent. +14. If the time it takes for the current scanned transaction to be processed is greater than + the expiration time and the outgoing message with the given `query_id` + is not found, then the request should (this is optional) be marked as expired and should be safely resent. +15. Look for incoming messages in the account. +16. If a message exists that uses the `excesses` op code, the message should be decoded and the `query_id` + value should be retrieved. A found `query_id` must be marked as successfully delivered. +17. Go to step 5. Expired requests that have not been successfully sent should be pushed back to the withdrawal list. + +## Jetton processing on-chain + +:::info Transaction Confirmation +TON transactions are irreversible after just one confirmation. For the best user experience, it is suggested to avoid waiting on additional blocks once transactions are finalized on the TON Blockchain. Read more in the [Catchain.pdf](https://docs.ton.org/catchain.pdf#page=3). +::: + +Generally, to accept and process jettons, a message handler responsible for internal messages uses the `op=0x7362d09c` op code. + +Below is a list of recommendations that must be considered when carrying out on-chain jetton processing: + +1. Identify incoming jettons using their wallet type, not their Jetton master contract. In other words, your contract should interact (receive and send messages) with a specific jetton wallet (not with some unknown wallet using a specific Jetton master contract). +2. When linking a Jetton Wallet and a Jetton Master, check that this connection is bidirectional where the wallet recognizes the master contract and vice versa. For instance, if your contract-system receives a notification from a jetton wallet (which considers its MySuperJetton as its master contract) its transfer information must be displayed to the user, prior to showing the `symbol`, `name` and `image` + of the MySuperJetton contract, check that the MySuperJetton wallet uses the correct contract system. In turn, if your contract system for some reason needs to send jettons using the MySuperJetton or MySuperJetton master contracts verify that wallet X as is the wallet using the same contract parameters. + Additionally, prior to sending a `transfer` request to X, make sure it recognizes MySuperJetton as its master. +3. The true power of decentralized finance (DeFi) is based on the ability to stack protocols on top of each other like lego blocks. For instance, say jetton A is swapped for jetton B, which in turn, is then used as leverage within a lending protocol (when a user supplies liquidity) which is then used to buy an NFT .... and so on. Therefore, consider how the contract is able to serve, not only off-chain users, but on-chain entities as well by attaching tokenized value to a transfer notification, adding a custom payload that can be sent with a transfer notification. +4. Be aware that not all contracts follow the same standards. Unfortunately, some jettons may be hostile (using attack-based vectors) and created for the sole purposes of attacking unsuspecting users. For security purposes, if the protocol in question consists of many contracts, do not create a large number of jetton wallets of the same type. In particular, do not send jettons inside the protocol between the deposit contract, vault contract, or user account contract etc. Attackers may intentionally interfere with contract logic by forging transfer notifications, jetton amounts, or payload parameters. Reduce the potential for attack potential by using only one wallet in the system per jetton (for all deposits and withdrawals). +5. It is also often a good idea to create subcontracts for each individualized jetton to reduce the chances of address spoofing (for example, when a transfer message is sent to jetton B using a contract intended for jetton A). +6. It is strongly recommended to work with indivisible jetton units on the contract level. Decimal-related logic is typically used to enhance the diplay’s user interface (UI), and is not related to numerical on-chain record keeping. +7. To learn more about [Secure Smart Contract Programming in FunC by CertiK](https://blog.ton.org/secure-smart-contract-programming-in-func), feel free to read this resource. It is recommended that developers handle all smart contract exceptions so they are never skipped during application development. + +## Jetton wallet processing + +Generally, all verification procedures used for off-chain jetton processing are suitable for wallets as well. For Jetton wallet processing our most important recommendations are as follows: + +1. When a wallet receives a transfer notification from an unknown jetton wallet, it is vitally important to trust the jetton wallet and its master address because it could be a malicious counterfeit. To protect yourself, check the Jetton Master (the master contract) using its provided address to ensure your verification processes recognize the jetton wallet as legitimate. After you trust the wallet and it is verified as legitimate, you can allow it to access your account balances and other in-wallet data. If the Jetton Master does not recognize this wallet it is recommended to not initiate or disclose your jetton transfers at all and to only show incoming TON transfers (of Toncoin attached to the transfer notifications) only. +2. In practice, if the user wants to interact with a Jetton and not a jetton wallet. In other words, users send wTON/oUSDT/jUSDT, jUSDC, jDAI instead of `EQAjN...`/`EQBLE...` + etc.. Often this means that when a user is initiating a jetton transfer, the wallet asks the corresponding jetton master which jetton wallet (owned by the user) should initiate the transfer request. It is important to never blindly trust this data from the Master (the master contract). Prior to sending the transfer request to a jetton wallet, always ensure that the jetton wallet indeed belongs to the Jetton Master it claims to belong to. +3. Be aware that hostile Jetton Masters/jetton wallets may change their wallets/Masters over time. Therefore, it is imperative that users do their due diligence and check the legitimacy of any wallets they interact with prior to each use. +4. Always ensure that you display jettons in your interface in a manner that will not mix with TON transfers, system notifications, etc.. Even the `symbol`,`name` and `image` + parameters can be crafted to mislead users, leaving those affected as potential fraud victims. There have been several instances, when malicious jettons were used to impersonate TON transfers, notification errors, reward earnings, or asset freezing announcements. +5. Always be on the lookout for potential malicious actors that create counterfeit jettons, it is always a good idea to give users the functionality needed to eliminate unwanted jettons in their main user interface. + +Authored by [kosrk](https://github.com/kosrk), [krigga](https://github.com/krigga), [EmelyanenkoK](https://github.com/EmelyanenkoK/) and [tolya-yanot](https://github.com/tolya-yanot/). + +## Best Practices + +Here we have provided several examples of jetton code processing created by TON Community members: + + + + +```js +const transfer = await wallet.methods.transfer({ + secretKey: keyPair.secretKey, + toAddress: jettonWalletAddress, + amount: 0, + seqno: seqno, + sendMode: 128 + 32, // mode 128 is used for messages that are to carry all the remaining balance; mode 32 means that the current account must be destroyed if its resulting balance is zero; + payload: await jettonWallet.createTransferBody({ + queryId: seqno, // any number + jettonAmount: jettonBalance, // jetton amount in units + toAddress: new TonWeb.utils.Address(MY_HOT_WALLET_ADDRESS), + responseAddress: new TonWeb.utils.Address(MY_HOT_WALLET_ADDRESS), + }), +}); +await transfer.send(); +``` + + + + +```go +client := liteclient.NewConnectionPool() + +// connect to testnet lite server +err := client.AddConnectionsFromConfigUrl(context.Background(), "https://ton.org/global.config.json") +if err != nil { + panic(err) +} + +ctx := client.StickyContext(context.Background()) + +// initialize ton api lite connection wrapper +api := ton.NewAPIClient(client) + +// seed words of account, you can generate them with any wallet or using wallet.NewSeed() method +words := strings.Split("birth pattern then forest walnut then phrase walnut fan pumpkin pattern then cluster blossom verify then forest velvet pond fiction pattern collect then then", " ") + +w, err := wallet.FromSeed(api, words, wallet.V3R2) +if err != nil { + log.Fatalln("FromSeed err:", err.Error()) + return +} + +token := jetton.NewJettonMasterClient(api, address.MustParseAddr("EQD0vdSA_NedR9uvbgN9EikRX-suesDxGeFg69XQMavfLqIw")) + +// find our jetton wallet +tokenWallet, err := token.GetJettonWallet(ctx, w.WalletAddress()) +if err != nil { + log.Fatal(err) +} + +amountTokens := tlb.MustFromDecimal("0.1", 9) + +comment, err := wallet.CreateCommentCell("Hello from tonutils-go!") +if err != nil { + log.Fatal(err) +} + +// address of receiver's wallet (not token wallet, just usual) +to := address.MustParseAddr("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N") +transferPayload, err := tokenWallet.BuildTransferPayload(to, amountTokens, tlb.ZeroCoins, comment) +if err != nil { + log.Fatal(err) +} + +// your TON balance must be > 0.05 to send +msg := wallet.SimpleMessage(tokenWallet.Address(), tlb.MustFromTON("0.05"), transferPayload) + +log.Println("sending transaction...") +tx, _, err := w.SendWaitTransaction(ctx, msg) +if err != nil { + panic(err) +} +log.Println("transaction confirmed, hash:", base64.StdEncoding.EncodeToString(tx.Hash)) +``` + + + + +```py +my_wallet = Wallet(provider=client, mnemonics=my_wallet_mnemonics, version='v4r2') + +# for TonCenterClient and LsClient +await my_wallet.transfer_jetton(destination_address='address', jetton_master_address=jetton.address, jettons_amount=1000, fee=0.15) + +# for all clients +await my_wallet.transfer_jetton_by_jetton_wallet(destination_address='address', jetton_wallet='your jetton wallet address', jettons_amount=1000, fee=0.1) +``` + + + + +### Jetton Transfer with Comment parse + +```ts +import { + Address, + TonClient, + Cell, + beginCell, + storeMessage, + JettonMaster, + OpenedContract, + JettonWallet, + Transaction +} from '@ton/ton'; + + +export async function retry(fn: () => Promise, options: { retries: number, delay: number }): Promise { + let lastError: Error | undefined; + for (let i = 0; i < options.retries; i++) { + try { + return await fn(); + } catch (e) { + if (e instanceof Error) { + lastError = e; + } + await new Promise(resolve => setTimeout(resolve, options.delay)); + } + } + throw lastError; +} + +export async function tryProcessJetton(orderId: string) : Promise { + + const client = new TonClient({ + endpoint: 'https://toncenter.com/api/v2/jsonRPC', + apiKey: 'TONCENTER-API-KEY', // https://t.me/tonapibot + }); + + interface JettonInfo { + address: string; + decimals: number; + } + + interface Jettons { + jettonMinter : OpenedContract, + jettonWalletAddress: Address, + jettonWallet: OpenedContract + } + + const MY_WALLET_ADDRESS = 'INSERT-YOUR-HOT-WALLET-ADDRESS'; // your HOT wallet + + const JETTONS_INFO : Record = { + 'jUSDC': { + address: 'EQB-MPwrd1G6WKNkLz_VnV6WqBDd142KMQv-g1O-8QUA3728', // + decimals: 6 + }, + 'jUSDT': { + address: 'EQBynBO23ywHy_CgarY9NK9FTz0yDsG82PtcbSTQgGoXwiuA', + decimals: 6 + }, + } + const jettons: Record = {}; + + const prepare = async () => { + for (const name in JETTONS_INFO) { + const info = JETTONS_INFO[name]; + const jettonMaster = client.open(JettonMaster.create(Address.parse(info.address))); + const userAddress = Address.parse(MY_WALLET_ADDRESS); + + const jettonUserAddress = await jettonMaster.getWalletAddress(userAddress); + + console.log('My jetton wallet for ' + name + ' is ' + jettonUserAddress.toString()); + + const jettonWallet = client.open(JettonWallet.create(jettonUserAddress)); + + //const jettonData = await jettonWallet; + const jettonData = await client.runMethod(jettonUserAddress, "get_wallet_data") + + jettonData.stack.pop(); //skip balance + jettonData.stack.pop(); //skip owneer address + const adminAddress = jettonData.stack.readAddress(); + + + if (adminAddress.toString() !== (Address.parse(info.address)).toString()) { + throw new Error('jetton minter address from jetton wallet doesnt match config'); + } + + jettons[name] = { + jettonMinter: jettonMaster, + jettonWalletAddress: jettonUserAddress, + jettonWallet: jettonWallet + }; + } + } + + const jettonWalletAddressToJettonName = (jettonWalletAddress : Address) => { + const jettonWalletAddressString = jettonWalletAddress.toString(); + for (const name in jettons) { + const jetton = jettons[name]; + + if (jetton.jettonWallet.address.toString() === jettonWalletAddressString) { + return name; + } + } + return null; + } + + // Subscribe + + const Subscription = async ():Promise =>{ + + const client = new TonClient({ + endpoint: 'https://toncenter.com/api/v2/jsonRPC', + apiKey: 'TONCENTER-API-KEY', // https://t.me/tonapibot + }); + + const myAddress = Address.parse('INSERT-YOUR-HOT-WALLET'); // Address of receiver TON wallet + const transactions = await client.getTransactions(myAddress, { + limit: 5, + }); + return transactions; + } + + + + + return retry(async () => { + + await prepare(); + const Transactions = await Subscription(); + + for (const tx of Transactions) { + + const sourceAddress = tx.inMessage?.info.src; + if (!sourceAddress) { + // external message - not related to jettons + continue; + } + + if (!(sourceAddress instanceof Address)) { + continue; + } + + const in_msg = tx.inMessage; + + if (in_msg?.info.type !== 'internal') { + // external message - not related to jettons + continue; + } + + // jetton master contract address check + const jettonName = jettonWalletAddressToJettonName(sourceAddress); + if (!jettonName) { + // unknown or fake jetton transfer + continue; + } + + if (tx.inMessage === undefined || tx.inMessage?.body.hash().equals(new Cell().hash())) { + // no in_msg or in_msg body + continue; + } + + const msgBody = tx.inMessage; + const sender = tx.inMessage?.info.src; + const originalBody = tx.inMessage?.body.beginParse(); + let body = originalBody?.clone(); + const op = body?.loadUint(32); + if (!(op == 0x7362d09c)) { + continue; // op == transfer_notification + } + + console.log('op code check passed', tx.hash().toString('hex')); + + const queryId = body?.loadUint(64); + const amount = body?.loadCoins(); + const from = body?.loadAddress(); + const maybeRef = body?.loadBit(); + const payload = maybeRef ? body?.loadRef().beginParse() : body; + const payloadOp = payload?.loadUint(32); + if (!(payloadOp == 0)) { + console.log('no text comment in transfer_notification'); + continue; + } + + const comment = payload?.loadStringTail(); + if (!(comment == orderId)) { + continue; + } + + console.log('Got ' + jettonName + ' jetton deposit ' + amount?.toString() + ' units with text comment "' + comment + '"'); + const txHash = tx.hash().toString('hex'); + return (txHash); + } + throw new Error('Transaction not found'); + }, {retries: 30, delay: 1000}); +} + +``` + +## See Also + +- [Payments Processing](/develop/dapps/asset-processing/) +- [NFT processing on TON](/develop/dapps/asset-processing/nfts) +- [Metadata parsing on TON](/develop/dapps/asset-processing/metadata) From 930209f24dd34f0f57dae2943355c35a1c766f2d Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:31 +0800 Subject: [PATCH 028/219] New translations message_layouts.png (Chinese Simplified) --- .../dapps/asset-processing/message_layouts.png | Bin 0 -> 26504 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/message_layouts.png diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/message_layouts.png b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/message_layouts.png new file mode 100644 index 0000000000000000000000000000000000000000..b802d60d05f60a597ed040427dd72cecc490975b GIT binary patch literal 26504 zcmZ6y2RM~){6Eg=$U!y<$+1_~;nKlgKw_x&EP*ZZBSud7Z@%0x;)KtPVsP%|VTfN+58&#=qj zuh}iSn*;=xe*~bg0iGdFt{#pATw<#KU2!2qT)h1QxWv@B5D1iqqeFn7w->ks_dUIF zu1>Cwxc|OGh#^wme*Z)~QZwbRdO*^E8sGFDx zN*^h$=@R1eU+-RaUf*RVuCRY#5v%kLojZJ zC{w=xzfhE(gJWQ@J5~q0TvA0XP}<$x#o0vKLI>lnZXgNf4srr=qoNZc=7jYU zan!a4BSqu%Onlu!Rh^8)^)&2sHA9?zf&=s{LYzh2eZ(a~^xV-ZrvBo}SV=Q+Whob= zkx#HzhgZ5Yj6|pe z)=pQ;-cH@Yz(m`{%urWd93^59VicsVspg|=XRL<`bk(!4_t!=my7@|pyPBGa`@5@Q zoINcx{Y`x(RVJxF5z56* zUBy()(cc{Oj+OHD3Jj9)l@OQ0*?9&awWQTGRYa7*H==s>s$R;fcE*xs1{!XLKH?(A zBC09~UGO$l2UV<^jzRTWhcQ3=s7(Loyd zOQ<8<^>Drtc3MH&Mn=jyh8A}I2o*Q!APtm1N;JqZ%sb4(#lzH92Q48j?(6B}j1BTb z8|m4rs~ZQH<8<|c-QAUSrJTS~Ksy+PnTcD7Soldf=!v+w8rrD`*(3c#4Z)ba{O!U5 zRMbQzy|rA`q$DxHI%qd>l(?R%v8slrrctoFi>8x?3jSz^d1+#u{M2;abwLYUf11?t=TI$20MNa^X@`I&&{14GRnJ=L^u zre2;QD91o2a}O>8+xSb;ft)Vef>q*ONAO_LU4V(RPsV*ES9_ zFgKRKArTUOVeUpDMj}22x;o1EZT6Q$8e&Dn4GpjX?m?!0s$NKYf6#?!XsErUnJU`d z7^NoSE`ih%QZ)|nn+zS2?;fHn7W!|fVMHvH7a6$0lt1f`c+H=FgpIG`T>3p zPClac4o2R}0VscjyP2{v(m@~V5aMEnat%{a3o~;!*9bB-5)(6Z3=(xf7)dHSxN91D zn`6AiO$|h}!VKKC0=$AfG3HWX%DSOIDuAL^L7IeuiAslvNrMj#o*@nvni5zo3u%P2 zv=0)a;U8wM>l$c=)c3Npckt8%ZNl&h#vT|C6*Y{cwkt;6!^hNK%pVhmadQdu_fSW9 zXo#pFBtds*EH+F6>1OJP^HW8e>8pE6_&J5Thu}P143VKh-qLz5X3|DZA_hKcenGZGRU@OhToJ#R}s%&v9nw%?)h@q||M1uYX`?Px| zO;IYiaZ4<5%)gy4Kd1IlLXI&58m}Lo;%Im z3Pwi${)O~heA-;)yhx7fRfN>}pF7YW^LS25zp!4jCb}r!3+|eE&Baku7F5hA=9D45 z+x}VmiFKFu?_b}?WwskW{~bLW)u29EO=caNsWlpJ_x(J!_3nXAPt>2WXLHufuB?Z4 zX-d+tM{oIvyS93m+upuAqWM!=c%as^{jfOWCbp^fddNy_^{tmdPJ=1bno6&g6|46U zUsaQ?h)&n&N#0R7d*r&@?yD{TZkQi}|YD=acI8LWyQwM(VmeT#D2Y`Gott@Lm#nhrdv^z*$-F&}&> zw5fCFMjhOiZa}49-RL_0I9)MIH}1}qq_SW6tyFw$-Da4o0L(F``&oaA-<;Ot*RNmK z(F>T~6?oE#DC2m$|LCpUfwobNE{p%yhlNhph*(~D!b+nrPSJsxo~<<6(f#<9k1Jwk zw)A~cXmRUBKrNF(MEinkO_|3|{jb_9Q0wsTNdI38&h9%JjMu)I#j6v1>iJf>;`^*} zNF*S$k%8GsqEw+Byg4l`jX&^R!li)a$1+Y$548$keV~u*X4k*nyXgMpvDa!|V>14M z!aSj?h4o4Dbp@i_HXmCLGZh>->P_me(v~(GZ2NekmFB9(TaBVV#ohZ^?T5sC4nUqsLVo$7EFsCufO!vq_iT{#%gZtDkM*b(!|3;86Df1_2686ZE(?w0tQ_lvc z#lt=D0Y8aRO8f|fKWy=M2WX;mB%QgAur}*Cw$s9iDKU+sRDI7XzE*5XZvDC$qw%uB zt9a)zVPtlzTfV1xv)EZ@uxMY@~EYfH74J~P>OP?;BBo=VA z{-<9GOXQE2Tj4>h5&N;yTNUwR&gGF;#b22|tr2dapROny@u$(0;RUAxiI-93WT~}T z9B8ME5=$Opr&0RbviZ361X^7Y zyheyV|B>8r{4sodl&9RI`|m|~>!MqIDC7yQstQ?aV+hqm!?(uk)-&^d3<0HZS2_N2 z5potgP#QREo@DQpVp*Vy6-;RSq0_IlpeO`(@N_Kn?VZePz)0>txgW{!&r< zk5|lz0WqgJTv}s5BeRDSnKn-A%If?geG(bJ>CtsG>jaFA(;fL=B9saz6H7(OaLUD~?(x->{$z+@dw%>Wy613nm^C`@qRB=X z);aseNKf9;tLw;=%_=LEO*Lu6!hO2>cDpfLD&=!^<%<@xzvWS9u6R{WR&CoeF0lkF zl3CAEg;JiyIu5ce?UX%S$#X-*`NfPw=G^|;x{#IalYTx9Yu=t>gE$a|@Iwm%e;$?S9MT*VATzn|P z1i?Cx0$TZavLaf6uu;(xe-V|@y{FTrTGR$}+OSyH@gTSXnr6`w`VmsWD{LfS||OlGjVmz-TxGdnnw#0+~4K z|2T9E=fqky&9i-qJTBv5{P9f@cbNw2tDLEL5wgI;W_UUR`y)V910-;`5O%lJSf5H; zLIJ^@mbf&OEPE)9b?i?Ya?Z*qk5st{Q&>T-I`w1nV)%~I)ZDt_-&4IyY<>K3 zwyEFWJxE>+D4syXAvWoDWZc1{Q<o%2x1 ze8S2S-#_eq0rZl|r?>uN^QBJ>)-o@YhgZBe*GKnQ(lI-9}CCa)rpTn(`?A zkW3cY*(N^4AA__+>hWI^_9{1<8+L$Zq|uABCG5TL_pP?(b*kd0ZspE9b56TaB7gen zFBx>5Zj?B7FBFoF_7SsMedk~EcP{l!YM|CAC_n0Vc8x!ww>L}U;>dEo8Qfmb)UI?W zvFO5~hgid+HHY}#i*jhgm36R+wjm;t+&_RWr+r1TJ(M@(EcKYGNo}7${U=yNv4JFV z+UkPWlOPnI7-EETN!;3)6)C{V^$;Ml<3&v94t%E-V zuA$*h348nDYqV0?Ere_F!P@IT-Wt2=0dK&a)Yfr&s0$gerB9+nppW94a;yQYjJI`qgLFyPXHRjAVb%aH_$#ROwHF zBFD&L&p(KASpOO39~NVC9-LiK?wx`p*}l3M31(>-KNA9n!4h36$8m+`fFbuv2`*Op zUAKsxy^6??-F*&?O7m_mz3NhJB!$iJZz7kGYMJri4*S#9_M#OA4S~Uiyx$%nB zKMdI9Hwp>+5gYf~*=7kj@B4m!69v&^Hx(FaDPy0u&!n5fW! zop4}l#=6~>7Ly-ROm(}5d2WBs33C$G-HQcODz0r@86Ww8uX5@7d_EZ7h zu1+g;gv~!M?u|OS$thdJ2(--0P`eX)Q}4=eRHmc;*`yjrg*)Qiw3)+ZJl-ufLKuFg zL!S|Hab)+id>ZJF%Kpq;q##`j+bY)nHg>ul{Ld%7Kv$Nhk%C4BY%B-4^MyIV{($qW!aLnojSaTfj;3S@>b&ouMs~z+^ zzW~XY|JK)(v`wu2s88o|iTCn9-Os<+Ki0;aLmXk3~Ej9q+HWLo#{} zSQ;&SCyGbh8zKHRrS2xWXSWo;tDP1-?Hn<%ubv=fh<(~HYCo#MCo?QhTgAU}#^-o5 zFC*b4dcdU%i%>~2&B=UttAT@q6V748a{Qfo*VRAxbX@XNUUcqaSz&hg`uXieLMnnZ zmLKxMLn|g4NIMBSJffXNuVX+OlU}@<<`!UozL{KDeDi%t+yIX-9Pah zH2rPN1bN;t%DHuWFbsCk*F2U#XDm2=BjC`KoiT(&aA#GddwR-};Et3#8AV43s zFx`~lQ^8e8V&2_hD(5tI4b%TMA}Qhq=l80&x6K~f{G8WMPg;wj+u-P6Hc9J>Vm+LX zcbkww^!Lv1m$i&?dMd*?IBnQc8A(hvjL`)uFhZj25arV@2F4$Cej2e{!NpdRO?%zH z8>^*FF7wWBuoJ-+)|s%knVtlSPHU=l=df^ky15Fn-z`-NL8cSyY=fxk(Eowt~iAvYe%G33>ZXW(mc} zQYGHTWRly5W9>fIFMm@S6%#E}(Qm3!vi6`Wq3gMQ;!=KJy|)_a`tt2+Z(@EyuHW~? zXZaawJyv82@9O&nUFVvurDg+}DkZMG560B%o)EZGt{Os{lPMUBq}{=tfQ@t$XSiQSEVt6_fZF;xO7uX;A6M z{T|viqEX483@wwvLG4woVEtrwxGp2q$Vuad9zjz}lKdvMJ7qmZe(=wbjyyK8do#EM z*11pZH?=TWXUtL{g(!6So-aStVU6cTY_52qu1DU#U@~rS42&K1i&tV3i+%*jkR^Hp zD+p|0({;4F$+m-}V2llE*@VxC)zwZ3|7MsK`v#5^NoN%6`boUY8!+r%I3qL>VSe}C z;&V@d#Aw?)(5#kIRh1AnzFXQ&6p4wpr5y#?Dkc=E#zAIcV?^`^%(E1AbSmPujnMHm z;`6zl3&&r}{(40bbpbSU{c+6(9#PqzgId4o6d=zZV@d*bcRe1JaGxKUkI{DWFVh^y zGP4S#7_An>3l`8&bniG=MYv2>#9r@h-rwZl$V!jMk0P;yKOX#Z{7INWI)vi4p`)YY z$4{yu9`x>`Bcj9CIZ9;LA#6#|mRK!l->?>mw-qlHbptCEnU&<}NQzm$>Y|^SSe1-j zW2xV3KEp)X^c15hW*gS@G+KIG(hB>TNBw*?5Ma!2dKUZumjW}xoq&dJ)BPh?tDk7; z)nWT%#Wp=Len||w+`K_M5whgjmioTj2ckj-=O4^y7vwl(0|(A)qSW^lBTcKuIqm`a zc9pz_X7?xmM{OnzTpZ6K1v60#)hD@W4}Qr?0qo&&^26JLM0oVUnZwC^Hy5B6W8K{& zj73{ea-%t19Cv*}Bk-J++syj24h7iQD{!1@KAvtCHn0OnEa8ky}`O#VInh3iz$<+xdCfWcfaYRtm&GdSJ5HFqBaoJY0B<_ZZO=OJ{* zwElE}CIY4nB4*a14|O7Ex}h<>Mb7v5=vivvwfAB3alNOT<=)1d^9bg?UmREP z*xtN!OD*y^tQP4$+rZC1TemP%&Bxy(oO!wpkUcSgi}Lj-dn!}IV1)dB^>0aF+Udn4 zTAN#8%`Q2Yb~JaxYVlYrs^~L_envBH$jkJqyBhhj`z-qo7Xdg?)76b0)hhu7uaw~U za?GO*Uit9+wE1!3@)0{OL3gci2|$)Qj*bsKBWEw@QhO&WRq@~f(zHrr`S9~Q_6ivu zi_U=vi+_;iU9oGPOQ*A1NJjG}+gNE3&FQ=h`aYII&2bo{^VFYqWLhMgoDngoU9Rv! z?riHpHE^n8pVr^zh1kJCM^}s=c#3J_C%xD~{@FDV-;wW)3~7(CyP}tAHU9_F&A*!< zDnR&bbX~Vh=6wdUr0%lEpR*&QIc=qRH?ep4ewa$*!!(!i7^=P)qmFFU+#5|?!Y;Ll z`2P=UMoqg%YQBRR8ynw!?zvQaEBM3v!|zLG@h0hL{T|y(0BH8OOB5a1qzsnxhoqTA z#OuAVQ7yE`H6?81q(_t;Tm5%_NScxm=sx4IHdvY^fJBw*f78Ebc5aqm-yMNI**v;JY1cKj|sP!!=i!#5Eps@D(kI zBxN5R-N$iem^}2v53QQor`(SPJ|mO!mCL}PA;kgp=3AHn;{IMTdj%L9A;*fs7a5)U zKu+5%qnXl128X5ZFj~(x`iIRaPT4|ao0Pn!h zgf6y)YH~^3v9GXHxPDYz+W(=v$-kkx!*GFj4Bu$Xn!d<10}a_xN*~0t`^!#}`4}Rck+b3PSu^xh00rro0HR|OGT1vf+nE>}nUU}M}UCWk^9y!5^ryuL&$ z?^?}Q^RNlF2pCae9i^WF)>^Y=eBB3)fSY6`9zfMT=?y0L08+{g;#^vbb z040S-k5bP;`;9`IsBF^IEP4&Hbs`$E=!*z8WLAlu!l~Lt_j^;zA_^9nyKl>6OFY{? z%szFU=C&Jg4W+41MqEg!p>rdT12bsTnKZA^H|+JW5vE#$@}aW24wTA)*Z6<<%`Mh7ey9 z{L$$Gl8@P2{x%Y)3co5j4hRAc<{x-OOR?37tCtB%&8!&Jc`mn>!Vygk3tcKcC8-ti(I5^u{`kWU+1Rv`$c*x{kU<;cYOWRaM_G9N+o9IaCyHX3~(xq89VkREph+`XG9I-cN_?)pydX++b z0i}VIt*=YT?gm&jP3Ka*nQ${xEnfQ_!jvSc^~$}R#_rlVjTbwP9BQYg0(2cF8UtT3 zO%hSM4yU3bfe}>^#;xK)lR~75?lF=KLgUtO5lQVzwr~U$QwljWd1r#^#u)GnBG2dc|Z)ClY2F?jP6$ONfkb*ETA71nB_g;_p#jm!WQS?Mo55T_22xDM_ zp1+akEpPhih(a&gViFZ3>qEKJr)CJu4%#StYgT2b-A z=m$w5sL-?<@?jd!-P#oKRkC-O1k>)x?Z3>*Q*|kf% zz<={0ch^c&QlUQi-u4MkfV(<!c+Owv_C+I-!@p79LFJ^S`+OR3U)m8cG|VFw&JC zDh*n6b$a&RRG^XiPe281%d;4&EQ2>o>MNfgwu(R zpJ@+mf!`CWF!ABq+~&TRr1wWF_V=IjK=?Bx_iNr^1Gu|)TA9y0Q1BpVdzre9fY)~A zrID^m!yAxw1{?a?ttSf8}opC;`CPHk=BWO`$n0| zj(z$^^BMh`Z2t@0xd9t0As*F=-{^I8=-kH!fkLP6SJ9 zdH+64EmjGROW;&4HEo}}QN7N=vAzl)5coQ?IZ^KI^tROX@|A101vkyvi^#k?f1pXG zie&=pPI6Q0qW_2T@`N+XhyS8;A6V&&=X%3#PWz3Y0%?j$dS|fqN!I+qze3o-I zs@bI8bR$!CR|HUqIWG`c10NIL+d>VNYU>&+gZ-_>bg>QaeXtmBT>pS0DM?qhn>n^A z2`Zgc5jT#hJ`Dw`$O1E5yPB!U$)z2;zskB<9h1hM6$A|tW@D#1&()RT`#$i@D`HE< z@QRW~Tu`5-BvwnNim-u87g~@sKq0GrL53fYo2rsXDHL609PFlI`={a_wBU*ckx5*k z&@KKCoCADw7mxll0a~RX$dp@5c2u9Dl0#6dZlV|8d1``c#2|)h)i|XeivXmv>Ix+ zm~TE+CP|h%)Gyup4fs~>zQ@; z3T5;sGUAS05i=N3CT8K3B-yF~8_Eq0`AZ?aPUTx-h!f*yle&Jq2^f#_LiM1Xc>@^r ztSLk`mHNRlQ8EjKLg&GtFZFOgw~qc}quYf#iTs^dJw~!wqQ(9%Zz|Vv^%SV(0}sX} zwXQHXIxc?s9Z?Xyq=qKAye+cJ^y;mSDm@C$3w=#$D-ST@p)P6@L}T#?Y%tc=L<2^W z1(O)MANvyy6DoIvCdRPGxrlo%8sDT?|JiH2&CS6}4+%`AWxGRK!{tT&qv+8n%xr&4 z@+us3DG2j%Cs*m!+mwkmc6h)qyeIB;wT-Gq<%e7Y>+;Pa+2F=1LmrJAYs%IX7rItV zJ;O4PqTaM#J^MF4($vmqDml@hb+Z1PxTD0UgxI)A+4hW7BGqG`M{6~nIH(6cgJsl5 zs{yWPLKh3C;!V%+Vd@=b%3phwnn3{DxMh9yelN=$sj$3oW?{)j0uDXGDC*zUOvPJVyuj@ z2~<>jS=3f~*L_B+GnC}6BjVn~tFp|}{;aXZ#FnmnY|hV^46fl9p^s}@a4M0i7R;ZR zA(*A9eg@nlr_-Nb*7QtLDG0rYD;uPwfpdgG6PR6QHeV)r4p$ir(YGXn#3A#y2cbf8asQL(P(4C_qRoCZLH<8_cfv#xZeGX#t+3y{l?o z{mx4TQ7m+Rrn$kMF~t(jei*+!*lB6?or{oN=z7W^SD}@CvA8RHU$sXkR{SItNyI?; z_mw?K-&)pePjqzZ@7~96r1;RA8Det?MEEZcG293hN!C!>&lw}VQF@E+&so$kX3x)2 z&oS4?Of8l_@PFZ`Xmi!<)feR8in-crHiMucNLf_dfl)Ci|FXYH`8@r0Y?XxF^AABw zsg|0#qKo1Jqu;d52^*NiXeMy(V;XYp+eYyyf;pnLcPR|bxAHRR?y;?*eURgKMF)P> z&Rm$MDVgIqzb%Ou*k226JCx8_&)U9p2_3_H$;w->8s^x0YjOjEjtOD#qF%jc$j1Ua zY9mOH!6qYLWqR#-+6E)$%Tp&e{kJ9yoLCeBDe8FT>Ncz`w*6PVp$vqn*8&n`=WNU6 zlMy4_(rL)rN3zb|!T7Z(HHIf918NKXL*@ClNAwTaa1-`VqxKrTjPHJk1kqH6{<&9S zb$$=pLf!i+Q1K^;&*M9L`q9ZVKd=cfO^!=!v98t=!i0u4Ht()9blXl*nx_$%*@Lt_ z0s7}AQ;A9->xD}|P%_6@Qx$kMB<+c}N|Izm)b1Xz5*3de%0!+8jOuF48pB}On>Qm6 z>?#trUuK@dAFvI^T_;crH&Rck(&YSFN$S}BsRyLa^zWo*I4k>nq59b`c5+8X4ul$F zr34x$mkg9^!{ik+1STv>suL3HY??_4e+K%z8GLTZW*VC}`4*4B=J@E{~OVGr-*4#Jtm% z*@);Fo{EoqKqFj0Z6(G_q(N*-;YTPnu>f4cAtET>k1&PZyo$tSVqV)W6$bH^MQdZ& zQ?Cz;a(~YclFEl*M>g|Kp~C(|>2~`01I?v5omXzHs7no22y47Lt{XjT0V!Q z<4@7a3rgIlQ@M? z^fUEN*mD+UC_$RAep@SlYop$<5pDCWC+L)(h&I$?8R3d_o$HEev((3lCHbB50EwYC z6W&9bQv31RrE*m=_VC=eJz;c{(tVVO!3y5&g0mh<+?nOuV!T-`N8Bd5>qsGl_4XGy zrSV5+Qc&p2-HB342)O`mpYYk`wyR!tJVi`lsia@9X(Q7R*Gh$}iL!lpdxm>Ss&|bH z?w%+#jASvVm2A}W?j#zC7MYAnq?aU>^FL(6YnB0>?jbD^5N>{ZLGCDiRQTkKjRQDl zf!{byY10hsKCFCES6+FoT;x-H|LrBx&-cQ2vo(uPqwe&ph}zyO177V+j?(FVcwXQ0 zB`*O@7%J&~ymQ4^=RKP-tsP>qHJpB4GHXN}Rf+tfyAly#cZ&ns)Q zO^<#yI*@ad%QQMz$#j@fQ#c+bhln*a$fch%Tjxh$kQ4K8((4g$jxDhUro+^MOxbZc z&V=5mZ*k4Oz9A~=m3tZ(y&h?`4gu1G$h5GZSFCtX`AHwh9;_NS&_AC!edgEvYLbuK zR**J8+rKOPxF8p!zgC!0q3?5RtVB{z`x&%xic*)W)J_V&XNejqWp2u<2Sc;}WpK6gRPI zaKVT>s-&W>Sn=y`V9Mp|M(p~Z1UryU%hB-}`*MeDxCCcq7cl6x;vZUQzonIqE#gD! zUCT{wvSeYriz;n{vir6hvbr8D?=qHRQ`M zd&4^MT!~m}dk5I-WI4O8m<**TI5)Pgc}0poky*k6`@Nb`F)HF?TK}>!e_;lQajSaD zejF)g5ts%alQgawtBAN1L!-riu<@$V!PAJ^bmrr7lnwm88R6YJrP@%m8#~8f!V3Q@ z8KqJpp2%eeD`zet&N+li7GI5i4in8_n&nmZ8sHz=@?E7{=p&RJBe?Cw*dbXMq-4 zd_sfi<7CJyDk(<0Yn@nVm~WjvuG$o)Mq|eQ)_kDVNA67+9_;|78IU_P>Sgrn7f#MTQ^y^`t zhgaJpRdy+9Xg&a|fZ~xRt<*A=9DOEr{_5r{;u|6Gri>g@N(_nb(1``T5lFVUoT&D>lv*U2q6xAy5-P0o>YP zsw=D8w_)`bjYcwt61d<|?-oHM2bCs)dv}fE|KNu2-+k*2zZ*-LBcJUaQq{`c)dd0} zdZ#FCR!uYt_1Uf&G!o0e1&0NbQCzc=-(~iV+!V(O*u0h3Pkk_t9 zwyET>N!YhA+6%5;#>8JHkBC-GWwin<%<|2CQk4z0lxu-Lc=Uini8MGTRC%)F7jtsf z2vzWE7#Cp@5!&W2K#+U$cClf*@VssNeLb#QX$xQK zg|z)t@8L;+^zAz9t}rK%7@YS>dB$hcP<9x@zt9%Gwo*y-zHB(Kgvtpf>htU7C?@yF;W!%iW$ zRUb7Ki5gwVb>4En7;a&Ien#i ziPvqOlaTrQ1NIeSLjLGn?_?Q~cHR3lBd}+7kK`{gt``3R78k37_QC^CQWIuFg_K|F z39fYXy}vZtf2Nh1vDfog!8>pj=sWe4%Y>D%sw*^**wrFqx=bUX&V3bCHaS1E^dRrs zi>=n_#F%{CVC)HiV)F_M7ziE`uvN+(gA`8>i{~D$L)sNC)}deM`TB{dE0q{D^Wts^ zT0CvKe`k>d1$TvI5m33W0k)4zbnz9l_w_TX^DJt6i=QSxh7xlv39pqUJ}jIU(I7#6 zOTkoT*>r#F$Cm@pJ%4bO{fw#L)>qzDo($zzG=piG#_2dStd!1vtYBDVBaV!V?H8kJ8 zDoI@xwBU@g(-p-!J5AgZ>~lS50qoV+<&M_8UG63w6yQbrRSC493DD!q%m_*CDPE9r zEYu#zc0D6^5azu?XO`>7Q~k#gLm)niViezN+a3hM{ki8AGXVH4lbKo2EubJ0=Rfe|$PG zEg?Uls>XasM;s*^TP!1^YAa5(57M8%0@4#OXCYH?rAauuggw8DentYUQAfu) z@V;m9rSIwNRP>O%BES!r7a7stEmc~-%&up!n6RwAZpY4=Yh+Sr#YKe>L}h0z%r1$K z-mPf~uf+i!j2?+zIIqeFb;%P-HDabmD{Uv7(XVqv>roja(5Z^H#b9CS- zB#};9d&+P7fi0RwS_!T%<}@Y(YSju?ra{&ONp_sRa>Gb|73hr`(CC;_e9X=Pc6<7@#V=FMfmLBjl%Y?K)vgeJi3)pY4oXcAJpA5~ET{2{kn zdR&%0a2kiZA9y!SDFz1e^)FLFd0NE=2_b~pG5Y(Au*)FtPXTeTA0LxLt;ox@ zFUFysnMd<+#u6U*RP0@qLUlJ$z6>$tjndw1*FroiO#*0ne@y@K{JeUXj>G2$=w!{8 zH-+gWdA;Hh9e0gSC991gy(hsSH5w9k--|f)^_BcrX)c%0%h!zY3aMntf61T0nG+V zSK~4J0(LW+@u9z}{jZ$`n!I*UKh)8uB*Gq>(ok3)eX*dQF;1P9{J8lQQ;RiBigP|o zY2ejuX00r{deg^s-f}o9LzWh>m(T>@JdwaOEK7VX)o1waw{H@Cdg3}=&Ddg?qi+8? zk1r|O`{_Hh%5{r2FVA90Z?9qrPm4&MR**dKeol-Ho|(#GALo8&i3S9@wcSb5fZP9< zmVoCd=qK?dJ|2Jyss>r*Ie%ksmY;)w2%m?c2*bU9TuAZ;M%L@eVo;w_ZpL0z9(zsu zu81>~aEu{UoUOneL7ZA6=p<$hu?yZKl@g=lZBh3|s|k#jAlvkAW&8QbNN~ z3dpzW(YIE_puCwwxxyus8LQrYYv4QMZTjYjmb9&pz_Y2M`3mY0gl1Faf6Wi}=kp5R61tv-Ck}*^{at)l7WMa-4%fv3W|RkNCA@_V8T&;n(TmUtaEiHSzU- zsl8w45Q$3GlH`O@4#S!BM#p1CVqC2iX=SjivSz@>uR*q!+MZNp=(c7Z{RFuMq9CWH zzWr9F)EbO0^`3F%%pW|9?o|fn@v+pWpM<6-2We22Tgo_5{FcwrN!-Ev$D_w;XkFRw z;?37>`M-gJtCRT-`3id;$npgoZHF%gI^23^f7JOp3yqUt`e%|-8U(tC3@W_&mkZ)OlfcNW`t7nLcvG}-TpC! zvIL^dfzHr}tWhe~DA&e)UU+-ooljq^EN$7uqMSy)zyui};zP3KB4QanMO zZ7XNEh*{b+Z=HXRV!i8(v~}x!lY0ZVp8TKES6;<@97peleAIb}nD5%48s$3B5|Rv7 zKCufxe~pK3mhrqdY-PvB3H6-&9SqXy-PIE67`z_7(_G8^0+hyTyIKTfhNF4?G#>Q> zU&g~a^2p=>%y-g;RKvP?|9HUoE;&Hc!^P$Lh;U8guZz8nKerWoWKEO~UFZd8XK z?^dhdxv};w*sLfs%t{3s(tbw^L#fP6iAIz$nMw%EjThPG-0T-B-oA! z)V3cMS|hx>Ppk1f_7HFLgu?H;m&uvbWR@)egI%`>*nCdfhp&><(jp&G4n$}=O#-14 zHc);n`6;56PQ3HyYXNfRg4fa1F~;{oH;R%VcR;eWw)jnGecfBkA>GFJ2*W^%)GH*M zn?cN`mn7`hxEAh8PdcJRG?aQ%6ce(5{U~5(fX-UDKc2LEWMXb^K14$Ia}-PjdZA2dHuz!>7JPmOt2C0b1NeSMG<}sE*2O7sMd90lsXmIjr_A zW^IRl*FPtKq9eL}^fEP@bPqH2s<$u^#6J~ z?|7*Hz>gmeXS=gk-DR9TPF5)#t~0XblDv%YS3|v(oOYoF{aBhy_sE+DLc>%mPqvt zWky@6TW_xwM6GaGperc=1}UNvdQfS?Pg&pfr(-#25iJH&=B9Qku60UA#kP>nY*esC zg^#^ID@XNn`{NBov!;qF_Be>870MT+@CYTNBZ~Dyi_Yq51uPUFR)6KX)ul~BKz#7c zX>PTEgn?&+L@c?!I0~=feY8>W=zWPCxE#m+w@u@2%E)}3H7$ze@{H<_NmJi4{j&2= zQZNZPJ-Kmbf<@(`$x{lJvyAI)zVHU8FqLI1|2Nvc99)3`v(HRlH6rM6uU3Z5?HZ)f ze67SZWoOx5m)R2TIn9B3MhY-vToA^3BE1{p8^)6z?}2M6gvKK5yWFkPg(>Q7hLhCi zz90X;=gjqM2l&%ZIAo;3~d7Ne&p zbj$D$RdSzzhf92nz+#6|?6rpQa<_zcpZSQ#hIsqvq6~y!XB{Xj1Sh4J_|Rd|rio+r zA99;{s)c?^r5u*IL)%n2WvnGNs`$KK_#le)w2Rb^zsS>h-UOV(@)U92ms1yKBKP}K zRJ^8K(2v-`EwGj6UEC%Kt~`CE^ui%UWni+};`OG)1PB)#IE%kDObHGHIssLCiQaqI z97g;OGWDX1rO{;a+~3K=l8{`4u$1K53vV1 z(cNFqk9vqXEc8)A2$>ZWT)NlV4lp-Fv{lpd;7Q{vrx{_L zcMc!Ou?n~OL~d^yvVYfeg;){Uo1ercCVqq&eUGCIA+5hO*_PmGyvIDUeUm-0*a+Qq zynXR4K>y?&1{k-z?q)ExYBPDe-AeNkkg)xCK&?I{FZ_()CckHE4ygX5Hv~}fk^Koy`6N~czR5ZC) zo=;+_JHnj#KrcM-QY0$(8Q2AQH}5>wHNYf8%A)~JR@r*$KtL! z1R<TC828G~nPt8U4fb9;*FG?Y%7OfS6|=E~s}Dnk(x7N3uclw;^KLzfKoj+im$ScLP!-d`@$>x51^i9!$E32dUMQ zfY{u7XJB@g-tSqWD9Ss!BIZ%a%hLvkCN=eOAL^Cj^U^nOvy_CyGxBS2PP4@%|aQKig5T_~ZY@v<>PB zlK>6zEcd=z3xYl-KTpH`sXxx2vPw}U+%IRp^K!LW@80bKO9)$7NzgwF_Egy(hLJPq zty4~94?TI_nx1);ZAw8b7q!uf&v(O!GR}k(($^7*v4E^`6~-*KCaYUN^}pctNi|{R zbB6;UWyjD(A;iG2s7)9Cuh9x!>uc5ZowHTq%a?-pQ?ohzA#tBT_sDQ(q_l(<9{3_E zefQTZWy#K%-XPS@y1o*wQb;f#N7{_Z;rlrh+b0q;624_4N~0)yvMGC>lMPx5V51Tt zq_}9pc|0MphedJj*ro9YNc`h4SSzZuZ?-Hs>UJ_fyck_b%>oD@o=@Q7o%2*QjIyU2 zFI4+mvWnj-x2sS?H(hGUDpMWjcB|jB=(n`n$iYFtMLNO4miG4m0;6|A>1@J%X8Gdf5^cP@1IqILaY_s1y2T4$FO zVl9`El4?*;2#%qT_z0%6wvBh{&{}!>*VYfGO)0{(G=8e~WY~AGGDn=36}ogf|D2gR zK~VL7jtYYFK@o}4*lAhkE6U5P%@4yg(2dg}AoGsZ8fqa^#LoW(=&Ak7E3dze`%$I* z%IGr$305bM#yh7s3&lBa&Bpu?u=DMy?(-i#xNMsf#r{Ol3rKC2rmgAlMI1hP!1@cm zY7jO|ac8*H_Hs;ew+Tu>f3C@Cq>76vDT1jIkUAJ2u+li*UgqTJeD3`jyG8j|_bx@B z38TUfYW};$-3rr6>mkz~Ja%u(2Xy0@1(sh=Dla&RL_E^Ot&SH+R2Z+O%^;!YhUnO* zX-X-JF!z^YoE+8;VTV@<45HME_t_)f%HUbIAbQDXG1y@TJaZnM$e7{0(Os0*wWfn#6e)6|cFcKAd7qCQMVZ4N|6dd~!l;Q}^KdW5X9+1bv}*hQ+9hc)vlmyJ`W|TVf`giN4uu z#26-hSk|tf_C;Pv2DPt&uZG{o$4`FlamU^Q~bUYx&P*-8L2-vR}Xi<=!E%C3N~f^ zQ#Q;uwOe~kgQ1Cn4pq#30WmG-2KOCtA+->pn8*NV6eL=@#gS~uolQ1k*O$$L8w`yn zT#O4wcT(~bG)d%>_Hx|sXKHd2RI(&sr;8;9gfrCf{N@N8o}926FG zXBMT`FCg^ouAE+@wbS4k5IH$~@lX0Rc*Wmj+G&I%g<6#+?%?_SH_lu(dJDUs=@hN-kFyjb7X~v6f-}AHKl7gd8p4<?_nMMi$yE z_Qc;kccxDSHJERDskB!>_F>*>gapfuq@LUSGF6-BUvQv3(C6Yi1k1AebRT$Nfy|@) zx%^6u9_qM;;csWRQZ$+3IITKKaYQVRn;}bMU$m~w>^hEH$ReOI%$rigDD}~qGW~F5 z1sAts(dXugN>lW=YSZoqZ>1R$#-x}K&Gx%Vbo4oFLVq(gaij-P3^dv){fX_pZMt7r zI1@mFg!y77W@jKeRC>7yS8!(d%bTB4P>o7c}J^j7z#mK#8n)9%bGS$+0u{b z1EY+NpP?F9-HtjP9j?guLzXg*m}Gtx6a8`0%$|;w8z#QdAx7{?=~9W$EZMxzs&QkP z2-n}6OSboHXX;JJwRN9kw*J1gGRtVsXx9ecKWWPOUZ=k8j+lFz%2cCBKqD(s6r%1s zyj$1-wKCOK+9ItF4&X3}Z!QEaOG8g3CQfUAL@-OW*p&R7Iw)(%f=gDiIxvruRk1Vi z>HarxGdrU2&-`SM-qgUz&ZPle4j!MqWQgKrCF>AQ3Yj>+Jenk0Ih14;0Cuj9x{D$V z%E%PEmIk8@lc)6|Ju)H(gKpxf%fN2D=-)yWXCVseA>uhFye`c zeE#M&SW8K!ct&BanB6e=GTnR4p=BbGH08+Zum(-+=V+_Hp3vRI}Rpw=Y7r|0N%oo*5>xUj6 zJ6cG{gPij^*e|&JY~S|iJ`uIdWU>kyT=n9u{)M&?f&fffD#Lr+>&8+09>^Ebpr!0N zN)U6V0PW9u)yO$l8ZBfC&8aSBqgdtTqEc%-vOCmJrucqw?H7TX)oN*bo=q*r+b?!! ziMVnk!P!=tKuqhE$8}42@`j1?=*(_PH+8*O_DS$m_Ca;+`7K$|8h9bg58k|Y^!J!~ zrWmi!qUG6}j zb0GOZTm0m8KU^M*yAerHa27f_Z23y3v6V0MDtSgxsr3hXKO0^L_l=nnBUqT;WDGQ) zYN&dGs2ug6F3BE}LNt@o1>pUoUsH0c0&vC;6v5bWDXF#HK4~p0l3rD1k4LmgT8XZeBT${`GRirVC z=+}6$r8hh557o2wlAn&ZfAotGGGEIexheA5egITDc&Oi5Q9&TP>7$7a4WSn#F?+Y z%Q;(e*i-me#WxP=7zOyod(W2yaj7FQx1GCYS~+4)3p%L(6dw9xq8%eM2tD zyrOhH=ecvkpWv0*M*3v-|vPQuSL^?C_dz%y6TXj3C>YJXSs$vWSK4s9C^@ zaKImC9=pDC;`#e8gmeYB&^pfs!(Ww;?7SR|pZAGtv06MQ80PvGD(5_iC(0$p-w`7dwi)9$OU+WIMMi9sFe$Qrl6o3hz09~*W?^dL2l=@ z4_^`1t*b6R85)GW`4N0MQ=%_R@*Q~Ue$QQm7p!jbO?5D6uCd?9D`y3#l}H1mpZtzw zoHeF^IF#`#FWf8ANyjwh6Z$*mt^v0NI6;?%3&a`!6s#Nh{+Hh%NaVg|+^%Ka+j$__ z`N|GW(}*n}GKV6WD2LuCv$uP7Hg^3d<5^Pt{PgNDw5(f09V*n$W8!&MCl_b)-xL1f z?mYS-m|A<*)jb8GmM`g?hEUwCpC-g-a-2uj$?wD_X3wYCBek0>0(6J5_tM}7&Ezw- z!x}RS_;M}YoAs_wB{|y~jAm44SV>qd*PGdjP78i9_jXECL^z&779CA19|# z-f~pdk6$(BU(E~B4GK)%-+jV5MvrAh!Q!|vCBFUHM~)}V66cQQhtjdU0sc|x1$+}d zVU%_~d%+;b5^v6l=%t_@5!Z^7944!k7$SKOXOSaHaj2j2(M(05uij{!q||hNu~=u9 zY}#jgIZzmVIZDWGOs4JO`h`KR_c!3K?HX0e*{0=@)zd*j-LdnH_pyY*;PD-lwxe;t zX27BG^|WhW1K6qZIxKz|UtR*6@Mq$k90E)9&_Xkv>Aq=r@{TPA?L;yy5$F=*qjSpl zWAmlBOi{N_$3U0feHF^dsn4Fo$j8W#*^iHq#K(Vst3&k_+OUT`5NInJl30v)M;3BIjqZMe+?;^s~%C6dYicH zibdNVezD^(yq)Lc-Q9Qd!H*bjy_!+jYlpKmHUmtzH4ZLY(+#Gb1uaeH+!9TK5vx4K z;|zR|?sM+sd@Va8>vv+69HxU9Y(ym}*23QlABXPocR5CHz94h_$909E%$h%Uoi=-O z=+7%5zur3r6HpC1;*yiCUsPq&AW~b>L5jpR3oYQS`nXP=WzEZ8h(LR8m+UV_? z-r;i_3>K;2r*RW56Jcx5Dg1``{Pp&{>CCVj({72$(A&ld@9XLbj}J~%yE48_?+e%$ zG$7vjKdCLh6M1qk)y*VeT%)NopUNmVfO&^QnzN$HqgcVxbQFdThEZP-sHOtbcQS$6 ze|8lz9C6+z?Xu)K_t}(pkx@);d`k59IHsr;CmY0rT6+B68#YW{sB?-t_>UHl3vRnG zb)52~vfRTjrt4}ojVnLXRHc(asnoUm^hd@IlQdQDNe44U?0Y}$!}^2p!?0Z)d$Exi zj&jge)Dfy~2_Ng#4&P%%QerE zEQDOFxv2jr|FMz2JE!$)wCDxN9xKMmlq!)2^FGja5%aZ^%;eIWYrCD^K~Cw?naMST zMr_p`KSDQe_dC0qwinZr#*y96t~7YHVla>3iJUJfH6G(|1HE zFl#v0cVmh5Og~vAzZryIigRF=Vr9MpP>Y{MzQ_1vK08`gQOVw9c(wR37Y)V0NN2oL zY-33v-Ci6wk(OpJ#;%7)uehD!thZ0i`{GnMZ$BdPJ_~3Qd}J-&=}WFY620$d;Z$~1 zY%0L9x-wTDp@Fy;nMviBdlo~PNB%;S=g06^BXzf>{kLC8Y*iez7AwuF$R+Z@E|#3y z9h;-dDT0P`3RWzJIFffrbd2vw(Xb@CeU4mQ1oHlNUvpWI^!^FZH)NWiz*2dR`0DhtZRZ2hV^nwPUU7Pa>T zIx~~mqns(#_*(6Espz)hR#xrKDr)mrCtSSa)Jd{8vCsy3U&_8*t`^(z)r?4|H;RpR zS7&2&a*x>hPZphDB|gZLw#kh>|C#3(VHaX6jJks`q^xt_zb=Q(rn07~L6iFTz`@s^ z_3DDl6QBav_I&LrxpS=fJ4EJoQICs$KAQ-^cXW4erGv8X4BIrT4;NhC{>e;}hzD7O zDKybF%%_&9mRL0BYTU?1(aTF3(A%U63r--yFRsZnSZkMYyKc(rbnSliGwlxzs4L&u zm`?&pMum1biMqR1j;}vU;kKaS?RC?P?@86M`z&My{^0fE+u<_i4#=<+W&R1&LW!6V zQ12CfNU(_L_8YNfSXJNb3~47YnDuHv-YSTgojdg4$eSvy3MU7G1hO`uspxx0>5SQWjQsTE$$W zqlo|kM6HPhwnoq6THy~#P+cxfkNigzFD-i4(O6hhr5d+}Ig(F3=p7d-!YJ%`8N3sc!u#}A4p zexPxy(IvrbAM{IU79!&DVzeMc{dwH@OYJLgxP~map5X}?(x;a*7Gt7-!FU99be991 z>YB6qj4KGG*u>y$+}Dq81UW#a%q+)CXbG#zri+jczOeKHT1S4htG;!UUsv6`FL~kGaAImvbdh*JN_{qDFJ?C#Y3_-y zYn^rNS6KYE@vm^Wca#XV@k0uZ8wrw~Z438JS1<+yQOQ6u{0ja7EU&(FAR+Dey(naJ zA(bwzH${gnF#~v)vo(iXUcgrjFej~O+~jfXGtx{v5*I&&A8b?<0Z7uqztZS+tuXx& zwN#6^ghcgIdprdy(;wvxtTf#o^auK~70sM2<#i{!0ma4n;7IBZ9*dCEmvFDO zeCBL!+>9G5w=?u;{(5oMBvDsb%$wmCRoKlzIZjCC-*e&pFzts$iW}RKk)O=gO1+%W z=4(cD+<|C583i+>P)04%;Z~NU2tj@DmYxb-JWSy}@)m2_9Q|{9a+Vs!Twnho6!eb24Mj_%uxNxTY zM?*geX}LxNZF=u4Csfn%6+lr`2o+Yi|97*xUdV@SBcn2yKlY)bXM@=q`4UynB{iuF zuO7hqrQ0w3JyoD%$554S({-`cy#%HUf!pptMDANqXKXJ6e)<5YsL-iVv+55${@S+U zP4#9Ilw_6?2m$ZRHMe5|zXRX^L}g5j>;Jd0JNUUstC-mDo@|{sMjm?*%a;!81kB8% zPuarrLSyoYDb;sDjz$sCwHPKo#pcq$p1>ur&nN&-qlE`F%Ub0uc`qmx`0l=>Ytw$G z@_CU@d+U^MAW7Y5)mQXZ^i|vksZN_n+xPNqAe#1m#wWoGcwA{WIOakJI!>EBc;|{) zpMo0V!9w-j+d$Ke#%lm75|niqOS0L#ekvk3ULjYM!XuA6=lm_N#cSR=AwfoT>5z5U z7Qiu!Ykgx=^)_8v9nP*;uRkY;O*(!GUftmoL|1O=R89@M)Zt)|&+&C)o_GgZHkujh z7nIG`gxqqUmH3^E4*x^C)_x2BL%PC~a+>MKauwK;q&eA{jZY0cz*=U~Xo0Z%+(wJr zbe8L?=Lj^5r^@^EF(7anfa!vrRQ7xay#hSUG6dCz(g`kmHJ=S^JQBBTreTQ9UQZfp zLhuwngIoapI&#JUI5veCfvthlALO_C$y04!^IFu^S&^bLXY&*TKVH1^MHK!CN)R@O z25#e}s(E~;(RSCuC@E~>d z{8Ar4gMmtrUP>Q|d-06XH1<(-;p^%_NIq6%UX4J%?W0G@>~F)vt^xQk0#qy;Zg)9I z3vcEzCh0s4(3IlAa3Nkke4n988+x>#z<>*_1H=>|8e*5vN~{4}W6f9){VP3BguUa; zf?V2m*Ri#6FXY_fORkJIK7U{%$;a(n)-B}51T zc)Wys#jnjaP{8<$09xua%HP+e1bRjr4UATK?ZbbN>7=WH=@ekZ!($-uSBl5J0NEt0 zmt{u#(C$Y##JH8edK5I7*ntJK3F{}q`mJODV{O@SQE?u4Ks;1WY~ zeEKsW@{-)pEKvP7=Uv84xdLfd_r>{H(sj^%)OHc{!fnd>Hi40 zZhob~h1p1;0gRg{r4wipE7gbm*xJA$t9u+b3e5!yz!zp>yZ=($9qTmvx+};Cz7wLO LZJ Date: Tue, 30 Apr 2024 17:58:32 +0800 Subject: [PATCH 029/219] New translations metadata.md (Chinese Simplified) --- .../dapps/asset-processing/metadata.md | 220 ++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/metadata.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/metadata.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/metadata.md new file mode 100644 index 0000000000..5e09887b14 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/metadata.md @@ -0,0 +1,220 @@ +# Metadata Parsing + +The metadata standard, which covers NFTs, NFT Collections, and Jettons, is outlined in TON Enhancement Proposal 64 [TEP-64](https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md). + +On TON, entities can have three types of metadata: on-chain, semi-chain, and off-chain. + +- **On-chain metadata:** stored inside the blockchain, including the name, attributes, and image. +- **Off-chain metadata:** stored using a link to a metadata file hosted outside of the chain. +- **Semi-chain metadata:** a hybrid between the two which allows for the storage of small fields such as names or attributes on the blockchain, while hosting the image off-chain and storing only a link to it. + +## Snake Data Encoding + +The Snake encoding format allows part of the data to be stored in a standardized cell, while the remaining portion is stored in a child cell (in a recursive manner). The Snake encoding format must be prefixed using the 0x00 byte. TL-B scheme: + +``` +tail#_ {bn:#} b:(bits bn) = SnakeData ~0; +cons#_ {bn:#} {n:#} b:(bits bn) next:^(SnakeData ~n) = SnakeData ~(n + 1); +``` + +The Snake format is used to store additional data in a cell when the data exceeds the maximum size that can be stored in a single cell. This is achieved by storing part of the data in the root cell and the remaining part in the first child cell, and continuing to do so recursively until all the data has been stored. + +Below is an example of Snake format encoding and decoding in TypeScript: + +```typescript +export function makeSnakeCell(data: Buffer): Cell { + const chunks = bufferToChunks(data, 127) + + if (chunks.length === 0) { + return beginCell().endCell() + } + + if (chunks.length === 1) { + return beginCell().storeBuffer(chunks[0]).endCell() + } + + let curCell = beginCell() + + for (let i = chunks.length - 1; i >= 0; i--) { + const chunk = chunks[i] + + curCell.storeBuffer(chunk) + + if (i - 1 >= 0) { + const nextCell = beginCell() + nextCell.storeRef(curCell) + curCell = nextCell + } + } + + return curCell.endCell() +} + +export function flattenSnakeCell(cell: Cell): Buffer { + let c: Cell | null = cell; + + const bitResult = new BitBuilder(); + while (c) { + const cs = c.beginParse(); + if (cs.remainingBits === 0) { + break; + } + + const data = cs.loadBits(cs.remainingBits); + bitResult.writeBits(data); + c = c.refs && c.refs[0]; + } + + const endBits = bitResult.build(); + const reader = new BitReader(endBits); + + return reader.loadBuffer(reader.remaining / 8); +} +``` + +It should be noted that the `0x00` byte prefix is not always required in the root cell when using the snake format, as is the case with off-chain NFT content. Additionally, cells are filled with bytes instead of bits to simplify parsing. To avoid the issue of adding a reference (within the next child cell) to a reference after it has already been written to its parent cell, the snake cell is constructed in reverse order. + +## Chunked Encoding + +The chunked encoding format is used to store data using a dictionary data structure as from the chunk_index to the chunk. Chunked encoding must be prefixed using the `0x01` byte. TL-B scheme: + +``` +chunked_data#_ data:(HashMapE 32 ^(SnakeData ~0)) = ChunkedData; +``` + +Below is an example of chunked data decoding using TypeScript: + +```typescript +interface ChunkDictValue { + content: Buffer; +} +export const ChunkDictValueSerializer = { + serialize(src: ChunkDictValue, builder: Builder) {}, + parse(src: Slice): ChunkDictValue { + const snake = flattenSnakeCell(src.loadRef()); + return { content: snake }; + }, +}; + +export function ParseChunkDict(cell: Slice): Buffer { + const dict = cell.loadDict( + Dictionary.Keys.Uint(32), + ChunkDictValueSerializer + ); + + let buf = Buffer.alloc(0); + for (const [_, v] of dict) { + buf = Buffer.concat([buf, v.content]); + } + return buf; +} +``` + +## NFT metadata attributes + +| Attribute | Type | Requirement | Description | +| ------------- | ------------ | ----------- | ------------------------------------------------------------------------------------------------------------------ | +| `uri` | ASCII string | optional | a URI pointing to the JSON document with metadata that is used by the "Semi-chain content layout." | +| `name` | UTF8 string | optional | identifies the asset | +| `description` | UTF8 string | optional | describes the asset | +| `image` | ASCII string | optional | a URI pointing to a resource with a mime type image | +| `image_data` | binary\* | optional | either a binary representation of the image for on-chain layout or base64 for off-chain layout | + +## Jetton metadata attributes + +1. `uri` - Optional. Used by "Semi-chain content layout". ASCII string. A URI pointing to JSON document with metadata. +2. `name` - Optional. UTF8 string. Identifies the asset. +3. `description` - Optional. UTF8 string. Describes the asset. +4. `image` - Optional. ASCII string. A URI pointing to a resource with mime type image. +5. `image_data` - Optional. Either binary representation of the image for onchain layout or base64 for offchain layout. +6. `symbol` - Optional. UTF8 string. The symbol of the token - e.g. "XMPL". Used in the form "You received 99 XMPL". +7. `decimals` - Optional. If not specified, 9 is used by default. UTF8 encoded string with number from 0 to 255. The number of decimals the token uses - e.g. 8, means to divide the token amount by 100000000 to get its user representation. +8. `amount_style` - Optional. Needed by external applications to understand which format for displaying the number of jettons. + +- "n" - number of jettons (default value). If the user has 100 tokens with decimals 0, then display that user has 100 tokens +- "n-of-total" - the number of jettons out of the total number of issued jettons. For example, totalSupply Jetton = 1000. A user has 100 jettons in the jetton wallet. For example must be displayed in the user's wallet as 100 of 1000 or in any other textual or graphical way to demonstrate the particular from the general. +- "%" - percentage of jettons from the total number of issued jettons. For example, totalSupply Jetton = 1000. A user has 100 jettons in the jetton wallet. For example it should be displayed in the user's wallet as 10%. + +9. `render_type` - Optional. Needed by external applications to understand which group the jetton belongs to and how to display it. + +- "currency" - display as currency (default value). +- "game" - display for games. It should be displayed as NFT, but at the same time display the number of jettons considering the `amount_style` + +| Attribute | Type | Requirement | Description | +| -------------- | ------------ | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `uri` | ASCII string | optional | a URI pointing to the JSON document with metadata that is used by the "Semi-chain content layout." | +| `name` | UTF8 string | optional | identifies the asset | +| `description` | UTF8 string | optional | describes the asset | +| `image` | ASCII string | optional | a URI pointing to a resource with a mime type image | +| `image_data` | binary\* | optional | either a binary representation of the image for on-chain layout or base64 for off-chain layout | +| `symbol` | UTF8 string | optional | the symbol of the token - e.g. "XMPL" and uses the form "You received 99 XMPL" | +| `decimals` | UTF8 string | optional | the number of decimals the token uses. If not specified, 9 is used by default. The UTF8 encoded string with the numbers between 0 to 255. - e.g. 8, means the token amount must be divided by 100000000 to get its user representation. | +| `amount_style` | | optional | needed by external applications to understand which format for displaying the number of jettons. Defines with _n_, _n-of-total_, _%_. | +| `render_type` | | optional | Needed by external applications to understand which group the jetton belongs to and how to display it. "currency" - displayed as a currency (default value)."game" - display used for games that is displayed as an NFT, but also displays the number of jettons and considers the amount_style value. | + +> `amount_style` parameters: + +- _n_ - number of jettons (default value). If the user has 100 tokens with 0 decimals, then it displays that the user has 100 tokens. +- _n-of-total_ - the number of jettons out of the total number of issued jettons. For example, if the totalSupply of Jettons is 1000 and a user has 100 jettons in their wallet, it must be displayed in the user's wallet as 100 of 1000 or in another textual or graphical way to demonstrate the ratio of user tokens to the the total amount of tokens available. +- _%_ - the percentage of jettons from the total number of jettons issued. For example, if the totalSupply of Jettons is 1000, if a user holds 100 jettons, the percentage should be displayed as 10% of the user’s wallet balance (100 ÷ 1000 = 0.1 or 10%). + +> `render_type` parameters: + +- _currency_ - displayed as a currency (default value). +- _game_ - display used for games that is displayed as an NFT, but also displays the number of jettons and considers the `amount_style value`. + +## Parsing Metadata + +To parse metadata, NFT data must first be obtained from the blockchain. To better understand this process, consider reading the [Getting NFT Data](/develop/dapps/asset-processing/nfts#getting-nft-data) section of our TON asset processing documentation section. + +After on-chain NFT data is retrieved, it must be parsed. To carry out this process, the NFT content type must be determined by reading the first byte that makes up the inner workings of the NFT. + +### Off-chain + +If the metadata byte string starts with `0x01` it signifies an off-chain NFT content type. The remaining portion of the NFT content is decoded using a Snake encoding format as an ASCII string. After the correct NFT URL is realized, and the NFT identification data is retrieved, the process is complete. Below is an example of a URL that makes use of off-chain NFT content metadata parsing: +`https://s.getgems.io/nft/b/c/62fba50217c3fe3cbaad9e7f/95/meta.json` + +URL contents (from directly above): + +```json +{ + "name": "TON Smart Challenge #2 Winners Trophy", + "description": "TON Smart Challenge #2 Winners Trophy 1 place out of 181", + "image": "https://s.getgems.io/nft/b/c/62fba50217c3fe3cbaad9e7f/images/943e994f91227c3fdbccbc6d8635bfaab256fbb4", + "content_url": "https://s.getgems.io/nft/b/c/62fba50217c3fe3cbaad9e7f/content/84f7f698b337de3bfd1bc4a8118cdfd8226bbadf", + "attributes": [] +} +``` + +### On-chain and Semi-chain + +If the metadata byte string starts with `0x00`, it indicates that the NFT either makes use of an on-chain or semi-chain format. + +The metadata for our NFT is stored in a dictionary where the key is the SHA256 hash of the attribute name and the value is the data stored in either the Snake or Chunked format. + +To determine what type of NFT is being used it is necessary for the developer to read known NFT attributes such as the `uri`, `name`, `image`, `description`, and `image_data`. If the `uri` field is present within the metadata, it indicates a semi-chain layout. In such cases, the off-chain content specified in the uri field should be downloaded and merged with the dictionary values. + +Example of an on-chain NFT: [EQBq5z4N_GeJyBdvNh4tPjMpSkA08p8vWyiAX6LNbr3aLjI0](https://getgems.io/collection/EQAVGhk_3rUA3ypZAZ1SkVGZIaDt7UdvwA4jsSGRKRo-MRDN/EQBq5z4N_GeJyBdvNh4tPjMpSkA08p8vWyiAX6LNbr3aLjI0) + +Example of a semi-chain NFT: [EQB2NJFK0H5OxJTgyQbej0fy5zuicZAXk2vFZEDrqbQ_n5YW](https://getgems.io/nft/EQB2NJFK0H5OxJTgyQbej0fy5zuicZAXk2vFZEDrqbQ_n5YW) + +Example of an on-chain Jetton Master: [EQA4pCk0yK-JCwFD4Nl5ZE4pmlg4DkK-1Ou4HAUQ6RObZNMi](https://tonscan.org/jetton/EQA4pCk0yK-JCwFD4Nl5ZE4pmlg4DkK-1Ou4HAUQ6RObZNMi) + +Example of an on-chain NFT parser: [stackblitz/ton-onchain-nft-parser](https://stackblitz.com/edit/ton-onchain-nft-parser?file=src%2Fmain.ts) + +## Important NFT Metadata Notes + +1. For NFT metadata, the `name`, `description`, and `image`(or `image_data`) fields are required to display the NFT. +2. For Jetton metadata, the `name`, `symbol`, `decimals` and `image`(or `image_data`) are primary. +3. It's important to be aware that anyone can create an NFT or Jetton using any `name`, `description`, or `image`. To avoid confusion and potential scams, users should always display their NFTs in a way that clearly distinguishes them from the other parts of their app. Malicious NFTs and Jettons can be sent to a user's wallet with misleading or false information. +4. Some items may have a `video` field, which links to video content associated with the NFT or Jetton. + +## References + +- [TON Enhancement Proposal 64 (TEP-64)](https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md) + +## See Also + +- [TON NFT processing](/develop/dapps/asset-processing/nfts) +- [TON Jetton processing](/develop/dapps/asset-processing/jettons) +- [Mint your first Jetton](/develop/dapps/tutorials/jetton-minter) From 43ba62c6e3e983871e6fa6062110b216268072dc Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:32 +0800 Subject: [PATCH 030/219] New translations nfts.md (Chinese Simplified) --- .../develop/dapps/asset-processing/nfts.md | 272 ++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/nfts.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/nfts.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/nfts.md new file mode 100644 index 0000000000..220a6f2bad --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/nfts.md @@ -0,0 +1,272 @@ +# NFT Processing + +## Overview + +In this section of our documentation we’ll provide readers with a better understanding of NFTs. This will teach the reader how to interact with NFTs, and how to accept NFTs via transactions sent on TON Blockchain. + +The information provided below assumes the reader has already taken a deep dive into our previous. +[section detailing Toncoin payment processing](/develop/dapps/asset-processing), while also assuming that they possess a basic understanding of how to interact with wallet smart contracts programmatically. + +## Understanding the Basics of NFTs + +NFTs operating on TON Blockchain are represented by the [TEP-62](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md) and [TEP-64](https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md) standards. + +The Open Network (TON) Blockchain is designed with high performance in mind and includes a feature that makes use of automatic sharding based on contract addresses on TON (which are used to help provision specific NFT designs). In order to achieve optimal performance, individual NFTs must make use of their own smart contract. This enables the creation of NFT collections of any size (whether large or small in number), while also reducing development costs and performance issues. However, this approach also introduces new considerations for the development of NFT collections. + +Because each NFT makes use of its own smart contract, it is not possible to obtain information about each individualized NFT within an NFT collection using a single contract. To retrieve information on an entire collection as a whole, as well as each individual NFT within a collection, it is necessary to query both the collection contract and each individual NFT contract individually. For the same reason, to track NFT transfers, it is necessary to track all transactions for each individualized NFT within a specific collection. + +### NFT Collections + +NFT Collection is a contract that serves to index and store NFT content and should contain the following interfaces: + +#### Get method `get_collection_data` + +``` +(int next_item_index, cell collection_content, slice owner_address) get_collection_data() +``` + +Retrieves general information about collection, which represented with the following: + +1. `next_item_index` - if the collection is ordered, this classification indicates the total number of NFTs in the collection, as well as the next index used for minting. For unordered collections, the `next_item_index` value is -1, meaning the collection uses unique mechanisms to keep track of NFTs (e.g., the hash of TON DNS domains). +2. `collection_content` - a cell that represents the collection content in TEP-64 compatible format. +3. `owner_address` - a slice that contains the collection owner's address (this value can also be empty). + +#### Get method `get_nft_address_by_index` + +``` +(slice nft_address) get_nft_address_by_index(int index) +``` + +This method can be used to verify the authenticity of an NFT and confirm whether it truly belongs to a specific collection. It also enables users to retrieve the address of an NFT by providing its index in the collection. The method should return a slice containing the address of the NFT that corresponds to the provided index. + +#### Get method `get_nft_content` + +``` +(cell full_content) get_nft_content(int index, cell individual_content) +``` + +Since the collection serves as a common data storage for NFTs, this method is necessary to complete the NFT content. To use this method, first, it’s necessary to obtain the NFT’s `individual_content` by calling the corresponding `get_nft_data()` method. After obtaining the `individual_content`, it’s possible to call the `get_nft_content()` method with the NFT index and the `individual_content` cell. The method should return a TEP-64 cell containing the full content of the NFT. + +### NFT Items + +Basic NFTs should implement: + +#### Get method `get_nft_data()` + +``` +(int init?, int index, slice collection_address, slice owner_address, cell individual_content) get_nft_data() +``` + +#### Inline message handler for `transfer` + +``` +transfer#5fcc3d14 query_id:uint64 new_owner:MsgAddress response_destination:MsgAddress custom_payload:(Maybe ^Cell) forward_amount:(VarUInteger 16) forward_payload:(Either Cell ^Cell) = InternalMsgBody +``` + +Let's look at each parameter you need to fill in your message: + +1. `OP` - `0x5fcc3d14` - a constant defined by the TEP-62 standard within the transfer message. +2. `queryId` - `uint64` - a uint64 number used to track the message. +3. `newOwnerAddress` - `MsgAddress` - the address of the contract used to transfer the NFT to. +4. `responseAddress` - `MsgAddress` - the address used to transfer excess funds to. Typically, an extra amount of TON (e.g., 1 TON) is sent to the NFT contract to ensure it has enough funds to pay transaction fees and create a new transfer if needed. All unused funds within the transaction are sent to the `responseAddress`. +5. `forwardAmount` - `Coins` - the amount of TON used in conjunction with the forward message (usually set to 0.01 TON). Since TON uses an asynchronous architecture, the new owner of the NFT will not be notified immediately upon successfully receiving the transaction. To notify the new owner, an internal message is sent from the NFT smart contract to the `newOwnerAddress` with a value denoted using the `forwardAmount`. The forward message will begin with the `ownership_assigned` OP (`0x05138d91`), followed by the previous owner's address and the `forwardPayload` (if present). +6. `forwardPayload` - `Slice | Cell` - is sent as a part of `ownership_assigned` notification message. + +This message (as explained above) is the primary way used to interact with an NFT that changes ownership after a notification is received as a result of the above message. + +For example, this message type above is often used to send a NFT Item Smart Contract from a Wallet Smart Contract. When an NFT Smart Contract receives this message and executes it, the NFT Contract's storage (inner contract data) is updated along with the Owner's ID. In this way, the NFT Item(contract) changes owners correctly. This process details a standard NFT Transfer + +In this case, the forward amount should be set to an appropriate value(0.01 TON for a regular wallet or more if you want to execute a contract by transferring an NFT), to ensure that the new owner receives a notification regarding the ownership transfer. This is important because the new owner won’t be notified that they have received the NFT without this notification. + +## Retrieving NFT Data + +Most SDKs make use of ready to use handlers for retrieving NFT data, including: [tonweb(js)](https://github.com/toncenter/tonweb/blob/b550969d960235314974008d2c04d3d4e5d1f546/src/contract/token/nft/NftItem.js#L38), [tonutils-go](https://github.com/xssnick/tonutils-go/blob/fb9b3fa7fcd734eee73e1a73ab0b76d2fb69bf04/ton/nft/item.go#L132), [pytonlib](https://github.com/toncenter/pytonlib/blob/d96276ec8a46546638cb939dea23612876a62881/pytonlib/client.py#L771), and others. + +To receive NFT data it is necessary to make use of the `get_nft_data()` retrieval mechanism. For example, we must verify the following NFT item address `EQB43-VCmf17O7YMd51fAvOjcMkCw46N_3JMCoegH_ZDo40e`(also known as [foundation.ton](https://tonscan.org/address/EQB43-VCmf17O7YMd51fAvOjcMkCw46N_3JMCoegH_ZDo40e) domain). + +First it is necessary to execute the get method by using the toncenter.com API as follows:. + +``` +curl -X 'POST' \ + 'https://toncenter.com/api/v2/runGetMethod' \ + -H 'accept: application/json' \ + -H 'Content-Type: application/json' \ + -d '{ + "address": "EQB43-VCmf17O7YMd51fAvOjcMkCw46N_3JMCoegH_ZDo40e", + "method": "get_nft_data", + "stack": [] +}' +``` + +The response is generally something similar to the following: + +```json +{ + "ok": true, + "result": { + "@type": "smc.runResult", + "gas_used": 1581, + "stack": [ + // init + [ "num", "-0x1" ], + // index + [ "num", "0x9c7d56cc115e7cf6c25e126bea77cbc3cb15d55106f2e397562591059963faa3" ], + // collection_address + [ "cell", { "bytes": "te6cckEBAQEAJAAAQ4AW7psr1kCofjDYDWbjVxFa4J78SsJhlfLDEm0U+hltmfDtDcL7" } ], + // owner_address + [ "cell", { "bytes": "te6cckEBAQEAJAAAQ4ATtS415xpeB1e+YRq/IsbVL8tFYPTNhzrjr5dcdgZdu5BlgvLe" } ], + // content + [ "cell", { "bytes": "te6cckEBCQEA7AABAwDAAQIBIAIDAUO/5NEvz/d9f/ZWh+aYYobkmY5/xar2cp73sULgTwvzeuvABAIBbgUGAER0c3/qevIyXwpbaQiTnJ1y+S20wMpSzKjOLEi7Jwi/GIVBAUG/I1EBQhz26hlqnwXCrTM5k2Qg5o03P1s9x0U4CBUQ7G4HAUG/LrgQbAsQe0P2KTvsDm8eA3Wr0ofDEIPQlYa5wXdpD/oIAEmf04AQe/qqXMblNo5fl5kYi9eYzSLgSrFtHY6k/DdIB0HmNQAQAEatAVFmGM9svpAE9og+dCyaLjylPtAuPjb0zvYqmO4eRJF0AIDBvlU=" } ] + ], + "exit_code": 0, + "@extra": "1679535187.3836682:8:0.06118075068995321" + } +} +``` + +Return parameters: + +- `init` - `boolean` - -1 means NFT is initialized and can be used +- `index` - `uint256` - index of the NFT in the collection. Can be sequential or derived in some other way. For example, this can denote an NFT doman hash used with TON DNS contracts, while collections should only have only one unique NFT within a given index. +- `collection_address` - `Cell` - a cell containing the NFT collection address (can be empty). +- `owner_address` - `Cell` - a cell containing the current owner’s NFT address (can be empty). +- `content` - `Cell` - a cell containing NFT item content (if parsing is needed it is necessary to consult the TEP-64 standard). + +## Retrieving all NFTs within a collection + +The process for retrieving all NFTs within a collection differs depending on whether the collection is ordered or not. Let’s outline both processes below. + +### Ordered collections + +Retrieving all NFTs in an ordered collection is relatively straightforward since the number of NFTs needed for retrieval is already known and their addresses can easily be easily obtained. To complete this process, the following steps should be followed in order: + +1. Invoke the `get_collection_data` method using the TonCenter API within the collection contract and retrieve the `next_item_index` value from the response. +2. Use the `get_nft_address_by_index` method, passing in the index value `i` (initially set to 0), to retrieve the address of the first NFT in the collection. +3. Retrieve the NFT Item data using the address obtained in the previous step. Next, verify that the initial NFT Collection smart contract coincides with the NFT Collection smart contract reported by the NFT item itself (to ensure the Collection didn't appropriate another user’s NFT smart contract). +4. Call the `get_nft_content` method with `i` and `individual_content` from the previous step. +5. Increase `i` by 1 and repeat steps 2-5 until `i` is equal to the `next_item_index`. +6. At this point, you will be in possession of the necessary information from the collection and its individual items. + +### Unordered collections + +Retrieving the list of NFTs in an unordered collection is more difficult because there is no inherent way to obtain the addresses of the NFTs that belong to the collection. Therefore, it is necessary to parse all transactions in the collection contract and check all outgoing messages to identify the ones that correspond to NFTs belonging to the collection. + +To do so, the NFT data must be retrieved, and the `get_nft_address_by_index` method is called in the collection with the ID returned by the NFT. If the NFT contract address and the address returned by the `get_nft_address_by_index` method match, it indicates that the NFT belongs to the current collection. However, parsing all messages to the collection can be a lengthy process and may require archive nodes. + +## Working with NFTs outside of TON + +### Sending NFTs + +To transfer NFT ownership it is necessary to send an internal message from the NFT owner’s wallet to the NFT contract by creating a cell that contains a transfer message. This can be accomplished using libraries (such as [tonweb(js)](https://github.com/toncenter/tonweb/blob/b550969d960235314974008d2c04d3d4e5d1f546/src/contract/token/nft/NftItem.js#L65), [ton(js)](https://github.com/getgems-io/nft-contracts/blob/debcd8516b91320fa9b23bff6636002d639e3f26/packages/contracts/nft-item/NftItem.data.ts#L102), [tonutils-go(go)](https://github.com/xssnick/tonutils-go/blob/fb9b3fa7fcd734eee73e1a73ab0b76d2fb69bf04/ton/nft/item.go#L132)) for the specific language. + +Once the transfer message has been created, it must be sent to the NFT item contract address from the owner's wallet contract, with an adequate amount of TON to cover the associated transaction fee. + +To transfer an NFT from another user to yourself, it is necessary to use TON Connect 2.0 or a simple QR code that contains a ton:// link. For example: +`ton://transfer/{nft_address}?amount={message_value}&bin={base_64_url(transfer_message)}` + +### Receiving NFTs + +The process of tracking NFTs sent to a certain smart contract address (i.e. a user's wallet) is similar to the mechanism used to track payments. This is completed by listening to all new transactions in your wallet and parsing them. + +The next steps may vary depending on the specific use case. Let’s examine several different scenarios below. + +#### Service waiting for known NFT address transfers: + +- Verify new transactions sent from the NFT item smart contract address. +- Read the first 32 bits of the message body as using the `uint` type and verify that it is equal to `op::ownership_assigned()`(`0x05138d91`) +- Read the next 64 bits from the message body as the `query_id`. +- Read the address from the message body as the `prev_owner_address`. +- It is now possible to manage your new NFT. + +#### Service listening to all types of NFT transfer: + +- Check all new transactions and ignore those with a body length less than 363 bits (OP - 32, QueryID - 64, Address - 267). +- Repeat the steps detailed in the previous list above. +- If the process is working correctly, it is necessary to verify the authenticity of the NFT by parsing it and the collection it belongs to. Next, it is necessary to ensure that the NFT belongs to the specified collection. More information detailing this process can be found in the `Getting all collection NFTs` section. This process can be simplified by using a whitelist of NFTs or collections. +- It is now possible to manage your new NFT. + +#### Tying NFT transfers to internal transactions: + +When a transaction of this type is received, it's necessary to repeat the steps from the previous list. Once this process is completed, it is possible to retrieve the `RANDOM_ID` parameter by reading a uint32 from the message body after reading the `prev_owner_address` value. + +#### NFTs sent without a notification message: + +All of the strategies outlined above rely on the services correctly creating a forward message with the NFT transfer. If they don't do this, we won't know that they transferred the NFT to us. However, there are a few workarounds: + +All of the strategies outlined above rely on the service correctly creating a forward message within the NFT transfer. If this process is not carried out, it won’t be clear whether the NFT was transferred to the correct party. However, there are a several workarounds that are possible in this scenario: + +- If a small number of NFTs is expected, it is possible to periodically parse them and verify if the owner has changed to the corresponding contract type. +- If a large number of NFTs is expected, it is possible to parse all new blocks and verify if there were any calls sent to the NFT destination using the `op::transfer` method. If a transaction like this is initiated, it is possible to verify the NFT owner and receive the transfer. +- If it's not possible to parse new blocks within the transfer, it is possible for users to trigger NFT ownership verification processes themselves. This way, it is possible to trigger the NFT ownership verification process after transferring an NFT without a notification. + +## Interacting with NFTs from smart contracts + +Now that we’ve covered the basics of sending and receiving NFTs, let’s explore how to receive and transfer NFTs from smart contracts using the [NFT Sale](https://github.com/ton-blockchain/token-contract/blob/1ad314a98d20b41241d5329e1786fc894ad811de/nft/nft-sale.fc) contract example. + +### Sending NFTs + +In this example, the NFT transfer message is found on [line 67](https://www.google.com/url?q=https://github.com/ton-blockchain/token-contract/blob/1ad314a98d20b41241d5329e1786fc894ad811de/nft/nft-sale.fc%23L67\&sa=D\&source=docs\&ust=1685436161341866\&usg=AOvVaw1yuoIzcbEuvqMS4xQMqfXE): + +``` +var nft_msg = begin_cell() + .store_uint(0x18, 6) + .store_slice(nft_address) + .store_coins(0) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) + .store_uint(op::transfer(), 32) + .store_uint(query_id, 64) + .store_slice(sender_address) ;; new_owner_address + .store_slice(sender_address) ;; response_address + .store_int(0, 1) ;; empty custom_payload + .store_coins(0) ;; forward amount to new_owner_address + .store_int(0, 1); ;; empty forward_payload + + +send_raw_message(nft_msg.end_cell(), 128 + 32); +``` + +Let's examine each line of code: + +- `store_uint(0x18, 6)` - stores message flags. +- `store_slice(nft_address)` - stores message destinations (NFT addresses). +- `store_coins(0)` - the amount of TON to send with the message is set to 0 because the `128` [message mode](/develop/smart-contracts/messages#message-modes) is used to send the message with its remaining balance. To send an amount other than the user’s entire balance, the number must be changed. Note that it should be large enough to pay for gas fees as well as any forwarding amount. +- `store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)` - the remaining components that make up the message header are left empty. +- `store_uint(op::transfer(), 32)` - this is the start of the msg_body. Here we start by using the transfer OP code so the receiver understands its transfer ownership message. +- `store_uint(query_id, 64)` - store query_id +- `store_slice(sender_address) ;; new_owner_address` - the first stored address is the address used for transferring NFTs and sending notifications. +- `store_slice(sender_address) ;; response_address` - the second stored address is a response address. +- `store_int(0, 1)` - the custom payload flag is set to 0, indicating there is no custom payload required. +- `store_coins(0)` - amount of TON to be forwarded with the message. In this example it’s set to 0, however, it is recommended to set this value to a higher amount (such as at least 0.01 TON) in order to create a forward message and notify the new owner that they have received the NFT. The amount should be sufficient to cover any associated fees and costs. +- `.store_int(0, 1)` - custom payload flag. It's necessary to set up to `1` if your service should pass payload as a ref. + +### Receiving NFTs + +Once we've sent the NFT, it is critical to determine when it has been received by the new owner. A good example of how to do this can be found in the same NFT sale smart contract: + +``` +slice cs = in_msg_full.begin_parse(); +int flags = cs~load_uint(4); + +if (flags & 1) { ;; ignore all bounced messages + return (); +} +slice sender_address = cs~load_msg_addr(); +throw_unless(500, equal_slices(sender_address, nft_address)); +int op = in_msg_body~load_uint(32); +throw_unless(501, op == op::ownership_assigned()); +int query_id = in_msg_body~load_uint(64); +slice prev_owner_address = in_msg_body~load_msg_addr(); +``` + +Let's again examine each line of code: + +- `slice cs = in_msg_full.begin_parse();` - used to parse the incoming message. +- `int flags = cs~load_uint(4);` - used to load flags from the first 4 bits of the message. +- `if (flags & 1) { return (); } ;; ignore all bounced messages` - used to verify that the message has not bounced. It’s important to carry out this process for all your incoming messages if there is no reason to do otherwise. Bounced messages are messages that encountered errors while trying to receive a transaction and were returned to the sender. +- `slice sender_address = cs~load_msg_addr();` - next the message sender is loaded. In this case specifically by using an NFT address. +- `throw_unless(500, equal_slices(sender_address, nft_address));` - used to verify that the sender is indeed an NFT that should have been transferred via a contract. It's quite difficult to parse NFT data from smart contracts, so in most cases the NFT address is predefined at contract creation. +- `int op = in_msg_body~load_uint(32);` - loads message OP code. +- `throw_unless(501, op == op::ownership_assigned());` - ensures that the received OP code matches the ownership assigned constant value. +- `slice prev_owner_address = in_msg_body~load_msg_addr();` - previous owner address that is extracted from the incoming message body and loaded into the `prev_owner_address` slice variable. This can be useful if the previous owner chooses to cancel the contract and have the NFT returned to them. + +Now that we have successfully parsed and validated the notification message, we can proceed with our business logic which is used to initiate a sale smart contract (which serves to handle NFT Item business sale processes for NFT Auctions, such as getgems.io) From 9981cd6a25d15ecdc7926aa0b901dc4d6b7852d2 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:33 +0800 Subject: [PATCH 031/219] New translations cookbook.md (Chinese Simplified) --- .../current/develop/dapps/cookbook.md | 1887 +++++++++++++++++ 1 file changed, 1887 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/cookbook.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/cookbook.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/cookbook.md new file mode 100644 index 0000000000..f51bf21ce2 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/cookbook.md @@ -0,0 +1,1887 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# TON Cookbook + +During product development, various questions often arise regarding interactions with different contracts on TON. + +This document is created to gather the best practices from all developers and share them with everyone. + +## Standard operations + + + +Full ecosystem scheme + +## Working with contracts' addresses + +### How to convert (user friendly <-> raw), assemble, and extract addresses from strings? + +TON address uniquely identifies contract in blockchain, indicating its workchain and original state hash. [Two common formats](/learn/overviews/addresses#raw-and-user-friendly-addresses) are used: **raw** (workchain and HEX-encoded hash separated with ":" character) and **user-friendly** (base64-encoded with certain flags). + +``` +User-friendly: EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF +Raw: 0:ca6e321c7cce9ecedf0a8ca2492ec8592494aa5fb5ce0387dff96ef6af982a3e +``` + +To obtain an address object from a string in your SDK, you can use the following code: + + + + +```js +import { Address } from "@ton/core"; + + +const address1 = Address.parse('EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF'); +const address2 = Address.parse('0:ca6e321c7cce9ecedf0a8ca2492ec8592494aa5fb5ce0387dff96ef6af982a3e'); + +// toStrings arguments: urlSafe, bounceable, testOnly +// defaults values: true, true, false + +console.log(address1.toString()); // EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF +console.log(address1.toRawString()); // 0:ca6e321c7cce9ecedf0a8ca2492ec8592494aa5fb5ce0387dff96ef6af982a3e + +console.log(address2.toString()); // EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF +console.log(address2.toRawString()); // 0:ca6e321c7cce9ecedf0a8ca2492ec8592494aa5fb5ce0387dff96ef6af982a3e +``` + + + + +```js +const TonWeb = require('tonweb'); + +const address1 = new TonWeb.utils.Address('EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF'); +const address2 = new TonWeb.utils.Address('0:ca6e321c7cce9ecedf0a8ca2492ec8592494aa5fb5ce0387dff96ef6af982a3e'); + +// toString arguments: isUserFriendly, isUrlSafe, isBounceable, isTestOnly + +console.log(address1.toString(true, true, true)); // EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF +console.log(address1.toString(isUserFriendly = false)); // 0:ca6e321c7cce9ecedf0a8ca2492ec8592494aa5fb5ce0387dff96ef6af982a3e + +console.log(address1.toString(true, true, true)); // EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF +console.log(address2.toString(isUserFriendly = false)); // 0:ca6e321c7cce9ecedf0a8ca2492ec8592494aa5fb5ce0387dff96ef6af982a3e +``` + + + + +```go +package main + +import ( + "fmt" + "github.com/xssnick/tonutils-go/address" +) + +// Here, we will need to manually implement the handling of raw addresses since they are not supported by the library. + +func main() { + address1 := address.MustParseAddr("EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF") + address2 := mustParseRawAddr("0:ca6e321c7cce9ecedf0a8ca2492ec8592494aa5fb5ce0387dff96ef6af982a3e", true, false) + + fmt.Println(address1.String()) // EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF + fmt.Println(printRawAddr(address1)) // 0:ca6e321c7cce9ecedf0a8ca2492ec8592494aa5fb5ce0387dff96ef6af982a3e + + fmt.Println(address2.String()) // EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF + fmt.Println(printRawAddr(address2)) // 0:ca6e321c7cce9ecedf0a8ca2492ec8592494aa5fb5ce0387dff96ef6af982a3e +} + +func mustParseRawAddr(s string, bounceable bool, testnet bool) *address.Address { + addr, err := parseRawAddr(s, bounceable, testnet) + if err != nil { + panic(err) + } + return addr +} + +func parseRawAddr(s string, bounceable bool, testnet bool) (*address.Address, error) { + var ( + workchain int32 + data []byte + ) + _, err := fmt.Sscanf(s, "%d:%x", &workchain, &data) + if err != nil { + return nil, err + } + if len(data) != 32 { + return nil, fmt.Errorf("address len must be 32 bytes") + } + + var flags byte = 0b00010001 + if !bounceable { + setBit(&flags, 6) + } + if testnet { + setBit(&flags, 7) + } + + return address.NewAddress(flags, byte(workchain), data), nil +} + +func printRawAddr(addr *address.Address) string { + return fmt.Sprintf("%v:%x", addr.Workchain, addr.Data()) +} + +func setBit(n *byte, pos uint) { + *n |= 1 << pos +} +``` + + + + +```py +from pytoniq_core import Address + +address1 = Address('EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF') +address2 = Address('0:ca6e321c7cce9ecedf0a8ca2492ec8592494aa5fb5ce0387dff96ef6af982a3e') + +# to_str() arguments: is_user_friendly, is_url_safe, is_bounceable, is_test_only + +print(address1.to_str(is_user_friendly=True, is_bounceable=True, is_url_safe=True)) # EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF +print(address1.to_str(is_user_friendly=False)) # 0:ca6e321c7cce9ecedf0a8ca2492ec8592494aa5fb5ce0387dff96ef6af982a3e + +print(address2.to_str(is_user_friendly=True, is_bounceable=True, is_url_safe=True)) # EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF +print(address2.to_str(is_user_friendly=False)) # 0:ca6e321c7cce9ecedf0a8ca2492ec8592494aa5fb5ce0387dff96ef6af982a3e +``` + + + + +### What flags are there in user-friendly addresses? + +Two flags are defined: **bounceable**/**non-bounceable** and **testnet**/**any-net**. They can be easily detected by looking at the first letter of the address, because it stands for first 6 bits in address encoding, and flags are located there according to [TEP-2](https://github.com/ton-blockchain/TEPs/blob/master/text/0002-address.md#smart-contract-addresses): + +| Address beginning | Binary form | Bounceable | Testnet-only | +| :--------------------------------------------------: | :-----------------------: | :--------: | :----------: | +| E... | 000100.01 | yes | no | +| U... | 010100.01 | no | no | +| k... | 100100.01 | yes | yes | +| 0... | 110100.01 | no | yes | + +:::tip +Testnet-only flag doesn't have representation in blockchain at all. Non-bounceable flag makes difference only when used as destination address for a transfer: in this case, it [disallows bounce](/develop/smart-contracts/guidelines/non-bouncable-messages) for a message sent; address in blockchain, again, does not contain this flag. +::: + +Also, in some libraries, you may notice a serialization parameter called `urlSafe`. The thing is, the base64 format is not URL safe, which means that some of characters (namely, `+` and `/`) can cause issues when transmitting address in a link. When `urlSafe = true`, all `+` symbols are replaced with `-`, and all `/` symbols are replaced with `_`. You can obtain these address formats using the following code: + + + + +```js +import { Address } from "@ton/core"; + +const address = Address.parse('EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF'); + +// toStrings arguments: urlSafe, bounceable, testOnly +// defaults values: true, true, false + +console.log(address.toString()); // EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHFэ +console.log(address.toString({urlSafe: false})) // EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff+W72r5gqPrHF +console.log(address.toString({bounceable: false})) // UQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPuwA +console.log(address.toString({testOnly: true})) // kQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPgpP +console.log(address.toString({bounceable: false, testOnly: true})) // 0QDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPleK +``` + + + + +```js +const TonWeb = require('tonweb'); + +const address = new TonWeb.utils.Address('EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF'); + +// toString arguments: isUserFriendly, isUrlSafe, isBounceable, isTestOnly + +console.log(address.toString(true, true, true, false)); // EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF +console.log(address.toString(true, false, true, false)); // EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff+W72r5gqPrHF +console.log(address.toString(true, true, false, false)); // UQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPuwA +console.log(address.toString(true, true, true, true)); // kQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPgpP +console.log(address.toString(true, true, false, true)); // 0QDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPleK +``` + + + + +```go +package main + +import ( + "fmt" + "github.com/xssnick/tonutils-go/address" +) + +func main() { + address := address.MustParseAddr("EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF") + + fmt.Println(address.String()) // EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF + address.SetBounce(false) + fmt.Println(address.String()) // UQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPuwA + address.SetBounce(true) + address.SetTestnetOnly(true) // kQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPgpP + fmt.Println(address.String()) + address.SetBounce(false) // 0QDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPleK + fmt.Println(address.String()) +} +``` + + + + +```py +from pytoniq_core import Address + +address = Address('EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF') + +# to_str() arguments: is_user_friendly, is_url_safe, is_bounceable, is_test_only + +print(address.to_str(is_user_friendly=True, is_bounceable=True, is_url_safe=True, is_test_only=False)) # EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF +print(address.to_str(is_user_friendly=True, is_bounceable=True, is_url_safe=False, is_test_only=False)) # EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff+W72r5gqPrHF +print(address.to_str(is_user_friendly=True, is_bounceable=False, is_url_safe=True, is_test_only=False)) # UQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPuwA +print(address.to_str(is_user_friendly=True, is_bounceable=True, is_url_safe=True, is_test_only=True)) # kQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPgpP +print(address.to_str(is_user_friendly=True, is_bounceable=False, is_url_safe=True, is_test_only=True)) # 0QDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPleK +``` + + + + +### How to Check the Validity of a TON Wallet Address? + + + + + +```js + +const TonWeb = require("tonweb") + +TonWeb.utils.Address.isValid('...') +``` + + + + +```python +package main + +import ( + "fmt" + "github.com/xssnick/tonutils-go/address" +) + +if _, err := address.ParseAddr("EQCD39VS5j...HUn4bpAOg8xqB2N"); err != nil { + return errors.New("invalid address") +} +``` + + + + +```javascript +try { + Address.of("..."); +} catch (e) { + // not valid address +} +``` + + + + +```javascript +try { + AddrStd("...") +} catch(e: IllegalArgumentException) { + // not valid address +} +``` + + + + +## Standard wallets in TON ecosystem + +### How to transfer TON? How to send a text message to other wallet? + +Wallet operations scheme + +Most SDKs provide the following process for sending messages from your wallet: + +- You create wallet wrapper (object in your program) of a correct version (in most cases, v3r2; see also [wallet versions](/participate/wallets/contracts)), using secret key and workchain (usually 0, which stands for [basechain](/learn/overviews/ton-blockchain#workchain-blockchain-with-your-own-rules)). +- You also create blockchain wrapper, or "client" - object that will route requests to API or liteservers, whichever you choose. +- Then, you _open_ contract in the blockchain wrapper. This means contract object is no longer abstract and represents actual account in either TON mainnet or testnet. +- After that, you can form messages you want and send them. You can also send up to 4 messages per request, as described in an [advanced manual](/develop/smart-contracts/tutorials/wallet#sending-multiple-messages-simultaneously). + + + + +```js +import { TonClient, WalletContractV4, internal } from "@ton/ton"; +import { mnemonicNew, mnemonicToPrivateKey } from "@ton/crypto"; + +const client = new TonClient({ + endpoint: 'https://testnet.toncenter.com/api/v2/jsonRPC', +}); + +// Convert mnemonics to private key +let mnemonics = "word1 word2 ...".split(" "); +let keyPair = await mnemonicToPrivateKey(mnemonics); + +// Create wallet contract +let workchain = 0; // Usually you need a workchain 0 +let wallet = WalletContractV4.create({ workchain, publicKey: keyPair.publicKey }); +let contract = client.open(wallet); + +// Create a transfer +let seqno: number = await contract.getSeqno(); +await contract.sendTransfer({ + seqno, + secretKey: keyPair.secretKey, + messages: [internal({ + value: '1', + to: 'EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N', + body: 'Example transfer body', + })] +}); +``` + + + + + +```kotlin +// Setup liteClient +val context: CoroutineContext = Dispatchers.Default +val json = Json { ignoreUnknownKeys = true } +val config = json.decodeFromString( + URI("https://ton.org/global-config.json").toURL().readText() +) +val liteClient = LiteClient(context, config) + +val WALLET_MNEMONIC = "word1 word2 ...".split(" ") + +val pk = PrivateKeyEd25519(Mnemonic.toSeed(WALLET_MNEMONIC)) +val walletAddress = WalletV3R2Contract.address(pk, 0) +println(walletAddress.toString(userFriendly = true, bounceable = false)) + +val wallet = WalletV3R2Contract(liteClient, walletAddress) +runBlocking { + wallet.transfer(pk, WalletTransfer { + destination = AddrStd("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N") + bounceable = true + coins = Coins(100000000) // 1 ton in nanotons + messageData = org.ton.contract.wallet.MessageData.raw( + body = buildCell { + storeUInt(0, 32) + storeBytes("Comment".toByteArray()) + } + ) + sendMode = 0 + }) +} +``` + + + + + +```py +from pytoniq import LiteBalancer, WalletV4R2 +import asyncio + +mnemonics = ["your", "mnemonics", "here"] + +async def main(): + provider = LiteBalancer.from_mainnet_config(1) + await provider.start_up() + + wallet = await WalletV4R2.from_mnemonic(provider=provider, mnemonics=mnemonics) + + transfer = { + "destination": "DESTINATION ADDRESS HERE", # please remember about bounceable flags + "amount": int(10**9 * 0.05), # amount sent, in nanoTON + "body": "Example transfer body", # may contain a cell; see next examples + } + + await wallet.transfer(**transfer) + await client.close_all() + +asyncio.run(main()) +``` + + + + + +### Writing comments: long strings in snake format + +Some times it's necessary to store long strings (or other large information) while cells can hold **maximum 1023 bits**. In this case, we can use snake cells. Snake cells are cells that contain a reference to another cell, which, in turn, contains a reference to another cell, and so on. + + + + +```js +const TonWeb = require("tonweb"); + +function writeStringTail(str, cell) { + const bytes = Math.floor(cell.bits.getFreeBits() / 8); // 1 symbol = 8 bits + if(bytes < str.length) { // if we can't write all string + cell.bits.writeString(str.substring(0, bytes)); // write part of string + const newCell = writeStringTail(str.substring(bytes), new TonWeb.boc.Cell()); // create new cell + cell.refs.push(newCell); // add new cell to current cell's refs + } else { + cell.bits.writeString(str); // write all string + } + + return cell; +} + +function readStringTail(slice) { + const str = new TextDecoder('ascii').decode(slice.array); // decode uint8array to string + if (cell.refs.length > 0) { + return str + readStringTail(cell.refs[0].beginParse()); // read next cell + } else { + return str; + } +} + +let cell = new TonWeb.boc.Cell(); +const str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In euismod, ligula vel lobortis hendrerit, lectus sem efficitur enim, vel efficitur nibh dui a elit. Quisque augue nisi, vulputate vitae mauris sit amet, iaculis lobortis nisi. Aenean molestie ultrices massa eu fermentum. Cras rhoncus ipsum mauris, et egestas nibh interdum in. Maecenas ante ipsum, sodales eget suscipit at, placerat ut turpis. Nunc ac finibus dui. Donec sit amet leo id augue tempus aliquet. Vestibulum eu aliquam ex, sit amet suscipit odio. Vestibulum et arcu dui."; +cell = writeStringTail(str, cell); +const text = readStringTail(cell.beginParse()); +console.log(text); +``` + + + + +Many SDKs already have functions responsible for parsing and storing long strings. In others, you can work with such cells using recursion, or possibly optimize it out (trick known as "tail calls"). + +Don't forget that comment message has 32 zero bits (one may say, that its opcode is 0)! + +## TEP-74 (Jettons Standard) + +Jetton operations scheme + +### How to calculate user's Jetton wallet address (offchain)? + +To calculate the user's jetton wallet address, we need to call the "get_wallet_address" get-method of the jetton master contract with user address actually. For this task we can easily use getWalletAddress method from JettonMaster or call master contract by ourselves. + +:::info +`JettonMaster` in `@ton/ton` lacks much functionality but has _this one_ present, fortunately. +::: + + + + +```js +const { Address, beginCell } = require("@ton/core") +const { TonClient, JettonMaster } = require("@ton/ton") + +const client = new TonClient({ + endpoint: 'https://toncenter.com/api/v2/jsonRPC', +}); + +const jettonMasterAddress = Address.parse('...') // for example EQBlqsm144Dq6SjbPI4jjZvA1hqTIP3CvHovbIfW_t-SCALE +const userAddress = Address.parse('...') + +const jettonMaster = client.open(JettonMaster.create(jettonMasterAddress)) +console.log(await jettonMaster.getWalletAddress(userAddress)) +``` + + + + + +```js +const { Address, beginCell } = require("@ton/core") +const { TonClient } = require("@ton/ton") + +async function getUserWalletAddress(userAddress, jettonMasterAddress) { + const client = new TonClient({ + endpoint: 'https://toncenter.com/api/v2/jsonRPC', + }); + const userAddressCell = beginCell().storeAddress(userAddress).endCell() + const response = await client.runMethod(jettonMasterAddress, "get_wallet_address", [ + {type: "slice", cell: userAddressCell} + ]) + return response.stack.readAddress() +} +const jettonMasterAddress = Address.parse('...') // for example EQBlqsm144Dq6SjbPI4jjZvA1hqTIP3CvHovbIfW_t-SCALE +const userAddress = Address.parse('...') + +getUserWalletAddress(userAddress, jettonMasterAddress) + .then((jettonWalletAddress) => {console.log(jettonWalletAddress)}) +``` + + + + + +```kotlin +// Setup liteClient +val context: CoroutineContext = Dispatchers.Default +val json = Json { ignoreUnknownKeys = true } +val config = json.decodeFromString( + URI("https://ton.org/global-config.json").toURL().readText() +) +val liteClient = LiteClient(context, config) + +val USER_ADDR = AddrStd("Wallet address") +val JETTON_MASTER = AddrStd("Jetton Master contract address") // for example EQBlqsm144Dq6SjbPI4jjZvA1hqTIP3CvHovbIfW_t-SCALE + +// we need to send regular wallet address as a slice +val userAddressSlice = CellBuilder.beginCell() + .storeUInt(4, 3) + .storeInt(USER_ADDR.workchainId, 8) + .storeBits(USER_ADDR.address) + .endCell() + .beginParse() + +val response = runBlocking { + liteClient.runSmcMethod( + LiteServerAccountId(JETTON_MASTER.workchainId, JETTON_MASTER.address), + "get_wallet_address", + VmStackValue.of(userAddressSlice) + ) +} + +val stack = response.toMutableVmStack() +val jettonWalletAddress = stack.popSlice().loadTlb(MsgAddressInt) as AddrStd +println("Calculated Jetton wallet:") +println(jettonWalletAddress.toString(userFriendly = true)) + +``` + + + + + +```py +from pytoniq import LiteBalancer, begin_cell +import asyncio + +async def main(): + provider = LiteBalancer.from_mainnet_config(1) + await provider.start_up() + + JETTON_MASTER_ADDRESS = "EQBlqsm144Dq6SjbPI4jjZvA1hqTIP3CvHovbIfW_t-SCALE" + USER_ADDRESS = "EQAsl59qOy9C2XL5452lGbHU9bI3l4lhRaopeNZ82NRK8nlA" + + + result_stack = await provider.run_get_method(address=JETTON_MASTER_ADDRESS, method="get_wallet_address", + stack=[begin_cell().store_address(USER_ADDRESS).end_cell().begin_parse()]) + jetton_wallet = result_stack[0].load_address() + print(f"Jetton wallet address for {USER_ADDRESS}: {jetton_wallet.to_str(1, 1, 1)}") + await provider.close_all() + +asyncio.run(main()) +``` + + + + + +### How to calculate user's Jetton wallet address (offline)? + +Calling the GET method every time to get the wallet address can take a lot of time and resources. If we know the Jetton Wallet code and its storage structure in advance, we can get the wallet address without any network requests. + +You can get the code using Tonviewer. Let's take `jUSDT` as an example, the Jetton Master address is `EQBynBO23ywHy_CgarY9NK9FTz0yDsG82PtcbSTQgGoXwiuA`. If we [go to this address](https://tonviewer.com/EQBynBO23ywHy_CgarY9NK9FTz0yDsG82PtcbSTQgGoXwiuA?section=method) and open the Methods tab, we can see that there is already a `get_jetton_data` method there. By calling it, we can get the hex form of the cell with the Jetton Wallet code: + +``` +b5ee9c7201021301000385000114ff00f4a413f4bcf2c80b0102016202030202cb0405001ba0f605da89a1f401f481f481a9a30201ce06070201580a0b02f70831c02497c138007434c0c05c6c2544d7c0fc07783e903e900c7e800c5c75c87e800c7e800c1cea6d0000b4c7c076cf16cc8d0d0d09208403e29fa96ea68c1b088d978c4408fc06b809208405e351466ea6cc1b08978c840910c03c06f80dd6cda0841657c1ef2ea7c09c6c3cb4b01408eebcb8b1807c073817c160080900113e910c30003cb85360005c804ff833206e953080b1f833de206ef2d29ad0d30731d3ffd3fff404d307d430d0fa00fa00fa00fa00fa00fa00300008840ff2f00201580c0d020148111201f70174cfc0407e803e90087c007b51343e803e903e903534544da8548b31c17cb8b04ab0bffcb8b0950d109c150804d50500f214013e809633c58073c5b33248b232c044bd003d0032c032481c007e401d3232c084b281f2fff274013e903d010c7e800835d270803cb8b13220060072c15401f3c59c3e809dc072dae00e02f33b51343e803e903e90353442b4cfc0407e80145468017e903e9014d771c1551cdbdc150804d50500f214013e809633c58073c5b33248b232c044bd003d0032c0325c007e401d3232c084b281f2fff2741403f1c147ac7cb8b0c33e801472a84a6d8206685401e8062849a49b1578c34975c2c070c00870802c200f1000aa13ccc88210178d4519580a02cb1fcb3f5007fa0222cf165006cf1625fa025003cf16c95005cc2391729171e25007a813a008aa005004a017a014bcf2e2c501c98040fb004300c85004fa0258cf1601cf16ccc9ed5400725269a018a1c882107362d09c2902cb1fcb3f5007fa025004cf165007cf16c9c8801001cb0527cf165004fa027101cb6a13ccc971fb0050421300748e23c8801001cb055006cf165005fa027001cb6a8210d53276db580502cb1fcb3fc972fb00925b33e24003c85004fa0258cf1601cf16ccc9ed5400eb3b51343e803e903e9035344174cfc0407e800870803cb8b0be903d01007434e7f440745458a8549631c17cb8b049b0bffcb8b0b220841ef765f7960100b2c7f2cfc07e8088f3c58073c584f2e7f27220060072c148f3c59c3e809c4072dab33260103ec01004f214013e809633c58073c5b3327b55200087200835c87b51343e803e903e9035344134c7c06103c8608405e351466e80a0841ef765f7ae84ac7cbd34cfc04c3e800c04e81408f214013e809633c58073c5b3327b5520 +``` + +Now, knowing the Jetton Wallet code, the Jetton Master address and the vault structure, we can manually calculate the wallet address: + + + + +```js +import { Address, Cell, beginCell, storeStateInit } from '@ton/core'; + +const JETTON_WALLET_CODE = Cell.fromBoc(Buffer.from('b5ee9c7201021301000385000114ff00f4a413f4bcf2c80b0102016202030202cb0405001ba0f605da89a1f401f481f481a9a30201ce06070201580a0b02f70831c02497c138007434c0c05c6c2544d7c0fc07783e903e900c7e800c5c75c87e800c7e800c1cea6d0000b4c7c076cf16cc8d0d0d09208403e29fa96ea68c1b088d978c4408fc06b809208405e351466ea6cc1b08978c840910c03c06f80dd6cda0841657c1ef2ea7c09c6c3cb4b01408eebcb8b1807c073817c160080900113e910c30003cb85360005c804ff833206e953080b1f833de206ef2d29ad0d30731d3ffd3fff404d307d430d0fa00fa00fa00fa00fa00fa00300008840ff2f00201580c0d020148111201f70174cfc0407e803e90087c007b51343e803e903e903534544da8548b31c17cb8b04ab0bffcb8b0950d109c150804d50500f214013e809633c58073c5b33248b232c044bd003d0032c032481c007e401d3232c084b281f2fff274013e903d010c7e800835d270803cb8b13220060072c15401f3c59c3e809dc072dae00e02f33b51343e803e903e90353442b4cfc0407e80145468017e903e9014d771c1551cdbdc150804d50500f214013e809633c58073c5b33248b232c044bd003d0032c0325c007e401d3232c084b281f2fff2741403f1c147ac7cb8b0c33e801472a84a6d8206685401e8062849a49b1578c34975c2c070c00870802c200f1000aa13ccc88210178d4519580a02cb1fcb3f5007fa0222cf165006cf1625fa025003cf16c95005cc2391729171e25007a813a008aa005004a017a014bcf2e2c501c98040fb004300c85004fa0258cf1601cf16ccc9ed5400725269a018a1c882107362d09c2902cb1fcb3f5007fa025004cf165007cf16c9c8801001cb0527cf165004fa027101cb6a13ccc971fb0050421300748e23c8801001cb055006cf165005fa027001cb6a8210d53276db580502cb1fcb3fc972fb00925b33e24003c85004fa0258cf1601cf16ccc9ed5400eb3b51343e803e903e9035344174cfc0407e800870803cb8b0be903d01007434e7f440745458a8549631c17cb8b049b0bffcb8b0b220841ef765f7960100b2c7f2cfc07e8088f3c58073c584f2e7f27220060072c148f3c59c3e809c4072dab33260103ec01004f214013e809633c58073c5b3327b55200087200835c87b51343e803e903e9035344134c7c06103c8608405e351466e80a0841ef765f7ae84ac7cbd34cfc04c3e800c04e81408f214013e809633c58073c5b3327b5520', 'hex'))[0]; +const JETTON_MASTER_ADDRESS = Address.parse('EQBynBO23ywHy_CgarY9NK9FTz0yDsG82PtcbSTQgGoXwiuA'); +const USER_ADDRESS = Address.parse('UQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPuwA'); + +const jettonWalletStateInit = beginCell().store(storeStateInit({ + code: JETTON_WALLET_CODE, + data: beginCell() + .storeCoins(0) + .storeAddress(USER_ADDRESS) + .storeAddress(JETTON_MASTER_ADDRESS) + .storeRef(JETTON_WALLET_CODE) + .endCell() +})) +.endCell(); +const userJettonWalletAddress = new Address(0, jettonWalletStateInit.hash()); + +console.log('User Jetton Wallet address:', userJettonWalletAddress.toString()); +``` + + + + + +```python + +from pytoniq_core import Address, Cell, begin_cell + +def calculate_jetton_address( + owner_address: Address, jetton_master_address: Address, jetton_wallet_code: str +): + # Recreate from jetton-utils.fc calculate_jetton_wallet_address() + # https://tonscan.org/jetton/EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs#source + + data_cell = ( + begin_cell() + .store_uint(0, 4) + .store_coins(0) + .store_address(owner_address) + .store_address(jetton_master_address) + .end_cell() + ) + + code_cell = Cell.one_from_boc(jetton_wallet_code) + + state_init = ( + begin_cell() + .store_uint(0, 2) + .store_maybe_ref(code_cell) + .store_maybe_ref(data_cell) + .store_uint(0, 1) + .end_cell() + ) + state_init_hex = state_init.hash.hex() + jetton_address = Address(f'0:{state_init_hex}') + + return jetton_address + +``` + +Read the entire example [here](/static/example-code-snippets/pythoniq/jetton-offline-address-calc-wrapper.py). + + + + +Most major tokens do not have a different storage structure because they use [a standard implementation of the TEP-74 standard](https://github.com/ton-blockchain/token-contract/blob/main/ft/jetton-wallet.fc). The exception is the new [Jetton-with-governance contracts](https://github.com/ton-blockchain/stablecoin-contract) for centralized stablecoins. In these, the difference is [the presence of a wallet status field and the absence of a code cell in the vault](https://github.com/ton-blockchain/stablecoin-contract/blob/7a22416d4de61336616960473af391713e100d7b/contracts/jetton-utils.fc#L3-L12). + +### How to construct a message for a jetton transfer with a comment? + +To understand how to construct a message for token transfer, we use [TEP-74](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#1-transfer), which describes the token standard. + +:::warning +When displayed, token doesn't usually show count of indivisible units user has; rather, amount is divided by `10 ^ decimals`. This value is commonly set to `9`, and this allows us to use `toNano` function. If decimals were different, we would **need to multiply by a different value** (for instance, if decimals are 6, then we would end up transferring thousand times the amount we wanted). + +Of course, one can always do calculation in indivisible units. +::: + + + + +```js +import { Address, beginCell, internal, storeMessageRelaxed, toNano } from "@ton/core"; + +async function main() { + const jettonWalletAddress = Address.parse('put your jetton wallet address'); + const destinationAddress = Address.parse('put destination wallet address'); + + const forwardPayload = beginCell() + .storeUint(0, 32) // 0 opcode means we have a comment + .storeStringTail('Hello, TON!') + .endCell(); + + const messageBody = beginCell() + .storeUint(0x0f8a7ea5, 32) // opcode for jetton transfer + .storeUint(0, 64) // query id + .storeCoins(toNano(5)) // jetton amount, amount * 10^9 + .storeAddress(destinationAddress) + .storeAddress(destinationAddress) // response destination + .storeBit(0) // no custom payload + .storeCoins(toNano('0.02')) // forward amount - if >0, will send notification message + .storeBit(1) // we store forwardPayload as a reference + .storeRef(forwardPayload) + .endCell(); + + const internalMessage = internal({ + to: jettonWalletAddress, + value: toNano('0.1'), + bounce: true, + body: messageBody + }); + const internalMessageCell = beginCell() + .store(storeMessageRelaxed(internalMessage)) + .endCell(); +} + +main().finally(() => console.log("Exiting...")); +``` + + + + +```js +const TonWeb = require("tonweb"); +const {mnemonicToKeyPair} = require("tonweb-mnemonic"); + +async function main() { + const tonweb = new TonWeb(new TonWeb.HttpProvider( + 'https://toncenter.com/api/v2/jsonRPC', { + apiKey: 'put your api key' + }) + ); + const destinationAddress = new TonWeb.Address('put destination wallet address'); + + const forwardPayload = new TonWeb.boc.Cell(); + forwardPayload.bits.writeUint(0, 32); // 0 opcode means we have a comment + forwardPayload.bits.writeString('Hello, TON!'); + + /* + Tonweb has a built-in class for interacting with jettons, which has + a method for creating a transfer. However, it has disadvantages, so + we manually create the message body. Additionally, this way we have a + better understanding of what is stored and how it functions. + */ + + const jettonTransferBody = new TonWeb.boc.Cell(); + jettonTransferBody.bits.writeUint(0xf8a7ea5, 32); // opcode for jetton transfer + jettonTransferBody.bits.writeUint(0, 64); // query id + jettonTransferBody.bits.writeCoins(new TonWeb.utils.BN('5')); // jetton amount, amount * 10^9 + jettonTransferBody.bits.writeAddress(destinationAddress); + jettonTransferBody.bits.writeAddress(destinationAddress); // response destination + jettonTransferBody.bits.writeBit(false); // no custom payload + jettonTransferBody.bits.writeCoins(TonWeb.utils.toNano('0.02')); // forward amount + jettonTransferBody.bits.writeBit(true); // we store forwardPayload as a reference + jettonTransferBody.refs.push(forwardPayload); + + const keyPair = await mnemonicToKeyPair('put your mnemonic'.split(' ')); + const jettonWallet = new TonWeb.token.ft.JettonWallet(tonweb.provider, { + address: 'put your jetton wallet address' + }); + + // available wallet types: simpleR1, simpleR2, simpleR3, + // v2R1, v2R2, v3R1, v3R2, v4R1, v4R2 + const wallet = new tonweb.wallet.all['v4R2'](tonweb.provider, { + publicKey: keyPair.publicKey, + wc: 0 // workchain + }); + + await wallet.methods.transfer({ + secretKey: keyPair.secretKey, + toAddress: jettonWallet.address, + amount: tonweb.utils.toNano('0.1'), + seqno: await wallet.methods.seqno().call(), + payload: jettonTransferBody, + sendMode: 3 + }).send(); // create transfer and send it +} + +main().finally(() => console.log("Exiting...")); +``` + + + + + +```py +from pytoniq import LiteBalancer, WalletV4R2, begin_cell +import asyncio + +mnemonics = ["your", "mnemonics", "here"] + +async def main(): + provider = LiteBalancer.from_mainnet_config(1) + await provider.start_up() + + wallet = await WalletV4R2.from_mnemonic(provider=provider, mnemonics=mnemonics) + USER_ADDRESS = wallet.address + JETTON_MASTER_ADDRESS = "EQBlqsm144Dq6SjbPI4jjZvA1hqTIP3CvHovbIfW_t-SCALE" + DESTINATION_ADDRESS = "EQAsl59qOy9C2XL5452lGbHU9bI3l4lhRaopeNZ82NRK8nlA" + + USER_JETTON_WALLET = (await provider.run_get_method(address=JETTON_MASTER_ADDRESS, + method="get_wallet_address", + stack=[begin_cell().store_address(USER_ADDRESS).end_cell().begin_parse()]))[0].load_address() + forward_payload = (begin_cell() + .store_uint(0, 32) # TextComment op-code + .store_snake_string("Comment") + .end_cell()) + transfer_cell = (begin_cell() + .store_uint(0xf8a7ea5, 32) # Jetton Transfer op-code + .store_uint(0, 64) # query_id + .store_coins(1 * 10**9) # Jetton amount to transfer in nanojetton + .store_address(DESTINATION_ADDRESS) # Destination address + .store_address(USER_ADDRESS) # Response address + .store_bit(0) # Custom payload is None + .store_coins(1) # Ton forward amount in nanoton + .store_bit(1) # Store forward_payload as a reference + .store_ref(forward_payload) # Forward payload + .end_cell()) + + await wallet.transfer(destination=USER_JETTON_WALLET, amount=int(0.05*1e9), body=transfer_cell) + await provider.close_all() + +asyncio.run(main()) +``` + + + + + +If `forward_amount` is nonzero, a notification regarding jetton reception is sent to destination contract, as can be seen in the scheme in the top of this section. If `response_destination` address is not null, toncoins left (they're called "excesses") are sent to that address. + +:::tip +Explorers support comments in jetton notifications as well as in common TON transfers. Their format is 32 zero bits and then text, preferably UTF-8. +::: + +:::tip +Jetton transfers need careful consideration for fees and amounts behind outgoing messages. For instance, if you "call" transfer with 0.2 TON, you won't be able to forward 0.1 TON and receive 0.1 TON in excess return message. +::: + +## TEP-62 (NFT Standard) + +NFT ecosystem scheme + +NFT collections are very different. Actually, NFT contract on TON can be defined as "contract that has appropriate get-method and returns valid metadata". Transfer operation is standartized and quite analogous to [jetton's one](/develop/dapps/cookbook#how-to-construct-a-message-for-a-jetton-transfer-with-a-comment), so we will not dive into it and rather see additional capabilities provided by most collections you may meet! + +:::warning +Reminder: all methods about NFT below are not bound by TEP-62 to work. Before trying them, please check if your NFT or collection will process those messages in an expected way. Wallet app emulation may prove useful in this case. +::: + +### How to use NFT batch deploy? + +Smart contracts for collections allow deploying up to 250 NFTs in a single transaction. However, it's essential to consider that, in practice, this maximum is around 100-130 NFTs due to the computation fee limit of 1 ton. To achieve this, we need to store information about the new NFTs in a dictionary. + + + + +```js +import { Address, Cell, Dictionary, beginCell, internal, storeMessageRelaxed, toNano } from "@ton/core"; +import { TonClient } from "@ton/ton"; + +async function main() { + const collectionAddress = Address.parse('put your collection address'); + const nftMinStorage = '0.05'; + const client = new TonClient({ + endpoint: 'https://testnet.toncenter.com/api/v2/jsonRPC' // for Testnet + }); + const ownersAddress = [ + Address.parse('EQBbQljOpEM4Z6Hvv8Dbothp9xp2yM-TFYVr01bSqDQskHbx'), + Address.parse('EQAUTbQiM522Y_XJ_T98QPhPhTmb4nV--VSPiha8kC6kRfPO'), + Address.parse('EQDWTH7VxFyk_34J1CM6wwEcjVeqRQceNwzPwGr30SsK43yo') + ]; + const nftsMeta = [ + '0/meta.json', + '1/meta.json', + '2/meta.json' + ]; + + const getMethodResult = await client.runMethod(collectionAddress, 'get_collection_data'); + let nextItemIndex = getMethodResult.stack.readNumber(); +``` + + + + +To begin with, let's assume that the minimum amount of TON for the storage fee is `0.05`. This means that after deploying an NFT, the smart contract of the collection will send this much TON to its balance. Next, we obtain arrays with the owners of the new NFTs and their content. Afterward, we get the `next_item_index` using the GET method `get_collection_data`. + + + + +```js + let counter = 0; + const nftDict = Dictionary.empty(); + for (let index = 0; index < 3; index++) { + const metaCell = beginCell() + .storeStringTail(nftsMeta[index]) + .endCell(); + const nftContent = beginCell() + .storeAddress(ownersAddress[index]) + .storeRef(metaCell) + .endCell(); + nftDict.set(nextItemIndex, nftContent); + nextItemIndex++; + counter++; + } + + /* + We need to write our custom serialization and deserialization + functions to store data correctly in the dictionary since the + built-in functions in the library are not suitable for our case. + */ + const messageBody = beginCell() + .storeUint(2, 32) + .storeUint(0, 64) + .storeDict(nftDict, Dictionary.Keys.Uint(64), { + serialize: (src, builder) => { + builder.storeCoins(toNano(nftMinStorage)); + builder.storeRef(src); + }, + parse: (src) => { + return beginCell() + .storeCoins(src.loadCoins()) + .storeRef(src.loadRef()) + .endCell(); + } + }) + .endCell(); + + const totalValue = String( + (counter * parseFloat(nftMinStorage) + 0.015 * counter).toFixed(6) + ); + + const internalMessage = internal({ + to: collectionAddress, + value: totalValue, + bounce: true, + body: messageBody + }); +} + +main().finally(() => console.log("Exiting...")); +``` + + + + +Next, we need to correctly calculate the total transaction cost. The value of `0.015` was obtained through testing, but it can vary for each case. This mainly depends on the content of the NFT, as an increase in content size results in a higher **forward fee** (the fee for delivery). + +### How to change the owner of a collection's smart contract? + +Changing the owner of a collection is very simple. To do this, you need to specify **opcode = 3**, any query_id, and the address of the new owner: + + + + +```js +import { Address, beginCell, internal, storeMessageRelaxed, toNano } from "@ton/core"; + +async function main() { + const collectionAddress = Address.parse('put your collection address'); + const newOwnerAddress = Address.parse('put new owner wallet address'); + + const messageBody = beginCell() + .storeUint(3, 32) // opcode for changing owner + .storeUint(0, 64) // query id + .storeAddress(newOwnerAddress) + .endCell(); + + const internalMessage = internal({ + to: collectionAddress, + value: toNano('0.05'), + bounce: true, + body: messageBody + }); + const internalMessageCell = beginCell() + .store(storeMessageRelaxed(internalMessage)) + .endCell(); +} + +main().finally(() => console.log("Exiting...")); +``` + + + + +```js +const TonWeb = require("tonweb"); +const {mnemonicToKeyPair} = require("tonweb-mnemonic"); + +async function main() { + const tonweb = new TonWeb(new TonWeb.HttpProvider( + 'https://toncenter.com/api/v2/jsonRPC', { + apiKey: 'put your api key' + }) + ); + const collectionAddress = new TonWeb.Address('put your collection address'); + const newOwnerAddress = new TonWeb.Address('put new owner wallet address'); + + const messageBody = new TonWeb.boc.Cell(); + messageBody.bits.writeUint(3, 32); // opcode for changing owner + messageBody.bits.writeUint(0, 64); // query id + messageBody.bits.writeAddress(newOwnerAddress); + + // available wallet types: simpleR1, simpleR2, simpleR3, + // v2R1, v2R2, v3R1, v3R2, v4R1, v4R2 + const keyPair = await mnemonicToKeyPair('put your mnemonic'.split(' ')); + const wallet = new tonweb.wallet.all['v4R2'](tonweb.provider, { + publicKey: keyPair.publicKey, + wc: 0 // workchain + }); + + await wallet.methods.transfer({ + secretKey: keyPair.secretKey, + toAddress: collectionAddress, + amount: tonweb.utils.toNano('0.05'), + seqno: await wallet.methods.seqno().call(), + payload: messageBody, + sendMode: 3 + }).send(); // create transfer and send it +} + +main().finally(() => console.log("Exiting...")); +``` + + + + +### How to change the content in a collection's smart contract? + +To change the content of a smart contract's collection, we need to understand how it is stored. The collection stores all the content in a single cell, inside of which there are two cells: **collection content** and **NFT common content**. The first cell contains the collection's metadata, while the second one contains the base URL for the NFT metadata. + +Often, the collection's metadata is stored in a format similar to `0.json` and continues incrementing, while the address before this file remains the same. It is this address that should be stored in the NFT common content. + + + + +```js +import { Address, beginCell, internal, storeMessageRelaxed, toNano } from "@ton/core"; + +async function main() { + const collectionAddress = Address.parse('put your collection address'); + const newCollectionMeta = 'put url fol collection meta'; + const newNftCommonMeta = 'put common url for nft meta'; + const royaltyAddress = Address.parse('put royalty address'); + + const collectionMetaCell = beginCell() + .storeUint(1, 8) // we have offchain metadata + .storeStringTail(newCollectionMeta) + .endCell(); + const nftCommonMetaCell = beginCell() + .storeUint(1, 8) // we have offchain metadata + .storeStringTail(newNftCommonMeta) + .endCell(); + + const contentCell = beginCell() + .storeRef(collectionMetaCell) + .storeRef(nftCommonMetaCell) + .endCell(); + + const royaltyCell = beginCell() + .storeUint(5, 16) // factor + .storeUint(100, 16) // base + .storeAddress(royaltyAddress) // this address will receive 5% of each sale + .endCell(); + + const messageBody = beginCell() + .storeUint(4, 32) // opcode for changing content + .storeUint(0, 64) // query id + .storeRef(contentCell) + .storeRef(royaltyCell) + .endCell(); + + const internalMessage = internal({ + to: collectionAddress, + value: toNano('0.05'), + bounce: true, + body: messageBody + }); + + const internalMessageCell = beginCell() + .store(storeMessageRelaxed(internalMessage)) + .endCell(); +} + +main().finally(() => console.log("Exiting...")); +``` + + + + +```js +const TonWeb = require("tonweb"); +const {mnemonicToKeyPair} = require("tonweb-mnemonic"); + +async function main() { + const tonweb = new TonWeb(new TonWeb.HttpProvider( + 'https://testnet.toncenter.com/api/v2/jsonRPC', { + apiKey: 'put your api key' + }) + ); + const collectionAddress = new TonWeb.Address('put your collection address'); + const newCollectionMeta = 'put url fol collection meta'; + const newNftCommonMeta = 'put common url for nft meta'; + const royaltyAddress = new TonWeb.Address('put royalty address'); + + const collectionMetaCell = new TonWeb.boc.Cell(); + collectionMetaCell.bits.writeUint(1, 8); // we have offchain metadata + collectionMetaCell.bits.writeString(newCollectionMeta); + const nftCommonMetaCell = new TonWeb.boc.Cell(); + nftCommonMetaCell.bits.writeUint(1, 8); // we have offchain metadata + nftCommonMetaCell.bits.writeString(newNftCommonMeta); + + const contentCell = new TonWeb.boc.Cell(); + contentCell.refs.push(collectionMetaCell); + contentCell.refs.push(nftCommonMetaCell); + + const royaltyCell = new TonWeb.boc.Cell(); + royaltyCell.bits.writeUint(5, 16); // factor + royaltyCell.bits.writeUint(100, 16); // base + royaltyCell.bits.writeAddress(royaltyAddress); // this address will receive 5% of each sale + + const messageBody = new TonWeb.boc.Cell(); + messageBody.bits.writeUint(4, 32); + messageBody.bits.writeUint(0, 64); + messageBody.refs.push(contentCell); + messageBody.refs.push(royaltyCell); + + // available wallet types: simpleR1, simpleR2, simpleR3, + // v2R1, v2R2, v3R1, v3R2, v4R1, v4R2 + const keyPair = await mnemonicToKeyPair('put your mnemonic'.split(' ')); + const wallet = new tonweb.wallet.all['v4R2'](tonweb.provider, { + publicKey: keyPair.publicKey, + wc: 0 // workchain + }); + + await wallet.methods.transfer({ + secretKey: keyPair.secretKey, + toAddress: collectionAddress, + amount: tonweb.utils.toNano('0.05'), + seqno: await wallet.methods.seqno().call(), + payload: messageBody, + sendMode: 3 + }).send(); // create transfer and send it +} + +main().finally(() => console.log("Exiting...")); +``` + + + + +Additionally, we need to include royalty information in our message, as they also change using this opcode. It's important to note that it's not necessary to specify new values everywhere. If, for example, only the NFT common content needs to be changed, then all other values can be specified as they were before. + +## Third-party: Decentralized Exchanges (DEX) + +### How to send a swap message to DEX (DeDust)? + +DEXs use different protocols for their work. In this example we will interact with **DeDust**. + +- [DeDust documentation](https://docs.dedust.io/). + +DeDust has two exchange paths: jetton <-> jetton or TON <-> jetton. Each has a different scheme. To swap, you need to send jettons (or toncoin) to a specific **vault** and provide a special payload. Here is the scheme for swapping jetton to jetton or jetton to toncoin: + +```tlb +swap#e3a0d482 _:SwapStep swap_params:^SwapParams = ForwardPayload; + step#_ pool_addr:MsgAddressInt params:SwapStepParams = SwapStep; + step_params#_ kind:SwapKind limit:Coins next:(Maybe ^SwapStep) = SwapStepParams; + swap_params#_ deadline:Timestamp recipient_addr:MsgAddressInt referral_addr:MsgAddress + fulfill_payload:(Maybe ^Cell) reject_payload:(Maybe ^Cell) = SwapParams; +``` + +This scheme shows what should be in the `forward_payload` of your jettons transfer message (`transfer#0f8a7ea5`). + +And the scheme of toncoin to jetton swap: + +```tlb +swap#ea06185d query_id:uint64 amount:Coins _:SwapStep swap_params:^SwapParams = InMsgBody; + step#_ pool_addr:MsgAddressInt params:SwapStepParams = SwapStep; + step_params#_ kind:SwapKind limit:Coins next:(Maybe ^SwapStep) = SwapStepParams; + swap_params#_ deadline:Timestamp recipient_addr:MsgAddressInt referral_addr:MsgAddress + fulfill_payload:(Maybe ^Cell) reject_payload:(Maybe ^Cell) = SwapParams; +``` + +This is the scheme for the body of transfer to the toncoin **vault**. + +First, you need to know the **vault** addresses of the jettons you will swap or toncoin **vault** address. This can be done using the `get_vault_address` get method of the contract [**Factory**](https://docs.dedust.io/reference/factory). As an argument you need to pass a slice according to the scheme: + +```tlb +native$0000 = Asset; // for ton +jetton$0001 workchain_id:int8 address:uint256 = Asset; // for jetton +``` + +Also for the exchange itself, we need the **pool** address - acquired from get method `get_pool_address`. As arguments - asset slices according to the scheme above. In response, both methods will return a slice of the address of the requested **vault** / **pool**. + +This is enough to build the message. + + + + +DEXs use different protocols for their work, we need to familiarize ourselves with key concepts and some vital components and also know the TL-B schema involved in doing our swap process correctly. In this tutorial, we deal with DeDust, one of the famous DEX implemented entirely in TON. +In DeDust, we have an abstract Asset concept that includes any swappable asset types. Abstraction over asset types simplifies the swap process because the type of asset does not matter, and extra currency or even assets from other chains in this approach will be covered with ease. + +Following is the TL-B schema that DeDust introduced for the Asset concept. + +```tlb +native$0000 = Asset; // for ton + +jetton$0001 workchain_id:int8 address:uint256 = Asset; // for any jetton,address refer to jetton master address + +// Upcoming, not implemented yet. +extra_currency$0010 currency_id:int32 = Asset; +``` + +Next, DeDust introduced three components, Vault, Pool, and Factory. These components are contracts or groups of contracts and are responsible for parts of the swap process. The factory acts as finding other component addresses (like vault, and pool) +and also building other components. +Vault is responsible for receiving transfer messages, holding assets, and just informing the corresponding pool that "user A wants to swap 100 X to Y". + +Pool, on the other hand, is responsible for calculating the swap amount based on the predefined formula informing other Vault that are responsible for asset Y, and telling it to pay a calculated amount to the user. +Calculations of swap amount are based on a mathematical formula, which means so far we have two different pools, one known as Volatile, that operates based on the commonly used "Constant Product" formula: x \* y = k, And the other known as Stable-Swap - Optimized for assets of near-equal value (e.g. USDT / USDC, TON / stTON). It uses the formula: x3 \* y + y3 \* x = k. +So for every swap we need the corresponding Vault and it needs just implement a specific API tailored for interacting with a distinct asset type. DeDust has three implementations of Vault, Native Vault - Handles the native coin (Toncoin). Jetton Vault - Manages jettons and Extra-Currency Vault (upcoming) - Designed for TON extra-currencies. + +DeDust provides a special SDk to work with contract, component, and API, it was written in typescript. +Enough theory, let's set up our environment to swap one jetton with TON. + +```bash +npm install --save @ton/core @ton/ton @ton/crypt + +``` + +we also need to bring DeDust SDK as well. + +```bash +npm install --save @dedust/sdk +``` + +Now we need to initialize some objects. + +```typescript +import { Factory, MAINNET_FACTORY_ADDR } from "@dedust/sdk"; +import { Address, TonClient4 } from "@ton/ton"; + +const tonClient = new TonClient4({ + endpoint: "https://mainnet-v4.tonhubapi.com", +}); +const factory = tonClient.open(Factory.createFromAddress(MAINNET_FACTORY_ADDR)); +//The Factory contract is used to locate other contracts. +``` + +The process of swapping has some steps, for example, to swap some TON with Jetton we first need to find the corresponding Vault and Pool +then make sure they are deployed. For our example TON and SCALE, the code is as follows : + +```typescript +import { Asset, VaultNative } from "@dedust/sdk"; + +//Native vault is for TON +const tonVault = tonClient.open(await factory.getNativeVault()); +//We use the factory to find our native coin (Toncoin) Vault. +``` + +The next step is to find the corresponding Pool, here (TON and SCALE) + +```typescript +import { PoolType } from "@dedust/sdk"; + +const SCALE_ADDRESS = Address.parse( + "EQBlqsm144Dq6SjbPI4jjZvA1hqTIP3CvHovbIfW_t-SCALE", +); +// master address of SCALE jetton +const TON = Asset.native(); +const SCALE = Asset.jetton(SCALE_ADDRESS); + +const pool = tonClient.open( + await factory.getPool(PoolType.VOLATILE, [TON, SCALE]), +); +``` + +Now we should ensure that these contracts exist since sending funds to an inactive contract could result in irretrievable loss. + +```typescript +import { ReadinessStatus } from "@dedust/sdk"; + +// Check if the pool exists: +if ((await pool.getReadinessStatus()) !== ReadinessStatus.READY) { + throw new Error("Pool (TON, SCALE) does not exist."); +} + +// Check if the vault exits: +if ((await tonVault.getReadinessStatus()) !== ReadinessStatus.READY) { + throw new Error("Vault (TON) does not exist."); +} +``` + +After that, we can send transfer messages with the amount of TON + +```typescript +import { toNano } from "@ton/core"; +import { mnemonicToPrivateKey } from "@ton/crypto"; + + if (!process.env.MNEMONIC) { + throw new Error("Environment variable MNEMONIC is required."); + } + + const mnemonic = process.env.MNEMONIC.split(" "); + + const keys = await mnemonicToPrivateKey(mnemonic); + const wallet = tonClient.open( + WalletContractV3R2.create({ + workchain: 0, + publicKey: keys.publicKey, + }), + ); + +const sender = wallet.sender(keys.secretKey); + +const amountIn = toNano("5"); // 5 TON + +await tonVault.sendSwap(sender, { + poolAddress: pool.address, + amount: amountIn, + gasAmount: toNano("0.25"), +}); +``` + +To swap Token X with Y, the process is the same, for instance, we send an amount of X token to vault X, vault X +receives our asset, holds it, and informs Pool of (X, Y) that this address asks for a swap, now Pool based on +calculation informs another Vault, here Vault Y releases equivalent Y to the user who requests swap. + +The difference between assets is just about the transfer method for example, for jettons, we transfer them to the Vault using a transfer message and attach a specific forward_payload, but for the native coin, we send a swap message to the Vault, attaching the corresponding amount of TON. + +This is the schema for TON and jetton : + +```tlb +swap#ea06185d query_id:uint64 amount:Coins _:SwapStep swap_params:^SwapParams = InMsgBody; +``` + +So every vault and corresponding Pool is designed for specific swaps and has a special API tailored to special assets. + +This was swapping TON with jetton SCALE. The process for swapping jetton with jetton is the same, the only difference is we should provide the payload that was described in the TL-B schema. + +```TL-B +swap#e3a0d482 _:SwapStep swap_params:^SwapParams = ForwardPayload; +``` + +```typescript +//find Vault +const scaleVault = tonClient.open(await factory.getJettonVault(SCALE_ADDRESS)); +``` + +```typescript +//find jetton address +import { JettonRoot, JettonWallet } from '@dedust/sdk'; + +const scaleRoot = tonClient.open(JettonRoot.createFromAddress(SCALE_ADDRESS)); +const scaleWallet = tonClient.open(await scaleRoot.getWallet(sender.address); + +// Transfer jettons to the Vault (SCALE) with corresponding payload + +const amountIn = toNano('50'); // 50 SCALE + +await scaleWallet.sendTransfer(sender, toNano("0.3"), { + amount: amountIn, + destination: scaleVault.address, + responseAddress: sender.address, // return gas to user + forwardAmount: toNano("0.25"), + forwardPayload: VaultJetton.createSwapPayload({ poolAddress }), +}); +``` + + + + + +Build Asset slice: + +```kotlin +val assetASlice = buildCell { + storeUInt(1,4) + storeInt(JETTON_MASTER_A.workchainId, 8) + storeBits(JETTON_MASTER_A.address) +}.beginParse() +``` + +Run get methods: + +```kotlin +val responsePool = runBlocking { + liteClient.runSmcMethod( + LiteServerAccountId(DEDUST_FACTORY.workchainId, DEDUST_FACTORY.address), + "get_pool_address", + VmStackValue.of(0), + VmStackValue.of(assetASlice), + VmStackValue.of(assetBSlice) + ) +} +stack = responsePool.toMutableVmStack() +val poolAddress = stack.popSlice().loadTlb(MsgAddressInt) as AddrStd +``` + +Build and transfer message: + +```kotlin +runBlocking { + wallet.transfer(pk, WalletTransfer { + destination = JETTON_WALLET_A // yours existing jetton wallet + bounceable = true + coins = Coins(300000000) // 0.3 ton in nanotons + messageData = MessageData.raw( + body = buildCell { + storeUInt(0xf8a7ea5, 32) // op Transfer + storeUInt(0, 64) // query_id + storeTlb(Coins, Coins(100000000)) // amount of jettons + storeSlice(addrToSlice(jettonAVaultAddress)) // destination address + storeSlice(addrToSlice(walletAddress)) // response address + storeUInt(0, 1) // custom payload + storeTlb(Coins, Coins(250000000)) // forward_ton_amount // 0.25 ton in nanotons + storeUInt(1, 1) + // forward_payload + storeRef { + storeUInt(0xe3a0d482, 32) // op swap + storeSlice(addrToSlice(poolAddress)) // pool_addr + storeUInt(0, 1) // kind + storeTlb(Coins, Coins(0)) // limit + storeUInt(0, 1) // next (for multihop) + storeRef { + storeUInt(System.currentTimeMillis() / 1000 + 60 * 5, 32) // deadline + storeSlice(addrToSlice(walletAddress)) // recipient address + storeSlice(buildCell { storeUInt(0, 2) }.beginParse()) // referral (null address) + storeUInt(0, 1) + storeUInt(0, 1) + endCell() + } + } + } + ) + sendMode = 3 + }) +} +``` + + + + + +This example shows how to swap Toncoins to Jettons. + +```py +from pytoniq import Address, begin_cell, LiteBalancer, WalletV4R2 +import time +import asyncio + +DEDUST_FACTORY = "EQBfBWT7X2BHg9tXAxzhz2aKiNTU1tpt5NsiK0uSDW_YAJ67" +DEDUST_NATIVE_VAULT = "EQDa4VOnTYlLvDJ0gZjNYm5PXfSmmtL6Vs6A_CZEtXCNICq_" + +mnemonics = ["your", "mnemonics", "here"] + +async def main(): + provider = LiteBalancer.from_mainnet_config(1) + await provider.start_up() + + wallet = await WalletV4R2.from_mnemonic(provider=provider, mnemonics=mnemonics) + + JETTON_MASTER = Address("EQBlqsm144Dq6SjbPI4jjZvA1hqTIP3CvHovbIfW_t-SCALE") # jetton address swap to + TON_AMOUNT = 10**9 # 1 ton - swap amount + GAS_AMOUNT = 10**9 // 4 # 0.25 ton for gas + + pool_type = 0 # Volatile pool type + + asset_native = (begin_cell() + .store_uint(0, 4) # Asset type is native + .end_cell().begin_parse()) + asset_jetton = (begin_cell() + .store_uint(1, 4) # Asset type is jetton + .store_uint(JETTON_MASTER.wc, 8) + .store_bytes(JETTON_MASTER.hash_part) + .end_cell().begin_parse()) + + stack = await provider.run_get_method( + address=DEDUST_FACTORY, method="get_pool_address", + stack=[pool_type, asset_native, asset_jetton] + ) + pool_address = stack[0].load_address() + + swap_params = (begin_cell() + .store_uint(int(time.time() + 60 * 5), 32) # Deadline + .store_address(wallet.address) # Recipient address + .store_address(None) # Referall address + .store_maybe_ref(None) # Fulfill payload + .store_maybe_ref(None) # Reject payload + .end_cell()) + swap_body = (begin_cell() + .store_uint(0xea06185d, 32) # Swap op-code + .store_uint(0, 64) # Query id + .store_coins(int(1*1e9)) # Swap amount + .store_address(pool_address) + .store_uint(0, 1) # Swap kind + .store_coins(0) # Swap limit + .store_maybe_ref(None) # Next step for multi-hop swaps + .store_ref(swap_params) + .end_cell()) + + await wallet.transfer(destination=DEDUST_NATIVE_VAULT, + amount=TON_AMOUNT + GAS_AMOUNT, # swap amount + gas + body=swap_body) + + await provider.close_all() + +asyncio.run(main()) + +``` + + + + +## Basics of incoming message processing + +### How to parse transactions of an account (Transfers, Jettons, NFTs)? + +The list of transactions on an account can be fetched through `getTransactions` API method. It returns an array of `Transaction` objects, with each item having lots of attributes. However, the fields that are the most commonly used are: + +- Sender, Body and Value of the message that initiated this transaction +- Transaction's hash and logical time (LT) + +_Sender_ and _Body_ fields may be used to determine the type of message (regular transfer, jetton transfer, nft transfer etc). + +Below is an example on how you can fetch 5 most recent transactions on any blockchain account, parse them depending on the type and print out in a loop. + + + + +```js +import { Address, TonClient, beginCell, fromNano } from '@ton/ton'; + +async function main() { + const client = new TonClient({ + endpoint: 'https://toncenter.com/api/v2/jsonRPC', + apiKey: '1b312c91c3b691255130350a49ac5a0742454725f910756aff94dfe44858388e', + }); + + const myAddress = Address.parse('EQBKgXCNLPexWhs2L79kiARR1phGH1LwXxRbNsCFF9doc2lN'); // address that you want to fetch transactions from + + const transactions = await client.getTransactions(myAddress, { + limit: 5, + }); + + for (const tx of transactions) { + const inMsg = tx.inMessage; + + if (inMsg?.info.type == 'internal') { + // we only process internal messages here because they are used the most + // for external messages some of the fields are empty, but the main structure is similar + const sender = inMsg?.info.src; + const value = inMsg?.info.value.coins; + + const originalBody = inMsg?.body.beginParse(); + let body = originalBody.clone(); + if (body.remainingBits < 32) { + // if body doesn't have opcode: it's a simple message without comment + console.log(`Simple transfer from ${sender} with value ${fromNano(value)} TON`); + } else { + const op = body.loadUint(32); + if (op == 0) { + // if opcode is 0: it's a simple message with comment + const comment = body.loadStringTail(); + console.log( + `Simple transfer from ${sender} with value ${fromNano(value)} TON and comment: "${comment}"` + ); + } else if (op == 0x7362d09c) { + // if opcode is 0x7362d09c: it's a Jetton transfer notification + + body.skip(64); // skip query_id + const jettonAmount = body.loadCoins(); + const jettonSender = body.loadAddressAny(); + const originalForwardPayload = body.loadBit() ? body.loadRef().beginParse() : body; + let forwardPayload = originalForwardPayload.clone(); + + // IMPORTANT: we have to verify the source of this message because it can be faked + const runStack = (await client.runMethod(sender, 'get_wallet_data')).stack; + runStack.skip(2); + const jettonMaster = runStack.readAddress(); + const jettonWallet = ( + await client.runMethod(jettonMaster, 'get_wallet_address', [ + { type: 'slice', cell: beginCell().storeAddress(myAddress).endCell() }, + ]) + ).stack.readAddress(); + if (!jettonWallet.equals(sender)) { + // if sender is not our real JettonWallet: this message was faked + console.log(`FAKE Jetton transfer`); + continue; + } + + if (forwardPayload.remainingBits < 32) { + // if forward payload doesn't have opcode: it's a simple Jetton transfer + console.log(`Jetton transfer from ${jettonSender} with value ${fromNano(jettonAmount)} Jetton`); + } else { + const forwardOp = forwardPayload.loadUint(32); + if (forwardOp == 0) { + // if forward payload opcode is 0: it's a simple Jetton transfer with comment + const comment = forwardPayload.loadStringTail(); + console.log( + `Jetton transfer from ${jettonSender} with value ${fromNano( + jettonAmount + )} Jetton and comment: "${comment}"` + ); + } else { + // if forward payload opcode is something else: it's some message with arbitrary structure + // you may parse it manually if you know other opcodes or just print it as hex + console.log( + `Jetton transfer with unknown payload structure from ${jettonSender} with value ${fromNano( + jettonAmount + )} Jetton and payload: ${originalForwardPayload}` + ); + } + + console.log(`Jetton Master: ${jettonMaster}`); + } + } else if (op == 0x05138d91) { + // if opcode is 0x05138d91: it's a NFT transfer notification + + body.skip(64); // skip query_id + const prevOwner = body.loadAddress(); + const originalForwardPayload = body.loadBit() ? body.loadRef().beginParse() : body; + let forwardPayload = originalForwardPayload.clone(); + + // IMPORTANT: we have to verify the source of this message because it can be faked + const runStack = (await client.runMethod(sender, 'get_nft_data')).stack; + runStack.skip(1); + const index = runStack.readBigNumber(); + const collection = runStack.readAddress(); + const itemAddress = ( + await client.runMethod(collection, 'get_nft_address_by_index', [{ type: 'int', value: index }]) + ).stack.readAddress(); + + if (!itemAddress.equals(sender)) { + console.log(`FAKE NFT Transfer`); + continue; + } + + if (forwardPayload.remainingBits < 32) { + // if forward payload doesn't have opcode: it's a simple NFT transfer + console.log(`NFT transfer from ${prevOwner}`); + } else { + const forwardOp = forwardPayload.loadUint(32); + if (forwardOp == 0) { + // if forward payload opcode is 0: it's a simple NFT transfer with comment + const comment = forwardPayload.loadStringTail(); + console.log(`NFT transfer from ${prevOwner} with comment: "${comment}"`); + } else { + // if forward payload opcode is something else: it's some message with arbitrary structure + // you may parse it manually if you know other opcodes or just print it as hex + console.log( + `NFT transfer with unknown payload structure from ${prevOwner} and payload: ${originalForwardPayload}` + ); + } + } + + console.log(`NFT Item: ${itemAddress}`); + console.log(`NFT Collection: ${collection}`); + } else { + // if opcode is something else: it's some message with arbitrary structure + // you may parse it manually if you know other opcodes or just print it as hex + console.log( + `Message with unknown structure from ${sender} with value ${fromNano( + value + )} TON and body: ${originalBody}` + ); + } + } + } + console.log(`Transaction Hash: ${tx.hash().toString('hex')}`); + console.log(`Transaction LT: ${tx.lt}`); + console.log(); + } +} + +main().finally(() => console.log('Exiting...')); +``` + + + + + +```py +from pytoniq import LiteBalancer, begin_cell +import asyncio + +async def parse_transactions(transactions): + for transaction in transactions: + if not transaction.in_msg.is_internal: + continue + if transaction.in_msg.info.dest.to_str(1, 1, 1) != MY_WALLET_ADDRESS: + continue + + sender = transaction.in_msg.info.src.to_str(1, 1, 1) + value = transaction.in_msg.info.value_coins + if value != 0: + value = value / 1e9 + + if len(transaction.in_msg.body.bits) < 32: + print(f"TON transfer from {sender} with value {value} TON") + else: + body_slice = transaction.in_msg.body.begin_parse() + op_code = body_slice.load_uint(32) + + # TextComment + if op_code == 0: + print(f"TON transfer from {sender} with value {value} TON and comment: {body_slice.load_snake_string()}") + + # Jetton Transfer Notification + elif op_code == 0x7362d09c: + body_slice.load_bits(64) # skip query_id + jetton_amount = body_slice.load_coins() / 1e9 + jetton_sender = body_slice.load_address().to_str(1, 1, 1) + if body_slice.load_bit(): + forward_payload = body_slice.load_ref().begin_parse() + else: + forward_payload = body_slice + + jetton_master = (await provider.run_get_method(address=sender, method="get_wallet_data", stack=[]))[2].load_address() + jetton_wallet = (await provider.run_get_method(address=jetton_master, method="get_wallet_address", + stack=[ + begin_cell().store_address(MY_WALLET_ADDRESS).end_cell().begin_parse() + ]))[0].load_address().to_str(1, 1, 1) + + if jetton_wallet != sender: + print("FAKE Jetton Transfer") + continue + + if len(forward_payload.bits) < 32: + print(f"Jetton transfer from {jetton_sender} with value {jetton_amount} Jetton") + else: + forward_payload_op_code = forward_payload.load_uint(32) + if forward_payload_op_code == 0: + print(f"Jetton transfer from {jetton_sender} with value {jetton_amount} Jetton and comment: {forward_payload.load_snake_string()}") + else: + print(f"Jetton transfer from {jetton_sender} with value {jetton_amount} Jetton and unknown payload: {forward_payload} ") + + # NFT Transfer Notification + elif op_code == 0x05138d91: + body_slice.load_bits(64) # skip query_id + prev_owner = body_slice.load_address().to_str(1, 1, 1) + if body_slice.load_bit(): + forward_payload = body_slice.load_ref().begin_parse() + else: + forward_payload = body_slice + + stack = await provider.run_get_method(address=sender, method="get_nft_data", stack=[]) + index = stack[1] + collection = stack[2].load_address() + item_address = (await provider.run_get_method(address=collection, method="get_nft_address_by_index", + stack=[index]))[0].load_address().to_str(1, 1, 1) + + if item_address != sender: + print("FAKE NFT Transfer") + continue + + if len(forward_payload.bits) < 32: + print(f"NFT transfer from {prev_owner}") + else: + forward_payload_op_code = forward_payload.load_uint(32) + if forward_payload_op_code == 0: + print(f"NFT transfer from {prev_owner} with comment: {forward_payload.load_snake_string()}") + else: + print(f"NFT transfer from {prev_owner} with unknown payload: {forward_payload}") + + print(f"NFT Item: {item_address}") + print(f"NFT Collection: {collection}") + print(f"Transaction hash: {transaction.cell.hash.hex()}") + print(f"Transaction lt: {transaction.lt}") + +MY_WALLET_ADDRESS = "EQAsl59qOy9C2XL5452lGbHU9bI3l4lhRaopeNZ82NRK8nlA" +provider = LiteBalancer.from_mainnet_config(1) + +async def main(): + await provider.start_up() + transactions = await provider.get_transactions(address=MY_WALLET_ADDRESS, count=5) + await parse_transactions(transactions) + await provider.close_all() + +asyncio.run(main()) +``` + + + + + +Note that this example covers only the simplest case with incoming messages, where it is enough to fetch the transactions on a single account. If you want to go deeper and handle more complex chains of transactions and messages, you should take `tx.outMessages` field into an account. It contains the list of the output messages sent by smart-contract in the result of this transaction. To understand the whole logic better, you can read these articles: + +- [Message Overview](/develop/smart-contracts/guidelines/message-delivery-guarantees) +- [Internal messages](/develop/smart-contracts/guidelines/internal-messages) + +This topic is explored in more depth in [Payments Processing](/develop/dapps/asset-processing) article. + +### How to find transaction for a certain TON Connect result? + +TON Connect 2 returns only cell which was sent to blockchain, not generated transaction hash (since that transaction may not come to pass, if external message gets lost or timeouts). Provided BOC, though, allows us to search for that exact message in our account history. + +:::tip +You can use an indexer to make the search easier. The provided implementation is for `TonClient` connected to a RPC. +::: + +Prepare `retry` function for attempts on listening blockchain: + +```typescript + +export async function retry(fn: () => Promise, options: { retries: number, delay: number }): Promise { + let lastError: Error | undefined; + for (let i = 0; i < options.retries; i++) { + try { + return await fn(); + } catch (e) { + if (e instanceof Error) { + lastError = e; + } + await new Promise(resolve => setTimeout(resolve, options.delay)); + } + } + throw lastError; +} + +``` + +Create listener function which will assert specific transaction on certain account with specific incoming external message, equal to body message in boc: + + + + +```typescript + +import {Cell, Address, beginCell, storeMessage, TonClient} from "@ton/ton"; + +const res = tonConnectUI.send(msg); // exBoc in the result of sending message +const exBoc = res.boc; +const client = new TonClient({ + endpoint: 'https://toncenter.com/api/v2/jsonRPC', + apiKey: 'INSERT YOUR API-KEY', // https://t.me/tonapibot + }); + +export async function getTxByBOC(exBoc: string): Promise { + + const myAddress = Address.parse('INSERT TON WALLET ADDRESS'); // Address to fetch transactions from + + return retry(async () => { + const transactions = await client.getTransactions(myAddress, { + limit: 5, + }); + for (const tx of transactions) { + const inMsg = tx.inMessage; + if (inMsg?.info.type === 'external-in') { + + const inBOC = inMsg?.body; + if (typeof inBOC === 'undefined') { + + reject(new Error('Invalid external')); + continue; + } + const extHash = Cell.fromBase64(exBoc).hash().toString('hex') + const inHash = beginCell().store(storeMessage(inMsg)).endCell().hash().toString('hex') + + console.log(' hash BOC', extHash); + console.log('inMsg hash', inHash); + console.log('checking the tx', tx, tx.hash().toString('hex')); + + + // Assuming `inBOC.hash()` is synchronous and returns a hash object with a `toString` method + if (extHash === inHash) { + console.log('Tx match'); + const txHash = tx.hash().toString('hex'); + console.log(`Transaction Hash: ${txHash}`); + console.log(`Transaction LT: ${tx.lt}`); + return (txHash); + } + } + } + throw new Error('Transaction not found'); + }, {retries: 30, delay: 1000}); +} + + txRes = getTxByBOC(exBOC); + console.log(txRes); + + +``` + + + + From 59b7d274fcb44797e94ac16a4113c7691f9d8888 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:34 +0800 Subject: [PATCH 032/219] New translations coins.md (Chinese Simplified) --- .../current/develop/dapps/defi/coins.md | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/defi/coins.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/defi/coins.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/defi/coins.md new file mode 100644 index 0000000000..13bd486ed4 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/defi/coins.md @@ -0,0 +1,29 @@ +# Native token: Toncoin + +The native cryptocurrency of TON Blockchain is **Toncoin**. + +Transaction fees, gas payments (i.e., smart contract message processing fees), and persistent storage payments are collected in Toncoin. + +Toncoin is used to make the deposits required to become a blockchain validator. + +The process of making Toncoin payments is described in the [corresponding section](/develop/dapps/asset-processing). + +You can find out where to buy or exchange Toncoin on the [website](https://ton.org/coin). + +## Extra currencies + +TON Blockchain supports up to 2^32 built-in extra currencies. + +Extra currency balances can be stored on each blockchain account and transferred to other accounts natively (in an internal message from one smart contract to another, you can specify a hashmap of the extra currency amounts in addition to the Toncoin amount). + +TLB: `extra_currencies$_ dict:(HashmapE 32 (VarUInteger 32)) = ExtraCurrencyCollection;` - hashmap of currency ID and amount. + +However, extra currencies can only be stored and transferred (like Toncoin) and do not have their own arbitrary code or functionality. + +Note that if there are a large number of extra currencies created, the accounts will "swell" because they need to store them. + +Thus, extra currencies are best used for well-known decentralized currencies (for example, Wrapped Bitcoin or Ether), and creating such an extra currency should be quite expensive. + +[Jettons](/develop/dapps/defi/tokens#jettons) are suitable for other tasks. + +At the moment, no extra currency has been created on TON Blockchain. TON Blockchain has full support for extra currencies by accounts and messages, but the minter system contract for their creation has not yet been created. From 4be2c5590aadd1d23f2a78dd37863ed3fe230dd2 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:35 +0800 Subject: [PATCH 033/219] New translations subscriptions.md (Chinese Simplified) --- .../develop/dapps/defi/subscriptions.md | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/defi/subscriptions.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/defi/subscriptions.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/defi/subscriptions.md new file mode 100644 index 0000000000..11c9f45c60 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/defi/subscriptions.md @@ -0,0 +1,38 @@ +# Content Subscriptions + +Due to the fact that transactions in TON Blockchain are fast and the network fees are low, you can process recurring payments on-chain via smart contracts. + +For example, users can subscribe to digital content (or anything else) and be charged a monthly fee of 1 TON. + +:::tip +This content is specific for wallets of version v4. Older wallets don't have this functionality; it is eligible to change in future versions as well. +::: + +:::warning +Subscription contract requires authorization exactly once, on installation; then it can withdraw TON as it pleases. Do your own research before attaching unknown subscriptions. + +On the other hand, user can't get subscription installed without their knowledge. +::: + +## Example flow + +- Users use a v4 wallet. It allows additional smart contracts, known as plugins, to extend its functionality. + + After ensuring their functionality, the user can approve the addresses of trusted smart contracts (plugins) for his wallet. Following that, the trusted smart contracts can withdraw Toncoin from the wallet. This is similar to "Infinite Approval" in some other blockchains. + +- An intermediate subscription smart contract is used between each user and service as a wallet plugin. + + This smart contract guarantees that a specified amount of Toncoin will be debited from a user's wallet no more than once within a specified period. + +- The service's backend initiates payments on a regular basis by sending an external message to subscription smart contracts. + +- Either user or service can decide they no longer need a subscription and terminate it. + +## Smart contract examples + +- [Wallet v4 smart contract source code](https://github.com/ton-blockchain/wallet-contract/blob/main/func/wallet-v4-code.fc) +- [Subscription smart contract source code](https://github.com/ton-blockchain/wallet-contract/blob/main/func/simple-subscription-plugin.fc) + +## Implementation + +A good example of implementation is decentralized subscriptions for Toncoin to private channels in Telegram by the [@donate](https://t.me/donate) bot and the [Tonkeeper wallet](https://tonkeeper.com). From eadf8b22d5e49bce4f87818dd6ab73aa6852a78b Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:36 +0800 Subject: [PATCH 034/219] New translations tokens.mdx (Chinese Simplified) --- .../current/develop/dapps/defi/tokens.mdx | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/defi/tokens.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/defi/tokens.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/defi/tokens.mdx new file mode 100644 index 0000000000..019c28c871 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/defi/tokens.mdx @@ -0,0 +1,92 @@ +import Button from '@site/src/components/button' + +# Tokens (FT, NFT) + +[Distributed TON tokens overview](https://telegra.ph/Scalable-DeFi-in-TON-03-30) + +> TON-powered tokens and NFTs do not have a single center and do not create bottlenecks. +> +> Each NFT in a given collection is a separate smart contract. The token balance for each user is stored in a separate user wallet. +> +> Smart contracts interact with one another directly, spreading the load on the whole network. +> +> With the growth in user and transaction count, the load will still be even, allowing the network to scale. + +## TON Course: Jettons & NFTs + +The [TON Blockchain Course](https://stepik.org/course/176754/) is a comprehensive guide to TON Blockchain development. + +Module 7 completely covers **NFT & Jettons** development. + + + +## Tutorials + +- [Web3 Game Tutorial](/develop/dapps/tutorials/building-web3-game) - Learn how to build a Web3 game with TON Blockchain. +- [Mint your first Jetton](/develop/dapps/tutorials/jetton-minter/) — Learn how to deploy and customize your first Jetton +- [\[YouTube\] TON Keeper founders Oleg Andreev and Oleg Illarionov on TON jettons](https://www.youtube.com/watch?v=oEO29KmOpv4) + +### TON Speed Run + +Check out the [TON Speed Run](https://tonspeedrun.com/) series, which includes NFT and Jetton development: + +- [🚩 Challenge 1: Simple NFT Deploy](https://github.com/romanovichim/TONQuest1) +- [🚩 Challenge 2: Chatbot Contract](https://github.com/romanovichim/TONQuest2) +- [🚩 Challenge 3: Jetton Vending Machine](https://github.com/romanovichim/TONQuest3) +- [🚩 Challenge 4: Lottery/Raffle](https://github.com/romanovichim/TONQuest4) +- [🚩 Challenge 5: Create UI to interact with the contract in 5 minutes](https://github.com/romanovichim/TONQuest5) +- [🚩 Challenge 6: Analyzing NFT sales on the Getgems marketplace](https://github.com/romanovichim/TONQuest6) + +## Jettons (Fungible Tokens) + +### Guides + +- [TON Jetton processing](/develop/dapps/asset-processing/jettons.md) +- [TON Metadata Parsing](/develop/dapps/asset-processing/metadata.md) + +### Standards + +- [Jettons standard](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md) + +### Smart contracts + +- [Smart contracts implementation (FunC)](https://github.com/ton-blockchain/token-contract/) + +### Jetton Deployer + +Jettons are custom fungible tokens on TON Blockchain. You can create your own token on TON Blockchain using the Jetton Deployer example below: + +- **[TON Minter](https://minter.ton.org/)** — open-source Jetton Deployer dApp +- [Jetton Deployer — contracts](https://github.com/ton-defi-org/jetton-deployer-contracts) (FunC, TL-B) +- [Jetton Deployer — WebClient](https://github.com/ton-defi-org/jetton-deployer-webclient) (React, TypeScript) + +### Tools to work with Jettons + +- [NFT Jetton Sale Contract](https://github.com/dvlkv/nft-jetton-sale-smc) - NFT Sale contract with jetton support +- [Scaleton](http://scaleton.io)—see your custom token balance +- [@tegro/ton3-client](https://github.com/TegroTON/ton3-client#jettons-example)—SDK to query information about Jettons + +## NFT + +### Standards + +- [NFT standard](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md) +- [SBT (Soulbound NFT) standard](https://github.com/ton-blockchain/TEPs/blob/master/text/0085-sbt-standard.md) +- [NFTRoyalty standard extension](https://github.com/ton-blockchain/TEPs/blob/master/text/0066-nft-royalty-standard.md) + +### Smart Contracts + +- [Smart contracts implementation (FunC)](https://github.com/ton-blockchain/token-contract/) +- [Getgems NFT, sale, auctions smart contracts (FunC)](https://github.com/getgems-io/nft-contracts) + +### NFT minters + +- [NFT Deployer](https://github.com/tondiamonds/ton-nft-deployer) by TON Diamonds (TypeScript, no comments) +- [NFT Minter example](https://github.com/ton-foundation/token-contract/tree/main/nft/web-example) (JavaScript, with comments) +- [NFT Minter using React](https://github.com/tonbuilders/tonbuilders-minter) (React, no comments) +- [NFT Deployer](https://github.com/anomaly-guard/nft-deployer) (Python, with comments) +- [NFT Minter using Golang](https://github.com/xssnick/tonutils-go#nft) (Golang library, with comments and full examples) + +### Tools to work with NFTs + +- [LiberMall/tnt](https://github.com/LiberMall/tnt)—TNT is an all-in-one command-line tool to query, edit, and mint new Non-Fungible Tokens on The Open Network. From b88b5853f3befd40183c5ccc99798cc5abecf037 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:36 +0800 Subject: [PATCH 035/219] New translations ton-payments.md (Chinese Simplified) --- .../develop/dapps/defi/ton-payments.md | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/defi/ton-payments.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/defi/ton-payments.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/defi/ton-payments.md new file mode 100644 index 0000000000..2be8c3afd7 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/defi/ton-payments.md @@ -0,0 +1,39 @@ +# TON Payments + +TON Payments is the platform for micropayment channels. + +It allows instant payments without the need to commit all transactions to the blockchain, pay the associated transaction fees (e.g., for the gas consumed), and wait five seconds until the block +containing the transactions in question is confirmed. + +Because the overall expense of such instant payments is so minimal, they can be used for micropayments in games, APIs, and off-chain apps. [See examples](/develop/dapps/defi/ton-payments#examples). + +- [Payments on TON](https://blog.ton.org/ton-payments) + +## Payment channels + +### Smart contracts + +- [ton-blockchain/payment-channels](https://github.com/ton-blockchain/payment-channels) + +### SDK + +To use payment channels, you don’t need deep knowledge of cryptography. + +You can use prepared SDKs: + +- [toncenter/tonweb](https://github.com/toncenter/tonweb) JavaScript SDK +- [toncenter/payment-channels-example](https://github.com/toncenter/payment-channels-example)—how to use a payments channel with tonweb. + +### Examples + +Find examples of using payment channels in the [Hack-a-TON #1](https://ton.org/hack-a-ton-1) winners: + +- [grejwood/Hack-a-TON](https://github.com/Grejwood/Hack-a-TON)—OnlyTONs payments project ([website](https://main.d3puvu1kvbh8ti.amplifyapp.com/), [video](https://www.youtube.com/watch?v=38JpX1vRNTk)) +- [nns2009/Hack-a-TON-1_Tonario](https://github.com/nns2009/Hack-a-TON-1_Tonario)—OnlyGrams payments project ([website](https://onlygrams.io/), [video](https://www.youtube.com/watch?v=gm5-FPWn1XM)) +- [sevenzing/hack-a-ton](https://github.com/sevenzing/hack-a-ton)—Pay-per-Request API usage in TON ([video](https://www.youtube.com/watch?v=7lAnbyJdpOA\&feature=youtu.be)) +- [illright/diamonds](https://github.com/illright/diamonds)—Pay-per-Minute learning platform ([website](https://diamonds-ton.vercel.app/), [video](https://www.youtube.com/watch?v=g9wmdOjAv1s)) + +## See Also + +- [Payments Processing](/develop/dapps/asset-processing) +- [TON Connect](/develop/dapps/ton-connect) From 2d0d4639ed7b1894144a84933635ff81561ea19b Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:37 +0800 Subject: [PATCH 036/219] New translations readme.mdx (Chinese Simplified) --- .../develop/dapps/telegram-apps/README.mdx | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/README.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/README.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/README.mdx new file mode 100644 index 0000000000..5a6d8df4a6 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/README.mdx @@ -0,0 +1,70 @@ +--- +description: Telegram Mini Apps (or TMAs) are web applications that run inside the Telegram messenger. They are built using web technologies — HTML, CSS, and JavaScript. +--- + +import Button from '@site/src/components/button' + +# What are Mini Apps? + +
+ +
+ +Telegram Mini Apps (or TMAs) are web applications that run inside the Telegram messenger. They are built using web technologies — HTML, CSS, and JavaScript. + +:::tip +Since TMAs are web pages and use JavaScript, you need to choose [JS/TS-based SDK](/develop/dapps/apis/sdk#typescript--javascript). +::: + +Unlock the gateway to an **800 million-strong Telegram audience**. Imagine offering your app or service to this massive user base with just a single click. + + + + +## Overview + +Telegram bots can completely replace any website. They support seamless authorization, integrated payments via 20 payment providers (with Google Pay and Apple Pay out of the box), delivering tailored push notifications to users, and much more. + +With Mini Apps, bots get a whole new dimension. Bot developers can create infinitely flexible interfaces with JavaScript, the most widely used programming language in the world. + +Here are some key points about Telegram Mini Apps: + +- **Integration within Telegram**: Telegram Mini Apps are intended to seamlessly integrate into the Telegram app, providing users with a cohesive experience. They can be accessed from within a Telegram chat or group conversation. +- **Enhanced Functionality**: Telegram Mini Apps can offer a wide range of functionalities. They can be used for various purposes, such as gaming, content sharing, productivity tools, and more. These apps extend the capabilities of the Telegram platform beyond basic messaging. +- **Cross-Platform Compatibility**: Since Telegram Mini Apps are web-based, they are available on Android, iOS, PC, Mac and Linux Telegram apps. Users can access them without the need for additional installations in one click. +- **Bot Interaction**: Telegram Mini Apps often make use of Telegram bots to provide interactive and automated experiences. Bots can respond to user input, perform tasks, and facilitate interactions within the Mini App. +- **Development Frameworks**: Developers can build Telegram Mini Apps using web development technologies like HTML, CSS, and JavaScript. Additionally, Telegram provides developer tools and APIs for creating these apps and integrating them with the Telegram platform. +- **Monetization Opportunities**: Telegram Mini Apps can be monetized in various ways, such as through in-app purchases, subscription models, or advertising, making them attractive to developers and businesses. +- **Web3 Ready**: TON SDK; TON Connect is a communication protocol between wallets and apps in TON; Tokens +- **Community Development**: Telegram has a thriving developer community, and many third-party developers create and share their Telegram Mini Apps with users. This community-driven approach fosters innovation and diversity in the available apps. + +Overall, Telegram Mini Apps serve as a means to enhance the Telegram experience by offering additional functionalities and services, while also providing developers with an opportunity to create and distribute their applications within the Telegram ecosystem. + +## Getting Started + +### TMA Documentation + +- [Telegram Mini Apps Documentation](https://docs.telegram-mini-apps.com) — a community-driven documentation for TWA. +- [TMA Documentation by Telegram](https://core.telegram.org/bots/webapps) — full description on Telegram website. + +### Telegram Developers Community + +Join a special Telegram Developers Chat to discuss Mini Apps development and get support: + + + +### Mini Apps SDKs + +- [twa-dev/sdk](https://github.com/twa-dev/sdk) — NPM package for TMA SDK +- [twa-dev/boilerplate](https://github.com/twa-dev/Boilerplate) — another boilerplate for a new TWA. +- [twa-dev/Mark42](https://github.com/twa-dev/Mark42) — Mark42 is a simple lightweight tree-shakable UI library for TWA. +- [ton-defi-org/tonstarter-twa](https://github.com/ton-defi-org/tonstarter-twa) — template for new TWA interaction with TON. + +## Integrate with TON Connect + +Connect with users wallets with the help of TON Connect protocol. Read more about it here: + + From b4072a7f542c51427b2b87f13b95f2dc8f701658 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:38 +0800 Subject: [PATCH 037/219] New translations app-examples.mdx (Chinese Simplified) --- .../dapps/telegram-apps/app-examples.mdx | 297 ++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/app-examples.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/app-examples.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/app-examples.mdx new file mode 100644 index 0000000000..120b5257ec --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/app-examples.mdx @@ -0,0 +1,297 @@ +import Button from '@site/src/components/button' + +# TMA Examples + +Check out the examples below to see how to create your own Telegram Mini App. + +## Basic TMA Example + +

+
+ logo of telegram mini apps +
+

+ +This is a basic and straightforward Telegram Mini App(TMA) implemented using plain JavaScript, HTML, and CSS. This project aims to provide a minimalistic example of how to create a simple TMA and launch it within Telegram without relying on complex build tools or bleeding-edge libraries. + +- App is available via direct link: [t.me/simple_telegram_mini_app_bot/app](https://t.me/simple_telegram_mini_app_bot/app) +- Or you can launch app with a bot menu button: [t.me/simple_telegram_mini_app_bot](https://t.me/simple_telegram_mini_app_bot) +- Deployment URL: + + + + +### Features + +- Minimalistic user interface. +- No external libraries or frameworks used. +- Easy to understand and modify. + +### Getting Started + +#### Prerequisites + +To run this example, you'll need a modern web browser with JavaScript enabled. + +#### Installation + +1. Clone this repository to your local machine: + +```bash +git clone https://github.com/Telegram-Mini-Apps-Dev/vanilla-js-boilerplate.git +``` + +2. Navigate to the project directory: + +```bash +cd vanilla-js-boilerplate +``` + +Open index.html in your preferred code editor or IDE. + +### Usage + +1. Open index.html in your preferred code editor or IDE. +2. Make your changes +3. Create your own GitHub repository, commit and push your updates. +4. Go to your repository GitHub page and open Settings. Check the Pages tab and Build and deployment section. If GitHub Actions option was selected, assets should be deployed to Pages and there will be an URL like `https://.github.io/vanilla-js-boilerplate/`. You can copy this URL and use it with [BotFather](https://tg.me/BotFather) bot to create your very own TWA. + +## Modern TMA Example + +### Introduction + +Vite (which means "fast" in French) is a front-end build tool and development server that aims to provide a faster and leaner development experience for modern web projects. We will utilise Vite to create Telegram Mini App example. + +You can find example project here or you can go through following instructions. + +### Prerequisites + +We are going to start with scaffolding your Vite project. + +With NPM: + +```bash +$ npm create vite@latest +``` + +With Yarn: + +```bash +$ yarn create vite +``` + +Then follow the prompts! + +Or you can simply run this command to create React project with TypeScript Support: + +```bash +# npm 7+, extra double-dash is needed: +npm create vite my-react-telegram-web-app -- --template react-ts + +# or yarn +yarn create vite my-react-telegram-web-app --template react-ts + +# this will change the directory to recently created project +cd my-react-telegram-web-app +``` + +### Development of Mini App + +Now we need to start development mode of the project, run following commands in terminal: + +```bash +# npm +npm install +npm run dev --host + +# or yarn +yarn +yarn dev --host +``` + +`--host` option allows to get URL with IP address, which you can use for test purposes during development process. Important note: In development mode we are going to use self-signed SSL certificate, which will give us option to test our app with hot reload only in web version of Telegram [https://web.telegram.org](https://web.telegram.org/a/#6549734463)/ due to the other platforms (iOS, Android, MacOS) policies. + +We need to add `@vitejs/plugin-basic-ssl` plugin: + +```bash npm2yarn +npm install @vitejs/plugin-basic-ssl +``` + +Now we need to change `vite.config.ts`. Add import: + +```jsx +import basicSsl from '@vitejs/plugin-basic-ssl'; +``` + +And add the plugin + +```jsx +export default defineConfig({ + plugins: [react(), basicSsl()] +}); +``` + +You can use `ngrok` to expose your local server to the Internet with SSL certificate. You will be able to develop with hot module replacement on all Telegram platforms. Open new terminal window and run: + +```bash +# where 5173 is the port number from npm/yarn dev --host +ngrok http 5173 +``` + +Also, we are going to prepare our project for deployment to GitHub Pages: + +```jsx +export default defineConfig({ + plugins: [react(), basicSsl()], + build: { + outDir: './docs' + }, + base: './' +}); +``` + +We will use deploy script for GitHub Actions which will run on pushes targeting the master branch. From the root of your project: + +```bash +# we are going to create GitHub Actions config for deployment +mkdir .github +cd .github +mkdir workflows +cd workflows +touch static.yml +``` + +Now add this config to `static.yml`: + +```yaml +# Simple workflow for deploying static content to GitHub Pages +name: Deploy static content to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ['master'] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets the GITHUB_TOKEN permissions to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow one concurrent deployment +concurrency: + group: 'pages' + cancel-in-progress: true + +jobs: + # Single deploy job since we're just deploying + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 18 + cache: 'npm' + cache-dependency-path: './' + - name: Install dependencies + run: npm install + - name: Build + run: npm run build + - name: Setup Pages + uses: actions/configure-pages@v3 + - name: Upload artifact + uses: actions/upload-pages-artifact@v1 + with: + # Upload dist repository + path: './docs' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v1 +``` + +Don’t forget to choose GitHub Actions option for Build and Deployment in Settings→Pages of your GitHub repo. Now after each push, your code will be deployed to Pages. + +![Screenshot 2023-09-11 at 22.07.44.png](/img/docs/telegram-apps/modern-1.png) + +And now we are going two add `@twa-dev/sdk`. Telegram distributes SDK via [link](https://core.telegram.org/bots/webapps#initializing-web-apps). It's kinda old fashion way to work with a library. `@twa-dev/sdk` package allows to work with SDK as with an npm package and with TypeScript support. + +```bash npm2yarn +npm install @twa-dev/sdk +``` + +Open `/src/main.tsx` file and add following: + +```tsx +import WebApp from '@twa-dev/sdk' + +WebApp.ready(); + +ReactDOM.createRoot... +``` + +`WebApp.ready()` - is a method that informs the Telegram app that the Mini App is ready to be displayed. It is recommended to call this method as early as possible, as soon as all essential interface elements are loaded. Once this method is called, the loading placeholder is hidden and the Mini App is shown. + +Then we will add some interaction with user. Go to `src/App.tsx` and we will add the button with alert. + +```tsx +import { useState } from 'react' +import reactLogo from './assets/react.svg' +import viteLogo from '/vite.svg' +import './App.css' + +import WebApp from '@twa-dev/sdk' + +function App() { + const [count, setCount] = useState(0) + + return ( + <> + +

Vite + React

+
+ +
+ {/* Here we add our button with alert callback */} +
+ +
+ + ) +} + +export default App +``` + +And now we need to create Telegram Bot so we can launch Telegram Mini App within messenger application. + +### Setting Up a Bot for the App + +To connect your Mini App to the Telegram, you need to create a bot and set up a Mini App for it. Follow these steps to set up a new Telegram bot: + + + +### Hints + +With self-signed SSL certificate you can challenge issues with this kind of alerts. Click “Advanced” button and the click `Proceed `. Without this steps you will not be able to debug in web version of Telegram + +![Screenshot 2023-09-11 at 18.58.24.png](/img/docs/telegram-apps/modern-2.png) From a832952c35a1e2d7cf66fa9790149e7826748e1c Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:39 +0800 Subject: [PATCH 038/219] New translations design-guidelines.mdx (Chinese Simplified) --- .../dapps/telegram-apps/design-guidelines.mdx | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/design-guidelines.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/design-guidelines.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/design-guidelines.mdx new file mode 100644 index 0000000000..994f084fa0 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/design-guidelines.mdx @@ -0,0 +1,62 @@ +# TMA Design Guidelines + +:::info +Starting with version **6.10**, Telegram has updated the color palette for Mini Apps: a couple of old ones were fixed, and new ones were added. +::: + +For context let's remember the history of updates. + +Changelog. + +1. The `bg_color` and `secondary_bg_color` have been updated. + +![](/img/docs/tma-design-guidelines/tma-design_1.png) + +What are the reasons: + +• These colors were originally intended for use on page backgrounds, not UI controls. + +• Therefore, for consistency, they have been updated. + +• To color the backgrounds of different sections and cards, section_bg_color was added. + +To improve the appearance of your applications, you should slightly adjust the use of color variables. + +Above is a clear example that explains exactly what will change for iOS. There should be no changes on Android. + +New colors. +Also, many new colors were added. Most of them are most noticeable on Android. Therefore, the examples below will be shown based on Android, but are relevant for all platforms. + +![](/img/docs/tma-design-guidelines/tma-design_2.png) + +2. For Mini Apps, the ability to use Telegram header colors has become available. + +![](/img/docs/tma-design-guidelines/tma-design_3.png) + +3. The token accent_text_color has become available, which is useful for any accent elements in your applications. Previously, everyone used the less suitable dark link_color. + +![](/img/docs/tma-design-guidelines/tma-design_4.png) + +4. For all secondary cell labels, it's now better to use `subtitle_text_color`. This will allow for more contrasting label, improving the accessibility of your applications. + +![](/img/docs/tma-design-guidelines/tma-design_5.png) + +5. For section headers of cards, there is now a dedicated token: `section_header_text_color`. + +![](/img/docs/tma-design-guidelines/tma-design_6.png) + +6. For cells whose pressing will lead to a destructive action, you can now use `destructive_text_color` instead of custom ones. + +

+
+ +
+

+ +7. A reasonable question arises: how should `link_color` and `hint_color` be used now? + +I recommend using them as colors for hint sections under the sections, and the link color for such backgrounds as `secondary_bg_color`. + +## See Also + +- [Original Source](https://telegra.ph/Changes-in-Color-Variables-for-Telegram-Mini-Apps-11-20) From de5b4139413141437092222ebcd30de0286a9cb2 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:40 +0800 Subject: [PATCH 039/219] New translations grants.mdx (Chinese Simplified) --- .../current/develop/dapps/telegram-apps/grants.mdx | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/grants.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/grants.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/grants.mdx new file mode 100644 index 0000000000..4b78e3a4ed --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/grants.mdx @@ -0,0 +1,13 @@ +import Button from '@site/src/components/button' + +# Grants + +## Telegram Web3 Grants + +To further boost innovation, [TON Foundation](https://ton.foundation/en) has rolled out the [Telegram Web3 Grants](http://t.me/toncoin/991) program. This initiative is designed to motivate more developers to either create new platforms or migrate existing ones to TON and Telegram. + +## How to participate? + +Whether you're a well-established business, a new startup, or an individual developer, now is the perfect time to get involved. Submit your Telegram application via [this bot](https://t.me/app_moderation_bot) and consider participating in [the grant program](https://t.me/trendingapps/33). Let's pioneer the future together. + + From 748360946e3e7beba08799025661ab82dfb1048e Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:40 +0800 Subject: [PATCH 040/219] New translations monetization.mdx (Chinese Simplified) --- .../dapps/telegram-apps/monetization.mdx | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/monetization.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/monetization.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/monetization.mdx new file mode 100644 index 0000000000..05bb243caa --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/monetization.mdx @@ -0,0 +1,54 @@ +import Button from '@site/src/components/button' +import ThemedImage from '@theme/ThemedImage'; + +# Monetization + +## Wallet Pay + +

+Wallet Pay illustration +

+Wallet Pay is the primary payment system for Telegram Mini Apps, supporting both crypto and fiat transactions. Monitor your order statistics and easily withdraw funds. +Embedded within the Wallet ecosystem, Wallet Pay facilitates seamless financial exchanges between merchants and their customers. + +Useful links: + +- [Wallet Pay Business Support](https://t.me/WalletPay_supportbot) is a Telegram bot for reaching out the Wallet Pay Support Team. +- [Demo Store Bot](https://t.me/PineAppleDemoWPStoreBot) is a Telegram bot for Wallet Pay functionality introduction. (Attention: all payments are carried out in real assets) +- [Merchant Community](https://t.me/+6TReWBEyZxI5Njli) is a Telegram group for sharing an experience and solutions between group members. + +## TON Connect + +

+ +

+ +TON Connect is a communication protocol between **wallets** and **apps** in TON. + +**Apps** built on TON provide rich functionality and high performance and are designed to protect user funds via smart contracts. Because apps are built using decentralized technologies such as Blockchain, they are typically called decentralized applications (dApps). + +**Wallets** provide the UI to approving transactions and hold users’ cryptographic keys securely on their personal devices. This separation of concerns enables rapid innovation and high level of security for the users: wallets do not need to build walled-garden ecosystems themselves, while the apps do not need to take the risk holding end users’ accounts. + +TON Connect aims to offer a seamless user experience between wallets and apps. + + + +## Integrate Tokens + +You can create your own token on TON Blockchain and integrate it into your app. You can also integrate existing tokens into your app. + + + +## Subscriptions on TON + +Due to the fact that transactions in TON Blockchain are fast and the network fees are low, you can process recurring payments on-chain via smart contracts. + +For example, users can subscribe to digital content (or anything else) and be charged a monthly fee of 1 TON. + + From 9c929daa44dc2a9625fd4c2ceeb4bec155bbdf73 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:41 +0800 Subject: [PATCH 041/219] New translations publishing.mdx (Chinese Simplified) --- .../dapps/telegram-apps/publishing.mdx | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/publishing.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/publishing.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/publishing.mdx new file mode 100644 index 0000000000..51e9924a81 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/publishing.mdx @@ -0,0 +1,106 @@ +import Button from '@site/src/components/button' + +# Publishing Mini Apps + +As developers, it's important to understand the ecosystem in which we operate. Telegram provides a unique opportunity for Mini App developers, thanks to its robust platform and expansive user base. This article will guide you through the available channels for publishing your Mini Apps on Telegram. + +## tApps Center + +**What is tApps Center?** The TON Foundation has introduced the Telegram Apps Center to create a centralized repository for Telegram Bots and Mini Apps (TMAs). This platform aims to enhance the user experience by offering an interface similar to the well-known app stores you're already familiar with. + +**Broad Ecosystem Support**. The Telegram Apps Center doesn't solely focus on the TON ecosystem; it also welcomes apps from other blockchains. You don't even need web3 integration to be part of this catalog. This inclusive approach aims to establish Telegram as an "Everything Super App," similar to platforms like WeChat, where users can access a variety of services within a single interface. + + + +### Why Publish on tApps Center? + +**Greater Visibility**. The Telegram Apps Center offers a golden opportunity for developers to showcase their projects to a wide audience, making it easier to attract users and investors. + +**Community Spirit**. The platform embraces a community-centric approach, encouraging collaborations and the sharing of resources and knowledge. + + + +## Launch within Telegram + +Telegram currently supports six different ways of launching Mini Apps: from a [keyboard button](https://core.telegram.org/bots/webapps#keyboard-button-web-apps), from an [inline button](https://core.telegram.org/bots/webapps#inline-button-web-apps), from the [bot menu button](https://core.telegram.org/bots/webapps#launching-web-apps-from-the-menu-button), via [inline mode](https://core.telegram.org/bots/webapps#inline-mode-web-apps), from a [direct link](https://core.telegram.org/bots/webapps#direct-link-web-apps) – and even from the [attachment menu](https://core.telegram.org/bots/webapps#launching-web-apps-from-the-attachment-menu). + +![](/img/docs/telegram-apps/publish-tg-1.jpeg) + +### Keyboard Button Mini Apps + +**TL;DR:** Mini Apps launched from a **web_app** type [keyboard button](https://core.telegram.org/bots/api#keyboardbutton) can send data back to the bot in a _service message_ using [Telegram.WebApp.sendData](https://core.telegram.org/bots/webapps#initializing-web-apps). This makes it possible for the bot to produce a response without communicating with any external servers. + +Users can interact with bots using [custom keyboards](https://core.telegram.org/bots#keyboards), [buttons under bot messages](https://core.telegram.org/bots#inline-keyboards-and-on-the-fly-updating), as well as by sending freeform **text messages** or any of the **attachment types** supported by Telegram: photos and videos, files, locations, contacts and polls. For even more flexibility, bots can utilize the full power of **HTML5** to create user-friendly input interfaces. + +You can send a **web_app** type [KeyboardButton](https://core.telegram.org/bots/api#keyboardbutton) that opens a Mini App from the specified URL. + +To transmit data from the user back to the bot, the Mini App can call the [Telegram.WebApp.sendData](https://core.telegram.org/bots/webapps#initializing-web-apps) method. Data will be transmitted to the bot as a String in a service message. The bot can continue communicating with the user after receiving it. + +**Good for:** + +- **Сustom data input interfaces** (a personalized calendar for selecting dates; selecting data from a list with advanced search options; a randomizer that lets the user “spin a wheel” and chooses one of the available options, etc.) +- **Reusable components** that do not depend on a particular bot. + +### Inline Button Mini Apps + +**TL;DR:** For more interactive Mini Apps like [@DurgerKingBot](https://t.me/durgerkingbot), use a **web_app** type [Inline KeyboardButton](https://core.telegram.org/bots/api#inlinekeyboardbutton), which gets basic user information and can be used to send a message on behalf of the user to the chat with the bot. + +If receiving text data alone is insufficient or you need a more advanced and personalized interface, you can open a Mini App using a **web_app** type [Inline KeyboardButton](https://core.telegram.org/bots/api#inlinekeyboardbutton). + +From the button, a Mini App will open with the URL specified in the button. In addition to the user's [theme settings](https://core.telegram.org/bots/webapps#color-schemes), it will receive basic user information (ID, name, username, language_code) and a unique identifier for the session, **query_id**, which allows messages on behalf of the user to be sent back to the bot. + +The bot can call the Bot API method [answerWebAppQuery](https://core.telegram.org/bots/api#answerwebappquery) to send an inline message from the user back to the bot and close the Mini App. After receiving the message, the bot can continue communicating with the user. + +**Good for:** + +- Fully-fledged web services and integrations of any kind. +- The use cases are effectively **unlimited**. + +### Launching Mini Apps from the Menu Button + +**TL;DR:** Mini Apps can be launched from a customized menu button. This simply offers a quicker way to access the app and is otherwise **identical** to [launching a Mini App from an inline button](https://core.telegram.org/bots/webapps#inline-button-web-apps). + +By default, chats with bots always show a convenient **menu button** that provides quick access to all listed [commands](https://core.telegram.org/bots#commands). With [Bot API 6.0](https://core.telegram.org/bots/api-changelog#april-16-2022), this button can be used to **launch a Mini App** instead. + +To configure the menu button, you must specify the text it should show and the Mini App URL. There are two ways to set these parameters: + +- To customize the button for **all users**, use [@BotFather](https://t.me/botfather) (the /setmenubutton command or _Bot Settings > Menu Button_). +- To customize the button for both **all users** and **specific users**, use the [setChatMenuButton](https://core.telegram.org/bots/api#setchatmenubutton) method in the Bot API. For example, change the button text according to the user's language, or show links to different Web Apps based on a user's settings in your bot. + +Apart from this, Web Apps opened via the menu button work in the exact same way as when [using inline buttons](https://core.telegram.org/bots/webapps#inline-button-web-apps). + +[@DurgerKingBot](https://t.me/durgerkingbot) allows launching its Mini App both from an inline button and from the menu button. + +### Inline Mode Mini Apps + +**TL;DR:** Mini Apps launched via **web_app** type [InlineQueryResultsButton](https://core.telegram.org/bots/api#inlinequeryresultsbutton) can be used anywhere in inline mode. Users can create content in a web interface and then seamlessly send it to the current chat via inline mode. + +NEW You can use the _button_ parameter in the [answerInlineQuery](https://core.telegram.org/bots/api#answerinlinequery) method to display a special 'Switch to Mini App' button either above or in place of the inline results. This button will **open a Mini App** from the specified URL. Once done, you can call the [Telegram.WebApp.switchInlineQuery](https://core.telegram.org/bots/webapps#initializing-web-apps) method to send the user back to inline mode. + +Inline Mini Apps have **no access** to the chat – they can't read messages or send new ones on behalf of the user. To send messages, the user must be redirected to **inline mode** and actively pick a result. + +**Good for:** + +- Fully-fledged web services and integrations in inline mode. + +### Direct Link Mini Apps + +**TL;DR:** Mini App Bots can be launched from a direct link in any chat. They support a _startapp_ parameter and are aware of the current chat context. + +NEW You can use direct links to **open a Mini App** directly in the current chat. If a non-empty _startapp_ parameter is included in the link, it will be passed to the Mini App in the _start_param_ field and in the GET parameter _tgWebAppStartParam_. + +In this mode, Mini Apps can use the _chat_type_ and _chat_instance_ parameters to keep track of the current chat context. This introduces support for **concurrent** and **shared** usage by multiple chat members – to create live whiteboards, group orders, multiplayer games and similar apps. + +Mini Apps opened from a direct link have **no access** to the chat – they can't read messages or send new ones on behalf of the user. To send messages, the user must be redirected to **inline mode** and actively pick a result. + +**Examples** + +- https://t.me/botusername/appname +- https://t.me/botusername/appname?startapp=command + +**Good for:** + +- Fully-fledged web services and integrations that any user can open in one tap. +- Cooperative, multiplayer or teamwork-oriented services within a chat context. + +The use cases are effectively **unlimited**. From d19b8e9b89d31d7826a669e00b716d84b7df1cbe Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:42 +0800 Subject: [PATCH 042/219] New translations step-by-step-guide.mdx (Chinese Simplified) --- .../telegram-apps/step-by-step-guide.mdx | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/step-by-step-guide.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/step-by-step-guide.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/step-by-step-guide.mdx new file mode 100644 index 0000000000..f4411565e8 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/step-by-step-guide.mdx @@ -0,0 +1,63 @@ +# TMA Launch Tutorial + +Telegram Mini Apps (TMA) are web applications that run inside the Telegram messenger. They are built using web technologies — HTML, CSS, and JavaScript. Telegram Mini Apps can be used to create DApps, games, and other types of apps that can be run inside Telegram. + +## Create your App + +1. To connect your Mini App to the Telegram, place the SDK script `telegram-web-app.js` using this code: + +```html + +``` + +:::tip +It's preferable to switch off cache in the HTML. To ensure your cache switched off, specify headers in your request according the following: + +```curl +Cache-Control: no-store, must-revalidate +Pragma: no-cache +Expires: 0 +``` + +::: + +2. Once the script is connected, a **[window.Telegram.WebApp](https://core.telegram.org/bots/webapps#initializing-web-apps)** object become available. You can read more about creating Mini App utilising [`telegram-web-app.js`](https://docs.ton.org/develop/dapps/telegram-apps/app-examples#basic-twa-example) here. + +3. The modern way to connect SDK is npm package for Telegram Mini Apps SDK: + +```bash npm2yarn +npm i @twa-dev/sdk +``` + +You can find guide for [`@twa-dev/sdk`](https://docs.ton.org/develop/dapps/telegram-apps/app-examples#modern-twa-example) here. + +5. When your Mini App is ready and deployed to the web server, follow to the next step. + +## Setting Up a Bot for the App + +To connect your Mini App to the Telegram, you need to create a bot and set up a Mini App for it. Follow these steps to set up a new Telegram bot: + +### 1. Start a Chat with BotFather + +- Open the Telegram app or web version. +- Search for `@BotFather` in the search bar or follow the link . +- Start a chat with BotFather by clicking on the `START` button. + +### 2. Create a New Bot + +- Send `/newbot` command to BotFather. +- BotFather will ask you to choose a name for your bot. This is a display name and can contain spaces. +- Next, you'll be asked to choose a username for your bot. This must end in `bot` (e.g., `sample_bot`) and be unique. + +### 3. Set Up Bot Mini App + +- Send `/mybots` command to BotFather. +- Choose your bot from the list and the **Bot settings** option +- Choose **Menu button** option +- Choose **Edit menu button URL** option and send URL to your Telegram Mini App, for example link from GitHub Pages deploy. + +### 4. Accessing the Bot + +- You can now search for your bot using its username in Telegram's search bar. +- Press the button next to attach picker to launch your Telegram Mini App in messenger +- You’re awesome! From 41d0553c6b2534fc3eb6ba39a86ca517a8e91d5d Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:42 +0800 Subject: [PATCH 043/219] New translations testing-apps.mdx (Chinese Simplified) --- .../dapps/telegram-apps/testing-apps.mdx | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/testing-apps.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/testing-apps.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/testing-apps.mdx new file mode 100644 index 0000000000..7e1a85f966 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/testing-apps.mdx @@ -0,0 +1,92 @@ +import ConceptImage from '@site/src/components/conceptImage' + +# Testing Mini Apps + +## Using bots in the test environment + +To log in to the test environment, use either of the following: + +- **iOS:** tap 10 times on the Settings icon > Accounts > Login to another account > Test. +- **Telegram Desktop:** open ☰ Settings > Shift + Alt + Right click ‘Add Account’ and select ‘Test Server’. +- **macOS:** click the Settings icon 10 times to open the Debug Menu, ⌘ + click ‘Add Account’ and log in via phone number. + +The test environment is completely separate from the main environment, so you will need to create a **new user account** and a **new bot** with @BotFather. + +After receiving your bot token, you can send requests to the Bot API in this format: + +`https://api.telegram.org/bot/test/METHOD_NAME` + +**Note:** When working with the test environment, you may use HTTP links without TLS to test your Mini App. + +## Debug Mode for Mini Apps + +Use these tools to find app-specific issues in your Mini App: + +### Android + +- [Enable USB-Debugging](https://developer.chrome.com/docs/devtools/remote-debugging/) on your device. +- In Telegram Settings, scroll all the way down, press and hold on the **version number** two times. +- Choose _Enable WebView Debug_ in the Debug Settings. +- Connect your phone to your computer and open chrome://inspect/#devices in Chrome – you will see your Mini App there when you launch it on your phone. + +### Telegram Desktop on Windows and Linux + +- Download and launch the [Beta Version](https://desktop.telegram.org/changelog#beta-version) of Telegram Desktop on **Windows** or **Linux** (not supported on Telegram Desktop for macOS yet). +- Go to _Settings > Advanced > Experimental settings > Enable webview inspection_. +- Right click in the WebView and choose _Inspect_. + +### Telegram macOS + +- Download and launch the [Beta Version](https://telegram.org/dl/macos/beta) of Telegram macOS. +- Quickly click 5 times on the Settings icon to open the debug menu and enable “Debug Mini Apps”. + +Right click in the mini app and choose _Inspect Element_. + +## Testing with Eruda + +[Eruda](https://github.com/liriliri/eruda) is a tool that provides a web-based console for debugging and inspecting web pages on mobile devices and desktop browsers. Here's a step-by-step guide on how to use Eruda in a Telegram Mini Apps project. + +![1](/img/docs/telegram-apps/eruda-1.png) + +### Step 1: Include Eruda Library + +First, you need to include the Eruda library in your HTML file. You can include it from a CDN: + +```html + + +``` + +Or you can get it on npm. + +```bash npm2yarn +npm install eruda --save +``` + +### Step 2: Initialise Eruda + +Next, you need to initialize Eruda. You typically do this when the web page loads. In case you run Eruda from CDN. + +```html + + + +``` + +If you prefer modern tooling and packages add this script to your project: + +```jsx +import eruda from 'eruda' + +eruda.init() +``` + +### Step 3: Launch Eruda + +Deploy your Mini App, launch it and just press Eruda icon to start debugging! + + + From e17c143df14e0717540a304586efcc64c5530948 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:43 +0800 Subject: [PATCH 044/219] New translations tips-and-tricks.mdx (Chinese Simplified) --- .../dapps/telegram-apps/tips-and-tricks.mdx | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/tips-and-tricks.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/tips-and-tricks.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/tips-and-tricks.mdx new file mode 100644 index 0000000000..b54ab16f78 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/telegram-apps/tips-and-tricks.mdx @@ -0,0 +1,54 @@ +# Tips and Tricks + +On this page, you'll find a list of frequently asked questions related to issues in TMA. + +### How to solve the cache overflow issue in TMA? + +:::tip +Only reinstalling the Telegram application could be helpful. +::: + +### Are there any recommendations on caching headers for HTML files? + +:::tip +It's preferable to switch off cache in the HTML. To ensure your cache switched off, specify headers in your request according the following: + +```curl +Cache-Control: no-store, must-revalidate +Pragma: no-cache +Expires: 0 +``` + +::: + +### What is suggested IDE for development TMA? + +The process of development in Google Chrome more convenient because of the familiar dev tools. + +You can retrieve the launch parameters of the mini-app and open this link in Chrome. For our case, the easiest way is to retrieve the launch parameters from the web version of Telegram: + +### Closing Behaviour + +In many web applications, users might accidentally close the app while scrolling to the top. This can happen if they drag a section of the application too far, inadvertently triggering the app to close. + +

+
+ closing_behaviour_durgerking +
+

+ +To prevent such accidental closures, enable `closing_behavior` in the TMA. This method will add a dialog where the user can either approve or decline the closing of the Web App. + +```typescript +window.Telegram.WebApp.enableClosingConfirmation() +``` + +## How to specify a description for a certain language in the TMA? + +:::tip +You can configure your description with following methods: + +- https://core.telegram.org/bots/api#setmydescription +- https://core.telegram.org/bots/api#setmyshortdescription + +::: From ad882b1a021c558865758086e810eed22cf05b42 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:44 +0800 Subject: [PATCH 045/219] New translations readme.mdx (Chinese Simplified) --- .../develop/dapps/ton-connect/README.mdx | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/README.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/README.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/README.mdx new file mode 100644 index 0000000000..bf0f092008 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/README.mdx @@ -0,0 +1,31 @@ +import Button from '@site/src/components/button' +import ThemedImage from '@theme/ThemedImage'; + +# How TON Connect works + +TON Connect is a communication protocol between **wallets** and **apps** in TON. + +

+ +

+ +## Bird's eye view + +**Apps** built on TON provide rich functionality and high performance and are designed to protect user funds via smart contracts. Because apps are built using decentralized technologies such as Blockchain, they are typically called decentralized applications (dApps). + +**Wallets** provide the UI to approving transactions and hold users’ cryptographic keys securely on their personal devices. +This separation of concerns enables rapid innovation and high level of security for the users: wallets do not need to build walled-garden ecosystems themselves, while the apps do not need to take the risk holding end users’ accounts. + +TON Connect aims to offer a seamless user experience between wallets and apps. + +## See also + +- [TON Connect for Business](/develop/dapps/ton-connect/business) +- [TON Connect Security](/develop/dapps/ton-connect/security) +- [TON Connect 2.0 vs 1.0](/develop/dapps/ton-connect/comparison) From d1d2b0883a7e2831fa7336976d4917d3ab06c6c4 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:45 +0800 Subject: [PATCH 046/219] New translations best-practices.md (Chinese Simplified) --- .../current/develop/dapps/ton-connect/best-practices.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/best-practices.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/best-practices.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/best-practices.md new file mode 100644 index 0000000000..7575adee2e --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/best-practices.md @@ -0,0 +1,3 @@ +# Best Practices + +TODO From 31b94037bfe523bed76d64f6d96d37e6c96703ca Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:46 +0800 Subject: [PATCH 047/219] New translations business.md (Chinese Simplified) --- .../develop/dapps/ton-connect/business.md | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/business.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/business.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/business.md new file mode 100644 index 0000000000..f10c35ce3f --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/business.md @@ -0,0 +1,46 @@ +# TON Connect for Business + +TON Connect is built to be customizable for businesses by offering powerful features that attract traffic and increase user retention. + +## Product features + +- secure and private authentication with controlled personal data disclosure +- arbitrary transaction signing on TON within a single user session +- instant connectivity between applications and user wallets +- automatic application availability directly within wallets + +## Adopting TON Connect + +### Basic steps + +In order for developers to integrate TON Connect into their applications the specialized TON Connect SDK is used. The process is quite simple and can be performed by accessing the correct documentation when needed. + +TON Connect allows users to connect their applications with numerous wallets via a QR code or universal connectivity link. Apps can also be opened within a wallet using a built-in browser extension and it is critical to keep up to date with additional features that are added to TON Connect moving forward. + +### Developer Integration assistance for TON Connect + +1. Describing the existing user flow of your application +2. Identifying the operations required (e.g. transaction authorization, message signing) +3. Describing your technology stack to our team + +If you’re interested in learning more about TON Connect and its various services and capabilities, feel free to reach out to TON Connect Business [developer](https://t.me/tonrostislav) to discuss your desired solution. + +### Common implementation cases + +By using the [TON Connect SDK](https://github.com/ton-connect/sdk) , detailed instructions to integrate TON Connect allows developers to: + +- connect their applications with carious TON wallet types +- backend login via the corresponding wallet's address +- sending request transactions and in-wallet signing(accepting requests) + +To gain a better understand of what is possible with this solution, check out our demo app that is available on GitHub: [https://github.com/ton-connect/](https://github.com/ton-connect/demo-dapp) + +### Currently supported technology stack: + +- all web applications — serverless and backends +- React-Native mobile apps +- coming soon: SDK for mobile applications in Swift, Java, Kotlin + +TON Connect is an open protocol and can be used to develop dapps with any programming language or development environment. + +For JavaScript (JS) applications, the TON developer community created a JavaScript SDK that allows developers to integrate TON Connect seamlessly in minutes. In the future, SDKs designed to operate with additional programming languages will be available. From 9723a065cfb62bb8a4a3a87650e318fe5ddd23ae Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:46 +0800 Subject: [PATCH 048/219] New translations comparison.md (Chinese Simplified) --- .../develop/dapps/ton-connect/comparison.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/comparison.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/comparison.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/comparison.md new file mode 100644 index 0000000000..1a6759226b --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/comparison.md @@ -0,0 +1,19 @@ +# TON Connect 2.0 vs 1.0 + +TON Connect 2.0 solves many problems that were present in TON Connect 1.0. + +The TON Connect 2.0 protocol provides the highest levels of security, a developer-friendly environment for the development of decentralized applications (dApps), and a streamlined user experience driven by real-time UX. + +Please see the comparison between both versions below: + +| | TON&nbsp;Connect&nbsp;v1 | TON&nbsp;Connect&nbsp;v2 | +| :-----------------------------------: | :----------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------: | +| Connecting to web-based dApps | ✔︎ | ✔︎ | +| Sending transactions | ✔︎ | ✔︎ | +| Connecting dapps within the wallet | | ✔︎ | +| QR code scanning | for every action | once, during connection | +| Serverless dApps | | ✔︎ | +| Realtime UX | | ✔︎ | +| Switching accounts | | soon | +| Messages sending between app and user | | soon | +| Wallet compatibility | Tonkeeper | Tonkeeper, OpenMask, MyTonWallet TonHub (soon) and [others](/participate/wallets/apps#basics-features) | From e327177c6d99a2d78a3ed393d3b5e90faed585ea Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:47 +0800 Subject: [PATCH 049/219] New translations developers.md (Chinese Simplified) --- .../develop/dapps/ton-connect/developers.md | 167 ++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/developers.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/developers.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/developers.md new file mode 100644 index 0000000000..bd02866842 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/developers.md @@ -0,0 +1,167 @@ +# TON Connect SDKs + +## SDK List + +:::info +If possible, it is recommended to use the [@tonconnect/ui-react](/develop/dapps/ton-connect/developers#ton-connect-ui-react) kit for your dApps. Only switch to lower levels of the SDK or reimplement your version of the protocol if it is really necessary for your product. +::: + +This page contents the list of useful libraries for TON Connect. + +- [TON Connect React](/develop/dapps/ton-connect/developers#ton-connect-react) +- [TON Connect JS SDK](/develop/dapps/ton-connect/developers#ton-connect-js-sdk) +- [TON Connect Python SDK](/develop/dapps/ton-connect/developers#ton-connect-python) +- [TON Connect Dart](/develop/dapps/ton-connect/developers#ton-connect-dart) +- [TON Connect C#](/develop/dapps/ton-connect/developers#ton-connect-c) +- [TON Connect Unity](/develop/dapps/ton-connect/developers#ton-connect-unity) +- [TON Connect Go](/develop/dapps/ton-connect/developers#ton-connect-go) + +## TON Connect React + +- [@tonconnect/ui-react](/develop/dapps/ton-connect/developers#ton-connect-ui-react) - TON Connect User Interface (UI) for React applications + +TonConnect UI React is a React UI kit for TonConnect SDK. Use it to connect your app to TON wallets via TonConnect protocol in React apps. + +- Example of a DAppwith `@tonconnect/ui-react`: [GitHub](https://github.com/ton-connect/demo-dapp-with-react-ui) +- Example of deployed `demo-dapp-with-react-ui`: [GitHub](https://ton-connect.github.io/demo-dapp-with-react-ui/) + +```bash +npm i @tonconnect/ui-react +``` + +- [GitHub](https://github.com/ton-connect/sdk/tree/main/packages/ui-react) +- [NPM](https://www.npmjs.com/package/@tonconnect/ui-react) +- [API Documentation](https://ton-connect.github.io/sdk/modules/_tonconnect_ui_react.html) + +## TON Connect JS SDK + +The TON Connect repository contains following main packages: + +- [@tonconnect/ui](/develop/dapps/ton-connect/developers#ton-connect-ui) - TON Connect User Interface (UI) +- [@tonconnect/sdk](/develop/dapps/ton-connect/developers#ton-connect-sdk) - TON Connect SDK +- [@tonconnect/protocol](/develop/dapps/ton-connect/developers#ton-connect-protocol-models) - TON Connect protocol specifications + +### TON Connect UI + +TonConnect UI is a UI kit for TonConnect SDK. Use it to connect your app to TON wallets via TonConnect protocol. It allows you to integrate TonConnect to your app easier using our UI elements such as "connect wallet button", "select wallet dialog" and confirmation modals. + +```bash +npm i @tonconnect/ui +``` + +- [GitHub](https://github.com/ton-connect/sdk/tree/main/packages/ui) +- [NPM](https://www.npmjs.com/package/@tonconnect/ui) +- [API Documentation](https://ton-connect.github.io/sdk/modules/_tonconnect_ui.html) + +The TON Connect User Interface (UI) is a framework that allows developers to improve the user experience (UX) for application users. + +TON Connect can easily be integrated with apps using simple UI elements such as the "connect wallet button", "select wallet dialog" and confirmation modals. Here are three main examples of how TON Connect improves UX in apps: + +- Example of app functionality in the DAppbrowser: [GitHub](https://ton-connect.github.io/demo-dapp/) +- Example of a backend partition of the DAppabove: [GitHub](https://github.com/ton-connect/demo-dapp-backend) +- Bridge server using Go: [GitHub](https://github.com/ton-connect/bridge) + +This kit will simplify the implementation of TON Connect in apps built for TON Blockchain. Standard frontend frameworks are supported, as well as applications that don’t use predetermined frameworks. + +### TON Connect SDK + +The most low-level of the three frameworks that helps developers integrate TON Connect into their applications is the TON Connect SDK. It is primarily used to connect apps to TON Wallets via the TON Connect protocol. + +- [GitHub](https://github.com/ton-connect/sdk/tree/main/packages/sdk) +- [NPM](https://www.npmjs.com/package/@tonconnect/sdk) + +### TON Connect protocol models + +This package contains protocol requests, protocol responses, event models and encoding and decoding functions. It can be used to integrate TON Connect to wallet apps written in TypeScript. In order to integrate TON Connect into a DAppthe [@tonconnect/sdk](https://www.npmjs.com/package/@tonconnect/sdk) should be used. + +- [GitHub](https://github.com/ton-connect/sdk/tree/main/packages/protocol) +- [NPM](https://www.npmjs.com/package/@tonconnect/protocol) + +## TON Connect Python + +### pytonconnect + +Python SDK for TON Connect 2.0. Analogue of the `@tonconnect/sdk` library. + +Use it to connect your app to TON wallets via TonConnect protocol. + +```bash +pip3 install pytonconnect +``` + +- [GitHub](https://github.com/XaBbl4/pytonconnect) + +### ClickoTON-Foundation tonconnect + +Library for connecting TON Connect to Python apps + +```bash +git clone https://github.com/ClickoTON-Foundation/tonconnect.git +pip install -e tonconnect +``` + +[GitHub](https://github.com/ClickoTON-Foundation/tonconnect) + +## TON Connect Dart + +Dart SDK for TON Connect 2.0. Analogue of the `@tonconnect/sdk` library. + +Use it to connect your app to TON wallets via TonConnect protocol. + +```bash + $ dart pub add darttonconnect +``` + +- [GitHub](https://github.com/romanovichim/dartTonconnect) + +## TON Connect C\# + +C# SDK for TON Connect 2.0. Analogue of the `@tonconnect/sdk` library. + +Use it to connect your app to TON wallets via TonConnect protocol. + +```bash + $ dotnet add package TonSdk.Connect +``` + +- [GitHub](https://github.com/continuation-team/TonSdk.NET/tree/main/TonSDK.Connect) + +## TON Connect Go + +Go SDK for TON Connect 2.0. + +Use it to connect your app to TON wallets via TonConnect protocol. + +```bash + go get github.com/cameo-engineering/tonconnect +``` + +- [GitHub](https://github.com/cameo-engineering/tonconnect) + +## General Questions and Concerns + +If any of our developers or community members encounter any additional issues during the implementation of TON Connect 2.0, please contact the [Tonkeeper developer](https://t.me/tonkeeperdev) channel. + +If you experience any additional issues, or would like to present a proposal on how to improve TON Connect 2.0, please contact us directly through the appropriate [GitHub directory](https://github.com/ton-connect/). + +## TON Connect Unity + +:::danger +This library is outdated at the moment. + +Please, use [@ton-connect/ui](https://www.npmjs.com/package/@tonconnect/ui) for your Unity applications. +::: + +Unity asset for TON Connect 2.0. Uses `continuation-team/TonSdk.NET/tree/main/TonSDK.Connect`. + +Use it to integrate TonConnect protocol with your game. + +- [GitHub](https://github.com/continuation-team/unity-ton-connect) + +## See Also + +- [Step by step guide for building your first web client](https://ton-community.github.io/tutorials/03-client/) +- [[YouTube] TON Smart Contracts | 10 | Telegram DApp[EN]](https://www.youtube.com/watch?v=D6t3eZPdgAU\&t=254s\&ab_channel=AlefmanVladimir%5BEN%5D) +- [Ton Connect Getting started](https://github.com/ton-connect/sdk/tree/main/packages/sdk) +- [Integration Manual](/develop/dapps/ton-connect/integration) +- [[YouTube] TON Dev Study TON Connect Protocol [RU]](https://www.youtube.com/playlist?list=PLyDBPwv9EPsCJ226xS5_dKmXXxWx1CKz_) From 7c0b6081b32533124e4309b95dfd23982b44beee Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:48 +0800 Subject: [PATCH 050/219] New translations integration.md (Chinese Simplified) --- .../develop/dapps/ton-connect/integration.md | 506 ++++++++++++++++++ 1 file changed, 506 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/integration.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/integration.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/integration.md new file mode 100644 index 0000000000..23e828b286 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/integration.md @@ -0,0 +1,506 @@ +# Integration manual with the JavaScript SDK + +In this tutorial, we’ll create a sample web app that supports TON Connect 2.0 authentication. It will allow for signature verification to eliminate the possibility of fraudulent identity impersonation without agreement establishment between parties. + +## Documentation links + +1. [@tonconnect/sdk documentation](https://www.npmjs.com/package/@tonconnect/sdk) +2. [Wallet-application message exchange protocol](https://github.com/ton-connect/docs/blob/main/requests-responses.md) +3. [Tonkeeper implementation of wallet side](https://github.com/tonkeeper/wallet/tree/main/src/tonconnect) + +## Prerequisites + +In order for connectivity to be fluent between apps and wallets, the web app must make use of manifest that is accessible via wallet applications. The prerequisite to accomplish this is typically a host for static files. For example, say if a developer wants to make use of GitHub pages, or deploy their website using TON Sites hosted on their computer. This would therefore mean their web app site is publicly accessible. + +## Getting wallets support list + +To increase the overall adoption of TON Blockchain, it is necessary that TON Connect 2.0 is able to facilitate a vast number of application and wallet connectivity integrations. Of late and of significant importance, the ongoing development of TON Connect 2.0 has allowed for the connection of the Tonkeeper, TonHub, MyTonWallet and other wallets with various TON Ecosystem Apps. It is our mission to eventually allow for the exchange of data between applications and all wallet types built on TON via the TON Connect protocol. For now, this is realized by providing the ability for TON Connect to load an extensive list of available wallets currently operating within the TON Ecosystem. + +At the moment our sample web app enables the following: + +1. loads the TON Connect SDK (library meant to simplify integration), +2. creates a connector (currently without an application manifest), +3. loads a list of supported wallets (from [wallets.json on GitHub](https://raw.githubusercontent.com/ton-connect/wallets-list/main/wallets.json)). + +For learning purposes, let's take a looks at the HTML page described by the following code: + +```html + + + + + + + + + + +``` + +If you load this page in browser and look into console, you may get something like that: + +```bash +> Array [ {…}, {…} ] + +0: Object { name: "Tonkeeper", imageUrl: "https://tonkeeper.com/assets/tonconnect-icon.png", aboutUrl: "https://tonkeeper.com", … } + aboutUrl: "https://tonkeeper.com" + bridgeUrl: "https://bridge.tonapi.io/bridge" + deepLink: undefined + embedded: false + imageUrl: "https://tonkeeper.com/assets/tonconnect-icon.png" + injected: false + jsBridgeKey: "tonkeeper" + name: "Tonkeeper" + tondns: "tonkeeper.ton" + universalLink: "https://app.tonkeeper.com/ton-connect" +``` + +According to TON Connect 2.0 specifications, wallet app information always makes use of the following format: + +```js +{ + name: string; + imageUrl: string; + tondns?: string; + aboutUrl: string; + universalLink?: string; + deepLink?: string; + bridgeUrl?: string; + jsBridgeKey?: string; + injected?: boolean; // true if this wallet is injected to the webpage + embedded?: boolean; // true if the DAppis opened inside this wallet's browser +} +``` + +## Button display for various wallet apps + +Buttons may vary according to your web application design. +The current page produces the following result: + +```html + + + + + + + // highlight-start + + // highlight-end + + + // highlight-start +
+
+ // highlight-end + + + + +``` + +Please note the following: + +1. If the web page is displayed through a wallet application, it sets the property `embedded` option to `true`. This means it is important to highlight this login option because it's most commonly used. +2. If a specific wallet is built using only JavaScript (it has no `bridgeUrl`) and it hasn't set property `injected` (or `embedded`, for safety), then it is clearly inaccessible and the button should be disabled. + +## Connection without the app manifest + +In the instance the connection is made without the app manifest, the script should be changed as follows: + +```js + const $ = document.querySelector.bind(document); + + window.onload = async () => { + const connector = new TonConnectSDK.TonConnect(); + const walletsList = await connector.getWallets(); + + const unsubscribe = connector.onStatusChange( + walletInfo => { + console.log('Connection status:', walletInfo); + } + ); + + let buttonsContainer = $('#tonconnect-buttons'); + + for (let wallet of walletsList) { + let connectButton = document.createElement('button'); + connectButton.innerText = 'Connect with ' + wallet.name; + + if (wallet.embedded) { + // `embedded` means we are browsing the app from wallet application + // we need to mark this sign-in option somehow + connectButton.classList.add('featured'); + } + + // highlight-start + if (wallet.embedded || wallet.injected) { + connectButton.onclick = () => { + connectButton.disabled = true; + connector.connect({jsBridgeKey: wallet.jsBridgeKey}); + }; + } else if (wallet.bridgeUrl) { + connectButton.onclick = () => { + connectButton.disabled = true; + console.log('Connection link:', connector.connect({ + universalLink: wallet.universalLink, + bridgeUrl: wallet.bridgeUrl + })); + }; + } else { + // wallet app does not provide any auth method + connectButton.disabled = true; + } + // highlight-end + + buttonsContainer.appendChild(connectButton); + } + }; +``` + +Now that the above process has been carried out, status changes are being logged (to see whether TON Connect works or not). Showing the modals with QR codes for connectivity is out of the scope of this manual. For testing purposes, it is possible to use a browser extension or send a connection request link to the user’s phone by any means necessary (for example, using Telegram). +Note: we haven't created an app manifest yet. At this time, the best approach is to analyze the end result if this requirement is not fulfilled. + +### Logging in with Tonkeeper + +In order to log into Tonkeeper, the following link is created for authentication (provided below for reference): + +``` +https://app.tonkeeper.com/ton-connect?v=2&id=3c12f5311be7e305094ffbf5c9b830e53a4579b40485137f29b0ca0c893c4f31&r=%7B%22manifestUrl%22%3A%22null%2Ftonconnect-manifest.json%22%2C%22items%22%3A%5B%7B%22name%22%3A%22ton_addr%22%7D%5D%7D +``` + +When decoded, the `r` parameter produces the following JSON format: + +```js +{"manifestUrl":"null/tonconnect-manifest.json","items":[{"name":"ton_addr"}]} +``` + +Upon clicking the mobile phone link, Tonkeeper automatically opens and then closes, dismissing the request. Additionally, the following error appears in the web app page console: +`Error: [TON_CONNECT_SDK_ERROR] Can't get null/tonconnect-manifest.json`. + +This means the application manifest must be available for download. + +## Connection with using app manifest + +Starting from this point forward, it is necessary to host user files (mostly tonconnect-manifest.json) somewhere. In this instance we’ll use the manifest from another web application. This however is not recommended for production environments, but allowed for testing purposes. + +The following code snippet: + +```js + window.onload = async () => { + const connector = new TonConnectSDK.TonConnect(); + + const walletsList = await connector.getWallets(); + + const unsubscribe = connector.onStatusChange( + walletInfo => { + console.log('Connection status:', walletInfo); + } + ); +``` + +Must be replaced with this version: + +```js + window.onload = async () => { + const connector = new TonConnectSDK.TonConnect({manifestUrl: 'https://ratingers.pythonanywhere.com/ratelance/tonconnect-manifest.json'}); + // highlight-next-line + window.connector = connector; // for experimenting in browser console + + const walletsList = await connector.getWallets(); + + const unsubscribe = connector.onStatusChange( + walletInfo => { + console.log('Connection status:', walletInfo); + } + ); + // highlight-next-line + connector.restoreConnection(); +``` + +In the newer version above, the storing `connector` variable in the `window` was added so it is accessible in the browser console. Additionally, the `restoreConnection` so users don’t have to log in on each web application page. + +### Logging in with Tonkeeper + +If we decline our request from wallet, The result that appeared in the console will `Error: [TON_CONNECT_SDK_ERROR] Wallet declined the request`. + +Therefore, the user is able to accept the same login request if the link is saved. This means the web app should be able to handle the authentication decline as non-final so it works correctly. + +Afterwards, the login request is accepted and is immediately reflected in the browser console as follows: + +```bash +22:40:13.887 Connection status: +Object { device: {…}, provider: "http", account: {…} } + account: Object { address: "0:b2a1ec...", chain: "-239", walletStateInit: "te6cckECFgEAAwQAAgE0ARUBFP8A9..." } + device: Object {platform: "android", appName: "Tonkeeper", appVersion: "2.8.0.261", …} + provider: "http" +``` + +The results above take the following into consideration: + +1. **Account**: information: contains the address (workchain+hash), network (mainnet/testnet), and the wallet stateInit that is used for public key extraction. +2. **Device**: information: contains the name and wallet application version (the name should be equal to what was requested initially, but this can be verified to ensure authenticity), and the platform name and supported features list. +3. **Provider**: contains http -- which allows all requests and responses between the wallet and web applications to be served over the bridge. + +## Logging out and requesting TonProof + +Now we have logged into our Mini App, but... how does the backend know that it is the correct party? To verify this we must request the wallet ownership proof. + +This can be completed only using authentication, so we must log out. Therefore, we run the following code in the console: + +```js +connector.disconnect(); +``` + +When the disconnection process is complete, the `Connection status: null` will be displayed. + +Before the TonProof is added, let's alter the code to show that the current implementation is insecure: + +```js +let connHandler = connector.statusChangeSubscriptions[0]; +connHandler({ + device: { + appName: "Uber Singlesig Cold Wallet App", + appVersion: "4.0.1", + features: [], + maxProtocolVersion: 3, + platform: "ios" + }, + account: { + /* TON Foundation address */ + address: '0:83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8', + chain: '-239', + walletStateInit: 'te6ccsEBAwEAoAAFcSoCATQBAgDe/wAg3SCCAUyXuiGCATOcurGfcbDtRNDTH9MfMdcL/+ME4KTyYIMI1xgg0x/TH9Mf+CMTu/Jj7UTQ0x/TH9P/0VEyuvKhUUS68qIE+QFUEFX5EPKj+ACTINdKltMH1AL7AOjRAaTIyx/LH8v/ye1UAFAAAAAAKamjF3LJ7WtipuLroUqTuQRi56Nnd3vrijj7FbnzOETSLOL/HqR30Q==' + }, + provider: 'http' +}); +``` + +The resulting lines of code in the console are almost identical to those displayed when the connection was initiated in the first place. Therefore, if the backend doesn't perform user authentication correctly as expected, a way to test if it is working correctly is required. To accomplish this, it is possible to act as the TON Foundation within the console, so the legitimacy of token balances and token ownership parameters can be tested. Naturally, the provided code doesn't change any variables in the connector, but the user is able to use the app as desired unless that connector is protected by the closure. Even if that is the case, it is not difficult to extract it using a debugger and coding breakpoints. + +Now that the authentication of the user has been verified, let's proceed to writing the code. + +## Connection using TonProof + +According to TON Connect’s SDK documentation, the second argument refers to the `connect()` method which contains a payload that will be wrapped and signed by the wallet. Therefore, the result is new connection code: + +```js + if (wallet.embedded || wallet.injected) { + connectButton.onclick = () => { + connectButton.disabled = true; + connector.connect({jsBridgeKey: wallet.jsBridgeKey}, + {tonProof: 'doc-example-'}); + }; + } else if (wallet.bridgeUrl) { + connectButton.onclick = () => { + connectButton.disabled = true; + console.log('Connection link:', connector.connect({ + universalLink: wallet.universalLink, + bridgeUrl: wallet.bridgeUrl + }, {tonProof: 'doc-example-'})); + }; +``` + +Connection link: + +``` +https://app.tonkeeper.com/ton-connect?v=2&id=4b0a7e2af3b455e0f0bafe14dcdc93f1e9e73196ae2afaca4d9ba77e94484a44&r=%7B%22manifestUrl%22%3A%22https%3A%2F%2Fratingers.pythonanywhere.com%2Fratelance%2Ftonconnect-manifest.json%22%2C%22items%22%3A%5B%7B%22name%22%3A%22ton_addr%22%7D%2C%7B%22name%22%3A%22ton_proof%22%2C%22payload%22%3A%22doc-example-%3CBACKEND_AUTH_ID%3E%22%7D%5D%7D +``` + +Expanded and simplified `r` parameter: + +```js +{ + "manifestUrl": + "https://ratingers.pythonanywhere.com/ratelance/tonconnect-manifest.json", + "items": [ + {"name": "ton_addr"}, + {"name": "ton_proof", "payload": "doc-example-"} + ] +} +``` + +Next, the url address link is sent to a mobile device and opened using Tonkeeper. + +After this process is complete, the following wallet-specific information is received: + +```js +{ + "device": { + "platform": "android", + "appName": "Tonkeeper", + "appVersion": "2.8.0.261", + "maxProtocolVersion": 2, + "features": [ + "SendTransaction" + ] + }, + "provider": "http", + "account": { + "address": "0:b2a1ecf5545e076cd36ae516ea7ebdf32aea008caa2b84af9866becb208895ad", + "chain": "-239", + "walletStateInit": "te6cckECFgEAAwQAAgE0ARUBFP8A9KQT9LzyyAsCAgEgAxACAUgEBwLm0AHQ0wMhcbCSXwTgItdJwSCSXwTgAtMfIYIQcGx1Z70ighBkc3RyvbCSXwXgA/pAMCD6RAHIygfL/8nQ7UTQgQFA1yH0BDBcgQEI9ApvoTGzkl8H4AXTP8glghBwbHVnupI4MOMNA4IQZHN0crqSXwbjDQUGAHgB+gD0BDD4J28iMFAKoSG+8uBQghBwbHVngx6xcIAYUATLBSbPFlj6Ahn0AMtpF8sfUmDLPyDJgED7AAYAilAEgQEI9Fkw7UTQgQFA1yDIAc8W9ADJ7VQBcrCOI4IQZHN0coMesXCAGFAFywVQA88WI/oCE8tqyx/LP8mAQPsAkl8D4gIBIAgPAgEgCQ4CAVgKCwA9sp37UTQgQFA1yH0BDACyMoHy//J0AGBAQj0Cm+hMYAIBIAwNABmtznaiaEAga5Drhf/AABmvHfaiaEAQa5DrhY/AABG4yX7UTQ1wsfgAWb0kK29qJoQICga5D6AhhHDUCAhHpJN9KZEM5pA+n/mDeBKAG3gQFImHFZ8xhAT48oMI1xgg0x/TH9MfAvgju/Jk7UTQ0x/TH9P/9ATRUUO68qFRUbryogX5AVQQZPkQ8qP4ACSkyMsfUkDLH1Iwy/9SEPQAye1U+A8B0wchwACfbFGTINdKltMH1AL7AOgw4CHAAeMAIcAC4wABwAORMOMNA6TIyx8Syx/L/xESExQAbtIH+gDU1CL5AAXIygcVy//J0Hd0gBjIywXLAiLPFlAF+gIUy2sSzMzJc/sAyEAUgQEI9FHypwIAcIEBCNcY+gDTP8hUIEeBAQj0UfKnghBub3RlcHSAGMjLBcsCUAbPFlAE+gIUy2oSyx/LP8lz+wACAGyBAQjXGPoA0z8wUiSBAQj0WfKnghBkc3RycHSAGMjLBcsCUAXPFlAD+gITy2rLHxLLP8lz+wAACvQAye1UAFEAAAAAKamjFyM60x2mt5eboNyOTE+5RGOe9Ee2rK1Qcb+0ZuiP9vb7QJRlz/c=" + }, + "connectItems": { + "tonProof": { + "name": "ton_proof", + "proof": { + "timestamp": 1674392728, + "domain": { + "lengthBytes": 28, + "value": "ratingers.pythonanywhere.com" + }, + "signature": "trCkHit07NZUayjGLxJa6FoPnaGHkqPy2JyNjlUbxzcc3aGvsExCmHXi6XJGuoCu6M2RMXiLzIftEm6PAoy1BQ==", + "payload": "doc-example-" + } + } + } +} +``` + +Let's verify the received signature. In order to accomplish this, the signature verification uses Python because it can easily interact with the backend. The libraries required to carry out this process are the `tonsdk` and the `pynacl`. + +Next, it is necessary to retrieve the wallet's public key. To accomplish this, `tonapi.io` or similar services are not used because the end result cannot be reliably trusted. Instead, this is accomplished by parsing the `walletStateInit`. + +It is also critical to ensure that the `address` and `walletStateInit` match, or the payload could be signed with their wallet key by providing their own wallet in the `stateInit` field and another wallet in the `address` field. + +The `StateInit` is made up of two reference types: one for code and one for data. In this context, the purpose is to retrieve the public key so the second reference (the data reference) is loaded. Then 8 bytes are skipped (4 bytes are used for the `seqno` field and 4 for `subwallet_id` in all modern wallet contracts) and the next 32 bytes are loaded (256 bits) -- the public key. + +```python +import nacl.signing +import tonsdk + +import hashlib +import base64 + +received_state_init = 'te6cckECFgEAAwQAAgE0ARUBFP8A9KQT9LzyyAsCAgEgAxACAUgEBwLm0AHQ0wMhcbCSXwTgItdJwSCSXwTgAtMfIYIQcGx1Z70ighBkc3RyvbCSXwXgA/pAMCD6RAHIygfL/8nQ7UTQgQFA1yH0BDBcgQEI9ApvoTGzkl8H4AXTP8glghBwbHVnupI4MOMNA4IQZHN0crqSXwbjDQUGAHgB+gD0BDD4J28iMFAKoSG+8uBQghBwbHVngx6xcIAYUATLBSbPFlj6Ahn0AMtpF8sfUmDLPyDJgED7AAYAilAEgQEI9Fkw7UTQgQFA1yDIAc8W9ADJ7VQBcrCOI4IQZHN0coMesXCAGFAFywVQA88WI/oCE8tqyx/LP8mAQPsAkl8D4gIBIAgPAgEgCQ4CAVgKCwA9sp37UTQgQFA1yH0BDACyMoHy//J0AGBAQj0Cm+hMYAIBIAwNABmtznaiaEAga5Drhf/AABmvHfaiaEAQa5DrhY/AABG4yX7UTQ1wsfgAWb0kK29qJoQICga5D6AhhHDUCAhHpJN9KZEM5pA+n/mDeBKAG3gQFImHFZ8xhAT48oMI1xgg0x/TH9MfAvgju/Jk7UTQ0x/TH9P/9ATRUUO68qFRUbryogX5AVQQZPkQ8qP4ACSkyMsfUkDLH1Iwy/9SEPQAye1U+A8B0wchwACfbFGTINdKltMH1AL7AOgw4CHAAeMAIcAC4wABwAORMOMNA6TIyx8Syx/L/xESExQAbtIH+gDU1CL5AAXIygcVy//J0Hd0gBjIywXLAiLPFlAF+gIUy2sSzMzJc/sAyEAUgQEI9FHypwIAcIEBCNcY+gDTP8hUIEeBAQj0UfKnghBub3RlcHSAGMjLBcsCUAbPFlAE+gIUy2oSyx/LP8lz+wACAGyBAQjXGPoA0z8wUiSBAQj0WfKnghBkc3RycHSAGMjLBcsCUAXPFlAD+gITy2rLHxLLP8lz+wAACvQAye1UAFEAAAAAKamjFyM60x2mt5eboNyOTE+5RGOe9Ee2rK1Qcb+0ZuiP9vb7QJRlz/c=' +received_address = '0:b2a1ecf5545e076cd36ae516ea7ebdf32aea008caa2b84af9866becb208895ad' + +state_init = tonsdk.boc.Cell.one_from_boc(base64.b64decode(received_state_init)) + +address_hash_part = base64.b16encode(state_init.bytes_hash()).decode('ascii').lower() +assert received_address.endswith(address_hash_part) + +public_key = state_init.refs[1].bits.array[8:][:32] + +print(public_key) +# bytearray(b'#:\xd3\x1d\xa6\xb7\x97\x9b\xa0\xdc\x8eLO\xb9Dc\x9e\xf4G\xb6\xac\xadPq\xbf\xb4f\xe8\x8f\xf6\xf6\xfb') + +verify_key = nacl.signing.VerifyKey(bytes(public_key)) +``` + +After the sequencing code above is implemented, the correct documentation is consulted to check which parameters are verified and signed using the wallet key: + +> ``` +> message = utf8_encode("ton-proof-item-v2/") ++ +> Address ++ +> AppDomain ++ +> Timestamp ++ +> Payload +> +> signature = Ed25519Sign( +> privkey, +> sha256(0xffff ++ utf8_encode("ton-connect") ++ sha256(message)) +> ) +> ``` + +> Whereby the: +> +> - `Address` denotes the wallet address encoded as a sequence: +> - `workchain`: 32-bit signed integer big endian; +> - `hash`: 256-bit unsigned integer big endian; +> - `AppDomain` is the Length ++ EncodedDomainName +> - `Length` uses a 32-bit value of utf-8 encoded app domain name length in bytes +> - `EncodedDomainName` id `Length`-byte utf-8 encoded app domain name +> - `Timestamp` denotes the 64-bit unix epoch time of the signing operation +> - `Payload` denotes a variable-length binary string +> - `utf8_encode` produces a plain byte string with no length prefixes. + +Let's reimplement this in Python. The endianness of some of the integers above is not specified, so several examples must be considered. Please refer to the following Tonkeeper implementation detailing some related examples: : [ConnectReplyBuilder.ts](https://github.com/tonkeeper/wallet/blob/77992c08c663dceb63ca6a8e918a2150c75cca3a/src/tonconnect/ConnectReplyBuilder.ts#L42). + +```python +received_timestamp = 1674392728 +signature = 'trCkHit07NZUayjGLxJa6FoPnaGHkqPy2JyNjlUbxzcc3aGvsExCmHXi6XJGuoCu6M2RMXiLzIftEm6PAoy1BQ==' + +message = (b'ton-proof-item-v2/' + + 0 .to_bytes(4, 'big') + si.bytes_hash() + + 28 .to_bytes(4, 'little') + b'ratingers.pythonanywhere.com' + + received_timestamp.to_bytes(8, 'little') + + b'doc-example-') +# b'ton-proof-item-v2/\x00\x00\x00\x00\xb2\xa1\xec\xf5T^\x07l\xd3j\xe5\x16\xea~\xbd\xf3*\xea\x00\x8c\xaa+\x84\xaf\x98f\xbe\xcb \x88\x95\xad\x1c\x00\x00\x00ratingers.pythonanywhere.com\x984\xcdc\x00\x00\x00\x00doc-example-' + +signed = b'\xFF\xFF' + b'ton-connect' + hashlib.sha256(message).digest() +# b'\xff\xffton-connectK\x90\r\xae\xf6\xb0 \xaa\xa9\xbd\xd1\xaa\x96\x8b\x1fp\xa9e\xff\xdf\x81\x02\x98\xb0)E\t\xf6\xc0\xdc\xfdx' + +verify_key.verify(hashlib.sha256(signed).digest(), base64.b64decode(signature)) +# b'\x0eT\xd6\xb5\xd5\xe8HvH\x0b\x10\xdc\x8d\xfc\xd3#n\x93\xa8\xe9\xb9\x00\xaaH%\xb5O\xac:\xbd\xcaM' +``` + +After implementing the above parameters, if an attacker tries to impersonate a user and doesn't provide a valid signature, the following error will be displayed: `nacl.exceptions.BadSignatureError: Signature was forged or corrupt`. + +## Next steps + +When writing a dApp, the following should also be considered: + +- after a successful connection is completed (either a restored or new connection), the `Disconnect` button should be displayed instead of several `Connect` buttons +- after a user disconnects, `Disconnect` buttons will need to be recreated +- wallet code should be checked, because + - newer wallet versions could place public keys in a different location and create issues + - the current user may sign in using another type of contract instead of a wallet. Thankfully, this will contain the public key in the expected location + +Good luck and have fun writing dApps! From 419490d8c49dac9b7c0ed641cd52004046774be8 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:49 +0800 Subject: [PATCH 051/219] New translations manifest.md (Chinese Simplified) --- .../develop/dapps/ton-connect/manifest.md | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/manifest.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/manifest.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/manifest.md new file mode 100644 index 0000000000..a86e0b8c67 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/manifest.md @@ -0,0 +1,40 @@ +# Creating manifest.json + +Every app needs to have its manifest to pass meta information to the wallet. Manifest is a JSON file named as `tonconnect-manifest.json` following format: + +```json +{ + "url": "", // required + "name": "", // required + "iconUrl": "", // required + "termsOfUseUrl": "", // optional + "privacyPolicyUrl": "" // optional +} +``` + +## Example + +You can find an example of the manifest below: + +```json +{ + "url": "https://ton.vote", + "name": "TON Vote", + "iconUrl": "https://ton.vote/logo.png" +} +``` + +## Best practices + +- Best practice is to place the manifest in the root of your app and repository, e.g. `https://myapp.com/tonconnect-manifest.json`. It allows the wallet to handle your app better and improve the UX connected to your app. +- Make sure that `manifest.json` file is available to GET by its URL. + +## Fields description + +| Field | Requirement | Description | +| ------------------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `url` | required | app URL. Will be used as the DAppidentifier. Will be used to open the DAppafter click to its icon in the wallet. It is recommended to pass url without closing slash, e.g. 'https://mydapp.com' instead of 'https://mydapp.com/'. | +| `name` | required | app name. Might be simple, will not be used as identifier. | +| `iconUrl` | required | Url to the app icon. Must be PNG, ICO, ... format. SVG icons are not supported. Perfectly pass url to a 180x180px PNG icon. | +| `termsOfUseUrl` | optional | url to the Terms Of Use document. Optional for usual apps, but required for the apps which is placed in the Tonkeeper recommended apps list. | +| `privacyPolicyUrl` | optional | url to the Privacy Policy document. Optional for usual apps, but required for the apps which is placed in the Tonkeeper recommended apps list. | From 79fbd52881032d532f72e1ed0db9745f3ebb106c Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:50 +0800 Subject: [PATCH 052/219] New translations message-builders.mdx (Chinese Simplified) --- .../dapps/ton-connect/message-builders.mdx | 1329 +++++++++++++++++ 1 file changed, 1329 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/message-builders.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/message-builders.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/message-builders.mdx new file mode 100644 index 0000000000..f28d1f2c46 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/message-builders.mdx @@ -0,0 +1,1329 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Preparing Messages + +While using TON Connect, you should construct the Message Body for the Payload used in various transactions. On this page you can find the most relevant examples of payload for use with the TON Connect SDKs. + +:::info +It is expected, that you learn basics on the creating TON Connect connection. Learn more with the [integration manual](/develop/dapps/ton-connect/integration). +::: + +## TON Connect JS SDK Examples + +### Transaction Template + +No matter what level of the task developer are solving, typically it is necessary to use connector entity from @tonconnect/sdk or @tonconnect/ui. +Examples created based on @tonconnect/sdk and @tonconnect/ui: + + + + + +```js +import { useTonConnectUI } from '@tonconnect/ui-react'; +const [tonConnectUI] = useTonConnectUI(); + +const transaction = { + //transaction body +}) + +export const Settings = () => { + const [tonConnectUI, setOptions] = useTonConnectUI(); + + return ( +
+ +
+ ); +}; +``` + +
+ + +```js +import TonConnectUI from '@tonconnect/ui'; + +const tonConnectUI = new TonConnectUI({ //connect application + manifestUrl: 'https:///tonconnect-manifest.json', + buttonRootId: '' +}); + +const transaction = { + //transaction body +} + +const result = await tonConnectUI.sendTransaction(transaction) + +``` + + + + +```js +import TonConnect from '@tonconnect/sdk'; +const connector = new TonConnect(); + +await connector.sendTransaction({ + //transaction body +}) + +``` + + + +
+ +### Regular TON Transfer + +TON Connect SDKs include wrappers for sending messages, making it easy to prepare regular transfers of Toncoins between two wallets as default transaction without payload. + +A regular TON transfer using the TON Connect JS SDKs can be executed as follows: + + + + +```js +import { useTonConnectUI } from '@tonconnect/ui-react'; +const [tonConnectUI] = useTonConnectUI(); + +const transaction = { + messages: [ + { + address: "0:412410771DA82CBA306A55FA9E0D43C9D245E38133CB58F1457DFB8D5CD8892F", // destination address + amount: "20000000" //Toncoin in nanotons + } + ] + +} + +export const Settings = () => { + const [tonConnectUI, setOptions] = useTonConnectUI(); + + return ( +
+ +
+ ); +}; +``` + +
+ + + +```js +import TonConnectUI from '@tonconnect/ui'; + +const tonConnectUI = new TonConnectUI({ //connect application + manifestUrl: 'https:///tonconnect-manifest.json', + buttonRootId: '' +}); + +const transaction = { + messages: [ + { + address: "0:412410771DA82CBA306A55FA9E0D43C9D245E38133CB58F1457DFB8D5CD8892F", // destination address + amount: "20000000" //Toncoin in nanotons + } + ] +} + +const result = await tonConnectUI.sendTransaction(transaction) +``` + + + + + +```js +import TonConnect from '@tonconnect/sdk'; +const connector = new TonConnect(); + +await connector.sendTransaction({ + messages: [ + { + address: "0:412410771DA82CBA306A55FA9E0D43C9D245E38133CB58F1457DFB8D5CD8892F", // destination address + amount: "20000000" //Toncoin in nanotons + } + ] +}) + +``` + + +
+ +:::tip +Learn more about [TON Smart Contract Addresses](/learn/overviews/addresses). +::: + +For specific custom transaction, a certain payload must be defined. + +### Transfer With a Comment + +The simplest example is adding a payload with a comment. See more details on [this page](/develop/smart-contracts/guidelines/internal-messages#simple-message-with-comment). +Before transaction, it is necessary prepare a `body` [cell](/develop/data-formats/cell-boc) via the [@ton/ton](https://github.com/ton-org/ton) JavaScript library. + +```js +import { beginCell } from '@ton/ton' + +const body = beginCell() + .storeUint(0, 32) // write 32 zero bits to indicate that a text comment will follow + .storeStringTail("Hello, TON!") // write our text comment + .endCell(); +``` + +The transaction body is created by the following: + + + + + +```js +import { useTonConnectUI } from '@tonconnect/ui-react'; + +const myTransaction = { + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: destination, + amount: toNano("0.05"), + payload: body.toBoc().toString("base64") // payload with comment in body + } + ] +} + +export const Settings = () => { + const [tonConnectUI, setOptions] = useTonConnectUI(); + + return ( +
+ +
+ ); +}; +``` + +
+ + + +```js +import TonConnectUI from '@tonconnect/ui' + +const transaction = { + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: destination, + amount: toNano("0.05"), + payload: body.toBoc().toString("base64") // payload with comment in body + } + ] +} + +const result = await tonConnectUI.sendTransaction(transaction) +``` + + + + + +```js +import TonConnect from '@tonconnect/sdk'; +const connector = new TonConnect(); + +await connector.sendTransaction({ + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: destination, + amount: toNano("0.05"), + payload: body.toBoc().toString("base64") // payload with comment in body + } + ] +}) +``` + + +
+ +### Jetton Transfer + +The `body` for Jetton Transfer([TEP-74](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#1-transfer)) typically should be done according the following way: + +```js + import {beginCell, toNano} from '@ton/ton' + // transfer#0f8a7ea5 query_id:uint64 amount:(VarUInteger 16) destination:MsgAddress + // response_destination:MsgAddress custom_payload:(Maybe ^Cell) + // forward_ton_amount:(VarUInteger 16) forward_payload:(Either Cell ^Cell) + // = InternalMsgBody; + + const body = beginCell() + .storeUint(0xf8a7ea5, 32) // jetton transfer op code + .storeUint(0, 64) // query_id:uint64 + .storeCoins(1000000) // amount:(VarUInteger 16) - Jetton amount for transfer (decimals = 6 - jUSDT, 9 - default) + .storeAddress(Wallet_DST) // destination:MsgAddress + .storeAddress(Wallet_SRC) // response_destination:MsgAddress + .storeUint(0, 1) // custom_payload:(Maybe ^Cell) + .storeCoins(toNano(0.05)) // forward_ton_amount:(VarUInteger 16) - if >0, will send notification message + .storeUint(0,1) // forward_payload:(Either Cell ^Cell) + .endCell(); +``` + +Next, sending the transaction with this body to sender's jettonWalletContract executed: + + + + +```js +import { useTonConnectUI } from '@tonconnect/ui-react'; + +const myTransaction = { + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: jettonWalletContract, // sender jetton wallet + amount: toNano("0.05"), // for commission fees, excess will be returned + payload: body.toBoc().toString("base64") // payload with jetton transfer body + } + ] +} + +export const Settings = () => { + const [tonConnectUI, setOptions] = useTonConnectUI(); + + return ( +
+ +
+ ); +}; +``` + +
+ + + +```js +import TonConnectUI from '@tonconnect/ui' + +const transaction = { + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: jettonWalletContract, // sender jetton wallet + amount: toNano("0.05"), // for commission fees, excess will be returned + payload: body.toBoc().toString("base64") // payload with jetton transfer body + } + ] +} + +const result = await tonConnectUI.sendTransaction(transaction) +``` + + + + + +```js +import TonConnect from '@tonconnect/sdk'; +const connector = new TonConnect(); +//... +await connector.sendTransaction({ + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: jettonWalletContract, // sender jetton wallet + amount: toNano("0.05"), // for commission fees, excess will be returned + payload: body.toBoc().toString("base64") // payload with jetton transfer body + } + ] +}) +``` + + +
+ +- `validUntil` - UNIX-time until message valid +- `jettonWalletAddress` - Address, JettonWallet address, that defined based on JettonMaser and Wallet contracts +- `balance` - Integer, amount of Toncoin for gas payments in nanotons. +- `body` - payload for the jettonContract + +
+ Jetton Wallet State Init and Address preparation example + +```js +import { Address, TonClient, beginCell, StateInit, storeStateInit } from '@ton/ton' + +async function main() { + const client = new TonClient({ + endpoint: 'https://toncenter.com/api/v2/jsonRPC', + apiKey: 'put your api key' + }) + + const jettonWalletAddress = Address.parse('Sender_Jetton_Wallet'); + let jettonWalletDataResult = await client.runMethod(jettonWalletAddress, 'get_wallet_data'); + jettonWalletDataResult.stack.readNumber(); + const ownerAddress = jettonWalletDataResult.stack.readAddress(); + const jettonMasterAddress = jettonWalletDataResult.stack.readAddress(); + const jettonCode = jettonWalletDataResult.stack.readCell(); + const jettonData = beginCell() + .storeCoins(0) + .storeAddress(ownerAddress) + .storeAddress(jettonMasterAddress) + .storeRef(jettonCode) + .endCell(); + + const stateInit: StateInit = { + code: jettonCode, + data: jettonData + } + + const stateInitCell = beginCell() + .store(storeStateInit(stateInit)) + .endCell(); + + console.log(new Address(0, stateInitCell.hash())); +} +``` + +
+ +### Jetton Transfer with Comment + +The `messageBody` for Jetton Transfer([TEP-74](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#1-transfer)) with comment we should additionally to the regular transfer `body` serialize comment and pack this in the `forwardPayload`: + +```js + import {beginCell, toNano, Address} from '@ton/ton' + // transfer#0f8a7ea5 query_id:uint64 amount:(VarUInteger 16) destination:MsgAddress + // response_destination:MsgAddress custom_payload:(Maybe ^Cell) + // forward_ton_amount:(VarUInteger 16) forward_payload:(Either Cell ^Cell) + // = InternalMsgBody; + + const destinationAddress = Address.parse('put destination wallet address'); + + const forwardPayload = beginCell() + .storeUint(0, 32) // 0 opcode means we have a comment + .storeStringTail('Hello, TON!') + .endCell(); + + const body = beginCell() + .storeUint(0x0f8a7ea5, 32) // opcode for jetton transfer + .storeUint(0, 64) // query id + .storeCoins(toNano(5)) // jetton amount, amount * 10^9 + .storeAddress(destinationAddress) // TON wallet destination address + .storeAddress(destinationAddress) // response excess destination + .storeBit(0) // no custom payload + .storeCoins(toNano('0.02')) // forward amount (if >0, will send notification message) + .storeBit(1) // we store forwardPayload as a reference + .storeRef(forwardPayload) + .endCell(); + +``` + +Next, sending the transaction with this body to sender's jettonWalletContract executed: + + + + +```js + import { useTonConnectUI } from '@tonconnect/ui-react'; + + + const jettonWalletContract = Address.parse('put your jetton wallet address'); + + const myTransaction = { + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: jettonWalletContract, // sender jetton wallet + amount: toNano("0.05"), // for commission fees, excess will be returned + payload: body.toBoc().toString("base64") // payload with jetton transfer and comment body + } + ] + } + + export const Settings = () => { + const [tonConnectUI, setOptions] = useTonConnectUI(); + + return ( +
+ +
+); +}; +``` + +
+ + + +```js + import TonConnectUI from '@tonconnect/ui' + + const transaction = { + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ +{ + address: jettonWalletContract, // sender jetton wallet + amount: toNano("0.05"), // for commission fees, excess will be returned + payload: body.toBoc().toString("base64") // payload with jetton transfer and comment body +} + ] +} + + const result = await tonConnectUI.sendTransaction(transaction) +``` + + + + + +```js + import TonConnect from '@tonconnect/sdk'; + const connector = new TonConnect(); + //... + await connector.sendTransaction({ + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ +{ + address: jettonWalletContract, // sender jetton wallet + amount: toNano("0.05"), // for commission fees, excess will be returned + payload: body.toBoc().toString("base64") // payload with jetton transfer and comment body +} + ] +}) +``` + + +
+ +- `validUntil` - UNIX-time until message valid +- `jettonWalletAddress` - Address, JettonWallet address, that defined based on JettonMaser and Wallet contracts +- `balance` - Integer, amount of Toncoin for gas payments in nanotons. +- `body` - payload for the jettonContract + +
+ Jetton Wallet State Init and Address preparation example + +```js + import { Address, TonClient, beginCell, StateInit, storeStateInit } from '@ton/ton' + + async function main() { + const client = new TonClient({ + endpoint: 'https://toncenter.com/api/v2/jsonRPC', + apiKey: 'put your api key' +}) + + const jettonWalletAddress = Address.parse('Sender_Jetton_Wallet'); + let jettonWalletDataResult = await client.runMethod(jettonWalletAddress, 'get_wallet_data'); + jettonWalletDataResult.stack.readNumber(); + const ownerAddress = jettonWalletDataResult.stack.readAddress(); + const jettonMasterAddress = jettonWalletDataResult.stack.readAddress(); + const jettonCode = jettonWalletDataResult.stack.readCell(); + const jettonData = beginCell() + .storeCoins(0) + .storeAddress(ownerAddress) + .storeAddress(jettonMasterAddress) + .storeRef(jettonCode) + .endCell(); + + const stateInit: StateInit = { + code: jettonCode, + data: jettonData +} + + const stateInitCell = beginCell() + .store(storeStateInit(stateInit)) + .endCell(); + + console.log(new Address(0, stateInitCell.hash())); +} +``` + +
+ +### Jetton Burn + +The `body` for Jetton Burn([TEP-74](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#2-burn)) typically should be done according the following way: + +```js + import {beginCell} from '@ton/ton' +// burn#595f07bc query_id:uint64 amount:(VarUInteger 16) +// response_destination:MsgAddress custom_payload:(Maybe ^Cell) +// = InternalMsgBody; + + const body = beginCell() + .storeUint(0x595f07bc, 32) // jetton burn op code + .storeUint(0, 64) // query_id:uint64 + .storeCoins(1000000) // amount:(VarUInteger 16) - Jetton amount in decimal + .storeAddress(Wallet_SRC) // response_destination:MsgAddress - owner's wallet + .storeUint(0, 1) // custom_payload:(Maybe ^Cell) - w/o payload typically + .endCell(); +``` + +Message places into the following request: + + + + +```js +import { useTonConnectUI } from '@tonconnect/ui-react'; + +const myTransaction = { + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: jettonWalletContract, // owner's jetton wallet + amount: toNano("0.05"), // for commission fees, excess will be returned + payload: body.toBoc().toString("base64") // payload with a jetton burn body + } + ] +} + +export const Settings = () => { + const [tonConnectUI, setOptions] = useTonConnectUI(); + + return ( +
+ +
+ ); +}; +``` + +
+ + + +```js +import TonConnectUI from '@tonconnect/ui' + +const transaction = { + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: jettonWalletContract, // owner's jetton wallet + amount: toNano("0.05"), // for commission fees, excess will be returned + payload: body.toBoc().toString("base64") // payload with a jetton burn body + } + ] +} + +const result = await tonConnectUI.sendTransaction(transaction) +``` + + + + + +```js +await connector.sendTransaction({ + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: jettonWalletContract, // owner's jetton wallet + amount: toNano("0.05"), // for commission fees, excess will be returned + payload: body.toBoc().toString("base64") // payload with a jetton burn body + } + ] +}) +``` + + +
+ +- `jettonWalletAddress` - Jetton Wallet contract address, that defined based on JettonMaser and Wallet contracts +- `amount` - Integer, amount of Toncoin for gas payments in nanotons. +- `body` - payload for the jetton wallet with the `burn#595f07bc` op code + +### NFT Transfer + +The `body` message typically should be done according the following way: + +```js +import { beginCell, toNano} from '@ton/ton' + +// transfer#5fcc3d14 query_id:uint64 new_owner:MsgAddress response_destination:MsgAddress custom_payload:(Maybe ^Cell) +// forward_amount:(VarUInteger 16) forward_payload:(Either Cell ^Cell) = InternalMsgBody; + + const body = beginCell() + .storeUint(0x5fcc3d14, 32) // NFT transfer op code 0x5fcc3d14 + .storeUint(0, 64) // query_id:uint64 + .storeAddress(NEW_OWNER_WALLET) // new_owner:MsgAddress + .storeAddress(Wallet_DST) // response_destination:MsgAddress + .storeUint(0, 1) // custom_payload:(Maybe ^Cell) + .storeCoins(toNano('0.000000001')) // forward_amount:(VarUInteger 16) + .storeUint(0,1) // forward_payload:(Either Cell ^Cell) + .endCell(); +``` + +`WALLET_DST` - Address - The address of the initial NFT owner for the receiving excess +Transfer the `NFTitem` to a new owner `NEW_OWNER_WALLET`. + + + + +```js +import { useTonConnectUI } from '@tonconnect/ui-react'; + +const myTransaction = { + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: jettonWalletContract, // NFT Item address, which will be transferred + amount: toNano("0.05"), // for commission fees, excess will be returned + payload: body.toBoc().toString("base64") // payload with a NFT transfer body + } + ] +} + +export const Settings = () => { + const [tonConnectUI, setOptions] = useTonConnectUI(); + + return ( +
+ +
+ ); +}; +``` + +
+ + + +```js +import TonConnectUI from '@tonconnect/ui' + +const transaction = { + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: NFTitem, // NFT Item address, which will be transferred + amount: toNano("0.05"), // for commission fees, excess will be returned + payload: body.toBoc().toString("base64") // payload with a NFT transfer body + } + ] +} + +const result = await tonConnectUI.sendTransaction(transaction) +``` + + + + + +```js +await connector.sendTransaction({ + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: NFTitem, // NFT Item address, which will be transferred + amount: toNano("0.05"), // for commission fees, excess will be returned + payload: body.toBoc().toString("base64") // payload with a NFT transfer body + } + ] +}) +``` + + +
+ +- `NFTitem` - Address - The address of NFT item smart contract which we want transfer to a new owner `NEW_OWNER_WALLET`. +- `balance` - Integer, amount of Toncoin for gas payments in nanotons. +- `body` - payload for the NFT contract + +### NFT Sale (GetGems) + +Here is an example of preparing message and transaction for sale on GetGems marketplace, according to contract [nft-fixprice-sale-v3r2](https://github.com/getgems-io/nft-contracts/blob/main/packages/contracts/sources/nft-fixprice-sale-v3r2.fc). + +To place NFT on GetGems Sale Contract, we should prepare special message body `transferNftBody` that will be transfer NFT to special NFT Sale Contract. + +```js + const transferNftBody = beginCell() + .storeUint(0x5fcc3d14, 32) // Opcode for NFT transfer + .storeUint(0, 64) // query_id + .storeAddress(destinationAddress) // new_owner - GetGems sale contracts deployer, should never change for this operation + .storeAddress(walletAddress) // response_destination for excesses + .storeBit(0) // we do not have custom_payload + .storeCoins(toNano("1")) // forward_amount + .storeBit(0) // we store forward_payload is this cell + .storeUint(0x0fe0ede, 31) // not 32, because previous 0 will be read as do_sale opcode in deployer + .storeRef(stateInitCell) + .storeRef(saleBody) + .endCell(); +``` + +Because message requires a lot of steps, the entire algorithm huge and could be found here: + +
+ Show entire algorithm for the creating NFT Sale message body + +```js +import { Address, beginCell, StateInit, storeStateInit, toNano, Cell } from '@ton/ton' + +async function main() { + const fixPriceV3R2Code = Cell.fromBase64('te6cckECCwEAArkAART/APSkE/S88sgLAQIBIAIDAgFIBAUAfvIw7UTQ0wDTH/pA+kD6QPoA1NMAMMABjh34AHAHyMsAFssfUATPFljPFgHPFgH6AszLAMntVOBfB4IA//7y8AICzQYHAFegOFnaiaGmAaY/9IH0gfSB9AGppgBgYaH0gfQB9IH0AGEEIIySsKAVgAKrAQH30A6GmBgLjYSS+CcH0gGHaiaGmAaY/9IH0gfSB9AGppgBgYOCmE44BgAEqYhOmPhW8Q4YBKGATpn8cIxbMbC3MbK2QV44LJOZlvKAVxFWAAyS+G8BJrpOEBFcCBFd0VYACRWdjYKdxjgthOjq+G6hhoaYPqGAD9gHAU4ADAgB92YIQO5rKAFJgoFIwvvLhwiTQ+kD6APpA+gAwU5KhIaFQh6EWoFKQcIAQyMsFUAPPFgH6AstqyXH7ACXCACXXScICsI4XUEVwgBDIywVQA88WAfoCy2rJcfsAECOSNDTiWnCAEMjLBVADzxYB+gLLaslx+wBwIIIQX8w9FIKAejy0ZSzjkIxMzk5U1LHBZJfCeBRUccF8uH0ghAFE42RFrry4fUD+kAwRlAQNFlwB8jLABbLH1AEzxZYzxYBzxYB+gLMywDJ7VTgMDcowAPjAijAAJw2NxA4R2UUQzBw8AXgCMACmFVEECQQI/AF4F8KhA/y8AkA1Dg5ghA7msoAGL7y4clTRscFUVLHBRWx8uHKcCCCEF/MPRQhgBDIywUozxYh+gLLassfFcs/J88WJ88WFMoAI/oCE8oAyYMG+wBxUGZFFQRwB8jLABbLH1AEzxZYzxYBzxYB+gLMywDJ7VQAlsjLHxPLPyPPFlADzxbKAIIJycOA+gLKAMlxgBjIywUmzxZw+gLLaszJgwb7AHFVUHAHyMsAFssfUATPFljPFgHPFgH6AszLAMntVNZeZYk='); + + const marketplaceAddress = Address.parse('EQBYTuYbLf8INxFtD8tQeNk5ZLy-nAX9ahQbG_yl1qQ-GEMS'); // GetGems Address + const marketplaceFeeAddress = Address.parse('EQCjk1hh952vWaE9bRguFkAhDAL5jj3xj9p0uPWrFBq_GEMS'); // GetGems Address for Fees + const destinationAddress = Address.parse("EQAIFunALREOeQ99syMbO6sSzM_Fa1RsPD5TBoS0qVeKQ-AR"); // GetGems sale contracts deployer + + const walletAddress = Address.parse('EQArLGBnGPvkxaJE57Y6oS4rwzDWuOE8l8_sghntXLkIt162'); + const royaltyAddress = Address.parse('EQArLGBnGPvkxaJE57Y6oS4rwzDWuOE8l8_sghntXLkIt162'); + const nftAddress = Address.parse('EQCUWoe7hLlklVxH8gduCf45vPNocsjRP4wbX42UJ0Ja0S2f'); + const price = toNano('5'); // 5 TON + + const feesData = beginCell() + .storeAddress(marketplaceFeeAddress) + // 5% - GetGems fee + .storeCoins(price / BigInt(100) * BigInt(5)) + .storeAddress(royaltyAddress) + // 5% - Royalty, can be changed + .storeCoins(price / BigInt(100) * BigInt(5)) + .endCell(); + + const saleData = beginCell() + .storeBit(0) // is_complete + .storeUint(Math.round(Date.now() / 1000), 32) // created_at + .storeAddress(marketplaceAddress) // marketplace_address + .storeAddress(nftAddress) // nft_address + .storeAddress(walletAddress) // previous_owner_address + .storeCoins(price) // full price in nanotons + .storeRef(feesData) // fees_cell + .storeBit(0) // can_be_deployed_externally + .endCell(); + + const stateInit: StateInit = { + code: fixPriceV3R2Code, + data: saleData + }; + const stateInitCell = beginCell() + .store(storeStateInit(stateInit)) + .endCell(); + + // not needed, just for example + const saleContractAddress = new Address(0, stateInitCell.hash()); + + const saleBody = beginCell() + .storeUint(1, 32) // just accept coins on deploy + .storeUint(0, 64) + .endCell(); + + const transferNftBody = beginCell() + .storeUint(0x5fcc3d14, 32) // Opcode for NFT transfer + .storeUint(0, 64) // query_id + .storeAddress(destinationAddress) // new_owner + .storeAddress(walletAddress) // response_destination for excesses + .storeBit(0) // we do not have custom_payload + .storeCoins(toNano("1")) // forward_amount + .storeBit(0) // we store forward_payload is this cell + // not 32, because we stored 0 bit before | do_sale opcode for deployer + .storeUint(0x0fe0ede, 31) + .storeRef(stateInitCell) + .storeRef(saleBody) + .endCell(); +``` + +
+ +Prepared `transferNftBody` should be sent to the NFT Item Contract with at least `1.08` TON, that expected for success processing. Excess will be returned to a sender's wallet. + + + + +```js +import { useTonConnectUI } from '@tonconnect/ui-react'; + +const myTransaction = { + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: NFTitem, //address of the NFT Item contract, that should be placed on market + amount: toNano("1.08"), // amount that will require on gas fees, excess will be return + payload: transferNftBody.toBoc().toString("base64") // payload with the transferNftBody message + } + ] +} + +export const Settings = () => { + const [tonConnectUI, setOptions] = useTonConnectUI(); + + return ( +
+ +
+ ); +}; +``` + +
+ + + +```js +import TonConnectUI from '@tonconnect/ui' + +const transaction = { + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: NFTitem, //address of NFT Item contract, that should be placed on market + amount: toNano("1.08"), // amount that will require on gas fees, excess will be return + payload: transferNftBody.toBoc().toString("base64") // payload with the transferNftBody message + } + ] +} + +const result = await tonConnectUI.sendTransaction(transaction) +``` + + + + + +```js +await connector.sendTransaction({ + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: NFTitem, //address of NFT Item contract, that should be placed on market + amount: toNano("1.08"), // amount that will require on gas fees, excess will be return + payload: transferNftBody.toBoc().toString("base64") // payload with the transferNftBody message + } + ] +}) +``` + + +
+ +### NFT Buy (GetGems) + +The process of buy NFT for [nft-fixprice-sale-v3r2](https://github.com/getgems-io/nft-contracts/blob/main/packages/contracts/sources/nft-fixprice-sale-v3r2.fc) sale contract could be carry out with regular transfer without payload, the only important thing is accurate TON amount, that calculates as follows: +`buyAmount = Nftprice TON + 1.0 TON`. + + + + +```js +import { useTonConnectUI } from '@tonconnect/ui-react'; + +const myTransaction = { + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: nftSaleContract, // NFT Sale contract, that is current desired NFT Item + amount: toNano(buyAmount), // NFT Price + exactly 1 TON, excess will be returned + } + ] +} + +export const Settings = () => { + const [tonConnectUI, setOptions] = useTonConnectUI(); + + return ( +
+ +
+ ); +}; +``` + +
+ + + +```js +import TonConnectUI from '@tonconnect/ui' + +const transaction = { + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: nftSaleContract, // NFT Sale contract, that is current desired NFT Item + amount: toNano(buyAmount), // NFT Price + exactly 1 TON, excess will be returned + } + ] +} + +const result = await tonConnectUI.sendTransaction(transaction) +``` + + + + +```js +await connector.sendTransaction({ +validUntil: Math.floor(Date.now() / 1000) + 360, +messages: [ + { + address: nftSaleContract, // NFT Sale contract, that is current desired NFT Item + amount: toNano(buyAmount), // NFT Price + exactly 1 TON, excess will be returned + } +] +}) +``` + + +
+ +## TON Connect Python SDK + +Python examples are using [PyTonConnect](https://github.com/XaBbl4/pytonconnect) and [pytoniq](https://github.com/yungwine/pytoniq). + +```python + from pytoniq_core import Address + from pytonconnect import TonConnect +``` + +:::tip +Read examples [source](https://github.com/yungwine/ton-connect-examples/blob/master/main.py). +::: + +### Regular TON Transfer + +```python +connector = TonConnect( + manifest_url='https://raw.githubusercontent.com/XaBbl4/pytonconnect/main/pytonconnect-manifest.json') +is_connected = await connector.restore_connection() + +transaction = { + 'valid_until': int(time.time() + 3600), + 'messages': [ + 'address' :'0:0000000000000000000000000000000000000000000000000000000000000000', # destination address + 'amount' : 1000000000, # amount should be specified in nanocoins, 1 TON + ) + ] +} +``` + +### Transfer With Comment + +At first order, implement a message with comment via the following function: + +```python + def get_comment_message(destination_address: str, amount: int, comment: str) -> dict: + + data = { + 'address': destination_address, + 'amount': str(amount), + 'payload': urlsafe_b64encode( + begin_cell() + .store_uint(0, 32) # op code for comment message + .store_string(comment) # store comment + .end_cell() # end cell + .to_boc() # convert it to boc + ) + .decode() # encode it to urlsafe base64 + } + + return data +``` + +Final transaction body for transfer with comment: + +```python +transaction = { + 'valid_until': int(time.time() + 3600), + 'messages': [ + get_comment_message( + destination_address='0:0000000000000000000000000000000000000000000000000000000000000000', + amount=int(0.01 * 10**9), # amount should be specified in nanocoins + comment='hello world!' + ) + ] +} +``` + +:::tip +Learn more about [TON Smart Contract Addresses](/learn/overviews/addresses). +::: + +### Jetton Transfer + +Example of function for building jetton transfer transaction: + +```python +from pytoniq_core import begin_cell +from base64 import urlsafe_b64encode + +def get_jetton_transfer_message(jetton_wallet_address: str, recipient_address: str, transfer_fee: int, jettons_amount: int, response_address: str = None) -> dict: + data = { + 'address': jetton_wallet_address, + 'amount': str(transfer_fee), + 'payload': urlsafe_b64encode( + begin_cell() + .store_uint(0xf8a7ea5, 32) # op code for jetton transfer message + .store_uint(0, 64) # query_id + .store_coins(jettons_amount) + .store_address(recipient_address) # destination address + .store_address(response_address or recipient_address) # address send excess to + .store_uint(0, 1) # custom payload + .store_coins(1) # forward amount + .store_uint(0, 1) # forward payload + .end_cell() # end cell + .to_boc() # convert it to boc + ) + .decode() # encode it to urlsafe base64 + } + + return data +``` + +Final transaction body: + +```python +transaction = { + 'valid_until': int(time.time() + 3600), + 'messages': [ + get_jetton_transfer_message( + jetton_wallet_address='EQCXsVvdxTVmSIvYv4tTQoQ-0Yq9mERGTKfbsIhedbN5vTVV', + recipient_address='0:0000000000000000000000000000000000000000000000000000000000000000', + transfer_fee=int(0.07 * 10**9), + jettons_amount=int(0.01 * 10**9), # replace 9 for jetton decimal. For example for jUSDT it should be (amount * 10**6) + response_address=wallet_address + ), + ] +} + +``` + +### Jetton Burn + +Example of function for building jetton burn transaction: + +```python +from pytoniq_core import begin_cell +from base64 import urlsafe_b64encode + +def get_jetton_burn_message(jetton_wallet_address: str, transfer_fee: int, jettons_amount: int, response_address: str = None) -> dict: + data = { + 'address': jetton_wallet_address, + 'amount': str(transfer_fee), + 'payload': urlsafe_b64encode( + begin_cell() + .store_uint(0x595f07bc, 32) # op code for jetton transfer message + .store_uint(0, 64) # query_id + .store_coins(jettons_amount) + .store_address(response_address) # address send excess to + .end_cell() # end cell + .to_boc() # convert it to boc + ) + .decode() # encode it to urlsafe base64 + } + return data +``` + +The final transaction body: + +```python +transaction = { + 'valid_until': int(time.time() + 3600), + 'messages': [ + get_jetton_burn_message( + jetton_wallet_address='EQCXsVvdxTVmSIvYv4tTQoQ-0Yq9mERGTKfbsIhedbN5vTVV', + transfer_fee=int(0.07 * 10 ** 9), + jettons_amount=int(0.01 * 10 ** 9), # replace 9 for jetton decimal. For example for jUSDT it should be (amount * 10**6) + response_address=wallet_address + ), + ] +} +``` + +### NFT Transfer + +Example of function for a NFT transfer transaction: + +```python +from pytoniq_core import begin_cell +from base64 import urlsafe_b64encode + + +def get_nft_transfer_message(nft_address: str, recipient_address: str, transfer_fee: int, response_address: str = None) -> dict: + data = { + 'address': nft_address, + 'amount': str(transfer_fee), + 'payload': urlsafe_b64encode( + begin_cell() + .store_uint(0x5fcc3d14, 32) # op code for nft transfer message + .store_uint(0, 64) # query_id + .store_address(recipient_address) # new owner + .store_address(response_address or recipient_address) # address send excess to + .store_uint(0, 1) # custom payload + .store_coins(1) # forward amount + .store_uint(0, 1) # forward payload + .end_cell() # end cell + .to_boc() # convert it to boc + ) + .decode() # encode it to urlsafe base64 + } + return data + +``` + +The final transaction body: + +```python +transaction = { + 'valid_until': int(time.time() + 3600), + 'messages': [ + get_nft_transfer_message( + nft_address='EQDrA-3zsJXTfGo_Vdzg8d07Da4vSdHZllc6W9qvoNoMstF-', + recipient_address='0:0000000000000000000000000000000000000000000000000000000000000000', + transfer_fee=int(0.07 * 10**9), + response_address=wallet_address + ), + ] +} +``` + +### NFT Sale (GetGems) + +Here is an example of preparing message and transaction for sale on GetGems marketplace, according to contract [nft-fixprice-sale-v3r2](https://github.com/getgems-io/nft-contracts/blob/main/packages/contracts/sources/nft-fixprice-sale-v3r2.fc). + +To place NFT on GetGems Sale Contract, we should prepare special message body `transferNftBody` that will be transfer NFT to special NFT Sale Contract. + +
+Example of creating NFT Sale Body + +```python + import time +from base64 import urlsafe_b64encode + +from pytoniq_core.boc import Cell, begin_cell, Address +from pytoniq_core.tlb import StateInit + + +def get_sale_body(wallet_address: str, royalty_address: str, nft_address: str, price: int, amount: int): + + # contract code + nft_sale_code_cell = Cell.one_from_boc('te6cckECCwEAArkAART/APSkE/S88sgLAQIBIAIDAgFIBAUAfvIw7UTQ0wDTH/pA+kD6QPoA1NMAMMABjh34AHAHyMsAFssfUATPFljPFgHPFgH6AszLAMntVOBfB4IA//7y8AICzQYHAFegOFnaiaGmAaY/9IH0gfSB9AGppgBgYaH0gfQB9IH0AGEEIIySsKAVgAKrAQH30A6GmBgLjYSS+CcH0gGHaiaGmAaY/9IH0gfSB9AGppgBgYOCmE44BgAEqYhOmPhW8Q4YBKGATpn8cIxbMbC3MbK2QV44LJOZlvKAVxFWAAyS+G8BJrpOEBFcCBFd0VYACRWdjYKdxjgthOjq+G6hhoaYPqGAD9gHAU4ADAgB92YIQO5rKAFJgoFIwvvLhwiTQ+kD6APpA+gAwU5KhIaFQh6EWoFKQcIAQyMsFUAPPFgH6AstqyXH7ACXCACXXScICsI4XUEVwgBDIywVQA88WAfoCy2rJcfsAECOSNDTiWnCAEMjLBVADzxYB+gLLaslx+wBwIIIQX8w9FIKAejy0ZSzjkIxMzk5U1LHBZJfCeBRUccF8uH0ghAFE42RFrry4fUD+kAwRlAQNFlwB8jLABbLH1AEzxZYzxYBzxYB+gLMywDJ7VTgMDcowAPjAijAAJw2NxA4R2UUQzBw8AXgCMACmFVEECQQI/AF4F8KhA/y8AkA1Dg5ghA7msoAGL7y4clTRscFUVLHBRWx8uHKcCCCEF/MPRQhgBDIywUozxYh+gLLassfFcs/J88WJ88WFMoAI/oCE8oAyYMG+wBxUGZFFQRwB8jLABbLH1AEzxZYzxYBzxYB+gLMywDJ7VQAlsjLHxPLPyPPFlADzxbKAIIJycOA+gLKAMlxgBjIywUmzxZw+gLLaszJgwb7AHFVUHAHyMsAFssfUATPFljPFgHPFgH6AszLAMntVNZeZYk=') + + # fees cell + + marketplace_address = Address('EQBYTuYbLf8INxFtD8tQeNk5ZLy-nAX9ahQbG_yl1qQ-GEMS') + marketplace_fee_address = Address('EQCjk1hh952vWaE9bRguFkAhDAL5jj3xj9p0uPWrFBq_GEMS') + destination_address = Address('EQAIFunALREOeQ99syMbO6sSzM_Fa1RsPD5TBoS0qVeKQ-AR') + + wallet_address = Address(wallet_address) + royalty_address = Address(royalty_address) + nft_address = Address(nft_address) + + marketplace_fee = int(price * 5 / 100) # 5% + royalty_fee = int(price * 5 / 100) # 5% + + fees_data_cell = (begin_cell() + .store_address(marketplace_fee_address) + .store_coins(marketplace_fee) + .store_address(royalty_address) + .store_coins(royalty_fee) + .end_cell()) + + + sale_data_cell = (begin_cell() + .store_bit_int(0) + .store_uint(int(time.time()), 32) + .store_address(marketplace_address) + .store_address(nft_address) + .store_address(wallet_address) + .store_coins(price) + .store_ref(fees_data_cell) + .store_bit_int(0) + .end_cell()) + + state_init_cell = StateInit(code=nft_sale_code_cell, data=sale_data_cell).serialize() + + sale_body = (begin_cell() + .store_uint(1, 32) + .store_uint(0, 64) + .end_cell()) + + transfer_nft_body = (begin_cell() + .store_uint(0x5fcc3d14, 32) + .store_uint(0, 64) + .store_address(destination_address) + .store_address(wallet_address) + .store_bit_int(0) + .store_coins(int(1 * 10**9)) + .store_bit_int(0) + .store_uint(0x0fe0ede, 31) + .store_ref(state_init_cell) + .store_ref(sale_body) + .end_cell()) + + data = { + 'address': nft_address.to_str(), + 'amount': str(amount), + 'payload': urlsafe_b64encode(transfer_nft_body.to_boc()).decode() + } + + return data +``` + +
+ +The final transaction body: + +```python +transaction = { + 'valid_until': int(time.time() + 3600), + 'messages': [ + get_nft_transfer_message( + nft_address='EQDrA-3zsJXTfGo_Vdzg8d07Da4vSdHZllc6W9qvoNoMstF-', + recipient_address='0:0000000000000000000000000000000000000000000000000000000000000000', + transfer_fee=int(0.07 * 10**9), + response_address=wallet_address + ), + ] +} +``` + +### NFT Buy (GetGems) + +The process of buy NFT for [nft-fixprice-sale-v3r2](https://github.com/getgems-io/nft-contracts/blob/main/packages/contracts/sources/nft-fixprice-sale-v3r2.fc) sale contract could be carry out with regular transfer without payload, the only important thing is accurate TON amount, that calculates as follows: +`buyAmount = Nftprice TON + 1.0 TON`. + +```python +transaction = { + 'valid_until': int(time.time() + 3600), + 'messages': [ + { + 'address': nft_address, + 'amount': buyAmount, + ] +} +``` + +## Authors + +- JavaScript examples provided by [@aSpite](https://t.me/aspite) +- Python examples provided by [@yunwine](https://t.me/yungwine) + +## See Also + +- [TON Connect SDKs](/develop/dapps/ton-connect/developers) +- [TON Connect - Sending Messages](/develop/dapps/ton-connect/transactions) +- [Smart Contract Developmet - Sending Messages (Low Level)](/develop/smart-contracts/messages) From 50eda6ee7567bbdecd0b52a3f66bc13d81c0921f Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:50 +0800 Subject: [PATCH 053/219] New translations mobile.mdx (Chinese Simplified) --- .../current/develop/dapps/ton-connect/mobile.mdx | 1 + 1 file changed, 1 insertion(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/mobile.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/mobile.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/mobile.mdx new file mode 100644 index 0000000000..53079f2372 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/mobile.mdx @@ -0,0 +1 @@ +# TON Connect for Mobiles with the React Native From c1ab0102189ad465a411314025d93bdf80a69d63 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:51 +0800 Subject: [PATCH 054/219] New translations overview.mdx (Chinese Simplified) --- .../develop/dapps/ton-connect/overview.mdx | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/overview.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/overview.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/overview.mdx new file mode 100644 index 0000000000..d3e403f9f4 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/overview.mdx @@ -0,0 +1,91 @@ +import Button from '@site/src/components/button' + +# About TON Connect + +Experience seamless connectivity with wallets within [TON](/learn/introduction) using TON Connect. + +![](/img/docs/ton-connect/ton-connect-overview.png?raw=true) + +Feel free to use one of the following flows for integration of your application: + + + + + +## For Wallet Developers + +If you are a wallet developer, please read how to [integrate TON Connect into your wallet](/develop/dapps/ton-connect/wallet/). + +## Use Cases For Your DApp + +Explore these deliverables that the TON Ecosystem provides for superior DAppintegration. + +- **Traffic**. Drive additional user visits via crypto wallets that support TON Connect. +- **Authenticity**. Leverage TON user's wallets as ready-made accounts, removing the need for additional authentication steps and thus, boosting user experience. +- **Payments**. Process transactions swiftly and securely through TON Blockchain using Toncoin or wrapped stable coins (jUSDC/jUSDT). +- **Retention**. Enhance user retention through the in-app list-saving feature which allows users to keep track of recently opened and favorite apps. + +## Success Stories + +- [GetGems — The Open Network Marketplace](https://getgems.io/) +- [STON.fi — AMM DEX for TON blockchain](https://ston.fi/) +- [Tonstarter](http://tonstarter.com/) + +
+ Show the entire list + +- [getgems.io](https://getgems.io/) +- [fragment.com](https://fragment.com/) (Ton Connect v.1) +- [ston.fi](https://ston.fi/) +- [ton.diamonds](https://ton.diamonds/) +- [beta.disintar.io](https://beta.disintar.io/) +- [tegro.finance](https://tegro.finance/liquidity) +- [minter.ton.org](https://minter.ton.org/) +- [libermall.com](https://libermall.com/) +- [dedust.io](https://dedust.io/swap) +- [toncap.net](https://toncap.net/) +- [cryptomus.com](https://cryptomus.com/) +- [avanchange.com](https://avanchange.com/) +- [wton.dev](https://wton.dev/) +- [mint.spiroverse.io/shop](https://mint.spiroverse.io/shop) +- [vk.com/vk_nft_hub](https://vk.com/vk_nft_hub) +- [tonverifier.live](https://verifier.ton.org/) +- [stickerface.io/member](https://stickerface.io/member) +- [tonstarter.com](https://tonstarter.com/) +- [cryptogas.shop/ton](https://cryptogas.shop/ton) +- [megaton.fi](https://megaton.fi/) +- [dns.ton.org](https://dns.ton.org/) +- [coinpaymaster.com](https://coinpaymaster.com/) +- [ton.gagarin.world/app/](https://ton.gagarin.world/app) +- [daolama.co](https://daolama.co/) +- [marketplace.playmuse.org](http://marketplace.playmuse.org/) +- [ton.vote](https://ton.vote/) +- [plane.tonfancy.io](https://plane.tonfancy.io/) +- [pi.oberton.io](https://pi.oberton.io/) +- [business.thetonpay.app](https://business.thetonpay.app/) +- [bridge.orbitchain.io](https://bridge.orbitchain.io/) +- [connecton-web-new.vercel.app](https://connecton-web-new.vercel.app/) +- [app.fanz.ee/staking](https://app.fanz.ee/staking) +- [testnet.pton.fi](https://testnet.pton.fi/) +- [tonft.app](https://tonft.app/) +- [cardify.casino](https://cardify.casino/) +- [4riends.org](https://4riends.org/#/) +- [tonflex.fi](https://tonflex.fi/swap) +- [soquest.xyz](https://soquest.xyz/) +- [app.evaa.finance](https://app.evaa.finance/) + +
+ +## Join the TON Ecosystem + +To connect your service with the TON Ecosystem, you need to implement the following: + +- **TON Connect**. Incorporate the TON Connect protocol within your application. +- **Transactions**. Create specified transaction messages using TON libraries. Dive into the process of [sending messages](/develop/dapps/ton-connect/message-builders) with our comprehensive guide. +- **Payments**. Process payments via the public API ([tonapi](https://tonapi.io/)) or your own indexer, for instance, [gobycicle](http://github.com/gobicycle/bicycle). Learn more from our extensive guide on [asset processing](/develop/dapps/asset-processing). From 7d58811ca7243c8b33361bf2e1e512f4c6d1c0aa Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:52 +0800 Subject: [PATCH 055/219] New translations readme.md (Chinese Simplified) --- .../dapps/ton-connect/protocol/README.md | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/README.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/README.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/README.md new file mode 100644 index 0000000000..d8dad12d74 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/README.md @@ -0,0 +1,55 @@ +# Protocol specifications + +Understand how TON Connect works under the hood. + +## Who is this section for? + +- If you implement a wallet +- If you develop an SDK +- If you want to learn how TON Connect works + +## Sections overview + +- [Protocol workflows](/develop/dapps/ton-connect/protocol/workflow) is an overview of all the protocols involved in TON Connect. +- [Bridge API](/develop/dapps/ton-connect/protocol/bridge) specifies how the data is transmitted between the app and the wallet. +- [Session protocol](/develop/dapps/ton-connect/protocol/session) ensures end-to-end encrypted communication over the bridge. +- [Requests protocol](/develop/dapps/ton-connect/protocol/requests-responses) defines requests and responses for the app and the wallet. +- [Wallet guidelines](/develop/dapps/ton-connect/protocol/wallet-guidelines) defines guidelines for wallet developers. + +## FAQ + +#### I am building an HTML/JS app, what should I read? + +Simply use the [Supported SDKs](/develop/dapps/ton-connect/developers) and do not worry about the underlying protocols. + +#### I need an SDK in my favorite language + +Please take the [JS SDK](/develop/dapps/ton-connect/developers) as a reference and check out the protocol docs above. + +#### How do you detect whether the app is embedded in the wallet? + +JS SDK does that for you; just get wallets list `connector.getWallets()` and check `embedded` property of the corresponding list item. If you build your own SDK you should check `window.[targetWalletJsBridgeKey].tonconnect.isWalletBrowser`. + +#### How do you detect if the wallet is a browser extension? + +Like with embedded apps (see above), JS SDK detects it for you via `injected` property of the corresponding `connector.getWallets()` list item. If you build your own SDK you should check that `window.[targetWalletJsBridgeKey].tonconnect` exists. + +#### How to implement backend authorization with tonconnect? + +[See an example of dapp-backend](https://github.com/ton-connect/demo-dapp-backend) + +#### How do I make my own bridge? + +You don’t need to, unless you are building a wallet. + +If you build a wallet, you will need to provide a bridge. See our [reference implementation in Go](https://github.com/ton-connect/bridge). + +Keep in mind that the wallet’s side of the bridge API is not mandated. + +For a quick start you can use the common TON Connect bridge https://bridge.tonapi.io/bridge. + +#### I make a wallet, how do I add it to the list of wallets? + +Submit a pull request for the [wallets-list](https://github.com/ton-blockchain/wallets-list) repository and fill our all the necessary metadata. + +Apps may also add wallets directly through the SDK. From 7dbb1efc990038240c29ca55d7b2aab61e8a98bf Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:53 +0800 Subject: [PATCH 056/219] New translations bridge.md (Chinese Simplified) --- .../dapps/ton-connect/protocol/bridge.md | 201 ++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/bridge.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/bridge.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/bridge.md new file mode 100644 index 0000000000..cad6a0a522 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/bridge.md @@ -0,0 +1,201 @@ +# Bridge API + +Bridge is a transport mechanism to deliver messages from the app to the wallet and vice versa. + +- **Bridge is maintained by the wallet provider**. App developers do not have to choose or build a bridge. Each wallet’s bridge is listed in the [wallets-list](https://github.com/ton-blockchain/wallets-list) config. +- **Messages are end-to-end encrypted.** Bridge does not see the contents or long-term identifiers of the app or wallet. +- **Communication is symmetrical.** Bridge does not distinguish between apps and wallets: both are simply clients. +- Bridge keeps separate queues of messages for each recipient’s **Client ID**. + +Bridge comes in two flavors: + +- [HTTP Bridge](#http-bridge): for the external apps and services. +- [JS Bridge](#js-bridge): for apps opened within the wallet; or when the wallet is a browser extension. + +## HTTP Bridge + +Client with ID **A** connects to the bridge to listen to incoming requests. + +**Client ID is semi-private:** apps and wallets are not supposed to share their IDs with other entities to avoid having their messages removed unexpectedly. + +**Client can subscribe on few Client IDs** - in this case it should enumerate IDs separated with commas. For example: `?client_id=,,` + +```tsx +request + GET /events?client_id= + + Accept: text/event-stream +``` + +**Subscribing to the bridge second (any other) time** + +```tsx +request + GET /events?client_id=&last_event_id= + + Accept: text/event-stream +``` + +**lastEventId** – the eventId of the last SSE event wallet got over the bridge. In this case wallet will fetch all the events which happened after the last connection. + +Sending message from client A to client B. Bridge returns error if ttl is too high. + +```tsx +request + POST /message?client_id=?to=&ttl=300&topic= + + body: +``` + +The `topic` [optional] query parameter can be used by the bridge to deliver the push notification to the wallet. If the parameter is given, it must correspond to the RPC method called inside the encrypted `message`. + +Bridge buffers messages up to TTL (in secs), but removes them as soon as the recipient receives the message. + +If the TTL exceeds the hard limit of the bridge server, it should respond with HTTP 400. Bridges should support at least 300 seconds TTL. + +When the bridge receives a message `base64_encoded_message` from client `A` addressed to client `B`, it generates a message `BridgeMessage`: + +```js +{ + "from": , + "message": +} +``` + +and sends it to the client B via SSE connection + +```js +resB.write(BridgeMessage) +``` + +### Heartbeat + +To keep the connection, bridge server should periodically send a "heartbeat" message to the SSE channel. Client should ignore such messages. +So, the bridge heartbeat message is a string with word `heartbeat`. + +## Universal link + +When the app initiates the connection it sends it directly to the wallet via the QR code or a universal link. + +```bash +https://? + v=2& + id=& + r=& + ret=back +``` + +Parameter **v** specifies the protocol version. Unsupported versions are not accepted by the wallets. + +Parameter **id** specifies app’s Client ID encoded as hex (without '0x' prefix). + +Parameter **r** specifies URL-safe json [ConnectRequest](/develop/dapps/ton-connect/protocol/requests-responses#initiating-connection). + +Parameter **ret** (optional) specifies return strategy for the deeplink when user signs/declines the request. + +- 'back' (default) means return to the app which initialized deeplink jump (e.g. browser, native app, ...), +- 'none' means no jumps after user action; +- a URL: wallet will open this URL after completing the user's action. Note, that you shouldn't pass your app's URL if it is a webpage. This option should be used for native apps to work around possible OS-specific issues with `'back'` option. + +`ret` parameter should be supported for empty deeplinks -- it might be used to specify the wallet behavior after other actions confirmation (send transaction, sign raw, ...). + +```bash +https://?ret=back +``` + +The link may be embedded in a QR code or clicked directly. + +The initial request is unencrypted because (1) there is no personal data being communicated yet, (2) app does not even know the identity of the wallet. + +### Unified deeplink `tc` + +In addition to its own universal link, the wallet must support the unified deeplink. + +This allows applications to create a single qr code, which can be used to connect to any wallet. + +More specifically, the wallet must support `tc://` deeplink as well as its own ``. + +Therefore, the following `connect request` must be processed by the wallet: + +```bash +tc://? + v=2& + id=& + r=& + ret=back +``` + +## JS bridge + +Used by the embedded apps via the injected binding `window..tonconnect`. + +`wallet-js-bridge-key` can be specified in the [wallets list](https://github.com/ton-blockchain/wallets-list) + +JS bridge runs on the same device as the wallet and the app, so communication is not encrypted. + +The app works directly with plaintext requests and responses, without session keys and encryption. + +```tsx +interface TonConnectBridge { + deviceInfo: DeviceInfo; // see Requests/Responses spec + walletInfo?: WalletInfo; + protocolVersion: number; // max supported Ton Connect version (e.g. 2) + isWalletBrowser: boolean; // if the page is opened into wallet's browser + connect(protocolVersion: number, message: ConnectRequest): Promise; + restoreConnection(): Promise; + send(message: AppRequest): Promise; + listen(callback: (event: WalletEvent) => void): () => void; +} +``` + +Just like with the HTTP bridge, wallet side of the bridge does not receive the app requests except for [ConnectRequest](/develop/dapps/ton-connect/protocol/requests-responses#initiating-connection) until the session is confirmed by the user. Technically, the messages arrive from the webview into the bridge controller, but they are silently ignored. + +SDK around the implements **autoconnect()** and **connect()** as silent and non-silent attempts at establishing the connection. + +### walletInfo (optional) + +Represents wallet metadata. Might be defined to make an injectable wallet works with TonConnect even if the wallet is not listed in the [wallets-list.json](https://github.com/ton-blockchain/wallets-list). + +Wallet metadata format: + +```ts +interface WalletInfo { + name: string; + image: ; + tondns?: string; + about_url: ; +} +``` + +Detailed properties description: https://github.com/ton-blockchain/wallets-list#entry-format. + +If `TonConnectBridge.walletInfo` is defined and the wallet is listed in the [wallets-list.json](https://github.com/ton-blockchain/wallets-list), `TonConnectBridge.walletInfo` properties will override corresponding wallet properties from the wallets-list.json. + +### connect() + +Initiates connect request, this is analogous to QR/link when using the HTTP bridge. + +If the app was previously approved for the current account — connects silently with ConnectEvent. Otherwise shows confirmation dialog to the user. + +You shouldn't use the `connect` method without explicit user action (e.g. connect button click). If you want automatically try to restore previous connection, you should use the `restoreConnection` method. + +### restoreConnection() + +Attempts to restore the previous connection. + +If the app was previously approved for the current account — connects silently with the new `ConnectEvent` with only a `ton_addr` data item. + +Otherwise returns `ConnectEventError` with error code 100 (Unknown app). + +### send() + +Sends a [message](/develop/dapps/ton-connect/protocol/requests-responses#messages) to the bridge, excluding the ConnectRequest (that goes into QR code when using HTTP bridge and into connect when using JS Bridge). +Directly returns promise with WalletResponse, do you don't need to wait for responses with `listen`; + +### listen() + +Registers a listener for events from the wallet. + +Returns unsubscribe function. + +Currently, only `disconnect` event is available. Later there will be a switch account event and other wallet events. From d98668c06d77a35d3eff197807dd199cbfc653eb Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:54 +0800 Subject: [PATCH 057/219] New translations requests-responses.md (Chinese Simplified) --- .../protocol/requests-responses.md | 500 ++++++++++++++++++ 1 file changed, 500 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/requests-responses.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/requests-responses.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/requests-responses.md new file mode 100644 index 0000000000..f826b1a74e --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/requests-responses.md @@ -0,0 +1,500 @@ +# Requests and Responses + +App sends requests to the wallet. Wallet sends responses and events to the app. + +```tsx +type AppMessage = ConnectRequest | AppRequest; + +type WalletMessage = WalletResponse | WalletEvent; +``` + +### App manifest + +App needs to have its manifest to pass meta information to the wallet. Manifest is a JSON file named as `tonconnect-manifest.json` following format: + +```json +{ + "url": "", // required + "name": "", // required + "iconUrl": "", // required + "termsOfUseUrl": "", // optional + "privacyPolicyUrl": "" // optional +} +``` + +Best practice is to place the manifest in the root of your app, e.g. `https://myapp.com/tonconnect-manifest.json`. It allows the wallet to handle your app better and improve the UX connected to your app. +Make sure that manifest is available to GET by its URL. + +#### Fields description + +- `url` -- app URL. Will be used as the DAppidentifier. Will be used to open the DAppafter click to its icon in the wallet. It is recommended to pass url without closing slash, e.g. 'https://mydapp.com' instead of 'https://mydapp.com/'. +- `name` -- app name. Might be simple, will not be used as identifier. +- `iconUrl` -- Url to the app icon. Must be PNG, ICO, ... format. SVG icons are not supported. Perfectly pass url to a 180x180px PNG icon. +- `termsOfUseUrl` -- (optional) url to the Terms Of Use document. Optional for usual apps, but required for the apps which is placed in the Tonkeeper recommended apps list. +- `privacyPolicyUrl` -- (optional) url to the Privacy Policy document. Optional for usual apps, but required for the apps which is placed in the Tonkeeper recommended apps list. + +### Initiating connection + +App’s request message is **InitialRequest**. + +```tsx +type ConnectRequest = { + manifestUrl: string; + items: ConnectItem[], // data items to share with the app +} + +// In the future we may add other personal items. +// Or, instead of the wallet address we may ask for per-service ID. +type ConnectItem = TonAddressItem | TonProofItem | ...; + +type TonAddressItem = { + name: "ton_addr"; +} +type TonProofItem = { + name: "ton_proof"; + payload: string; // arbitrary payload, e.g. nonce + expiration timestamp. +} +``` + +ConnectRequest description: + +- `manifestUrl`: link to the app's tonconnect-manifest.json +- `items`: data items to share with the app. + +Wallet responds with **ConnectEvent** message if the user approves the request. + +```tsx +type ConnectEvent = ConnectEventSuccess | ConnectEventError; + +type ConnectEventSuccess = { + event: "connect"; + id: number; // increasing event counter + payload: { + items: ConnectItemReply[]; + device: DeviceInfo; + } +} +type ConnectEventError = { + event: "connect_error", + id: number; // increasing event counter + payload: { + code: number; + message: string; + } +} + +type DeviceInfo = { + platform: "iphone" | "ipad" | "android" | "windows" | "mac" | "linux"; + appName: string; // e.g. "Tonkeeper" + appVersion: string; // e.g. "2.3.367" + maxProtocolVersion: number; + features: Feature[]; // list of supported features and methods in RPC + // Currently there is only one feature -- 'SendTransaction'; +} + +type Feature = { name: 'SendTransaction', maxMessages: number } | // `maxMessages` is maximum number of messages in one `SendTransaction` that the wallet supports + { name: 'SignData' }; + +type ConnectItemReply = TonAddressItemReply | TonProofItemReply ...; + +// Untrusted data returned by the wallet. +// If you need a guarantee that the user owns this address and public key, you need to additionally request a ton_proof. +type TonAddressItemReply = { + name: "ton_addr"; + address: string; // TON address raw (`0:`) + network: NETWORK; // network global_id + publicKey: string; // HEX string without 0x + walletStateInit: string; // Base64 (not url safe) encoded stateinit cell for the wallet contract +} + +type TonProofItemReply = TonProofItemReplySuccess | TonProofItemReplyError; + +type TonProofItemReplySuccess = { + name: "ton_proof"; + proof: { + timestamp: string; // 64-bit unix epoch time of the signing operation (seconds) + domain: { + lengthBytes: number; // AppDomain Length + value: string; // app domain name (as url part, without encoding) + }; + signature: string; // base64-encoded signature + payload: string; // payload from the request + } +} + +type TonProofItemReplyError = { + name: "ton_addr"; + error: { + code: ConnectItemErrorCode; + message?: string; + } +} + +enum NETWORK { + MAINNET = '-239', + TESTNET = '-3' +} +``` + +**Connect event error codes:** + +| code | description | +| ---- | ---------------------------- | +| 0 | Unknown error | +| 1 | Bad request | +| 2 | App manifest not found | +| 3 | App manifest content error | +| 100 | Unknown app | +| 300 | User declined the connection | + +**Connect item error codes:** + +| code | description | +| ---- | ----------------------- | +| 0 | Unknown error | +| 400 | Method is not supported | + +If the wallet doesn't support the requested `ConnectItem` (e.g. "ton_proof"), it must send reply with the following ConnectItemReply corresponding to the requested item. +with following structure: + +```ts +type ConnectItemReplyError = { + name: ""; + error: { + code: 400; + message?: string; + } +} +``` + +### Address proof signature (`ton_proof`) + +If `TonProofItem` is requested, wallet proves ownership of the selected account’s key. The signed message is bound to: + +- Unique prefix to separate messages from on-chain messages. (`ton-connect`) +- Wallet address. +- App domain +- Signing timestamp +- App’s custom payload (where server may put its nonce, cookie id, expiration time). + +``` +message = utf8_encode("ton-proof-item-v2/") ++ + Address ++ + AppDomain ++ + Timestamp ++ + Payload +signature = Ed25519Sign(privkey, sha256(0xffff ++ utf8_encode("ton-connect") ++ sha256(message))) +``` + +where: + +- `Address` is the wallet address encoded as a sequence: + - `workchain`: 32-bit signed integer big endian; + - `hash`: 256-bit unsigned integer big endian; +- `AppDomain` is Length ++ EncodedDomainName + - `Length` is 32-bit value of utf-8 encoded app domain name length in bytes + - `EncodedDomainName` id `Length`-byte utf-8 encoded app domain name +- `Timestamp` 64-bit unix epoch time of the signing operation +- `Payload` is a variable-length binary string. + +Note: payload is variable-length untrusted data. To avoid using unnecessary length prefixes we simply put it last in the message. + +The signature must be verified by public key: + +1. First, try to obtain public key via `get_public_key` get-method on smart contract deployed at `Address`. + +2. If the smart contract is not deployed yet, or the get-method is missing, you need: + + 1. Parse `TonAddressItemReply.walletStateInit` and get public key from stateInit. You can compare the `walletStateInit.code` with the code of standard wallets contracts and parse the data according to the found wallet version. + + 2. Check that `TonAddressItemReply.publicKey` equals to obtained public key + + 3. Check that `TonAddressItemReply.walletStateInit.hash()` equals to `TonAddressItemReply.address`. `.hash()` means BoC hash. + +## Messages + +- All messages from the app to the wallet are requests for an operation. +- Messages from the wallet to the application can be either responses to app requests or events triggered by user actions on the side of the wallet. + +**Available operations:** + +- sendTransaction +- signData +- disconnect + +**Available events:** + +- connect +- connect_error +- disconnect + +### Structure + +**All app requests have the following structure (like json-rpc 2.0)** + +```tsx +interface AppRequest { + method: string; + params: string[]; + id: string; +} +``` + +Where + +- `method`: name of the operation ('sendTransaction', 'signMessage', ...) +- `params`: array of the operation specific parameters +- `id`: increasing identifier that allows to match requests and responses + +**Wallet messages are responses or events.** + +Response is an object formatted as a json-rpc 2.0 response. Response `id` must match request's id. + +Wallet doesn't accept any request with an id that does not greater the last processed request id of that session. + +```tsx +type WalletResponse = WalletResponseSuccess | WalletResponseError; + +interface WalletResponseSuccess { + result: string; + id: string; +} + +interface WalletResponseError { + error: { code: number; message: string; data?: unknown }; + id: string; +} +``` + +Event is an object with property `event` that is equal to event's name, `id` that is increasing event counter (**not** related to `request.id` because there is no request for an event), and `payload` that contains event additional data. + +```tsx +interface WalletEvent { + event: WalletEventName; + id: number; // increasing event counter + payload: ; // specific payload for each event +} + +type WalletEventName = 'connect' | 'connect_error' | 'disconnect'; +``` + +Wallet must increase `id` while generating a new event. (Every next event must have `id` > previous event `id`) + +DAppdoesn't accept any event with an id that does not greater the last processed event id of that session. + +### Methods + +#### Sign and send transaction + +App sends **SendTransactionRequest**: + +```tsx +interface SendTransactionRequest { + method: 'sendTransaction'; + params: []; + id: string; +} +``` + +Where `` is JSON with following properties: + +- `valid_until` (integer, optional): unix timestamp. after th moment transaction will be invalid. +- `network` (NETWORK, optional): The network (mainnet or testnet) where DAppintends to send the transaction. If not set, the transaction is sent to the network currently set in the wallet, but this is not safe and DAppshould always strive to set the network. If the `network` parameter is set, but the wallet has a different network set, the wallet should show an alert and DO NOT ALLOW TO SEND this transaction. +- `from` (string in `:` format, optional) - The sender address from which DAppintends to send the transaction. If not set, wallet allows user to select the sender's address at the moment of transaction approval. If `from` parameter is set, the wallet should DO NOT ALLOW user to select the sender's address; If sending from the specified address is impossible, the wallet should show an alert and DO NOT ALLOW TO SEND this transaction. +- `messages` (array of messages): 1-4 outgoing messages from the wallet contract to other accounts. All messages are sent out in order, however **the wallet cannot guarantee that messages will be delivered and executed in same order**. + +Message structure: + +- `address` (string): message destination +- `amount` (decimal string): number of nanocoins to send. +- `payload` (string base64, optional): raw one-cell BoC encoded in Base64. +- `stateInit` (string base64, optional): raw once-cell BoC encoded in Base64. + +#### Common cases + +1. No payload, no stateInit: simple transfer without a message. +2. payload is prefixed with 32 zero bits, no stateInit: simple transfer with a text message. +3. No payload or prefixed with 32 zero bits; stateInit is present: deployment of the contract. + +
+Example + +```json5 +{ + "valid_until": 1658253458, + "network": "-239", // enum NETWORK { MAINNET = '-239', TESTNET = '-3'} + "from": "0:348bcf827469c5fc38541c77fdd91d4e347eac200f6f2d9fd62dc08885f0415f", + "messages": [ + { + "address": "0:412410771DA82CBA306A55FA9E0D43C9D245E38133CB58F1457DFB8D5CD8892F", + "amount": "20000000", + "stateInit": "base64bocblahblahblah==" //deploy contract + },{ + "address": "0:E69F10CC84877ABF539F83F879291E5CA169451BA7BCE91A37A5CED3AB8080D3", + "amount": "60000000", + "payload": "base64bocblahblahblah==" //transfer nft to new deployed account 0:412410771DA82CBA306A55FA9E0D43C9D245E38133CB58F1457DFB8D5CD8892F + } + ] +} +``` + +
+ +Wallet replies with **SendTransactionResponse**: + +```tsx +type SendTransactionResponse = SendTransactionResponseSuccess | SendTransactionResponseError; + +interface SendTransactionResponseSuccess { + result: ; + id: string; + +} + +interface SendTransactionResponseError { + error: { code: number; message: string }; + id: string; +} +``` + +**Error codes:** + +| code | description | +| ---- | ----------------------------- | +| 0 | Unknown error | +| 1 | Bad request | +| 100 | Unknown app | +| 300 | User declined the transaction | +| 400 | Method not supported | + +#### Sign Data (Experimental) + +> WARNING: this is currently an experimental method and its signature may change in the future + +App sends **SignDataRequest**: + +```tsx +interface SignDataRequest { + method: 'signData'; + params: []; + id: string; +} +``` + +Where `` is JSON with following properties: + +- `schema_crc` (integer): indicates the layout of payload cell that in turn defines domain separation. +- `cell` (string, base64 encoded Cell): contains arbitrary data per its TL-B definition. +- `publicKey` (HEX string without 0x, optional): The public key of key pair from which DAppintends to sign the data. If not set, the wallet is not limited in what keypair to sign. If `publicKey` parameter is set, the wallet SHOULD to sign by keypair corresponding this public key; If sign by this specified keypair is impossible, the wallet should show an alert and DO NOT ALLOW TO SIGN this data. + +The signature will be computed in the following way: +`ed25519(uint32be(schema_crc) ++ uint64be(timestamp) ++ cell_hash(X), privkey)` + +[See details](https://github.com/oleganza/TEPs/blob/datasig/text/0000-data-signatures.md) + +Wallet should decode the cell in accordance with the schema_crc and show corresponding data to the user. +If the schema_crc is unknown to the wallet, the wallet should show danger notification/UI to the user. + +Wallet replies with **SignDataResponse**: + +```tsx +type SignDataResponse = SignDataResponseSuccess | SignDataResponseError; + +interface SignDataResponseSuccess { + result: { + signature: string; // base64 encoded signature + timestamp: string; // UNIX timestamp in seconds (UTC) at the moment on creating the signature. + }; + id: string; +} + +interface SignDataResponseError { + error: { code: number; message: string }; + id: string; +} +``` + +**Error codes:** + +| code | description | +| ---- | ------------------------- | +| 0 | Unknown error | +| 1 | Bad request | +| 100 | Unknown app | +| 300 | User declined the request | +| 400 | Method not supported | + +#### Disconnect operation + +When user disconnects the wallet in the dApp, DAppshould inform the wallet to help the wallet save resources and delete unnecessary session. +Allows the wallet to update its interface to the disconnected state. + +```tsx +interface DisconnectRequest { + method: 'disconnect'; + params: []; + id: string; +} +``` + +Wallet replies with **DisconnectResponse**: + +```ts +type DisconnectResponse = DisconnectResponseSuccess | DisconnectResponseError; + +interface DisconnectResponseSuccess { + id: string; + result: { }; +} + +interface DisconnectResponseError { + error: { code: number; message: string }; + id: string; +} +``` + +Wallet **shouldn't** emit a 'Disconnect' event if disconnect was initialized by the dApp. + +**Error codes:** + +| code | description | +| ---- | -------------------- | +| 0 | Unknown error | +| 1 | Bad request | +| 100 | Unknown app | +| 400 | Method not supported | + +### Wallet events + +Disconnect + +The event fires when the user deletes the app in the wallet. The app must react to the event and delete the saved session. If the user disconnects the wallet on the app side, then the event does not fire, and the session information remains in the localstorage + +```tsx +interface DisconnectEvent { + type: "disconnect", + id: number; // increasing event counter + payload: { } +} +``` + +Connect + +```tsx +type ConnectEventSuccess = { + event: "connect", + id: number; // increasing event counter + payload: { + items: ConnectItemReply[]; + device: DeviceInfo; + } +} +type ConnectEventError = { + event: "connect_error", + id: number; // increasing event counter + payload: { + code: number; + message: string; + } +} +``` From 632f585f12d60ee2ca74c2c12f85377fe5ec0046 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:55 +0800 Subject: [PATCH 058/219] New translations session.md (Chinese Simplified) --- .../dapps/ton-connect/protocol/session.md | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/session.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/session.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/session.md new file mode 100644 index 0000000000..67181e7602 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/session.md @@ -0,0 +1,60 @@ +# Session protocol + +Session protocol defines client identifiers and offers end-to-end encryption for the app and the wallet. This means the HTTP bridge is fully untrusted and cannot read the user’s data transmitted between the app and the wallet. JS bridge does not use this protocol since both the wallet and the app run on the same device. + +## Definitions + +### Client Keypair + +X25519 keypair for the use with NaCl “crypto_box” protocol. + +``` +a <- random 23 bytes +A <- X25519Pubkey(s) +``` + +or + +``` +(a,A) <- nacl.box.keyPair() +``` + +### Client ID + +The public key part of the [Client Keypair](#client-keypair) (32 bytes). + +### Session + +A session is defined by a pair of two client IDs. Both the app and the wallet create their own [Client IDs](#client-id). + +### Creating client Keypair + +``` +(a,A) <- nacl.box.keyPair() +``` + +### Encryption + +All requests from the app (except the initial request) and all responses from the wallet are encrypted. + +Given a binary encoding of message **m**, recipient’s [Client ID](#client-id) **X** and sender’s private key **y** the message is encrypted as follows: + +``` +nonce <- random(24 bytes) +ct <- nacl.box(m, nonce, X, y) +M <- nonce ++ ct +``` + +That is, the final message **M** has the first 24 bytes set to the random nonce. + +### Decryption + +To decrypt the message **M**, the recipient uses its private key **x** and sender’s public key **Y** (aka [Client ID](#client-id)): + +``` +nonce <- M[0..24] +ct <- M[24..] +m <- nacl.box.open(ct, nonce, Y, x) +``` + +Plaintext **m** is recovered and parsed per [Requests/Responses](/develop/dapps/ton-connect/protocol/requests-responses#requests-and-responses). From a1fabcbf7afa8f9a3edee31834cf519e31c6c6b3 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:55 +0800 Subject: [PATCH 059/219] New translations wallet-guidelines.md (Chinese Simplified) --- .../ton-connect/protocol/wallet-guidelines.md | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/wallet-guidelines.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/wallet-guidelines.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/wallet-guidelines.md new file mode 100644 index 0000000000..93c4fd416e --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/wallet-guidelines.md @@ -0,0 +1,61 @@ +# Wallet Guidelines + +## Networks + +### There aren't many networks. + +At the moment, there are only two networks - Mainnet and Testnet. +In the foreseeable future, the emergence of new Mainnet TON-like networks is not expected. Note that the current Mainnet has a built-in mechanism for alternative networks - workchains. + +### Hide the Testnet from ordinary users. + +Testnet is used exclusively by developers. Ordinary users should not see the Testnet. +This means that switching to Testnet should not be readily available and users SHOULD NOT be prompted to switch wallet to Testnet even if DAppis in Testnet. +Users switch to Testnet, don't understand this action, can't switch back to Mainnet. + +For these reasons, dapps do not need to switch networks in runtime, on the contrary, it is more preferable to have different instances of DAppon different domains dapp.com, Testnet.dapp.com. +For the same reason there is no `NetworkChanged` or `ChainChanged` event in the Ton Connect protocol. + +### Do not send anything if the DAppis in Testnet and the wallet is in Mainnet. + +It is necessary to prevent loss of funds when DApptries to send a transaction in Testnet, and the wallet sends it in Mainnet. + +Dapps should explicitly indicate `network` field in `SendTransaction` request. + +If the `network` parameter is set, but the wallet has a different network set, the wallet should show an alert and DO NOT ALLOW TO SEND this transaction. + +The wallet SHOULD NOT offer to switch to another network in this case. + +## Multi accounts + +Multiple network accounts can be created for one key pair. Implement this functionality in your wallet - users will find it useful. + +### In general, there is no current "active" account + +At the moment, the TON Connect is not built on the paradigm that there is one selected account in the wallet, and when the user switches to another account, the `AccountChanged` event is sent to dapp. + +We think of a wallet as a physical wallet that can contain many "bank cards" (accounts). + +In most cases the sender address is not important to dapp, in these cases the user can select the appropriate account at the time of approving the transaction and the transaction will be sent from selected account. + +In some cases, it is important for DAppto send the transaction from a specific address, in which case it explicitly specifies the `from` field in `SendTransaction` request. If `from` parameter is set, the wallet should DO NOT ALLOW user to select the sender's address; If sending from the specified address is impossible, the wallet should show an alert and DO NOT ALLOW TO SEND this transaction. + +### Login flow + +When DAppconnects the wallet, the user selects in the wallet one of their accounts that they want to log into dapp. + +Regardless of what accounts the user uses next in the wallet, DAppworks with the account he received on the connection. + +Just like if you logged into a web service with one of your email accounts - if you then change the email account in the email service, the web service continues to use the one he got when you logged in. + +For this reason, the protocol does not provide the `AccountChanged` event. + +To switch account user need to disconnect (Log out) and connect (Login) again in DAppUI. + +We recommend wallets provide the ability to disconnect session with a specified DAppbecause the DAppmay have an incomplete UI. + +## See Also + +- [TON Connect Overview](/dapps/ton-connect/overview) +- [Protocol specifications](/dapps/ton-connect/protocol/) +- [Connect a Wallet](/dapps/ton-connect/wallet) From 9451944dc2f618c6f095d358b822074799b97893 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:56 +0800 Subject: [PATCH 060/219] New translations workflow.md (Chinese Simplified) --- .../dapps/ton-connect/protocol/workflow.md | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/workflow.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/workflow.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/workflow.md new file mode 100644 index 0000000000..762f56a0a4 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/protocol/workflow.md @@ -0,0 +1,41 @@ +# Workflows + +## Overview + +### First-time connection via http bridge + +1. App initiates SSE connection with bridge; +2. App passes connection info to the wallet via universal link or deeplink or QR code; +3. Wallet connects to the bridge with given parameters, and save connection info locally; +4. Wallet sends account information to the app using bridge; +5. App receives message and save connection info locally; + +### Reconnection with http bridge + +1. App reads connection info from localstorage +2. App connects to the bridge +3. User opens the wallet, the wallet connects to the bridge using stored connection info + +### First-time connection via js bridge + +1. App checks existing of the `window.[walletJsBridgeKey].tonconnect` +2. App calls `window.[walletJsBridgeKey].tonconnect.connect()` and waits for a response +3. Wallet sends account information to the app; + +### Making ordinary requests and responses + +1. App and wallet are in a connected state +2. App generates request and sends it to the bridge +3. Bridge forwards message to the wallet +4. Wallet generates response and sends it to the bridge +5. Bridge forwards message to the app + +## Specification + +Read detailed specification [here](https://github.com/ton-blockchain/ton-connect/blob/main/workflows.md#details). + +## See Also + +- [Ton Connect Overview](/dapps/ton-connect/) +- [Integration manual](/develop/dapps/ton-connect/integration) +- [Telegram bot integration manual](/develop/dapps/ton-connect/tg-bot-integration) From 6bdc4f3185c09ad199ab4809e4ef3d46cf882d9a Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:57 +0800 Subject: [PATCH 061/219] New translations react.mdx (Chinese Simplified) --- .../develop/dapps/ton-connect/react.mdx | 332 ++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/react.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/react.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/react.mdx new file mode 100644 index 0000000000..bfca13178b --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/react.mdx @@ -0,0 +1,332 @@ +# TON Connect for React + +Recommended SDK for React Apps is a [UI React SDK](/develop/dapps/ton-connect/developers#ton-connect-react). It is a React component that provides a high-level way to interact with TON Connect. + +## Implementation + +### 1. Installation + +To start integrating TON Connect into your DApp, you need to install the `@tonconnect/ui-react` package. You can use npm or yarn for this purpose: + +```bash npm2yarn +npm i @tonconnect/ui-react +``` + +### 2. TON Connect Initiation + +After installing the package, you should create a `manifest.json` file for your application. More information on how to create a manifest.json file can be found [here](/develop/dapps/ton-connect/manifest). + +After creating the manifest file, import TonConnectUIProvider to the root of your Mini App and pass the manifest URL: + +```tsx +import { TonConnectUIProvider } from '@tonconnect/ui-react'; + +export function App() { + return ( + + { /* Your app */ } + + ); +} + +``` + +### 3. Connect to the Wallet + +Add the `TonConnectButton`. The TonConnect Button is a universal UI component for initializing a connection. After the wallet is connected, it transforms into a wallet menu. It is recommended to place it in the top right corner of your app. + +```tsx +export const Header = () => { + return ( +
+ My App with React UI + +
+ ); +}; +``` + +You can add className and style props to the button as well. Note that you cannot pass child to the TonConnectButton: + +```js + +``` + +Moreover, you always can initiate the connection manually, using `useTonConnectUI` hook and [connectWallet](https://github.com/ton-connect/sdk/tree/main/packages/ui#call-connect) method. + +### 4. Redirects + +If you want to redirect user to a specific page after wallet connection, you can use `useTonConnectUI` hook and [customize your return strategy](https://github.com/ton-connect/sdk/tree/main/packages/ui#add-the-return-strategy). + +#### Telegram Mini Apps + +If you want to redirect user to a [Telegram Mini App](/develop/dapps/telegram-apps/) after wallet connection, you can customize the `TonConnectUIProvider` element: + +```tsx + ' + }} + > + +``` + +[Open example on GitHub](https://github.com/ton-connect/demo-dapp-with-wallet/blob/master/src/App.tsx) + +### 5. UI customization + +To [customize UI](https://github.com/ton-connect/sdk/tree/main/packages/ui#ui-customisation) of the modal you can use `useTonConnectUI` hook and `setOptions` function. See more about useTonConnectUI hook in [Hooks](#hooks) section. + +## Hooks + +If you want to use some low-level TON Connect UI SDK features in your React app, you can use hooks from `@tonconnect/ui-react` package. + +### useTonAddress + +Use it to get user's current ton wallet address. Pass boolean parameter isUserFriendly to choose format of the address. If wallet is not connected hook will return empty string. + +```tsx +import { useTonAddress } from '@tonconnect/ui-react'; + +export const Address = () => { + const userFriendlyAddress = useTonAddress(); + const rawAddress = useTonAddress(false); + + return ( + userFriendlyAddress && ( +
+ User-friendly address: {userFriendlyAddress} + Raw address: {rawAddress} +
+ ) + ); +}; +``` + +### useTonWallet + +Use it to get user's current ton wallet. If wallet is not connected hook will return null. + +See all wallet's properties + +[Wallet interface](https://ton-connect.github.io/sdk/interfaces/_tonconnect_sdk.Wallet.html) +[WalletInfo interface](https://ton-connect.github.io/sdk/types/_tonconnect_sdk.WalletInfo.html) + +```tsx +import { useTonWallet } from '@tonconnect/ui-react'; + +export const Wallet = () => { + const wallet = useTonWallet(); + + return ( + wallet && ( +
+ Connected wallet: {wallet.name} + Device: {wallet.device.appName} +
+ ) + ); +}; +``` + +### useTonConnectUI + +Use it to get access to the `TonConnectUI` instance and UI options updating function. + +[See more about TonConnectUI instance methods](https://github.com/ton-connect/sdk/tree/main/packages/ui#send-transaction) + +[See more about setOptions function](https://github.com/ton-connect/sdk/tree/main/packages/ui#change-options-if-needed) + +```tsx +import { Locales, useTonConnectUI } from '@tonconnect/ui-react'; + +export const Settings = () => { + const [tonConnectUI, setOptions] = useTonConnectUI(); + + const onLanguageChange = (lang: string) => { + setOptions({ language: lang as Locales }); + }; + + return ( +
+ + +
+ + +
+
+ ); +}; +``` + +### useIsConnectionRestored + +Indicates current status of the connection restoring process. +You can use it to detect when connection restoring process if finished. + +```tsx +import { useIsConnectionRestored } from '@tonconnect/ui-react'; + +export const EntrypointPage = () => { + const connectionRestored = useIsConnectionRestored(); + + if (!connectionRestored) { + return Please wait...; + } + + return ; +}; +``` + +## Usage + +Let's take a look at how to use the React UI SDK on practice. + +### Sending transactions + +Send TON coins (in nanotons) to a specific address: + +```js +import { useTonConnectUI } from '@tonconnect/ui-react'; + +const transaction = { + messages: [ + { + address: "0:412410771DA82CBA306A55FA9E0D43C9D245E38133CB58F1457DFB8D5CD8892F", // destination address + amount: "20000000" //Toncoin in nanotons + } + ] + +} + +export const Settings = () => { + const [tonConnectUI, setOptions] = useTonConnectUI(); + + return ( +
+ +
+ ); +}; +``` + +- Get more examples here: [Preparing Messages](/develop/dapps/ton-connect/message-builders) + +### Understanding Transaction Status by Hash + +The principle located in Payment Processing (using tonweb). [See more](/develop/dapps/asset-processing/#checking-contracts-transactions) + +### Optional Check (ton_proof) on the Backend + +:::tip +Understand how to sign and verify messages: [Signing and Verification](/develop/dapps/ton-connect/sign) +::: + +Use `tonConnectUI.setConnectRequestParameters` function to pass your connect request parameters. + +This function takes one parameter: + +Set state to 'loading' while you are waiting for the response from your backend. If user opens connect wallet modal at this moment, he will see a loader. + +```ts +const [tonConnectUI] = useTonConnectUI(); + +tonConnectUI.setConnectRequestParameters({ + state: 'loading' +}); +``` + +or + +Set state to 'ready' and define `tonProof` value. Passed parameter will be applied to the connect request (QR and universal link). + +```ts +const [tonConnectUI] = useTonConnectUI(); + +tonConnectUI.setConnectRequestParameters({ + state: 'ready', + value: { + tonProof: '' + } +}); +``` + +or + +Remove loader if it was enabled via `state: 'loading'` (e.g. you received an error instead of a response from your backend). Connect request will be created without any additional parameters. + +```ts +const [tonConnectUI] = useTonConnectUI(); + +tonConnectUI.setConnectRequestParameters(null); +``` + +You can call `tonConnectUI.setConnectRequestParameters` multiple times if your tonProof payload has bounded lifetime (e.g. you can refresh connect request parameters every 10 minutes). + +```ts +const [tonConnectUI] = useTonConnectUI(); + +// enable ui loader +tonConnectUI.setConnectRequestParameters({ state: 'loading' }); + +// fetch you tonProofPayload from the backend +const tonProofPayload: string | null = await fetchTonProofPayloadFromBackend(); + +if (!tonProofPayload) { + // remove loader, connect request will be without any additional parameters + tonConnectUI.setConnectRequestParameters(null); +} else { + // add tonProof to the connect request + tonConnectUI.setConnectRequestParameters({ + state: "ready", + value: { tonProof: tonProofPayload } + }); +} + +``` + +You can find `ton_proof` result in the `wallet` object when wallet will be connected: + +```ts +import {useTonConnectUI} from "@tonconnect/ui-react"; + +const [tonConnectUI] = useTonConnectUI(); + +useEffect(() => + tonConnectUI.onStatusChange(wallet => { + if (wallet.connectItems?.tonProof && 'proof' in wallet.connectItems.tonProof) { + checkProofInYourBackend(wallet.connectItems.tonProof.proof, wallet.account); + } + }), []); +``` + +### Wallet Disconnection + +Call to disconnect the wallet: + +```js +import { useTonConnectUI } from '@tonconnect/ui-react'; + +const [tonConnectUI] = useTonConnectUI(); + +await tonConnectUI.disconnect(); +``` + +## API Documentation + +[Latest API documentation](https://ton-connect.github.io/sdk/modules/_tonconnect_ui_react.html) + +## Examples + +- Step-by-step [TON Hello World guide](https://ton-community.github.io/tutorials/03-client/) to create a simple DAppwith React UI. +- [Demo dApp](https://github.com/ton-connect/demo-dapp-with-react-ui) - Example of a DAppwith `@tonconnect/ui-react`. +- [ton.vote](https://github.com/orbs-network/ton-vote) - Example of React website with TON Connect implementation. From 2bb28d8f9d948cfd7396191522ab4a04140cf34f Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:58 +0800 Subject: [PATCH 062/219] New translations security.md (Chinese Simplified) --- .../current/develop/dapps/ton-connect/security.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/security.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/security.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/security.md new file mode 100644 index 0000000000..b07d280938 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/security.md @@ -0,0 +1,11 @@ +# TON Connect for Security + +TON Connect ensures that users have explicit control over the data they share, meaning data is not susceptible to leakage during app and wallet transfer. To reinforce this design, wallets and apps employ strong cryptographic authentication systems that work together. + +## User data and funds security + +- On TON Connect, user data is end-to-end encrypted when transmitted to wallets via bridges. This allows apps and wallets to employ third-party bridge servers that decrease the possibility of data theft and manipulation, dramatically increasing data integrity and safety in the process. +- Through TON Connect, security parameters are put in place to allow users data to be directly authenticated with their wallet address. This allows users to make use of multiple wallets and choose which one is used within a particular app. +- The TON Connect protocol allows for personal data item sharing (such as contact details and KYC info etc.) meaning the user explicitly confirms the sharing of such data. + +Specific details and related code examples pertaining to TON Connect and its underlying security-focused design can be found via [TON Connect GitHub](https://github.com/ton-connect/). From 9c90bca317762c1b9228e1acdc2081559d37b722 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:58 +0800 Subject: [PATCH 063/219] New translations sign.mdx (Chinese Simplified) --- .../develop/dapps/ton-connect/sign.mdx | 221 ++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/sign.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/sign.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/sign.mdx new file mode 100644 index 0000000000..8156f76f49 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/sign.mdx @@ -0,0 +1,221 @@ +import ThemedImage from '@theme/ThemedImage'; + +# Verifying signed in users on backend + +This page describes a way for backend to ensure that the user truly owns the declared address. +Please note that the user verification is not required for all DApps. + +It is useful if you want to verify a user to provide them with their personal information from the back end. + +## How does it work? + +- User initiates sign in process. +- Backend generates a ton_proof entity and sends it to frontend. +- Frontend signs in to wallet using ton_proof and receives back a signed ton_proof. +- Frontend sends signed ton_proof to backend for verification. + +

+ +

+ +## Structure of ton_proof + +We will be using the TonProof entity, implemented inside connector. + +```js +type TonProofItemReply = TonProofItemReplySuccess | TonProofItemReplyError; + +type TonProofItemReplySuccess = { + name: "ton_proof"; + proof: { + timestamp: string; // 64-bit unix epoch time of the signing operation (seconds) + domain: { + lengthBytes: number; // AppDomain Length + value: string; // app domain name (as url part, without encoding) + }; + signature: string; // base64-encoded signature + payload: string; // payload from the request + } +} + +``` + +## Checking ton_proof on Server Side + +1. Retrieve `TonProofItemReply` from a user. +2. Verify that the received domain corresponds to the domain of your application. +3. Check if `TonProofItemReply.payload` is permitted by original server and is still active. +4. Check if `timestamp` is actual at the moment. +5. Assemble a message according to the [message scheme](/develop/dapps/ton-connect/sign#concept-explanation). +6. Retrieve `public_key` either from API (a) or via back-end logic (b) + +- 6a: + - Retrieve `{public_key, address}` from the `walletStateInit` with [TON API](https://docs.tonconsole.com/tonapi/api-v2#:~:text=/v2/-,tonconnect,-/stateinit) method `POST /v2/tonconnect/stateinit`. + - Check that the `address` extracted from `walletStateInit` corresponds to wallet `address` declared by user. +- 6b: + - Obtain the wallet `public_key` via the wallet contract [get method](https://github.com/ton-blockchain/wallet-contract/blob/main/func/wallet-v4-code.fc#L174). + - If the contract is not active, or if it lacks the get_method found in older wallet versions (v1-v3), then obtaining the key in this manner will be impossible. Instead, you will need to parse the walletStateInit provided by the frontend. Ensure that TonAddressItemReply.walletStateInit.hash() is equal to TonAddressItemReply.address.hash(), indicating a BoC hash. + +7. Verify that the `signature` from the frontend actually signs the assembled message and corresponds to the `public_key` of the address. + +## React Example + +1. Add token provider to the root of your app: + +```tsx +function App() { + const [token, setToken] = useState(null); + + return ( + + { /* Your app */ } + + ) +} +``` + +2. Implement authentication on backend: + +
+Example + +```tsx +import {useContext, useEffect, useRef} from "react"; +import {BackendTokenContext} from "./BackendTokenContext"; +import {useIsConnectionRestored, useTonConnectUI, useTonWallet} from "@tonconnect/ui-react"; +import {backendAuth} from "./backend-auth"; + +const localStorageKey = 'my-dapp-auth-token'; +const payloadTTLMS = 1000 * 60 * 20; + +export function useBackendAuth() { + const { setToken } = useContext(BackendTokenContext); + const isConnectionRestored = useIsConnectionRestored(); + const wallet = useTonWallet(); + const [tonConnectUI] = useTonConnectUI(); + const interval = useRef | undefined>(); + + useEffect(() => { + if (!isConnectionRestored || !setToken) { + return; + } + + clearInterval(interval.current); + + if (!wallet) { + localStorage.removeItem(localStorageKey); + setToken(null); + + const refreshPayload = async () => { + tonConnectUI.setConnectRequestParameters({ state: 'loading' }); + + const value = await backendAuth.generatePayload(); + if (!value) { + tonConnectUI.setConnectRequestParameters(null); + } else { + tonConnectUI.setConnectRequestParameters({state: 'ready', value}); + } + } + + refreshPayload(); + setInterval(refreshPayload, payloadTTLMS); + return; + } + + const token = localStorage.getItem(localStorageKey); + if (token) { + setToken(token); + return; + } + + if (wallet.connectItems?.tonProof && !('error' in wallet.connectItems.tonProof)) { + backendAuth.checkProof(wallet.connectItems.tonProof.proof, wallet.account).then(result => { + if (result) { + setToken(result); + localStorage.setItem(localStorageKey, result); + } else { + alert('Please try another wallet'); + tonConnectUI.disconnect(); + } + }) + } else { + alert('Please try another wallet'); + tonConnectUI.disconnect(); + } + + }, [wallet, isConnectionRestored, setToken]) +} +``` + +
+ +## Concept Explanation + +If `TonProofItem` is requested, wallet proves ownership of the selected account’s key. The signed message is bound to: + +- Unique prefix to separate messages from on-chain messages. (`ton-connect`) +- Wallet address +- App domain +- Signing timestamp +- App’s custom payload (where server may put its nonce, cookie id, expiration time) + +``` +message = utf8_encode("ton-proof-item-v2/") ++ + Address ++ + AppDomain ++ + Timestamp ++ + Payload + +signature = Ed25519Sign(privkey, sha256(0xffff ++ utf8_encode("ton-connect") ++ sha256(message))) +``` + +where: + +- `Address` is the wallet address encoded as a sequence: +- `workchain`: 32-bit signed integer big endian; +- `hash`: 256-bit unsigned integer big endian; +- `AppDomain` is Length ++ EncodedDomainName + + + +- `Length` is 32-bit value of utf-8 encoded app domain name length in bytes +- `EncodedDomainName` id `Length`-byte utf-8 encoded app domain name + + + +- `Timestamp` 64-bit unix epoch time of the signing operation +- `Payload` is a variable-length binary string. + +Note: payload is variable-length untrusted data. We put it last to avoid using unnecessary length prefixes. + +The signature must be verified by public key: + +1. First, try to obtain public key via `get_public_key` get-method on smart contract deployed at `Address`. + +2. If the smart contract is not deployed yet, or the get-method is missing, then: + + 1. Parse `TonAddressItemReply.walletStateInit` and get public key from stateInit. You can compare the `walletStateInit.code` with the code of standard wallets contracts and parse the data according to the found wallet version. + + 2. Check that `TonAddressItemReply.publicKey` equals to obtained public key + + 3. Check that `TonAddressItemReply.walletStateInit.hash()` equals to `TonAddressItemReply.address`. `.hash()` means BoC hash. + +### Examples of TON Proof Verification + +- [GO demo app](https://github.com/ton-connect/demo-dapp-backend/blob/master/proof.go) +- [Rust demo app](https://github.com/liketurbo/demo-dapp-backend-rs) +- [TS example](https://gist.github.com/TrueCarry/cac00bfae051f7028085aa018c2a05c6) +- [Python example](https://github.com/XaBbl4/pytonconnect/blob/main/examples/check_proof.py) +- [PHP example](https://github.com/vladimirfokingithub/Ton-Connect-Proof-Php-Check) + +## See Also + +- [\[YouTube\] Check ton_proof for @tonconnect/react-ui \[RU\]](https://youtu.be/wIMbkJHv0Fs?list=PLyDBPwv9EPsCJ226xS5_dKmXXxWx1CKz_&t=2971) +- [Preparing Messages](/develop/dapps/ton-connect/message-builders) +- [Sending Messages](/develop/dapps/ton-connect/transactions) From d25311e494b9ef83a6f49c6afbf7eac4b65d1cab Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:58:59 +0800 Subject: [PATCH 064/219] New translations tg-bot-integration-py.md (Chinese Simplified) --- .../ton-connect/tg-bot-integration-py.md | 568 ++++++++++++++++++ 1 file changed, 568 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/tg-bot-integration-py.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/tg-bot-integration-py.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/tg-bot-integration-py.md new file mode 100644 index 0000000000..288ddea815 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/tg-bot-integration-py.md @@ -0,0 +1,568 @@ +import Button from '@site/src/components/button' + +# TON Connect for Telegram Bots - Python + +In this tutorial, we’ll create a sample telegram bot that supports TON Connect 2.0 authentication using Python TON Connect SDK [pytonconnect](https://github.com/XaBbl4/pytonconnect). +We will analyze connecting a wallet, sending a transaction, getting data about the connected wallet, and disconnecting a wallet. + +\ +\ + +## Preparing + +### Install libraries + +To make bot we are going to use `aiogram` 3.0 Python library. +To start integrating TON Connect into your Telegram bot, you need to install the `pytonconnect` package. +And to use TON primitives and parse user address we need `pytoniq-core`. +You can use pip for this purpose: + +```bash +pip install aiogram pytoniq-core python-dotenv +pip install pytonconnect +``` + +### Set up config + +Specify in `.env` file [bot token](https://t.me/BotFather) and link to the TON Connect [manifest file](https://github.com/ton-connect/sdk/tree/main/packages/sdk#add-the-tonconnect-manifest). After load them in `config.py`: + +```dotenv +# .env + +TOKEN='1111111111:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' # your bot token here +MANIFEST_URL='https://raw.githubusercontent.com/XaBbl4/pytonconnect/main/pytonconnect-manifest.json' +``` + +```python +# config.py + +from os import environ as env + +from dotenv import load_dotenv +load_dotenv() + +TOKEN = env['TOKEN'] +MANIFEST_URL = env['MANIFEST_URL'] +``` + +## Create simple bot + +Create `main.py` file which will contain the main bot code: + +```python +# main.py + +import sys +import logging +import asyncio + +import config + +from aiogram import Bot, Dispatcher, F +from aiogram.enums import ParseMode +from aiogram.filters import CommandStart, Command +from aiogram.types import Message, CallbackQuery + + +logger = logging.getLogger(__file__) + +dp = Dispatcher() +bot = Bot(config.TOKEN, parse_mode=ParseMode.HTML) + + +@dp.message(CommandStart()) +async def command_start_handler(message: Message): + await message.answer(text='Hi!') + +async def main() -> None: + await bot.delete_webhook(drop_pending_updates=True) # skip_updates = True + await dp.start_polling(bot) + + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO, stream=sys.stdout) + asyncio.run(main()) + +``` + +## Wallet connection + +### TON Connect Storage + +Let's create simple storage for TON Connect + +```python +# tc_storage.py + +from pytonconnect.storage import IStorage, DefaultStorage + + +storage = {} + + +class TcStorage(IStorage): + + def __init__(self, chat_id: int): + self.chat_id = chat_id + + def _get_key(self, key: str): + return str(self.chat_id) + key + + async def set_item(self, key: str, value: str): + storage[self._get_key(key)] = value + + async def get_item(self, key: str, default_value: str = None): + return storage.get(self._get_key(key), default_value) + + async def remove_item(self, key: str): + storage.pop(self._get_key(key)) + +``` + +### Connection handler + +Firstly, we need function which returns different instances for each user: + +```python +# connector.py + +from pytonconnect import TonConnect + +import config +from tc_storage import TcStorage + + +def get_connector(chat_id: int): + return TonConnect(config.MANIFEST_URL, storage=TcStorage(chat_id)) + +``` + +Secondary, let's add connection handler in `command_start_handler()`: + +```python +# main.py + +@dp.message(CommandStart()) +async def command_start_handler(message: Message): + chat_id = message.chat.id + connector = get_connector(chat_id) + connected = await connector.restore_connection() + + mk_b = InlineKeyboardBuilder() + if connected: + mk_b.button(text='Send Transaction', callback_data='send_tr') + mk_b.button(text='Disconnect', callback_data='disconnect') + await message.answer(text='You are already connected!', reply_markup=mk_b.as_markup()) + else: + wallets_list = TonConnect.get_wallets() + for wallet in wallets_list: + mk_b.button(text=wallet['name'], callback_data=f'connect:{wallet["name"]}') + mk_b.adjust(1, ) + await message.answer(text='Choose wallet to connect', reply_markup=mk_b.as_markup()) + +``` + +Now, for a user who has not yet connected a wallet, the bot sends a message with buttons for all available wallets. +So we need to write function to handle `connect:{wallet["name"]}` callbacks: + +```python +# main.py + +async def connect_wallet(message: Message, wallet_name: str): + connector = get_connector(message.chat.id) + + wallets_list = connector.get_wallets() + wallet = None + + for w in wallets_list: + if w['name'] == wallet_name: + wallet = w + + if wallet is None: + raise Exception(f'Unknown wallet: {wallet_name}') + + generated_url = await connector.connect(wallet) + + mk_b = InlineKeyboardBuilder() + mk_b.button(text='Connect', url=generated_url) + + await message.answer(text='Connect wallet within 3 minutes', reply_markup=mk_b.as_markup()) + + mk_b = InlineKeyboardBuilder() + mk_b.button(text='Start', callback_data='start') + + for i in range(1, 180): + await asyncio.sleep(1) + if connector.connected: + if connector.account.address: + wallet_address = connector.account.address + wallet_address = Address(wallet_address).to_str(is_bounceable=False) + await message.answer(f'You are connected with address {wallet_address}', reply_markup=mk_b.as_markup()) + logger.info(f'Connected with address: {wallet_address}') + return + + await message.answer(f'Timeout error!', reply_markup=mk_b.as_markup()) + + +@dp.callback_query(lambda call: True) +async def main_callback_handler(call: CallbackQuery): + await call.answer() + message = call.message + data = call.data + if data == "start": + await command_start_handler(message) + elif data == "send_tr": + await send_transaction(message) + elif data == 'disconnect': + await disconnect_wallet(message) + else: + data = data.split(':') + if data[0] == 'connect': + await connect_wallet(message, data[1]) +``` + +Bot gives user 3 minutes to connect a wallet, after which it reports a timeout error. + +## Implement Transaction requesting + +Let's take one of examples from the [Message builders](/develop/dapps/ton-connect/message-builders) article: + +```python +# messages.py + +from base64 import urlsafe_b64encode + +from pytoniq_core import begin_cell + + +def get_comment_message(destination_address: str, amount: int, comment: str) -> dict: + + data = { + 'address': destination_address, + 'amount': str(amount), + 'payload': urlsafe_b64encode( + begin_cell() + .store_uint(0, 32) # op code for comment message + .store_string(comment) # store comment + .end_cell() # end cell + .to_boc() # convert it to boc + ) + .decode() # encode it to urlsafe base64 + } + + return data + +``` + +And add `send_transaction()` function in the `main.py` file: + +```python +# main.py + +@dp.message(Command('transaction')) +async def send_transaction(message: Message): + connector = get_connector(message.chat.id) + connected = await connector.restore_connection() + if not connected: + await message.answer('Connect wallet first!') + return + + transaction = { + 'valid_until': int(time.time() + 3600), + 'messages': [ + get_comment_message( + destination_address='0:0000000000000000000000000000000000000000000000000000000000000000', + amount=int(0.01 * 10 ** 9), + comment='hello world!' + ) + ] + } + + await message.answer(text='Approve transaction in your wallet app!') + await connector.send_transaction( + transaction=transaction + ) +``` + +But we also should handle possible errors, so we wrap the `send_transaction` method into `try - except` statement: + +```python +@dp.message(Command('transaction')) +async def send_transaction(message: Message): + ... + await message.answer(text='Approve transaction in your wallet app!') + try: + await asyncio.wait_for(connector.send_transaction( + transaction=transaction + ), 300) + except asyncio.TimeoutError: + await message.answer(text='Timeout error!') + except pytonconnect.exceptions.UserRejectsError: + await message.answer(text='You rejected the transaction!') + except Exception as e: + await message.answer(text=f'Unknown error: {e}') +``` + +## Add disconnect handler + +This function implementation is simple enough: + +```python +async def disconnect_wallet(message: Message): + connector = get_connector(message.chat.id) + await connector.restore_connection() + await connector.disconnect() + await message.answer('You have been successfully disconnected!') +``` + +Currently, the project has the following structure: + +```bash +. +.env +├── config.py +├── connector.py +├── main.py +├── messages.py +└── tc_storage.py +``` + +And the `main.py` looks like this: + +
+Show main.py + +```python +# main.py + +import sys +import logging +import asyncio +import time + +import pytonconnect.exceptions +from pytoniq_core import Address +from pytonconnect import TonConnect + +import config +from messages import get_comment_message +from connector import get_connector + +from aiogram import Bot, Dispatcher, F +from aiogram.enums import ParseMode +from aiogram.filters import CommandStart, Command +from aiogram.types import Message, CallbackQuery +from aiogram.utils.keyboard import InlineKeyboardBuilder + + +logger = logging.getLogger(__file__) + +dp = Dispatcher() +bot = Bot(config.TOKEN, parse_mode=ParseMode.HTML) + + +@dp.message(CommandStart()) +async def command_start_handler(message: Message): + chat_id = message.chat.id + connector = get_connector(chat_id) + connected = await connector.restore_connection() + + mk_b = InlineKeyboardBuilder() + if connected: + mk_b.button(text='Send Transaction', callback_data='send_tr') + mk_b.button(text='Disconnect', callback_data='disconnect') + await message.answer(text='You are already connected!', reply_markup=mk_b.as_markup()) + + else: + wallets_list = TonConnect.get_wallets() + for wallet in wallets_list: + mk_b.button(text=wallet['name'], callback_data=f'connect:{wallet["name"]}') + mk_b.adjust(1, ) + await message.answer(text='Choose wallet to connect', reply_markup=mk_b.as_markup()) + + +@dp.message(Command('transaction')) +async def send_transaction(message: Message): + connector = get_connector(message.chat.id) + connected = await connector.restore_connection() + if not connected: + await message.answer('Connect wallet first!') + return + + transaction = { + 'valid_until': int(time.time() + 3600), + 'messages': [ + get_comment_message( + destination_address='0:0000000000000000000000000000000000000000000000000000000000000000', + amount=int(0.01 * 10 ** 9), + comment='hello world!' + ) + ] + } + + await message.answer(text='Approve transaction in your wallet app!') + try: + await asyncio.wait_for(connector.send_transaction( + transaction=transaction + ), 300) + except asyncio.TimeoutError: + await message.answer(text='Timeout error!') + except pytonconnect.exceptions.UserRejectsError: + await message.answer(text='You rejected the transaction!') + except Exception as e: + await message.answer(text=f'Unknown error: {e}') + + +async def connect_wallet(message: Message, wallet_name: str): + connector = get_connector(message.chat.id) + + wallets_list = connector.get_wallets() + wallet = None + + for w in wallets_list: + if w['name'] == wallet_name: + wallet = w + + if wallet is None: + raise Exception(f'Unknown wallet: {wallet_name}') + + generated_url = await connector.connect(wallet) + + mk_b = InlineKeyboardBuilder() + mk_b.button(text='Connect', url=generated_url) + + await message.answer(text='Connect wallet within 3 minutes', reply_markup=mk_b.as_markup()) + + mk_b = InlineKeyboardBuilder() + mk_b.button(text='Start', callback_data='start') + + for i in range(1, 180): + await asyncio.sleep(1) + if connector.connected: + if connector.account.address: + wallet_address = connector.account.address + wallet_address = Address(wallet_address).to_str(is_bounceable=False) + await message.answer(f'You are connected with address {wallet_address}', reply_markup=mk_b.as_markup()) + logger.info(f'Connected with address: {wallet_address}') + return + + await message.answer(f'Timeout error!', reply_markup=mk_b.as_markup()) + + +async def disconnect_wallet(message: Message): + connector = get_connector(message.chat.id) + await connector.restore_connection() + await connector.disconnect() + await message.answer('You have been successfully disconnected!') + + +@dp.callback_query(lambda call: True) +async def main_callback_handler(call: CallbackQuery): + await call.answer() + message = call.message + data = call.data + if data == "start": + await command_start_handler(message) + elif data == "send_tr": + await send_transaction(message) + elif data == 'disconnect': + await disconnect_wallet(message) + else: + data = data.split(':') + if data[0] == 'connect': + await connect_wallet(message, data[1]) + + +async def main() -> None: + await bot.delete_webhook(drop_pending_updates=True) # skip_updates = True + await dp.start_polling(bot) + + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO, stream=sys.stdout) + asyncio.run(main()) + +``` + +
+ +## Improving + +### Add permanent storage - Redis + +Currently, our TON Connect Storage uses dict which causes to lost sessions after bot restart. +Let's add permanent database storage with Redis: + +After you launched Redis database install python library to interact with it: + +```bash +pip install redis +``` + +And update `TcStorage` class in `tc_storage.py`: + +```python +import redis.asyncio as redis + +client = redis.Redis(host='localhost', port=6379) + + +class TcStorage(IStorage): + + def __init__(self, chat_id: int): + self.chat_id = chat_id + + def _get_key(self, key: str): + return str(self.chat_id) + key + + async def set_item(self, key: str, value: str): + await client.set(name=self._get_key(key), value=value) + + async def get_item(self, key: str, default_value: str = None): + value = await client.get(name=self._get_key(key)) + return value.decode() if value else default_value + + async def remove_item(self, key: str): + await client.delete(self._get_key(key)) +``` + +### Add QR Code + +Install python `qrcode` package to generate them: + +```bash +pip install qrcode +``` + +Change `connect_wallet()` function so it generates qrcode and sends it as a photo to the user: + +```python +from io import BytesIO +import qrcode +from aiogram.types import BufferedInputFile + + +async def connect_wallet(message: Message, wallet_name: str): + ... + + img = qrcode.make(generated_url) + stream = BytesIO() + img.save(stream) + file = BufferedInputFile(file=stream.getvalue(), filename='qrcode') + + await message.answer_photo(photo=file, caption='Connect wallet within 3 minutes', reply_markup=mk_b.as_markup()) + + ... +``` + +## Summary + +What is next? + +- You can add better errors handling in the bot. +- You can add start text and something like `/connect_wallet` command. + +## See Also + +- [Full bot code](https://github.com/yungwine/ton-connect-bot) +- [Preparing messages](/develop/dapps/ton-connect/message-builders) From 7b7acdedf52e34a992dc285af9e7b96b0b45eecf Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:00 +0800 Subject: [PATCH 065/219] New translations tg-bot-integration.mdx (Chinese Simplified) --- .../dapps/ton-connect/tg-bot-integration.mdx | 2052 +++++++++++++++++ 1 file changed, 2052 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/tg-bot-integration.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/tg-bot-integration.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/tg-bot-integration.mdx new file mode 100644 index 0000000000..f5c30620ed --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/tg-bot-integration.mdx @@ -0,0 +1,2052 @@ +import Button from '@site/src/components/button' + +# TON Connect for Telegram Bots + +In this tutorial, we’ll create a sample telegram bot that supports TON Connect 2.0 authentication using Javascript TON Connect SDK. +We will analyze connecting a wallet, sending a transaction, getting data about the connected wallet, and disconnecting a wallet. + + + + +## Documentation links + +- [TON Connect SDK documentation](https://www.npmjs.com/package/@tonconnect/sdk) + +## Prerequisites + +- You need to create a telegram bot using [@BotFather](https://t.me/BotFather) and save its token. +- Node JS should be installed (we use version 18.1.0 in this tutorial). +- Docker should be installed. + +## Creating project + +### Set up dependencies + +Firstly we have to create a Node JS project. We will use TypeScript and [node-telegram-bot-api](https://www.npmjs.com/package/node-telegram-bot-api) library (you can choose any other suitable for you library as well). Also, we will use [qrcode](https://www.npmjs.com/package/qrcode) library for QR codes generation, but you can replace it with any other same library. + +Let's create a directory `ton-connect-bot`. Add the following package.json file there: + +```json +{ + "name": "ton-connect-bot", + "version": "1.0.0", + "scripts": { + "compile": "npx rimraf dist && tsc", + "run": "node ./dist/main.js" + }, + "dependencies": { + "@tonconnect/sdk": "^3.0.0-beta.1", + "dotenv": "^16.0.3", + "node-telegram-bot-api": "^0.61.0", + "qrcode": "^1.5.1" + }, + "devDependencies": { + "@types/node-telegram-bot-api": "^0.61.4", + "@types/qrcode": "^1.5.0", + "rimraf": "^3.0.2", + "typescript": "^4.9.5" + } +} +``` + +Run `npm i` to install dependencies. + +### Add a tsconfig.json + +Create a `tsconfig.json`: + +
+tsconfig.json code + +```json +{ + "compilerOptions": { + "declaration": true, + "lib": ["ESNext", "dom"], + "resolveJsonModule": true, + "experimentalDecorators": false, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "target": "es6", + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictPropertyInitialization": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": true, + "useUnknownInCatchVariables": false, + "noUncheckedIndexedAccess": true, + "emitDecoratorMetadata": false, + "importHelpers": false, + "skipLibCheck": true, + "skipDefaultLibCheck": true, + "allowJs": true, + "outDir": "./dist" + }, + "include": ["src"], + "exclude": [ + "./tests","node_modules", "lib", "types"] +} +``` + +
+ +[Read more about tsconfig.json](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) + +### Add simple bot code + +Create a `.env` file and add your bot token, DAppmanifest and wallets list cache time to live there: + +[See more about tonconnect-manifes.json](https://github.com/ton-connect/sdk/tree/main/packages/sdk#add-the-tonconnect-manifest) + +```dotenv +# .env +TELEGRAM_BOT_TOKEN= +TELEGRAM_BOT_LINK= +MANIFEST_URL=https://raw.githubusercontent.com/ton-connect/demo-telegram-bot/master/tonconnect-manifest.json +WALLETS_LIST_CAHCE_TTL_MS=86400000 +``` + +Create directory `src` and file `bot.ts` inside. Let's create a TelegramBot instance there: + +```ts +// src/bot.ts + +import TelegramBot from 'node-telegram-bot-api'; +import * as process from 'process'; + +const token = process.env.TELEGRAM_BOT_TOKEN!; + +export const bot = new TelegramBot(token, { polling: true }); +``` + +Now we can create an entrypoint file `main.ts` inside the `src` directory: + +```ts +// src/main.ts +import dotenv from 'dotenv'; +dotenv.config(); + +import { bot } from './bot'; + +bot.on('message', msg => { + const chatId = msg.chat.id; + + bot.sendMessage(chatId, 'Received your message'); +}); +``` + +Here we go. You can run `npm run compile` and `npm run start`, and send any message to your bot. Bot will reply "Received your message". We are ready for the TonConnect integration. + +At the moment we have the following files structure: + +```text +ton-connect-bot +├── src +│ ├── bot.ts +│ └── main.ts +├── package.json +├── package-lock.json +├── .env +└── tsconfig.json +``` + +## Connecting a wallet + +We've already installed the `@tonconnect/sdk`, so we can just import it to get started. + +We will start with getting wallets list. We need only http-bridge-compatible wallets. Create a folder `ton-connect` into `src` and add `wallets.ts` file there: +We also define function `getWalletInfo` that queries detailed wallet info by its `appName`. +The difference between `name` and `appName` is that `name` is a human-readable label of the wallet, and `appName` is the wallet's uniq identifier. + +```ts +// src/ton-connect/wallets.ts + +import { isWalletInfoRemote, WalletInfoRemote, WalletsListManager } from '@tonconnect/sdk'; + +const walletsListManager = new WalletsListManager({ + cacheTTLMs: Number(process.env.WALLETS_LIST_CAHCE_TTL_MS) +}); + +export async function getWallets(): Promise { + const wallets = await walletsListManager.getWallets(); + return wallets.filter(isWalletInfoRemote); +} + +export async function getWalletInfo(walletAppName: string): Promise { + const wallets = await getWallets(); + return wallets.find(wallet => wallet.appName.toLowerCase() === walletAppName.toLowerCase()); +} +``` + +Now we need to define a TonConnect storage. TonConnect uses `localStorage` to save connection details when running in the browser, however there is no `localStorage` in NodeJS environment. That's why we should add a custom simple storage implementation. + +[See details about TonConnect storage](https://github.com/ton-connect/sdk/tree/main/packages/sdk#init-connector) + +Create `storage.ts` inside `ton-connect` directory: + +```ts +// src/ton-connect/storage.ts + +import { IStorage } from '@tonconnect/sdk'; + +const storage = new Map(); // temporary storage implementation. We will replace it with the redis later + +export class TonConnectStorage implements IStorage { + constructor(private readonly chatId: number) {} // we need to have different stores for different users + + private getKey(key: string): string { + return this.chatId.toString() + key; // we will simply have different keys prefixes for different users + } + + async removeItem(key: string): Promise { + storage.delete(this.getKey(key)); + } + + async setItem(key: string, value: string): Promise { + storage.set(this.getKey(key), value); + } + + async getItem(key: string): Promise { + return storage.get(this.getKey(key)) || null; + } +} +``` + +We are moving on implementing a wallet connection. +Modify `src/main.ts` and add `connect` command. We are going to implement a wallet connection in this command handler. + +```ts +import dotenv from 'dotenv'; +dotenv.config(); + +import { bot } from './bot'; +import { getWallets } from './ton-connect/wallets'; +import TonConnect from '@tonconnect/sdk'; +import { TonConnectStorage } from './ton-connect/storage'; +import QRCode from 'qrcode'; + +bot.onText(/\/connect/, async msg => { + const chatId = msg.chat.id; + const wallets = await getWallets(); + + const connector = new TonConnect({ + storage: new TonConnectStorage(chatId), + manifestUrl: process.env.MANIFEST_URL + }); + + connector.onStatusChange(wallet => { + if (wallet) { + bot.sendMessage(chatId, `${wallet.device.appName} wallet connected!`); + } + }); + + const tonkeeper = wallets.find(wallet => wallet.appName === 'tonkeeper')!; + + const link = connector.connect({ + bridgeUrl: tonkeeper.bridgeUrl, + universalLink: tonkeeper.universalLink + }); + const image = await QRCode.toBuffer(link); + + await bot.sendPhoto(chatId, image); +}); +``` + +Let's analyze what we are doing here. Firstly we fetch the wallets list and create a TonConnect instance. +After that we subscribe to wallet change. When user connects a wallet, bot will send a message `${wallet.device.appName} wallet connected!`. +Next we find the Tonkeeper wallet and create connection link. In the end we generate a QR code with the link and send it as a photo to the user. + +Now you can run the bot (`npm run compile` and `npm run start` then) and send `/connect` message to the bot. Bot should reply with the QR. Scan it with the Tonkeeper wallet. You will see a message `Tonkeeper wallet connected!` in the chat. + +We will use connector in many places, so let's move connector creating code to a separate file: + +```ts +// src/ton-connect/conenctor.ts + +import TonConnect from '@tonconnect/sdk'; +import { TonConnectStorage } from './storage'; +import * as process from 'process'; + +export function getConnector(chatId: number): TonConnect { + return new TonConnect({ + manifestUrl: process.env.MANIFEST_URL, + storage: new TonConnectStorage(chatId) + }); +} +``` + +And import it in the `src/main.ts` + +```ts +// src/main.ts + +import dotenv from 'dotenv'; +dotenv.config(); + +import { bot } from './bot'; +import { getWallets } from './ton-connect/wallets'; +import QRCode from 'qrcode'; +import { getConnector } from './ton-connect/connector'; + +bot.onText(/\/connect/, async msg => { + const chatId = msg.chat.id; + const wallets = await getWallets(); + + const connector = getConnector(chatId); + + connector.onStatusChange(wallet => { + if (wallet) { + bot.sendMessage(chatId, `${wallet.device.appName} wallet connected!`); + } + }); + + const tonkeeper = wallets.find(wallet => wallet.appName === 'tonkeeper')!; + + const link = connector.connect({ + bridgeUrl: tonkeeper.bridgeUrl, + universalLink: tonkeeper.universalLink + }); + const image = await QRCode.toBuffer(link); + + await bot.sendPhoto(chatId, image); +}); +``` + +At the moment we have the following files structure: + +```text +bot-demo +├── src +│ ├── ton-connect +│ │ ├── connector.ts +│ │ ├── wallets.ts +│ │ └── storage.ts +│ ├── bot.ts +│ └── main.ts +├── package.json +├── package-lock.json +├── .env +└── tsconfig.json +``` + +## Creating connect wallet menu + +### Add inline keyboard + +We've done the Tonkeeper wallet connection. But we didn't implement connection via universal QR code for all wallets, and didn't allow the user to choose suitable wallet. Let's cover it now. + +For better UX we are going to use `callback_query` and `inline_keyboard` Telegram features. If you don't fill familiar with that, you can read more about it [here](https://core.telegram.org/bots/api#callbackquery). + +We will implement following UX for wallet connection: + +```text +First screen: + +, , + +Second screen: + + +<@wallet button (opens third screen)>, , , <...> + +Third screen: + + + +``` + +Let's start with adding inline keyboard to the `/connect` command handler in the `main.ts` + +```ts +// src/main.ts +bot.onText(/\/connect/, async msg => { + const chatId = msg.chat.id; + const wallets = await getWallets(); + + const connector = getConnector(chatId); + + connector.onStatusChange(async wallet => { + if (wallet) { + const walletName = + (await getWalletInfo(wallet.device.appName))?.name || wallet.device.appName; + bot.sendMessage(chatId, `${walletName} wallet connected!`); + } + }); + + const link = connector.connect(wallets); + const image = await QRCode.toBuffer(link); + + await bot.sendPhoto(chatId, image, { + reply_markup: { + inline_keyboard: [ + [ + { + text: 'Choose a Wallet', + callback_data: JSON.stringify({ method: 'chose_wallet' }) + }, + { + text: 'Open Link', + url: `https://ton-connect.github.io/open-tc?connect=${encodeURIComponent( + link + )}` + } + ] + ] + } + }); +}); +``` + +We need to wrap TonConnect deeplink as https://ton-connect.github.io/open-tc?connect=${encodeURIComponent(link)} because only `http` links are allowed in the Telegram inline keyboard. +Website https://ton-connect.github.io/open-tc just redirects user to link passed in the `connect` query param, so it's only workaround to open `tc://` link in the Telegram. + +Note that we replaced `connector.connect` call arguments. Now we are generating a unified link for all wallets. + +Next we tell Telegram to call `callback_query` handler with `{ "method": "chose_wallet" }` value when user clicks to the `Choose a Wallet` button. + +### Add Choose a Wallet button handler + +Create a file `src/connect-wallet-menu.ts`. + +Let's add 'Choose a Wallet' button click handler there: + +```ts +// src/connect-wallet-menu.ts + +async function onChooseWalletClick(query: CallbackQuery, _: string): Promise { + const wallets = await getWallets(); + + await bot.editMessageReplyMarkup( + { + inline_keyboard: [ + wallets.map(wallet => ({ + text: wallet.name, + callback_data: JSON.stringify({ method: 'select_wallet', data: wallet.appName }) + })), + [ + { + text: '« Back', + callback_data: JSON.stringify({ + method: 'universal_qr' + }) + } + ] + ] + }, + { + message_id: query.message!.message_id, + chat_id: query.message!.chat.id + } + ); +} +``` + +Here we are replacing the message inline keyboard with a new one that contains clickable list of wallets and 'Back' button. + +Now we will add global `callback_query` handler and register `onChooseWalletClick` there: + +```ts +// src/connect-wallet-menu.ts +import { CallbackQuery } from 'node-telegram-bot-api'; +import { getWallets } from './ton-connect/wallets'; +import { bot } from './bot'; + +export const walletMenuCallbacks = { // Define buttons callbacks + chose_wallet: onChooseWalletClick +}; + +bot.on('callback_query', query => { // Parse callback data and execute corresponing function + if (!query.data) { + return; + } + + let request: { method: string; data: string }; + + try { + request = JSON.parse(query.data); + } catch { + return; + } + + if (!walletMenuCallbacks[request.method as keyof typeof walletMenuCallbacks]) { + return; + } + + walletMenuCallbacks[request.method as keyof typeof walletMenuCallbacks](query, request.data); +}); + +// ... other code from the previous ster +async function onChooseWalletClick ... +``` + +Here we define buttons handlers list and `callback_query` parser. Unfortunately callback data is always string, so we have to pass JSON to the `callback_data` and parse it later in the `callback_query` handler. +Then we are looking for the requested method and call it with passed parameters. + +Now we should add `conenct-wallet-menu.ts` import to the `main.ts` + +```ts +// src/main.ts + +// ... other imports + +import './connect-wallet-menu'; + +// ... other code +``` + +Compile and run the bot. You can click to the Choose a wallet button and bot will replace inline keyboard buttons! + +### Add other buttons handlers + +Let's complete this menu and add rest commands handlers. + +Firstly we will create a utility function `editQR`. Editing message media (QR image) is a bit tricky. We need to store image to the file and send it to the Telegram server. Then we can remove this file. + +```ts +// src/connect-wallet-menu.ts + +// ... other code + + +async function editQR(message: TelegramBot.Message, link: string): Promise { + const fileName = 'QR-code-' + Math.round(Math.random() * 10000000000); + + await QRCode.toFile(`./${fileName}`, link); + + await bot.editMessageMedia( + { + type: 'photo', + media: `attach://${fileName}` + }, + { + message_id: message?.message_id, + chat_id: message?.chat.id + } + ); + + await new Promise(r => fs.rm(`./${fileName}`, r)); +} +``` + +In `onOpenUniversalQRClick` handler we just regenerate a QR and deeplink and modify the message: + +```ts +// src/connect-wallet-menu.ts + +// ... other code + +async function onOpenUniversalQRClick(query: CallbackQuery, _: string): Promise { + const chatId = query.message!.chat.id; + const wallets = await getWallets(); + + const connector = getConnector(chatId); + + connector.onStatusChange(wallet => { + if (wallet) { + bot.sendMessage(chatId, `${wallet.device.appName} wallet connected!`); + } + }); + + const link = connector.connect(wallets); + + await editQR(query.message!, link); + + await bot.editMessageReplyMarkup( + { + inline_keyboard: [ + [ + { + text: 'Choose a Wallet', + callback_data: JSON.stringify({ method: 'chose_wallet' }) + }, + { + text: 'Open Link', + url: `https://ton-connect.github.io/open-tc?connect=${encodeURIComponent( + link + )}` + } + ] + ] + }, + { + message_id: query.message?.message_id, + chat_id: query.message?.chat.id + } + ); +} + +// ... other code +``` + +In `onWalletClick` handler we are creating special QR and universal link for selected wallet only, and modify the message. + +```ts +// src/connect-wallet-menu.ts + +// ... other code + +async function onWalletClick(query: CallbackQuery, data: string): Promise { + const chatId = query.message!.chat.id; + const connector = getConnector(chatId); + + connector.onStatusChange(wallet => { + if (wallet) { + bot.sendMessage(chatId, `${wallet.device.appName} wallet connected!`); + } + }); + + const selectedWallet = await getWalletInfo(data); + if (!selectedWallet) { + return; + } + + const link = connector.connect({ + bridgeUrl: selectedWallet.bridgeUrl, + universalLink: selectedWallet.universalLink + }); + + await editQR(query.message!, link); + + await bot.editMessageReplyMarkup( + { + inline_keyboard: [ + [ + { + text: '« Back', + callback_data: JSON.stringify({ method: 'chose_wallet' }) + }, + { + text: `Open ${selectedWallet.name}`, + url: link + } + ] + ] + }, + { + message_id: query.message?.message_id, + chat_id: chatId + } + ); +} + +// ... other code +``` + +Now we have to register this functions as callbacks (`walletMenuCallbacks`): + +```ts +// src/connect-wallet-menu.ts +import TelegramBot, { CallbackQuery } from 'node-telegram-bot-api'; +import { getWallets } from './ton-connect/wallets'; +import { bot } from './bot'; +import * as fs from 'fs'; +import { getConnector } from './ton-connect/connector'; +import QRCode from 'qrcode'; + +export const walletMenuCallbacks = { + chose_wallet: onChooseWalletClick, + select_wallet: onWalletClick, + universal_qr: onOpenUniversalQRClick +}; + +// ... other code +``` + +
+Currently src/connect-wallet-menu.ts looks like that + +```ts +// src/connect-wallet-menu.ts + +import TelegramBot, { CallbackQuery } from 'node-telegram-bot-api'; +import { getWallets, getWalletInfo } from './ton-connect/wallets'; +import { bot } from './bot'; +import { getConnector } from './ton-connect/connector'; +import QRCode from 'qrcode'; +import * as fs from 'fs'; + +export const walletMenuCallbacks = { + chose_wallet: onChooseWalletClick, + select_wallet: onWalletClick, + universal_qr: onOpenUniversalQRClick +}; + +bot.on('callback_query', query => { // Parse callback data and execute corresponing function + if (!query.data) { + return; + } + + let request: { method: string; data: string }; + + try { + request = JSON.parse(query.data); + } catch { + return; + } + + if (!callbacks[request.method as keyof typeof callbacks]) { + return; + } + + callbacks[request.method as keyof typeof callbacks](query, request.data); +}); + + +async function onChooseWalletClick(query: CallbackQuery, _: string): Promise { + const wallets = await getWallets(); + + await bot.editMessageReplyMarkup( + { + inline_keyboard: [ + wallets.map(wallet => ({ + text: wallet.name, + callback_data: JSON.stringify({ method: 'select_wallet', data: wallet.appName }) + })), + [ + { + text: '« Back', + callback_data: JSON.stringify({ + method: 'universal_qr' + }) + } + ] + ] + }, + { + message_id: query.message!.message_id, + chat_id: query.message!.chat.id + } + ); +} + +async function onOpenUniversalQRClick(query: CallbackQuery, _: string): Promise { + const chatId = query.message!.chat.id; + const wallets = await getWallets(); + + const connector = getConnector(chatId); + + connector.onStatusChange(wallet => { + if (wallet) { + bot.sendMessage(chatId, `${wallet.device.appName} wallet connected!`); + } + }); + + const link = connector.connect(wallets); + + await editQR(query.message!, link); + + await bot.editMessageReplyMarkup( + { + inline_keyboard: [ + [ + { + text: 'Choose a Wallet', + callback_data: JSON.stringify({ method: 'chose_wallet' }) + }, + { + text: 'Open Link', + url: `https://ton-connect.github.io/open-tc?connect=${encodeURIComponent( + link + )}` + } + ] + ] + }, + { + message_id: query.message?.message_id, + chat_id: query.message?.chat.id + } + ); +} + +async function onWalletClick(query: CallbackQuery, data: string): Promise { + const chatId = query.message!.chat.id; + const connector = getConnector(chatId); + + connector.onStatusChange(wallet => { + if (wallet) { + bot.sendMessage(chatId, `${wallet.device.appName} wallet connected!`); + } + }); + + const selectedWallet = await getWalletInfo(data); + if (!selectedWallet) { + return; + } + + const link = connector.connect({ + bridgeUrl: selectedWallet.bridgeUrl, + universalLink: selectedWallet.universalLink + }); + + await editQR(query.message!, link); + + await bot.editMessageReplyMarkup( + { + inline_keyboard: [ + [ + { + text: '« Back', + callback_data: JSON.stringify({ method: 'chose_wallet' }) + }, + { + text: `Open ${selectedWallet.name}`, + url: link + } + ] + ] + }, + { + message_id: query.message?.message_id, + chat_id: chatId + } + ); +} + +async function editQR(message: TelegramBot.Message, link: string): Promise { + const fileName = 'QR-code-' + Math.round(Math.random() * 10000000000); + + await QRCode.toFile(`./${fileName}`, link); + + await bot.editMessageMedia( + { + type: 'photo', + media: `attach://${fileName}` + }, + { + message_id: message?.message_id, + chat_id: message?.chat.id + } + ); + + await new Promise(r => fs.rm(`./${fileName}`, r)); +} +``` + +
+ +Compile and run the bot to check how wallet connection works now. + +You may note that we haven't considered QR code expiration and stopping connectors yet. We will handle it later. + +At the moment we have the following files structure: + +```text +bot-demo +├── src +│ ├── ton-connect +│ │ ├── connector.ts +│ │ ├── wallets.ts +│ │ └── storage.ts +│ ├── bot.ts +│ ├── connect-wallet-menu.ts +│ └── main.ts +├── package.json +├── package-lock.json +├── .env +└── tsconfig.json +``` + +## Implement transaction sending + +Before write new code that sends a transaction, let's clean up the code. We're going to create a new file for bot commands handlers ('/connect', '/send_tx', ...) + +```ts +// src/commands-handlers.ts + +import { bot } from './bot'; +import { getWallets } from './ton-connect/wallets'; +import QRCode from 'qrcode'; +import TelegramBot from 'node-telegram-bot-api'; +import { getConnector } from './ton-connect/connector'; + +export async function handleConnectCommand(msg: TelegramBot.Message): Promise { + const chatId = msg.chat.id; + const wallets = await getWallets(); + + const connector = getConnector(chatId); + + connector.onStatusChange(wallet => { + if (wallet) { + bot.sendMessage(chatId, `${wallet.device.appName} wallet connected!`); + } + }); + + const link = connector.connect(wallets); + const image = await QRCode.toBuffer(link); + + await bot.sendPhoto(chatId, image, { + reply_markup: { + inline_keyboard: [ + [ + { + text: 'Choose a Wallet', + callback_data: JSON.stringify({ method: 'chose_wallet' }) + }, + { + text: 'Open Link', + url: `https://ton-connect.github.io/open-tc?connect=${encodeURIComponent( + link + )}` + } + ] + ] + } + }); +} +``` + +Let's import that in the `main.ts` and also move `callback_query` entrypoint from `connect-wallet-menu.ts` to the `main.ts`: + +```ts +// src/main.ts + +import dotenv from 'dotenv'; +dotenv.config(); + +import { bot } from './bot'; +import './connect-wallet-menu'; +import { handleConnectCommand } from './commands-handlers'; +import { walletMenuCallbacks } from './connect-wallet-menu'; + +const callbacks = { + ...walletMenuCallbacks +}; + +bot.on('callback_query', query => { + if (!query.data) { + return; + } + + let request: { method: string; data: string }; + + try { + request = JSON.parse(query.data); + } catch { + return; + } + + if (!callbacks[request.method as keyof typeof callbacks]) { + return; + } + + callbacks[request.method as keyof typeof callbacks](query, request.data); +}); + +bot.onText(/\/connect/, handleConnectCommand); +``` + +```ts +// src/connect-wallet-menu.ts + +// ... imports + + +export const walletMenuCallbacks = { + chose_wallet: onChooseWalletClick, + select_wallet: onWalletClick, + universal_qr: onOpenUniversalQRClick +}; + +async function onChooseWalletClick(query: CallbackQuery, _: string): Promise { + +// ... other code +``` + +Now we can add `send_tx` command handler: + +```ts +// src/commands-handlers.ts + +// ... other code + +export async function handleSendTXCommand(msg: TelegramBot.Message): Promise { + const chatId = msg.chat.id; + + const connector = getConnector(chatId); + + await connector.restoreConnection(); + if (!connector.connected) { + await bot.sendMessage(chatId, 'Connect wallet to send transaction'); + return; + } + + connector + .sendTransaction({ + validUntil: Math.round(Date.now() / 1000) + 600, // timeout is SECONDS + messages: [ + { + amount: '1000000', + address: '0:0000000000000000000000000000000000000000000000000000000000000000' + } + ] + }) + .then(() => { + bot.sendMessage(chatId, `Transaction sent successfully`); + }) + .catch(e => { + if (e instanceof UserRejectsError) { + bot.sendMessage(chatId, `You rejected the transaction`); + return; + } + + bot.sendMessage(chatId, `Unknown error happened`); + }) + .finally(() => connector.pauseConnection()); + + let deeplink = ''; + const walletInfo = await getWalletInfo(connector.wallet!.device.appName); + if (walletInfo) { + deeplink = walletInfo.universalLink; + } + + await bot.sendMessage( + chatId, + `Open ${walletInfo?.name || connector.wallet!.device.appName} and confirm transaction`, + { + reply_markup: { + inline_keyboard: [ + [ + { + text: 'Open Wallet', + url: deeplink + } + ] + ] + } + } + ); +} +``` + +Here we check if user's wallet is connected and process sending transaction. +Then we send a message to the user with a button that opens user's wallet (wallet universal link without additional parameters). +Note that this button contains an empty deeplink. It means that send transaction request data goes through the http-bridge, and transaction will appear it the user's wallet even if he just open the wallet app without clicking to this button. + +Let's register this handler: + +```ts +// src/main.ts + +// ... other code + +bot.onText(/\/connect/, handleConnectCommand); + +bot.onText(/\/send_tx/, handleSendTXCommand); +``` + +Compile and run the bot to check that transaction sending works correctly. + +At the moment we have the following files structure: + +```text +bot-demo +├── src +│ ├── ton-connect +│ │ ├── connector.ts +│ │ ├── wallets.ts +│ │ └── storage.ts +│ ├── bot.ts +│ ├── connect-wallet-menu.ts +│ ├── commands-handlers.ts +│ └── main.ts +├── package.json +├── package-lock.json +├── .env +└── tsconfig.json +``` + +## Add disconnect and show connected wallet commands + +This commands implementation is simple enough: + +```ts +// src/commands-handlers.ts + +// ... other code + +export async function handleDisconnectCommand(msg: TelegramBot.Message): Promise { + const chatId = msg.chat.id; + + const connector = getConnector(chatId); + + await connector.restoreConnection(); + if (!connector.connected) { + await bot.sendMessage(chatId, "You didn't connect a wallet"); + return; + } + + await connector.disconnect(); + + await bot.sendMessage(chatId, 'Wallet has been disconnected'); +} + +export async function handleShowMyWalletCommand(msg: TelegramBot.Message): Promise { + const chatId = msg.chat.id; + + const connector = getConnector(chatId); + + await connector.restoreConnection(); + if (!connector.connected) { + await bot.sendMessage(chatId, "You didn't connect a wallet"); + return; + } + + const walletName = + (await getWalletInfo(connector.wallet!.device.appName))?.name || + connector.wallet!.device.appName; + + + await bot.sendMessage( + chatId, + `Connected wallet: ${walletName}\nYour address: ${toUserFriendlyAddress( + connector.wallet!.account.address, + connector.wallet!.account.chain === CHAIN.TESTNET + )}` + ); +} +``` + +And register this commands: + +```ts +// src/main.ts + +// ... other code + +bot.onText(/\/connect/, handleConnectCommand); +bot.onText(/\/send_tx/, handleSendTXCommand); +bot.onText(/\/disconnect/, handleDisconnectCommand); +bot.onText(/\/my_wallet/, handleShowMyWalletCommand); +``` + +Compile and run the bot to check that commands above works correctly. + +## Optimisation + +We've done all basic commands. But it is important to keep in mind that each connector keeps SSE connection opened until it is paused. +Also, we didn't handle case when user calls `/connect` multiple times, or calls `/connect` or `/send_tx` and doesn't scan the QR. We should set a timeout and close the connection to save server resources. +Then we should notify user that QR / transaction request is expired. + +### Send transaction optimisation + +Let's create a utility function that wraps a promise and rejects it after the specified timeout: + +```ts +// src/utils.ts + +export const pTimeoutException = Symbol(); + +export function pTimeout( + promise: Promise, + time: number, + exception: unknown = pTimeoutException +): Promise { + let timer: ReturnType; + return Promise.race([ + promise, + new Promise((_r, rej) => (timer = setTimeout(rej, time, exception))) + ]).finally(() => clearTimeout(timer)) as Promise; +} +``` + +You can use this code or pick a library you like. + +Let's add a timeout parameter value to the `.env` + +```dotenv +# .env +TELEGRAM_BOT_TOKEN= +MANIFEST_URL=https://raw.githubusercontent.com/ton-connect/demo-telegram-bot/master/tonconnect-manifest.json +WALLETS_LIST_CAHCE_TTL_MS=86400000 +DELETE_SEND_TX_MESSAGE_TIMEOUT_MS=600000 +``` + +Now we are going to improve `handleSendTXCommand` function and wrap tx sending to the `pTimeout` + +```ts +// src/commands-handlers.ts + +// export async function handleSendTXCommand(msg: TelegramBot.Message): Promise { ... + +pTimeout( + connector.sendTransaction({ + validUntil: Math.round( + (Date.now() + Number(process.env.DELETE_SEND_TX_MESSAGE_TIMEOUT_MS)) / 1000 + ), + messages: [ + { + amount: '1000000', + address: '0:0000000000000000000000000000000000000000000000000000000000000000' + } + ] + }), + Number(process.env.DELETE_SEND_TX_MESSAGE_TIMEOUT_MS) +) + .then(() => { + bot.sendMessage(chatId, `Transaction sent successfully`); + }) + .catch(e => { + if (e === pTimeoutException) { + bot.sendMessage(chatId, `Transaction was not confirmed`); + return; + } + + if (e instanceof UserRejectsError) { + bot.sendMessage(chatId, `You rejected the transaction`); + return; + } + + bot.sendMessage(chatId, `Unknown error happened`); + }) + .finally(() => connector.pauseConnection()); + +// ... other code +``` + +
+Full handleSendTXCommand code + +```ts +export async function handleSendTXCommand(msg: TelegramBot.Message): Promise { + const chatId = msg.chat.id; + + const connector = getConnector(chatId); + + await connector.restoreConnection(); + if (!connector.connected) { + await bot.sendMessage(chatId, 'Connect wallet to send transaction'); + return; + } + + pTimeout( + connector.sendTransaction({ + validUntil: Math.round( + (Date.now() + Number(process.env.DELETE_SEND_TX_MESSAGE_TIMEOUT_MS)) / 1000 + ), + messages: [ + { + amount: '1000000', + address: '0:0000000000000000000000000000000000000000000000000000000000000000' + } + ] + }), + Number(process.env.DELETE_SEND_TX_MESSAGE_TIMEOUT_MS) + ) + .then(() => { + bot.sendMessage(chatId, `Transaction sent successfully`); + }) + .catch(e => { + if (e === pTimeoutException) { + bot.sendMessage(chatId, `Transaction was not confirmed`); + return; + } + + if (e instanceof UserRejectsError) { + bot.sendMessage(chatId, `You rejected the transaction`); + return; + } + + bot.sendMessage(chatId, `Unknown error happened`); + }) + .finally(() => connector.pauseConnection()); + + let deeplink = ''; + const walletInfo = await getWalletInfo(connector.wallet!.device.appName); + if (walletInfo) { + deeplink = walletInfo.universalLink; + } + + await bot.sendMessage( + chatId, + `Open ${walletInfo?.name || connector.wallet!.device.appName} and confirm transaction`, + { + reply_markup: { + inline_keyboard: [ + [ + { + text: 'Open Wallet', + url: deeplink + } + ] + ] + } + } + ); +} +``` + +
+ +If user doesn't confirm the transaction during `DELETE_SEND_TX_MESSAGE_TIMEOUT_MS` (10min), the transaction will be cancelled and bot will send a message `Transaction was not confirmed`. + +You can set this parameter to `5000` compile and rerun the bot and test its behaviour. + +### Wallet connect flow optimisation + +At this moment we create a new connector on the every navigation through the wallet connection menu step. +That is poorly because we don't close previous connectors connection when create new connectors. +Let's improve this behaviour and create a cache-mapping for users connectors. + +
+src/ton-connect/connector.ts code + +```ts +// src/ton-connect/connector.ts + +import TonConnect from '@tonconnect/sdk'; +import { TonConnectStorage } from './storage'; +import * as process from 'process'; + +type StoredConnectorData = { + connector: TonConnect; + timeout: ReturnType; + onConnectorExpired: ((connector: TonConnect) => void)[]; +}; + +const connectors = new Map(); + +export function getConnector( + chatId: number, + onConnectorExpired?: (connector: TonConnect) => void +): TonConnect { + let storedItem: StoredConnectorData; + if (connectors.has(chatId)) { + storedItem = connectors.get(chatId)!; + clearTimeout(storedItem.timeout); + } else { + storedItem = { + connector: new TonConnect({ + manifestUrl: process.env.MANIFEST_URL, + storage: new TonConnectStorage(chatId) + }), + onConnectorExpired: [] + } as unknown as StoredConnectorData; + } + + if (onConnectorExpired) { + storedItem.onConnectorExpired.push(onConnectorExpired); + } + + storedItem.timeout = setTimeout(() => { + if (connectors.has(chatId)) { + const storedItem = connectors.get(chatId)!; + storedItem.connector.pauseConnection(); + storedItem.onConnectorExpired.forEach(callback => callback(storedItem.connector)); + connectors.delete(chatId); + } + }, Number(process.env.CONNECTOR_TTL_MS)); + + connectors.set(chatId, storedItem); + return storedItem.connector; +} +``` + +
+ +This code may look a little tricky, but here we go. +Here we store a connector, it's cleaning timeout and list of callback that should be executed after the timeout for each user. + +When `getConnector` is called we check if there is an existing connector for this `chatId` (user) it the cache. If it exists we reset the cleaning timeout and return the connector. +That allows keep active users connectors in cache. It there is no connector in the cache we create a new one, register a timeout clean function and return this connector. + +To make it works we have to add a new parameter to the `.env` + +```dotenv +# .env +TELEGRAM_BOT_TOKEN= +MANIFEST_URL=https://ton-connect.github.io/demo-dapp-with-react-ui/tonconnect-manifest.json +WALLETS_LIST_CAHCE_TTL_MS=86400000 +DELETE_SEND_TX_MESSAGE_TIMEOUT_MS=600000 +CONNECTOR_TTL_MS=600000 +``` + +Now let's use it in the handelConnectCommand + +
+src/commands-handlers.ts code + +```ts +// src/commands-handlers.ts + +import { + CHAIN, + isWalletInfoRemote, + toUserFriendlyAddress, + UserRejectsError +} from '@tonconnect/sdk'; +import { bot } from './bot'; +import { getWallets, getWalletInfo } from './ton-connect/wallets'; +import QRCode from 'qrcode'; +import TelegramBot from 'node-telegram-bot-api'; +import { getConnector } from './ton-connect/connector'; +import { pTimeout, pTimeoutException } from './utils'; + +let newConnectRequestListenersMap = new Map void>(); + +export async function handleConnectCommand(msg: TelegramBot.Message): Promise { + const chatId = msg.chat.id; + let messageWasDeleted = false; + + newConnectRequestListenersMap.get(chatId)?.(); + + const connector = getConnector(chatId, () => { + unsubscribe(); + newConnectRequestListenersMap.delete(chatId); + deleteMessage(); + }); + + await connector.restoreConnection(); + if (connector.connected) { + const connectedName = + (await getWalletInfo(connector.wallet!.device.appName))?.name || + connector.wallet!.device.appName; + + await bot.sendMessage( + chatId, + `You have already connect ${connectedName} wallet\nYour address: ${toUserFriendlyAddress( + connector.wallet!.account.address, + connector.wallet!.account.chain === CHAIN.TESTNET + )}\n\n Disconnect wallet firstly to connect a new one` + ); + + return; + } + + const unsubscribe = connector.onStatusChange(async wallet => { + if (wallet) { + await deleteMessage(); + + const walletName = + (await getWalletInfo(wallet.device.appName))?.name || wallet.device.appName; + await bot.sendMessage(chatId, `${walletName} wallet connected successfully`); + unsubscribe(); + newConnectRequestListenersMap.delete(chatId); + } + }); + + const wallets = await getWallets(); + + const link = connector.connect(wallets); + const image = await QRCode.toBuffer(link); + + const botMessage = await bot.sendPhoto(chatId, image, { + reply_markup: { + inline_keyboard: [ + [ + { + text: 'Choose a Wallet', + callback_data: JSON.stringify({ method: 'chose_wallet' }) + }, + { + text: 'Open Link', + url: `https://ton-connect.github.io/open-tc?connect=${encodeURIComponent( + link + )}` + } + ] + ] + } + }); + + const deleteMessage = async (): Promise => { + if (!messageWasDeleted) { + messageWasDeleted = true; + await bot.deleteMessage(chatId, botMessage.message_id); + } + }; + + newConnectRequestListenersMap.set(chatId, async () => { + unsubscribe(); + + await deleteMessage(); + + newConnectRequestListenersMap.delete(chatId); + }); +} + +// ... other code +``` + +
+ +We defined `newConnectRequestListenersMap` to store cleanup callback for the last connect request for each user. +If user calls `/connect` multiple times, bot will delete previous message with QR. +Also, we subscribed to the connector expiration timeout to delete the QR-code message when it is expired. + +Now we should remove `connector.onStatusChange` subscription from the `connect-wallet-menu.ts` functions, +because they use the same connector instance and one subscription in the `handleConnectCommand` in enough. + +
+src/connect-wallet-menu.ts code + +```ts +// src/connect-wallet-menu.ts + +// ... other code + +async function onOpenUniversalQRClick(query: CallbackQuery, _: string): Promise { + const chatId = query.message!.chat.id; + const wallets = await getWallets(); + + const connector = getConnector(chatId); + + const link = connector.connect(wallets); + + await editQR(query.message!, link); + + await bot.editMessageReplyMarkup( + { + inline_keyboard: [ + [ + { + text: 'Choose a Wallet', + callback_data: JSON.stringify({ method: 'chose_wallet' }) + }, + { + text: 'Open Link', + url: `https://ton-connect.github.io/open-tc?connect=${encodeURIComponent( + link + )}` + } + ] + ] + }, + { + message_id: query.message?.message_id, + chat_id: query.message?.chat.id + } + ); +} + +async function onWalletClick(query: CallbackQuery, data: string): Promise { + const chatId = query.message!.chat.id; + const connector = getConnector(chatId); + + const wallets = await getWallets(); + + const selectedWallet = wallets.find(wallet => wallet.name === data); + if (!selectedWallet) { + return; + } + + const link = connector.connect({ + bridgeUrl: selectedWallet.bridgeUrl, + universalLink: selectedWallet.universalLink + }); + + await editQR(query.message!, link); + + await bot.editMessageReplyMarkup( + { + inline_keyboard: [ + [ + { + text: '« Back', + callback_data: JSON.stringify({ method: 'chose_wallet' }) + }, + { + text: `Open ${data}`, + url: link + } + ] + ] + }, + { + message_id: query.message?.message_id, + chat_id: chatId + } + ); +} + +// ... other code +``` + +
+ +That's it! Compile and run the bot and try to call `/connect` twice. + +### Improve interaction with @wallet + +Starting from v3 TonConnect supports connection to TWA wallets like @wallet. At the moment in the tutorial the bot could be connected to the @wallet. +However, we should improve redirection strategy to provide better UX. Moreover, let's add `Connect @wallet` button to the first ("Universal QR") screen. + +First, let's create some utility functions: + +```ts +// src/utils.ts +import { encodeTelegramUrlParameters, isTelegramUrl } from '@tonconnect/sdk'; + +export const AT_WALLET_APP_NAME = 'telegram-wallet'; + +// ... other code +export function addTGReturnStrategy(link: string, strategy: string): string { + const parsed = new URL(link); + parsed.searchParams.append('ret', strategy); + link = parsed.toString(); + + const lastParam = link.slice(link.lastIndexOf('&') + 1); + return link.slice(0, link.lastIndexOf('&')) + '-' + encodeTelegramUrlParameters(lastParam); +} + +export function convertDeeplinkToUniversalLink(link: string, walletUniversalLink: string): string { + const search = new URL(link).search; + const url = new URL(walletUniversalLink); + + if (isTelegramUrl(walletUniversalLink)) { + const startattach = 'tonconnect-' + encodeTelegramUrlParameters(search.slice(1)); + url.searchParams.append('startattach', startattach); + } else { + url.search = search; + } + + return url.toString(); +} +``` + +TonConnect parameters in Telegram links have to be encoded in special way, that's why we use `encodeTelegramUrlParameters` to encode return strategy parameter. +We will use `addTGReturnStrategy` to provide correct return url to the demo bot for @wallet. + +Since we use Universal QR page creation code in two places, we are moving it to the separate function: + +```ts +// src/utils.ts + +// ... other code + +export async function buildUniversalKeyboard( + link: string, + wallets: WalletInfoRemote[] +): Promise { + const atWallet = wallets.find(wallet => wallet.appName.toLowerCase() === AT_WALLET_APP_NAME); + const atWalletLink = atWallet + ? addTGReturnStrategy( + convertDeeplinkToUniversalLink(link, atWallet?.universalLink), + process.env.TELEGRAM_BOT_LINK! + ) + : undefined; + + const keyboard = [ + { + text: 'Choose a Wallet', + callback_data: JSON.stringify({ method: 'chose_wallet' }) + }, + { + text: 'Open Link', + url: `https://ton-connect.github.io/open-tc?connect=${encodeURIComponent(link)}` + } + ]; + + if (atWalletLink) { + keyboard.unshift({ + text: '@wallet', + url: atWalletLink + }); + } + + return keyboard; +} +``` + +Here we are adding separate button for @wallet to the First screen (Universal QR screen). All that remains is to use this function in +connect-wallet-menu and command-handlers: + +
+src/connect-wallet-menu.ts code + +```ts +// src/connect-wallet-menu.ts + +// ... other code + +async function onOpenUniversalQRClick(query: CallbackQuery, _: string): Promise { + const chatId = query.message!.chat.id; + const wallets = await getWallets(); + + const connector = getConnector(chatId); + + const link = connector.connect(wallets); + + await editQR(query.message!, link); + + const keyboard = await buildUniversalKeyboard(link, wallets); + + await bot.editMessageReplyMarkup( + { + inline_keyboard: [keyboard] + }, + { + message_id: query.message?.message_id, + chat_id: query.message?.chat.id + } + ); +} + +// ... other code +``` + +
+ +
+src/commands-handlers.ts code + +```ts +// src/commands-handlers.ts + +// ... other code + +export async function handleConnectCommand(msg: TelegramBot.Message): Promise { + const chatId = msg.chat.id; + let messageWasDeleted = false; + + newConnectRequestListenersMap.get(chatId)?.(); + + const connector = getConnector(chatId, () => { + unsubscribe(); + newConnectRequestListenersMap.delete(chatId); + deleteMessage(); + }); + + await connector.restoreConnection(); + if (connector.connected) { + const connectedName = + (await getWalletInfo(connector.wallet!.device.appName))?.name || + connector.wallet!.device.appName; + await bot.sendMessage( + chatId, + `You have already connect ${connectedName} wallet\nYour address: ${toUserFriendlyAddress( + connector.wallet!.account.address, + connector.wallet!.account.chain === CHAIN.TESTNET + )}\n\n Disconnect wallet firstly to connect a new one` + ); + + return; + } + + const unsubscribe = connector.onStatusChange(async wallet => { + if (wallet) { + await deleteMessage(); + + const walletName = + (await getWalletInfo(wallet.device.appName))?.name || wallet.device.appName; + await bot.sendMessage(chatId, `${walletName} wallet connected successfully`); + unsubscribe(); + newConnectRequestListenersMap.delete(chatId); + } + }); + + const wallets = await getWallets(); + + const link = connector.connect(wallets); + const image = await QRCode.toBuffer(link); + + const keyboard = await buildUniversalKeyboard(link, wallets); + + const botMessage = await bot.sendPhoto(chatId, image, { + reply_markup: { + inline_keyboard: [keyboard] + } + }); + + const deleteMessage = async (): Promise => { + if (!messageWasDeleted) { + messageWasDeleted = true; + await bot.deleteMessage(chatId, botMessage.message_id); + } + }; + + newConnectRequestListenersMap.set(chatId, async () => { + unsubscribe(); + + await deleteMessage(); + + newConnectRequestListenersMap.delete(chatId); + }); +} + +// ... other code +``` + +
+ +Now we are going to handle TG links properly when user clicks to a wallet button on the second Screen (Choosing a wallet): + +
+src/connect-wallet-menu.ts code + +```ts +// src/connect-wallet-menu.ts + +// ... other code + + +async function onWalletClick(query: CallbackQuery, data: string): Promise { + const chatId = query.message!.chat.id; + const connector = getConnector(chatId); + + const selectedWallet = await getWalletInfo(data); + if (!selectedWallet) { + return; + } + + let buttonLink = connector.connect({ + bridgeUrl: selectedWallet.bridgeUrl, + universalLink: selectedWallet.universalLink + }); + + let qrLink = buttonLink; + + if (isTelegramUrl(selectedWallet.universalLink)) { + buttonLink = addTGReturnStrategy(buttonLink, process.env.TELEGRAM_BOT_LINK!); + qrLink = addTGReturnStrategy(qrLink, 'none'); + } + + await editQR(query.message!, qrLink); + + await bot.editMessageReplyMarkup( + { + inline_keyboard: [ + [ + { + text: '« Back', + callback_data: JSON.stringify({ method: 'chose_wallet' }) + }, + { + text: `Open ${selectedWallet.name}`, + url: buttonLink + } + ] + ] + }, + { + message_id: query.message?.message_id, + chat_id: chatId + } + ); +} + +// ... other code +``` + +
+ +Note that we place different links to the QR and button-link (`qrLink` and `buttonLink`), +because we don't need redirection when user scans QR by @wallet, and at the same time we need redirect back to the bot when user connects @wallet using button-link. + +Now let's add return strategy for TG links in `send transaction` handler: + +
+src/commands-handlers.ts code + +```ts +// src/commands-handlers.ts + +// ... other code + +export async function handleSendTXCommand(msg: TelegramBot.Message): Promise { + const chatId = msg.chat.id; + + const connector = getConnector(chatId); + + await connector.restoreConnection(); + if (!connector.connected) { + await bot.sendMessage(chatId, 'Connect wallet to send transaction'); + return; + } + + pTimeout( + connector.sendTransaction({ + validUntil: Math.round( + (Date.now() + Number(process.env.DELETE_SEND_TX_MESSAGE_TIMEOUT_MS)) / 1000 + ), + messages: [ + { + amount: '1000000', + address: '0:0000000000000000000000000000000000000000000000000000000000000000' + } + ] + }), + Number(process.env.DELETE_SEND_TX_MESSAGE_TIMEOUT_MS) + ) + .then(() => { + bot.sendMessage(chatId, `Transaction sent successfully`); + }) + .catch(e => { + if (e === pTimeoutException) { + bot.sendMessage(chatId, `Transaction was not confirmed`); + return; + } + + if (e instanceof UserRejectsError) { + bot.sendMessage(chatId, `You rejected the transaction`); + return; + } + + bot.sendMessage(chatId, `Unknown error happened`); + }) + .finally(() => connector.pauseConnection()); + + let deeplink = ''; + const walletInfo = await getWalletInfo(connector.wallet!.device.appName); + if (walletInfo) { + deeplink = walletInfo.universalLink; + } + + if (isTelegramUrl(deeplink)) { + const url = new URL(deeplink); + url.searchParams.append('startattach', 'tonconnect'); + deeplink = addTGReturnStrategy(url.toString(), process.env.TELEGRAM_BOT_LINK!); + } + + await bot.sendMessage( + chatId, + `Open ${walletInfo?.name || connector.wallet!.device.appName} and confirm transaction`, + { + reply_markup: { + inline_keyboard: [ + [ + { + text: `Open ${walletInfo?.name || connector.wallet!.device.appName}`, + url: deeplink + } + ] + ] + } + } + ); +} + +// ... other code +``` + +
+ +That is it. Now user is able to connect @wallet using special button on the main screen, also we have provided proper return strategy for TG links. + +## Add a permanent storage + +At this moment we store TonConnect sessions in the Map object. But you may want to store it to the database or other permanent storage to save the sessions when you restart the server. +We will use Redis for that, but you can pick any permanent storage. + +### Set up redis + +Firstly run `npm i redis`. + +[See package details](https://www.npmjs.com/package/redis) + +To work with redis you have to start redis server. We will use the Docker image: +`docker run -p 6379:6379 -it redis/redis-stack-server:latest` + +Now add redis connection parameter to the `.env`. Default redis url is `redis://127.0.0.1:6379`. + +```dotenv +# .env +TELEGRAM_BOT_TOKEN= +MANIFEST_URL=https://ton-connect.github.io/demo-dapp-with-react-ui/tonconnect-manifest.json +WALLETS_LIST_CAHCE_TTL_MS=86400000 +DELETE_SEND_TX_MESSAGE_TIMEOUT_MS=600000 +CONNECTOR_TTL_MS=600000 +REDIS_URL=redis://127.0.0.1:6379 +``` + +Let's integrate redis to the `TonConnectStorage`: + +```ts +// src/ton-connect/storage.ts + +import { IStorage } from '@tonconnect/sdk'; +import { createClient } from 'redis'; + +const client = createClient({ url: process.env.REDIS_URL }); + +client.on('error', err => console.log('Redis Client Error', err)); + +export async function initRedisClient(): Promise { + await client.connect(); +} +export class TonConnectStorage implements IStorage { + constructor(private readonly chatId: number) {} + + private getKey(key: string): string { + return this.chatId.toString() + key; + } + + async removeItem(key: string): Promise { + await client.del(this.getKey(key)); + } + + async setItem(key: string, value: string): Promise { + await client.set(this.getKey(key), value); + } + + async getItem(key: string): Promise { + return (await client.get(this.getKey(key))) || null; + } +} +``` + +To make it work we have to wait for the redis initialisation in the `main.ts`. Let's wrap code in this file to an async function: + +```ts +// src/main.ts +// ... imports + +async function main(): Promise { + await initRedisClient(); + + const callbacks = { + ...walletMenuCallbacks + }; + + bot.on('callback_query', query => { + if (!query.data) { + return; + } + + let request: { method: string; data: string }; + + try { + request = JSON.parse(query.data); + } catch { + return; + } + + if (!callbacks[request.method as keyof typeof callbacks]) { + return; + } + + callbacks[request.method as keyof typeof callbacks](query, request.data); + }); + + bot.onText(/\/connect/, handleConnectCommand); + + bot.onText(/\/send_tx/, handleSendTXCommand); + + bot.onText(/\/disconnect/, handleDisconnectCommand); + + bot.onText(/\/my_wallet/, handleShowMyWalletCommand); +} + +main(); +``` + +## Summary + +What is next? + +- If you want to run the bot in production you may want to install and use a process manager like [pm2](https://pm2.keymetrics.io/). +- You can add better errors handling in the bot. + +## See Also + +- [Sending messages](/develop/dapps/ton-connect/transactions) +- [Integration Manual](/develop/dapps/ton-connect/integration) From a687fda452d085c3c089d30759d9f9bb82df4168 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:01 +0800 Subject: [PATCH 066/219] New translations tg-bot-tonapi-nft.md (Chinese Simplified) --- .../dapps/ton-connect/tg-bot-tonapi-nft.md | 307 ++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/tg-bot-tonapi-nft.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/tg-bot-tonapi-nft.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/tg-bot-tonapi-nft.md new file mode 100644 index 0000000000..8f0799bc3a --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/tg-bot-tonapi-nft.md @@ -0,0 +1,307 @@ +# Telegram Bot to check ownership of NFT + +## 👋 Introduction + +This article aims to provide guidance on verifying token ownership as the popularity of NFTs continues to soar, with a growing number of individuals searching for effective methods to accomplish this. + +## 📝 Obtaining a token for the bot + +1. Visit [BotFather](https://t.me/BotFather) on Telegram. + +2. Follow the instructions to create a new bot. + +3. Once created, BotFather will provide you with a unique token. This token is crucial as it allows your bot to communicate with the Telegram API. + +## 🧠 Description of the bot's functionality + +### Functionality + +Our Telegram bot will perform the fascinating example task of verifying if a user owns an NFT item from the TON Footsteps collection. The key components will be: + +- aiogram library: For interfacing with the Telegram client. +- TON Connect: To connect with the user's wallet. +- Redis database: To handle data relevant to TON Connect. + +### 🗂️ Project structure + +- Main file: Containing the primary logic of the bot. +- Helper files: + - Keyboards: Telegram bot keyboard objects. + - Database Preparation: Facilitating TON Connect. + +### 🛠️ Install the libraries + +Execute the following command to install all the necessary libraries through `pip`: + +```bash +pip install aiogram redis qrcode tonsdk pytonconnect requests +``` + +And then, import them to the main file: + +```python +import asyncio +import requests +import qrcode +import os +import random + +from aiogram import Bot, Dispatcher, executor, types +from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton +from aiogram.types.input_file import InputFile +from tonsdk.utils import Address +from pytonconnect import TonConnect +``` + +### 🗄️ Redis database setup + +Additionally, for setting up and launching the Redis database, I recommend acquainting yourself with the information regarding its installation and initiation, which can be found [here](https://redis.io/docs/getting-started/installation/) + +## 🎨 Writing the bot + +### 🎹 Designing the keyboards + +To begin with, let's create a file containing all the necessary keyboard configurations, and we'll name it `keyboards.py` + +```python +# Creating custom keyboard buttons and reply markup for the Telegram bot. + +from aiogram.types import ReplyKeyboardMarkup, KeyboardButton + +# Creating a KeyboardButton for the "Check for footstep NFT" action. +CheckButton = KeyboardButton('Check for footstep NFT') + +# Creating a ReplyKeyboardMarkup for the "Check" action using the CheckButton. +# The 'resize_keyboard' parameter is set to True, allowing the keyboard to be resized in the Telegram app. +Checkkb = ReplyKeyboardMarkup(resize_keyboard=True).add(CheckButton) + +# Creating additional buttons for the "Tonkeeper" and "Tonhub" actions. +TonkeeperButton = KeyboardButton('Tonkeeper') +TonhubButton = KeyboardButton('Tonhub') + +# Creating a ReplyKeyboardMarkup for the "Wallet" action using the TonkeeperButton and TonhubButton. +# The 'resize_keyboard' parameter is set to True to allow the keyboard to be resized in the Telegram app. +Walletkb = ReplyKeyboardMarkup(resize_keyboard=True).add(TonkeeperButton).add(TonhubButton) +``` + +And let's add the import of this file to the `main.py`. + +```python +import keyboards as kb +``` + +### 🧩 Database preparation + +Now, we need to prepare our database to interface with `pytonconnect`. +To do this, we will create a new file named `database.py` + +```python +# Importing the Redis library to interact with the Redis database +import redis +# Importing the IStorage interface from pytonconnect +from pytonconnect.storage import IStorage + +# Creating a connection to the Redis database running on localhost at port 6379 +r = redis.Redis(host='localhost', port=6379, decode_responses=True) + +# Defining a class Storage that implements the IStorage interface from pytonconnect +class Storage(IStorage): + def __init__(self, id): + # Constructor method initializing the unique identifier for each storage instance + self.id = id + + # Asynchronous method to set a key-value pair in Redis, with the key being appended with the unique ID + async def set_item(self, key: str, value: str): + r.set(key + self.id, value) + + # Asynchronous method to retrieve the value for a given key from Redis, with the key being appended with the unique ID + # If the key does not exist, returns the default value + async def get_item(self, key: str, default_value: str = None): + if r.exists(key + self.id): + return r.get(key + self.id) + else: + return default_value + + # Asynchronous method to remove the key-value pair for a given key from Redis, with the key being appended with the unique ID + async def remove_item(self, key: str): + r.delete(key + self.id) +``` + +And also import it into our main file with the bot + +```python +import database +``` + +### 🌟 Writing the startup handler + +```python +# Define a command handler for the '/start' command for private chats +@dp.message_handler(commands=['start'], chat_type=types.ChatType.PRIVATE) +async def start_command(message: types.Message): + # Send a greeting message to the user, explaining the bot's functionality + await message.answer("Hi👋, I am an example of a bot for checking the ownership of the NFT", reply_markup=kb.Checkkb) + # Further explain how the bot can help with NFT collection checking + await message.answer("With my help, you can check if you have an NFT from the TON Footsteps collection") +``` + +### 🕵️ Function for checking the presence of NFT + +```python +# A message handler function to check if the user has a footstep NFT and respond accordingly. + +@dp.message_handler(text='Check for footstep NFT', chat_type=types.ChatType.PRIVATE) +async def connect_wallet_tonkeeper(message: types.Message): + # Checking if the user's wallet address is present in the database for the given Telegram ID. + # If the address is not available, prompt the user to connect their wallet (Tonkeeper or Tonhub). + if cur.execute(f"SELECT address FROM Users WHERE id_tg == {message.from_user.id}").fetchall()[0][0] is None: + await message.answer(text="To check for the presence of NFT, connect your wallet (Tonkeeper or Tonhub)", reply_markup=kb.Walletkb) + else: + # If the user's wallet address is available, proceed to check for the presence of the footstep NFT. + address = cur.execute(f"SELECT address FROM Users WHERE id_tg == {message.from_user.id}").fetchall()[0][0] + + # Forming the URL to query the TON API for the user's NFTs from the TON Footsteps collection. + url = f'https://tonapi.io/v2/accounts/{address}/nfts?collection=EQCV8xVdWOV23xqOyC1wAv-D_H02f7gAjPzOlNN6Nv1ksVdL&limit=1000&offset=0&indirect_ownership=false' + + try: + # Sending a GET request to the TON API and parsing the JSON response to extract NFT items. + response = requests.get(url).json()['nft_items'] + except: + # If there's an error with the API request, notify the user. + await message.answer(text="Something went wrong...") + return + + # Based on the response from the TON API, informing the user about the NFT presence or absence. + if response: + await message.answer(text="You have an NFT from the TON Footsteps collection") + else: + await message.answer(text="Unfortunately, you don't have NFT from the TON Footsteps collection") +``` + +In order to check whether the NFT user has the necessary collection, we will use the [TONAPI](https://tonapi.io/). The request will look like this: + +```bash +https://tonapi.io/v2/accounts/
/nfts?collection=&limit=1000&offset=0&indirect_ownership=false +``` + +Where: + +- `ADDRESS` - This is the wallet address of the user we want to check for the required NFT. +- `NFT_COLLECTION` - This is the address of the required NFT collection. + +The API request will return all the user's NFTs from the specified collection. + +### 🏡 Function for getting the user's address via TON Connect + +```python +# Define a message handler for connection to wallets (Tonkeeper or Tonhub) in private chats +@dp.message_handler(text=['Tonkeeper', 'Tonhub'], chat_type=types.ChatType.PRIVATE) +async def connect_wallet_tonkeeper(message: types.Message): + # Create a storage instance based on the user's ID + storage = database.Storage(str(message.from_user.id)) + + # Initialize a connection using the given manifest URL and storage + connector = TonConnect(manifest_url='https://raw.githubusercontent.com/AndreyBurnosov/Checking_for_nft_availability/main/pytonconnect-manifest.json', storage=storage) + # Attempt to restore the existing connection, if any + is_connected = await connector.restore_connection() + + # If already connected, inform the user and exit the function + if is_connected: + await message.answer('Your wallet is already connected.') + return + + # Define the connection options for different wallet + connection = {'Tonkeeper': 0, 'Tonhub': 2} + + # Retrieve the available wallets + wallets_list = connector.get_wallets() + + # Generate a connection URL for the selected wallet + generated_url_tonkeeper = await connector.connect(wallets_list[connection[message.text]]) + + # Create an inline keyboard markup with a button to open the connection URL + urlkb = InlineKeyboardMarkup(row_width=1) + urlButton = InlineKeyboardButton(text=f'Open {message.text}', url=generated_url_tonkeeper) + urlkb.add(urlButton) + + # Generate a QR code for the connection URL and save it as an image + img = qrcode.make(generated_url_tonkeeper) + path = f'image{random.randint(0, 100000)}.png' + img.save(path) + photo = InputFile(path) + + # Send the QR code image to the user with the inline keyboard markup + msg = await bot.send_photo(chat_id=message.chat.id, photo=photo, reply_markup=urlkb) + # Remove the saved image from the local file system + os.remove(path) + + # Check for a successful connection in a loop, with a maximum of 300 iterations (300 seconds) + for i in range(300): + await asyncio.sleep(1) + if connector.connected: + if connector.account.address: + address = Address(connector.account.address).to_string(True, True, True) + break + + # Delete the previously sent QR code message + await msg.delete() + + # Confirm to the user that the wallet has been successfully connected + await message.answer('Your wallet has been successfully connected.', reply_markup=kb.Checkkb) +``` + +#### 📄 Creating the manifest for TON Connect + +In order to properly use the TON Connect we also need to create a file named `pytonconnect-manifest.json`, following this template: + +```json +{ + "url": "", // required + "name": "", // required + "iconUrl": "", // required + "termsOfUseUrl": "", // optional + "privacyPolicyUrl": "" // optional +} +``` + +For this bot, it'll be enough to simply use some default icon and any desired name: + +```json +{ + "url": "", + "name": "Example bot", + "iconUrl": "https://raw.githubusercontent.com/XaBbl4/pytonconnect/main/pytonconnect.png" +} +``` + +You can learn more about the `pytonconnect` library [in its repository](https://github.com/XaBbl4/pytonconnect) + +### 🚀 Launching the bot + +Add the folliwing code to the end of `main.py` and we'll be ready to test our bot! + +```python +# The main entry point of the Telegram bot application. + +if __name__ == '__main__': + # Start polling for updates from the Telegram Bot API using the executor. + # The `dp` (Dispatcher) object handles message handling and other event processing. + # The `skip_updates=True` parameter tells the executor to skip pending updates when starting. + executor.start_polling(dp, skip_updates=True) +``` + +Now simply run this command in your terminal: + +```bash +python3 main.py +``` + +After that, open the dialogue with your bot in Telegram and try to use it. If you followed this guide correctly, the bot should work as expected! + +## [🎁 Final code and resources](https://github.com/AndreyBurnosov/Checking_for_nft_availability) + +## 📌 References + +- [TON API](https://tonapi.io/) +- [Python library for TON Connect2.0](https://github.com/XaBbl4/pytonconnect) +- The tutorial was developed by [Andrew Burnosov](https://github.com/AndreyBurnosov) (TG: [@AndrewBurnosov](https://t.me/AndreyBurnosov)) From f04a234d10ec1c1912994bc90ef709a3bf05a3e4 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:02 +0800 Subject: [PATCH 067/219] New translations transactions.md (Chinese Simplified) --- .../develop/dapps/ton-connect/transactions.md | 194 ++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/transactions.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/transactions.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/transactions.md new file mode 100644 index 0000000000..9a8d85d060 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/transactions.md @@ -0,0 +1,194 @@ +# Sending Messages + +TON Connect 2.0 has more powerful options than just authenticating users in the dApp: it's possible to send outgoing messages via connected wallets! + +You will understand: + +- how to send messages from the DAppto the blockchain +- how to send multiple messages in one transaction +- how to deploy a contract using TON Connect + +## Playground page + +We will use the low level [TON Connect SDK](https://github.com/ton-connect/sdk/tree/main/packages/sdk) for JavaScript. We'll experiment in the browser console on a page where the wallet is already connected. Here is the sample page: + +```html + + + + + + + + + + + +``` + +Feel free to copy-paste it into your browser console and run it. + +## Sending multiple messages + +### 1. Understanding a task + +We will send two separate messages in one transaction: one to your own address, carrying 0.2 TON, and one to the other wallet address carrying 0.1 TON. + +By the way, there is a limit of messages sent in one transaction: + +- standard ([v3](/participate/wallets/contracts#wallet-v3)/[v4](/participate/wallets/contracts#wallet-v4)) wallets: 4 outgoing messages; +- highload wallets: 255 outgoing messages (close to blockchain limitations). + +### 2. Sending the messages + +Run the following code: + +```js +console.log(await connector.sendTransaction({ + validUntil: Math.floor(new Date() / 1000) + 360, + messages: [ + { + address: connector.wallet.account.address, + amount: "200000000" + }, + { + address: "0:b2a1ecf5545e076cd36ae516ea7ebdf32aea008caa2b84af9866becb208895ad", + amount: "100000000" + } + ] +})); +``` + +You'll notice that this command does not print anything into the console, `null` or `undefined`, as functions returning nothing do. This means that `connector.sendTransaction` does not exit immediately. + +Open your wallet application, and you'll see why. There is a request, showing what you are sending and where coins would go. Please, accept it. + +### 3. Getting the result + +The function will exit, and the output from the blockchain will be printed: + +```json +{ + boc: "te6cckEBAwEA4QAC44gBZUPZ6qi8Dtmm1cot1P175lXUARlUVwlfMM19lkERK1oCUB3RqDxAFnPpeo191X/jiimn9Bwnq3zwcU/MMjHRNN5sC5tyymBV3SJ1rjyyscAjrDDFAIV/iE+WBySEPP9wCU1NGLsfcvVgAAACSAAYHAECAGhCAFlQ9nqqLwO2abVyi3U/XvmVdQBGVRXCV8wzX2WQRErWoAmJaAAAAAAAAAAAAAAAAAAAAGZCAFlQ9nqqLwO2abVyi3U/XvmVdQBGVRXCV8wzX2WQRErWnMS0AAAAAAAAAAAAAAAAAAADkk4U" +} +``` + +BOC is [Bag of Cells](/learn/overviews/cells), the way of how is data stored in TON. Now we can decode it. + +Decode this BOC in the tool of your choice, and you'll get the following tree of cells: + +```bash +x{88016543D9EAA8BC0ED9A6D5CA2DD4FD7BE655D401195457095F30CD7D9641112B5A02501DD1A83C401673E97A8D7DD57FE38A29A7F41C27AB7CF0714FCC3231D134DE6C0B9B72CA6055DD2275AE3CB2B1C023AC30C500857F884F960724843CFF70094D4D18BB1F72F5600000024800181C_} + x{42005950F67AAA2F03B669B5728B753F5EF9957500465515C257CC335F6590444AD6A00989680000000000000000000000000000} + x{42005950F67AAA2F03B669B5728B753F5EF9957500465515C257CC335F6590444AD69CC4B40000000000000000000000000000} +``` + +This is serialized external message, and two references are outgoing messages representations. + +```bash +x{88016543D9EAA8BC0ED9A6D5CA2DD4FD7BE655D401195457095F30CD7D964111... + $10 ext_in_msg_info + $00 src:MsgAddressExt (null address) + "EQ..."a dest:MsgAddressInt (your wallet) + 0 import_fee:Grams + $0 (no state_init) + $0 (body starts in this cell) + ... +``` + +The purpose of returning BOC of the sent transaction is to track it. + +## Sending complex transactions + +### Serialization of cells + +Before we proceed, let's talk about the format of messages we are going to send. + +- **payload** (string base64, optional): raw one-cell BoC encoded in Base64. + - we will use it to store text comment on transfer +- **stateInit** (string base64, optional): raw one-cell BoC encoded in Base64. + - we will use it to deploy a smart contract + +After building a message, you can serialize it into BOC. + +```js +TonWeb.utils.bytesToBase64(await payloadCell.toBoc()) +``` + +### Transfer with comment + +You can use [toncenter/tonweb](https://github.com/toncenter/tonweb) JS SDK or your favourite tool to serialize cells to BOC. + +Text comment on transfer is encoded as opcode 0 (32 zero bits) + UTF-8 bytes of comment. Here's an example of how to convert it into a bag of cells. + +```js +let a = new TonWeb.boc.Cell(); +a.bits.writeUint(0, 32); +a.bits.writeString("TON Connect 2 tutorial!"); +let payload = TonWeb.utils.bytesToBase64(await a.toBoc()); + +console.log(payload); +// te6ccsEBAQEAHQAAADYAAAAAVE9OIENvbm5lY3QgMiB0dXRvcmlhbCFdy+mw +``` + +### Smart contract deployment + +And we'll deploy an instance of super simple [chatbot Doge](https://github.com/LaDoger/doge.fc), mentioned as one of [smart contract examples](/develop/smart-contracts/#smart-contract-examples). First of all, we load its code and store something unique in data, so that we receive our very own instance that has not been deployed by someone other. Then we combine code and data into stateInit. + +```js +let code = TonWeb.boc.Cell.oneFromBoc(TonWeb.utils.base64ToBytes('te6cckEBAgEARAABFP8A9KQT9LzyyAsBAGrTMAGCCGlJILmRMODQ0wMx+kAwi0ZG9nZYcCCAGMjLBVAEzxaARfoCE8tqEssfAc8WyXP7AN4uuM8=')); +let data = new TonWeb.boc.Cell(); +data.bits.writeUint(Math.floor(new Date()), 64); + +let state_init = new TonWeb.boc.Cell(); +state_init.bits.writeUint(6, 5); +state_init.refs.push(code); +state_init.refs.push(data); + +let state_init_boc = TonWeb.utils.bytesToBase64(await state_init.toBoc()); +console.log(state_init_boc); +// te6ccsEBBAEAUwAABRJJAgE0AQMBFP8A9KQT9LzyyAsCAGrTMAGCCGlJILmRMODQ0wMx+kAwi0ZG9nZYcCCAGMjLBVAEzxaARfoCE8tqEssfAc8WyXP7AAAQAAABhltsPJ+MirEd + +let doge_address = '0:' + TonWeb.utils.bytesToHex(await state_init.hash()); +console.log(doge_address); +// 0:1c7c35ed634e8fa796e02bbbe8a2605df0e2ab59d7ccb24ca42b1d5205c735ca +``` + +And, it's time to send our transaction! + +```js +console.log(await connector.sendTransaction({ + validUntil: Math.floor(new Date() / 1000) + 360, + messages: [ + { + address: "0:1c7c35ed634e8fa796e02bbbe8a2605df0e2ab59d7ccb24ca42b1d5205c735ca", + amount: "69000000", + payload: "te6ccsEBAQEAHQAAADYAAAAAVE9OIENvbm5lY3QgMiB0dXRvcmlhbCFdy+mw", + stateInit: "te6ccsEBBAEAUwAABRJJAgE0AQMBFP8A9KQT9LzyyAsCAGrTMAGCCGlJILmRMODQ0wMx+kAwi0ZG9nZYcCCAGMjLBVAEzxaARfoCE8tqEssfAc8WyXP7AAAQAAABhltsPJ+MirEd" + } + ] +})); +``` + +:::info +Get more examples in [Preparing Messages](/develop/dapps/ton-connect/message-builders) page for Transfer NFT and Jettons. +::: + +After confirmation, we may see our transaction complete at [tonscan.org](https://tonscan.org/tx/pCA8LzWlCRTBc33E2y-MYC7rhUiXkhODIobrZVVGORg=). + +## What happens if the user rejects a transaction request? + +It's pretty easy to handle request rejection, but when you're developing some project it's better to know what would happen in advance. + +When a user clicks "Cancel" in the popup in the wallet application, an exception is thrown: `Error: [TON_CONNECT_SDK_ERROR] Wallet declined the request`. This error can be considered final (unlike connection cancellation) - if it has been raised, then the requested transaction will definitely not happen until the next request is sent. + +## See Also + +- [Preparing Messages](/develop/dapps/ton-connect/message-builders) From e4f7cea50f2238e56285d316c45d1627f70ac910 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:02 +0800 Subject: [PATCH 068/219] New translations wallet.mdx (Chinese Simplified) --- .../current/develop/dapps/ton-connect/wallet.mdx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/wallet.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/wallet.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/wallet.mdx new file mode 100644 index 0000000000..a81bd46920 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/wallet.mdx @@ -0,0 +1,15 @@ +import Button from '@site/src/components/button' + +# Connect a Wallet + +If you are a wallet developer, you can connect your wallet to TON Connect and enable your users to interact with TON apps in a secure and convenient way. + +## Integration + +Use the following steps to connect your wallet to TON Connect: + +1. Read carefully [Protocol specifications](/develop/dapps/ton-connect/protocol/). +2. Implement the protocol using one of the [SDKs](/develop/dapps/ton-connect/developers). +3. Add your wallet to the [wallets-list](https://github.com/ton-blockchain/wallets-list) with a pull request. + + From 326d13106b5fb6d14f138b316635fec2cf800029 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:03 +0800 Subject: [PATCH 069/219] New translations web.mdx (Chinese Simplified) --- .../current/develop/dapps/ton-connect/web.mdx | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/web.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/web.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/web.mdx new file mode 100644 index 0000000000..64573665a8 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/ton-connect/web.mdx @@ -0,0 +1,159 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# TON Connect for JS + +This guide will help you integrate TON Connect into your Javascript application for user authentication and transactions. + +If you use React for your DApp, take a look at [TON Connect UI React SDK](/develop/dapps/ton-connect/react). + +## Implementation + +### 1. Installation + + + +Add script in the HEAD element of your website: +
+
+ +```html + +``` + +
+ + To begin the integration of TON Connect into your app, install the @tonconnect/ui package: + +```bash npm2yarn +npm i @tonconnect/ui +``` + + +
+ +### 2. TON Connect Initiation + +After installing the package, you should create a `manifest.json` file for your application. More information on how to create a manifest.json file can be found [here](/develop/dapps/ton-connect/manifest). + +Add a button with `ton-connect` id to connect to the wallet: + +```html +
+``` + +_After this tag_ add a script for `tonConnectUI` in `` part of application page: + +```html + +``` + +### 3. Connect to the Wallet + +"Connect" button (which is added at `buttonRootId`) automatically handles clicks. + +But you can open "connect modal" programmatically, e.g. after click on custom button: + +```html + +``` + +### 4. Redirects + +#### Customizing return strategy + +To redirect user to a specific URL after connection, you can [customize your return strategy](https://github.com/ton-connect/sdk/tree/main/packages/ui#add-the-return-strategy). + +#### Telegram Mini Apps + +To redirect user to a [Telegram Mini App](/develop/dapps/telegram-apps/) after wallet connection use `twaReturnUrl` option: + +```tsx +tonConnectUI.uiOptions = { + twaReturnUrl: 'https://t.me/YOUR_APP_NAME' + }; +``` + +[Read more in SDK documentation](https://github.com/ton-connect/sdk/tree/main/packages/ui#use-inside-twa-telegram-web-app) + +### 5. UI customization + +TonConnect UI provides an interface that should be familiar and recognizable to the user when using various apps. However, the app developer can make changes to this interface to keep it consistent with the app interface. + +- [TonConnect UI documentation](https://github.com/ton-connect/sdk/tree/main/packages/ui#ui-customisation) + +## SDK Documentation + +- [SDK documentation](https://github.com/ton-connect/sdk/blob/main/packages/ui/README.md) +- [Latest API documentation](https://ton-connect.github.io/sdk/modules/_tonconnect_ui.html) + +## Usage + +Let's take a look at the example of using the TON Connect UI in the application. + +### Sending messages + +Here is an example of sending a transaction using the TON Connect UI: + +```js +import TonConnectUI from '@tonconnect/ui'; + +const tonConnectUI = new TonConnectUI({ //connect application + manifestUrl: 'https:///tonconnect-manifest.json', + buttonRootId: '' +}); + +const transaction = { + messages: [ + { + address: "0:412410771DA82CBA306A55FA9E0D43C9D245E38133CB58F1457DFB8D5CD8892F", // destination address + amount: "20000000" //Toncoin in nanotons + } + ] +} + +const result = await tonConnectUI.sendTransaction(transaction) +``` + +- Get more examples here: [Preparing Messages](/develop/dapps/ton-connect/message-builders) + +### Understanding Transaction Status by Hash + +The principle located in Payment Processing (using tonweb). [See more](/develop/dapps/asset-processing/#checking-contracts-transactions) + +### Signing and Verification + +Understand how to sign and verify messages using the TON Connect: + +- [Signing and Verification](/develop/dapps/ton-connect/sign) +- [TON Connect UI implementation on GitHub](https://github.com/ton-connect/sdk/tree/main/packages/ui#add-connect-request-parameters-ton_proof) + +### Wallet Disconnection + +Call to disconnect the wallet: + +```js +await tonConnectUI.disconnect(); +``` + +## See Also + +- [UI Customization](https://github.com/ton-connect/sdk/tree/main/packages/ui#ui-customisation) +- [\[YouTube\] TON Connect UI React \[RU\]](https://youtu.be/wIMbkJHv0Fs?list=PLyDBPwv9EPsCJ226xS5_dKmXXxWx1CKz_&t=1747) +- [\[YouTube\] Connect TON Connect UI to Site \[RU\]](https://www.youtube.com/watch?v=HUQ1DPfFxG4&list=PLyDBPwv9EPsAIWi8vgic9kiV3KF_wvIcz&index=4) From ff412df78817314fd43c5001cfe07f8e5c8aa0b4 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:04 +0800 Subject: [PATCH 070/219] New translations accept-payments-in-a-telegram-bot-2.md (Chinese Simplified) --- .../accept-payments-in-a-telegram-bot-2.md | 541 ++++++++++++++++++ 1 file changed, 541 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/accept-payments-in-a-telegram-bot-2.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/accept-payments-in-a-telegram-bot-2.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/accept-payments-in-a-telegram-bot-2.md new file mode 100644 index 0000000000..2b0f1f0137 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/accept-payments-in-a-telegram-bot-2.md @@ -0,0 +1,541 @@ +--- +description: In this article, we'll create a simple Telegram bot for accepting payments in TON. +--- + +# Bot with own balance + +In this article, we'll create a simple Telegram bot for accepting payments in TON. + +## 🦄 What it looks like + +The bot will look like this: + +![image](/img/tutorials/bot1.png) + +### Source code + +Sources are available on GitHub: + +- https://github.com/Gusarich/ton-bot-example + +## 📖 What you'll learn + +You'll learn how to: + +- create a Telegram bot in Python3 using Aiogram +- work with SQLITE databases +- work with public TON API + +## ✍️ What you need to get started + +Install [Python](https://www.python.org/) if you haven't yet. + +Also you need these PyPi libraries: + +- aiogram +- requests + +You can install them with one command in the terminal. + +```bash +pip install aiogram==2.21 requests +``` + +## 🚀 Let's get started! + +Create a directory for our bot with four files in it: + +- `bot.py`—program to run a Telegram bot +- `config.py`—config file +- `db.py`—module to interact with the sqlite3 database +- `ton.py`— module to handle payments in TON + +The directory should look like this: + +``` +my_bot +├── bot.py +├── config.py +├── db.py +└── ton.py +``` + +Now let's begin writing code! + +## Config + +Let's start with `config.py` because it is the smallest one. We just need to set a few parameters in it. + +**config.py** + +```python +BOT_TOKEN = 'YOUR BOT TOKEN' +DEPOSIT_ADDRESS = 'YOUR DEPOSIT ADDRESS' +API_KEY = 'YOUR API KEY' +RUN_IN_MAINNET = True # Switch True/False to change mainnet to testnet + +if RUN_IN_MAINNET: + API_BASE_URL = 'https://toncenter.com' +else: + API_BASE_URL = 'https://testnet.toncenter.com' +``` + +Here you need to fill in the values in the first three lines: + +- `BOT_TOKEN` is your Telegram Bot token which you can get after [creating a bot](https://t.me/BotFather). +- `DEPOSIT_ADDRESS` is your project's wallet address which will accept all payments. You can just create a new TON Wallet and copy its address. +- `API_KEY` is your API key from TON Center which you can get in [this bot](https://t.me/tonapibot). + +You can also choose whether your bot will run on the testnet or the mainnet (4th line). + +That's all for the Config file, so we can move forward! + +## Database + +Now let's edit the `db.py` file that will work with the database of our bot. + +Import the sqlite3 library. + +```python +import sqlite3 +``` + +Initialize the database connection and cursor (you can choose any filename instead of `db.sqlite`). + +```python +con = sqlite3.connect('db.sqlite') +cur = con.cursor() +``` + +To store information about users (their balances in our case), create a table called "Users" with User ID and balance rows. + +```python +cur.execute('''CREATE TABLE IF NOT EXISTS Users ( + uid INTEGER, + balance INTEGER + )''') +con.commit() +``` + +Now we need to declare a few functions to work with the database. + +`add_user` function will be used to insert new users into the database. + +```python +def add_user(uid): + # new user always has balance = 0 + cur.execute(f'INSERT INTO Users VALUES ({uid}, 0)') + con.commit() +``` + +`check_user` function will be used to check if the user exists in the database or not. + +```python +def check_user(uid): + cur.execute(f'SELECT * FROM Users WHERE uid = {uid}') + user = cur.fetchone() + if user: + return True + return False +``` + +`add_balance` function will be used to increase the user's balance. + +```python +def add_balance(uid, amount): + cur.execute(f'UPDATE Users SET balance = balance + {amount} WHERE uid = {uid}') + con.commit() +``` + +`get_balance` function will be used to retrieve the user's balance. + +```python +def get_balance(uid): + cur.execute(f'SELECT balance FROM Users WHERE uid = {uid}') + balance = cur.fetchone()[0] + return balance +``` + +And that's all for the `db.py` file! + +Now we can use these four functions in other components of the bot to work with the database. + +## TON Center API + +In the `ton.py` file we'll declare a function that will process all new deposits, increase user balances, and notify users. + +### getTransactions method + +We'll use the TON Center API. Their docs are available here: +https://toncenter.com/api/v2/ + +We need the [getTransactions](https://toncenter.com/api/v2/#/accounts/get_transactions_getTransactions_get) method to get information about latest transactions of a given account. + +Let's have a look at what this method takes as input parameters and what it returns. + +There is only one mandatory input field `address`, but we also need the `limit` field to specify how many transactions we want to get in return. + +Now let's try to run this method on the [TON Center website](https://toncenter.com/api/v2/#/accounts/get_transactions_getTransactions_get) with any existing wallet address to understand what we should get from the output. + +```json +{ + "ok": true, + "result": [ + { + ... + }, + { + ... + } + ] +} +``` + +Well, so the `ok` field is set to `true` when everything is good, and we have an array `result` with the list of `limit` latest transactions. Now let's look at one single transaction: + +```json +{ + "@type": "raw.transaction", + "utime": 1666648337, + "data": "...", + "transaction_id": { + "@type": "internal.transactionId", + "lt": "32294193000003", + "hash": "ez3LKZq4KCNNLRU/G4YbUweM74D9xg/tWK0NyfuNcxA=" + }, + "fee": "105608", + "storage_fee": "5608", + "other_fee": "100000", + "in_msg": { + "@type": "raw.message", + "source": "EQBIhPuWmjT7fP-VomuTWseE8JNWv2q7QYfsVQ1IZwnMk8wL", + "destination": "EQBKgXCNLPexWhs2L79kiARR1phGH1LwXxRbNsCFF9doc2lN", + "value": "100000000", + "fwd_fee": "666672", + "ihr_fee": "0", + "created_lt": "32294193000002", + "body_hash": "tDJM2A4YFee5edKRfQWLML5XIJtb5FLq0jFvDXpv0xI=", + "msg_data": { + "@type": "msg.dataText", + "text": "SGVsbG8sIHdvcmxkIQ==" + }, + "message": "Hello, world!" + }, + "out_msgs": [] +} +``` + +We can see that information that can help us identify the exact transaction is stored in `transaction_id` field. We need the `lt` field from it to understand which transaction happened earlier and which happened later. + +The information about the coin transfer is in the `in_msg` field. We'll need `value` and `message` from it. + +Now we're ready to create a payment handler. + +### Sending API requests from code + +Let's begin with importing the required libraries and our two previous files: `config.py` and `db.py`. + +```python +import requests +import asyncio + +# Aiogram +from aiogram import Bot +from aiogram.types import ParseMode + +# We also need config and database here +import config +import db +``` + +Let's think about how payment processing can be implemented. + +We can call the API every few seconds and check if there are any new transactions to our wallet address. + +For that we need to know what the last processed transaction was. The simplest approach would be to just save info about that transaction in some file and update it every time we process a new transaction. + +What information about the transaction will we store in the file? Actually, we only need to store the `lt` value—logical time. +With that value we'll be able to understand what transactions we need to process. + +So we need to define a new async function; let's call it `start`. Why does this function need to be asynchronous? That is because the Aiogram library for Telegram bots is also asynchronous, and it'll be easier to work with async functions later. + +This is what our `start` function should look like: + +```python +async def start(): + try: + # Try to load last_lt from file + with open('last_lt.txt', 'r') as f: + last_lt = int(f.read()) + except FileNotFoundError: + # If file not found, set last_lt to 0 + last_lt = 0 + + # We need the Bot instance here to send deposit notifications to users + bot = Bot(token=config.BOT_TOKEN) + + while True: + # Here we will call API every few seconds and fetch new transactions. + ... +``` + +Now let's write the body of while loop. We need to call TON Center API there every few seconds. + +```python +while True: + # 2 Seconds delay between checks + await asyncio.sleep(2) + + # API call to TON Center that returns last 100 transactions of our wallet + resp = requests.get(f'{config.API_BASE_URL}/api/v2/getTransactions?' + f'address={config.DEPOSIT_ADDRESS}&limit=100&' + f'archival=true&api_key={config.API_KEY}').json() + + # If call was not successful, try again + if not resp['ok']: + continue + + ... +``` + +After the call with `requests.get`, we have a variable `resp` that contains the response from the API. `resp` is an object and `resp['result']` is a list with the last 100 transactions for our address. + +Now let's just iterate over these transactions and find the new ones. + +```python +while True: + ... + + # Iterating over transactions + for tx in resp['result']: + # LT is Logical Time and Hash is hash of our transaction + lt, hash = int(tx['transaction_id']['lt']), tx['transaction_id']['hash'] + + # If this transaction's logical time is lower than our last_lt, + # we already processed it, so skip it + + if lt <= last_lt: + continue + + # at this moment, `tx` is some new transaction that we haven't processed yet + ... +``` + +How do we process a new transaction? We need to: + +- understand which user sent it +- increase that user's balance +- notify the user about their deposit + +Here is the code that will do all of that: + +```python +while True: + ... + + for tx in resp['result']: + ... + # at this moment, `tx` is some new transaction that we haven't processed yet + + value = int(tx['in_msg']['value']) + if value > 0: + uid = tx['in_msg']['message'] + + if not uid.isdigit(): + continue + + uid = int(uid) + + if not db.check_user(uid): + continue + + db.add_balance(uid, value) + + await bot.send_message(uid, 'Deposit confirmed!\n' + f'*+{value / 1e9:.2f} TON*', + parse_mode=ParseMode.MARKDOWN) +``` + +Let's have a look at it and understand what it does. + +All the information about the coin transfer is in `tx['in_msg']`. We just need the 'value' and 'message' fields from it. + +First of all, we check if the value is greater than zero and only continue if it is. + +Then we expect the transfer to have a comment ( `tx['in_msg']['message']` ), to have a user ID from our bot, so we verify if it is a valid number and if that UID exists in our database. + +After these simple checks, we have a variable `value` with the deposit amount, and a variable `uid` with the ID of the user that made this deposit. So we can just add funds to their account and send a notification message. + +Also note that value is in nanotons by default, so we need to divide it by 1 billion. We do that in line with notification: +`{value / 1e9:.2f}` +Here we divide the value by `1e9` (1 billion) and leave only two digits after the decimal point to show it to the user in a friendly format. + +Great! The program can now process new transactions and notify users about deposits. But we should not forget about storing `lt` that we have used before. We must update the last `lt` because a newer transaction was processed. + +It's simple: + +```python +while True: + ... + for tx in resp['result']: + ... + # we have processed this tx + + # lt variable here contains LT of the last processed transaction + last_lt = lt + with open('last_lt.txt', 'w') as f: + f.write(str(last_lt)) +``` + +And that's all for the `ton.py` file! +Our bot is now 3/4 done; we only need to create a user interface with a few buttons in the bot itself. + +## Telegram bot + +### Initialization + +Open the `bot.py` file and import all the modules we need. + +```python +# Logging module +import logging + +# Aiogram imports +from aiogram import Bot, Dispatcher, types +from aiogram.dispatcher.filters import Text +from aiogram.types import ParseMode, ReplyKeyboardMarkup, KeyboardButton, \ + InlineKeyboardMarkup, InlineKeyboardButton +from aiogram.utils import executor + +# Local modules to work with the Database and TON Network +import config +import ton +import db +``` + +Let's set up logging to our program so that we can see what happens later for debugging. + +```python +logging.basicConfig(level=logging.INFO) +``` + +Now we need to initialize the bot object and its dispatcher with Aiogram. + +```python +bot = Bot(token=config.BOT_TOKEN) +dp = Dispatcher(bot) +``` + +Here we use `BOT_TOKEN` from our config that we made at the beginning of the tutorial. + +We initialized the bot but it's still empty. We must add some functions for interaction with the user. + +### Message handlers + +#### /start Command + +Let's begin with the `/start` and `/help` commands handler. This function will be called when the user launches the bot for the first time, restarts it, or uses the `/help` command. + +```python +@dp.message_handler(commands=['start', 'help']) +async def welcome_handler(message: types.Message): + uid = message.from_user.id # Not neccessary, just to make code shorter + + # If user doesn't exist in database, insert it + if not db.check_user(uid): + db.add_user(uid) + + # Keyboard with two main buttons: Deposit and Balance + keyboard = ReplyKeyboardMarkup(resize_keyboard=True) + keyboard.row(KeyboardButton('Deposit')) + keyboard.row(KeyboardButton('Balance')) + + # Send welcome text and include the keyboard + await message.answer('Hi!\nI am example bot ' + 'made for [this article](/develop/dapps/payment-processing/accept-payments-in-a-telegram-bot-2).\n' + 'My goal is to show how simple it is to receive ' + 'payments in Toncoin with Python.\n\n' + 'Use keyboard to test my functionality.', + reply_markup=keyboard, + parse_mode=ParseMode.MARKDOWN) +``` + +The welcome message can be anything you want. Keyboard buttons can be any text, but in this example they are labeled in the most clear way for our bot: `Deposit` and `Balance`. + +#### Balance button + +Now the user can start the bot and see the keyboard with two buttons. But after calling one of these, the user won't get any response because we didn't create any function for them. + +So let's add a function to request a balance. + +```python +@dp.message_handler(commands='balance') +@dp.message_handler(Text(equals='balance', ignore_case=True)) +async def balance_handler(message: types.Message): + uid = message.from_user.id + + # Get user balance from database + # Also don't forget that 1 TON = 1e9 (billion) Nanoton + user_balance = db.get_balance(uid) / 1e9 + + # Format balance and send to user + await message.answer(f'Your balance: *{user_balance:.2f} TON*', + parse_mode=ParseMode.MARKDOWN) +``` + +It's pretty simple. We just get the balance from the database and send the message to the user. + +#### Deposit button + +And what about the second `Deposit` button? Here is the function for it: + +```python +@dp.message_handler(commands='deposit') +@dp.message_handler(Text(equals='deposit', ignore_case=True)) +async def deposit_handler(message: types.Message): + uid = message.from_user.id + + # Keyboard with deposit URL + keyboard = InlineKeyboardMarkup() + button = InlineKeyboardButton('Deposit', + url=f'ton://transfer/{config.DEPOSIT_ADDRESS}&text={uid}') + keyboard.add(button) + + # Send text that explains how to make a deposit into bot to user + await message.answer('It is very easy to top up your balance here.\n' + 'Simply send any amount of TON to this address:\n\n' + f'`{config.DEPOSIT_ADDRESS}`\n\n' + f'And include the following comment: `{uid}`\n\n' + 'You can also deposit by clicking the button below.', + reply_markup=keyboard, + parse_mode=ParseMode.MARKDOWN) +``` + +What we do here is also easy to understand. + +Remember when in the `ton.py` file we were determining which user made a deposit by commenting with their UID? Now here in the bot we need to ask the user to send a transaction with a comment containing their UID. + +### Bot start + +The only thing we have to do now in `bot.py` is to launch the bot itself and also run the `start` function from `ton.py`. + +```python +if __name__ == '__main__': + # Create Aiogram executor for our bot + ex = executor.Executor(dp) + + # Launch the deposit waiter with our executor + ex.loop.create_task(ton.start()) + + # Launch the bot + ex.start_polling() +``` + +At this moment, we have written all the required code for our bot. If you did everything correctly, it must work when you run it with `python my-bot/bot.py` command in the terminal. + +If your bot doesn't work correctly, compare your code with code [from this repository](https://github.com/Gusarich/ton-bot-example). + +## References + +- Made for TON as part of [ton-footsteps/8](https://github.com/ton-society/ton-footsteps/issues/8) +- By Gusarich ([Telegram @Gusarich](https://t.me/Gusarich), [Gusarich on GitHub](https://github.com/Gusarich)) From 1038273402ebd689cbf39df849f47fa494e7cc9d Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:05 +0800 Subject: [PATCH 071/219] New translations accept-payments-in-a-telegram-bot-js.md (Chinese Simplified) --- .../accept-payments-in-a-telegram-bot-js.md | 483 ++++++++++++++++++ 1 file changed, 483 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/accept-payments-in-a-telegram-bot-js.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/accept-payments-in-a-telegram-bot-js.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/accept-payments-in-a-telegram-bot-js.md new file mode 100644 index 0000000000..043ea142a1 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/accept-payments-in-a-telegram-bot-js.md @@ -0,0 +1,483 @@ +--- +description: At the end of the tutorial, you will write a beautiful bot that will be able to accept payments for your product directly in TON. +--- + +# Bot for sales of dumplings + +In this article, we'll create a simple Telegram bot for accepting payments in TON. + +## 🦄 What it looks like + +At the end of the tutorial, you will write a beautiful bot that will be able to accept payments for your product directly in TON. + +The bot will look like this: + +![bot preview](/img/tutorials/js-bot-preview.jpg) + +## 📖 What you'll learn + +You'll learn how to: + +- Create a Telegram bot in NodeJS using grammY +- Work with public TON Center API + +> Why do we use grammY? +> Because grammY is a modern, young, high-level framework for comfortable & fast development of telegram bots on JS/TS/Deno, in addition to this grammY has great [documentation](https://grammy.dev) and an active community that can always help you. + +## ✍️ What you need to get started + +Install [NodeJS](https://nodejs.org/en/download/) if you haven't yet. + +Also you need these libraries: + +- grammy +- ton +- dotenv + +You can install them with one command in the terminal. + +```bash npm2yarn +npm install ton dotenv grammy @grammyjs/conversations +``` + +## 🚀 Let's get started! + +The structure of our project will look like this: + +``` +src + ├── bot + ├── start.js + ├── payment.js + ├── services + ├── ton.js + ├── app.js +.env +``` + +- `bot/start.js` & `bot/payment.js` - files with handlers for telegram bot +- `src/ton.js` - file with business logic related to TON +- `app.js` - file for initializing and launching the bot + +Now let's begin writing code! + +## Config + +Let's start with `.env`. We just need to set a few parameters in it. + +**.env** + +``` +BOT_TOKEN= +TONCENTER_TOKEN= +NETWORK= +OWNER_WALLET= +``` + +Here you need to fill in the values in the first four lines: + +- `BOT_TOKEN` is your Telegram Bot token which you can get after [creating a bot](https://t.me/BotFather). +- `OWNER_WALLET` is your project's wallet address which will accept all payments. You can just create a new TON wallet and copy its address. +- `API_KEY` is your API key from TON Center which you can get from [@tonapibot](https://t.me/tonapibot)/[@tontestnetapibot](https://t.me/tontestnetapibot) for the mainnet and testnet, respectively. +- `NETWORK` is about on what network your bot will run - testnet or mainnet + +That's all for the config file, so we can move forward! + +## TON Center API + +In the `src/services/ton.py` file we'll declare a functions to verify the existence of a transaction and generate links for a quick transition to the wallet application for payment + +### Getting the latest wallet transactions + +Our task is to check the availability of the transaction we need from a certain wallet. + +We will solve it like this: + +1. We will receive the last transactions that were received to our wallet. Why ours? In this case, we do not have to worry about what the user's wallet address is, we do not have to confirm that it is his wallet, we do not have to store this wallet anywhere. +2. Sort and leave only incoming transactions +3. Let's go through all the transactions, and each time we will check whether the comment and the amount are equal to the data that we have +4. celebrating the solution of our problem🎉 + +#### Getting the latest transactions + +If we use the TON Center API, then we can refer to their [documentation](https://toncenter.com/api/v2/) and find a method that ideally solves our problem - [getTransactions](https://toncenter.com/api/v2/#/accounts/get_transactions_getTransactions_get) + +One parameter is enough for us to get transactions - the wallet address for accepting payments, but we will also use the limit parameter in order to limit the issuance of transactions to 100 pieces. + +Let's try to call a test request for the `EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N` address (by the way, this is the TON Foundation address) + +```bash +curl -X 'GET' \ + 'https://toncenter.com/api/v2/getTransactions?address=EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N&limit=100' \ + -H 'accept: application/json' +``` + +Great, now we have a list of transactions on hand in ["result"], now let's take a closer look at 1 transaction + +```json +{ + "@type": "raw.transaction", + "utime": 1667148685, + "data": "*data here*", + "transaction_id": { + "@type": "internal.transactionId", + "lt": "32450206000003", + "hash": "rBHOq/T3SoqWta8IXL8THxYqTi2tOkBB8+9NK0uKWok=" + }, + "fee": "106508", + "storage_fee": "6508", + "other_fee": "100000", + "in_msg": { + "@type": "raw.message", + "source": "EQA0i8-CdGnF_DhUHHf92R1ONH6sIA9vLZ_WLcCIhfBBXwtG", + "destination": "EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N", + "value": "1000000", + "fwd_fee": "666672", + "ihr_fee": "0", + "created_lt": "32450206000002", + "body_hash": "Fl+2CzHNgilBE4RKhfysLU8VL8ZxYWciCRDva2E19QQ=", + "msg_data": { + "@type": "msg.dataText", + "text": "aGVsbG8g8J+Riw==" + }, + "message": "hello 👋" + }, + "out_msgs": [] + } +``` + +From this json file, we can understand some information that can be usefull for us: + +- This is an incoming transaction, since the "out_msgs" field is empty +- We can also get a comment of the transaction, its sender and the transaction amount + +Now we're ready to create a transaction checker + +### Work with TON + +Let's start by importing the necessary library TON + +```js +import { HttpApi, fromNano, toNano } from "ton"; +``` + +Let's think about how to check if the user has sent the transaction we need. + +Everything is elementary simple. We can just sort only incoming transactions to our wallet, and then go through the last 100 transactions, and if a transaction is found that has the same comment and amount, then we have found the transaction we need! + +Let's start with initializing the http client, for convenient work with TON + +```js +export async function verifyTransactionExistance(toWallet, amount, comment) { + const endpoint = + process.env.NETWORK === "mainnet" + ? "https://toncenter.com/api/v2/jsonRPC" + : "https://testnet.toncenter.com/api/v2/jsonRPC"; + const httpClient = new HttpApi( + endpoint, + {}, + { apiKey: process.env.TONCENTER_TOKEN } + ); +``` + +Here we simply generate the endpoint url based on which network is selected in the config. And after that we initialize the http client. + +So, now we can get the last 100 transactions from the owner's wallet + +```js +const transactions = await httpClient.getTransactions(toWallet, { + limit: 100, + }); +``` + +and filter, leaving only incoming transactions (if the out_msgs of transaction is empty, we leave it) + +```js +let incomingTransactions = transactions.filter( + (tx) => Object.keys(tx.out_msgs).length === 0 + ); +``` + +Now we just have to go through all the transactions, and provided that the comment and the transaction value match, we return true + +```js + for (let i = 0; i < incomingTransactions.length; i++) { + let tx = incomingTransactions[i]; + // Skip the transaction if there is no comment in it + if (!tx.in_msg.msg_data.text) { + continue; + } + + // Convert transaction value from nano + let txValue = fromNano(tx.in_msg.value); + // Get transaction comment + let txComment = tx.in_msg.message + + if (txComment === comment && txValue === value.toString()) { + return true; + } + } + + return false; +``` + +Note that value is in nanotons by default, so we need to divide it by 1 billion or we can just use `fromNano` method from the TON library. +And that's all for the `verifyTransactionExistance` function! + +Now we can create function to generate link for a quick transition to the wallet application for payment + +```js +export function generatePaymentLink(toWallet, amount, comment, app) { + if (app === "tonhub") { + return `https://tonhub.com/transfer/${toWallet}?amount=${toNano( + amount + )}&text=${comment}`; + } + return `https://app.tonkeeper.com/transfer/${toWallet}?amount=${toNano( + amount + )}&text=${comment}`; +} +``` + +All we need is just to substitute the transaction parameters in the URL. Without forgetting to transfer the value of the transaction to nano. + +## Telegram bot + +### Initialization + +Open the `app.js` file and import all the handlers and modules we need. + +```js +import dotenv from "dotenv"; +import { Bot, session } from "grammy"; +import { conversations, createConversation } from "@grammyjs/conversations"; + +import { + startPaymentProcess, + checkTransaction, +} from "./bot/handlers/payment.js"; +import handleStart from "./bot/handlers/start.js"; +``` + +Let's set up dotenv module to comfy work with environment variables that we set at .env file + +```js +dotenv.config(); +``` + +After that we create a function that will run our project. In order for our bot not to stop if any errors appear, we add this code + +```js +async function runApp() { + console.log("Starting app..."); + + // Handler of all errors, in order to prevent the bot from stopping + process.on("uncaughtException", function (exception) { + console.log(exception); + }); +``` + +Now initialize the bot and the necessary plugins + +```js + // Initialize the bot + const bot = new Bot(process.env.BOT_TOKEN); + + // Set the initial data of our session + bot.use(session({ initial: () => ({ amount: 0, comment: "" }) })); + // Install the conversation plugin + bot.use(conversations()); + + bot.use(createConversation(startPaymentProcess)); +``` + +Here we use `BOT_TOKEN` from our config that we made at the beginning of the tutorial. + +We initialized the bot but it's still empty. We must add some functions for interaction with the user. + +```js + // Register all handelrs + bot.command("start", handleStart); + bot.callbackQuery("buy", async (ctx) => { + await ctx.conversation.enter("startPaymentProcess"); + }); + bot.callbackQuery("check_transaction", checkTransaction); +``` + +Reacting to the command/start, the handleStart function will be executed. If the user clicks on the button with callback_data equal to "buy", we will start our "conversation", which we registered just above. And when we click on the button with callback_data equal to "check_transaction", we will execute the checkTransaction function. + +And all that remains for us is to launch our bot and output a log about a successful launch + +```js + // Start bot + await bot.init(); + bot.start(); + console.info(`Bot @${bot.botInfo.username} is up and running`); +``` + +### Message handlers + +#### /start Command + +Let's begin with the `/start` command handler. This function will be called when the user launches the bot for the first time, restarts it + +```js +import { InlineKeyboard } from "grammy"; + +export default async function handleStart(ctx) { + const menu = new InlineKeyboard() + .text("Buy dumplings🥟", "buy") + .row() + .url("Article with a detailed explanation of the bot's work", "/develop/dapps/payment-processing/accept-payments-in-a-telegram-bot-js/"); + + await ctx.reply( + `Hello stranger! +Welcome to the best Dumplings Shop in the world and concurrently an example of accepting payments in TON`, + { reply_markup: menu, parse_mode: "HTML" } + ); +} +``` + +Here we first import the InlineKeyboard from the grammy module. After that, we create an inline keyboard in the handler with an offer to buy dumplings and a link to this article (a bit of recursion here😁). +.row() stands for the transfer of the next button to a new line. +After that, we send a welcome message with the text (important, I use html markup in my message to decorate it) along with the created keyboard +The welcome message can be anything you want. + +#### Payment process + +As always, we will start our file with the necessary imports + +```js +import { InlineKeyboard } from "grammy"; + +import { + generatePaymentLink, + verifyTransactionExistance, +} from "../../services/ton.js"; +``` + +After that, we will create a startPaymentProcess handler, which we have already registered in the app.js for execution when a certain button is pressed + +In the telegram when you click on the inline button a spinning watch appears in order to remove them, we respond to the callback + +```js + await ctx.answerCallbackQuery(); +``` + +After that, we need to send the user a picture of dumplings, ask him to send the number of dumplings that he wants to buy. And we are waiting for him to enter this number + +```js + await ctx.replyWithPhoto( + "https://telegra.ph/file/bad2fd69547432e16356f.jpg", + { + caption: + "Send the number of portions of yummy dumplings you want buy\nP.S. Current price for 1 portion: 3 TON", + } + ); + + // Wait until the user enters the number + const count = await conversation.form.number(); +``` + +Now we calculate the total amount of the order and generate a random string, which we will use to comment on the transaction and add the dumplings postfix + +```js + // Get the total cost: multiply the number of portions by the price of the 1 portion + const amount = count * 3; + // Generate random comment + const comment = Math.random().toString(36).substring(2, 8) + "dumplings"; +``` + +And we save the resulting data to the session so that we can get this data in the next handler. + +```js + conversation.session.amount = amount; + conversation.session.comment = comment; +``` + +We generate links to go to a quick payment and create an inline keyboard + +```js +const tonhubPaymentLink = generatePaymentLink( + process.env.OWNER_WALLET, + amount, + comment, + "tonhub" + ); + const tonkeeperPaymentLink = generatePaymentLink( + process.env.OWNER_WALLET, + amount, + comment, + "tonkeeper" + ); + + const menu = new InlineKeyboard() + .url("Click to pay in TonHub", tonhubPaymentLink) + .row() + .url("Click to pay in Tonkeeper", tonkeeperPaymentLink) + .row() + .text(`I sent ${amount} TON`, "check_transaction"); +``` + +And we send our message with the keyboard, where we ask the user to send a transaction to our wallet address with a randomly generated comment + +```js + await ctx.reply( + ` +Fine, all you have to do is transfer ${amount} TON to the wallet ${process.env.OWNER_WALLET} with the comment ${comment}. + +WARNING: I am currently working on ${process.env.NETWORK} + +P.S. You can conveniently make a transfer by clicking on the appropriate button below and confirm the transaction in the offer`, + { reply_markup: menu, parse_mode: "HTML" } + ); +} +``` + +Now all we have to do is create a handler to check for the presence of a transaction + +```js +export async function checkTransaction(ctx) { + await ctx.answerCallbackQuery({ + text: "Wait a bit, I need to check the availability of your transaction", + }); + + if ( + await verifyTransactionExistance( + process.env.OWNER_WALLET, + ctx.session.amount, + ctx.session.comment + ) + ) { + const menu = new InlineKeyboard().text("Buy more dumplings🥟", "buy"); + + await ctx.reply("Thank you so much. Enjoy your meal!", { + reply_markup: menu, + }); + + // Reset the session data + ctx.session.amount = 0; + ctx.session.comment = ""; + } else { + await ctx.reply("I didn't receive your transaction, wait a bit"); + } +} +``` + +All we are doing here is just checking for a transaction, and provided that it exists, we tell the user about it and reset the data in the session + +### Bot start + +To start use this command: + +```bash npm2yarn +npm run app +``` + +If your bot doesn't work correctly, compare your code with code [from this repository](https://github.com/coalus/DumplingShopBot). If it didn't help, feel free to write me in telegram. You can find my telegram account below + +## References + +- Made for TON as part of [ton-footsteps/58](https://github.com/ton-society/ton-footsteps/issues/58) +- By Coalus ([Telegram @coalus](https://t.me/coalus), [Coalus on GitHub](https://github.com/coalus)) +- [Bot Sources](https://github.com/coalus/DumplingShopBot) From 5ef3b7ecdd1f7089b02dba16877287b2c9447785 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:05 +0800 Subject: [PATCH 072/219] New translations accept-payments-in-a-telegram-bot.md (Chinese Simplified) --- .../accept-payments-in-a-telegram-bot.md | 867 ++++++++++++++++++ 1 file changed, 867 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/accept-payments-in-a-telegram-bot.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/accept-payments-in-a-telegram-bot.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/accept-payments-in-a-telegram-bot.md new file mode 100644 index 0000000000..96c1e95207 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/accept-payments-in-a-telegram-bot.md @@ -0,0 +1,867 @@ +--- +description: In this article, we'll guide you through the process of accepting payments in a Telegram bot. +--- + +# Storefront bot with payments in TON + +In this article, we'll guide you through the process of accepting payments in a Telegram bot. + +## 📖 What you'll learn + +In this article, you'll learn how to: + +- create a Telegram bot using Python + Aiogram +- work with the public TON API (TON Center) +- work with SQlite database + +And finally: how to accept payments in a Telegram bot with the knowledge from previous steps. + +## 📚 Before we begin + +Make sure you have installed the latest version of Python and have installed the following packages: + +- aiogram +- requests +- sqlite3 + +## 🚀 Let's get started! + +We'll follow the order below: + +1. Work with SQlite database +2. Work with the public TON API (TON Center) +3. Create a Telegram bot using Python + Aiogram +4. Profit! + +Let's create the following four files in our project directory: + +``` +telegram-bot +├── config.json +├── main.py +├── api.py +└── db.py +``` + +## Config + +In `config.json` we'll store our bot token and our public TON API key. + +```json +{ + "BOT_TOKEN": "Your bot token", + "MAINNET_API_TOKEN": "Your mainnet api token", + "TESTNET_API_TOKEN": "Your testnet api token", + "MAINNET_WALLET": "Your mainnet wallet", + "TESTNET_WALLET": "Your testnet wallet", + "WORK_MODE": "testnet" +} +``` + +In `config.json` we decide which network we'll use: `testnet` or `mainnet`. + +## Database + +### Create a database + +This example uses a local Sqlite database. + +Create `db.py`. + +To start working with the database, we need to import the sqlite3 module +and some modules for working with time. + +```python +import sqlite3 +import datetime +import pytz +``` + +- `sqlite3`—module for working with sqlite database +- `datetime`—module for working with time +- `pytz`—module for working with timezones + +Next, we need to create a connection to the database and a cursor to work with it: + +```python +locCon = sqlite3.connect('local.db', check_same_thread=False) +cur = locCon.cursor() +``` + +If the database does not exist, it will be created automatically. + +Now we can create tables. We have two of them. + +#### Transactions: + +```sql +CREATE TABLE transactions ( + source VARCHAR (48) NOT NULL, + hash VARCHAR (50) UNIQUE + NOT NULL, + value INTEGER NOT NULL, + comment VARCHAR (50) +); +``` + +- `source`—payer's wallet address +- `hash`—transaction hash +- `value`—transaction value +- `comment`—transaction comment + +#### Users: + +```sql +CREATE TABLE users ( + id INTEGER UNIQUE + NOT NULL, + username VARCHAR (33), + first_name VARCHAR (300), + wallet VARCHAR (50) DEFAULT none +); +``` + +- `id`—Telegram user ID +- `username`—Telegram username +- `first_name`—Telegram user's first name +- `wallet`—user wallet address + +In the `users` table we store users :) Their Telegram ID, @username, +first name, and wallet. The wallet is added to the database on the first +successful payment. + +The `transactions` table stores verified transactions. +To verify a transaction, we need the hash, source, value and comment. + +To create these tables, we need to run the following function: + +```python +cur.execute('''CREATE TABLE IF NOT EXISTS transactions ( + source VARCHAR (48) NOT NULL, + hash VARCHAR (50) UNIQUE + NOT NULL, + value INTEGER NOT NULL, + comment VARCHAR (50) +)''') +locCon.commit() + +cur.execute('''CREATE TABLE IF NOT EXISTS users ( + id INTEGER UNIQUE + NOT NULL, + username VARCHAR (33), + first_name VARCHAR (300), + wallet VARCHAR (50) DEFAULT none +)''') +locCon.commit() +``` + +This code will create the tables if they are not already created. + +### Work with database + +Let's analyze the situation: +User made a transaction. How to verify it? How to make sure that the same transaction is not confirmed twice? + +There is a body_hash in transactions, with the help of which we can easily understand whether there is a transaction in the database or not. + +We add transactions to the database in which we are sure. The `check_transaction` function checks whether the found transaction is in the database or not. + +`add_v_transaction` adds transaction to the transactions table. + +```python +def add_v_transaction(source, hash, value, comment): + cur.execute("INSERT INTO transactions (source, hash, value, comment) VALUES (?, ?, ?, ?)", + (source, hash, value, comment)) + locCon.commit() +``` + +```python +def check_transaction(hash): + cur.execute(f"SELECT hash FROM transactions WHERE hash = '{hash}'") + result = cur.fetchone() + if result: + return True + return False +``` + +`check_user` checks if the user is in the database and adds him if not. + +```python +def check_user(user_id, username, first_name): + cur.execute(f"SELECT id FROM users WHERE id = '{user_id}'") + result = cur.fetchone() + + if not result: + cur.execute("INSERT INTO users (id, username, first_name) VALUES (?, ?, ?)", + (user_id, username, first_name)) + locCon.commit() + return False + return True +``` + +The user can store a wallet in the table. It is added with the first successful purchase. The `v_wallet` function checks if the user has an associated wallet. If there is, then returns it. If not, then adds. + +```python +def v_wallet(user_id, wallet): + cur.execute(f"SELECT wallet FROM users WHERE id = '{user_id}'") + result = cur.fetchone() + if result[0] == "none": + cur.execute( + f"UPDATE users SET wallet = '{wallet}' WHERE id = '{user_id}'") + locCon.commit() + return True + else: + return result[0] +``` + +`get_user_wallet` simply returns the user's wallet. + +```python +def get_user_wallet(user_id): + cur.execute(f"SELECT wallet FROM users WHERE id = '{user_id}'") + result = cur.fetchone() + return result[0] +``` + +`get_user_payments` returns the user's payments list. +This function checks if the user has a wallet. If he has, then it returns the payment list. + +```python +def get_user_payments(user_id): + wallet = get_user_wallet(user_id) + + if wallet == "none": + return "You have no wallet" + else: + cur.execute(f"SELECT * FROM transactions WHERE source = '{wallet}'") + result = cur.fetchall() + tdict = {} + tlist = [] + try: + for transaction in result: + tdict = { + "value": transaction[2], + "comment": transaction[3], + } + tlist.append(tdict) + return tlist + + except: + return False +``` + +## API + +_We have the ability to interact with the blockchain using third-party APIs provided by some network members. With these services, developers can skip the step of running their own node and customizing their API._ + +### Required Requests + +In fact, what do we need to confirm that the user has transferred the required amount to us? + +We just need to look at the latest incoming transfers to our wallet and find among them a transaction from the right address with the right amount (and possibly a unique comment). +For all of this, TON Center has a `getTransactions` method. + +### getTransactions + +By default, if we apply it, we will get the last 10 transactions. However, we can also indicate that we need more, but this will slightly increase the time of a response. And, most likely, you do not need so much. + +If you want more, then each transaction has `lt` and `hash`. You can look at, for example, 30 transactions and if the right one was not found among them, then take `lt` and `hash` from the last one and add them to the request. + +So you get the next 30 transactions and so on. + +For example, there is a wallet in the test network `EQAVKMzqtrvNB2SkcBONOijadqFZ1gMdjmzh1Y3HB1p_zai5`, it has some transactions: + +Using a [query](https://testnet.toncenter.com/api/v2/getTransactions?address=EQAVKMzqtrvNB2SkcBONOijadqFZ1gMdjmzh1Y3HB1p_zai5\&limit=2\&to_lt=0\&archival=true) we will get the response that contains two transactions (some of the information that is not needed now has been hidden, you can see the full answer at the link above). + +```json +{ + "ok": true, + "result": [ + { + "transaction_id": { + // highlight-next-line + "lt": "1944556000003", + // highlight-next-line + "hash": "swpaG6pTBXwYI2024NAisIFp59Fw3k1DRQ5fa5SuKAE=" + }, + "in_msg": { + "source": "EQCzQJJBAQ-FrEFcvxO5sNxhV9CaOdK9CCfq2yCBnwZ4aJ9R", + "destination": "EQAVKMzqtrvNB2SkcBONOijadqFZ1gMdjmzh1Y3HB1p_zai5", + "value": "1000000000", + "body_hash": "kBfGYBTkBaooeZ+NTVR0EiVGSybxQdb/ifXCRX5O7e0=", + "message": "Sea breeze 🌊" + }, + "out_msgs": [] + }, + { + "transaction_id": { + // highlight-next-line + "lt": "1943166000003", + // highlight-next-line + "hash": "hxIQqn7lYD/c/fNS7W/iVsg2kx0p/kNIGF6Ld0QEIxk=" + }, + "in_msg": { + "source": "EQCzQJJBAQ-FrEFcvxO5sNxhV9CaOdK9CCfq2yCBnwZ4aJ9R", + "destination": "EQAVKMzqtrvNB2SkcBONOijadqFZ1gMdjmzh1Y3HB1p_zai5", + "value": "1000000000", + "body_hash": "7iirXn1RtliLnBUGC5umIQ6KTw1qmPk+wwJ5ibh9Pf0=", + "message": "Spring forest 🌲" + }, + "out_msgs": [] + } + ] +} +``` + +We have received the last two transactions from this address. When adding `lt` and `hash` to the query, we will again receive two transactions. However, the second one will become the next one in a row. That is, we will get the second and third transactions for this address. + +```json +{ + "ok": true, + "result": [ + { + "transaction_id": { + "lt": "1943166000003", + "hash": "hxIQqn7lYD/c/fNS7W/iVsg2kx0p/kNIGF6Ld0QEIxk=" + }, + "in_msg": { + "source": "EQCzQJJBAQ-FrEFcvxO5sNxhV9CaOdK9CCfq2yCBnwZ4aJ9R", + "destination": "EQAVKMzqtrvNB2SkcBONOijadqFZ1gMdjmzh1Y3HB1p_zai5", + "value": "1000000000", + "body_hash": "7iirXn1RtliLnBUGC5umIQ6KTw1qmPk+wwJ5ibh9Pf0=", + "message": "Spring forest 🌲" + }, + "out_msgs": [] + }, + { + "transaction_id": { + "lt": "1845458000003", + "hash": "k5U9AwIRNGhC10hHJ3MBOPT//bxAgW5d9flFiwr1Sao=" + }, + "in_msg": { + "source": "EQCzQJJBAQ-FrEFcvxO5sNxhV9CaOdK9CCfq2yCBnwZ4aJ9R", + "destination": "EQAVKMzqtrvNB2SkcBONOijadqFZ1gMdjmzh1Y3HB1p_zai5", + "value": "1000000000", + "body_hash": "XpTXquHXP64qN6ihHe7Tokkpy88tiL+5DeqIrvrNCyo=", + "message": "Second" + }, + "out_msgs": [] + } + ] +} +``` + +The request will look like [this.](https://testnet.toncenter.com/api/v2/getTransactions?address=EQAVKMzqtrvNB2SkcBONOijadqFZ1gMdjmzh1Y3HB1p_zai5\&limit=2\<=1943166000003\&hash=hxIQqn7lYD%2Fc%2FfNS7W%2FiVsg2kx0p%2FkNIGF6Ld0QEIxk%3D\&to_lt=0\&archival=true) + +We will also need a method `detectAddress`. + +Here is an example of a Tonkeeper wallet address on testnet: `kQCzQJJBAQ-FrEFcvxO5sNxhV9CaOdK9CCfq2yCBnwZ4aCTb`. If we look for the transaction in the explorer, instead of the above address, there is: `EQCzQJJBAQ-FrEFcvxO5sNxhV9CaOdK9CCfq2yCBnwZ4aJ9R`. + +This method returns us the “right” address. + +```json +{ + "ok": true, + "result": { + "raw_form": "0:b3409241010f85ac415cbf13b9b0dc6157d09a39d2bd0827eadb20819f067868", + "bounceable": { + "b64": "EQCzQJJBAQ+FrEFcvxO5sNxhV9CaOdK9CCfq2yCBnwZ4aJ9R", + // highlight-next-line + "b64url": "EQCzQJJBAQ-FrEFcvxO5sNxhV9CaOdK9CCfq2yCBnwZ4aJ9R" + }, + "non_bounceable": { + "b64": "UQCzQJJBAQ+FrEFcvxO5sNxhV9CaOdK9CCfq2yCBnwZ4aMKU", + "b64url": "UQCzQJJBAQ-FrEFcvxO5sNxhV9CaOdK9CCfq2yCBnwZ4aMKU" + } + } +} +``` + +We need `b64url`. + +This method allows us to validate the user's address. + +For the most part, that's all we need. + +### API requests and what to do with them + +Let's go back to the IDE. Create the file `api.py`. + +Import the necessary libraries. + +```python +import requests +import json +# We import our db module, as it will be convenient to add from here +# transactions to the database +import db +``` + +- `requests`—to make requests to the API +- `json`—to work with json +- `db`—to work with our sqlite database + +Let's create two variables for storing the start of the requests. + +```python +# This is the beginning of our requests +MAINNET_API_BASE = "https://toncenter.com/api/v2/" +TESTNET_API_BASE = "https://testnet.toncenter.com/api/v2/" +``` + +Get all API tokens and wallets from the config.json file. + +```python +# Find out which network we are working on +with open('config.json', 'r') as f: + config_json = json.load(f) + MAINNET_API_TOKEN = config_json['MAINNET_API_TOKEN'] + TESTNET_API_TOKEN = config_json['TESTNET_API_TOKEN'] + MAINNET_WALLET = config_json['MAINNET_WALLET'] + TESTNET_WALLET = config_json['TESTNET_WALLET'] + WORK_MODE = config_json['WORK_MODE'] +``` + +Depending on the network, we take the necessary data. + +```python +if WORK_MODE == "mainnet": + API_BASE = MAINNET_API_BASE + API_TOKEN = MAINNET_API_TOKEN + WALLET = MAINNET_WALLET +else: + API_BASE = TESTNET_API_BASE + API_TOKEN = TESTNET_API_TOKEN + WALLET = TESTNET_WALLET +``` + +Our first request function `detectAddress`. + +```python +def detect_address(address): + url = f"{API_BASE}detectAddress?address={address}&api_key={API_TOKEN}" + r = requests.get(url) + response = json.loads(r.text) + try: + return response['result']['bounceable']['b64url'] + except: + return False +``` + +At the input, we have the estimated address, and at the output, we have either the "correct" address necessary for us to do further work or False. + +You may notice that an API key has appeared at the end of the request. It is needed to remove the limit on the number of requests to the API. Without it, we are limited to one request per second. + +Here is next function for `getTransactions`: + +```python +def get_address_transactions(): + url = f"{API_BASE}getTransactions?address={WALLET}&limit=30&archival=true&api_key={API_TOKEN}" + r = requests.get(url) + response = json.loads(r.text) + return response['result'] +``` + +This function returns the last 30 transactions to our `WALLET`. + +Here you can see `archival=true`. It is needed so that we only take transactions from a node with a complete history of the blockchain. + +At the output, we get a list of transactions—[{0},{1},{…},{29}]. List of dictionaries in short. + +And finally the last function: + +```python +def find_transaction(user_wallet, value, comment): + # Get the last 30 transactions + transactions = get_address_transactions() + for transaction in transactions: + # Select the incoming "message" - transaction + msg = transaction['in_msg'] + if msg['source'] == user_wallet and msg['value'] == value and msg['message'] == comment: + # If all the data match, we check that this transaction + # we have not verified before + t = db.check_transaction(msg['body_hash']) + if t == False: + # If not, we write in the table to the verified + # and return True + db.add_v_transaction( + msg['source'], msg['body_hash'], msg['value'], msg['message']) + print("find transaction") + print( + f"transaction from: {msg['source']} \nValue: {msg['value']} \nComment: {msg['message']}") + return True + # If this transaction is already verified, we check the rest, we can find the right one + else: + pass + # If the last 30 transactions do not contain the required one, return False + # Here you can add code to see the next 29 transactions + # However, within the scope of the Example, this would be redundant. + return False +``` + +At the input are the “correct” wallet address, amount and comment. If the intended incoming transaction is found, the output is True; otherwise, it is False. + +## Telegram bot + +First, let's create the basis for a bot. + +### Imports + +In this part, we will import the necessary libraries. + +From `aiogram` we need `Bot`, `Dispatcher`, `types` and `executor`. + +```python +from aiogram import Bot, Dispatcher, executor, types +``` + +`MemoryStorage` is needed for the temporary storage of information. + +`FSMContext`, `State`, and `StatesGroup` are needed for working with the state machine. + +```python +from aiogram.contrib.fsm_storage.memory import MemoryStorage +from aiogram.dispatcher import FSMContext +from aiogram.dispatcher.filters.state import State, StatesGroup +``` + +`json` is needed to work with json files. `logging` is needed to log errors. + +```python +import json +import logging +``` + +`api` and `db` are our own files which we will fill in later. + +```python +import db +import api +``` + +### Config setup + +It is recommended that you store data such as `BOT_TOKEN` and your wallets for receiving payments in a separate file called `config.json` for convenience. + +```json +{ + "BOT_TOKEN": "Your bot token", + "MAINNET_API_TOKEN": "Your mainnet api token", + "TESTNET_API_TOKEN": "Your testnet api token", + "MAINNET_WALLET": "Your mainnet wallet", + "TESTNET_WALLET": "Your testnet wallet", + "WORK_MODE": "testnet" +} +``` + +#### Bot token + +`BOT_TOKEN` is your Telegram bot token from [@BotFather](https://t.me/BotFather) + +#### Working mode + +In the `WORK_MODE` key, we will define the bot's mode of operation—in the test or main network; `testnet` or `mainnet` respectively. + +#### API tokens + +API tokens for `*_API_TOKEN` can be obtained in the [TON Center](https://toncenter.com/) bots: + +- for mainnet — [@tonapibot](https://t.me/tonapibot) +- for testnet — [@tontestnetapibot](https://t.me/tontestnetapibot) + +#### Connect config to our bot + +Next, we finish setting up the bot. + +Get the token for the bot to work from `config.json` : + +```python +with open('config.json', 'r') as f: + config_json = json.load(f) + # highlight-next-line + BOT_TOKEN = config_json['BOT_TOKEN'] + # put wallets here to receive payments + MAINNET_WALLET = config_json['MAINNET_WALLET'] + TESTNET_WALLET = config_json['TESTNET_WALLET'] + WORK_MODE = config_json['WORK_MODE'] + +if WORK_MODE == "mainnet": + WALLET = MAINNET_WALLET +else: + # By default, the bot will run on the testnet + WALLET = TESTNET_WALLET +``` + +### Logging and bot setup + +```python +logging.basicConfig(level=logging.INFO) +bot = Bot(token=BOT_TOKEN, parse_mode=types.ParseMode.HTML) +dp = Dispatcher(bot, storage=MemoryStorage()) +``` + +### States + +We need States to split the bot workflow into stages. We can specialize each stage for a specific task. + +```python +class DataInput (StatesGroup): + firstState = State() + secondState = State() + WalletState = State() + PayState = State() +``` + +For details and examples see the [Aiogram documentation](https://docs.aiogram.dev/en/latest/). + +### Message handlers + +This is the part where we will write the bot interaction logic. + +We'll be using two types of handlers: + +- `message_handler` is used to handle messages from user. +- `callback_query_handler` is used to handle callbacks from inline keyboards. + +If we want to handle a message from the user, we will use `message_handler` by placing `@dp.message_handler` decorator above the function. In this case, the function will be called when the user sends a message to the bot. + +In the decorator, we can specify the conditions under which the function will be called. For example, if we want the function to be called only when the user sends a message with the text `/start`, then we will write the following: + +``` +@dp.message_handler(commands=['start']) +``` + +Handlers need to be assigned to an async function. In this case, we will use `async def` syntax. The `async def` syntax is used to define the function that will be called asynchronously. + +#### /start + +Let's start with `/start` command handler. + +```python +@dp.message_handler(commands=['start'], state='*') +async def cmd_start(message: types.Message): + await message.answer(f"WORKMODE: {WORK_MODE}") + # check if user is in database. if not, add him + isOld = db.check_user( + message.from_user.id, message.from_user.username, message.from_user.first_name) + # if user already in database, we can address him differently + if isOld == False: + await message.answer(f"You are new here, {message.from_user.first_name}!") + await message.answer(f"to buy air send /buy") + else: + await message.answer(f"Welcome once again, {message.from_user.first_name}!") + await message.answer(f"to buy more air send /buy") + await DataInput.firstState.set() +``` + +In the decorator of this handler we see `state='*'`. This means that this handler will be called regardless of the state of bot. If we want the handler to be called only when the bot is in a specific state, we will write `state=DataInput.firstState`. In this case, the handler will be called only when the bot is in the `firstState` state. + +After the user sends `/start` command, the bot will check if the user is in database using `db.check_user` function. If not, it will add him. This function will also return the bool value and we can use it to address the user differently. After that, the bot will set the state to `firstState`. + +#### /cancel + +Next is the /cancel command handler. It is needed to return to the `firstState` state. + +```python +@dp.message_handler(commands=['cancel'], state="*") +async def cmd_cancel(message: types.Message): + await message.answer("Canceled") + await message.answer("/start to restart") + await DataInput.firstState.set() +``` + +#### /buy + +And, of course, `/buy` command handler. In this example we will sell different types of air. We will use the reply keyboard to choose the type of air. + +```python +# /buy command handler +@dp.message_handler(commands=['buy'], state=DataInput.firstState) +async def cmd_buy(message: types.Message): + # reply keyboard with air types + keyboard = types.ReplyKeyboardMarkup( + resize_keyboard=True, one_time_keyboard=True) + keyboard.add(types.KeyboardButton('Just pure 🌫')) + keyboard.add(types.KeyboardButton('Spring forest 🌲')) + keyboard.add(types.KeyboardButton('Sea breeze 🌊')) + keyboard.add(types.KeyboardButton('Fresh asphalt 🛣')) + await message.answer(f"Choose your air: (or /cancel)", reply_markup=keyboard) + await DataInput.secondState.set() +``` + +So, when a user sends `/buy` command, the bot sends him a reply keyboard with air types. After the user chooses the type of air, the bot will set the state to `secondState`. + +This handler will work only when `secondState` is set and will be waiting for a message from the user with the air type. In this case, we need to store the air type that the user choses, so we pass FSMContext as an argument to the function. + +FSMContext is used to store data in the bot's memory. We can store any data in it but this memory is not persistent, so if the bot is restarted, the data will be lost. But it's good to store temporary data in it. + +```python +# handle air type +@dp.message_handler(state=DataInput.secondState) +async def air_type(message: types.Message, state: FSMContext): + if message.text == "Just pure 🌫": + await state.update_data(air_type="Just pure 🌫") + elif message.text == "Fresh asphalt 🛣": + await state.update_data(air_type="Fresh asphalt 🛣") + elif message.text == "Spring forest 🌲": + await state.update_data(air_type="Spring forest 🌲") + elif message.text == "Sea breeze 🌊": + await state.update_data(air_type="Sea breeze 🌊") + else: + await message.answer("Wrong air type") + await DataInput.secondState.set() + return + await DataInput.WalletState.set() + await message.answer(f"Send your wallet address") +``` + +Use... + +```python +await state.update_data(air_type="Just pure 🌫") +``` + +...to store the air type in FSMContext. After that, we set the state to `WalletState` and ask the user to send his wallet address. + +This handler will work only when `WalletState` is set and will be waiting for a message from user with the wallet address. + +The next handler seems to be very complicated but it's not. First, we check if the message is a valid wallet address using `len(message.text) == 48` because wallet address is 48 characters long. After that, we use `api.detect_address` function to check if the address is valid. As you remember from the API part, this function also returns "Correct" address which will be stored in the database. + +After that, we get the air type from FSMContext using `await state.get_data()` and store it in `user_data` variable. + +Now we have all the data required for the payment process. We just need to generate a payment link and send it to the user. Let's use the inline keyboard. + +Three buttons will be created for payment in this example: + +- for official TON Wallet +- for Tonhub +- for Tonkeeper + +The advantage of special buttons for wallets is that if the user does not yet have a wallet, then the site will prompt him to install one. + +You are free to use whatever you want. + +And we need a button that the user will press after transaction so we can check if the payment was successful. + +```python +@dp.message_handler(state=DataInput.WalletState) +async def user_wallet(message: types.Message, state: FSMContext): + if len(message.text) == 48: + res = api.detect_address(message.text) + if res == False: + await message.answer("Wrong wallet address") + await DataInput.WalletState.set() + return + else: + user_data = await state.get_data() + air_type = user_data['air_type'] + # inline button "check transaction" + keyboard2 = types.InlineKeyboardMarkup(row_width=1) + keyboard2.add(types.InlineKeyboardButton( + text="Check transaction", callback_data="check")) + keyboard1 = types.InlineKeyboardMarkup(row_width=1) + keyboard1.add(types.InlineKeyboardButton( + text="Ton Wallet", url=f"ton://transfer/{WALLET}?amount=1000000000&text={air_type}")) + keyboard1.add(types.InlineKeyboardButton( + text="Tonkeeper", url=f"https://app.tonkeeper.com/transfer/{WALLET}?amount=1000000000&text={air_type}")) + keyboard1.add(types.InlineKeyboardButton( + text="Tonhub", url=f"https://tonhub.com/transfer/{WALLET}?amount=1000000000&text={air_type}")) + await message.answer(f"You choose {air_type}") + await message.answer(f"Send 1 toncoin to address \n{WALLET} \nwith comment \n{air_type} \nfrom your wallet ({message.text})", reply_markup=keyboard1) + await message.answer(f"Click the button after payment", reply_markup=keyboard2) + await DataInput.PayState.set() + await state.update_data(wallet=res) + await state.update_data(value_nano="1000000000") + else: + await message.answer("Wrong wallet address") + await DataInput.WalletState.set() +``` + +#### /me + +One last message handler that we need is for `/me` command. It shows the user's payments. + +```python +# /me command handler +@dp.message_handler(commands=['me'], state="*") +async def cmd_me(message: types.Message): + await message.answer(f"Your transactions") + # db.get_user_payments returns list of transactions for user + transactions = db.get_user_payments(message.from_user.id) + if transactions == False: + await message.answer(f"You have no transactions") + else: + for transaction in transactions: + # we need to remember that blockchain stores value in nanotons. 1 toncoin = 1000000000 in blockchain + await message.answer(f"{int(transaction['value'])/1000000000} - {transaction['comment']}") +``` + +### Callback handlers + +We can set callback data in buttons which will be sent to the bot when the user presses the button. In the button that the user will press after the transaction, we set callback data to "check." As a result, we need to handle this callback. + +Callback handlers are very similar to message handlers but they have `types.CallbackQuery` as an argument instead of `message`. Function decorator is also different. + +```python +@dp.callback_query_handler(lambda call: call.data == "check", state=DataInput.PayState) +async def check_transaction(call: types.CallbackQuery, state: FSMContext): + # send notification + user_data = await state.get_data() + source = user_data['wallet'] + value = user_data['value_nano'] + comment = user_data['air_type'] + result = api.find_transaction(source, value, comment) + if result == False: + await call.answer("Wait a bit, try again in 10 seconds. You can also check the status of the transaction through the explorer (tonscan.org/)", show_alert=True) + else: + db.v_wallet(call.from_user.id, source) + await call.message.edit_text("Transaction is confirmed \n/start to restart") + await state.finish() + await DataInput.firstState.set() +``` + +In this handler we get user data from FSMContext and use `api.find_transaction` function to check if the transaction was successful. If it was, we store the wallet address in the database and send a notification to the user. After that, the user can find his transactions using `/me` command. + +### Last part of main.py + +At the end, don't forget: + +```python +if __name__ == '__main__': + executor.start_polling(dp, skip_updates=True) +``` + +This part is needed to start the bot. +In `skip_updates=True` we specify that we do not want to process old messages. But if you want to process all messages, you can set it to `False`. + +:::info + +All code of `main.py` can be found [here](https://github.com/LevZed/ton-payments-in-telegram-bot/blob/main/bot/main.py). + +::: + +## Bot in action + +We finally did it! You should now have a working bot. You can test it! + +Steps to run the bot: + +1. Fill in the `config.json` file. +2. Run `main.py`. + +All files must be in the same folder. To start the bot, you need to run `main.py` file. You can do it in your IDE or in the terminal like this: + +``` +python main.py +``` + +If you have any errors, you can check them in the terminal. Maybe you missed something in the code. + +Example of a working bot [@AirDealerBot](https://t.me/AirDealerBot) + +![bot](/img/tutorials/apiatb-bot.png) + +## References + +- Made for TON as part of [ton-footsteps/8](https://github.com/ton-society/ton-footsteps/issues/8) +- By Lev ([Telegram @Revuza](https://t.me/revuza), [LevZed on GitHub](https://github.com/LevZed)) From 56d41df4b291740f138e53cf37b6fc16842b959c Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:06 +0800 Subject: [PATCH 073/219] New translations building-web3-game.md (Chinese Simplified) --- .../dapps/tutorials/building-web3-game.md | 304 ++++++++++++++++++ 1 file changed, 304 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/building-web3-game.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/building-web3-game.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/building-web3-game.md new file mode 100644 index 0000000000..6ea642b8ba --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/building-web3-game.md @@ -0,0 +1,304 @@ +# 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). + +![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](https://docs.ton.org/learn/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 + +### Install GameFi SDK + +First, we will set up the game environment. To do that we need to install `assets-sdk`. The package is designed to prepare everything developers need to integrate the blockchain into games. The lib can be used either from CLI or from Node.js scripts. In this tutorial we stick with the CLI approach. + +```sh +npm install -g @ton-community/assets-sdk@beta +``` + +### Create a master wallet + +Next, we need to create a master wallet. The master wallet is a wallet we will use to mint the jetton, collections, NFTs, SBTs and receive payments. + +```sh +assets-cli setup-env +``` + +You will be asked a few questions: + +| Field | Hint | +| :----------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Network | Select `testnet` as far it’s a test game. | +| Type | Select `highload-v2`type of wallet as far it’s the best, performant option to use as a master wallet. | +| Storage | The storage will be used to store `NFT`/`SB`T files. `Amazon S3` (centralized) or `Pinata` (decentralized). For the tutorial let’s use `Pinata` as far as decentralized storage will be more illustrative for the Web3 game. | +| IPFS gateway | Service to load assets metadata from: `pinata`, `ipfs.io` or enter other service URL. | + +The script outputs the link you can open to see the created wallet state. + +![New wallet in Nonexist status](/img/tutorials/gamefi-flappy/wallet-nonexist-status.png) + +As you can see the wallet is not actually created yet. To the wallet be really created we need to deposit some funds into it. In the real-world scenario, you can deposit the wallet any way you prefer using the wallet address. In our case we will use [Testgiver TON Bot](https://t.me/testgiver_ton_bot). Please open it to claim 5 test TON coins. + +A bit later you could see 5 TON on the wallet and its status became `Uninit`. The wallet is ready. After the first usage it changes status to `Active`. + +![Wallet status after top-up](/img/tutorials/gamefi-flappy/wallet-nonexist-status.png) + +### Mint in-game currency + +We are going to create in-game currency to reward users with it: + +```sh +assets-cli deploy-jetton +``` + +You will be asked a few questions: + +| Field | Hint | +| :---------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Name | Token name, for example `Flappy Jetton`. | +| Description | Token description, for instance: A vibrant digital token from the Flappy Bird universe. | +| Image | Download prepared [jetton logo](https://raw.githubusercontent.com/ton-community/flappy-bird/ca4b6335879312a9b94f0e89384b04cea91246b1/scripts/tokens/flap/image.png) and specify file path. Of course, you can use any image. | +| Symbol | `FLAP` or enter any abbreviation you want to use. | +| Decimals | How many zeros after the dot your currency will have. Let’ it be `0` in our case. | + +The script outputs the link you can open to see the created jetton state. It will have `Active` status. The wallet status will change the status from `Uninit` to `Active`. + +![In-game currency / jetton](/img/tutorials/gamefi-flappy/jetton-active-status.png) + +### Create collections for SBTs + +Just for example, in the demo game we will reward users for the first and the fifth games. So, we will mint two collections to put SBTs into them when users achieve related conditions – play first and fifth time: + +```sh +assets-cli deploy-nft-collection +``` + +| Field | First game | Fifth game | +| :---------- | :-------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- | +| Type | `sbt` | `sbt` | +| Name | Flappy First Flight | Flappy High Fiver | +| Description | Commemorating your inaugural journey in the Flappy Bird game! | Celebrate your persistent play with the Flappy High Fiver NFT! | +| Image | You can download [the image](https://raw.githubusercontent.com/ton-community/flappy-bird/article-v1/scripts/tokens/first-time/image.png) here | You can download [the image](https://raw.githubusercontent.com/ton-community/flappy-bird/article-v1/scripts/tokens/five-times/image.png) here | + +We are fully prepared. So, let’s go to the logic implementation. + +## Connecting wallet + +Everything starts from a user connects his wallet. So, let’s add wallet connect integration. To work with the blockchain from the client side we need to install GameFi SDK for Phaser: + +```sh +npm install --save @ton/phaser-sdk@beta +``` + +Now let’s setup GameFi SDK and create an instance of it: + +```typescript +import { GameFi } from '@ton/phaser-sdk' + +const gameFi = await GameFi.create({ + network: 'testnet' + connector: { + // if tonconnect-manifest.json is placed in the root you can skip this option + manifestUrl: '/assets/tonconnect-manifest.json', + actionsConfiguration: { + // address of your Telegram Mini App to return to after the wallet is connected + // url you provided to BothFather during the app creation process + // to read more please read https://github.com/ton-community/flappy-bird#telegram-bot--telegram-web-app + twaReturnUrl: URL_YOU_ASSIGNED_TO_YOUR_APP + }, + contentResolver: { + // some NFT marketplaces don't support CORS, so we need to use a proxy + // you are able to use any format of the URL, %URL% will be replaced with the actual URL + urlProxy: `${YOUR_BACKEND_URL}/${PROXY_URL}?url=%URL%` + }, + // where in-game purchases come to + merchant: { + // in-game jetton purchases (FLAP) + // use address you got running `assets-cli deploy-jetton` + jettonAddress: FLAP_ADDRESS, + // in-game TON purchases + // use master wallet address you got running `assets-cli setup-env` + tonAddress: MASTER_WALLET_ADDRESS + } + }, + +}) +``` + +> To learn more about initialization options please read the [library documentation](https://github.com/ton-org/game-engines-sdk). + +> To learn what `tonconnect-manifest.json` is please check ton-connect [manifest description](https://docs.ton.org/develop/dapps/ton-connect/manifest). + +Now we are ready to create a wallet connect button. Let’s create a UI scene in Phaser which will contain the connect button: + +```typescript +class UiScene extends Phaser.Scene { + // receive gameFi instance via constructor + private gameFi: GameFi; + + create() { + this.button = this.gameFi.createConnectButton({ + scene: this, + // you can calculate the position for the button in your UI scene + x: 0, + y: 0, + button: { + onError: (error) => { + console.error(error) + } + // other options, read the docs + } + }) + } +} +``` + +> Read how to create [connect button](https://github.com/ton-community/flappy-bird/blob/article-v1/workspaces/client/src/connect-wallet-ui.ts#L82) and the [UI scene](https://github.com/ton-community/flappy-bird/blob/article-v1/workspaces/client/src/connect-wallet-ui.ts#L45). + +To watch when a user connects or disconnects his wallet let’s use the following piece of code: + +```typescript +function onWalletChange(wallet: Wallet | null) { + if (wallet) { + // wallet is ready to use + } else { + // wallet is disconnected + } +} +const unsubscribe = gameFi.onWalletChange(onWalletChange) +``` + +> To learn about more complex scenarios please check out the full implementation of [wallet connect flow](https://github.com/ton-community/flappy-bird/blob/article-v1/workspaces/client/src/index.ts#L16). + +Read how [game UI managing](https://github.com/ton-community/flappy-bird/blob/article-v1/workspaces/client/src/index.ts#L50) might be implemented. + +Now we have the user wallet connected and we can move forward. + +![Connect wallet button](/img/tutorials/gamefi-flappy/wallet-connect-button.png) +![Confirm wallet connection](/img/tutorials/gamefi-flappy/wallet-connect-confirmation.png) +![Wallet is connected](/img/tutorials/gamefi-flappy/wallet-connected.png) + +## Implementing achievements & rewards + +To implement achievements and reward system we need to prepare an endpoint which will be requested per user try. + +### `/played` endpoint + +We need to create an endpoint `/played ` which must do the following: + +- receive a body with a user wallet address and Telegram initial data passed to Mini App during the app launch. The initial data needs to be parsed to extract authentication data and ensure a user sends the request only on its behalf. +- the endpoint must calculate and store the number of games a user played. +- the endpoint must check if it’s the first or fifth game for a user and if so, reward a user with related SBT. +- the endpoint must reward a user with 1 FLAP for each game. + +> Read [/played endpoint](https://github.com/ton-community/flappy-bird/blob/article-v1/workspaces/server/src/index.ts#L197) code. + +### Request `/played` endpoint + +Every time the bird hits a pipe or falls down the client code must call `/played` endpoint passing the correct body: + +```typescript +async function submitPlayed(endpoint: string, walletAddress: string) { + return await (await fetch(endpoint + '/played', { + body: JSON.stringify({ + tg_data: (window as any).Telegram.WebApp.initData, + wallet: walletAddress + }), + headers: { + 'content-type': 'application/json' + }, + method: 'POST' + })).json() +} + +const playedInfo = await submitPlayed('http://localhost:3001', wallet.account.address); +``` + +> 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! + +![Rewarded with token and SBT](/img/tutorials/gamefi-flappy/sbt-rewarded.png) + +Play 4 more times to get the second SBT, then open your Wallet, TON Space. Here your collectibles are: + +![Achievements as SBT in Wallet](/img/tutorials/gamefi-flappy/sbts-in-wallet.png) + +## Implementing game shop + +To have an in-game shop we need to have two components. The first is an endpoint that provides info about users purchases. The second is a global loop to watch user transactions and assign game properties to its owners. + +### `/purchases` endpoint + +The endpoint does the following: + +- receive `auth` get param with Telegram Mini Apps initial data. +- the endpoint gets items a user purchased and responds with the items list. + +> Read [/purchases](https://github.com/ton-community/flappy-bird/blob/article-v1/workspaces/server/src/index.ts#L303) endpoint code. + +### Purchases loop + +To know when users make payments, we need to watch the master wallet transactions. Every transaction must contain a message `userId`:`itemId`. We will remember the last processed transaction, get only new ones, assign users properties they bought using `userId` and `itemId`, rewrite the last transaction hash. This will work in an infinite loop. + +> Read [purchase loop](https://github.com/ton-community/flappy-bird/blob/article-v1/workspaces/server/src/index.ts#L110) code. + +### Client side for the shop + +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. + +Opening the Shop will trigger purchased items loading and updating it every 10 seconds: + +```typescript +// inside of fetchPurchases function +await fetch('http://localhost:3000/purchases?auth=' + encodeURIComponent((window as any).Telegram.WebApp.initData)) +// watch for purchases +setTimeout(() => { fetchPurchases() }, 10000) +``` + +> Read [showShop function](https://github.com/ton-community/flappy-bird/blob/article-v1/workspaces/client/src/ui.ts#L191) code. + +Now we need to implement the purchase itself. To do that, we will create GameFi SDK instance first and then use `buyWithJetton` method: + +```typescript +gameFi.buyWithJetton({ + amount: BigInt(price), + forwardAmount: BigInt(1), + forwardPayload: (window as any).Telegram.WebApp.initDataUnsafe.user.id + ':' + itemId +}); +``` + +![Game prop to purchase](/img/tutorials/gamefi-flappy/purchase-item.png) +![Purchase confirmation](/img/tutorials/gamefi-flappy/purchase-confirmation.png) +![Property is ready to use](/img/tutorials/gamefi-flappy/purchase-done.png) + +It's also possible to pay with the TON coin: + +```typescript +import { toNano } from '@ton/phaser-sdk' + +gameFi.buyWithTon({ + amount: toNano(0.5), + comment: (window as any).Telegram.WebApp.initDataUnsafe.user.id + ':' + 1 +}); +``` + +## Afterword + +That’s it for this tutorial! We considered the basic GameFi features, but the SDK delivers more functionality like transfers between players, utils to work NFTs and collections, etc. We will deliver even more features in the future. + +To learn about all the GameFi features you can use read the docs of [ton-org/game-engines-sdk](https://github.com/ton-org/game-engines-sdk) and [@ton-community/assets-sdk](https://github.com/ton-community/assets-sdk). + +So, let us know what you think in the [Discussions](https://github.com/ton-org/game-engines-sdk/discussions)! + +The complete implementation is available in [flappy-bird](https://github.com/ton-community/flappy-bird) repo. From d4d59d6473811fb181d7e878ea78200063485a74 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:07 +0800 Subject: [PATCH 074/219] New translations collection-minting.md (Chinese Simplified) --- .../dapps/tutorials/collection-minting.md | 1190 +++++++++++++++++ 1 file changed, 1190 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/collection-minting.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/collection-minting.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/collection-minting.md new file mode 100644 index 0000000000..9548073587 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/collection-minting.md @@ -0,0 +1,1190 @@ +# Step by step NFT collection minting + +## 👋 Introduction + +Non-fungible tokens, or NFTs, have become one of the hottest topics in the world of digital art and collectibles. NFTs are unique digital assets that use blockchain technology to verify ownership and authenticity. They have opened up new possibilities for creators and collectors to monetize and trade digital art, music, videos, and other forms of digital content. In recent +years, the NFT market has exploded, with some high-profile sales reaching millions of dollars. In this article, we will build our NFT collection on TON step by step. + +**This is the beautiful collection of ducks you will create by the end of this tutorial:** + +![](/img/tutorials/nft/collection.png) + +## 🦄 What you will learn + +1. You will mint NFT collection on TON +2. You will understand how NFT's on TON works +3. You will put NFT on sale +4. You will upload metadata to [pinata.cloud](https://pinata.cloud) + +## 💡 Prerequisites + +You must already have a testnet wallet with at least 2 TON on it. It's possible to get testnet coins from [@testgiver_ton_bot](https://t.me/testgiver_ton_bot). + +:::info How to open testnet version of my Tonkeeper wallet?\ +To open testnet network on tonkeeper go to the settings and click 5 times on the tonkeeper logo located in the bottom, after that choose testnet instead of mainnet. +::: + +We will use Pinata as our IPFS storage system, so you also need to create an account on [pinata.cloud](https://pinata.cloud) and get api_key & api_secreat. Official Pinata [documentation tutorial](https://docs.pinata.cloud/pinata-api/authentication) can help with that. As long as you get these api tokens, I'll be waiting for you here!!! + +## 💎 What is it NFT on TON? + +Before start of main part of our tutorial, we need to understand, how actually NFT works on TON in general terms. And unexpectedly, but we will start with an explanation of how NFT works in ETH, in order to understand what is the peculiarity of the implementation of NFT in TON, compared to the usual blockchains in the industry. + +### NFT implementation on ETH + +The implementation of the NFT in ETH is extremely simple - there is 1 main contract of the collection, which stores a simple hashmap, which in turn stores the data of the NFT from this collection. All requests related to this collection(if any user wants to transfer the NFT, put it up for sale, etc.) are sent specifically to this 1 contract of the collection. + +![](/img/tutorials/nft/eth-collection.png) + +### Problems that can occur with such implementation in TON + +The problems of such an implementation in the context of TON are perfectly described by the [NFT standart](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md) in TON: + +- Unpredictable gas consumption. In TON, gas consumption for dictionary operations depends on exact set of keys. Also, TON is an asynchronous blockchain. This means that if you send a message to a smart contract, then you do not know how many messages from other users will reach the smart contract before your message. Thus, you do not know what the size of the dictionary will be at the moment when your message reaches the smart contract. This is OK with a simple wallet -> NFT smart contract interaction, but not acceptable with smart contract chains, e.g. wallet -> NFT smart contract -> auction -> NFT smart contract. If we cannot predict gas consumption, then a situation may occur like that the owner has changed on the NFT smart contract, but there were no enough Toncoins for the auction operation. Using smart contracts without dictionaries gives deterministic gas consumption. + +- Does not scale (becomes a bottleneck). Scaling in TON is based on the concept of sharding, i.e. automatic partitioning of the network into shardchains under load. The single big smart contract of the popular NFT contradicts this concept. In this case, many transactions will refer to one single smart contract. The TON architecture provides for sharded smart contracts(see whitepaper), but at the moment they are not implemented. + +_TL;DR ETH solution it's not scalable and not suitable for asynchronous blockchain like TON._ + +### TON NFT implementation + +In TON we have 1 master contract - smart-contract of our collection, that store it's metadata and address of it's owner and the main thing - that if we want to create("mint") new NFT Item - we just need to send message to this collection contract. And this collection contract will deploy contract of new NFT item for us, with the data we provided. + +![](/img/tutorials/nft/ton-collection.png) + +:::info +You can check [NFT processing on TON](/develop/dapps/asset-processing/nfts) article or read [NFT standart](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md) if you want to dive deeper into this topic +::: + +## ⚙ Setup development environment + +Let's start by creating an empty project: + +1. Create new folder + `mkdir MintyTON` +2. Open this folder + `cd MintyTON` +3. Init our project `yarn init -y` +4. Install typescript + +``` +yarn add typescript @types/node -D +``` + +5. Copy this config to tsconfig.json + +```json +{ + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "lib": ["ES2022"], + "moduleResolution": "node", + "sourceMap": true, + "outDir": "dist", + "baseUrl": "src", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "strict": true, + "esModuleInterop": true, + "strictPropertyInitialization": false + }, + "include": ["src/**/*"] +} +``` + +6. Add script to build & start our app to package.json + +```json +"scripts": { + "start": "tsc --skipLibCheck && node dist/app.js" + }, +``` + +7. Install required libraries + +``` +yarn add @pinata/sdk dotenv ton ton-core ton-crypto +``` + +8. Create `.env` file and add your own data based on this template + +``` +PINATA_API_KEY=your_api_key +PINATA_API_SECRET=your_secret_api_key +MNEMONIC=word1 word2 word3 word4 +TONCENTER_API_KEY=aslfjaskdfjasasfas +``` + +You can get toncenter api key from [@tonapibot](https://t.me/tonapibot) and choose mainnet or testnet. In `MNEMONIC` variable store 24 words of collection owner wallet seed phrase. + +Great! Now we are ready to start writing code for our project. + +### Write helper functions + +Firstly let's create function in `src/utils.ts`, that will open our wallet by mnemonic and return publicKey/secretKey of it. + +We get a pair of keys based on 24 words(seed phrase): + +```ts +import { KeyPair, mnemonicToPrivateKey } from "ton-crypto"; +import { + beginCell, + Cell, + OpenedContract, + TonClient, + WalletContractV4, +} from "ton"; + +export type OpenedWallet = { + contract: OpenedContract; + keyPair: KeyPair; +}; + +export async function openWallet(mnemonic: string[], testnet: boolean) { + const keyPair = await mnemonicToPrivateKey(mnemonic); +} +``` + +Create a class instance to interact with toncenter: + +```ts +const toncenterBaseEndpoint: string = testnet + ? "https://testnet.toncenter.com" + : "https://toncenter.com"; + +const client = new TonClient({ + endpoint: `${toncenterBaseEndpoint}/api/v2/jsonRPC`, + apiKey: process.env.TONCENTER_API_KEY, +}); +``` + +And finally open our wallet: + +```ts +const wallet = WalletContractV4.create({ + workchain: 0, + publicKey: keyPair.publicKey, + }); + +const contract = client.open(wallet); +return { contract, keyPair }; +``` + +Nice, after that we will create main entrypoint for our project - `app.ts`. +Here will use just created function `openWallet` and call our main function `init`. +Thats enough for now. + +```ts +import * as dotenv from "dotenv"; + +import { openWallet } from "./utils"; +import { readdir } from "fs/promises"; + +dotenv.config(); + +async function init() { + const wallet = await openWallet(process.env.MNEMONIC!.split(" "), true); +} + +void init(); +``` + +And by the end, let's create `delay.ts` file, in which we will create function to wait until `seqno` increases. + +```ts +import { OpenedWallet } from "utils"; + +export async function waitSeqno(seqno: number, wallet: OpenedWallet) { + for (let attempt = 0; attempt < 10; attempt++) { + await sleep(2000); + const seqnoAfter = await wallet.contract.getSeqno(); + if (seqnoAfter == seqno + 1) break; + } +} + +export function sleep(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +} +``` + +:::info What is it - seqno? +In simply words, seqno it's just a counter of outgoing transactions sent by wallet. +Seqno used to prevent Replay Attacks. When a transaction is sent to a wallet smart contract, it compares the seqno field of the transaction with the one inside its storage. If they match, it's accepted and the stored seqno is incremented by one. If they don't match, the transaction is discarded. That's why we will need to wait a bit, after every outgoing transaction. +::: + +## 🖼 Prepare metadata + +Metadata - is just a simple information that will describe our NFT or collection. For example it's name, it's description, etc. + +Firstly, we need to store images of our NFT's in `/data/images` with name `0.png`, `1.png`, ... for photo of items, and `logo.png` for avatar of our collection. You can easily [download pack](/img/tutorials/nft/ducks.zip) with ducks images or put your images into that folder. And also we will store all our metadata files in `/data/metadata/` folder. + +### NFT specifications + +Most of the products on TON supports such metatadata specifications to store information about NFT collection: + +| Name | Explanation | +| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | +| name | Collection name | +| description | Collection description | +| image | Link to the image, that will be displayed as the avatar. Supported link formats: https, ipfs, TON Storage. | +| cover_image | Link to the image, that will be displayed as the collection’s cover image. | +| social_links | List of links to the project’s social media profiles. Use no more than 10 links. | + +![image](/img/tutorials/nft/collection-metadata.png) + +Based on this info, let's create our own metadata file `collection.json`, that will describe metadata of our collection! + +```json +{ + "name": "Ducks on TON", + "description": "This collection is created for showing an example of minting NFT collection on TON. You can support creator by buying one of this NFT.", + "social_links": ["https://t.me/DucksOnTON"] +} +``` + +Note that we didn't write the "image" parameter, you will know why a bit later, just wait! + +After creation of collection metadata file we need to create metadata of our NFT's + +Specifications of NFT Item metadata: + +| Name | Explanation | +| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| name | NFT name. Recommended length: No more than 15-30 characters | +| description | NFT description. Recommended length: Up to 500 characters | +| image | Link to the image of NFT. | +| attributes | NFT attributes. A list of attributes, where a trait_type (attribute name) and value (a short description of the attribution) is specified. | +| lottie | Link to the json file with Lottie animation. If specified, the Lottie animation from this link will be played on page with the NFT. | +| content_url | Link to additional content. | +| content_type | The type of content added through the content_url link. For example, a video/mp4 file. | + +![image](/img/tutorials/nft/item-metadata.png) + +```json +{ + "name": "Duck #00", + "description": "What about a round of golf?", + "attributes": [{ "trait_type": "Awesomeness", "value": "Super cool" }] +} +``` + +After that, you can create as many files of NFT item with their metadata as you want. + +### Upload metadata + +Now let's write some code, that will upload our metadata files to IPFS. Create `metadata.ts` file and add all needed imports: + +```ts +import pinataSDK from "@pinata/sdk"; +import { readdirSync } from "fs"; +import { writeFile, readFile } from "fs/promises"; +import path from "path"; +``` + +After that, we need to create function, that will actually upload all files from our folder to IPFS: + +```ts +export async function uploadFolderToIPFS(folderPath: string): Promise { + const pinata = new pinataSDK({ + pinataApiKey: process.env.PINATA_API_KEY, + pinataSecretApiKey: process.env.PINATA_API_SECRET, + }); + + const response = await pinata.pinFromFS(folderPath); + return response.IpfsHash; +} +``` + +Excellent! Let's return to the question at hand: why did we leave the "image" field in the metadata files empty? Imagine a situation where you want to create 1000 NFTs in your collection and, accordingly, you have to manually go through each item and manually insert a link to your picture. +This is really inconvenient and wrong, so let's write a function that will do this automatically! + +```ts +export async function updateMetadataFiles(metadataFolderPath: string, imagesIpfsHash: string): Promise { + const files = readdirSync(metadataFolderPath); + + files.forEach(async (filename, index) => { + const filePath = path.join(metadataFolderPath, filename) + const file = await readFile(filePath); + + const metadata = JSON.parse(file.toString()); + metadata.image = + index != files.length - 1 + ? `ipfs://${imagesIpfsHash}/${index}.jpg` + : `ipfs://${imagesIpfsHash}/logo.jpg`; + + await writeFile(filePath, JSON.stringify(metadata)); + }); +} +``` + +Here we firstly read all of the files in specified folder: + +```ts +const files = readdirSync(metadataFolderPath); +``` + +Iterate over each file and get its content + +```ts +const filePath = path.join(metadataFolderPath, filename) +const file = await readFile(filePath); + +const metadata = JSON.parse(file.toString()); +``` + +After that, we assign the value `ipfs://{IpfsHash}/{index}.jpg` to the image field if it's not last file in the folder, otherwise `ipfs://{imagesIpfsHash}/logo.jpg` and actually rewrite our file with new data. + +Full code of metadata.ts: + +```ts +import pinataSDK from "@pinata/sdk"; +import { readdirSync } from "fs"; +import { writeFile, readFile } from "fs/promises"; +import path from "path"; + +export async function uploadFolderToIPFS(folderPath: string): Promise { + const pinata = new pinataSDK({ + pinataApiKey: process.env.PINATA_API_KEY, + pinataSecretApiKey: process.env.PINATA_API_SECRET, + }); + + const response = await pinata.pinFromFS(folderPath); + return response.IpfsHash; +} + +export async function updateMetadataFiles(metadataFolderPath: string, imagesIpfsHash: string): Promise { + const files = readdirSync(metadataFolderPath); + + files.forEach(async (filename, index) => { + const filePath = path.join(metadataFolderPath, filename) + const file = await readFile(filePath); + + const metadata = JSON.parse(file.toString()); + metadata.image = + index != files.length - 1 + ? `ipfs://${imagesIpfsHash}/${index}.jpg` + : `ipfs://${imagesIpfsHash}/logo.jpg`; + + await writeFile(filePath, JSON.stringify(metadata)); + }); +} +``` + +Great, let's call this methods in our app.ts file. +Add imports of our functions: + +```ts +import { updateMetadataFiles, uploadFolderToIPFS } from "./metadata"; +``` + +Save variables with path to the metadata/images folder and call our functions to upload metadata. + +```ts +async function init() { + const metadataFolderPath = "./data/metadata/"; + const imagesFolderPath = "./data/images/"; + + const wallet = await openWallet(process.env.MNEMONIC!.split(" "), true); + + console.log("Started uploading images to IPFS..."); + const imagesIpfsHash = await uploadFolderToIPFS(imagesFolderPath); + console.log( + `Successfully uploaded the pictures to ipfs: https://gateway.pinata.cloud/ipfs/${imagesIpfsHash}` + ); + + console.log("Started uploading metadata files to IPFS..."); + await updateMetadataFiles(metadataFolderPath, imagesIpfsHash); + const metadataIpfsHash = await uploadFolderToIPFS(metadataFolderPath); + console.log( + `Successfully uploaded the metadata to ipfs: https://gateway.pinata.cloud/ipfs/${metadataIpfsHash}` + ); +} +``` + +After that you can run `yarn start` and see link to your deployed metadata! + +### Encode offchain content + +How will link to our metadata files stored in smart contract? This question can be fully answered by the [Token Data Standart](https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md). +In some cases, it will not be enough to simply provide the desired flag and provide the link as ASCII characters, which is why let's consider an option in which it will be necessary to split our link into several parts using the snake format. + +Firstly create function, that will convert our buffer into chunks: + +```ts +function bufferToChunks(buff: Buffer, chunkSize: number) { + const chunks: Buffer[] = []; + while (buff.byteLength > 0) { + chunks.push(buff.subarray(0, chunkSize)); + buff = buff.subarray(chunkSize); + } + return chunks; +} +``` + +And create function, that will bind all the chunks into 1 snake-cell: + +```ts +function makeSnakeCell(data: Buffer): Cell { + const chunks = bufferToChunks(data, 127); + + if (chunks.length === 0) { + return beginCell().endCell(); + } + + if (chunks.length === 1) { + return beginCell().storeBuffer(chunks[0]).endCell(); + } + + let curCell = beginCell(); + + for (let i = chunks.length - 1; i >= 0; i--) { + const chunk = chunks[i]; + + curCell.storeBuffer(chunk); + + if (i - 1 >= 0) { + const nextCell = beginCell(); + nextCell.storeRef(curCell); + curCell = nextCell; + } + } + + return curCell.endCell(); +} +``` + +Finally, we need to create function that will encode offchain content into cell using this functions: + +```ts +export function encodeOffChainContent(content: string) { + let data = Buffer.from(content); + const offChainPrefix = Buffer.from([0x01]); + data = Buffer.concat([offChainPrefix, data]); + return makeSnakeCell(data); +} +``` + +## 🚢 Deploy NFT Collection + +When our metadata is ready and already uploaded to IPFS, we can start with deploying our collection! + +We will create file, that will store all logic related to our collection in `/contracts/NftCollection.ts` file. As always will start with imports: + +```ts +import { + Address, + Cell, + internal, + beginCell, + contractAddress, + StateInit, + SendMode, +} from "ton-core"; +import { encodeOffChainContent, OpenedWallet } from "../utils"; +``` + +And declare type wich will describe init data that we need for our collection: + +```ts +export type collectionData = { + ownerAddress: Address; + royaltyPercent: number; + royaltyAddress: Address; + nextItemIndex: number; + collectionContentUrl: string; + commonContentUrl: string; +} +``` + +| Name | Explanation | +| -------------------- | ------------------------------------------------------------------------------------------------------------ | +| ownerAddress | Address that will be set as owner of our collection. Only owner will be able to mint new NFT | +| royaltyPercent | Percent of each sale amount, that will go to the specified address | +| royaltyAddress | Address of wallet, that will receive royalty from sales of this NFT collection | +| nextItemIndex | The index that the next NFT item should have | +| collectionContentUrl | URL to the collection metadata | +| commonContentUrl | Base url for NFT items metadata | + +Firstly let's write private method, that will return cell with code of our collection. + +```ts +export class NftCollection { + private collectionData: collectionData; + + constructor(collectionData: collectionData) { + this.collectionData = collectionData; + } + + private createCodeCell(): Cell { + const NftCollectionCodeBoc = + "te6cckECFAEAAh8AART/APSkE/S88sgLAQIBYgkCAgEgBAMAJbyC32omh9IGmf6mpqGC3oahgsQCASAIBQIBIAcGAC209H2omh9IGmf6mpqGAovgngCOAD4AsAAvtdr9qJofSBpn+pqahg2IOhph+mH/SAYQAEO4tdMe1E0PpA0z/U1NQwECRfBNDUMdQw0HHIywcBzxbMyYAgLNDwoCASAMCwA9Ra8ARwIfAFd4AYyMsFWM8WUAT6AhPLaxLMzMlx+wCAIBIA4NABs+QB0yMsCEsoHy//J0IAAtAHIyz/4KM8WyXAgyMsBE/QA9ADLAMmAE59EGOASK3wAOhpgYC42Eit8H0gGADpj+mf9qJofSBpn+pqahhBCDSenKgpQF1HFBuvgoDoQQhUZYBWuEAIZGWCqALnixJ9AQpltQnlj+WfgOeLZMAgfYBwGyi544L5cMiS4ADxgRLgAXGBEuAB8YEYGYHgAkExIREAA8jhXU1DAQNEEwyFAFzxYTyz/MzMzJ7VTgXwSED/LwACwyNAH6QDBBRMhQBc8WE8s/zMzMye1UAKY1cAPUMI43gED0lm+lII4pBqQggQD6vpPywY/egQGTIaBTJbvy9AL6ANQwIlRLMPAGI7qTAqQC3gSSbCHis+YwMlBEQxPIUAXPFhPLP8zMzMntVABgNQLTP1MTu/LhklMTugH6ANQwKBA0WfAGjhIBpENDyFAFzxYTyz/MzMzJ7VSSXwXiN0CayQ=="; + return Cell.fromBase64(NftCollectionCodeBoc); + } +} +``` + +In this code, we just read Cell from base64 representation of collection smart contract. + +Okey, remained only cell with init data of our collection. +Basicly, we just need to store data from collectionData in correct way. Firstly we need to create an empty cell and store there collection owner address and index of next item that will be minted. + +```ts +private createDataCell(): Cell { + const data = this.collectionData; + const dataCell = beginCell(); + + dataCell.storeAddress(data.ownerAddress); + dataCell.storeUint(data.nextItemIndex, 64); +``` + +Next after that, we creating an empty cell that will store content of our collection, and after that store ref to the cell with encoded content of our collection. And right after that store ref to contentCell in our main data cell. + +```ts +const contentCell = beginCell(); + +const collectionContent = encodeOffChainContent(data.collectionContentUrl); + +const commonContent = beginCell(); +commonContent.storeBuffer(Buffer.from(data.commonContentUrl)); + +contentCell.storeRef(collectionContent); +contentCell.storeRef(commonContent.asCell()); +dataCell.storeRef(contentCell); +``` + +After that we just create cell of code of NFT item's, that will be created in our collection, and store ref to this cell in dataCell + +```ts +const NftItemCodeCell = Cell.fromBase64( + "te6cckECDQEAAdAAART/APSkE/S88sgLAQIBYgMCAAmhH5/gBQICzgcEAgEgBgUAHQDyMs/WM8WAc8WzMntVIAA7O1E0NM/+kAg10nCAJp/AfpA1DAQJBAj4DBwWW1tgAgEgCQgAET6RDBwuvLhTYALXDIhxwCSXwPg0NMDAXGwkl8D4PpA+kAx+gAxcdch+gAx+gAw8AIEs44UMGwiNFIyxwXy4ZUB+kDUMBAj8APgBtMf0z+CEF/MPRRSMLqOhzIQN14yQBPgMDQ0NTWCEC/LJqISuuMCXwSED/LwgCwoAcnCCEIt3FzUFyMv/UATPFhAkgEBwgBDIywVQB88WUAX6AhXLahLLH8s/Im6zlFjPFwGRMuIByQH7AAH2UTXHBfLhkfpAIfAB+kDSADH6AIIK+vCAG6EhlFMVoKHeItcLAcMAIJIGoZE24iDC//LhkiGOPoIQBRONkchQCc8WUAvPFnEkSRRURqBwgBDIywVQB88WUAX6AhXLahLLH8s/Im6zlFjPFwGRMuIByQH7ABBHlBAqN1viDACCAo41JvABghDVMnbbEDdEAG1xcIAQyMsFUAfPFlAF+gIVy2oSyx/LPyJus5RYzxcBkTLiAckB+wCTMDI04lUC8ANqhGIu" +); +dataCell.storeRef(NftItemCodeCell); +``` + +Royalty params stored in smart-contract by royaltyFactor, royaltyBase, royaltyAddress. Percentage of royalty can be calculated with the formula `(royaltyFactor / royaltyBase) * 100%`. So if we know royaltyPercent it's not a problem to get royaltyFactor. + +```ts +const royaltyBase = 1000; +const royaltyFactor = Math.floor(data.royaltyPercent * royaltyBase); +``` + +After our calculations we need to store royalty data in separate cell and provide ref to this cell in dataCell. + +```ts +const royaltyCell = beginCell(); +royaltyCell.storeUint(royaltyFactor, 16); +royaltyCell.storeUint(royaltyBase, 16); +royaltyCell.storeAddress(data.royaltyAddress); +dataCell.storeRef(royaltyCell); + +return dataCell.endCell(); +} +``` + +Now let's actually write getter, that will return StateInit of our collection: + +```ts +public get stateInit(): StateInit { + const code = this.createCodeCell(); + const data = this.createDataCell(); + + return { code, data }; +} +``` + +And getter, that will calculate Address of our collection(address of smart-contract in TON is just hash of it's StateInit) + +```ts +public get address(): Address { + return contractAddress(0, this.stateInit); + } +``` + +It remains only to write method, that will deploy the smart contract to the blockchain! + +```ts +public async deploy(wallet: OpenedWallet) { + const seqno = await wallet.contract.getSeqno(); + await wallet.contract.sendTransfer({ + seqno, + secretKey: wallet.keyPair.secretKey, + messages: [ + internal({ + value: "0.05", + to: this.address, + init: this.stateInit, + }), + ], + sendMode: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS, + }); + return seqno; + } +``` + +Deploy of new smart contract in our case - it's just sending a message from our wallet to the collection address(which one we can calculate if we have StateInit), with its StateInit! + +When owner mint a new NFT, the collection accepts the owner's message and sends a new message to the created NFT smart-contract(which requires paying a fee), so let's write a method that will replenish the balance of the collection based on the number of nfts for a mint: + +```ts +public async topUpBalance( + wallet: OpenedWallet, + nftAmount: number + ): Promise { + const feeAmount = 0.026 // approximate value of fees for 1 transaction in our case + const seqno = await wallet.contract.getSeqno(); + const amount = nftAmount * feeAmount; + + await wallet.contract.sendTransfer({ + seqno, + secretKey: wallet.keyPair.secretKey, + messages: [ + internal({ + value: amount.toString(), + to: this.address.toString({ bounceable: false }), + body: new Cell(), + }), + ], + sendMode: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS, + }); + + return seqno; + } +``` + +Perfect, let's now add few lines to our `app.ts` to deploy new collection: + +```ts +console.log("Start deploy of nft collection..."); +const collectionData = { + ownerAddress: wallet.contract.address, + royaltyPercent: 0.05, // 0.05 = 5% + royaltyAddress: wallet.contract.address, + nextItemIndex: 0, + collectionContentUrl: `ipfs://${metadataIpfsHash}/collection.json`, + commonContentUrl: `ipfs://${metadataIpfsHash}/`, +}; +const collection = new NftCollection(collectionData); +let seqno = await collection.deploy(wallet); +console.log(`Collection deployed: ${collection.address}`); +await waitSeqno(seqno, wallet); +``` + +## 🚢 Deploy NFT Items + +When our collection is ready, we can start minting our NFT! We will store code in `src/contracts/NftItem.ts` + +Unexpectedly, but now we need to go back to the `NftCollection.ts`. And add this type near to `collectionData` at the top of the file. + +```ts +export type mintParams = { + queryId: number | null, + itemOwnerAddress: Address, + itemIndex: number, + amount: bigint, + commonContentUrl: string +} +``` + +| Name | Explanation | +| ---------------- | ------------------------------------------------------------------------------------------------------ | +| itemOwnerAddress | Address that will be set as owner of item | +| itemIndex | Index of NFT Item | +| amount | Amount of TON, that will be sent to the NFT with deploy | +| commonContentUrl | The full link to the Item URL can be shown as "commonContentUrl" of collection + this commonContentUrl | + +And create method in NftCollection class, that will construct body for the deploy of our NFT Item. Firstly store bit, that will indicate to collection smart contract that we want to create new NFT. After that just store queryId & index of this NFT Item. + +```ts +public createMintBody(params: mintParams): Cell { + const body = beginCell(); + body.storeUint(1, 32); + body.storeUint(params.queryId || 0, 64); + body.storeUint(params.itemIndex, 64); + body.storeCoins(params.amount); + } +``` + +Later on create an empty cell and store owner address of this NFT: + +```ts + const nftItemContent = beginCell(); + nftItemContent.storeAddress(params.itemOwnerAddress); +``` + +And store ref in this cell(with NFT Item content) ref to the metadata of this item: + +```ts +const uriContent = beginCell(); +uriContent.storeBuffer(Buffer.from(params.commonContentUrl)); +nftItemContent.storeRef(uriContent.endCell()); +``` + +Store ref to cell with item content in our body cell: + +```ts +body.storeRef(nftItemContent.endCell()); +return body.endCell(); +``` + +Great! Now we can comeback to `NftItem.ts`. All we have to do is just send message to our collection contract with body of our NFT. + +```ts +import { internal, SendMode } from "ton-core"; +import { OpenedWallet } from "utils"; +import { NftCollection, mintParams } from "./NftCollection"; + +export class NftItem { + private collection: NftCollection; + + constructor(collection: NftCollection) { + this.collection = collection; + } + + public async deploy( + wallet: OpenedWallet, + params: mintParams + ): Promise { + const seqno = await wallet.contract.getSeqno(); + await wallet.contract.sendTransfer({ + seqno, + secretKey: wallet.keyPair.secretKey, + messages: [ + internal({ + value: "0.05", + to: this.collection.address, + body: this.collection.createMintBody(params), + }), + ], + sendMode: SendMode.IGNORE_ERRORS + SendMode.PAY_GAS_SEPARATELY, + }); + return seqno; + } +} +``` + +By the end, we will write short method, that will get address of NFT by it's index. + +Start with creation of client variable, that will help us to call get-method of collection. + +```ts +static async getAddressByIndex( + collectionAddress: Address, + itemIndex: number +): Promise
{ + const client = new TonClient({ + endpoint: "https://testnet.toncenter.com/api/v2/jsonRPC", + apiKey: process.env.TONCENTER_API_KEY, + }); +} +``` + +Then we will call get-method of collection, that will return address of NFT in this collection with such index + +```ts +const response = await client.runMethod( + collectionAddress, + "get_nft_address_by_index", + [{ type: "int", value: BigInt(itemIndex) }] +); +``` + +... and parse this address! + +```ts +return response.stack.readAddress(); +``` + +Now let's add some code in `app.ts`, to automate the minting process of each NFT. Firstly read all of the files in folder with our metadata: + +```ts +const files = await readdir(metadataFolderPath); +files.pop(); +let index = 0; +``` + +Secondly top up balance of our collection: + +```ts +seqno = await collection.topUpBalance(wallet, files.length); +await waitSeqno(seqno, wallet); +console.log(`Balance top-upped`); +``` + +Eventually, go through each file with metadata, create `NftItem` instance and call deploy method. After that we need to wait a bit, until the seqno increases: + +```ts +for (const file of files) { + console.log(`Start deploy of ${index + 1} NFT`); + const mintParams = { + queryId: 0, + itemOwnerAddress: wallet.contract.address, + itemIndex: index, + amount: toNano("0.05"), + commonContentUrl: file, + }; + + const nftItem = new NftItem(collection); + seqno = await nftItem.deploy(wallet, mintParams); + console.log(`Successfully deployed ${index + 1} NFT`); + await waitSeqno(seqno, wallet); + index++; + } +``` + +## 🏷 Put NFT on sale + +In order to put the nft for sale, we need two smart contracts. + +- Marketplace, which is responsible only for logic of creating new sales +- Sale contract, which is responsible for the logic of buying/cancelling a sale + +### Deploy marketplace + +Create new file in `/contracts/NftMarketplace.ts`. As usual create basic class, which will accept address of owner of this marketplace and create cell with code(we will use [basic version of NFT-Marketplace smart-contract](https://github.com/ton-blockchain/token-contract/blob/main/nft/nft-marketplace.fc)) of this smart contract & initial data. + +```ts +import { + Address, + beginCell, + Cell, + contractAddress, + internal, + SendMode, + StateInit, +} from "ton-core"; +import { OpenedWallet } from "utils"; + +export class NftMarketplace { + public ownerAddress: Address; + + constructor(ownerAddress: Address) { + this.ownerAddress = ownerAddress; + } + + + public get stateInit(): StateInit { + const code = this.createCodeCell(); + const data = this.createDataCell(); + + return { code, data }; + } + + private createDataCell(): Cell { + const dataCell = beginCell(); + + dataCell.storeAddress(this.ownerAddress); + + return dataCell.endCell(); + } + + private createCodeCell(): Cell { + const NftMarketplaceCodeBoc = "te6cckEBBAEAbQABFP8A9KQT9LzyyAsBAgEgAgMAqtIyIccAkVvg0NMDAXGwkVvg+kDtRND6QDASxwXy4ZEB0x8BwAGOK/oAMAHU1DAh+QBwyMoHy//J0Hd0gBjIywXLAljPFlAE+gITy2vMzMlx+wCRW+IABPIwjvfM5w=="; + return Cell.fromBase64(NftMarketplaceCodeBoc) + } +} +``` + +And let's create method, that will calculate address of our smart contract based on StateInit: + +```ts +public get address(): Address { + return contractAddress(0, this.stateInit); + } +``` + +After that we need to create method, that will deploy our marketplace actually: + +```ts +public async deploy(wallet: OpenedWallet): Promise { + const seqno = await wallet.contract.getSeqno(); + await wallet.contract.sendTransfer({ + seqno, + secretKey: wallet.keyPair.secretKey, + messages: [ + internal({ + value: "0.5", + to: this.address, + init: this.stateInit, + }), + ], + sendMode: SendMode.IGNORE_ERRORS + SendMode.PAY_GAS_SEPARATELY, + }); + return seqno; + } +``` + +As you can see, this code does not differ from the deployment of other smart contracts (nft-item smart contract, from the deployment of a new collection). The only thing is that you can see that we initially replenish our marketplace not by 0.05 TON, but by 0.5. What is the reason for this? When a new smart sales contract is deployed, the marketplace accepts the request, processes it, and sends a message to the new contract (yes, the situation is similar to the situation with the NFT collection). Which is why we need a little extra tone to pay fees. + +By the end, let's add few lines of code to our `app.ts` file, to deploy our marketplace: + +```ts +console.log("Start deploy of new marketplace "); +const marketplace = new NftMarketplace(wallet.contract.address); +seqno = await marketplace.deploy(wallet); +await waitSeqno(seqno, wallet); +console.log("Successfully deployed new marketplace"); +``` + +### Deploy sale contract + +Great! Right now we can already deploy smart contract of our NFT sale. How it will works? We need to deploy new contract, and after that "transfer" our nft to sale contract(in other words, we just need to change owner of our NFT to sale contract in item data). In this tutorial we will use [nft-fixprice-sale-v2](https://github.com/getgems-io/nft-contracts/blob/main/packages/contracts/sources/nft-fixprice-sale-v2.fc) sale smart contract. + +First of all let's declare new type, that will describe data of our sale smart-contract: + +```ts +import { + Address, + beginCell, + Cell, + contractAddress, + internal, + SendMode, + StateInit, + storeStateInit, + toNano, +} from "ton-core"; +import { OpenedWallet } from "utils"; + +export type GetGemsSaleData = { + isComplete: boolean; + createdAt: number; + marketplaceAddress: Address; + nftAddress: Address; + nftOwnerAddress: Address | null; + fullPrice: bigint; + marketplaceFeeAddress: Address; + marketplaceFee: bigint; + royaltyAddress: Address; + royaltyAmount: bigint; +}; +``` + +And now let's create class, and basic method, that will create init data cell for our smart-contract. + +We will begin with creation of cell with the fees information. We need to store address that will receive fee's for marketplace, amount of TON to send to the marketplace as fee. Store address that will receive royalty from the sell and royalty amount. + +```ts +export class NftSale { + private data: GetGemsSaleData; + + constructor(data: GetGemsSaleData) { + this.data = data; + } + + private createDataCell(): Cell { + const saleData = this.data; + + const feesCell = beginCell(); + + feesCell.storeAddress(saleData.marketplaceFeeAddress); + feesCell.storeCoins(saleData.marketplaceFee); + feesCell.storeAddress(saleData.royaltyAddress); + feesCell.storeCoins(saleData.royaltyAmount); + } +} +``` + +Following that we can create an empty cell and just store in it information from saleData in correct order and right after that store ref to the cell with the fees information: + +```ts +const dataCell = beginCell(); + +dataCell.storeUint(saleData.isComplete ? 1 : 0, 1); +dataCell.storeUint(saleData.createdAt, 32); +dataCell.storeAddress(saleData.marketplaceAddress); +dataCell.storeAddress(saleData.nftAddress); +dataCell.storeAddress(saleData.nftOwnerAddress); +dataCell.storeCoins(saleData.fullPrice); +dataCell.storeRef(feesCell.endCell()); + +return dataCell.endCell(); +``` + +And as always add method's to get stateInit, init code cell and address of our smart contract. + +```ts +public get address(): Address { + return contractAddress(0, this.stateInit); +} + +public get stateInit(): StateInit { + const code = this.createCodeCell(); + const data = this.createDataCell(); + + return { code, data }; +} + +private createCodeCell(): Cell { + const NftFixPriceSaleV2CodeBoc = + "te6cckECDAEAAikAART/APSkE/S88sgLAQIBIAMCAATyMAIBSAUEAFGgOFnaiaGmAaY/9IH0gfSB9AGoYaH0gfQB9IH0AGEEIIySsKAVgAKrAQICzQgGAfdmCEDuaygBSYKBSML7y4cIk0PpA+gD6QPoAMFOSoSGhUIehFqBSkHCAEMjLBVADzxYB+gLLaslx+wAlwgAl10nCArCOF1BFcIAQyMsFUAPPFgH6AstqyXH7ABAjkjQ04lpwgBDIywVQA88WAfoCy2rJcfsAcCCCEF/MPRSBwCCIYAYyMsFKs8WIfoCy2rLHxPLPyPPFlADzxbKACH6AsoAyYMG+wBxVVAGyMsAFcsfUAPPFgHPFgHPFgH6AszJ7VQC99AOhpgYC42EkvgnB9IBh2omhpgGmP/SB9IH0gfQBqGBNgAPloyhFrpOEBWccgGRwcKaDjgskvhHAoomOC+XD6AmmPwQgCicbIiV15cPrpn5j9IBggKwNkZYAK5Y+oAeeLAOeLAOeLAP0BZmT2qnAbE+OAcYED6Y/pn5gQwLCQFKwAGSXwvgIcACnzEQSRA4R2AQJRAkECPwBeA6wAPjAl8JhA/y8AoAyoIQO5rKABi+8uHJU0bHBVFSxwUVsfLhynAgghBfzD0UIYAQyMsFKM8WIfoCy2rLHxnLPyfPFifPFhjKACf6AhfKAMmAQPsAcQZQREUVBsjLABXLH1ADzxYBzxYBzxYB+gLMye1UABY3EDhHZRRDMHDwBTThaBI="; + + return Cell.fromBase64(NftFixPriceSaleV2CodeBoc); +} +``` + +It remains only to form a message that we will send to our marketplace to deploy sale contract and actually send this message + +Firstly, we will create an cell, that will store StateInit of our new sale contract + +```ts +public async deploy(wallet: OpenedWallet): Promise { + const stateInit = beginCell() + .store(storeStateInit(this.stateInit)) + .endCell(); +} +``` + +Create cell with the body for our message. Firstly we need to set op-code to 1(to indicate marketplace, that we want to deploy new sale smart-contract). After that we need to store coins, that will be sent to our new sale smart-contract. And last of all we need to store 2 ref to stateInit of new smart-contract, and a body, that will be sent to this new smart-contract. + +```ts +const payload = beginCell(); +payload.storeUint(1, 32); +payload.storeCoins(toNano("0.05")); +payload.storeRef(stateInit); +payload.storeRef(new Cell()); +``` + +And at the end let's send our message: + +```ts +const seqno = await wallet.contract.getSeqno(); +await wallet.contract.sendTransfer({ + seqno, + secretKey: wallet.keyPair.secretKey, + messages: [ + internal({ + value: "0.05", + to: this.data.marketplaceAddress, + body: payload.endCell(), + }), + ], + sendMode: SendMode.IGNORE_ERRORS + SendMode.PAY_GAS_SEPARATELY, +}); +return seqno; +``` + +Perfect, when sale smart-contract is deployed all that's left is to change owner of our NFT Item to address of this sale. + +### Transfer item + +What does it mean to transfer an item? Simply send a message from the owner's wallet to the smart contract with information about who the new owner of the item is. + +Go to `NftItem.ts` and create new static method in NftItem class, that will create body for such message: + +Just create an empty cell and fill the data. + +```ts +static createTransferBody(params: { + newOwner: Address; + responseTo?: Address; + forwardAmount?: bigint; + }): Cell { + const msgBody = beginCell(); + msgBody.storeUint(0x5fcc3d14, 32); // op-code + msgBody.storeUint(0, 64); // query-id + msgBody.storeAddress(params.newOwner); + + } +``` + +In addition to the op-code, query-id and address of the new owner, we must also store the address where to send a response with confirmation of a successful transfer and the rest of the incoming message coins. The amount of TON that will come to the new owner and whether he will receive a text payload. + +```ts +msgBody.storeAddress(params.responseTo || null); +msgBody.storeBit(false); // no custom payload +msgBody.storeCoins(params.forwardAmount || 0); +msgBody.storeBit(0); // no forward_payload + +return msgBody.endCell(); +``` + +And create a transfer function to transfer the NFT. + +```ts +static async transfer( + wallet: OpenedWallet, + nftAddress: Address, + newOwner: Address + ): Promise { + const seqno = await wallet.contract.getSeqno(); + + await wallet.contract.sendTransfer({ + seqno, + secretKey: wallet.keyPair.secretKey, + messages: [ + internal({ + value: "0.05", + to: nftAddress, + body: this.createTransferBody({ + newOwner, + responseTo: wallet.contract.address, + forwardAmount: toNano("0.02"), + }), + }), + ], + sendMode: SendMode.IGNORE_ERRORS + SendMode.PAY_GAS_SEPARATELY, + }); + return seqno; + } +``` + +Nice, now we can we are already very close to the end. Back to the `app.ts` and let's get address of our nft, that we want to put on sale: + +```ts +const nftToSaleAddress = await NftItem.getAddressByIndex(collection.address, 0); +``` + +Create variable, that will store information about our sale: + +```ts +const saleData: GetGemsSaleData = { + isComplete: false, + createdAt: Math.ceil(Date.now() / 1000), + marketplaceAddress: marketplace.address, + nftAddress: nftToSaleAddress, + nftOwnerAddress: null, + fullPrice: toNano("10"), + marketplaceFeeAddress: wallet.contract.address, + marketplaceFee: toNano("1"), + royaltyAddress: wallet.contract.address, + royaltyAmount: toNano("0.5"), +}; +``` + +Note, that we set nftOwnerAddress to null, because if we will do so, our sale contract would just accept our coins on deploy. + +Deploy our sale: + +```ts +const nftSaleContract = new NftSale(saleData); +seqno = await nftSaleContract.deploy(wallet); +await waitSeqno(seqno, wallet); +``` + +... and transfer it! + +```ts +await NftItem.transfer(wallet, nftToSaleAddress, nftSaleContract.address); +``` + +Now we can launch our project and enjoy the process! + +``` +yarn start +``` + +Go to https://testnet.getgems.io/collection/\ and look to this perfect ducks! + +## Conclusion + +Today you have learned a lot of new things about TON and even created your own beautiful NFT collection in the testnet! If you still have any questions or have noticed an error - feel free to write to the author - [@coalus](https://t.me/coalus) + +## References + +- [GetGems NFT-contracts](https://github.com/getgems-io/nft-contracts) +- [NFT Standart](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md) + +## About the author + +- Coalus on [Telegram](https://t.me/coalus) or [GitHub](https://github.com/coalus) From d2a3a1806296b525085aa59aec021c11820e8dcb Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:08 +0800 Subject: [PATCH 075/219] New translations how-to-run-ton-site.md (Chinese Simplified) --- .../dapps/tutorials/how-to-run-ton-site.md | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/how-to-run-ton-site.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/how-to-run-ton-site.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/how-to-run-ton-site.md new file mode 100644 index 0000000000..8b33964911 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/how-to-run-ton-site.md @@ -0,0 +1,63 @@ +# How to run TON Sites + +## 👋 Introduction + +[TON Sites](https://blog.ton.org/ton-sites) work almost like regular sites except for their installation. A number of additional actions are required to launch them. In this tutorial I will show you how to do it. + +## 🖥 Running TON Site + +Install [Tonutils Reverse Proxy](https://github.com/tonutils/reverse-proxy) to use TON Proxy for your website. + +### Installation on any Linux + +##### Download + +```bash +wget https://github.com/ton-utils/reverse-proxy/releases/download/v0.2.0/tonutils-reverse-proxy-linux-amd64 +chmod 777 tonutils-reverse-proxy-linux-amd64 +``` + +##### Run + +Run with domain configuration, and follow the steps: + +``` +./tonutils-reverse-proxy-linux-amd64 --domain your-domain.ton +``` + +Scan QR code from your terminal using Tonkeeper, Tonhub or any other wallet, execute transaction. Your domain will be linked to your site. + +###### Run without domain + +Alternatively, you can run in simple mode, with .adnl domain, if you don't have .ton or .t.me domain: + +``` +./tonutils-reverse-proxy-linux-amd64 +``` + +##### Use + +Now anyone can access your TON Site! Using ADNL address or domain. + +If you want to change some settings, like proxy pass url - open `config.json` file, edit and restart proxy. Default proxy pass url is `http://127.0.0.1:80/` + +Proxy adds additional headers: +`X-Adnl-Ip` - ip of client, and `X-Adnl-Id` - adnl id of client + +### Installation on any other OS + +Build it from sources using `./build.sh`, and run as in the step 2 for linux. Go environment is required to build. + +## 👀 Further steps + +### 🔍 Сhecking availability of the site + +After completing all the steps of the method you selected, the TON Proxy should have started. If everything was successful, your site will be available at the ADNL address received at the corresponding step. + +You can check the availability of the site by opening this address with the domain `.adnl`. Also note that in order for the site to open, you must have a TON Proxy running in your browser, for example through an extension [MyTonWallet](https://mytonwallet.io/). + +## 📌 References + +- [TON Sites, TON WWW and TON Proxy](https://blog.ton.org/ton-sites) +- [Tonutils Reverse Proxy](https://github.com/tonutils/reverse-proxy) +- Authors: [Andrew Burnosov](https://github.com/AndreyBurnosov) (TG: [@AndrewBurnosov](https://t.me/AndreyBurnosov)), [Daniil Sedov](https://gusarich.com) (TG: [@sedov](https://t.me/sedov)), [George Imedashvili](https://github.com/drforse) From 9e537aeb6b62947c16c07b1a8ee00742963e181d Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:09 +0800 Subject: [PATCH 076/219] New translations jetton-minter.md (Chinese Simplified) --- .../develop/dapps/tutorials/jetton-minter.md | 214 ++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/jetton-minter.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/jetton-minter.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/jetton-minter.md new file mode 100644 index 0000000000..68e1914824 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/jetton-minter.md @@ -0,0 +1,214 @@ +# Mint your first Jetton + +Welcome, dev! It's great to have you here. 👋 + +In this article, we'll tell you about creating your first fungible token (Jetton) on TON. + +To mint Jettons we will be using the [TON Minter](https://minter.ton.org/) browser service. + +## 📖 What you'll learn + +In this article, you'll learn how to: + +- deploy a Jetton using your browser +- customize your token +- manage and use your token +- edit the token parameters + +## 📌 Prepare before you start + +1. First you need to have the [Tonhub](https://ton.app/wallets/tonhub-wallet) / [Tonkeeper](https://ton.app/wallets/tonkeeper) wallet or [Chrome extension](https://ton.app/wallets/chrome-plugin) or any other supported on the service. +2. You must have on your balance more than 0.25 Toncoin + funds to cover the blockchain commission. + +:::tip Starter tip +~0.5 TON is definitely enough for this tutorial. +::: + +## 🚀 Let's get started! + +Use your web browser to open the service [TON Minter](https://minter.ton.org/). + +![image](/img/tutorials/jetton/jetton-main-page.png) + +### Deploy a Jetton using your browser + +#### Connect Wallet + +Click the `Connect Wallet` button to connect your [Tonhub](https://ton.app/wallets/tonhub-wallet) wallet or a [Chrome extension](https://ton.app/wallets/chrome-plugin) or another wallet from the ones below. + +#### ![image](/img/tutorials/jetton/jetton-connect-wallet.png) + +**Scan the QR-code** in a [Mobile wallet (Tonhub e.g.)](https://ton.app/wallets/tonhub-wallet) or **sign in** to the wallet via the [Chrome extension](https://ton.app/wallets/chrome-plugin). + +#### Fill in the blanks with relevant information + +1. Name (usually 1-3 words). +2. Symbol (usually 3-5 uppercase characters). +3. Amount (for example, 1,000,000). +4. Description of the token (optional). + +#### Token logo URL (optional) + +![image](/img/tutorials/jetton/jetton-token-logo.png) + +If you want to have an attractive Jetton token, you need a beautiful logo hosted somewhere. For example: + +- https://bitcoincash-example.github.io/website/logo.png + +:::info +You can easily find out about url placement of the logo in the [repository](https://github.com/ton-blockchain/minter-contract#jetton-metadata-field-best-practices) in paragraph "Where is this metadata stored". + +- On-chain. +- Off-chain IPFS. +- Off-chain website. + ::: + +#### How to create your logo URL? + +1. Prepare a **256x256** PNG image of the token logo with a transparent background. +2. Get a link to your logo. A good solution is [GitHub Pages](https://pages.github.com/). Let's use them. +3. [Create a new public repository](https://docs.github.com/en/get-started/quickstart/create-a-repo) with the name `website`. +4. Upload your prepared image to git and enable `GitHub Pages`. + 1. [Add GitHub Pages to your repository](https://docs.github.com/en/pages/getting-started-with-github-pages/creating-a-github-pages-site). + 2. [Upload your image and get a link](https://docs.github.com/en/repositories/working-with-files/managing-files/adding-a-file-to-a-repository). +5. If you have your own domain, then it would be good to use `.org` instead of `github.io`. + +## 💸 Send Jettons + +On the right side of the screen, you can **send tokens** to multi-currency wallets like [Tonkeeper](https://tonkeeper.com/) or [Tonhub](https://ton.app/wallets/tonhub-wallet). + +![image](/img/tutorials/jetton/jetton-send-tokens.png) + +:::info +You always also **burn** your Jettons to reduce their amount. + +![image](/img/tutorials/jetton/jetton-burn-tokens.png) +::: + +### 📱 Send tokens from phone using Tonkeeper + +Prerequisites: + +1. You must already have tokens on your balance to send them. +2. There must be at least 0.1 Toncoin to pay transaction fees. + +#### Step-by-step guide + +Then go to **your token**, set the **amount** to send, and enter the **recipient address.** + +![image](/img/tutorials/jetton/jetton-send-tutorial.png) + +## 📚 Using the token on the site + +You can access the **search field** at the top of the site by entering the address of the token to use the owner's rights to manage. + +:::info +The address can be found on the right side if you are already in the owner panel, or you can find the token address when receiving an airdrop. + +![image](/img/tutorials/jetton/jetton-wallet-address.png) +::: + +## ✏️ Jetton (token) customization + +With [FunC](/develop/func/overview) language you can change the behavior of the token in your favor. + +To make any changes, begin here: + +- https://github.com/ton-blockchain/minter-contract + +### Step-by-step guide for developers + +1. Make sure you have all "Dependencies and Requirements" from the [tonstarter-contracts](https://github.com/ton-defi-org/tonstarter-contracts) repo. +2. Clone the [minter-contract repository](https://github.com/ton-blockchain/minter-contract) and rename the project. +3. To install you need to open a terminal at the root and run: + +```bash npm2yarn +npm install +``` + +4. Edit the original smart contract files same way in the root terminal. All contract files are in `contracts/*.fc` + +5. Build a project by using: + +```bash npm2yarn +npm run build +``` + +The build result will be describes the process of creating the necessary files, as well as the search for smart contracts. + +:::info +Read the console, there are a lot of tips! +::: + +6. You can test your changes by using: + +```bash npm2yarn +npm run test +``` + +7. Edit the **name** and other metadata of the token in `build/jetton-minter.deploy.ts` by changing JettonParams object. + +```js +// This is example data - Modify these params for your own jetton! +// - Data is stored on-chain (except for the image data itself) +// - Owner should usually be the deploying wallet's address. + +const jettonParams = { + owner: Address.parse("EQD4gS-Nj2Gjr2FYtg-s3fXUvjzKbzHGZ5_1Xe_V0-GCp0p2"), + name: "MyJetton", + symbol: "JET1", + image: "https://www.linkpicture.com/q/download_183.png", // Image url + description: "My jetton", +}; +``` + +8. To deploy a token use the following command: + +```bash npm2yarn +npm run deploy +``` + +The result of running your project: + +```` +```js +> @ton-defi.org/jetton-deployer-contracts@0.0.2 deploy +> ts-node ./build/_deploy.ts + +================================================================= +Deploy script running, let's find some contracts to deploy.. + +* We are working with 'mainnet' + +* Config file '.env' found and will be used for deployment! + - Wallet address used to deploy from is: YOUR-ADDRESS + - Wallet balance is YOUR-BALANCE TON, which will be used for gas + +* Found root contract 'build/jetton-minter.deploy.ts - let's deploy it': + - Based on your init code+data, your new contract address is: YOUR-ADDRESS + - Let's deploy the contract on-chain.. + - Deploy transaction sent successfully + - Block explorer link: https://tonwhales.com/explorer/address/YOUR-ADDRESS + - Waiting up to 20 seconds to check if the contract was actually deployed.. + - SUCCESS! Contract deployed successfully to address: YOUR-ADDRESS + - New contract balance is now YOUR-BALANCE TON, make sure it has enough to pay rent + - Running a post deployment test: +{ + name: 'MyJetton', + description: 'My jetton', + image: 'https://www.linkpicture.com/q/download_183.png', + symbol: 'JET1' +} +``` +```` + +## What's next? + +If you want to go deeper, read this article by Tal Kol: + +- [How and why to shard your smart contract—studying the anatomy of TON Jettons](https://blog.ton.org/how-to-shard-your-ton-smart-contract-and-why-studying-the-anatomy-of-tons-jettons) + +## References + +- Project: https://github.com/ton-blockchain/minter-contract +- By Slava ([Telegram @delovoyslava](https://t.me/delovoyslava), [delovoyhomie on GitHub](https://github.com/delovoyhomie)) From eb29a1bed1dab073638336f3d6247fb4cb617bea Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:10 +0800 Subject: [PATCH 077/219] New translations simple-zk-on-ton.md (Chinese Simplified) --- .../dapps/tutorials/simple-zk-on-ton.md | 632 ++++++++++++++++++ 1 file changed, 632 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/simple-zk-on-ton.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/simple-zk-on-ton.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/simple-zk-on-ton.md new file mode 100644 index 0000000000..a92948766e --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/tutorials/simple-zk-on-ton.md @@ -0,0 +1,632 @@ +# Building a simple ZK project on TON + +## 👋 Introduction + +**Zero-knowledge** (ZK) proofs are a fundamental cryptographic primitive that allows one party (the prover) to prove to another party (the verifier) that a statement is true without revealing any information beyond the validity of the statement itself. Zero-knowledge proofs are a powerful tool for building privacy-preserving systems and have been used in a variety of applications including anonymous payments, anonymous messaging systems, and trustless bridges. + +:::tip TVM Upgrade 2023.07 +Prior to June 2023 it wasn't possible to verify cryptographic proofs on TON. Due to the prevalence of complex computation behind the pairing algorithm, it was necessary to increase the functionality of TVM by adding TVM opcodes to conduct proof verification. This functionality was added in the [June 2023 update](https://docs.ton.org/learn/tvm-instructions/tvm-upgrade#bls12-381) and at the time of this writing is only available on testnet. +::: + +## 🦄 This tutorial will cover + +1. The basics of zero-knowledge cryptography and specifically zk-SNARKs (Zero-Knowledge Succinct Non-Interactive Argument of Knowledge) +2. Initiating a trusted setup ceremony (using the Powers of Tau) +3. Writing and compiling a simple ZK circuit (using the Circom language) +4. Generating, deploying, and testing a FunC contract to verify a sample ZK-proof + +## 🟥🟦 Explaining ZK-proofs with a color-focused example + +Before we dig into the details of zero-knowledge, let's start with a simple problem. Suppose you want to prove to a color-blind person that it is possible to distinguish between different colors. We’ll use an interactive solution to solve this problem. Assume the color-blind person (the verifier) finds two identical pieces of paper, with one being red 🟥 and one being blue 🟦. + +The verifier shows one of the pieces of paper to you (the prover) and asks you to remember the color. Then the verifier holds that specific piece of paper behind their back and either keeps it the same or changes it and asks you whether the color has changed or not. If you can tell the difference, then you can see colors (or you were just lucky because you had a 50% chance of guessing the correct color). + +Now if the verifier completes this process 10 times, and you can tell the difference each time, then the verifier is ~99.90234% (1 - (1/2)^10) confident that the correct colors are being used. Therefore, if the verifier completes the process 30 times, then the verifier will be 99.99999990686774% (1 - (1/2)^30) confident. + +Nonetheless, this is an interactive solution and it's not efficient to have a DApp that asks users to send 30 transactions to prove specific data. Therefore, a non-interactive solution is needed; this is where Zk-SNARKs and Zk-STARKs come in. + +For the purposes of this tutorial, we’ll only cover Zk-SNARKs. However, you can read more about how Zk-STARKs work on the [StarkWare website](https://starkware.co/stark/), while info that compares the differences between Zk-SNARKs and Zk-STARKs can be found on this [Panther Protocol blog post](https://blog.pantherprotocol.io/zk-snarks-vs-zk-starks-differences-in-zero-knowledge-technologies/).\*\* + +### 🎯 Zk-SNARK: Zero-Knowledge Succinct Non-Interactive Argument of Knowledge + +A Zk-SNARK is a non-interactive proof system where the prover can demonstrate to the verifier that a statement is true by simply submitting one proof. And the verifier is able to verify the proof in a very short time. Typically, dealing with a Zk-SNARK consists of three main phases: + +- Conducting a trusted setup using a [multi-party computation (MPC)](https://en.wikipedia.org/wiki/Secure_multi-party_computation) protocol to generate proving and verification keys (using Powers of TAU) +- Generating a proof using a prover key, public input, and secret input (witness) +- Verifying the proof + +Let's set up our development environment and start coding! + +## ⚙ Development environment setup + +Let's begin the process by taking the following steps: + +1. Create a new project called "simple-zk" using [Blueprint](https://github.com/ton-org/blueprint) by executing the following command, after that, enter a name for your contract (e.g. ZkSimple) and then select the 1st option (using an empty contract). + +```bash +npm create ton@latest simple-zk +``` + +2. Next we’ll clone the [snarkjs repo](https://github.com/kroist/snarkjs) that is adjusted to support FunC contracts + +```bash +git clone https://github.com/kroist/snarkjs.git +cd snarkjs +npm ci +cd ../simple-zk +``` + +3. Then we’ll install the required libraries needed for ZkSNARKs + +```bash +npm add --save-dev snarkjs ffjavascript +npm i -g circom +``` + +4. Next we’ll add the below section to the package.json (note that some of the opcodes that we’ll use are not available in the mainnet release yet) + +```json +"overrides": { + "@ton-community/func-js-bin": "0.4.5-tvmbeta.1", + "@ton-community/func-js": "0.6.3-tvmbeta.1" +} +``` + +5. Additionally, we’ll need to change the version of the @ton-community/sandbox to be able to use the [latest TVM updates](https://t.me/thetontech/56) + +```bash +npm i --save-dev @ton-community/sandbox@0.12.0-tvmbeta.1 +``` + +Great! Now we are ready to start writing our first ZK project on TON! + +We currently have two main folders that make up our ZK project: + +- `simple-zk` folder: contains our Blueprint template which will enable us to write our circuit and contracts and tests +- `snarkjs` folder: contains the snarkjs repo that we cloned in step 2 + +## Circom circuit + +First let's create a folder `simple-zk/circuits` and then create a file in it and add the following code to it: + +```circom +template Multiplier() { + signal private input a; + signal private input b; + //private input means that this input is not public and will not be revealed in the proof + + signal output c; + + c <== a*b; + } + +component main = Multiplier(); +``` + +Above we added a simple multiplier circuit. By using this circuit we can prove that we know two numbers that when multiplied together result in a specific number (c) without revealing the corresponding numbers (a and b) themselves. + +To read more about the circom language consider having a look at [this website](https://docs.circom.io/). + +Next we’ll create a folder for our build files and move the data there by conducting the following (while being in the `simple-zk` folder): + +```bash +mkdir -p ./build/circuits +cd ./build/circuits +``` + +### 💪 Creating a trusted setup with Powers of TAU + +Now it's time to build a trusted setup. To carry out this process, we’ll make use of the [Powers of Tau](https://a16zcrypto.com/posts/article/on-chain-trusted-setup-ceremony/) method (which probably takes a few minutes to complete). Let’s get into it: + +```bash +echo 'prepare phase1' +node ../../../snarkjs/build/cli.cjs powersoftau new bls12-381 14 pot14_0000.ptau -v +echo 'contribute phase1 first' +node ../../../snarkjs/build/cli.cjs powersoftau contribute pot14_0000.ptau pot14_0001.ptau --name="First contribution" -v -e="some random text" +echo 'contribute phase1 second' +node ../../../snarkjs/build/cli.cjs powersoftau contribute pot14_0001.ptau pot14_0002.ptau --name="Second contribution" -v -e="some random text" +echo 'apply a random beacon' +node ../../../snarkjs/build/cli.cjs powersoftau beacon pot14_0002.ptau pot14_beacon.ptau 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f 10 -n="Final Beacon" +echo 'prepare phase2' +node ../../../snarkjs/build/cli.cjs powersoftau prepare phase2 pot14_beacon.ptau pot14_final.ptau -v +echo 'Verify the final ptau' +node ../../../snarkjs/build/cli.cjs powersoftau verify pot14_final.ptau +``` + +After the process above is completed, it will create the pot14_final.ptau file in the build/circuits folder, which can be used for writing future related circuits. + +:::caution Constraint size +If a more complex circuit is written with more constraints, it is necessary to generate your PTAU setup using a larger parameter. +::: + +You can remove the unnecessary files: + +```bash +rm pot14_0000.ptau pot14_0001.ptau pot14_0002.ptau pot14_beacon.ptau +``` + +### 📜 Circuit compilation + +Now let's compile the circuit by running the following command from the `build/circuits` folder: + +```bash +circom ../../circuits/test.circom --r1cs circuit.r1cs --wasm circuit.wasm --prime bls12381 --sym circuit.sym +``` + +Now we have our circuit compiled to the `build/circuits/circuit.sym`, `build/circuits/circuit.r1cs`, and `build/circuits/circuit.wasm` files. + +:::info altbn-128 and bls12-381 curves +The altbn-128 and bls12-381 elliptic curves are currently supported by snarkjs. The [altbn-128](https://eips.ethereum.org/EIPS/eip-197) curve is only supported on Ethereum. However, on TON only the bls12-381 curve is supported. +::: + +Let's check the constraint size of our circuit by entering the following command: + +```bash +node ../../../snarkjs/build/cli.cjs r1cs info circuit.r1cs +``` + +Therefore, the correct result should be: + +```bash +[INFO] snarkJS: Curve: bls12-381 +[INFO] snarkJS: # of Wires: 4 +[INFO] snarkJS: # of Constraints: 1 +[INFO] snarkJS: # of Private Inputs: 2 +[INFO] snarkJS: # of Public Inputs: 0 +[INFO] snarkJS: # of Labels: 4 +[INFO] snarkJS: # of Outputs: 1 +``` + +Now we can generate the reference zkey by executing the following: + +```bash +node ../../../snarkjs/build/cli.cjs zkey new circuit.r1cs pot14_final.ptau circuit_0000.zkey +``` + +Then we’ll add the below contribution to the zkey: + +```bash +echo "some random text" | node ../../../snarkjs/build/cli.cjs zkey contribute circuit_0000.zkey circuit_0001.zkey --name="1st Contributor Name" -v +``` + +Next, let's export the final zkey: + +```bash +echo "another random text" | node ../../../snarkjs/build/cli.cjs zkey contribute circuit_0001.zkey circuit_final.zkey +``` + +Now we have our final zkey present in the `build/circuits/circuit_final.zkey` file. The zkey is then verified by entering the following: + +```bash +node ../../../snarkjs/build/cli.cjs zkey verify circuit.r1cs pot14_final.ptau circuit_final.zkey +``` + +Finally, it's time to generate the verification key: + +```bash +node ../../../snarkjs/build/cli.cjs zkey export verificationkey circuit_final.zkey verification_key.json +``` + +Then we’ll remove the unnecessary files: + +```bash +rm circuit_0000.zkey circuit_0001.zkey +``` + +After conducting the above processes, the `build/circuits` folder should be displayed as follows: + +``` +build +└── circuits + ├── circuit_final.zkey + ├── circuit.r1cs + ├── circuit.sym + ├── circuit.wasm + ├── pot14_final.ptau + └── verification_key.json + +``` + +### ✅ Exporting the verifier contract + +The final step in this section is to generate the FunC verifier contract which we’ll use in our ZK project. + +```bash +node ../../../snarkjs/build/cli.cjs zkey export funcverifier circuit_final.zkey ../../contracts/verifier.fc +``` + +Then the `verifier.fc` file is generated in the `contracts` folder. + +## 🚢 Verifier contract deployment​ + +Let's review the `contracts/verifier.fc` file step-by-step because it contains the magic of ZK-SNARKs: + +```func +const slice IC0 = "b514a6870a13f33f07bc314cdad5d426c61c50b453316c241852089aada4a73a658d36124c4df0088f2cd8838731b971"s; +const slice IC1 = "8f9fdde28ca907af4acff24f772448a1fa906b1b51ba34f1086c97cd2c3ac7b5e0e143e4161258576d2a996c533d6078"s; + +const slice vk_gamma_2 = "93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8"s; +const slice vk_delta_2 = "97b0fdbc9553a62a79970134577d1b86f7da8937dd9f4d3d5ad33844eafb47096c99ee36d2eab4d58a1f5b8cc46faa3907e3f7b12cf45449278832eb4d902eed1d5f446e5df9f03e3ce70b6aea1d2497fd12ed91bd1d5b443821223dca2d19c7"s; +const slice vk_alpha_1 = "a3fa7b5f78f70fbd1874ffc2104f55e658211db8a938445b4a07bdedd966ec60090400413d81f0b6e7e9afac958abfea"s; +const slice vk_beta_2 = "b17e1924160eff0f027c872bc13ad3b60b2f5076585c8bce3e5ea86e3e46e9507f40c4600401bf5e88c7d6cceb05e8800712029d2eff22cbf071a5eadf166f266df75ad032648e8e421550f9e9b6c497b890a1609a349fbef9e61802fa7d9af5"s; +``` + +Above are the constants that verifier contracts must make use of to implement proof verification. These parameters can be found in the `build/circuits/verification_key.json` file. + +```func +slice bls_g1_add(slice x, slice y) asm "BLS_G1_ADD"; +slice bls_g1_neg(slice x) asm "BLS_G1_NEG"; +slice bls_g1_multiexp( + slice x1, int y1, + int n +) asm "BLS_G1_MULTIEXP"; +int bls_pairing(slice x1, slice y1, slice x2, slice y2, slice x3, slice y3, slice x4, slice y4, int n) asm "BLS_PAIRING"; +``` + +The above lines are the new [TVM opcodes](https://docs.ton.org/learn/tvm-instructions/tvm-upgrade-2023-07#bls12-381) (BLS12-381) that allow pairing checks to be conducted on the TON Blockchain. + +The load_data and save_data functions are simply used to load and save the proof verification results (only for test purposes). + +```func +() load_data() impure { + + var ds = get_data().begin_parse(); + + ctx_res = ds~load_uint(32); + + ds.end_parse(); +} + +() save_data() impure { + set_data( + begin_cell() + .store_uint(ctx_res, 32) + .end_cell() + ); +} +``` + +Next there are several simple util functions that are used to load the proof data sent to the contract: + +```func +(slice, slice) load_p1(slice body) impure { + ... +} + +(slice, slice) load_p2(slice body) impure { + ... +} + +(slice, int) load_newint(slice body) impure { + ... +} +``` + +And the last part is the groth16Verify function which is required to check the validity of the proof sent to the contract. + +```func +() groth16Verify( + slice pi_a, + slice pi_b, + slice pi_c, + + int pubInput0 + +) impure { + + slice cpub = bls_g1_multiexp( + + IC1, pubInput0, + + 1 + ); + + + cpub = bls_g1_add(cpub, IC0); + slice pi_a_neg = bls_g1_neg(pi_a); + int a = bls_pairing( + cpub, vk_gamma_2, + pi_a_neg, pi_b, + pi_c, vk_delta_2, + vk_alpha_1, vk_beta_2, + 4); + ;; ctx_res = a; + if (a == 0) { + ctx_res = 0; + } else { + ctx_res = 1; + } + save_data(); +} +``` + +Now it’s necessary to edit the two files in the `wrappers` folder. The first file that needs our attention is the `ZkSimple.compile.ts` file (if another name for the contract was set in step 1, its name will be different). We’ll put the `verifier.fc` file in the list of contracts that must be compiled. + +```ts +import { CompilerConfig } from '@ton-community/blueprint'; + +export const compile: CompilerConfig = { + lang: 'func', + targets: ['contracts/verifier.fc'], // <-- here we put the path to our contract +}; +``` + +The other file that needs attention is `ZkSimple.ts`. We need to first add the opcode of `verify` to the `Opcodes` enum: + +```ts +export const Opcodes = { + verify: 0x3b3cca17, +}; +``` + +Next, it’s necessary to add the `sendVerify` function to the `ZkSimple` class. This function is used to send the proof to the contract and test it and is presented as follows: + +```ts +async sendVerify( + provider: ContractProvider, + via: Sender, + opts: { + pi_a: Buffer; + pi_b: Buffer; + pi_c: Buffer; + pubInputs: bigint[]; + value: bigint; + queryID?: number; +} +) { + await provider.internal(via, { + value: opts.value, + sendMode: SendMode.PAY_GAS_SEPARATELY, + body: beginCell() + .storeUint(Opcodes.verify, 32) + .storeUint(opts.queryID ?? 0, 64) + .storeRef( + beginCell() + .storeBuffer(opts.pi_a) + .storeRef( + beginCell() + .storeBuffer(opts.pi_b) + .storeRef( + beginCell() + .storeBuffer(opts.pi_c) + .storeRef( + this.cellFromInputList(opts.pubInputs) + ) + ) + ) + ) + .endCell(), + }); +} +``` + +Next, we’ll add the `cellFromInputList` function to the `ZkSimple` class. This function is used to create a cell from the public inputs which will be sent to the contract. + +```ts + cellFromInputList(list: bigint[]) : Cell { + var builder = beginCell(); + builder.storeUint(list[0], 256); + if (list.length > 1) { + builder.storeRef( + this.cellFromInputList(list.slice(1)) + ); + } + return builder.endCell() +} +``` + +Finally, the last function we’ll add to the `ZkSimple` class is the `getRes` function. This function is used to receive the proof verification result. + +```ts + async getRes(provider: ContractProvider) { + const result = await provider.get('get_res', []); + return result.stack.readNumber(); +} +``` + +Now we can run the required tests needed to deploy the contract. For this to be possible, the contract should be able to successfully pass the deployment test. Run this command in the root of `simple-zk` folder: + +```bash +npx blueprint test +``` + +## 🧑‍💻 Writing tests for the verifier + +Let's open the `ZkSimple.spec.ts` file in the `tests` folder and write a test for the `verify` function. The test is conducted as follows: + +```ts +describe('ZkSimple', () => { + let code: Cell; + + beforeAll(async () => { + code = await compile('ZkSimple'); + }); + + let blockchain: Blockchain; + let zkSimple: SandboxContract; + + beforeEach(async () => { + // deploy contract + }); + + it('should deploy', async () => { + // the check is done inside beforeEach + // blockchain and zkSimple are ready to use + }); + + it('should verify', async () => { + // todo write the test + }); +}); +``` + +First, we’ll need to import several packages that we will use in the test: + +```ts +import * as snarkjs from "snarkjs"; +import path from "path"; +import {buildBls12381, utils} from "ffjavascript"; +const {unstringifyBigInts} = utils; +``` + +- If you run the test, the result will be a TypeScript error, because we don't have a declaration file for the module 'snarkjs' & ffjavascript. This can be addressed by editing the `tsconfig.json` file in the root of the `simple-zk` folder. We'll need to change the _**strict**_ option to **_false_** in that file +- + +We'll also need to import the `circuit.wasm` and `circuit_final.zkey` files which will be used to generate the proof to send to the contract. + +```ts +const wasmPath = path.join(__dirname, "../build/circuits", "circuit.wasm"); +const zkeyPath = path.join(__dirname, "../build/circuits", "circuit_final.zkey"); +``` + +Let's fill the `should verify` test. We'll need to generate the proof first. + +```ts +it('should verify', async () => { + // proof generation + let input = { + "a": "123", + "b": "456", + } + let {proof, publicSignals} = await snarkjs.groth16.fullProve(input, wasmPath, zkeyPath); + let curve = await buildBls12381(); + let proofProc = unstringifyBigInts(proof); + var pi_aS = g1Compressed(curve, proofProc.pi_a); + var pi_bS = g2Compressed(curve, proofProc.pi_b); + var pi_cS = g1Compressed(curve, proofProc.pi_c); + var pi_a = Buffer.from(pi_aS, "hex"); + var pi_b = Buffer.from(pi_bS, "hex"); + var pi_c = Buffer.from(pi_cS, "hex"); + + // todo send the proof to the contract +}); +``` + +To carry out the next step it is necessary to define the `g1Compressed`, `g2Compressed`, and `toHexString` functions. They will be used to convert the cryptographic proof to the format that the contract expects. + +```ts +function g1Compressed(curve, p1Raw) { + let p1 = curve.G1.fromObject(p1Raw); + + let buff = new Uint8Array(48); + curve.G1.toRprCompressed(buff, 0, p1); + // convert from ffjavascript to blst format + if (buff[0] & 0x80) { + buff[0] |= 32; + } + buff[0] |= 0x80; + return toHexString(buff); +} + +function g2Compressed(curve, p2Raw) { + let p2 = curve.G2.fromObject(p2Raw); + + let buff = new Uint8Array(96); + curve.G2.toRprCompressed(buff, 0, p2); + // convert from ffjavascript to blst format + if (buff[0] & 0x80) { + buff[0] |= 32; + } + buff[0] |= 0x80; + return toHexString(buff); +} + +function toHexString(byteArray) { + return Array.from(byteArray, function (byte: any) { + return ('0' + (byte & 0xFF).toString(16)).slice(-2); + }).join(""); +} +``` + +Now we can send the cryptographic proof to the contract. We'll use the sendVerify function for this. The `sendVerify` function expects 5 parameters: `pi_a`, `pi_b`, `pi_c`, `pubInputs`, and `value`. + +```ts +it('should verify', async () => { + // proof generation + + + // send the proof to the contract + const verifier = await blockchain.treasury('verifier'); + const verifyResult = await zkSimple.sendVerify(verifier.getSender(), { + pi_a: pi_a, + pi_b: pi_b, + pi_c: pi_c, + pubInputs: publicSignals, + value: toNano('0.15'), // 0.15 TON for fee + }); + expect(verifyResult.transactions).toHaveTransaction({ + from: verifier.address, + to: zkSimple.address, + success: true, + }); + + const res = await zkSimple.getRes(); + + expect(res).not.toEqual(0); // check proof result + + return; + +}); +``` + +Are you ready to verify your first proof on TON blockchain? To start off this process, let's run the Blueprint test by inputting the following: + +```bash +npx blueprint test +``` + +The result should be as follows: + +```bash + PASS tests/ZkSimple.spec.ts + ZkSimple + ✓ should deploy (857 ms) + ✓ should verify (1613 ms) + +Test Suites: 1 passed, 1 total +Tests: 2 passed, 2 total +Snapshots: 0 total +Time: 4.335 s, estimated 5 s +Ran all test suites. +``` + +In order to check the repo that contains the code from this tutorial, click on the following link found [here](https://github.com/SaberDoTcodeR/zk-ton-doc). + +## 🏁 Conclusion + +In this tutorial you learned the following skills: + +- The intricacies of zero-knowledge and specifically ZK-SNARKs +- Writing and compiling Circom circuiting +- Increased familiarity with MPC and the Powers of TAU, which were used to generate verification keys for a circuit +- Became familiar with a Snarkjs library to export a FunC verifier for a circuit +- Became familiar with Blueprint for verifier deployment and test writing + +Note: The above examples taught us how to build a simple ZK use case. That said, there are a wide range of highly complex ZK-focused use cases that can be implemented in a wide range of industries. Some of these include: + +- private voting systems 🗳 +- private lottery systems 🎰 +- private auction systems 🤝 +- private transactions💸 (for Toncoin or Jettons) + +If you have any questions or have noticed an error - feel free to write to the author - [@saber_coder](https://t.me/saber_coder) + +If you have any questions or encounter any errors in this tutorial, feel free to write to the author: [@saber_coder](https://t.me/saber_coder) + +## 📌 References + +- [TVM June 2023 Upgrade](https://docs.ton.org/learn/tvm-instructions/tvm-upgrade) +- [SnarkJs](https://github.com/iden3/snarkjs) +- [SnarkJs FunC fork](https://github.com/kroist/snarkjs) +- [Sample ZK on TON](https://github.com/SaberDoTcodeR/ton-zk-verifier) +- [Blueprint](https://github.com/ton-org/blueprint) + +## 📖 See Also + +- [TON Trustless bridge EVM contracts](https://github.com/ton-blockchain/ton-trustless-bridge-evm-contracts) +- [Tonnel Network: Privacy protocol on TON](http://github.com/saberdotcoder/tonnel-network) +- [TVM Challenge](https://blog.ton.org/tvm-challenge-is-here-with-over-54-000-in-rewards) + +## 📬 About the author + +- Saber on [Telegram](https://t.me/saber_coder) or [GitHub](https://github.com/saberdotcoder) or [LinkedIn](https://www.linkedin.com/in/szafarpoor/) From 16ceb64387d51bf8d80231c414a345df53f9c7c1 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:10 +0800 Subject: [PATCH 078/219] New translations block-layout.md (Chinese Simplified) --- .../develop/data-formats/block-layout.md | 234 ++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/block-layout.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/block-layout.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/block-layout.md new file mode 100644 index 0000000000..1294eccb86 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/block-layout.md @@ -0,0 +1,234 @@ +# Block layout + +:::info +To maximize your comprehension of this page, familiarizing yourself with the [TL-B language](/develop/data-formats/cell-boc) is highly recommended. +::: + +A block in the blockchain is a record of new transactions that, once completed, is added to the blockchain as a permanent and immutable part of this decentralized ledger. Each block contains information such as transaction data, time, and a reference to the previous block, thereby forming a chain of blocks. + +The blocks in the TON Blockchain possess a rather complex structure due to the system's overall complexity. This page describes the structure and layout of these blocks. + +## Block + +Raw TL-B scheme of a block looks as: + +```tlb +block#11ef55aa global_id:int32 + info:^BlockInfo value_flow:^ValueFlow + state_update:^(MERKLE_UPDATE ShardState) + extra:^BlockExtra = Block; +``` + +Let's take a closer look at each field. + +## global_id:int32 + +An ID of the network where this block is created. `-239` for mainnet and `-3` for testnet. + +## info:^BlockInfo + +This field contains information about the block, such as its version, sequence numbers, identifiers, and other flags. + +```tlb +block_info#9bc7a987 version:uint32 + not_master:(## 1) + after_merge:(## 1) before_split:(## 1) + after_split:(## 1) + want_split:Bool want_merge:Bool + key_block:Bool vert_seqno_incr:(## 1) + flags:(## 8) { flags <= 1 } + seq_no:# vert_seq_no:# { vert_seq_no >= vert_seqno_incr } + { prev_seq_no:# } { ~prev_seq_no + 1 = seq_no } + shard:ShardIdent gen_utime:uint32 + start_lt:uint64 end_lt:uint64 + gen_validator_list_hash_short:uint32 + gen_catchain_seqno:uint32 + min_ref_mc_seqno:uint32 + prev_key_block_seqno:uint32 + gen_software:flags . 0?GlobalVersion + master_ref:not_master?^BlkMasterInfo + prev_ref:^(BlkPrevInfo after_merge) + prev_vert_ref:vert_seqno_incr?^(BlkPrevInfo 0) + = BlockInfo; +``` + +| Field | Type | Description | +| ------------------------------- | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | +| `version` | uint32 | The version of the block structure. | +| `not_master` | (## 1) | A flag indicating if this block is a masterchain block. | +| `after_merge` | (## 1) | A flag indicating if this block was created right after the merge of two shardchains, so it has two parent blocks | +| `before_split` | (## 1) | A flag indicating if this block was created right before the split of its shardchain | +| `after_split` | (## 1) | A flag indicating if this block was created right after the split of its shardchain | +| `want_split` | Bool | A flag indicating whether a shardchain split is desired. | +| `want_merge` | Bool | A flag indicating whether a shardchain merge is desired. | +| `key_block` | Bool | A flag indicating if this block is a key block. | +| `vert_seqno_incr` | (## 1) | Increment of the vertical sequence number. | +| `flags` | (## 8) | Additional flags for the block. | +| `seq_no` | # | Sequence number related to the block. | +| `vert_seq_no` | # | Vertical sequence number related to the block. | +| `shard` | ShardIdent | The identifier of the shard where this block belongs. | +| `gen_utime` | uint32 | The generation time of the block. | +| `start_lt` | uint64 | Start logical time associated with the block. | +| `end_lt` | uint64 | End logical time associated with the block. | +| `gen_validator_list_hash_short` | uint32 | Short hash related to the list of validators at the moment of generation of this block. | +| `gen_catchain_seqno` | uint32 | [Catchain](/catchain.pdf) sequence number related to this block. | +| `min_ref_mc_seqno` | uint32 | Minimum sequence number of referenced masterchain block. | +| `prev_key_block_seqno` | uint32 | Sequence number of the previous key block. | +| `gen_software` | GlobalVersion | The version of the software that generated the block. Only presented if the first bit of the `version` is set to `1`. | +| `master_ref` | BlkMasterInfo | A reference to the master block if the block is not a master. Stored in a reference. block. | +| `prev_ref` | BlkPrevInfo after_merge | A reference to the previous block. Stored in a reference. | +| `prev_vert_ref` | BlkPrevInfo 0 | A reference to the previous block in the vertical sequence if it exists. Stored in a reference. | + +### value_flow:^ValueFlow + +This field represents the flow of currency within the block, including fees collected and other transactions involving currency. + +```tlb +value_flow#b8e48dfb ^[ from_prev_blk:CurrencyCollection + to_next_blk:CurrencyCollection + imported:CurrencyCollection + exported:CurrencyCollection ] + fees_collected:CurrencyCollection + ^[ + fees_imported:CurrencyCollection + recovered:CurrencyCollection + created:CurrencyCollection + minted:CurrencyCollection + ] = ValueFlow; +``` + +| Field | Type | Description | +| ---------------- | ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | +| `from_prev_blk` | [CurrencyCollection](/develop/data-formats/msg-tlb#currencycollection) | Represents the flow of currencies from the previous block. | +| `to_next_blk` | [CurrencyCollection](/develop/data-formats/msg-tlb#currencycollection) | Represents the flow of currencies to the next block. | +| `imported` | [CurrencyCollection](/develop/data-formats/msg-tlb#currencycollection) | Represents the flow of currencies imported to the block. | +| `exported` | [CurrencyCollection](/develop/data-formats/msg-tlb#currencycollection) | Represents the flow of currencies exported from the block. | +| `fees_collected` | [CurrencyCollection](/develop/data-formats/msg-tlb#currencycollection) | The total amount of fees collected in the block. | +| `fees_imported` | [CurrencyCollection](/develop/data-formats/msg-tlb#currencycollection) | The amount of fees imported into the block. Non-zero only in masterchain. | +| `recovered` | [CurrencyCollection](/develop/data-formats/msg-tlb#currencycollection) | The amount of currencies recovered in the block. Non-zero only in masterchain. | +| `created` | [CurrencyCollection](/develop/data-formats/msg-tlb#currencycollection) | The amount of new currencies created in the block. Non-zero only in masterchain. | +| `minted` | [CurrencyCollection](/develop/data-formats/msg-tlb#currencycollection) | The amount of currencies minted in the block. Non-zero only in masterchain. | + +## state_update:^(MERKLE_UPDATE ShardState) + +This field represents the update of the shard state. + +```tlb +!merkle_update#02 {X:Type} old_hash:bits256 new_hash:bits256 + old:^X new:^X = MERKLE_UPDATE X; +``` + +| Field | Type | Description | +| ---------- | ------------------------- | ---------------------------------------------------------------------------------- | +| `old_hash` | bits256 | The old hash of the shard state. | +| `new_hash` | bits256 | The new hash of the shard state. | +| `old` | [ShardState](#shardstate) | The old state of the shard. Stored in a reference. | +| `new` | [ShardState](#shardstate) | The new state of the shard. Stored in a reference. | + +### ShardState + +`ShardState` can contain either information about the shard, or, in case if this shard is splitted, information about left and right splitted parts. + +```tlb +_ ShardStateUnsplit = ShardState; +split_state#5f327da5 left:^ShardStateUnsplit right:^ShardStateUnsplit = ShardState; +``` + +### ShardState Unsplitted + +```tlb +shard_state#9023afe2 global_id:int32 + shard_id:ShardIdent + seq_no:uint32 vert_seq_no:# + gen_utime:uint32 gen_lt:uint64 + min_ref_mc_seqno:uint32 + out_msg_queue_info:^OutMsgQueueInfo + before_split:(## 1) + accounts:^ShardAccounts + ^[ overload_history:uint64 underload_history:uint64 + total_balance:CurrencyCollection + total_validator_fees:CurrencyCollection + libraries:(HashmapE 256 LibDescr) + master_ref:(Maybe BlkMasterInfo) ] + custom:(Maybe ^McStateExtra) + = ShardStateUnsplit; +``` + +| Field | Type | Required | Description | +| ---------------------- | ---------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `global_id` | int32 | Yes | An ID of the network where this shard belongs. `-239` for mainnet and `-3` for testnet. | +| `shard_id` | ShardIdent | Yes | The identifier of the shard. | +| `seq_no` | uint32 | Yes | The latest sequence number associated with this shardchain. | +| `vert_seq_no` | # | Yes | The latest vertical sequence number associated with this shardchain. | +| `gen_utime` | uint32 | Yes | The generation time associated with the creation of the shard. | +| `gen_lt` | uint64 | Yes | The logical time associated with the creation of the shard. | +| `min_ref_mc_seqno` | uint32 | Yes | Sequence number of the latest referenced masterchain block. | +| `out_msg_queue_info` | OutMsgQueueInfo | Yes | Information about the out message queue of this shard. Stored in a reference. | +| `before_split` | ## 1 | Yes | A flag indicating whether a split will in the next block of this shardchain. | +| `accounts` | ShardAccounts | Yes | The state of accounts in the shard. Stored in a reference. | +| `overload_history` | uint64 | Yes | History of overload events for the shard. Used for load balancing through sharding. | +| `underload_history` | uint64 | Yes | History of underload events for the shard. Used for load balancing through sharding. | +| `total_balance` | [CurrencyCollection](/develop/data-formats/msg-tlb#currencycollection) | Yes | Total balance for the shard. | +| `total_validator_fees` | [CurrencyCollection](/develop/data-formats/msg-tlb#currencycollection) | Yes | Total validator fees for the shard. | +| `libraries` | HashmapE 256 LibDescr | Yes | A hashmap of library descriptions in this shard. Currently, non-empty only in the masterchain. | +| `master_ref` | BlkMasterInfo | No | A reference to the master block info. | +| `custom` | McStateExtra | No | Custom extra data for the shard state. This field is present only in the masterchain and contains all the masterchain-specific data. Stored in a reference. | + +### ShardState Splitted + +| Field | Type | Description | +| ------- | ------------------------------------------- | ------------------------------------------------------------------------------------------ | +| `left` | [ShardStateUnsplit](#shardstate-unsplitted) | The state of the left split shard. Stored in a reference. | +| `right` | [ShardStateUnsplit](#shardstate-unsplitted) | The state of the right split shard. Stored in a reference. | + +## extra:^BlockExtra + +This field contains extra information about the block. + +```tlb +block_extra in_msg_descr:^InMsgDescr + out_msg_descr:^OutMsgDescr + account_blocks:^ShardAccountBlocks + rand_seed:bits256 + created_by:bits256 + custom:(Maybe ^McBlockExtra) = BlockExtra; +``` + +| Field | Type | Required | Description | +| ---------------- | ----------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `in_msg_descr` | InMsgDescr | Yes | Descriptor of the incoming messages in the block. Stored in a reference. | +| `out_msg_descr` | OutMsgDescr | Yes | Descriptor of the outgoing messages in the block. Stored in a reference. | +| `account_blocks` | ShardAccountBlocks | Yes | The collection of all transactions processed in the block along with all updates of the states of the accounts assigned to the shard. Stored in a reference. | +| `rand_seed` | bits256 | Yes | The random seed for the block. | +| `created_by` | bits256 | Yes | The entity (usually a validator's public key) that created the block. | +| `custom` | [McBlockExtra](#mcblockextra) | No | This field is present only in the masterchain and contains all the masterchain-specific data. Custom extra data for the block. Stored in a reference. | + +### McBlockExtra + +This field contains extra information about the masterchain block. + +```tlb +masterchain_block_extra#cca5 + key_block:(## 1) + shard_hashes:ShardHashes + shard_fees:ShardFees + ^[ prev_blk_signatures:(HashmapE 16 CryptoSignaturePair) + recover_create_msg:(Maybe ^InMsg) + mint_msg:(Maybe ^InMsg) ] + config:key_block?ConfigParams + = McBlockExtra; +``` + +| Field | Type | Required | Description | +| --------------------- | ------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| `key_block` | ## 1 | Yes | Flag indicating whether the block is a key block. | +| `shard_hashes` | ShardHashes | Yes | The hashes of the latest blocks of the corresponding shardchains. | +| `shard_fees` | ShardFees | Yes | The total fees collected from all shards in this block. | +| `prev_blk_signatures` | HashmapE 16 CryptoSignaturePair | Yes | Previous block signatures. | +| `recover_create_msg` | InMsg | No | The message related to recovering extra-currencies, if any. Stored in a reference. | +| `mint_msg` | InMsg | No | The message related to minting extra-currencies, if any. Stored in a reference. | +| `config` | ConfigParams | No | The actual configuration parameters for this block. This field is present only if `key_block` is set. | + +## See also + +- Original description of [Block layout](https://docs.ton.org/tblkch.pdf#page=96\&zoom=100,148,172) from whitepaper From f6aaf081bb53f8a5832d3a193c1eab2154329c09 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:11 +0800 Subject: [PATCH 079/219] New translations cell-boc.mdx (Chinese Simplified) --- .../current/develop/data-formats/cell-boc.mdx | 301 ++++++++++++++++++ 1 file changed, 301 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/cell-boc.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/cell-boc.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/cell-boc.mdx new file mode 100644 index 0000000000..70a1b45998 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/cell-boc.mdx @@ -0,0 +1,301 @@ +import ThemedImage from '@theme/ThemedImage'; + +# Cell & Bag of Cells (BoC) + +## Cell + +A cell represents a data structure on TON Blockchain. Cells are able to store up to 1023 bits and possess up to 4 references to other cells. + +

+ +

+ +## Bag of Cells + +Bag of Cells (BoC) is a format for serializing cells into byte arrays, which is further described in the [TL-B schema](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/tl/boc.tlb#L25). + +

+ +

+ +On TON, everything consists of cells, including contract code, stored data, blocks, achieving streamline and robust flexibility in the process. + +

+ +

+ +### Cell serialization + +Let's analyze our first example of a Bag of Cells : + +

+ +

+ +```json +1[8_] -> { + 24[0AAAAA], + 7[FE] -> { + 24[0AAAAA] + } +} +``` + +In this example we have a 1-bit size root cell that has 2 links: the first to a 24-bit cell and the second to a 7-bit cell which possesses 1 link to a 24-bit cell. + +For this framework to work as intended, it’s necessary to turn the cells into a single sequence of bytes. To accomplish this, first, we leverage only unique cell types, below 3 out of 4 are presented as follows: + +```json +1[8_] +24[0AAAAA] +7[FE] +``` + +:::note +In order to leave only unique cells they need to be compared. To do this, we need to compare the [hashes](#cell-hash) of the cells. +::: + +```json +1[8_] -> index 0 (root cell) +7[FE] -> index 1 +24[0AAAAA] -> index 2 +``` + +Now, let's calculate descriptions for each of the 3 cells touched on above. These descriptions are made up of 2 bytes that store flags composed of information about the length of the data and the number of data linkages. + +The first byte - **refs descriptor** - is calculated as `r+8s+32l`, where `0 ≤ r ≤ 4` is amount of the Cell references (links), `0 ≤ s ≤ 1` is 1 for [exotic](#special-exotic-cells) cells and 0 for ordinary ones, and `0 ≤ l ≤ 3` is the [level](#cell-level) of the Cell. + +The second one - **bits descriptor** - is equals `floor(b / 8) + ceil (b / 8)` where `0 <= b <= 1023` is number of bits in the Cell. This descriptor represents the length of the full 4-bit groups of the Cell data (but at least 1 if it isn’t empty). + +The result is: + +```json +1[8_] -> 0201 -> 2 refs, length 1 +7[FE] -> 0101 -> 1 ref, length 1 +24[0AAAAA] -> 0006 -> 0 refs, length 6 +``` + +For data with incomplete 4-bit groups, 1 bit is added to the end of the sequence. This means it denotes the end bit of the group and is used to determine the true size of incomplete groups. Let's add the bits below: + +```json +1[8_] -> C0 -> 0b10000000->0b11000000 +7[FE] -> FF -> 0b11111110->0b11111111 +24[0AAAAA] -> 0AAAAA -> do not change (full groups) +``` + +Now let's add the refs indexes: + +```json +0 1[8_] -> 0201 -> refers to 2 cells with such indexes +1 7[FE] -> 02 -> refers to cells with index 2 +2 24[0AAAAA] -> no refs +``` + +And put it all together: + +```json +0201 C0 0201 +0101 FF 02 +0006 0AAAAA +``` + +And concat it by joining the corresponding strings into a single array of bytes: +`0201c002010101ff0200060aaaaa`, size 14 bytes. + +
+ Show example + +```golang +func (c *Cell) descriptors() []byte { +ceilBytes := c.bitsSz / 8 +if c.bitsSz%8 ! = 0 { +ceilBytes++ +} + + // calc size + ln := ceilBytes + c.bitsSz/8 + + specBit := byte(0) + if c.special { + specBit = 8 + } + + return []byte{byte(len(c.refs)) + specBit + c.level*32, byte(ln)} +} +``` + +[Source](https://github.com/xssnick/tonutils-go/blob/3d9ee052689376061bf7e4a22037ff131183afad/tvm/cell/serialize.go#L205) + +
+ +### Packing a Bag of Cells + +Let's pack the cell from the section directly above. We have already serialized it into a flat 14 byte array. + +Therefore, we build the header according to its [schema](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/tl/boc.tlb#L25). + +``` +b5ee9c72 -> id tl-b of the BoC structure +01 -> flags and size:(## 3), in our case the flags are all 0, + and the number of bytes needed to store the number of cells is 1. + we get - 0b0_0_0_00_001 +01 -> number of bytes to store the size of the serialized cells +03 -> number of cells, 1 byte (defined by 3 bits size:(## 3), equal to 3. +01 -> number of root cells - 1 +00 -> absent, always 0 (in current implementations) +0e -> size of serialized cells, 1 byte (size defined above), equal to 14 +00 -> root cell index, size 1 (determined by 3 size:(## 3) bits from header), + always 0 +0201c002010101ff0200060aaaaa -> serialized cells +``` + +Next, we concat everything above into an array of bytes into our final BoC: +`b5ee9c7201010301000e000201c002010101ff0200060aaaaa` + +Bag of Cells Implementation Examples: [Serialization](https://github.com/xssnick/tonutils-go/blob/master/tvm/cell/serialize.go), [Deserialization](https://github.com/xssnick/tonutils-go/blob/master/tvm/cell/parse.go) + +## Special (Exotic) Cells + +Generally, cells operating on TON are divided into two main types: ordinary cells and special cells. Most of the cells that the users work with are ordinary cells responsible for carrying information. + +Nevertheless, to realize internal functionality of the network, special cells are sometimes needed and are used for a diverse range of purposes, depending on their subtype. + +## Cell Level + +Every Cell has an attribute called `Level`, which is represented by an integer from 0 to 3. + +### Ordinary cells level + +The Level of an ordinary Cell is always equal to the maximum of the levels of all its references: + +```cpp +Lvl(c) = max(Lvl(r_0), ..., Lvl(r_i), ..., Lvl(r_e)) +``` + +Where `i` is a `c` reference index, `e` is a `c` reference amount. + +_Ordinary Cell without refs level is zero_ + +### Exotic cells level + +Exotic cells have different rules for setting their level, which are described in [this](/develop/data-formats/exotic-cells) article. + +## Cell hash + +In most cases users work with ordinary cells with level 0 which have only one hash, called representation hash (or hash infinity). + +Cell `c` with level `Lvl(c) = l`, where `1 ≤ l ≤ 3` has representation hash and `l` **"higher"** hashes. + +### Standard Cell representation hash calculation + +First, we need to calculate Cell representation (which is similar to Cell serialization described above) + +1. Compute descriptors bytes +2. Add serialized Cell data +3. For every Cell's refs add its depth +4. For every Cell's refs add its representation hash +5. Compute SHA256 hash of the result + +Let's analyze the following examples: + +#### Cell without references + +```json +32[0000000F] +``` + +1. Descriptors computation + +The reference descriptor is equals to `r+8s+32l = 0 + 0 + 0 = 0 = 00` + +The bits descriptor is equals to `floor(b / 8) + ceil (b / 8) = 8 = 08` + +Concatenating these bytes we get `0008` + +2. Cell data serialization + +In this case we have complete 4-bit groups, so we don't have to add any bits to the Cell data. The result is `0000000f` + +3. Refs depth + +We skip this part, because our Cell doesn't have any refs + +4. Refs hashes + +We skip this part, because our Cell doesn't have any refs + +5. SHA256 computation + +Concatenating bytes from the previous steps we get `00080000000f` and the SHA256 from this bytestring is `57b520dbcb9d135863fc33963cde9f6db2ded1430d88056810a2c9434a3860f9` - that's the Cell representation hash. + +#### Cell with references + +```json +24[00000B] -> { + 32[0000000F], + 32[0000000F] +} +``` + +1. Descriptors computation + +The reference descriptor is equals to `r+8s+32l = 2 + 0 + 0 = 0 = 02` + +The bits descriptor is equals to `floor(b / 8) + ceil (b / 8) = 6 = 06` + +Concatenating these bytes we get `0206` + +2. Cell data serialization + +In this case we have complete 4-bit groups, so we don't have to add any bits to the Cell data. The result is `00000b` + +3. Refs depth + +The depth is represented by 2 bytes. Our Cell has 2 refs and depth of each is zero so the result of this step is `00000000`. + +4. Refs hashes + +For every reference we add its hash (we computed above), so the result is `57b520dbcb9d135863fc33963cde9f6db2ded1430d88056810a2c9434a3860f957b520dbcb9d135863fc33963cde9f6db2ded1430d88056810a2c9434a3860f9` + +5. SHA256 computation + +Concatenating bytes from the previous steps we get `020600000b0000000057b520dbcb9d135863fc33963cde9f6db2ded1430d88056810a2c9434a3860f957b520dbcb9d135863fc33963cde9f6db2ded1430d88056810a2c9434a3860f9` +and the SHA256 from this bytestring is `f345277cc6cfa747f001367e1e873dcfa8a936b8492431248b7a3eeafa8030e7` - that's the Cell representation hash. + +### Higher hashes calculation + +Higher hashes of an Ordinary Cell `c` are computed similarly to its representation hash, +but using the higher hashes of its references instead of their representation hashes. + +Exotic cells have their own rules for computing their higher hashes, which are described in [this](/develop/data-formats/exotic-cells) article. + +## See Also + +[//]: # "* [Original article on RU](https://github.com/xssnick/ton-deep-doc/blob/master/Cells-BoC.md)" + +- [Exotic (Special) Cells](/develop/data-formats/exotic-cells) +- [Merkle Proofs verifying](/develop/data-formats/proofs) From 3e3a6a448f113a9b72263392ea4c442abf6b7363 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:12 +0800 Subject: [PATCH 080/219] New translations crc32.md (Chinese Simplified) --- .../current/develop/data-formats/crc32.md | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/crc32.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/crc32.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/crc32.md new file mode 100644 index 0000000000..52e285863d --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/crc32.md @@ -0,0 +1,60 @@ +# CRC32 + +## Overview + +CRC stands for Cyclic Redundancy Check, a commonly used method for verifying the integrity of digital data. It is an error-detecting algorithm used to check if errors have occurred in digital data during transmission or storage. A CRC generates a short checksum or hash of the data being transmitted or stored, which is appended to the data. When the data is received or retrieved, the CRC is recalculated and compared to the original checksum. If the two checksums match, it is assumed that the data has not been corrupted. If they do not match, it indicates that an error has occurred and the data needs to be resent or retrieved again + +The CRC32 IEEE version used for TL-B schemes. By viewing this [NFT op code](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md#tl-b-schema) example a clearer understanding of calculation TL-B for various messages is achieved. + +## Tools + +### Online calculator + +- [Online calculator example](https://emn178.github.io/online-tools/crc32.html) +- [Tonwhales Introspection ID Generator](https://tonwhales.com/tools/introspection-id) + +### VS Code extension + +- [crc32-opcode-helper](https://marketplace.visualstudio.com/items?itemName=Gusarich.crc32-opcode-helper) + +### Python + +```python +import zlib +print(zlib.crc32(b'') & 0x7FFFFFFF) +``` + +### Go + +```python +func main() { + + var schema = "some" + + schema = strings.ReplaceAll(schema, "(", "") + schema = strings.ReplaceAll(schema, ")", "") + data := []byte(schema) + var crc = crc32.Checksum(data, crc32.MakeTable(crc32.IEEE)) + + var b_data = make([]byte, 4) + binary.BigEndian.PutUint32(b_data, crc) + var res = hex.EncodeToString(b_data) + fmt.Println(res) +} +``` + +### TypeScript + +```typescript +import * as crc32 from 'crc-32'; + +function calculateRequestOpcode_1(str: string): string { + return (BigInt(crc32.str(str)) & BigInt(0x7fffffff)).toString(16); +} + +function calculateResponseOpcode_2(str: string): string { + const a = BigInt(crc32.str(str)); + const b = BigInt(0x80000000); + return ((a | b) < 0 ? (a | b) + BigInt('4294967296') : a | b).toString(16); +} +``` From 2d742bc008227923431ec1dffb31db07c0fdb26a Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:13 +0800 Subject: [PATCH 081/219] New translations exotic-cells.md (Chinese Simplified) --- .../develop/data-formats/exotic-cells.md | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/exotic-cells.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/exotic-cells.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/exotic-cells.md new file mode 100644 index 0000000000..713c886657 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/exotic-cells.md @@ -0,0 +1,138 @@ +# Exotic Cells + +Every Cell has its own type encoded by an integer from -1 to 255. +Cell with type -1 is an `ordinary` Cell, and all others Cells called `exotic` or `special`. +The type of an exotic cell is stored as the first eight bits of its data. If an exotic cell has less than eight data bits, it is invalid. +Currently, there are 4 exotic Cell types: + +```json +{ + Prunned Branch: 1, + Library Reference: 2, + Merkle Proof: 3, + Merkle Update: 4 +} +``` + +### Pruned Branch + +Pruned branches are Cells that represent deleted subtrees of Cells. + +They can have level `1 <= l <= 3` and contain exactly `8 + 8 + 256 * l + 16 * l` bits. + +First byte is always `01` - Cell type. The second one is the Pruned Branch level mask. Then goes `l * 32` bytes hashes of deleted subtrees and after that `l * 2` bytes depths of deleted subtrees. + +The level `l` of a Pruned branch Cell may be called its De Bruijn index, because it determines the outer Merkle proof or Merkle update during the construction of which the branch has been pruned. + +Higher hashes of Pruned branches are stored in their data and can be obtained like this: + +```cpp +Hash_i = CellData[2 + (i * 32) : 2 + ((i + 1) * 32)] +``` + +### Library Reference + +Library reference cells are used for using libraries in smart contracts. + +They always have level 0, and contain `8 + 256` bits. + +First byte is always `02` - Cell type. Next 32 bytes are [Representation hash](/develop/data-formats/cell-boc#standard-cell-representation-hash-calculation) of the library cell being referred to. + +### Merkle Proof + +Merkle Proof cells are used to verify that a portion of the Cell tree data belongs to the full tree. This design allows the verifier to not store the whole content of the tree, while still being able to verify the content by root hash. + +Merkle Proof has exactly one reference and its level `0 <= l <= 3` must be `max(Lvl(ref) - 1, 0)`. These cells contain exactly `8 + 256 + 16 = 280` bits. + +First byte is always `03` - Cell type. Next 32 bytes are `Hash_1(ref)` (or `ReprHash(ref)` if reference level is 0). The next 2 bytes are depth of the deleted subtree, which was replaced by the reference. + +The higher hashes `Hash_i` of Merkle Proof Cell are computed similarly to the higher hashes of an ordinary cell, but with `Hash_i+1(ref)` used instead of `Hash_i(ref)`. + +### Merkle Update + +Merkle update cells are always have 2 refs and behave like a Merkle proof for both of them. + +Merkle Update level `0 <= l <= 3` is `max(Lvl(ref1) − 1, Lvl(ref2) − 1, 0)`. They contain exactly `8 + 256 + 256 + 16 + 16 = 552` bits. + +First byte is always `04` - Cell type. Next 64 bytes are `Hash_1(ref1)` and `Hash_2(ref2)` - called old hash and new hash. Then goes 4 bytes with actual depth of deleted old subtree and deleted new subtree. + +## Simple Proof verifying example + +Let's assume there is a Cell `c`: + +```json +24[000078] -> { + 32[0000000F] -> { + 1[80] -> { + 32[0000000E] + }, + 1[00] -> { + 32[0000000C] + } + }, + 16[000B] -> { + 4[80] -> { + 267[800DEB78CF30DC0C8612C3B3BE0086724D499B25CB2FBBB154C086C8B58417A2F040], + 512[00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064] + } + } +} +``` + +But we know only its hash `44efd0fdfffa8f152339a0191de1e1c5901fdcfe13798af443640af99616b977`, and we want to prove that cell `a` `267[800DEB78CF30DC0C8612C3B3BE0086724D499B25CB2FBBB154C086C8B58417A2F040]` is actually a part of the `c`, without receiving the whole `c`. +So we ask the prover to create a Merkle Proof, replacing all branches that we are not interested in with Pruned branch cells. + +The first `c` descendant from which there is no way to get to `a` is `ref1`: + +```json +32[0000000F] -> { + 1[80] -> { + 32[0000000E] + }, + 1[00] -> { + 32[0000000C] + } +} +``` + +So the prover computes its hash (`ec7c1379618703592804d3a33f7e120cebe946fa78a6775f6ee2e28d80ddb7dc`), creates a Pruned Branch `288[0101EC7C1379618703592804D3A33F7E120CEBE946FA78A6775F6EE2E28D80DDB7DC0002]` and replaces `ref1` with this Pruned Branch. + +The second one is `512[0000000...00000000064]`, so the prover creates Pruned branch to replace this Cell as well: + +```json +24[000078] -> { + 288[0101EC7C1379618703592804D3A33F7E120CEBE946FA78A6775F6EE2E28D80DDB7DC0002], + 16[000B] -> { + 4[80] -> { + 267[800DEB78CF30DC0C8612C3B3BE0086724D499B25CB2FBBB154C086C8B58417A2F040], + 288[0101A458B8C0DC516A9B137D99B701BB60FE25F41F5ACFF2A54A2CA4936688880E640000] + } + } +} +``` + +The result Merkle Proof which prover sends to verifier (us in this example) looks like this: + +```json +280[0344EFD0FDFFFA8F152339A0191DE1E1C5901FDCFE13798AF443640AF99616B9770003] -> { + 24[000078] -> { + 288[0101EC7C1379618703592804D3A33F7E120CEBE946FA78A6775F6EE2E28D80DDB7DC0002], + 16[000B] -> { + 4[80] -> { + 267[800DEB78CF30DC0C8612C3B3BE0086724D499B25CB2FBBB154C086C8B58417A2F040], + 288[0101A458B8C0DC516A9B137D99B701BB60FE25F41F5ACFF2A54A2CA4936688880E640000] + } + } + } +} +``` + +When we (verifier) get the Proof Cell we make sure that its data contains the `c` hash and then compute `Hash_1` from the only Proof reference: `44efd0fdfffa8f152339a0191de1e1c5901fdcfe13798af443640af99616b977`, and compare it to the `c` hash. + +Now, when we've checked that hashes are match, we need to go deep in the Cell and verify that there is a Cell `a` (we were interested in). + +Such proofs repeatedly reduce the computational load and the amount of data that needs to be sent to or stored in the verifier. + +## See Also + +- [Advanced Proofs verifying examples](/develop/data-formats/proofs) From 7a2e8378799a7d9bb1b1e21ebe747a63c107cc42 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:13 +0800 Subject: [PATCH 082/219] New translations library-cells.md (Chinese Simplified) --- .../develop/data-formats/library-cells.md | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/library-cells.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/library-cells.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/library-cells.md new file mode 100644 index 0000000000..a00f83e41c --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/library-cells.md @@ -0,0 +1,147 @@ +# Library Cells + +## Introduction + +One of the native feature of how TON stores data in Cells is deduplication: in storage, messages, blocks, transactions and so on duplicate cells are stored only once. This tremendously decrease size of serialized data, and allows efficient storage of step-wise updated data. + +For the same reason many structures in TON are simultaneously rich, convinient and efficient: block structure contains the same copy of each message in many places: in Message queue, in list of Transaction, in Merkle updates and so on: since duplication has no overhead we can store data multiple times where we need it without worring about efficiency. + +Library cells employ a deduplication mechanism on-chain, allowing the integration of this technology into custom smart contracts. +:::info +If you store jetton-wallet code as library cell (1 cell and 256+8 bits, instead of ~20 cells and 6000 bits) for instance, forward fees for a message that contains `init_code` will be decreased from 0.011 to 0.003 TON. +::: + +## General Info + +Lets consider basechain step from block 1'000'000 to block 1'000'001. While each block contains small amount of data (usually less than 1000 transactions), the whole Basechain state contains millions of accounts and since blockchain need to keep integrity of the data (in particular to commit merkle root hash of whole state to the block) whole tree of the state need to be updated. + +For the blockchains of previous generations this means that generally you keep track of only recent states because storing separate chain states for each block will require too much space. But in TON Blockchain due to deduplication, for each block you only add to storage new cells. This not only make processing faster but also allows you to efficiently work with history: check balances, states and even run getmethods for any point in history without much overhead! + +For the case when we have a family of similar contracts (for instance jetton-wallets), node stores duplicating data (the same code of each jetton-wallet) only once. Library Cells allows to utilize deduplication mechanism for such contracts to decrease storage and forward fees. + +:::info Highlevel analogy +You can consider library cell as C++ pointer: one small cell that points to larger Cell with (possibly) many refs. The referenced cell (cell to which library cell points) should exist and registered in public context (_"published"_). +::: + +## Structure of Library Cells + +Library cell is [exotic cell](/develop/data-formats/exotic-cells) that contains a reference to some other static cell. In particular it contains 256 bit of hash of referenced cell. + +For TVM, library cells works as follows: whenever TVM receives a command to open a cell to a slice (TVM Instruction: `CTOS`, funC method: `.begin_parse()`), it searches cell with the corresponding hash from library cell in the Masterchain library context. If found it, it opens referenced cell and returns its slice. + +Opening library cell costs the same as opening ordinar cell, so it can be used as transparent replacement for static cells that however occupy much less space (and thus costs less fees for storage and sending). + +Note that it is possible to create a library cell that references another library cell, which in turn references another, and so on. For such case `.begin_parse()` will raise exception. Such library however can be unwrapped step-wise with `XLOAD` opcode. + +Another important peculiarities of Library Cell is that since it contains hash of referenced cell it is ultimatively reference to some satic data. You can not change data to which this library cell is referenced. + +To be found in the Masterchain library context and thus referenced by a Library Cell, a source Cell needs to be published in the Masterchain. This means that a smart contract existing in the Masterchain needs to add this cell to its state with the `public=true` flag. This can be accomplished using the `SETLIBCODE` opcode. + +## Using in Smart Contracts + +Since library cell behaves the same way as ordinary cell it referenced to in all contexts except fee calculation you can just use it instead of any cell with static data. For instance, you can store jetton-wallet code as library cell (so 1 cell and 256+8 bits, instead of usually ~20 cells and 6000 bits) which will result is order magnitude less storage and forward fees. In particular, forward fees for `internal_transfer` message that contains `init_code` will be decreased from 0.011 to 0.003 TON. + +### Store Data in the Library Cell + +Lets consider example of storing jetton-wallet code as library cell to decrease fees. First we need to compile jetton-wallet to ordinary cell that contains it's code. + +Than you need to create library cell with reference to ordinary cell. Library cell contains 8-bit tag of library `0x02` followed by 256-bit of referenced cell hash. + +### Using in Fift + +Basically you need to put tag and hash to the builder and then "close builder as exotic cell". + +It can be done in Fift-asm construction like [this](https://github.com/ton-blockchain/multisig-contract-v2/blob/master/contracts/auto/order_code.func), example of compilation some contract directly to library cell [here](https://github.com/ton-blockchain/multisig-contract-v2/blob/master/wrappers/Order.compile.ts). + +```fift +;; https://docs.ton.org/tvm.pdf, page 30 +;; Library reference cell — Always has level 0, and contains 8+256 data bits, including its 8-bit type integer 2 +;; and the representation hash Hash(c) of the library cell being referred to. When loaded, a library +;; reference cell may be transparently replaced by the cell it refers to, if found in the current library context. + +cell order_code() asm "spec PUSHREF"; +``` + +### Using in @ton/ton + +Alternatively, you can form Library Cell entirely on ts-level in Blueprint with the `@ton/ton` library: + +```ts +import { Cell, beginCell } from '@ton/core'; + +let lib_prep = beginCell().storeUint(2,8).storeBuffer(jwallet_code_raw.hash()).endCell(); +jwallet_code = new Cell({ exotic:true, bits: lib_prep.bits, refs:lib_prep.refs}); +``` + +- Learn source [here](https://github.com/ton-blockchain/stablecoin-contract/blob/de08b905214eb253d27009db6a124fd1feadbf72/sandbox_tests/JettonWallet.spec.ts#L104C1-L105C90). + +### Publish ordinary cell in masterchain library context + +Practical example is available [here](https://github.com/ton-blockchain/multisig-contract-v2/blob/master/contracts/helper/librarian.func). The core of this contract is `set_lib_code(lib_to_publish, 2);` - it accepts as input ordinary cell that need to be published and flag=2 (means that everybody can use it). + +Note, that contract that publish cell pays for it's storage and storage in masterchain 1000x higher than in basechain. So library cell usage is only efficient for contracts used by thousands users. + +### Testing in the Blueprint + +To test how contract that use Library Cells work in blueprint you need to manually add referenced cells to library context of blueprint emulator. It can be done this way: + +1. you need to create library context dictionary (Hashmap) `uint256->Cell` where `uint256` is hash of the corresponding Cell. +2. install library context to the emulator settings. + +Example how it can be done is shown [here](https://github.com/ton-blockchain/stablecoin-contract/blob/de08b905214eb253d27009db6a124fd1feadbf72/sandbox_tests/JettonWallet.spec.ts#L100C9-L103C32). + +:::info +Note, that current blueprint version (`@ton/blueprint:0.19.0`) doesn't automatically update library context if some contract during emulation publish new library, you need do it manually. +Actual for 04.2024 and suppose to be enhanced in the near future. +::: + +### Get Methods for Library Cell Based Contracts + +You have jetton-wallet with its code stored in a library cell and desire to check balance. + +To check its balance, you need to execute a get method in the code. This involves: + +- accessing the library cell +- retrieving the hash of the referenced cell +- finding the cell with that hash in the masterchain's library collection +- executing the code from there. + +In Layered Solutions (LS), all these processes happen behind the scenes without the user needing to know about the specific code storage method. + +However, when working locally, things are different. For example, if you use an explorer or wallet, you may take an account state and try to determine its type—whether it's an NFT, wallet, token, or auction. + +For regular contracts, you can look at the available get methods, i.e., the interface, to understand it. Or, you may "steal" an account state to my local pseudonet and execute methods there. + +For the a library cell, this isn't possible because it doesn't contain data on its own. You must manually detect and retrieve the necessary cells from the context. This can be done through LS (though bindings do not yet support this) or via DTon. + +#### Retrieving Library Cell with Liteserver + +Liteserver when running get methods automatically set correct library context. If you want to detect type of contract by get methods or run getmethods locally you need to download corresponding cells via LS method [liteServer.getLibraries](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/lite_api.tl#L85). + +#### Retrieving Library Cell with DTon + +You can also get library from dton.io/graphql: + +``` +{ + get_lib( + lib_hash: "" + ) +} +``` + +as well as list of libraries for specific masterchain block: + +``` +{ + blocks{ + libs_publishers + libs_hash + } +} +``` + +## See Also + +- [Exotic Cells](/develop/data-formats/exotic-cells) +- [TVM Instructions](/learn/tvm-instructions/instructions) From 4a1054081d36156f0bb0ab9f2d62520f02bea0b4 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:14 +0800 Subject: [PATCH 083/219] New translations msg-tlb.mdx (Chinese Simplified) --- .../current/develop/data-formats/msg-tlb.mdx | 343 ++++++++++++++++++ 1 file changed, 343 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/msg-tlb.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/msg-tlb.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/msg-tlb.mdx new file mode 100644 index 0000000000..69a7b0c613 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/msg-tlb.mdx @@ -0,0 +1,343 @@ +import ThemedImage from '@theme/ThemedImage'; + +# Messages TL-B schemes + +In this section detailed explanation of TL-B schemes for messages. + +## Message TL-B + +### TL-B + +Main message TL-B scheme declared as a combination of several nested structures + +```tlb +message$_ {X:Type} info:CommonMsgInfo +init:(Maybe (Either StateInit ^StateInit)) +body:(Either X ^X) = Message X; + +message$_ {X:Type} info:CommonMsgInfoRelaxed +init:(Maybe (Either StateInit ^StateInit)) +body:(Either X ^X) = MessageRelaxed X; + +_ (Message Any) = MessageAny; +``` + +Here `Message X` - is common message structure, `MessageRelaxed X` additional type with CommonMsgInfoRelaxed body and `Message Any` is a union of both. +Message structure unified with X:Type, that in other words is a Cell. +According to TL-B we can combine all data in one cell(if it will be fit to 1023 bits) or use references declared with caret `^`. + +Serialized `Message X` placed to action list with FunC method send_raw_message(), than smart contract execute this action and message send. + +### Definition of explicit serialization + +For building valid binary data according TL-B structure we should do serialization, which defined for each type recurrently. It means, that for serialization of Message X we need to know how to serialize +`StateInit`, `CommonMsgInfo` and so on. + +Every nested structure we should get from another TL-B scheme according to link recurrently, until serialization for top structure will be explicit - every bit defined by Boolean or bit-like type(bits, uint, varuint). + +Structures that currently does not use in regular development will mark with `*` in Type column, for example \*Anycast usually skipped in serialization. + +### message$\_ + +There is the top TL-B scheme whole messages `Message X`: + +```tlb +message$_ {X:Type} info:CommonMsgInfo +init:(Maybe (Either StateInit ^StateInit)) +body:(Either X ^X) = Message X; +``` + +| Structure | Type | Required | Description | | +| ---------- | --------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------- | - | +| message$\_ | Constructor | | It defined according the constructor ruler. Empty tag `$_` means we will not add any bits in the beginning | | +| info | [CommonMsgInfo](#commonmsginfo) | Required | Detailed Message properties define destination and its value. Always placed in message root cell. | | +| init | [StateInit](#stateinit-tl-b) | Optional | General structure using in TON for initilizing new contracts. Could be write in cell reference or root cell. | | +| body | X | Required | Message Payload. Could be write in cell reference or root cell. | | + +```tlb +nothing$0 {X:Type} = Maybe X; +just$1 {X:Type} value:X = Maybe X; +left$0 {X:Type} {Y:Type} value:X = Either X Y; +right$1 {X:Type} {Y:Type} value:Y = Either X Y; +``` + +Recall how `Maybe` and `Either` works, we can serialize different cases: + +- `[CommonMsgInfo][10][StateInit][0][X]` - `Message X` in the one cell + +

+ +

+ +- `[CommonMsgInfo][11][^StateInit][1][^X]` - `Message X` with references + +

+ +

+ +## CommonMsgInfo TL-B + +### CommonMsgInfo + +`CommonMsgInfo` is a list of parameters, that defines how message will be delivered in TON blockchain. + +```tlb +//internal message +int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool + src:MsgAddressInt dest:MsgAddressInt + value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams + created_lt:uint64 created_at:uint32 = CommonMsgInfo; + +//external incoming message +ext_in_msg_info$10 src:MsgAddressExt dest:MsgAddressInt + import_fee:Grams = CommonMsgInfo; + +//external outgoing message +ext_out_msg_info$11 src:MsgAddressInt dest:MsgAddressExt + created_lt:uint64 created_at:uint32 = CommonMsgInfo; +``` + +### int_msg_info$0 + +`int_msg_info` is a case of internal message. This means they could be sent between contracts, and only between contracts. +Use case - ordinary cross contract messages. + +```tlb +nanograms$_ amount:(VarUInteger 16) = Grams; +//internal message +int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool + src:MsgAddressInt dest:MsgAddressInt + value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams + created_lt:uint64 created_at:uint32 = CommonMsgInfo; +``` + +| Structure | Type | Required | Description | +| -------------- | ------------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------- | +| int_msg_info$0 | Constructor | Required | $0 tag means, that in serialization CommonMsgInfo started with 0 bit describes a internal message. | +| ihr_disabled | Bool | Required | Hyper cube routing flag. | +| bounce | Bool | Required | Message should be bounced if there are errors during processing. If message's flat bounce = 1, it calls bounceable. | +| bounced | Bool | Required | Flag that describes, that message itself is a result of bounce. | +| src | [MsgAddressInt](#msgaddressint-tl-b) | Required | Address of smart contract sender of message. | +| dest | [MsgAddressInt](#msgaddressint-tl-b) | Required | Address of smart contract destination of message. | +| value | [CurrencyCollection](#currencycollection) | Required | Structure which describes currency information including total funds transferred in message. | +| ihr_fee | [VarUInteger 16](#varuinteger-n) | Required | Fees for hyper routing delivery | +| fwd_fee | [VarUInteger 16](#varuinteger-n) | Required | Fees for forwarding messages assigned by validators | +| created_lt | uint64 | Required | Logic time of sending message assigned by validator. Using for odering actions in smart contract. | +| created_at | uint32 | Required | Unix time | + +### ext_in_msg_info$10 + +`ext_in_msg_info$10` is a case of external incoming message. This means this type of messages sent from contracts to off-chain space.\ +Use case - wallet application request to wallet contract. + +```tlb +nanograms$_ amount:(VarUInteger 16) = Grams; +//external incoming message +ext_in_msg_info$10 src:MsgAddressExt dest:MsgAddressInt + import_fee:Grams = CommonMsgInfo; +``` + +| Structure | Type | Required | Description | +| ------------------- | ------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| ext_out_msg_info$10 | Constructor | Required | `$10` tag means, that in serialization CommonMsgInfo started with `10` bits describes a external incoming message. | +| ihr_disabled | Bool | Required | Hyper routing flag. (currently always true) | +| src | [MsgAddressExt](#msgaddressext-tl-b) | Required | Address of a external sender of the message. | +| dest | [MsgAddressInt](#msgaddressint-tl-b) | Required | Address of smart contract destination of message. | +| import_fee | [VarUInteger 16](#varuinteger-n) | Required | Fee for executing and delivering of message. | + +### ext_out_msg_info$11 + +`ext_out_msg_info$11` is a case of external outgoing message. This means they could be sent from contracts to off-chain space. +Use case - logs. + +```tlb +//internal message +ext_out_msg_info$11 src:MsgAddressInt dest:MsgAddressExt + created_lt:uint64 created_at:uint32 = CommonMsgInfo; +``` + +| Structure | Type | Required | Description | +| ------------------- | ------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------- | +| ext_out_msg_info$11 | Constructor | Required | `$11` tag means, that in serialization CommonMsgInfo started with `11` bit describes a external outgoing message. | +| src | [MsgAddressInt](#msgaddressint-tl-b) | Required | Hyper routing flag. v | +| dest | [MsgAddressExt](#msgaddressext-tl-b) | Required | General structure using in TON for initializing new contracts. Could be write in cell reference or root cell. | +| created_lt | uint64 | Required | Logic time of sending message assigned by validator. Using for odering actions in smart contract. | +| created_at | uint32 | Required | Unix time | + +## StateInit TL-B + +StateInit serves to delivery inital data to contract and used in contract deployment. + +```tlb +_ split_depth:(Maybe (## 5)) special:(Maybe TickTock) + code:(Maybe ^Cell) data:(Maybe ^Cell) + library:(HashmapE 256 SimpleLib) = StateInit; +``` + +| Structure | Type | Required | Description | +| ----------- | ------------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | +| split_depth | (## 5) | Optional | Parameter for the highload contracts, defines behaviour of splitting into multiple instances in different shards. Currently StateInit used without it. | +| special | TickTock\* | Optional | Used for invoking smart contracts in every new block of the blockchain. Available only in the masterchain. Regular user's contracts used without it. | +| code | Cell | Optional | Contract's serialized code. | +| data | Cell | Optional | Contract initial data. | +| library | HashmapE 256 SimpleLib\* | Optional | Currently used StateInit without libs | + +[General detailed explanations for Hashmaps](../data-formats/tl-b#hashmap) + +## MsgAddressExt TL-B + +```tlb +addr_none$00 = MsgAddressExt; +addr_extern$01 len:(## 9) external_address:(bits len) += MsgAddressExt; +``` + +`MsgAddress` is a scheme of various serialization for addresses. Depends on which participant(off-chain or smartcontract) messages sent, different structures using. + +### addr_none$00 + +`addr_none$00` - using for defining null address of off-chain participant. It means, that we can send external message to contract without unique sender's address. + +```tlb +addr_none$00 = MsgAddressExt; +``` + +| Structure | Type | Required | Description | +| ------------------- | ------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------- | +| addr_none$00 | Constructor | Required | `$00` tag means, that in serialization MsgAddressExt started with `00` bits. This means whole external address is `00`. | + +### addr_extern$01 + +```tlb +addr_extern$01 len:(## 9) external_address:(bits len) += MsgAddressExt; +``` + +| Structure | Type | Required | Description | +| ---------------- | ------------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------- | +| addr_extern$01 | Constructor | Required | `$01` tag means, that in the serialization MsgAddressExt started with a `01` bits describes an external address. | +| len | ## 9 | Required | Same as uintN - means an unsigned N-bit number | +| external_address | (bits len) | Required | Address is a bitstring of the len equal to previous `len` | + +## MsgAddressInt TL-B + +```tlb +addr_std$10 anycast:(Maybe Anycast) +workchain_id:int8 address:bits256 = MsgAddressInt; + +addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9) +workchain_id:int32 address:(bits addr_len) = MsgAddressInt; +``` + +### addr_std$10 + +```tlb +addr_std$10 anycast:(Maybe Anycast) +workchain_id:int8 address:bits256 = MsgAddressInt; +``` + +| Structure | Type | Required | Description | +| ------------ | ------------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------- | +| addr_std$10 | Constructor | Required | `$10` tag means, that in the serialization MsgAddressExt started with a `10` bits describes an external address. | +| anycast | Anycast\* | Optional | Additional address data, currently do not used in ordinary internal messages | +| workchain_id | int8 | Required | Workchain where smart contract of destination address placed. At moment always equals zero. | +| address | (bits256) | Required | Smart contract account ID number | + +### addr_var$11 + +```tlb +addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9) +workchain_id:int32 address:(bits addr_len) = MsgAddressInt; +``` + +| Structure | Type | Required | Description | +| ------------ | ------------------------------ | --------- | -------------------------------------------------------------------------------------------------------------------------------------------- | +| addr_var$11 | Constructor | Required | `$11` tag means, that in the serialization MsgAddressInt started with a `11` bits describes an internal contract address. | +| anycast | Anycast\* | Optional | Additional address data, currently do not used in ordinary internal messages | +| addr_len | ## 9 | Required | Same as uintN - means an unsigned N-bit number | +| workchain_id | int32 | Required | Workchain where smart contract of destination address placed. At moment always equals zero. | +| address | (bits256) | Required | Payload address(could be account ID) | + +## Basic used types + +### CurrencyCollection + +```tlb +nanograms$_ amount:(VarUInteger 16) = Grams; +currencies$_ grams:Grams other:ExtraCurrencyCollection += CurrencyCollection; +``` + +| Structure | Type | Required | Description | +| ------------- | ------------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------ | +| currencies$\_ | Constructor | Required | `$_` empty tag means, that in the serialization CurrencyCollection we will not add any bits in the beginning | +| grams | (VarUInteger 16) | Required | Message value in nanoTons | +| other | ExtraCurrencyCollection | Optional | ExtraCurrencyCollection is a dict designed for additional currencies, that usually empty | + +- ExtraCurrencyCollection complex type, that usually wrote as empty dict in messages + +### VarUInteger n + +```tlb +var_uint$_ {n:#} len:(#< n) value:(uint (len * 8)) += VarUInteger n; +var_int$_ {n:#} len:(#< n) value:(int (len * 8)) += VarInteger n; +``` + +| Structure | Type | Required | Description | +| ---------- | ------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- | +| var_uint$_ | Constructor | Required | `var_uint$_` empty tag means, that in the serialization CurrencyCollection we will not add any bits in the beginning | +| len | uintN | Required | bits len parameter for next value | +| value | (uint (len \* 8)) | Optional | uint value for integer number wrote in (len \* 8) bits | + +## Message example + +### Regular func internal message + +```func + var msg = begin_cell() + .store_uint(0, 1) ;; tag + .store_uint(1, 1) ;; ihr_disabled + .store_uint(1, 1) ;; allow bounces + .store_uint(0, 1) ;; not bounced itself + .store_slice(source) + .store_slice(destination) + ;; serialize CurrencyCollection (see below) + .store_coins(amount) + .store_dict(extra_currencies) + .store_coins(0) ;; ihr_fee + .store_coins(fwd_value) ;; fwd_fee + .store_uint(cur_lt(), 64) ;; lt of transaction + .store_uint(now(), 32) ;; unixtime of transaction + .store_uint(0, 1) ;; no init-field flag (Maybe) + .store_uint(0, 1) ;; inplace message body flag (Either) + .store_slice(msg_body) + .end_cell(); +``` + +### Regular func message in short form + +Message parts that are always overwritten by validators could be skipped(fill with zero bits). Message's sender here skipped too, serialized as `addr_none$00`. + +```func + cell msg = begin_cell() + .store_uint(0x18, 6) + .store_slice(addr) + .store_coins(amount) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) + .store_slice(message_body) +.end_cell(); +``` From 8e41c32d01c9b23556b09f27a8ac29cae71394c2 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:15 +0800 Subject: [PATCH 084/219] New translations proofs.mdx (Chinese Simplified) --- .../current/develop/data-formats/proofs.mdx | 936 ++++++++++++++++++ 1 file changed, 936 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/proofs.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/proofs.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/proofs.mdx new file mode 100644 index 0000000000..2b9d01bf6a --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/proofs.mdx @@ -0,0 +1,936 @@ +import ThemedImage from '@theme/ThemedImage'; + +# Proofs verifying (low-level) + +## Overview + +:::caution +This section describes instructions and manuals for interacting with TON at a low level. +it is assumed that you have already become familiar with [exotic cells](/develop/data-formats/exotic-cells), [TL-B language](/develop/data-formats/tl-b-language) and +understand the [simple proof verifying](/develop/data-formats/exotic-cells#simple-proof-verifying-example) example. +::: + +This article describes advanced examples of verifying proofs from Liteservers. + +It's important to check any data you receive from a node for trustless interaction with the blockchain. +However, the article covers only part of trustless communication with Liteserver, +because its assumed that you verified the Block hash you received from a Liteserver (or from anyone else). +Block hash verifying is more advanced, because you need to sync key blocks and (or) check block signatures, +and will be described in the other article in the future. But anyway even using only these examples +you're decreasing probability that Liteserver will send you the wrong data that you will believe. + +## Block Header + +Let's say we know a Block ID: + +```json + +``` + +And we ask a Liteserver for a Header for this block. Liteserver [response](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/tl/generate/scheme/lite_api.tl#L35) +contains a `header_proof` boc. + +
+ +Show boc + +```boc + +b5ee9c72010207010001470009460351ed3b9e728e7c548b15a5e5ce988b4a74984c3f8374f3f1a52c7b1f46c26406001601241011ef55aaffffff110204050601a09bc7a98700000000040101dc65010000000100ffffffff000000000000000064b6c356000023d38ba64000000023d38ba64004886d00960007028101dc64fd01dc42bec400000003000000000000002e030098000023d38b96fdc401dc650048a3971c46472b85c8d761060a6e7ae9f13a90cdda815915a89597cfecb393a6b568807adfb3c1c5efc920907225175db61ca384e4f8b313799e3cbb8b7b4085284801018c6053c1185700c0fe4311d5cf8fa533ea0382e361a7b76d0cf299b75ac0356c000328480101741100d622b0d5264bcdb86a14e36fc8c349b82ae49e037002eb07079ead8b060015284801015720b6aefcbf406209522895faa6c0d10cc3315d90bcaf09791b19f595e86f8f0007 + +``` + +
+ +After boc deserialization, we got Cell: + +```json +280[0351ED3B9E728E7C548B15A5E5CE988B4A74984C3F8374F3F1A52C7B1F46C264060016] -> { + 64[11EF55AAFFFFFF11] -> { + 640[9BC7A98700000000040101DC65010000000100FFFFFFFF000000000000000064B6C356000023D38BA64000000023D38BA64004886D00960007028101DC64FD01DC42BEC400000003000000000000002E] -> { + 608[000023D38B96FDC401DC650048A3971C46472B85C8D761060A6E7AE9F13A90CDDA815915A89597CFECB393A6B568807ADFB3C1C5EFC920907225175DB61CA384E4F8B313799E3CBB8B7B4085] + }, + 288[01018C6053C1185700C0FE4311D5CF8FA533EA0382E361A7B76D0CF299B75AC0356C0003], + 288[0101741100D622B0D5264BCDB86A14E36FC8C349B82AE49E037002EB07079EAD8B060015], + 288[01015720B6AEFCBF406209522895FAA6C0D10CC3315D90BCAF09791B19F595E86F8F0007] + } +} +``` + +that we should deserialize according to the Block [Tlb scheme](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L442): + +```python +{ + 'global_id': -239, + 'info': + { + 'version': 0, + 'not_master': 0, + 'after_merge': 0, + 'before_split': 0, + 'after_split': 0, + 'want_split': False, + 'want_merge': True, + 'key_block': False, + 'vert_seqno_incr': 0, + 'flags': 1, + 'seqno': 31220993, + 'vert_seqno': 1, + 'shard': {'shard_pfx_bits': 0, 'workchain_id': -1, 'shard_prefix': 0}, + 'gen_utime': 1689699158, + 'start_lt': 39391488000000, + 'end_lt': 39391488000004, + 'gen_validator_list_hash_short': 2288844950, + 'gen_catchain_seqno': 459393, + 'min_ref_mc_seqno': 31220989, + 'prev_key_block_seqno': 31212222, + 'gen_software': {'version': 3, 'capabilities': 46}, + 'master_ref': None, + 'prev_ref': {'type_': 'prev_blk_info', 'prev': {'end_lt': 39391487000004, 'seqno': 31220992, 'root_hash': b'H\xa3\x97\x1cFG+\x85\xc8\xd7a\x06\nnz\xe9\xf1:\x90\xcd\xda\x81Y\x15\xa8\x95\x97\xcf\xec\xb3\x93\xa6', 'file_hash': b'\xb5h\x80z\xdf\xb3\xc1\xc5\xef\xc9 \x90r%\x17]\xb6\x1c\xa3\x84\xe4\xf8\xb3\x13y\x9e<\xbb\x8b{@\x85'}}, + 'prev_vert_ref': None + }, + 'value_flow': None, + 'state_update': None, + 'extra': None +} +``` + +Now, we should check that `seqno` in deserialized block matches to block `seqno` we know, and then compute hash_1 of the only +Merkle Proof reference and compare it to block hash we know: + +```python +assert h_proof.refs[0].get_hash(0) == block_id.root_hash +``` + +Now, we can trust all other data, this Cell contains + +_Checking Proof Examples:_ [Python](https://github.com/yungwine/pytoniq-core/blob/873a96aa2256db33b8f35fbe2ab8fe8cf8ae49c7/pytoniq_core/proof/check_proof.py#L19), [Kotlin](https://github.com/andreypfau/ton-kotlin/blob/b1edc4b134e89ccf252149f27c85fd530377cebe/ton-kotlin-liteclient/src/commonMain/kotlin/CheckProofUtils.kt#L15), [C++](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/check-proof.cpp#L34) + +## Full Block + +For `liteserver.getBlock` method the proof verifying is the same as described above, however it contains full Cells instead of Pruned Branches for [Value Flow](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L464), [State Update](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L412-L413) and [Block Extra](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L452) schemas. + +## Shard Block + +Shard proofs are proofs that shard reference is actually stored in the masterchain block we provided to Liteserver. We need to check such proofs when we call `liteServer.getShardInfo`, `liteServer.getAccountState` and `liteServer.runSmcMethod` methods. + +Let's ask a Liteserver for a Shard info for masterchain block we mentioned in above: + +```python +await client.raw_get_shard_info(master, wc=0) +``` + +Liteserver response contains BlockIdExt of the shard block: + +```json + +``` + +Shard Proof boc: + +
+ +Show boc + +```boc + +b5ee9c72010219020004b9010009460332bf3592969931ca4fbc7715494b50597f1884c0d847456029d8cf0e526e6046016f0209460351ed3b9e728e7c548b15a5e5ce988b4a74984c3f8374f3f1a52c7b1f46c26406001611245b9023afe2ffffff1100ffffffff000000000000000001dc65010000000164b6c356000023d38ba6400401dc64fd600304050628480101affe84cdd73951bce07eeaad120d00400295220d6f66f1163b5fa8668202d72b000128480101faed0dd3ca110ada3d22980e3795d2bdf15450e9159892bbf330cdfd13a3b880016e22330000000000000000ffffffffffffffff820ce9d9c3929379c82807082455cc26aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac23519b11eddc69b7e090a0b0c28480101a5a7d24057d8643b2527709d986cda3846adcb3eddc32d28ec21f69e17dbaaef000128480101deab5a5aaf79c5e24f8dcbbe51747d6804104f75f58ed5bed4702c353545c6ac00110103d0400d284801015394592e3a3f1e3bc2d4249e993d0ec1e33ca18f49533991274ebc65276cd9a5001122bf0001aaa0161d000702816000047a7172dfb88800011e8b625908200ee215f71061846393a08c682e87bc3a12aff2d246eb97a09164f5657f96f9a252ef71580fe5309a823f73f3c4c3f8ab73f5a85bbf204bfd22e68d36d0efab1818e7b428be0f1028480101b20e36a3b36a4cdee601106c642e90718b0a58daf200753dbb3189f956b494b6000101db50119963380ee3280800011e9c5cb7ee0000011e9c5cb7ee29cf2e5e52dfb4ba85aecc4bc3961d06bd1f30a7290e29f26f3947d7f69c6e713f8f872e6e25c50967921c6e55b07a38968ee0279bc958eb97928065fb204a45b88000381abc00000000000000000ee327eb25b61a8a0e001343c9b67a721dcd6500202848010150fcc05bd9723571b83316a5f650be31edb131d05fdc78d271486e5d4ef077e1001928480101e5be728200b172cf7e2356cba2ae1c6e2c790be7c03cd7814c6e6fe3080b944b0011241011ef55aaffffff111213141501a09bc7a98700000000040101dc65010000000100ffffffff000000000000000064b6c356000023d38ba64000000023d38ba64004886d00960007028101dc64fd01dc42bec400000003000000000000002e16284801018c6053c1185700c0fe4311d5cf8fa533ea0382e361a7b76d0cf299b75ac0356c00032a8a0478e0f0e601ba1161ecc1395e9a0475c4f80aadbd6c483f210e96e29cf36789e432bf3592969931ca4fbc7715494b50597f1884c0d847456029d8cf0e526e6046016f016f1718284801015720b6aefcbf406209522895faa6c0d10cc3315d90bcaf09791b19f595e86f8f00070098000023d38b96fdc401dc650048a3971c46472b85c8d761060a6e7ae9f13a90cdda815915a89597cfecb393a6b568807adfb3c1c5efc920907225175db61ca384e4f8b313799e3cbb8b7b4085688c010378e0f0e601ba1161ecc1395e9a0475c4f80aadbd6c483f210e96e29cf36789e46492304dfb6ef9149781871464af686056a9627f882f60e3b24f8c944a75ebaf016f0014688c010332bf3592969931ca4fbc7715494b50597f1884c0d847456029d8cf0e526e6046da58493ccb5da3876129b0190f3c375e69e59c3ad9ff550be708999dad1f6f39016f0014 + +``` + +
+ +And `shard_descr` boc which we can use as a result if we trust Liteserver. + +
+ +Show boc + +```boc + +b5ee9c7201010201007d0001db50119963380ee3280800011e9c5cb7ee0000011e9c5cb7ee29cf2e5e52dfb4ba85aecc4bc3961d06bd1f30a7290e29f26f3947d7f69c6e713f8f872e6e25c50967921c6e55b07a38968ee0279bc958eb97928065fb204a45b88000381abc00000000000000000ee327eb25b61a8a01001343c9b67a721dcd650020 + +``` + +
+ +After the shard proof boc deserialization we got 2 roots: + +```json +[ 1 refs>, 1 refs>] +``` + +The first one is a Masterchain block Merkle Proof which we should check (using `check_block_header` function): + +```json +280[0351ED3B9E728E7C548B15A5E5CE988B4A74984C3F8374F3F1A52C7B1F46C264060016] -> { + 64[11EF55AAFFFFFF11] -> { + 640[9BC7A98700000000040101DC65010000000100FFFFFFFF000000000000000064B6C356000023D38BA64000000023D38BA64004886D00960007028101DC64FD01DC42BEC400000003000000000000002E] -> { + 608[000023D38B96FDC401DC650048A3971C46472B85C8D761060A6E7AE9F13A90CDDA815915A89597CFECB393A6B568807ADFB3C1C5EFC920907225175DB61CA384E4F8B313799E3CBB8B7B4085] + }, + 288[01018C6053C1185700C0FE4311D5CF8FA533EA0382E361A7B76D0CF299B75AC0356C0003], + 552[0478E0F0E601BA1161ECC1395E9A0475C4F80AADBD6C483F210E96E29CF36789E432BF3592969931CA4FBC7715494B50597F1884C0D847456029D8CF0E526E6046016F016F] -> { + 560[010378E0F0E601BA1161ECC1395E9A0475C4F80AADBD6C483F210E96E29CF36789E46492304DFB6EF9149781871464AF686056A9627F882F60E3B24F8C944A75EBAF016F0014], + 560[010332BF3592969931CA4FBC7715494B50597F1884C0D847456029D8CF0E526E6046DA58493CCB5DA3876129B0190F3C375E69E59C3AD9FF550BE708999DAD1F6F39016F0014] + }, + 288[01015720B6AEFCBF406209522895FAA6C0D10CC3315D90BCAF09791B19F595E86F8F0007] + } +} +``` + +The Cell + +```json +552[0478E0F0E601BA1161ECC1395E9A0475C4F80AADBD6C483F210E96E29CF36789E432BF3592969931CA4FBC7715494B50597F1884C0D847456029D8CF0E526E6046016F016F] -> { + 560[010378E0F0E601BA1161ECC1395E9A0475C4F80AADBD6C483F210E96E29CF36789E46492304DFB6EF9149781871464AF686056A9627F882F60E3B24F8C944A75EBAF016F0014], + 560[010332BF3592969931CA4FBC7715494B50597F1884C0D847456029D8CF0E526E6046DA58493CCB5DA3876129B0190F3C375E69E59C3AD9FF550BE708999DAD1F6F39016F0014] +} +``` + +Is a Merkle Update of [ShardState](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L412-L413) TLB scheme, so we need to remember the new hash. + +After we've made sure that the only Merkle Proof Cell reference Hash_1 is matching with the block hash we know, and remembered the new ShardState hash, we check the second `shard proof` Cell: + +```json +280[0332BF3592969931CA4FBC7715494B50597F1884C0D847456029D8CF0E526E6046016F] -> { + 362[9023AFE2FFFFFF1100FFFFFFFF000000000000000001DC65010000000164B6C356000023D38BA6400401DC64FD40] -> { + 288[0101AFFE84CDD73951BCE07EEAAD120D00400295220D6F66F1163B5FA8668202D72B0001], + 288[0101FAED0DD3CA110ADA3D22980E3795D2BDF15450E9159892BBF330CDFD13A3B880016E], + 204[0000000000000000FFFFFFFFFFFFFFFF820CE9D9C3929379C820] -> { + 288[0101A5A7D24057D8643B2527709D986CDA3846ADCB3EDDC32D28EC21F69E17DBAAEF0001], + 288[0101DEAB5A5AAF79C5E24F8DCBBE51747D6804104F75F58ED5BED4702C353545C6AC0011] + }, + 342[CC26AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC23519B11EDDC69B7C] -> { + 9[D000] -> { + 878[50119963380EE3280800011E9C5CB7EE0000011E9C5CB7EE29CF2E5E52DFB4BA85AECC4BC3961D06BD1F30A7290E29F26F3947D7F69C6E713F8F872E6E25C50967921C6E55B07A38968EE0279BC958EB97928065FB204A45B88000381ABC00000000000000000EE327EB25B61A88] -> { + 74[43C9B67A721DCD650000] + } + }, + 288[01015394592E3A3F1E3BC2D4249E993D0EC1E33CA18F49533991274EBC65276CD9A50011], + 766[0001AAA0161D000702816000047A7172DFB88800011E8B625908200EE215F71061846393A08C682E87BC3A12AFF2D246EB97A09164F5657F96F9A252EF71580FE5309A823F73F3C4C3F8AB73F5A85BBF204BFD22E68D36D0EFAB1818E7B428BC] -> { + 288[010150FCC05BD9723571B83316A5F650BE31EDB131D05FDC78D271486E5D4EF077E10019], + 288[0101E5BE728200B172CF7E2356CBA2AE1C6E2C790BE7C03CD7814C6E6FE3080B944B0011] + }, + 288[0101B20E36A3B36A4CDEE601106C642E90718B0A58DAF200753DBB3189F956B494B60001] + } + } +} +``` + +As we can see the only Merkle Proof reference has prefix `9023AFE2` which is [ShardStateUnsplit](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L410) +TLB Scheme prefix, so we need to compare this reference Hash_1 with the one we remembered in the previous step: + +```python +""" +Here mc_block_cell is a first shard proof root and mc_state_root is the second one. +The check_block_header_proof function returns new hash of the ShardState Merkle Update. +""" + +mc_state_hash = mc_state_root[0].get_hash(0) +state_hash = check_block_header_proof(mc_block_cell[0], blk.root_hash, True) + +if mc_state_hash != state_hash: + raise ProofError('mc state hashes mismatch') +``` + +- _Why?_ - because we've checked the block header proof and that means, that we can trust other Cell data. So now we trust the new hash of ShardState Merkle Update, and to trust the second Cell data we need to check if hashes are match. + +Now, let's deserialize the second Cell: + +```python +{ + 'global_id': -239, + 'shard_id': {'shard_pfx_bits': 0, 'workchain_id': -1, 'shard_prefix': 0}, + 'seq_no': 31220993, + 'vert_seq_no': 1, + 'gen_utime': 1689699158, + 'gen_lt': 39391488000004, + 'min_ref_mc_seqno': 31220989, + 'out_msg_queue_info': 0 refs>, + 'before_split': 0, + 'accounts': 0 refs>, + 'overload_history': 0, + 'underload_history': 18446744073709551615, + 'total_balance': {'grams': 2364000148715550620, 'other': None}, + 'total_validator_fees': {'grams': 0, 'other': None}, + 'libraries': None, + 'master_ref': None, + 'custom': { + 'shard_hashes': { + 0: {'list': [{ + 'seq_no': 36908135, + 'reg_mc_seqno': 31220993, + 'start_lt': 39391487000000, + 'end_lt': 39391487000005, + 'root_hash': b"9\xe5\xcb\xca[\xf6\x97P\xb5\xd9\x89xr\xc3\xa0\xd7\xa3\xe6\x14\xe5!\xc5>M\xe7(\xfa\xfe\xd3\x8d\xce'", + 'file_hash': b'\xf1\xf0\xe5\xcd\xc4\xb8\xa1,\xf2C\x8d\xca\xb6\x0fG\x12\xd1\xdc\x04\xf3y+\x1dr\xf2P\x0c\xbfd\tH\xb7', + 'before_split': False, + 'before_merge': False, + 'want_split': False, + 'want_merge': True, + 'nx_cc_updated': False, + 'flags': 0, + 'next_catchain_seqno': 459607, + 'next_validator_shard': 9223372036854775808, + 'min_ref_mc_seqno': 31220989, + 'gen_utime': 1689699153, + 'split_merge_at': None, + 'fees_collected': {'grams': 1016817575, 'other': None}, 'funds_created': {'grams': 1000000000, 'other': None} + }] + } + }, + 'config': {'config_addr': '5555555555555555555555555555555555555555555555555555555555555555', 'config': None}, + 'flags': 1, + 'validator_info': {'validator_list_hash_short': 2862618141, 'catchain_seqno': 459393, 'nx_cc_updated': False}, + 'prev_blocks': None, + 'after_key_block': True, + 'last_key_block': {'end_lt': 39382372000004, 'seqno': 31212222, 'root_hash': b'\xe2\x0c0\x8crt\x11\x8d\x05\xd0\xf7\x87BU\xfeZH\xddr\xf4\x12,\x9e\xac\xaf\xf2\xdf4J]\xee+', 'file_hash': b'\x01\xfc\xa6\x13PG\xee~x\x98\x7f\x15n~\xb5\x0bw\xe4\t\x7f\xa4\\\xd1\xa6\xda\x1d\xf5c\x03\x1c\xf6\x85'}, + 'block_create_stats': {'type_': 'block_create_stats', 'counters': None}, + 'global_balance': {'grams': 5089971531496870767, 'other': None} + } +} +``` + +Since we trust this Cell we can trust the Shard Block data (`ShardStateUnsplit` -> `custom` -> `shard_hashes` -> `0 (shrdblk wc)` -> `leaf`). + +_Checking Proof Examples:_ [Python](https://github.com/yungwine/pytoniq/blob/master/pytoniq/proof/check_proof.py#L43), [C++](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/check-proof.cpp#L104) + +## Account State + +Let's prove state of account `EQBvW8Z5huBkMJYdnfAEM5JqTNkuWX3diqYENkWsIL0XggGG` for the same Masterchain block we started with in the article beginning. + +Liteserver [response](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/tl/generate/scheme/lite_api.tl#L37) contains masterchain block id _(must be the same we sent to ls)_, shard block id and `shard_proof` boc which we should check as described above, +`proof` boc, and `state` boc. + +
+ +Show bocs + +```boc +Proof boc: + b5ee9c7201023d020008480100094603f93fe5eda41a6ce9ecb353fd589842bd3f5d5e73b846cb898525293fc742fd6902190209460339e5cbca5bf69750b5d9897872c3a0d7a3e614e521c53e4de728fafed38dce27001d34235b9023afe2ffffff110000000000000000000000000002332c670000000164b6c351000023d38b96fdc501dc64fd200304052848010138f8d1c6e9f798a477d13aa26cb4d6cfe1a17949ac276b2f1e0ce037a521b9bc0001221382097522af06ffaff1f0063321d90000000000000000ffffffffffffffff825d48abc1bfebfc7bc2df8993c189361000023d38b69370401dc64fd2fa78ec529bcf9931e14f9d8b27ec1469290c0baef8256d657ce573b9679c5997431fcda6bf2d0be39344a9336cfe0ae9c844a88d2bd8022102e4012a760d4db83323130104ba9157837fd7f8f8070833231301032030fdc45f2d3838090a0b284801013e38e2548c5236a9652c45e553ced677f76550097b94138a4576f122443944d400692848010159e1a18ee4e5670306b5203912c87dffc17898f0999bd128a6965027d53b6fa40215231301013fa38088aaea2b780c0d10284801016f315f25b4a39ac12c85fea4ecfe7a83e5e59d1f059783fa0c3ef2797308806100002848010188d5f8a73382aea73dede03fc3bcda2634a717ef50e7428d5a4a44c771b014b90066231301005ecd9e51e5d22a380e0f1023130100303b3b607d7ffc781112132848010182eb0e24c842092ec2705486cbbe98de8016d55f5cff4ea910471a4c3a7a1cf1003b28480101ed7e26bd36efa6d5d9b4f6aaab9813af0742a84244977f74fd4074c9c98908be000028480101ca85960e3fc3dfb6d26e83ae87a837ae5c2faf7c8d43ea177393c602fadaa0300039221100e0f41ada252e2f08141528480101d7acbb602338c86d610f35cfb362fd76fc18b1812476b6fca99a0678e665fcf50000284801014fae109c41f3d5e2be0a3ff00a007f2e50a796797700d18a7aa663e531c37180002d221100e05c33225b78bce8161728480101545b5925b3ab2a8df2470fe22a5a3c9cc64e3cb24407c26167e0fbb476e05309002c221100e03480847f372168181928480101844a14c99695506e920635d18e76d9e685adee74e5fba6f6d3b371ca77e348130029220f00d0b1cce62aecc81a1b220f00c625c7e90dfc681c1d284801019ca2157c92d49b9d051388de45d07072c78a3aa65a5b05547d94e0369aa6bdee002a284801010326812b62712345473070d679bc38cdbbce58b7a2bf6c5c6f091fc8d36e81cd001f220f00c279d628dbf2081e1f220f00c0b8f29f9d04e82021284801019143abf2a72662054eda4f4949d010c897aff4383b514b387cff790408231c6c001a28480101de5072f46a0e0ecab2bbfc2cfc62a3fe200f12d5d457df833a46eb747fa004e30059220f00c03fa2ec9ad848222328480101baee90fd11a130d6d2e2ded21ae4a7b86553116015b7e7ebfc52369534d298b20017220f00c02e722bded7282425220d00ab138e7f18482627284801017f1df311101e472b1d443334d2426fd339539f558694c60e3428221dcb1a5478001628480101e1fc242c29e519f9740ca2570d85779aed0c593cc36b59119852945988e186960015220d00a21324d3ff2828292848010199fe288fdce2606d39f9b6af72f9c2643ef06e6bacc15dd72cfa84d63c9e44a40013220d00a1e877ec8ba82a2b284801019e019e92be76a5ae7aee239299f561682afbe445dc42ee57ccc31ecb427fdf42000e220d00a1db848431a82c2d284801012345b80e66c025fb62c41261b5d230616303ec47f3bb7a255872fada62a1e8bf0010220d00a1d633bc10682e2f220d00a02ca3ddc468303128480101654781e5d466ec4ca50cb2983b20170bb5d90e2e6ab83ed7d42a829651a5eec1000a219abb19e61b8190c2587677c010ce49a93364b965f7762a9810d916b082f45e080a02bc35ebaa649b46ac72e6e4d4c1293b66d58d9ed7a54902beefd97f5bff7977dd85998b3d000023c5643934413228480101edced2278013ea497dd2e286f495b4f7f8df6ea73e08e85414fc43a611c17797000b284801018282d13bf66b9ace1fbf5d3abd1c59cc46d61af1d47af1665d3013d8f9e47488000828480101b3e9649d10ccb379368e81a3a7e8e49c8eb53f6acc69b0ba2ffa80082f70ee390001241011ef55aaffffff113536373802a09bc7a98700000000840102332c67000000010000000000000000000000000064b6c351000023d38b96fdc0000023d38b96fdc5d41c6e3c0007035701dc64fd01dc42bec400000003000000000000002e393a28480101cb54530ac857df730e82ee239b2150528c6e5f6ed3678eab6e1e789f0e3c7a5300032a8a04f2ad1ede336a68623ddabf36cb8fa405dbe70a38c453f711000f9a9f92592db0f93fe5eda41a6ce9ecb353fd589842bd3f5d5e73b846cb898525293fc742fd69021902193b3c28480101d0cf03a1058c2fd6029288951051a0d82733953c1e9181a67c502ce59b180200000b0098000023d38b69370401dc64fd2fa78ec529bcf9931e14f9d8b27ec1469290c0baef8256d657ce573b9679c5997431fcda6bf2d0be39344a9336cfe0ae9c844a88d2bd8022102e4012a760d4db0098000023d38b87bb8402332c662b4e96320f9d0afb02e5d55b6b42c3349e33540620ecc07b399211fd56e4de3e2555617cdde457cd65a0ad033aafc0c6c25df716b04e455f49179668a46300db688c0103f2ad1ede336a68623ddabf36cb8fa405dbe70a38c453f711000f9a9f92592db04a4ff9713b206e420baaee4dd21febbeb426fcd9ce158db2a56dce9188fc313e0219001b688c0103f93fe5eda41a6ce9ecb353fd589842bd3f5d5e73b846cb898525293fc742fd6987d796744ca386906016c56921370d01f72cb004a1d7c294752afe4446da07bb0219001b +State boc: + b5ee9c720102160100033c000271c006f5bc67986e06430961d9df00433926a4cd92e597ddd8aa6043645ac20bd178222c859043259e0d9000008f1590e4d10d405786bd75534001020114ff00f4a413f4bcf2c80b030051000000e929a9a317c1b3226ce226d6d818bafe82d3633aa0f06a6c677272d1f9b760ff0d0dcf56d8400201200405020148060704f8f28308d71820d31fd31fd31f02f823bbf264ed44d0d31fd31fd3fff404d15143baf2a15151baf2a205f901541064f910f2a3f80024a4c8cb1f5240cb1f5230cbff5210f400c9ed54f80f01d30721c0009f6c519320d74a96d307d402fb00e830e021c001e30021c002e30001c0039130e30d03a4c8cb1f12cb1fcbff1213141502e6d001d0d3032171b0925f04e022d749c120925f04e002d31f218210706c7567bd22821064737472bdb0925f05e003fa403020fa4401c8ca07cbffc9d0ed44d0810140d721f404305c810108f40a6fa131b3925f07e005d33fc8258210706c7567ba923830e30d03821064737472ba925f06e30d08090201200a0b007801fa00f40430f8276f2230500aa121bef2e0508210706c7567831eb17080185004cb0526cf1658fa0219f400cb6917cb1f5260cb3f20c98040fb0006008a5004810108f45930ed44d0810140d720c801cf16f400c9ed540172b08e23821064737472831eb17080185005cb055003cf1623fa0213cb6acb1fcb3fc98040fb00925f03e20201200c0d0059bd242b6f6a2684080a06b90fa0218470d4080847a4937d29910ce6903e9ff9837812801b7810148987159f31840201580e0f0011b8c97ed44d0d70b1f8003db29dfb513420405035c87d010c00b23281f2fff274006040423d029be84c6002012010110019adce76a26840206b90eb85ffc00019af1df6a26840106b90eb858fc0006ed207fa00d4d422f90005c8ca0715cbffc9d077748018c8cb05cb0222cf165005fa0214cb6b12ccccc973fb00c84014810108f451f2a7020070810108d718fa00d33fc8542047810108f451f2a782106e6f746570748018c8cb05cb025006cf165004fa0214cb6a12cb1fcb3fc973fb0002006c810108d718fa00d33f305224810108f459f2a782106473747270748018c8cb05cb025005cf165003fa0213cb6acb1f12cb3fc973fb00000af400c9ed54 +``` + +
+ +When we've checked the `Shard Proof`, we need to deserialize both `proof` and `state` cells. First, `proof` proof cell must have exactly 2 roots: + +```json + +[ 1 refs>, 1 refs>] + +``` + +The first root is a Merkle Proof for shard block (we already proved and trust it hash): + +```json +280[0339E5CBCA5BF69750B5D9897872C3A0D7A3E614E521C53E4DE728FAFED38DCE27001D] -> { + 64[11EF55AAFFFFFF11] -> { + 640[9BC7A98700000000840102332C67000000010000000000000000000000000064B6C351000023D38B96FDC0000023D38B96FDC5D41C6E3C0007035701DC64FD01DC42BEC400000003000000000000002E] -> { + 608[000023D38B69370401DC64FD2FA78EC529BCF9931E14F9D8B27EC1469290C0BAEF8256D657CE573B9679C5997431FCDA6BF2D0BE39344A9336CFE0AE9C844A88D2BD8022102E4012A760D4DB], + 608[000023D38B87BB8402332C662B4E96320F9D0AFB02E5D55B6B42C3349E33540620ECC07B399211FD56E4DE3E2555617CDDE457CD65A0AD033AAFC0C6C25DF716B04E455F49179668A46300DB] + }, + 288[0101CB54530AC857DF730E82EE239B2150528C6E5F6ED3678EAB6E1E789F0E3C7A530003], + 552[04F2AD1EDE336A68623DDABF36CB8FA405DBE70A38C453F711000F9A9F92592DB0F93FE5EDA41A6CE9ECB353FD589842BD3F5D5E73B846CB898525293FC742FD6902190219] -> { + 560[0103F2AD1EDE336A68623DDABF36CB8FA405DBE70A38C453F711000F9A9F92592DB04A4FF9713B206E420BAAEE4DD21FEBBEB426FCD9CE158DB2A56DCE9188FC313E0219001B], + 560[0103F93FE5EDA41A6CE9ECB353FD589842BD3F5D5E73B846CB898525293FC742FD6987D796744CA386906016C56921370D01F72CB004A1D7C294752AFE4446DA07BB0219001B] + }, + 288[0101D0CF03A1058C2FD6029288951051A0D82733953C1E9181A67C502CE59B180200000B] + } +} +``` + +As we did in the `Shard Proof` verifying we need to use function `check_block_header`: check if Block Cell is valid and remember the new `StateUpdate` hash. + +Then we deserialize the second root (let's call it `state_cell`) and check if its Hash_1 matches the hash we remembered: + +```python +proof_cells = Cell.from_boc(proof) +if len(proof_cells) != 2: + raise ProofError('expected 2 root cells in account state proof') + +state_cell = proof_cells[1] + +state_hash = check_block_header_proof(proof_cells[0][0], shrd_blk.root_hash, True) + +if state_cell[0].get_hash(0) != state_hash: + raise ProofError('state hashes mismatch') +``` + +Now we can trust the `state_cell` which looks like this: + +
+ +Show Cell + +```json +280[03F93FE5EDA41A6CE9ECB353FD589842BD3F5D5E73B846CB898525293FC742FD690219] -> { + 362[9023AFE2FFFFFF110000000000000000000000000002332C670000000164B6C351000023D38B96FDC501DC64FD00] -> { + 288[010138F8D1C6E9F798A477D13AA26CB4D6CFE1A17949AC276B2F1E0CE037A521B9BC0001], + 75[82097522AF06FFAFF1E0] -> { + 76[0104BA9157837FD7F8F0] -> { + 76[01032030FDC45F2D3830] -> { + 288[010159E1A18EE4E5670306B5203912C87DFFC17898F0999BD128A6965027D53B6FA40215], + 76[01013FA38088AAEA2B70] -> { + 288[010188D5F8A73382AEA73DEDE03FC3BCDA2634A717EF50E7428D5A4A44C771B014B90066], + 76[01005ECD9E51E5D22A30] -> { + 76[0100303B3B607D7FFC70] -> { + 288[0101CA85960E3FC3DFB6D26E83AE87A837AE5C2FAF7C8D43EA177393C602FADAA0300039], + 68[00E0F41ADA252E2F00] -> { + 288[01014FAE109C41F3D5E2BE0A3FF00A007F2E50A796797700D18A7AA663E531C37180002D], + 68[00E05C33225B78BCE0] -> { + 288[0101545B5925B3AB2A8DF2470FE22A5A3C9CC64E3CB24407C26167E0FBB476E05309002C], + 68[00E03480847F372160] -> { + 288[0101844A14C99695506E920635D18E76D9E685ADEE74E5FBA6F6D3B371CA77E348130029], + 60[00D0B1CCE62AECC0] -> { + 60[00C625C7E90DFC60] -> { + 288[01010326812B62712345473070D679BC38CDBBCE58B7A2BF6C5C6F091FC8D36E81CD001F], + 60[00C279D628DBF200] -> { + 60[00C0B8F29F9D04E0] -> { + 288[0101DE5072F46A0E0ECAB2BBFC2CFC62A3FE200F12D5D457DF833A46EB747FA004E30059], + 60[00C03FA2EC9AD840] -> { + 288[0101BAEE90FD11A130D6D2E2DED21AE4A7B86553116015B7E7EBFC52369534D298B20017], + 60[00C02E722BDED720] -> { + 52[00AB138E7F1840] -> { + 288[0101E1FC242C29E519F9740CA2570D85779AED0C593CC36B59119852945988E186960015], + 52[00A21324D3FF20] -> { + 288[010199FE288FDCE2606D39F9B6AF72F9C2643EF06E6BACC15DD72CFA84D63C9E44A40013], + 52[00A1E877EC8BA0] -> { + 288[01019E019E92BE76A5AE7AEE239299F561682AFBE445DC42EE57CCC31ECB427FDF42000E], + 52[00A1DB848431A0] -> { + 288[01012345B80E66C025FB62C41261B5D230616303EC47F3BB7A255872FADA62A1E8BF0010], + 52[00A1D633BC1060] -> { + 52[00A02CA3DDC460] -> { + 616[BB19E61B8190C2587677C010CE49A93364B965F7762A9810D916B082F45E080A02BC35EBAA649B46AC72E6E4D4C1293B66D58D9ED7A54902BEEFD97F5BFF7977DD85998B3D000023C564393441] -> { + 288[01018282D13BF66B9ACE1FBF5D3ABD1C59CC46D61AF1D47AF1665D3013D8F9E474880008] + }, + 288[0101EDCED2278013EA497DD2E286F495B4F7F8DF6EA73E08E85414FC43A611C17797000B] + }, + 288[0101654781E5D466EC4CA50CB2983B20170BB5D90E2E6AB83ED7D42A829651A5EEC1000A] + } + } + } + } + }, + 288[01017F1DF311101E472B1D443334D2426FD339539F558694C60E3428221DCB1A54780016] + } + } + }, + 288[01019143ABF2A72662054EDA4F4949D010C897AFF4383B514B387CFF790408231C6C001A] + } + }, + 288[01019CA2157C92D49B9D051388DE45D07072C78A3AA65A5B05547D94E0369AA6BDEE002A] + } + } + } + }, + 288[0101D7ACBB602338C86D610F35CFB362FD76FC18B1812476B6FCA99A0678E665FCF50000] + }, + 288[010182EB0E24C842092EC2705486CBBE98DE8016D55F5CFF4EA910471A4C3A7A1CF1003B], + 288[0101ED7E26BD36EFA6D5D9B4F6AAAB9813AF0742A84244977F74FD4074C9C98908BE0000] + }, + 288[0101ED7E26BD36EFA6D5D9B4F6AAAB9813AF0742A84244977F74FD4074C9C98908BE0000] + }, + 288[01016F315F25B4A39AC12C85FEA4ECFE7A83E5E59D1F059783FA0C3EF279730880610000] + }, + 288[01013E38E2548C5236A9652C45E553CED677F76550097B94138A4576F122443944D40069], + 288[0101B3E9649D10CCB379368E81A3A7E8E49C8EB53F6ACC69B0BA2FFA80082F70EE390001] + }, + 288[0101B3E9649D10CCB379368E81A3A7E8E49C8EB53F6ACC69B0BA2FFA80082F70EE390001] + }, + 868[0000000000000000FFFFFFFFFFFFFFFF825D48ABC1BFEBFC7BC2DF8993C189361000023D38B69370401DC64FD2FA78EC529BCF9931E14F9D8B27EC1469290C0BAEF8256D657CE573B9679C5997431FCDA6BF2D0BE39344A9336CFE0AE9C844A88D2BD8022102E4012A760D4DB0] -> { + 288[0101B3E9649D10CCB379368E81A3A7E8E49C8EB53F6ACC69B0BA2FFA80082F70EE390001] + } + } +} +``` + +
+ +Again, the only Merkle Proof reference has prefix `9023AFE2` which is [ShardStateUnsplit](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L410) +TLB Scheme prefix, so we're gonna deserialize it according to the TLB Scheme: + +```python +{ + 'global_id': -239, + 'shard_id': {'shard_pfx_bits': 0, 'workchain_id': 0, 'shard_prefix': 0}, + 'seq_no': 36908135, + 'vert_seq_no': 1, + 'gen_utime': 1689699153, + 'gen_lt': 39391487000005, + 'min_ref_mc_seqno': 31220989, + 'out_msg_queue_info': 0 refs>, + 'before_split': 0, + 'accounts': ( + { + 50368879097771769677871174881221998657607998794347754829932074327482686052226: { + 'account': None, + 'last_trans_hash': b'd\x9bF\xacr\xe6\xe4\xd4\xc1);f\xd5\x8d\x9e\xd7\xa5I\x02\xbe\xef\xd9\x7f[\xffyw\xdd\x85\x99\x8b=', + 'last_trans_lt': 39330697000001, + 'cell': 1 refs> + } + }, + [ + {'split_depth': 0, 'balance': {'grams': 5873792469, 'other': None}}, + {'split_depth': 0, 'balance': {'grams': 5991493155, 'other': None}}, + {'split_depth': 0, 'balance': {'grams': 63109456003, 'other': None}}, + {'split_depth': 0, 'balance': {'grams': 63822897549, 'other': None}}, + ... + {'split_depth': 0, 'balance': {'grams': 21778458402704, 'other': None}}, + {'split_depth': 0, 'balance': {'grams': 54074699968483, 'other': None}}, + {'split_depth': 0, 'balance': {'grams': 2725956214994157511, 'other': None}} + ] + ), + 'overload_history': 0, + 'underload_history': 18446744073709551615, + 'total_balance': {'grams': 2725956214994157511, 'other': None}, + 'total_validator_fees': {'grams': 37646260890702444, 'other': None}, + 'libraries': None, + 'master_ref': {'master': {'end_lt': 39391484000004, 'seqno': 31220989, 'root_hash': b'/\xa7\x8e\xc5)\xbc\xf9\x93\x1e\x14\xf9\xd8\xb2~\xc1F\x92\x90\xc0\xba\xef\x82V\xd6W\xceW;\x96y\xc5\x99', 'file_hash': b't1\xfc\xdak\xf2\xd0\xbe94J\x936\xcf\xe0\xae\x9c\x84J\x88\xd2\xbd\x80"\x10.@\x12\xa7`\xd4\xdb'}}, + 'custom': None +} +``` + +Here we need the `account` field which has [ShardAccounts](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L261) type. +`ShardAccounts` is a HashmapAugE where key is an address hash_part, value has `ShardAccount` type and extra has `DeepBalanceInfo` type. + +Parsing the address `EQBvW8Z5huBkMJYdnfAEM5JqTNkuWX3diqYENkWsIL0XggGG` we get the hash_part is equals to `50368879097771769677871174881221998657607998794347754829932074327482686052226`, so we need to +get from Hashmap value of this key: + +```python +{ + 50368879097771769677871174881221998657607998794347754829932074327482686052226: { + 'account': None, + 'last_trans_hash': b'd\x9bF\xacr\xe6\xe4\xd4\xc1);f\xd5\x8d\x9e\xd7\xa5I\x02\xbe\xef\xd9\x7f[\xffyw\xdd\x85\x99\x8b=', + 'last_trans_lt': 39330697000001, + 'cell': 1 refs> + } +} +``` + +We need to remember `last_trans_hash` and `last_trans_lt`, because we can use them to get account transactions and let's check the whole Cell of this data: + +```json +320[649B46AC72E6E4D4C1293B66D58D9ED7A54902BEEFD97F5BFF7977DD85998B3D000023C564393441] -> { + 288[01018282D13BF66B9ACE1FBF5D3ABD1C59CC46D61AF1D47AF1665D3013D8F9E474880008] +} +``` + +As we can see the Cell is an ordinary Cell with level 1, which has only one reference - the pruned account data, so let's compute the Hash_1 of this pruned branch - +this is the account state hash we can trust: `8282d13bf66b9ace1fbf5d3abd1c59cc46d61af1d47af1665d3013d8f9e47488`. + +Now the last step is to deserialize the `state` boc: + +```json +449[C006F5BC67986E06430961D9DF00433926A4CD92E597DDD8AA6043645AC20BD178222C859043259E0D9000008F1590E4D10D405786BD755300] -> { + 80[FF00F4A413F4BCF2C80B] -> { + 2[00] -> { + 4[40] -> { + 920[D001D0D3032171B0925F04E022D749C120925F04E002D31F218210706C7567BD22821064737472BDB0925F05E003FA403020FA4401C8CA07CBFFC9D0ED44D0810140D721F404305C810108F40A6FA131B3925F07E005D33FC8258210706C7567BA923830E30D03821064737472BA925F06E30D] -> { + 480[01FA00F40430F8276F2230500AA121BEF2E0508210706C7567831EB17080185004CB0526CF1658FA0219F400CB6917CB1F5260CB3F20C98040FB0006], + 552[5004810108F45930ED44D0810140D720C801CF16F400C9ED540172B08E23821064737472831EB17080185005CB055003CF1623FA0213CB6ACB1FCB3FC98040FB00925F03E2] + }, + 2[00] -> { + 2[00] -> { + 4[50] -> { + 242[B29DFB513420405035C87D010C00B23281F2FFF274006040423D029BE84C40], + 2[00] -> { + 97[ADCE76A26840206B90EB85FF80], + 97[AF1DF6A26840106B90EB858F80] + } + }, + 68[B8C97ED44D0D70B1F0] + }, + 357[BD242B6F6A2684080A06B90FA0218470D4080847A4937D29910CE6903E9FF9837812801B7810148987159F3180] + } + }, + 992[F28308D71820D31FD31FD31F02F823BBF264ED44D0D31FD31FD3FFF404D15143BAF2A15151BAF2A205F901541064F910F2A3F80024A4C8CB1F5240CB1F5230CBFF5210F400C9ED54F80F01D30721C0009F6C519320D74A96D307D402FB00E830E021C001E30021C002E30001C0039130E30D03A4C8CB1F12CB1FCBFF] -> { + 440[D207FA00D4D422F90005C8CA0715CBFFC9D077748018C8CB05CB0222CF165005FA0214CB6B12CCCCC973FB00C84014810108F451F2A702], + 448[810108D718FA00D33FC8542047810108F451F2A782106E6F746570748018C8CB05CB025006CF165004FA0214CB6A12CB1FCB3FC973FB0002], + 432[810108D718FA00D33F305224810108F459F2A782106473747270748018C8CB05CB025005CF165003FA0213CB6ACB1F12CB3FC973FB00], + 40[F400C9ED54] + } + } + }, + 321[000000E929A9A317C1B3226CE226D6D818BAFE82D3633AA0F06A6C677272D1F9B760FF0D0DCF56D800] +} +``` + +Compute its representation hash and make sure that it matches to the one we got from the Pruned: `8282d13bf66b9ace1fbf5d3abd1c59cc46d61af1d47af1665d3013d8f9e47488`. + +And deserialize it according to the [Account](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L231-L233) TLB Scheme: + +```python +{ + 'addr': Address, + 'storage_stat': {'used': {'cells': 22, 'bits': 5697, 'public_cells': None}, 'last_paid': 1689502130, 'due_payment': None}, + 'storage': { + 'last_trans_lt': 39330697000003, + 'balance': {'grams': 5873792469, 'other': None}, + 'state': { + 'type_': 'account_active', + 'state_init': {'split_depth': None, 'special': None, 'code': 1 refs>, 'data': 0 refs>, 'library': None} + } + } +} +``` + +Now we can trust this account state data. + +_Checking Proof Examples:_ [Python](https://github.com/yungwine/pytoniq/blob/master/pytoniq/proof/check_proof.py#L87), [Kotlin](https://github.com/andreypfau/ton-kotlin/blob/b1edc4b134e89ccf252149f27c85fd530377cebe/ton-kotlin-liteclient/src/commonMain/kotlin/CheckProofUtils.kt#L37), [C++](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/check-proof.cpp#L161) + +## Account transactions + +For [liteServer.getTransactions](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/tl/generate/scheme/lite_api.tl#L71) request we must provide `lt` and `hash` of the transaction to start from. +If we want to get last account transactions we can get them from `ShardAccount` (described above) and trust these `lt` and `hash`. + +When we receive transactions from the Liteserver we get boc with amount of transactions we asked roots. Each root is a Cell, which we should deserialize according to the [Transaction](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L263-L269) TLB Scheme. +For the first transaction cell we should check that its hash matches with `last_trans_hash` we got from account state. Then we remember value of `prev_trans_hash` field and compare it to the hash of the second root and so on. + +## Block transactions + +Let's ask a Liteserver for transactions belong to the block we started with in the article beginning. +LiteServer [response](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/lite_api.tl#L46) contains `ids` field with transactions and `proof` boc. First, let's deserialize the `proof`: + +```json +280[0351ED3B9E728E7C548B15A5E5CE988B4A74984C3F8374F3F1A52C7B1F46C264060016] -> { + 64[11EF55AAFFFFFF11] -> { + 288[0101F8039FE65901BE422094ED29FA05DD4A9406708D7C54EBF7F6010F2E8A9DCBB10001], + 288[01018C6053C1185700C0FE4311D5CF8FA533EA0382E361A7B76D0CF299B75AC0356C0003], + 288[0101741100D622B0D5264BCDB86A14E36FC8C349B82AE49E037002EB07079EAD8B060015], + 545[4A33F6FD11224E018A0801116DBA929FAA60F8B9DFB39286C07FDE613D4F158E4031612597E23F312DA061732C2DB7C7C7F0BCA6295EF25D04F46FA21A055CF213A1270A80] -> { + 288[0101E057F7AA0545EF9E6BF187542A5141298303A33BA7C9CE26C71FFD9C7D2050600004], + 6[00], + 6[80] -> { + 9[4000] -> { + 605[BFB333333333333333333333333333333333333333333333333333333333333333029999999999999999999999999999999999999999999999999999999999999999CF800008F4E2E9900000] -> { + 9[5000] -> { + 288[01015EF0532AF460BCF3BECF1A94597C1EC04879E0F26BF58269D319121376AAD4730002] + }, + 9[4000] -> { + 288[0101B1E091FCB9DF53917EAA0CAE05041B3D0956242871E3CA8D6909D0AA31FF36040002] + }, + 520[7239A4AED4308E2E6AC11C880CCB29DFEE407A3E94FC1EDBDD4D29AF3B5DFEEE58A9B07203A0F457150A2BF7972DA7E2A79642DEBE792E919DE5E2FC284D2B158A] + }, + 607[BF955555555555555555555555555555555555555555555555555555555555555502AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD0000008F4E2E99000C0] -> { + 288[0101924B5992DF95114196994A6D449D89E1C002CB96C14D11C4A667F843A3FAF4410002], + 520[72899B3A210DDD28D905C583FF8559BCF73D0CF0C05C11210BD7059BAB2AB453E03524184B116C9E39D9D5293179588F4B7D8F5D8192FEFE66B9FE40A71518DBC7] + } + } + }, + 288[01010FC5CF36DC84BC46E7175768AB3EC0F94988D454F2C496DC1AC32E638CD3C23D0005] + } + } +} +``` + +Now we should check the Block Header Proof (to trust this Cell data) and deserialize it according to the Block TLB Scheme: + +```python +{ + 'global_id': -239, + 'info': None, + 'value_flow': None, + 'state_update': None, + 'extra': { + 'in_msg_descr': 0 refs>, + 'out_msg_descr': ({}, [ 0 refs>]), + 'account_blocks': ( + { + 23158417847463239084714197001737581570653996933128112807891516801582625927987: { + 'account_addr': '3333333333333333333333333333333333333333333333333333333333333333', + 'transactions': ( + { + 39391488000001: 0 refs>, + 39391488000002: 0 refs> + }, + [{'grams': 0, 'other': None}, {'grams': 0, 'other': None}, {'grams': 0, 'other': None}] + ), + 'state_update': {'old_hash': b'9\xa4\xae\xd40\x8e.j\xc1\x1c\x88\x0c\xcb)\xdf\xee@z>\x94\xfc\x1e\xdb\xddM)\xaf;]\xfe\xeeX', 'new_hash': b'\xa9\xb0r\x03\xa0\xf4W\x15\n+\xf7\x97-\xa7\xe2\xa7\x96B\xde\xbey.\x91\x9d\xe5\xe2\xfc(M+\x15\x8a'} + }, + 38597363079105398474523661669562635951089994888546854679819194669304376546645: { + 'account_addr': '5555555555555555555555555555555555555555555555555555555555555555', + 'transactions': ( + { + 39391488000003: 0 refs> + }, + [{'grams': 0, 'other': None}] + ), + 'state_update': {'old_hash': b'\x89\x9b:!\r\xdd(\xd9\x05\xc5\x83\xff\x85Y\xbc\xf7=\x0c\xf0\xc0\\\x11!\x0b\xd7\x05\x9b\xab*\xb4S\xe0', 'new_hash': b'5$\x18K\x11l\x9e9\xd9\xd5)1yX\x8fK}\x8f]\x81\x92\xfe\xfef\xb9\xfe@\xa7\x15\x18\xdb\xc7'} + } + }, + [{'grams': 0, 'other': None}, {'grams': 0, 'other': None}, {'grams': 0, 'other': None}] + ), + 'rand_seed': b'\x11"N\x01\x8a\x08\x01\x11m\xba\x92\x9f\xaa`\xf8\xb9\xdf\xb3\x92\x86\xc0\x7f\xdea=O\x15\x8e@1a%', + 'created_by': b"\x97\xe2?1-\xa0as,-\xb7\xc7\xc7\xf0\xbc\xa6)^\xf2]\x04\xf4o\xa2\x1a\x05\\\xf2\x13\xa1'\n", + 'custom': None + } +} +``` + +In this case we should remember field `block` -> `extra` -> `account_blocks`, which has type of [ShardAccountBlocks](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L282), +which is HashmapAugE where key is an address hash_part, value has [AccountBlock](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L277-L280) type and extra has `CurrencyCollection` type: + +```python +{ + 23158417847463239084714197001737581570653996933128112807891516801582625927987: { + 'account_addr': '3333333333333333333333333333333333333333333333333333333333333333', + 'transactions': ( + { + 39391488000001: 0 refs>, + 39391488000002: 0 refs> + }, + [{'grams': 0, 'other': None}, {'grams': 0, 'other': None}, {'grams': 0, 'other': None}] + ), + 'state_update': {'old_hash': b'9\xa4\xae\xd40\x8e.j\xc1\x1c\x88\x0c\xcb)\xdf\xee@z>\x94\xfc\x1e\xdb\xddM)\xaf;]\xfe\xeeX', 'new_hash': b'\xa9\xb0r\x03\xa0\xf4W\x15\n+\xf7\x97-\xa7\xe2\xa7\x96B\xde\xbey.\x91\x9d\xe5\xe2\xfc(M+\x15\x8a'} + }, + 38597363079105398474523661669562635951089994888546854679819194669304376546645: { + 'account_addr': '5555555555555555555555555555555555555555555555555555555555555555', + 'transactions': ( + { + 39391488000003: 0 refs> + }, + [{'grams': 0, 'other': None}] + ), + 'state_update': {'old_hash': b'\x89\x9b:!\r\xdd(\xd9\x05\xc5\x83\xff\x85Y\xbc\xf7=\x0c\xf0\xc0\\\x11!\x0b\xd7\x05\x9b\xab*\xb4S\xe0', 'new_hash': b'5$\x18K\x11l\x9e9\xd9\xd5)1yX\x8fK}\x8f]\x81\x92\xfe\xfef\xb9\xfe@\xa7\x15\x18\xdb\xc7'} + } +} +``` + +Now lets check the `ids`: + +```python +[ + {'mode': 39, 'account': '3333333333333333333333333333333333333333333333333333333333333333', 'lt': 39391488000001, 'hash': '5ef0532af460bcf3becf1a94597c1ec04879e0f26bf58269d319121376aad473'}, + {'mode': 39, 'account': '3333333333333333333333333333333333333333333333333333333333333333', 'lt': 39391488000002, 'hash': 'b1e091fcb9df53917eaa0cae05041b3d0956242871e3ca8d6909d0aa31ff3604'}, + {'mode': 39, 'account': '5555555555555555555555555555555555555555555555555555555555555555', 'lt': 39391488000003, 'hash': '924b5992df95114196994a6d449d89e1c002cb96c14d11c4a667f843a3faf441'} +] +``` + +For every transaction here we need to find it in the `account_block` we remembered and compare hashes: + +```python +block_trs: dict = acc_block.get(int(tr['account'], 16)).transactions[0] +block_tr: Cell = block_trs.get(tr['lt']) +assert block_tr.get_hash(0) == tr['hash'] +``` + +:::note + +In this example it was unnecessary to check `ids` field, we could just take transactions from the account block. +But when you request the [liteServer.listBlockTransactionsExt](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/lite_api.tl#L80) method +you check proofs similar but in that case you really need to compare hashes. + +::: + +## Config + +Let's ask a Liteserver for 1, 4, 5, 7, 8 and 15 [Config params](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/lite_api.tl#L83) (for [liteServer.getConfigAll](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/lite_api.tl#L82) where you get all params, the proof verifying is the same). +The [response](https://github.com/ton-blockchain/ton/blob/master/tl/generate/scheme/lite_api.tl#L53) contains `state_proof` and `config_proof`. + +First, let's deserialize the `state_proof` Cell: + +```json +280[0351ED3B9E728E7C548B15A5E5CE988B4A74984C3F8374F3F1A52C7B1F46C264060016] -> { + 64[11EF55AAFFFFFF11] -> { + 640[9BC7A98700000000040101DC65010000000100FFFFFFFF000000000000000064B6C356000023D38BA64000000023D38BA64004886D00960007028101DC64FD01DC42BEC400000003000000000000002E] -> { + 608[000023D38B96FDC401DC650048A3971C46472B85C8D761060A6E7AE9F13A90CDDA815915A89597CFECB393A6B568807ADFB3C1C5EFC920907225175DB61CA384E4F8B313799E3CBB8B7B4085] + }, + 288[01018C6053C1185700C0FE4311D5CF8FA533EA0382E361A7B76D0CF299B75AC0356C0003], + 552[0478E0F0E601BA1161ECC1395E9A0475C4F80AADBD6C483F210E96E29CF36789E432BF3592969931CA4FBC7715494B50597F1884C0D847456029D8CF0E526E6046016F016F] -> { + 560[010378E0F0E601BA1161ECC1395E9A0475C4F80AADBD6C483F210E96E29CF36789E46492304DFB6EF9149781871464AF686056A9627F882F60E3B24F8C944A75EBAF016F0014], + 560[010332BF3592969931CA4FBC7715494B50597F1884C0D847456029D8CF0E526E6046DA58493CCB5DA3876129B0190F3C375E69E59C3AD9FF550BE708999DAD1F6F39016F0014] + }, + 288[01015720B6AEFCBF406209522895FAA6C0D10CC3315D90BCAF09791B19F595E86F8F0007] + } +} +``` + +For it, we should check Block Header proof and remember the `StateUpdate` new hash. + +Now, let's deserialize the `config_proof` Cell: + +
+ +Show Cell + +```json +280[0332BF3592969931CA4FBC7715494B50597F1884C0D847456029D8CF0E526E6046016F] -> { + 362[9023AFE2FFFFFF1100FFFFFFFF000000000000000001DC65010000000164B6C356000023D38BA6400401DC64FD40] -> { + 288[0101AFFE84CDD73951BCE07EEAAD120D00400295220D6F66F1163B5FA8668202D72B0001], + 288[0101FAED0DD3CA110ADA3D22980E3795D2BDF15450E9159892BBF330CDFD13A3B880016E], + 204[0000000000000000FFFFFFFFFFFFFFFF820CE9D9C3929379C820] -> { + 288[0101A5A7D24057D8643B2527709D986CDA3846ADCB3EDDC32D28EC21F69E17DBAAEF0001], + 288[0101DEAB5A5AAF79C5E24F8DCBBE51747D6804104F75F58ED5BED4702C353545C6AC0011] + }, + 342[CC26AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC23519B11EDDC69B7C] -> { + 288[0101C7DAE90A1FCEAD235CACC318A048986B2E12D0F68C136845669E02C4E28F018D0002], + 2[00] -> { + 8[D8] -> { + 2[00] -> { + 2[00] -> { + 2[00] -> { + 2[00] -> { + 2[00] -> { + 2[00] -> { + 288[0101F89085ED347F5F928A0DF7B1271F906F6E1EF43D89B5912774C8B42D0E24AB120001], + 2[00] -> { + 256[3333333333333333333333333333333333333333333333333333333333333333] + } + }, + 4[40] -> { + 256[0000000000000000000000000000000000000000000000000000000000000000] + } + }, + 2[00] -> { + 2[00] -> { + 2[00] -> { + 256[E56754F83426F69B09267BD876AC97C44821345B7E266BD956A7BFBFB98DF35C] + }, + 2[00] -> { + 329[01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF800000008000000100] + } + }, + 4[50] -> { + 1[80] -> { + 2[00] -> { + 83[BE000003BCB3670DC15540], + 83[BFFFFFFFBCBD1A94A20000] + } + } + } + } + }, + 2[00] -> { + 2[00] -> { + 2[00] -> { + 2[00] -> { + 104[C400000002000000000000002E] + }, + 288[0101C1F3C2ADA12BD901BBA1552C0C090CC3989649807C2B764D02548C1F664C20890007] + }, + 288[010187DADFBB3AE954E7F5472C46A729ED80AD087C5D9CEBB8D644D16DD73F88DF390009] + }, + 2[00] -> { + 288[01017CF937AF64AED1AB2CDD1435F8FF79F86E521320CC7B0CB30C9AAE81748124090002], + 2[00] -> { + 288[0101BEE8EB75C37500A75962E4FD99AFC62B3C9245948D2AC56061B0E21DDD6E9E840001], + 2[00] -> { + 128[00010000000080000000200000008000] + } + } + } + } + }, + 288[0101289F7704162F68EF3CC5B4865BD72067277E25B21514AB741396C54BD92294FA0009] + }, + 288[0101EF6962F43C1C86B216773B443F61829550DD9E956EE54EA3AC5C60E127DADD51000E] + }, + 288[0101112A0556A091DC4F72BD31FF2790783FB3238CE2AA41E1C137424D279664D7E3000A] + }, + 288[010124D21CF7AE96B1C55A1230E823DB0317CE24EC33E3BF2585C79605684304FAF20007] + }, + 766[0001AAA0161D000702816000047A7172DFB88800011E8B625908200EE215F71061846393A08C682E87BC3A12AFF2D246EB97A09164F5657F96F9A252EF71580FE5309A823F73F3C4C3F8AB73F5A85BBF204BFD22E68D36D0EFAB1818E7B428BC] -> { + 288[010150FCC05BD9723571B83316A5F650BE31EDB131D05FDC78D271486E5D4EF077E10019], + 288[0101E5BE728200B172CF7E2356CBA2AE1C6E2C790BE7C03CD7814C6E6FE3080B944B0011] + }, + 2[00] -> { + 83[BE000003BCB3670DC15540], + 83[BFFFFFFFBCBD1A94A20000] + } + } + } +} +``` + +
+ +We need to compare Hash_1 of this Merkle Proof only reference with the hash we got from `check_block_header` function above, so we can trust this Cell: + +```python +state_hash = check_block_header_proof(state_proof[0], block.root_hash, True) +if config_proof[0].get_hash(0) != state_hash: + raise LiteClientError('hashes mismach') +``` + +Now, let's deserialize the Cell according to the `ShardStateUnsplit` scheme: + +```python +{ + 'global_id': -239, + 'shard_id': {'shard_pfx_bits': 0, 'workchain_id': -1, 'shard_prefix': 0}, + 'seq_no': 31220993, + 'vert_seq_no': 1, + 'gen_utime': 1689699158, + 'gen_lt': 39391488000004, + 'min_ref_mc_seqno': 31220989, + 'out_msg_queue_info': 0 refs>, + 'before_split': 0, + 'accounts': 0 refs>, + 'overload_history': 0, + 'underload_history': 18446744073709551615, + 'total_balance': {'grams': 2364000148715550620, 'other': None}, + 'total_validator_fees': {'grams': 0, 'other': None}, + 'libraries': None, + 'master_ref': None, + 'custom': { + 'shard_hashes': None, + 'config': { + 'config_addr': '5555555555555555555555555555555555555555555555555555555555555555', + 'config': { + 1: 0 refs>, + 4: 0 refs>, + 5: 0 refs>, + 7: 1 refs>, + 8: 0 refs>, + 15: 0 refs>} + }, + 'flags': 1, + 'validator_info': {'validator_list_hash_short': 2862618141, 'catchain_seqno': 459393, 'nx_cc_updated': False}, + 'prev_blocks': None, + 'after_key_block': True, + 'last_key_block': {'end_lt': 39382372000004, 'seqno': 31212222, 'root_hash': b'\xe2\x0c0\x8crt\x11\x8d\x05\xd0\xf7\x87BU\xfeZH\xddr\xf4\x12,\x9e\xac\xaf\xf2\xdf4J]\xee+', 'file_hash': b'\x01\xfc\xa6\x13PG\xee~x\x98\x7f\x15n~\xb5\x0bw\xe4\t\x7f\xa4\\\xd1\xa6\xda\x1d\xf5c\x03\x1c\xf6\x85'}, + 'block_create_stats': {'type_': 'block_create_stats', 'counters': None}, + 'global_balance': {'grams': 5089971531496870767, 'other': {239: 666666666666, 4294967279: 1000000000000}} + } +} +``` + +And take the `ShardStateUnsplit` -> `custom` -> `config` -> `config` field, which is a Hashmap where key is a ConfigParam number and value is a Cell with parameter value. + +After deserialization of all params we got: + +```python +{ + 1: { + 'elector_addr': b'33333333333333333333333333333333', + }, + 4: { + 'dns_root_addr': b'\xe5gT\xf84&\xf6\x9b\t&{\xd8v\xac\x97\xc4H!4[~&k\xd9V\xa7\xbf\xbf\xb9\x8d\xf3\\', + }, + 5: { + 'blackhole_addr': b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff', + 'fee_burn_nom': 1, + 'fee_burn_denom': 2 + }, + 7: { + 'to_mint': {'dict': {239: 666666666666, 4294967279: 1000000000000}} + }, + 8: { + 'version': 2, + 'capabilities': 46 + }, + 15: { + 'validators_elected_for': 65536, + 'elections_start_before': 32768, + 'elections_end_before': 8192, + 'stake_held_for': 32768 + } +} +``` + +## See Also + +- [Exotic Cells](/develop/data-formats/exotic-cells) From 07194b73676261f74ca95c7c6658b80a41bf1b2a Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:16 +0800 Subject: [PATCH 085/219] New translations tl-b-language.mdx (Chinese Simplified) --- .../develop/data-formats/tl-b-language.mdx | 662 ++++++++++++++++++ 1 file changed, 662 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/tl-b-language.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/tl-b-language.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/tl-b-language.mdx new file mode 100644 index 0000000000..5440b91ba0 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/tl-b-language.mdx @@ -0,0 +1,662 @@ +import ThemedImage from '@theme/ThemedImage'; + +# TL-B Language + +TL-B (Type Language - Binary) serves to describe the type system, constructors and existing functions. For example, we +can use TL-B schemes to build binary structures associated with TON Blockchain. Special TL-B parsers can read schemes to +deserialize binary data into different objects. TL-B describes data schemes for `Cell` objects. If you not familiar +with `Cells`, please read [Cell & Bag of Cells(BOC)](https://docs.ton.org/develop/data-formats/cell-boc#cell) article. + +## Overview + +We refer to any set of TL-B constructs as TL-B documents. A TL-B document usually consists of declarations of types ( +i.e. their constructors) and functional combinators. The declaration of each combinator ends with a semicolon (`;`). + +Here is an example of a possible combinator declaration: + +

+ +

+ +## Constructors + +The left-hand side of each equation describes the way to define, or serialize, a value of the type indicated on the +right-hand side. Such a description begins with the name of a constructor. + +

+ +

+ +Constructors are used to specify the type of combinator, including the state at serialization. For example, constructors +can also be used when you want to specify an `op`(operation code) in query to a smart contract in TON. + +```tlb +// .... +transfer#5fcc3d14 <...> = InternalMsgBody; +// .... +``` + +- constructor name: `transfer` +- constructor prefix code: `#5fcc3d14` + +Notice, every constructor name immediately followed by an optional constructor tag, such as `#_` or `$10`, which +describes the bitstring used to encode (serialize) the constructor in question. + +```tlb +message#3f5476ca value:# = CoolMessage; +bool_true$0 = Bool; +bool_false$1 = Bool; +``` + +The left-hand side of each equation describes the way to define, or serialize, a value of the type indicated on the +right-hand side. Such a description begins with the name of a constructor, such as `message` or `bool_true`, immediately +followed by an optional constructor tag, such as `#3f5476ca` or `$0`, which describes the bits used to encode ( +serialize) +the constructor in question. + +| constructor | serialization | +| --------------------------- | ----------------------------------------- | +| `some#3f5476ca` | 32-bit uint serialize from hex value | +| `some#5fe` | 12-bit uint serialize from hex value | +| `some$0101` | serialize `0101` raw bits | +| `some` or `some#` | serialize `crc32(equation) \| 0x80000000` | +| `some#_` or `some$_` or `_` | serialize nothing | + +Constructor names (`some` in this example) are used as variables in codegen. For example: + +```tlb +bool_true$1 = Bool; +bool_false$0 = Bool; +``` + +Type `Bool` has two tags `0` and `1`. Codegen pseudocode might look like: + +```python3 + +class Bool: + tags = [1, 0] + tags_names = ['bool_true', 'bool_false'] +``` + +If you don't want to define any name for current constructor, just pass `_`, e.g. `_ a:(## 32) = 32Int;` + +Constructor tags may be given in either binary (after a dollar sign) or hexadecimal notation (after a hash sign). If a +tag is not +explicitly provided, the TL-B parser must compute a default 32-bit constructor tag by hashing with CRC32 algorithm +the text of the “equation” with `| 0x80000000` defining this constructor in a certain fashion. Therefore, empty tags +must be explicitly provided by `#_` or `$_`. + +This tag willies used to guess current type of bitstring in deserialization process. E.g. we have 1 bit bitstring `0`, +if we tell TLB to parse this bitstring in type of `Bool` it will parse it as `Bool.bool_false`. + +Let's say we have more complex examples: + +```tbl +tag_a$10 val:(## 32) = A; +tag_b$00 val(## 64) = A; +``` + +If we parse `1000000000000000000000000000000001` (1 and 32 zeroes and 1) in TLB type `A` - firstly we need to get first +two bits to define tag. In this example `10` is two first bits and they represent `tag_a`. So now we know that next 32 +bits are `val` variable, `1` in our example. Some "parsed" pseudocode variables may look like: + +```python3 +A.tag = 'tag_a' +A.tag_bits = '10' +A.val = 1 +``` + +All constructor names must be distinct and constructor tags for the same type must constitute a prefix code (otherwise +the deserialization would not be unique); i.e. no tag can be a prefix of any other in same type. + +Maximum number of constructors per one type: `64` +Maximum bits for tag: `63` + +Binary example: + +```tlb +example_a$10 = A; +example_b$01 = A; +example_c$11 = A; +example_d$00 = A; +``` + +Codegen pseudocode might look like: + +```python3 + +class A: + tags = [2, 1, 3, 0] + tags_names = ['example_a', 'example_b', 'example_c', 'example_d'] +``` + +Hex tag example: + +```tlb +example_a#0 = A; +example_b#1 = A; +example_c#f = A; +``` + +Codegen pseudocode might look like: + +```python3 + +class A: + tags = [0, 1, 15] + tags_names = ['example_a', 'example_b', 'example_c'] +``` + +If you use `hex` tag, keep in mind that it will be serialized as 4 bits for each hex symbol. Maximum value is 63-bit +unsigned integer. This means: + +```tlb +a#32 a:(## 32) = AMultiTagInt; +b#1111 a:(## 32) = AMultiTagInt; +c#5FE a:(## 32) = AMultiTagInt; +d#3F5476CA a:(## 32) = AMultiTagInt; +``` + +| constructor | serialization | +| ------------ | ------------------------------------ | +| `a#32` | 8-bit uint serialize from hex value | +| `b#1111` | 16-bit uint serialize from hex value | +| `c#5FE` | 12-bit uint serialize from hex value | +| `d#3F5476CA` | 32-bit uint serialize from hex value | + +Also hex values allowed both in upper and lower case. + +#### More about hex tags + +In addition to the classic hex tag definition, a hexadecimal number can be followed by the underscore character. +This means that the tag is equal to the specified hexadecimal number without the least significant bit. +For example there is a scheme: + +```tlb +vm_stk_int#0201_ value:int257 = VmStackValue; +``` + +And the tag is not actually equal to `0x0201`. To compute it we need to remove LSb from the binary representation of `0x0201`: + +``` +0000001000000001 -> 000000100000000 +``` + +So the tag equals to the 15-bit binary number `0b000000100000000`. + +## Field definitions + +The constructor and its optional tag are followed by field definitions. Each field definition is of the +form `ident:type-expr`, where ident is an identifier with the name of the field (replaced by an underscore for +anonymous fields), and type-expr is the field’s type. The type provided here is a type expression, which may include +simple types, parametrized types with suitable parameters or complex expressions. + +In sum up all fields defined in type must not be greater than Cell (`1023` bits and `4` refs) + +### Simple types + +- `_ a:# = Type;` - `Type.a` here is 32-bit integer +- `_ a:(## 64) = Type;` - `Type.a` here is 64-bit integer +- `_ a:Owner = NFT;` - `NFT.a` here is `Owner` type +- `_ a:^Owner = NFT;` - `NFT.a` here is cell ref to `Owner` type means `Owner` is stored in next cell reference. + +### Anonymous fields + +- `_ _:# = A;` - first field is anonymous 32-bit integer + +### Extend cell with references + +```tlb +_ a:(##32) ^[ b:(##32) c:(## 32) d:(## 32)] = A; +``` + +- If for some reason we want to separate some fields to another + cell we can use `^[ ... ]` syntax. + In this example `A.a` / `A.b` / `A.c` / `A.d` are 32-bit unsigned integers, but `A.a` is stored in first cell, + and `A.b` / `A.c` / `A.d` are stored in next cell (1 ref) + +```tlb +_ ^[ a:(## 32) ^[ b:(## 32) ^[ c:(## 32) ] ] ] = A; +``` + +- Chain of references are also allowed. In this example each of + variables (`a`, `b`, `c`) are stored in separated cells + +### Parametrized types + +Suppose we have `IntWithObj` type: + +```tlb +_ {X:Type} a:# b:X = IntWithObj X; +``` + +Now we can use it in other types: + +```tlb +_ a:(IntWithObj uint32) = IntWithUint32; +``` + +### Complex expressions + +- Conditional fields (only for `Nat`) (`E?T` means if expression `E` is True than field has type `T`) + ```tlb + _ a:(## 1) b:a?(## 32) = Example; + ``` + In `Example` type variable `b` serialized only if `a` is `1` + +- Multiply expression for tuples creation (`x * T` means create tuple of length `x` of type `T`): + + ```tlb + a$_ a:(## 32) = A; + b$_ b:(2 * A) = B; + ``` + + ```tlb + _ (## 1) = Bit; + _ 2bits:(2 * Bit) = 2Bits; + ``` + +- Bit selection (only for `Nat`) (`E . B` means take bit `B` of `Nat` `E`) + ```tlb + _ a:(## 2) b:(a . 1)?(## 32) = Example; + ``` + In `Example` type variable `b` serialized only if second bit `a` is `1` + +- Other `Nat` operators also allowed (look `Allowed contraints`) + +Note: you can combine several complex expressions: + +```tlb +_ a:(## 1) b:(## 1) c:(## 2) d:(a?(b?((c . 1)?(## 64)))) = A; +``` + +## Built-in types + +- `#` - `Nat` 32 bits unsigned integer +- `## x` - `Nat` with `x` bits +- `#< x` - `Nat` less than `x` bit unsigned integer stored as `lenBits(x - 1)` bits, up to 31 bits +- `#<= x` - `Nat` less or equal than `x` bit unsigned integer stored as `lenBits(x)` bits, up to 32 bits +- `Any` / `Cell` - rest of cell bits&refs +- `Int` - 257 bits +- `UInt` - 256 bits +- `Bits` - 1023 bits +- `uint1` - `uint256` - 1 - 256 bits +- `int1` - `int257` - 1 - 257 bits +- `bits1` - `bits1023` - 1 - 1023 bits +- `uint X` / `int X` / `bits X` - same as `uintX` but you can use parametrized `X` in this types + +## Constraints + +```tlb +_ flags:(## 10) { flags <= 100 } = Flag; +``` + +`Nat` fields allowed in constraints. In this example `{ flags <= 100 }` constraint means that `flags` variable is less +or +equal `100`. + +Allowed contraints: `E` | `E = E` | `E <= E` | `E < E` | `E >= E` | `E > E` | `E + E` | `E * E` | `E ? E` + +## Implicit fields + +Some fields may be implicit. Their definitions are surrounded by curly +brackets(`{`, `}`), which indicate that the field is not actually present in the serialization, but that its value must +be deduced from other data (usually the parameters of the type being serialized). Example: + +```tlb +nothing$0 {X:Type} = Maybe X; +just$1 {X:Type} value:X = Maybe X; +``` + +```tlb +_ {x:#} a:(## 32) { ~x = a + 1 } = Example; +``` + +## Parametrized types + +Variables — i.e. the (identifiers of the) previously +defined fields of types `#` (natural numbers) or `Type` (type of types) — may be used as parameters for the parametrized +types. The serialization process recursively serializes each field according to its type and the serialization of a +value ultimately consists of the concatenation of bits representing the constructor (i.e. the constructor tag) and +the field values. + +### Natural numbers (`Nat`) + +```tlb +_ {x:#} my_val:(## x) = A x; +``` + +Means that `A` is parametrized by `x` `Nat`. In deserialization process we will fetch `x`-bit unsigned integer E.g.: + +```tlb +_ value:(A 32) = My32UintValue; +``` + +Means than in deserialization process of `My32UintValue` type we will fetch 32-bit unsigned integer (because of `32` +parameter to `A` type) + +### Types + +```tlb +_ {X:Type} my_val:(## 32) next_val:X = A X; +``` + +Means that `A` is parametrized by `X` type. In deserialization process we will fetch 32-bit unsigned integer and than +parse +bits&refs of type `X`. + +Usage example of such parametrized type can be: + +```tlb +_ bit:(## 1) = Bit; +_ 32intwbit:(A Bit) = 32IntWithBit; +``` + +In this example we pass type `Bit` to `A` as parameter. + +If you don't want to define type, but want to deserialize by this scheme you may use `Any` word: + +```tlb +_ my_val:(A Any) = Example; +``` + +Means that if we deserialize `Example` type we will fetch 32-bit integer and then rest of cell (bits&refs) to `my_val`. + +You can create complex types with several parameters: + +```tlb +_ {X:Type} {Y:Type} my_val:(## 32) next_val:X next_next_val:Y = A X Y; +_ bit:(## 1) = Bit; +_ a_with_two_bits:(A Bit Bit) = AWithTwoBits; +``` + +Also you can use partial apply on such parametrized types: + +```tlb +_ {X:Type} {Y:Type} v1:X v2:Y = A X Y; +_ bit:(## 1) = Bit; +_ {X:Type} bits:(A Bit X) = BitA X; +``` + +Or even parametrized types itself: + +```tlb +_ {X:Type} v1:X = A X; +_ {X:Type} d1:X = B X; +_ {X:Type} bits:(A (B X)) = AB X; +``` + +### NAT fields usage for parametrized types + +You can use fields defined previously like parameters to types. Serialization will be determinate in runtime. + +Simple example: + +```tlb +_ a:(## 8) b:(## a) = A; +``` + +This means that we store size of `b` field inside of `a` field. So when we want to serialize type `A` we need to load 8 +bit unsigned integer of `a` field and then use this number to determinate size of `b` field. + +This strategy works for parametrized types as well: + +```tlb +_ {input:#} c:(## input) = B input; +_ a:(## 8) c_in_b:(B a) = A; +``` + +### Expression in parametrized types + +```tlb +_ {x:#} value:(## x) = Example (x * 2); +_ _:(Example 4) = 2BitInteger; +``` + +In this example `Example.value` type is determinate in runtime. + +In `2BitInteger` definition we set value `Example 4` type. To determinate this type we use `Example (x * 2)` +definition and calculate `x` by formula (`y = 2`, `z = 4`): + +```c++ +static inline bool mul_r1(int& x, int y, int z) { + return y && !(z % y) && (x = z / y) >= 0; +} +``` + +We can also use add operator: + +```tlb +_ {x:#} value:(## x) = ExampleSum (x + 3); +_ _:(ExampleSum 4) = 1BitInteger; +``` + +In `1BitInteger` definition we set value `ExampleSum 4` type. To determinate this type we use `ExampleSum (x + 3)` +definition and calculate `x` by formula (`y = 3`, `z = 4`): + +```c++ +static inline bool add_r1(int& x, int y, int z) { + return z >= y && (x = z - y) >= 0; +} +``` + +## Negate operator (`~`) + +Some occurrences of “variables” (i.e. already-defined fields) are prefixed by a tilde(`~`). This indicates that the +variable’s occurrence is used in the opposite way to the default behavior: on the left-hand side of the equation, it +means that the variable will be deduced (computed) based on this occurrence, instead of substituting its previously +computed value; in the right-hand side, conversely, it means that the variable will not be deduced from the type being +serialized, but rather that it will be computed during the deserialization process. In other words, a tilde transforms +an “input argument” into an “output argument” or vice versa. + +Simple example for negate operator is definition of new variable base on another variable: + +```tlb +_ a:(## 32) { b:# } { ~b = a + 100 } = B_Calc_Example; +``` + +After definition, you can use new variable for passing it to `Nat` types: + +```tlb +_ a:(## 8) { b:# } { ~b = a + 10 } + example_dynamic_var:(## b) = B_Calc_Example; +``` + +The size of `example_dynamic_var` will be computed in runtime, when we load `a` variable and use it value for +determination of `example_dynamic_var` size. + +Or to other types: + +```tlb +_ {X:Type} a:^X = PutToRef X; +_ a:(## 32) { b:# } { ~b = a + 100 } + my_ref: (PutToRef b) = B_Calc_Example; +``` + +Also you can define variables with negate operator in add or multiply complex expressions: + +```tlb +_ a:(## 32) { b:# } { ~b + 100 = a } = B_Calc_Example; +``` + +```tlb +_ a:(## 32) { b:# } { ~b * 5 = a } = B_Calc_Example; +``` + +### Negate operator (`~`) in type definition + +```tlb +_ {m:#} n:(## m) = Define ~n m; +_ {n_from_define:#} defined_val:(Define ~n_from_define 8) real_value:(## n_from_define) = Example; +``` + +Assume we have class `Define ~n m` which takes `m` and compute `n` loading it from `m` bit unsigned integer. + +In `Example` type we store variable computed by `Define` type into `n_from_define`, also we know that it's `8` bit +unsigned integer, because we apply `Define` type with `Define ~n_from_define 8`. Now we can use `n_from_define` variable +in other types to determinate serialization process. + +This technic lead to more complex type definitions (such as Unions, Hashmaps). + +```tlb +unary_zero$0 = Unary ~0; +unary_succ$1 {n:#} x:(Unary ~n) = Unary ~(n + 1); +_ u:(Unary Any) = UnaryChain; +``` + +This is example has good explanation in [TL-B Types](https://docs.ton.org/develop/data-formats/tl-b-types#unary) +article. The main idea here is that `UnaryChain` will recursively deserialize until reach of `unary_zero$0` (because we +know last element of `Unary X` type by definition `unary_zero$0 = Unary ~0;` and `X` is calculated in runtime +due `Unary ~(n + 1)` definition). + +Note: `x:(Unary ~n)` means that `n` is defined in process of serialization of `Unary` class. + +## Special types + +Currently, TVM allow types of cells: + +- Ordinary +- PrunnedBranch +- Library +- MerkleProof +- MerkleUpdate + +By default, all cells are `Ordinary`. And all cells described in tlb are `Ordinary`. + +To allow load of special types in constructor you need to add `!` before constructor. + +Example: + +```tlb +!merkle_update#02 {X:Type} old_hash:bits256 new_hash:bits256 + old:^X new:^X = MERKLE_UPDATE X; + +!merkle_proof#03 {X:Type} virtual_hash:bits256 depth:uint16 virtual_root:^X = MERKLE_PROOF X; +``` + +This technic allow codegen code to mark `SPECIAL` cells when you want to print structure, also it allow to correctly +validate structures with special cells. + +## Several instances for one type without constructor uniqueness tag check + +It's allowed to create several instances of one type depending only on type parameters. +In this way of definition constructor tag unique check will not be applied. + +Example: + +```tlb +_ = A 1; +a$01 = A 2; +b$01 = A 3; +_ test:# = A 4; +``` + +Means that actual tag for deserialization will be determinate by `A` type parameter: + +```python3 +# class for type `A` +class A(TLBComplex): + class Tag(Enum): + a = 0 + b = 1 + cons1 = 2 + cons4 = 3 + + cons_len = [2, 2, 0, 0] + cons_tag = [1, 1, 0, 0] + + m_: int = None + + def __init__(self, m: int): + self.m_ = m + + def get_tag(self, cs: CellSlice) -> Optional["A.Tag"]: + tag = self.m_ + + if tag == 1: + return A.Tag.cons1 + + if tag == 2: + return A.Tag.a + + if tag == 3: + return A.Tag.b + + if tag == 4: + return A.Tag.cons4 + + return None +``` + +Same works with several parameters: + +```tlb +_ = A 1 1; +a$01 = A 2 1; +b$01 = A 3 3; +_ test:# = A 4 2; +``` + +Please, keep in mind that when you add parametrized type definition, tags between predefined type definition (`a` +and `b` in our example) and +parametrized type definition (`c` in our example) must be unique: + +_Not valid example:_ + +``` +a$01 = A 2 1; +b$11 = A 3 3; +c$11 {X:#} {Y:#} = A X Y; +``` + +_Valid example:_ + +```tlb +a$01 = A 2 1; +b$01 = A 3 3; +c$11 {X:#} {Y:#} = A X Y; +``` + +## Comments + +Comments are the same as in C++ + +```tlb +/* +This is +a comment +*/ + +// This is one line comment +``` + +## IDE Support + +The [intellij-ton](https://github.com/andreypfau/intellij-ton) plugin supports Fift, FunC and also TL-B.\ +The TL-B grammar is described in +the [TlbParser.bnf](https://github.com/ton-blockchain/intellij-ton/blob/main/src/main/grammar/TlbParser.bnf) file. + +## Useful sources + +- [A description of an older version of TL](https://core.telegram.org/mtproto/TL) +- [block.tlb](https://github.com/ton-blockchain/ton/blob/master/crypto/block/block.tlb) +- [tlbc tool](https://github.com/ton-blockchain/ton/blob/master/crypto/tl/tlbc.cpp) +- [CPP Codegen](https://github.com/ton-blockchain/ton/blob/master/crypto/tl/tlbc-gen-cpp.cpp) +- [tonpy tlb tests](https://github.com/disintar/tonpy/blob/main/src/tonpy/tests/test_tlb.py) +- [tonpy py codegen](https://github.com/disintar/ton/blob/master/crypto/tl/tlbc-gen-py.cpp) + +
+ +Documentation provided by [Disintar](https://dton.io/) team. From 557157affe0f9bb7e6451d5c39f4307d2b515a8e Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:16 +0800 Subject: [PATCH 086/219] New translations tl-b-types.mdx (Chinese Simplified) --- .../develop/data-formats/tl-b-types.mdx | 390 ++++++++++++++++++ 1 file changed, 390 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/tl-b-types.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/tl-b-types.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/tl-b-types.mdx new file mode 100644 index 0000000000..d7a519315c --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/tl-b-types.mdx @@ -0,0 +1,390 @@ +import ThemedImage from '@theme/ThemedImage'; + +# TL-B Types + +:::caution advanced level +This information is **very low-level** and could be hard to understand for newcomers. +So feel free to read about it later. +::: + +In this section, complex and unconventional typed language binary (TL-B) structures are analyzed. To get started, we recommend reading [this documentation](/develop/data-formats/tl-b-language) first to become more familiar with the topic. + +tlb structure + +## Either + +```tlb +left$0 {X:Type} {Y:Type} value:X = Either X Y; +right$1 {X:Type} {Y:Type} value:Y = Either X Y; +``` + +The Either type is used when one of two resulting types are possible. In this case, the type choice depends on the prefix bit shown. If the prefix bit is 0, the left type is serialized, while if the 1 prefix bit is used, the right one is serialized. + +It is used, for example, when serializing messages, when the body is either part of the main cell or linked to another cell. + +## Maybe + +```tlb +nothing$0 {X:Type} = Maybe X; +just$1 {X:Type} value:X = Maybe X; +``` + +The Maybe type is used in conjunction with optional values. In these instances, if the first bit is 0, the value itself is not serialized (and is actually skipped), while if the value is 1, it is serialized. + +## Both + +```tlb +pair$_ {X:Type} {Y:Type} first:X second:Y = Both X Y; +``` + +The Both type variation is used only in conjunction with normal pairs, whereby both types are serialized, one after the other, without conditions. + +## Unary + +The Unary functional type is commonly used for dynamic sizing in structures such as [hml_short](https://github.com/ton-blockchain/ton/blob/master/crypto/block/block.tlb#L29). + +Unary presents two main options: + +```tlb +unary_zero$0 = Unary ~0; +unary_succ$1 {n:#} x:(Unary ~n) = Unary ~(n + 1); +``` + +### Unary serialization + +Generally, using the `unary_zero` variation is quite simple: if the first bit is 0, then the result of whole Unary deserialization is 0. + +That said, the `unary_succ` variation is more complex because it is loaded recursively and possesses a value of `~(n + 1)`. This means it sequentially calls itself until it reaches `unary_zero`. In other words, the desired value will be equal to the number of units in a row. + +For instance, let's analyze serialization of the bitstring `110`. + +The call chain will be as follows: + +```tlb +unary_succ$1 -> unary_succ$1 -> unary_zero$0 +``` + +Once we reach `unary_zero`, the value is returned to the end of a serialized bitstring similarly to a recursive function call. + +Now, to understand the result more clearly, let's retrieve the return value path, which is displayed as follows: + +`0 -> ~(0 + 1) -> ~(1 + 1) -> 2`, that means we serialized `110` in `Unary 2`. + +### Unary deserialization + +Suppose we have `Foo` type: + +```tlb +foo$_ u:(Unary 2) = Foo; +``` + +According said above, `Foo` will be deserialized into: + +

+ +

+ +```tlb +foo u:(unary_succ x:(unary_succ x:(unnary_zero))) +``` + +## Hashmap + +The Hashmap complex type is used for storing dict from the FunC smart contract code(`dict`). + +The following TL-B structures are used to serialize a Hashmap with a fixed key length: + +```tlb +hm_edge#_ {n:#} {X:Type} {l:#} {m:#} label:(HmLabel ~l n) + {n = (~m) + l} node:(HashmapNode m X) = Hashmap n X; + +hmn_leaf#_ {X:Type} value:X = HashmapNode 0 X; +hmn_fork#_ {n:#} {X:Type} left:^(Hashmap n X) + right:^(Hashmap n X) = HashmapNode (n + 1) X; + +hml_short$0 {m:#} {n:#} len:(Unary ~n) {n <= m} s:(n * Bit) = HmLabel ~n m; +hml_long$10 {m:#} n:(#<= m) s:(n * Bit) = HmLabel ~n m; +hml_same$11 {m:#} v:Bit n:(#<= m) = HmLabel ~n m; + +unary_zero$0 = Unary ~0; +unary_succ$1 {n:#} x:(Unary ~n) = Unary ~(n + 1); + +hme_empty$0 {n:#} {X:Type} = HashmapE n X; +hme_root$1 {n:#} {X:Type} root:^(Hashmap n X) = HashmapE n X; +``` + +This means the root structure uses `HashmapE` and either of its two states: including `hme_empty` or `hme_root`. + +### Hashmap parsing example + +As an example, consider the following Cell, given in binary form. + +```json +1[1] -> { + 2[00] -> { + 7[1001000] -> { + 25[1010000010000001100001001], + 25[1010000010000000001101111] + }, + 28[1011100000000000001100001001] + } +} +``` + +This Cell makes use of the `HashmapE` structure type and possesses an 8-bit key size and its values make use of the `uint16` number framework (`HashmapE 8 uint16`). HashmapE makes use of distinct 3 key types: + +``` +1 = 777 +17 = 111 +128 = 777 +``` + +To parse this Hashmap, we need to know in advance which structure type to use, `either hme_empty` or `hme_root`. This is determined by identifying the `correct prefix`. The hme empty variation uses one bit 0 (`hme_empty$0`), while hme root uses one bit 1 (`hme_root$1`). After reading the first bit, it is determined that it is equal to one (`1[1]`), meaning it is the `hme_root` variation. + +Now, let's fill out the structure variables with known values, with the initial result being: +`hme_root$1 {n:#} {X:Type} root:^(Hashmap 8 uint16) = HashmapE 8 uint16;` + +Here the one bit prefix is already read, yet within the `{}` denotes conditions that don’t need to be read. The condition `{n:#}` means that n is any uint32 number, while `{X:Type}` means that X can employ any type. + +The next portion that needs to be read is the `root:^(Hashmap 8 uint16)`, while the `^` symbol denotes a link that must be loaded. + +```json +2[00] -> { + 7[1001000] -> { + 25[1010000010000001100001001], + 25[1010000010000000001101111] + }, + 28[1011100000000000001100001001] + } +``` + +#### Initiating Branch Parsing + +According to our schema, this is the correct `Hashmap 8 uint16` structure. Next, we fill it with known values and obtain a result of: + +```tlb +hm_edge#_ {n:#} {X:Type} {l:#} {m:#} label:(HmLabel ~l 8) + {8 = (~m) + l} node:(HashmapNode m uint16) = Hashmap 8 uint16; +``` + +As is shown above, conditional variables `{l:#}` and `{m:#}` have now appeared, yet the values of both variables are unknown to us. Also, after reading the corresponding `label`, it's clear that `n` is involved in the equation `{n = (~m) + l}`, in this case we calculate `l` and `m`, the sign `tells us the resulting value of ~`. + +To determine the value of `l`, we must load the `label:(HmLabel ~l uint16)` sequence. As is shown below, the `HmLabel` has 3 basic structural options: + +```tlb +hml_short$0 {m:#} {n:#} len:(Unary ~n) {n <= m} s:(n * Bit) = HmLabel ~n m; +hml_long$10 {m:#} n:(#<= m) s:(n * Bit) = HmLabel ~n m; +hml_same$11 {m:#} v:Bit n:(#<= m) = HmLabel ~n m; +``` + +Each option is determined by the corresponding prefix. Currently, our root cell is made up of 2 zero bits, which is displayed as: (`2[00]`). Therefore, the only logical option is `hml_short$0`, which makes use of a prefix beginning with 0. + +Fill `hml_short` with known values: + +```tlb +hml_short$0 {m:#} {n:#} len:(Unary ~n) {n <= 8} s:(n * Bit) = HmLabel ~n 8 +``` + +In this case, we don't know the value of `n`, but since it has a `~` character, it’s possible to calculate it. To accomplish this, we load `len:(Unary ~n)`, [more about Unary here](#unary). + +In this case, we started with `2[00]`, yet after defining the type `HmLabel`, only one of the two bits still exists. + +Therefore, we load it and see that its value is 0, which means it clearly uses the `unary_zero$0` variation. That means that the n value using the `HmLabel` variation is zero. + +Next, it is necessary to complete the `hml_short` variation sequence using the calculated n value: + +```tlb +hml_short$0 {m:#} {n:#} len:0 {n <= 8} s:(0 * Bit) = HmLabel 0 8 +``` + +It turns out we have an empty `HmLabel` denoted as, s = 0, therefore there is nothing to download. + +Next, we supplement our structure with the calculated value of `l` as follows: + +```tlb +hm_edge#_ {n:#} {X:Type} {l:0} {m:#} label:(HmLabel 0 8) + {8 = (~m) + 0} node:(HashmapNode m uint16) = Hashmap 8 uint16; +``` + +Now that we have calculated the value of `l`, we can also calculate `m` using the equation `n = (~m) + 0`, i.e. `m = n - 0`, m = n = 8. + +After determining all unknown values it is now possible to load the `node:(HashmapNode 8 uint16)`. + +As far as the HashmapNode goes, we have options: + +```tlb +hmn_leaf#_ {X:Type} value:X = HashmapNode 0 X; +hmn_fork#_ {n:#} {X:Type} left:^(Hashmap n X) + right:^(Hashmap n X) = HashmapNode (n + 1) X; +``` + +In this case, we determine the option not by using the prefix, but by using the parameter. This means that if n = 0, then the correct end result will be either `hmn_leaf` or `hmn_fork`. +In this example, the result is n = 8 (the hmn_fork variation). We make use of the `hmn_fork` variation and fill in the known values: + +```tlb +hmn_fork#_ {n:#} {X:uint16} left:^(Hashmap n uint16) + right:^(Hashmap n uint16) = HashmapNode (n + 1) uint16; +``` + +After entering the known values, we must calculate the `HashmapNode (n + 1) uint16`. This means that the resulting value of n must be equal to our parameter, i.e. 8. +To calculate the local value of n, we need to calculate it using the following formula: `n = (n_local + 1)` -> `n_local = (n - 1)` -> `n_local = (8 - 1)` -> `n_local = 7`. + +```tlb +hmn_fork#_ {n:#} {X:uint16} left:^(Hashmap 7 uint16) + right:^(Hashmap 7 uint16) = HashmapNode (7 + 1) uint16; +``` + +Now that we know the above formula is required, obtaining the end result is simple. +Next we load the left and right branches and for each subsequent branch [the process is repeated](#initiating-branch-parsing). + +#### Analyzing Loaded Hashmap Values + +Continuing with the previous example, let’s examine how the process of loading branches works (for dict values)., i.e. `28[1011100000000000001100001001]` + +The end result becomes `hm_edge` yet again and the next step is to fill the sequence with the correct known values as follows: + +```tlb +hm_edge#_ {n:#} {X:Type} {l:#} {m:#} label:(HmLabel ~l 7) + {7 = (~m) + l} node:(HashmapNode m uint16) = Hashmap 7 uint16; +``` + +Next the `HmLabel` response is loaded using the `HmLabel` variation because the prefix is `10`. + +```tlb +hml_long$10 {m:#} n:(#<= m) s:(n * Bit) = HmLabel ~n m; +``` + +Now, let's fill in the sequence: + +```tlb +hml_long$10 {m:#} n:(#<= 7) s:(n * Bit) = HmLabel ~n 7; +``` + +The new construction - `n:(#<= 7)`, clearly denotes a sizing value that corresponds to the number 7, which is in fact a log2 from the number + 1. But for simplicity, we could count the number of bits needed to write the number 7. +Relatedly, the number 7 in binary form is `111`; therefore 3 bits are required, meaning the value for `n = 3`. + +```tlb +hml_long$10 {m:#} n:(## 3) s:(n * Bit) = HmLabel ~n 7; +``` + +Next we load `n` into the sequence, with an end result of `111`, which as we noted above = 7 coincidentally. Next, we load `s` into the sequence, 7 bits - `0000000`. Remember, `s` is part of the key. + +Next we return to the top of the sequence and fill in the resulting `l`: + +```tlb +hm_edge#_ {n:#} {X:Type} {l:#} {m:#} label:(HmLabel 7 7) + {7 = (~m) + 7} node:(HashmapNode m uint16) = Hashmap 7 uint16; +``` + +Then we calculate the value of `m`, `m = 7 - 7`, therefore the value of `m = 0`. +Since the value of `m = 0`, the structure is perfect for use with a HashmapNode: + +```tlb +hmn_leaf#_ {X:Type} value:X = HashmapNode 0 X; +``` + +Next we substitute our uint16 type and load the value. The remaining 16 bits of `0000001100001001` in decimal form is 777, therefore our value. + +Now let's restore the key, we must combine the ordered list of all parts of the key that were computed previously. +Each of the two related key parts unite with one bit based on which type branches are used. +For the right branch, ‘1’ bit is added, and for the left branch ‘0’ bit is added. If a full HmLabel exists above, then its bits are added to the key. + +In this case specifically, 7 bits are taken from the HmLabel `0000000` and ‘1’ bit is added before the sequence of zeros because the value was obtained from the right branch. The end result is 8 bits in total or `10000000`, meaning the key value equals `128`. + +## Other Hashmap Types + +Now that we have discussed Hashmaps and how to load the standardized Hashmap type, let’s explain how the additional Hashmap types work. + +### HashmapAugE + +```tlb +ahm_edge#_ {n:#} {X:Type} {Y:Type} {l:#} {m:#} + label:(HmLabel ~l n) {n = (~m) + l} + node:(HashmapAugNode m X Y) = HashmapAug n X Y; + +ahmn_leaf#_ {X:Type} {Y:Type} extra:Y value:X = HashmapAugNode 0 X Y; + +ahmn_fork#_ {n:#} {X:Type} {Y:Type} left:^(HashmapAug n X Y) + right:^(HashmapAug n X Y) extra:Y = HashmapAugNode (n + 1) X Y; + +ahme_empty$0 {n:#} {X:Type} {Y:Type} extra:Y + = HashmapAugE n X Y; + +ahme_root$1 {n:#} {X:Type} {Y:Type} root:^(HashmapAug n X Y) + extra:Y = HashmapAugE n X Y; +``` + +The main difference between the `HashmapAugE` and the regular `Hashmap` is the presence of an `extra:Y` field in each node (not just in leafs with values). + +### PfxHashmap + +```tlb +phm_edge#_ {n:#} {X:Type} {l:#} {m:#} label:(HmLabel ~l n) + {n = (~m) + l} node:(PfxHashmapNode m X) + = PfxHashmap n X; + +phmn_leaf$0 {n:#} {X:Type} value:X = PfxHashmapNode n X; +phmn_fork$1 {n:#} {X:Type} left:^(PfxHashmap n X) + right:^(PfxHashmap n X) = PfxHashmapNode (n + 1) X; + +phme_empty$0 {n:#} {X:Type} = PfxHashmapE n X; +phme_root$1 {n:#} {X:Type} root:^(PfxHashmap n X) + = PfxHashmapE n X; +``` + +The main difference between the PfxHashmap and the regular Hashmap is its ability to store different key lengths due to the presence of the `phmn_leaf$0` and `phmn_fork$1` nodes. + +### VarHashmap + +```tlb +vhm_edge#_ {n:#} {X:Type} {l:#} {m:#} label:(HmLabel ~l n) + {n = (~m) + l} node:(VarHashmapNode m X) + = VarHashmap n X; +vhmn_leaf$00 {n:#} {X:Type} value:X = VarHashmapNode n X; +vhmn_fork$01 {n:#} {X:Type} left:^(VarHashmap n X) + right:^(VarHashmap n X) value:(Maybe X) + = VarHashmapNode (n + 1) X; +vhmn_cont$1 {n:#} {X:Type} branch:Bit child:^(VarHashmap n X) + value:X = VarHashmapNode (n + 1) X; + +// nothing$0 {X:Type} = Maybe X; +// just$1 {X:Type} value:X = Maybe X; + +vhme_empty$0 {n:#} {X:Type} = VarHashmapE n X; +vhme_root$1 {n:#} {X:Type} root:^(VarHashmap n X) + = VarHashmapE n X; +``` + +The main difference between the VarHashmap and the regular Hashmap is its ability to store different key lengths due to the presence of the `vhmn_leaf$00` and `vhmn_fork$01` nodes. Additionally, the `VarHashmap` is able to form a common value prefix (child map) at the expense of the `vhmn_cont$1`. + +### BinTree + +```tlb +bta_leaf$0 {X:Type} {Y:Type} extra:Y leaf:X = BinTreeAug X Y; +bta_fork$1 {X:Type} {Y:Type} left:^(BinTreeAug X Y) + right:^(BinTreeAug X Y) extra:Y = BinTreeAug X Y; +``` + +The binary tree key generation mechanism works in a similar manner to the standardized Hashmap framework but doesn't use labels and only includes branch prefixes. + +## Addresses + +TON addresses are formed with the sha256 hashing mechanism using the TL-B StateInit structure. This means that the address can be calculated prior to network contract deployment. + +### Serialization + +Standard addresses such as `EQBL2_3lMiyywU17g-or8N7v9hDmPCpttzBPE2isF2GTzpK4` use the base64 uri for byte encoding. +Typically, they have a length of 36 bytes, the last 2 of which are the crc16 checksum calculated with XMODEM table, while the first byte represents the flag, with the second representing the workchain. +The 32 bytes in the middle are the data of the address itself (also calls AccountID), often represented in schemas such as int256. + +[Decoding example](https://github.com/xssnick/tonutils-go/blob/3d9ee052689376061bf7e4a22037ff131183afad/address/addr.go#L156) + +## References + +_Here a [link to the original article](https://github.com/xssnick/ton-deep-doc/blob/master/TL-B.md) by [Oleg Baranov](https://github.com/xssnick)._ From e949081518d409a51e177dacb4338df1a1ff2735 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:17 +0800 Subject: [PATCH 087/219] New translations tl.md (Chinese Simplified) --- .../current/develop/data-formats/tl.md | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/tl.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/tl.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/tl.md new file mode 100644 index 0000000000..078182296d --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/tl.md @@ -0,0 +1,45 @@ +# TL + +TL (Type Language) is a language for describing data structures. + +For structuring useful data, when communicating, [TL schemas](https://github.com/ton-blockchain/ton/tree/master/tl/generate/scheme) are used. + +TL operates on 32 bit blocks. Accordingly, the data size in TL must be a multiple of 4 bytes. +If the size of the object is not a multiple of 4, we need to add the required number of zero bytes up to the multiple. + +Numbers are always encoded in Little Endian order. + +More details about TL can be found in [Telegram documentation](https://core.telegram.org/mtproto/TL) + +## Encoding bytes array + +To encode an array of bytes, we first need to determine its size. +If it is less than 254 bytes, then the encoding with 1 byte as the size is used. If more, +then 0xFE is written as the first byte, as an indicator of a large array, and after it 3 bytes of size follow. + +For example, we encode the array `[0xAA, 0xBB]`, its size is 2. We use 1 byte +size and then write the data itself, we get `[0x02, 0xAA, 0xBB]`, done, but we see +that the final size is 3 and not a multiple of 4 bytes, then we need to add 1 byte of padding to make it 4. Result: `[0x02, 0xAA, 0xBB, 0x00]`. + +In case we need to encode an array whose size will be equal to, for example, 396, +we do this: 396 >= 254, so we use 3 bytes for size encoding and 1 byte oversize indicator, +we get: `[0xFE, 0x8C, 0x01, 0x00, array bytes]`, 396+4 = 400, which is a multiple of 4, no need to align. + +## Non-obvious serialization rules + +Often, a 4-byte prefix is written before the schema itself - its ID. The schema ID is a CRC32 with an IEEEE table from the schema text, while symbols such as `;` and brackets `()` are previously removed from the text. The serialization of a schema with an ID prefix is called **boxed**, this allows the parser to determine which schema comes before it if there are multiple options. + +How to determine whether to serialize as boxed or not? If our schema is part of another schema, then we need to look at how the field type is specified, if it is specified explicitly, then we serialize without a prefix, if not explicitly (there are many such types), then we need to serialize as boxed. Example: + +```tlb +pub.unenc data:bytes = PublicKey; +pub.ed25519 key:int256 = PublicKey; +pub.aes key:int256 = PublicKey; +pub.overlay name:bytes = PublicKey; +``` + +We have such types, if `PublicKey` is specified in the schema, for example `adnl.node id:PublicKey addr_list:adnl.addressList = adnl.Node`, then it is not explicitly specified and we need to serialize with an ID prefix (boxed). And if it were specified like this: `adnl.node id:pub.ed25519 addr_list:adnl.addressList = adnl.Node`, then it would be explicit, and the prefix would not be needed. + +## References + +_Here a [link to the original article](https://github.com/xssnick/ton-deep-doc/blob/master/TL.md) by [Oleg Baranov](https://github.com/xssnick)._ From 427faff778b8dc1dd47725c630b7fbcb1365733f Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:18 +0800 Subject: [PATCH 088/219] New translations tlb-ide.md (Chinese Simplified) --- .../current/develop/data-formats/tlb-ide.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/tlb-ide.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/tlb-ide.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/tlb-ide.md new file mode 100644 index 0000000000..0f00abd95c --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/tlb-ide.md @@ -0,0 +1,18 @@ +# IDE Support + +### Highlight + +The [intellij-ton](https://github.com/andreypfau/intellij-ton) plugin supports the Fift and FunC programming languages as well as the typed language binary (TL-B) format. + +Additionally, the correct TL-B syntax specifications are described in the [TlbParser.bnf](https://github.com/andreypfau/intellij-ton/blob/main/src/main/grammars/TlbParser.bnf) file. + +### TL-B Parsers + +TL-B parsers help carry out the serialization of basic [TL-B types](/develop/data-formats/tl-b-types). Each of which implements TL-B types as an object, and returns serialized binary data. + +| Language | SDK | Social | +| ---------- | --------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | +| Kotlin | [ton-kotlin](https://github.com/andreypfau/ton-kotlin/tree/main/ton-kotlin-tlb) (+ parsing `.tlb` files) | https://t.me/tonkotlin | +| Go | [tonutils](https://github.com/xssnick/tonutils-go/tree/master/tlb) | https://t.me/tonutils | +| Go | [tongo](https://github.com/tonkeeper/tongo/tree/master/tlb) (+ parsing `.tlb` files) | https://t.me/tongo_lib | +| TypeScript | [tlb-parser](https://github.com/ton-community/tlb-parser) | - | From 5aa14decb7aa4d462f9048de349f3ebdf7a467ad Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:19 +0800 Subject: [PATCH 089/219] New translations transaction-layout.md (Chinese Simplified) --- .../data-formats/transaction-layout.md | 232 ++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/transaction-layout.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/transaction-layout.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/transaction-layout.md new file mode 100644 index 0000000000..1a00722f58 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/data-formats/transaction-layout.md @@ -0,0 +1,232 @@ +# Transaction layout + +:::info +To maximize your comprehension of this page, familiarizing yourself with the [TL-B language](/develop/data-formats/cell-boc) is highly recommended. +::: + +The TON Blockchain operates using three key parts: accounts, messages, and transactions. This page describes the structure and layout of transactions. + +A transaction is an operation that processes inbound and outbound messages related to a specific account, altering its state and potentially generating fees for validators. + +## Transaction + +```tlb +transaction$0111 account_addr:bits256 lt:uint64 + prev_trans_hash:bits256 prev_trans_lt:uint64 now:uint32 + outmsg_cnt:uint15 + orig_status:AccountStatus end_status:AccountStatus + ^[ in_msg:(Maybe ^(Message Any)) out_msgs:(HashmapE 15 ^(Message Any)) ] + total_fees:CurrencyCollection state_update:^(HASH_UPDATE Account) + description:^TransactionDescr = Transaction; +``` + +| Field | Type | Required | Description | +| ----------------- | ---------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `account_addr` | bits256 | Yes | The hash part of the address on which the transaction was executed. [More about addresses](https://docs.ton.org/learn/overviews/addresses#address-of-smart-contract) | +| `lt` | uint64 | Yes | Represents _Logical time_. [More about logical time](https://docs.ton.org/develop/smart-contracts/guidelines/message-delivery-guarantees#what-is-a-logical-time) | +| `prev_trans_hash` | bits256 | Yes | The hash of the previous transaction on this account. | +| `prev_trans_lt` | uint64 | Yes | The `lt` of the previous transaction on this account. | +| `now` | uint32 | Yes | The `now` value that was set when executing this transaction. It's a Unix timestamp in seconds. | +| `outmsg_cnt` | uint15 | Yes | The number of outgoing messages created while executing this transaction. | +| `orig_status` | [AccountStatus](#accountstatus) | Yes | The status of this account before the transaction was executed. | +| `end_status` | [AccountStatus](#accountstatus) | Yes | The status of this account after executing the transaction. | +| `in_msg` | (Message Any) | No | The incoming message that triggered the execution of the transaction. Stored in a reference. | +| `out_msgs` | HashmapE 15 ^(Message Any) | Yes | The dictionary that contains the list of outgoing messages that were created while executing this transaction. | +| `total_fees` | [CurrencyCollection](/develop/data-formats/msg-tlb#currencycollection) | Yes | The total amount of fees that were collected while executing this transaction. It consists of a _Toncoin_ value and possibly some [Extra-currencies](https://docs.ton.org/develop/dapps/defi/coins#extra-currencies). | +| `state_update` | [HASH_UPDATE](#hash_update) Account | Yes | The `HASH_UPDATE` structure. Stored in a reference. | +| `description` | [TransactionDescr](#transactiondescr-types) | Yes | A detailed description of the transaction execution process. Stored in a reference. | + +## AccountStatus + +```tlb +acc_state_uninit$00 = AccountStatus; +acc_state_frozen$01 = AccountStatus; +acc_state_active$10 = AccountStatus; +acc_state_nonexist$11 = AccountStatus; +``` + +- `[00]`: Account is not initialized +- `[01]`: Account is frozen +- `[10]`: Account is active +- `[11]`: Account does not exist + +## HASH_UPDATE + +```tlb +update_hashes#72 {X:Type} old_hash:bits256 new_hash:bits256 + = HASH_UPDATE X; +``` + +| Field | Type | Description | +| ---------- | ------- | ------------------------------------------------------------------------------- | +| `old_hash` | bits256 | The hash of the account state before executing the transaction. | +| `new_hash` | bits256 | The hash of the account state after executing the transaction. | + +## TransactionDescr Types + +- [Ordinary](#ordinary) +- [Storage](#storage) +- [Tick-tock](#tick-tock) +- [Split prepare](#split-prepare) +- [Split install](#split-install) +- [Merge prepare](#merge-prepare) +- [Merge install](#merge-install) + +## Ordinary + +This is the most common type of transaction and it fulfills most developers' needs. Transactions of this type have exactly one incoming message and can create several outgoing messages. + +```tlb +trans_ord$0000 credit_first:Bool + storage_ph:(Maybe TrStoragePhase) + credit_ph:(Maybe TrCreditPhase) + compute_ph:TrComputePhase action:(Maybe ^TrActionPhase) + aborted:Bool bounce:(Maybe TrBouncePhase) + destroyed:Bool + = TransactionDescr; +``` + +| Field | Type | Required | Description | +| -------------- | -------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `credit_first` | Bool | Yes | A flag that correlates with `bounce` flag of an incoming message. `credit_first = !bounce` | +| `storage_ph` | TrStoragePhase | No | Contains information about storage phase of a transaction execution. [More Info](https://docs.ton.org/learn/tvm-instructions/tvm-overview#transactions-and-phases) | +| `credit_ph` | TrCreditPhase | No | Contains information about credit phase of a transaction execution. [More Info](https://docs.ton.org/learn/tvm-instructions/tvm-overview#transactions-and-phases) | +| `compute_ph` | TrComputePhase | Yes | Contains information about compute phase of a transaction execution. [More Info](https://docs.ton.org/learn/tvm-instructions/tvm-overview#transactions-and-phases) | +| `action` | TrActionPhase | No | Contains information about action phase of a transaction execution. [More Info](https://docs.ton.org/learn/tvm-instructions/tvm-overview#transactions-and-phases). Stored in a reference. | +| `aborted` | Bool | Yes | Indicates whether the transaction execution was aborted. | +| `bounce` | TrBouncePhase | No | Contains information about bounce phase of a transaction execution. [More Info](https://docs.ton.org/develop/smart-contracts/guidelines/non-bouncable-messages) | +| `destroyed` | Bool | Yes | Indicates whether the account was destroyed during the execution. | + +## Storage + +Transactions of this type can be inserted by validators at their discretion. They do not process any inbound messages and do not invoke any code. Their only effect is to collect storage payments from an account, affecting its storage statistics and balance. If the resulting _Toncoin_ balance of the account drops below a certain amount, the account may be frozen, and its code and data replaced by their combined hash. + +```tlb +trans_storage$0001 storage_ph:TrStoragePhase + = TransactionDescr; +``` + +| Field | Type | Description | +| ------------ | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `storage_ph` | TrStoragePhase | Contains information about storage phase of a transaction execution. [More Info](https://docs.ton.org/learn/tvm-instructions/tvm-overview#transactions-and-phases) | + +## Tick-tock + +`Tick` and `Tock` transactions are reserved for special system smart contracts that are required to be automatically invoked in each block. `Tick` transactions are invoked at the beginning of each masterchain block, and `Tock` transactions are invoked at the end. + +```tlb +trans_tick_tock$001 is_tock:Bool storage_ph:TrStoragePhase + compute_ph:TrComputePhase action:(Maybe ^TrActionPhase) + aborted:Bool destroyed:Bool = TransactionDescr; +``` + +| Field | Type | Required | Description | +| ------------ | -------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `is_tock` | Bool | Yes | A flag indicating the type of transaction. Used to separate `Tick` and `Tock` transactions | +| `storage_ph` | TrStoragePhase | Yes | Contains information about storage phase of a transaction execution. [More Info](https://docs.ton.org/learn/tvm-instructions/tvm-overview#transactions-and-phases) | +| `compute_ph` | TrComputePhase | Yes | Contains information about compute phase of a transaction execution. [More Info](https://docs.ton.org/learn/tvm-instructions/tvm-overview#transactions-and-phases) | +| `action` | TrActionPhase | No | Contains information about action phase of a transaction execution. [More Info](https://docs.ton.org/learn/tvm-instructions/tvm-overview#transactions-and-phases). Stored in a reference. | +| `aborted` | Bool | Yes | Indicates whether the transaction execution was aborted. | +| `destroyed` | Bool | Yes | Indicates whether the account was destroyed during the execution. | + +## Split Prepare + +:::note +This type of transaction is currently not in use. Information about this process is limited. +::: + +Split transactions are initiated on large smart contracts that need to be divided under high load. The contract should support this transaction type and manage the splitting process to balance the load. + +Split Prepare transactions are initiated when a smart contract needs to be split. The smart contract should generate the state for a new instance of itself to be deployed. + +```tlb +trans_split_prepare$0100 split_info:SplitMergeInfo + storage_ph:(Maybe TrStoragePhase) + compute_ph:TrComputePhase action:(Maybe ^TrActionPhase) + aborted:Bool destroyed:Bool + = TransactionDescr; +``` + +| Field | Type | Required | Description | +| ------------ | -------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `split_info` | SplitMergeInfo | Yes | Information about split process. | +| `storage_ph` | TrStoragePhase | No | Contains information about storage phase of a transaction execution. [More Info](https://docs.ton.org/learn/tvm-instructions/tvm-overview#transactions-and-phases) | +| `compute_ph` | TrComputePhase | Yes | Contains information about compute phase of a transaction execution. [More Info](https://docs.ton.org/learn/tvm-instructions/tvm-overview#transactions-and-phases) | +| `action` | TrActionPhase | No | Contains information about action phase of a transaction execution. [More Info](https://docs.ton.org/learn/tvm-instructions/tvm-overview#transactions-and-phases). Stored in a reference. | +| `aborted` | Bool | Yes | Indicates whether the transaction execution was aborted. | +| `destroyed` | Bool | Yes | Indicates whether the account was destroyed during the execution. | + +## Split install + +:::note +This type of transaction is currently not in use. Information about this process is limited. +::: + +Split Install transactions are used for creating new instances of large smart contracts. The state for the new smart contract is generated by a [Split Prepare](#split-prepare) transaction. + +```tlb +trans_split_install$0101 split_info:SplitMergeInfo + prepare_transaction:^Transaction + installed:Bool = TransactionDescr; +``` + +| Field | Type | Description | +| --------------------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | +| `split_info` | SplitMergeInfo | Information about split process. | +| `prepare_transaction` | [Transaction](#transaction) | Information about the [transaction prepared](#split-prepare) for the split operation. Stored in a reference. | +| `installed` | Bool | Indicates whether the transaction was installed. | + +## Merge prepare + +:::note +This type of transaction is currently not in use. Information about this process is limited. +::: + +Merge transactions are initiated on large smart contracts that need to recombine after being split due to high load. The contract should support this transaction type and manage the merging process to balance the load. + +Merge Prepare transactions are initiated when two smart contracts need to be merged. The smart contract should generate a message for another instance of itself to facilitate the merge. + +```tlb +trans_merge_prepare$0110 split_info:SplitMergeInfo + storage_ph:TrStoragePhase aborted:Bool + = TransactionDescr; +``` + +| Field | Type | Description | +| ------------ | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `split_info` | SplitMergeInfo | Information about merge process. | +| `storage_ph` | TrStoragePhase | Contains information about storage phase of a transaction execution. [More Info](https://docs.ton.org/learn/tvm-instructions/tvm-overview#transactions-and-phases) | +| `aborted` | Bool | Indicates whether the transaction execution was aborted. | + +## Merge install + +:::note +This type of transaction is currently not in use. Information about this process is limited. +::: + +Merge Install transactions are used for merging instances of large smart contracts. The special message facilitating the merge is generated by a [Merge Prepare](#merge-prepare) transaction. + +```tlb +trans_merge_install$0111 split_info:SplitMergeInfo + prepare_transaction:^Transaction + storage_ph:(Maybe TrStoragePhase) + credit_ph:(Maybe TrCreditPhase) + compute_ph:TrComputePhase action:(Maybe ^TrActionPhase) + aborted:Bool destroyed:Bool + = TransactionDescr; +``` + +| Field | Type | Required | Description | +| --------------------- | --------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `split_info` | SplitMergeInfo | Yes | Information about merge process. | +| `prepare_transaction` | [Transaction](#transaction) | Yes | Information about the [transaction prepared](#merge-prepare) for the merge operation. Stored in a reference. | +| `storage_ph` | TrStoragePhase | No | Contains information about storage phase of a transaction execution. [More Info](https://docs.ton.org/learn/tvm-instructions/tvm-overview#transactions-and-phases) | +| `credit_ph` | TrCreditPhase | No | Contains information about credit phase of a transaction execution. [More Info](https://docs.ton.org/learn/tvm-instructions/tvm-overview#transactions-and-phases) | +| `compute_ph` | TrComputePhase | Yes | Contains information about compute phase of a transaction execution. [More Info](https://docs.ton.org/learn/tvm-instructions/tvm-overview#transactions-and-phases) | +| `action` | TrActionPhase | No | Contains information about action phase of a transaction execution. [More Info](https://docs.ton.org/learn/tvm-instructions/tvm-overview#transactions-and-phases). Stored in a reference. | +| `aborted` | Bool | Yes | Indicates whether the transaction execution was aborted. | +| `destroyed` | Bool | Yes | Indicates whether the account was destroyed during the execution. | + +## See also + +- Original description of [Transaction layout](https://ton.org/docs/tblkch.pdf#page=75\&zoom=100,148,290) from whitepaper From 20dd15762ccd0b401c37b828e37244c54e61dde0 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:20 +0800 Subject: [PATCH 090/219] New translations fift-and-tvm-assembly.md (Chinese Simplified) --- .../develop/fift/fift-and-tvm-assembly.md | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/fift/fift-and-tvm-assembly.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/fift/fift-and-tvm-assembly.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/fift/fift-and-tvm-assembly.md new file mode 100644 index 0000000000..36d8963700 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/fift/fift-and-tvm-assembly.md @@ -0,0 +1,129 @@ +# Fift and TVM assembly + +Fift is stack-based programming language that has TON-specific features and therefore can work with cells. TVM assembly is also a stack-based programming language, also specific to TON, and it also can work with cells. So what's the difference between them? + +## The difference + +Fift is executed **at compile-time** - when your compiler builds smart-contract code BOC, after FunC code is processed. Fift can look differently: + +``` +// tuple primitives +x{6F0} @Defop(4u) TUPLE +x{6F00} @Defop NIL +x{6F01} @Defop SINGLE +x{6F02} dup @Defop PAIR @Defop CONS +``` + +> TVM opcode definitions in Asm.fif + +``` +"Asm.fif" include +<{ SETCP0 DUP IFNOTRET // return if recv_internal + DUP 85143 INT EQUAL OVER 78748 INT EQUAL OR IFJMP:<{ // "seqno" and "get_public_key" get-methods + 1 INT AND c4 PUSHCTR CTOS 32 LDU 32 LDU NIP 256 PLDU CONDSEL // cnt or pubk + }> + INC 32 THROWIF // fail unless recv_external + 9 PUSHPOW2 LDSLICEX DUP 32 LDU 32 LDU 32 LDU // signature in_msg subwallet_id valid_until msg_seqno cs + NOW s1 s3 XCHG LEQ 35 THROWIF // signature in_msg subwallet_id cs msg_seqno + c4 PUSH CTOS 32 LDU 32 LDU 256 LDU ENDS // signature in_msg subwallet_id cs msg_seqno stored_seqno stored_subwallet public_key + s3 s2 XCPU EQUAL 33 THROWIFNOT // signature in_msg subwallet_id cs public_key stored_seqno stored_subwallet + s4 s4 XCPU EQUAL 34 THROWIFNOT // signature in_msg stored_subwallet cs public_key stored_seqno + s0 s4 XCHG HASHSU // signature stored_seqno stored_subwallet cs public_key msg_hash + s0 s5 s5 XC2PU // public_key stored_seqno stored_subwallet cs msg_hash signature public_key + CHKSIGNU 35 THROWIFNOT // public_key stored_seqno stored_subwallet cs + ACCEPT + WHILE:<{ + DUP SREFS // public_key stored_seqno stored_subwallet cs _51 + }>DO<{ // public_key stored_seqno stored_subwallet cs + 8 LDU LDREF s0 s2 XCHG // public_key stored_seqno stored_subwallet cs _56 mode + SENDRAWMSG + }> // public_key stored_seqno stored_subwallet cs + ENDS SWAP INC // public_key stored_subwallet seqno' + NEWC 32 STU 32 STU 256 STU ENDC c4 POP +}>c +``` + +> wallet_v3_r2.fif + +Last fragment of code looks like TVM assembly, and most of it really is! How can this happen? + +Imagine you're talking to a trainee programmer, saying him "and now add commands doing this, this and that to the end of function". Your commands end up being in trainee's program. They're processed twice - just like here, opcodes in capital letters (SETCP0, DUP, etc) are processed both by Fift and by TVM. + +You can explain high-level abstractions to your trainee, eventually he will understand and be able to use them. Fift is also extensible - you're able to define your own commands. In fact, Asm[Tests].fif is all about defining TVM opcodes. + +TVM opcodes, on the other hand, are executed **at run-time** - they're code of smart contracts. They can be thought of as program of your trainee - TVM assembly can do less things (e.g. it doesn't have builtin primitives for signing data - because everything that TVM does in blockchain is public), but it can really interact with its environment. + +## Usage in smart-contracts + +### [Fift] - Putting big BOC into contract + +This is possible if you are using `toncli`. If you use other compilers to build contract, possibly there are other ways to include big BOC. +Edit `project.yaml` so that `fift/blob.fif` is included when building smart-contract code: + +``` +contract: + fift: + - fift/blob.fif + func: + - func/code.fc +``` + +Put the BOC in `fift/blob.boc`, then add the following code to `fift/blob.fif`: + +``` +B B>boc ref, b> s PUSHSLICE"; +``` + +The reason is obvious: Fift is doing calculations in compile-time, where no `x` is yet available for conversion. To convert non-constant integer to string slice, you need TVM assembly. For example, this is code by one of TON Smart Challenge 3 participants': + +``` +tuple digitize_number(int value) + asm "NIL WHILE:<{ OVER }>DO<{ SWAP TEN DIVMOD s1 s2 XCHG TPUSH }> NIP"; + +builder store_number(builder msg, tuple t) + asm "WHILE:<{ DUP TLEN }>DO<{ TPOP 48 ADDCONST ROT 8 STU SWAP }> DROP"; + +builder store_signed(builder msg, int v) inline_ref { + if (v < 0) { + return msg.store_uint(45, 8).store_number(digitize_number(- v)); + } elseif (v == 0) { + return msg.store_uint(48, 8); + } else { + return msg.store_number(digitize_number(v)); + } +} +``` + +### [TVM assembly] - Cheap modulo multiplication + +``` +int mul_mod(int a, int b, int m) inline_ref { ;; 1232 gas units + (_, int r) = muldivmod(a % m, b % m, m); + return r; +} +int mul_mod_better(int a, int b, int m) inline_ref { ;; 1110 gas units + (_, int r) = muldivmod(a, b, m); + return r; +} +int mul_mod_best(int a, int b, int m) asm "x{A988} s,"; ;; 65 gas units +``` + +`x{A988}` is opcode formatted according to [5.2 Division](/learn/tvm-instructions/instructions#52-division): division with pre-multiplication, where the only returned result is remainder modulo third argument. But opcode needs to get into smart-contract code - that's what `s,` does: it stores slice on top of stack into builder slightly below. From 28856af76c3a2ddedb4eaee933954dab99d4aa9c Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:20 +0800 Subject: [PATCH 091/219] New translations fift-deep-dive.md (Chinese Simplified) --- .../current/develop/fift/fift-deep-dive.md | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/fift/fift-deep-dive.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/fift/fift-deep-dive.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/fift/fift-deep-dive.md new file mode 100644 index 0000000000..a4a53da095 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/fift/fift-deep-dive.md @@ -0,0 +1,137 @@ +# Fift deep dive + +A high-level stack-based language Fift is used for local manipulation with cells and other TVM primitives, mostly for converting TVM assembly code into contract code bag-of-cells. + +:::caution +This section describes interacting with TON-specific features at **very** low level. +Serious understanding of stack languages' basics required. +::: + +## Simple arithmetic + +You are able to use Fift interpreter as calculator, writing in expressions in [reverse Polish notation](https://en.wikipedia.org/wiki/Reverse_Polish_notation). + +``` +6 17 17 * * 289 + . +2023 ok +``` + +## Standard output + +``` +27 emit ."[30;1mgrey text" 27 emit ."[37m" +grey text ok +``` + +`emit` takes number from top of stack and prints Unicode character with the specified code into stdout. +`."..."` prints constant string. + +## Defining functions (Fift words) + +The main way of defining a word is to enclose its effects in curly braces then write `:` and word name. + +``` +{ minmax drop } : min +{ minmax nip } : max +``` + +> Fift.fif + +Though, there are several _defining words_, not only `:`. They're different in the sense words defined with some of them are **active** (work inside curly braces) and some are **prefix** (don't require space character to be after them): + +``` +{ bl word 1 2 ' (create) } "::" 1 (create) +{ bl word 0 2 ' (create) } :: : +{ bl word 2 2 ' (create) } :: :_ +{ bl word 3 2 ' (create) } :: ::_ +{ bl word 0 (create) } : create +``` + +> Fift.fif + +## Conditional execution + +Code blocks (those delimited by curly braces) can be executed, either conditionally or unconditionally. + +``` +{ { ."true " } { ."false " } cond } : ?. 4 5 = ?. 4 5 < ?. +false true ok +{ ."hello " } execute ."world" +hello world ok +``` + +## Loops + +``` +// ( l c -- l') deletes first c elements from list l +{ ' safe-cdr swap times } : list-delete-first +``` + +> GetOpt.fif + +Loop word `times` takes two arguments - let's call them `cont` and `n` - and executes `cont` `n` times. +Here `list-delete-first` takes continuation of `safe-cdr` (command deleting head from Lisp-style list), places it under `c` and then `c` times removes head from list present on stack. + +There are also words `while` and `until`. + +## Comments + +``` +{ 0 word drop 0 'nop } :: // +{ char " word 1 { swap { abort } if drop } } ::_ abort" +{ { bl word dup "" $= abort"comment extends after end of file" "*/" $= } until 0 'nop } :: /* +``` + +> Fift.fif + +Comments are defined in `Fift.fif`. Single-line comment starts with `//` and continues to the end of line; multiline comment starts with `/*` and ends with `*/`. + +Let's understand why they work so.\ +Fift program is essentially sequence of words, each of those transforming stack in some way or defining new words. First line of `Fift.fif` (code shown above) is declaration of new word `//`. +Comments have to work even when defining new words, so they must work in nested environment. That's why they are defined as **active** words, by means of `::`. Actions of the word being created are listed in the curly braces: + +1. `0`: zero is pushed onto stack +2. `word`: this command reads chars until one equal to top of stack is reached and pushes the data read as String. Zero is special case: here `word` skips leading spaces and then reads until the end of the current input line. +3. `drop`: top element (comment data) is dropped from stack. +4. `0`: zero is pushed onto stack again - number of results, used because word is defined with `::`. +5. `'nop` pushes execution token doing nothing when called. It's pretty much equivalent to `{ nop }`. + +## Using Fift for defining TVM assembly codes + +``` +x{00} @Defop NOP +{ 1 ' @addop does create } : @Defop +{ tuck sbitrefs @ensurebitrefs swap s, } : @addop +{ @havebitrefs ' @| ifnot } : @ensurebitrefs +{ 2 pick brembitrefs 1- 2x<= } : @havebitrefs +{ rot >= -rot <= and } : 2x<= +... +``` + +> Asm.fif (lines order reversed) + +`@Defop` takes care of checking if there is enough space for opcode (`@havebitrefs`), and if there is not, it goes on writing to another builder (`@|`; also known as implicit jump). That's why you generally don't want to write `x{A988} s,` as an opcode: there could be insufficient space to place this opcode, so compilation would fail; you should write `x{A988} @addop` instead. + +You may use Fift for including big bag-of-cells into contract: + +``` +B B>boc ref, b> pub - generates public key from private + - ed25519_sign[_uint] - generates signature given data and private key + - ed25519_chksign - checks Ed25519 signature +- Interaction with TVM + - runvmcode and similar - invokes TVM with code slice taken from stack +- Writing BOC into files: + `boc>B ".../contract.boc" B>file` + +## Continue learning + +- [Fift: A Brief Introduction](https://docs.ton.org/fiftbase.pdf) by Nikolai Durov From 3410d115d8e13c903d2eb76dc59c2a494ec7e135 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:21 +0800 Subject: [PATCH 092/219] New translations overview.mdx (Chinese Simplified) --- .../current/develop/fift/overview.mdx | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/fift/overview.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/fift/overview.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/fift/overview.mdx new file mode 100644 index 0000000000..cb1fb85d30 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/fift/overview.mdx @@ -0,0 +1,42 @@ +import Button from '@site/src/components/button' + +# Overview + +Fift is a stack-based general purpose programming language optimized for creating, debugging, and managing TON Blockchain smart contracts. +Fift has been specifically designed to interact with the TON Virtual Machine (TON VM or TVM) and TON Blockchain. + +```fift +{ ."hello " } execute ."world" +hello world ok +``` + +:::info +Usually, using the Fift is not required for programming smart contracts in TON. However, Sometimes, you may need to use the Fift language to solve uncommon technical challenges as part of your task. +::: + + +



+ +## Documentation + +- [Fift: A Brief Introduction](https://ton.org/fiftbase.pdf) +- [TON Virtual Machine](/learn/tvm-instructions/tvm-overview) + +## Examples + +- [Fift Smart Conctract examples](/develop/smart-contracts/examples#fift-smart-contracts) + +## Tutorials + +- [Introduction To Fift](https://blog.ton.org/introduction-to-fift) +- [\[YouTube\]His majesty Fift](https://www.youtube.com/watch?v=HVsveTmVowc&list=PLtUBO1QNEKwttRsAs9eacL2oCMOhWaOZs) \[[RU version](https://www.youtube.com/playlist?list=PLyDBPwv9EPsCYG-hR4N5FRTKUkfM8POgh)] by **@MarcoDaTr0p0je** & **@Wikimar**. + +## Sources + +- [Fift scripts for standard smart contracts](https://github.com/ton-blockchain/ton/tree/master/crypto/smartcont) From 2164d65fe485036ed331c871379b264d1267eab3 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:22 +0800 Subject: [PATCH 093/219] New translations builtins.md (Chinese Simplified) --- .../current/develop/func/builtins.md | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/func/builtins.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/builtins.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/builtins.md new file mode 100644 index 0000000000..9e1a454b02 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/builtins.md @@ -0,0 +1,31 @@ +# Built-ins + +This section describes some language constructions which are less fundamental than the ones described in previous articles. They could be defined in [stdlib.fc](/develop/func/stdlib) but it would leave less room for the FunC optimizer. + +## Throwing exceptions + +Exceptions can be thrown by conditional primitives `throw_if`, and `throw_unless`, and by unconditional `throw`. The first argument is the error code; the second is the condition (`throw` has only one argument). These primitives have parametrized versions `throw_arg_if`, `throw_arg_unless`, and `throw_arg`. The first argument is the exception parameter of any type; the second is the error code; the third is the condition (`throw_arg` has only two arguments). + +## Booleans + +- `true` is alias for `-1` +- `false` is alias for `0` + +## Dump variable + +A variable can be dumped to the debug log by the `~dump` function. + +## Dump string + +A string can be dumped to the debug log by the `~strdump` function. + +## Integer operations + +- `muldiv` is a multiple-then-divide operation. The intermediate result is stored in 513-bit integer, so it won't overflow if the actual result fits into a 257-bit integer. +- `divmod` is a operation that takes two numbers as parameters and gives the quotient and remainder of their division. + +## Other primitives + +- `null?` checks whether the argument is `null`. By the value `null` of a TVM type, `Null` FunC represents absence of a value of some atomic type; see [null values](/develop/func/types#null-values). +- `touch` and `~touch` move a variable to the top of the stack +- `at` gets the value of a tuple component on the specified position From 374853ab36ace61f10a2003f082851bf15810429 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:23 +0800 Subject: [PATCH 094/219] New translations changelog.md (Chinese Simplified) --- .../current/develop/func/changelog.md | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/func/changelog.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/changelog.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/changelog.md new file mode 100644 index 0000000000..df50c8efb8 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/changelog.md @@ -0,0 +1,61 @@ +# History of FunC + +# Initial version + +Initial version was done by Telegram and active development was ceased after May 2020. We refer to version of May 2020 as "initial". + +# Version 0.1.0 + +Released in [05.2022 update](https://github.com/ton-blockchain/ton/releases/tag/v2022.05). + +In this version were added: + +- [Constants](/develop/func/literals_identifiers#constants) +- [Extended string literals](/develop/func/literals_identifiers#string-literals) +- [Semver pragmas](/develop/func/compiler_directives#pragma-version) +- [Includes](/develop/func/compiler_directives#pragma-version) + +Fixed: + +- Fixed rarely manifested bugs in Asm.fif. + +# Version 0.2.0 + +Released in [08.2022 update](https://github.com/ton-blockchain/ton/releases/tag/v2022.08). + +In this version were added: + +- Unbalanced if/else branches (when some branches return and some are not) + +Fixed: + +- [FunC incorrectly handles while(false) loops #377](https://github.com/ton-blockchain/ton/issues/377) +- [FunC incorreclty generate code for ifelse branches #374](https://github.com/ton-blockchain/ton/issues/374) +- [FunC incorrectly return from condition in inline functions #370](https://github.com/ton-blockchain/ton/issues/370) +- [Asm.fif: splitting of large function bodies incorrectly interfere with inlines #375](https://github.com/ton-blockchain/ton/issues/375) + +# Version 0.3.0 + +Released in [10.2022 update](https://github.com/ton-blockchain/ton/releases/tag/v2022.10). + +In this version were added: + +- [Multiline asms](/develop/func/functions#multiline-asms) +- Duplication of identical definition for constants and asms became allowed +- Bitwise operations for constants for constants became allowed + +# Version 0.4.0 + +Released in [01.2023 update](https://github.com/ton-blockchain/ton/releases/tag/v2023.01). + +In this version were added: + +- [try/catch statements](/develop/func/statements#try-catch-statements) +- [throw_arg functions](/develop/func/builtins#throwing-exceptions) +- allowed in-place modification and mass-assignments of global variables: `a~inc()` and `(a, b) = (3, 5)`, where `a` is global + +Fixed: + +- forbidden ambiguous modification of local variables after it's usage in the same expression: `var x = (ds, ds~load_uint(32), ds~load_unit(64));` are forbidden, while `var x = (ds~load_uint(32), ds~load_unit(64), ds);` are not +- Allowed empty inline functions +- fix rare `while` optimization bug From a0e66025a503b326ba64c95046b2e98715137488 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:24 +0800 Subject: [PATCH 095/219] New translations comments.md (Chinese Simplified) --- .../current/develop/func/comments.md | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/func/comments.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/comments.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/comments.md new file mode 100644 index 0000000000..b60a28ab99 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/comments.md @@ -0,0 +1,32 @@ +# Comments + +FunC has single-line comments which start with `;;` (double `;`). For example: + +```func +int x = 1; ;; assign 1 to x +``` + +It also has multi-line comments which start with `{-` and end with `-}`. Note that unlike in many other languages, FunC multi-line comments can be nested. For example: + +```func +{- This is a multi-line comment + {- this is a comment in the comment -} +-} +``` + +Moreover, there can be one-line comments inside multi-line ones, and one-line comments `;;` are "stronger" than multiline `{- -}`. In other words in the following example: + +```func +{- + Start of the comment + +;; this comment ending is itself commented -> -} + +const a = 10; +;; this comment begining is itself commented -> {- + + End of the comment +-} +``` + +`const a = 10;` is inside multiline comment and is commented out. From 1c903e4a13e4b1aa392ba5866ce8799c9e4a1d9d Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:24 +0800 Subject: [PATCH 096/219] New translations compiler_directives.md (Chinese Simplified) --- .../develop/func/compiler_directives.md | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/func/compiler_directives.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/compiler_directives.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/compiler_directives.md new file mode 100644 index 0000000000..c48aa1c36c --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/compiler_directives.md @@ -0,0 +1,92 @@ +# Compiler directives + +These are keywords that start with `#` and instruct the compiler to do some actions, checks, or change parameters. + +Those directives can be used only at the outermost level (not inside any function definition). + +## #include + +The `#include` directive allows to include another FunC source code file that will be parsed in place of include. + +Syntax is `#include "filename.fc";`. Files are automatically checked for re-inclusion, and attempts to include +a file more than once will be ignored by default, with a warning if the verbosity level is no lower than 2. + +If an error happens during the parsing of an included file, additionally, a stack of inclusions is printed with the locations +of each included file in the chain. + +## #pragma + +The `#pragma` directive is used to provide additional information to the compiler beyond what the language itself conveys. + +### #pragma version + +Version pragma is used to enforce a specific version of FunC compiler when compiling the file. + +The version is specified in a semver format, that is, _a.b.c_, where _a_ is the major version, _b_ is the minor, and _c_ is the patch. + +There are several comparison operators available to a developer: + +- _a.b.c_ or _=a.b.c_—requires exactly the _a.b.c_ version of the compiler +- _>a.b.c_—requires the compiler version to be higher than _a.b.c_, + - _>=a.b.c_—requires the compiler version to be higher or equal to _a.b.c_ +- _\_, _>=_, _<_, _<=_) short format assumes zeros in omitted parts, that is: + +- _>a.b_ is the same as _>a.b.0_ (and therefore does NOT match thd _a.b.0_ version) +- _<=a_ is the same as _<=a.0.0_ (and therefore does NOT match the _a.0.1_ version) +- _^a.b.0_ is **NOT** the same as _^a.b_ + +For example, _^a.1.2_ matches _a.1.3_ but not _a.2.3_ or _a.1.0_, however, _^a.1_ matches them all. + +This directive may be used multiple times; the compiler version must satisfy all provided conditions. + +### #pragma not-version + +The syntax of this pragma is the same as the version pragma but it fails if the condition is satisfied. + +It can be used for blacklisting a specific version known to have problems, for example. + +### #pragma allow-post-modification + +_funC v0.4.1_ + +By default it is prohibited to use variable prior to its modification in the same expression. In other words, expression `(x, y) = (ds, ds~load_uint(8))` won't compile, while `(x, y) = (ds~load_uint(8), ds)` is valid. + +This rule can be overwritten, by `#pragma allow-post-modification`, which allow to modify variable after usage in mass assignments and function invocation; as usual sub-expressions will be computed left to right: `(x, y) = (ds, ds~load_bits(8))` will result in `x` containing initial `ds`; `f(ds, ds~load_bits(8))` first argument of `f` will contain initial `ds`, and second - 8 bits of `ds`. + +`#pragma allow-post-modification` works only for code after the pragma. + +### #pragma compute-asm-ltr + +_funC v0.4.1_ + +Asm declarations can overwrite order of arguments, for instance in the following expression + +```func +idict_set_ref(ds~load_dict(), ds~load_uint(8), ds~load_uint(256), ds~load_ref()) +``` + +order of parsing will be: `load_ref()`, `load_uint(256)`, `load_dict()` and `load_uint(8)` due to following asm declaration (note `asm(value index dict key_len)`): + +```func +cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF"; +``` + +This behavior can be changed to strict left-to-right order of computation via `#pragma compute-asm-ltr` + +As a result in + +```func +#pragma compute-asm-ltr +... +idict_set_ref(ds~load_dict(), ds~load_uint(8), ds~load_uint(256), ds~load_ref()); +``` + +order of parsing will be `load_dict()`, `load_uint(8)`, `load_uint(256)`, `load_ref()` and all asm permutation will happen after computation. + +`#pragma compute-asm-ltr` works only for code after the pragma. From b669e2fd18551b60b53ae14cbc099ea45322009f Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:25 +0800 Subject: [PATCH 097/219] New translations cookbook.md (Chinese Simplified) --- .../current/develop/func/cookbook.md | 1486 +++++++++++++++++ 1 file changed, 1486 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/func/cookbook.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/cookbook.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/cookbook.md new file mode 100644 index 0000000000..ad693560c1 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/cookbook.md @@ -0,0 +1,1486 @@ +# FunC Cookbook + +The core reason for creating the FunC Cookbook is to collect all the experience from FunC developers in one place so that future developers will use it! + +Compared to the [FunC Documentation](/develop/func/types), this article is more focused on everyday tasks every FunC developer resolve during the development of smart contracts. + +## Basics + +### How to write an if statement + +Let's say we want to check if any event is relevant. To do this, we use the flag variable. Remember that in FunC `true` is `-1` and `false` is `0`. + +```func +int flag = 0; ;; false + +if (flag) { + ;; do something +} +else { + ;; reject the transaction +} +``` + +> 💡 Noted +> +> We do not need the operator `==`, because the value `0` is `false`, so any other value will be `true`. + +> 💡 Useful links +> +> ["If statement" in docs](/develop/func/statements#if-statements) + +### How to write a repeat loop + +As an example, we can take exponentiation + +```func +int number = 2; +int multiplier = number; +int degree = 5; + +repeat(degree - 1) { + + number *= multiplier; +} +``` + +> 💡 Useful links +> +> ["Repeat loop" in docs](/develop/func/statements#repeat-loop) + +### How to write a while loop + +While is useful when we do not know how often to perform a particular action. For example, take a `cell`, which is known to store up to four references to other cells. + +```func +cell inner_cell = begin_cell() ;; create a new empty builder + .store_uint(123, 16) ;; store uint with value 123 and length 16 bits + .end_cell(); ;; convert builder to a cell + +cell message = begin_cell() + .store_ref(inner_cell) ;; store cell as reference + .store_ref(inner_cell) + .end_cell(); + +slice msg = message.begin_parse(); ;; convert cell to slice +while (msg.slice_refs_empty?() != -1) { ;; we should remind that -1 is true + cell inner_cell = msg~load_ref(); ;; load cell from slice msg + ;; do something +} +``` + +> 💡 Useful links +> +> ["While loop" in docs](/develop/func/statements#while-loop) +> +> ["Cell" in docs](/learn/overviews/cells) +> +> ["slice_refs_empty?()" in docs](/develop/func/stdlib#slice_refs_empty) +> +> ["store_ref()" in docs](/develop/func/stdlib#store_ref) +> +> ["begin_cell()" in docs](/develop/func/stdlib#begin_cell) +> +> ["end_cell()" in docs](/develop/func/stdlib#end_cell) +> +> ["begin_parse()" in docs](/develop/func/stdlib#begin_parse) + +### How to write a do until loop + +When we need the cycle to run at least once, we use `do until`. + +```func +int flag = 0; + +do { + ;; do something even flag is false (0) +} until (flag == -1); ;; -1 is true +``` + +> 💡 Useful links +> +> ["Until loop" in docs](/develop/func/statements#until-loop) + +### How to determine if slice is empty + +Before working with `slice`, it is necessary to check whether it has any data to process it correctly. We can use `slice_empty?()` to do this, but we have to consider that it will return `-1` (`true`) if there is at least one `bit` of data or one `ref`. + +```func +;; creating empty slice +slice empty_slice = ""; +;; `slice_empty?()` returns `true`, because slice dosen't have any `bits` and `refs` +empty_slice.slice_empty?(); + +;; creating slice which contains bits only +slice slice_with_bits_only = "Hello, world!"; +;; `slice_empty?()` returns `false`, because slice have any `bits` +slice_with_bits_only.slice_empty?(); + +;; creating slice which contains refs only +slice slice_with_refs_only = begin_cell() + .store_ref(null()) + .end_cell() + .begin_parse(); +;; `slice_empty?()` returns `false`, because slice have any `refs` +slice_with_refs_only.slice_empty?(); + +;; creating slice which contains bits and refs +slice slice_with_bits_and_refs = begin_cell() + .store_slice("Hello, world!") + .store_ref(null()) + .end_cell() + .begin_parse(); +;; `slice_empty?()` returns `false`, because slice have any `bits` and `refs` +slice_with_bits_and_refs.slice_empty?(); +``` + +> 💡 Useful links +> +> ["slice_empty?()" in docs](/develop/func/stdlib#slice_empty) +> +> ["store_slice()" in docs](/develop/func/stdlib#store_slice) +> +> ["store_ref()" in docs](/develop/func/stdlib#store_ref) +> +> ["begin_cell()" in docs](/develop/func/stdlib#begin_cell) +> +> ["end_cell()" in docs](/develop/func/stdlib#end_cell) +> +> ["begin_parse()" in docs](/develop/func/stdlib#begin_parse) + +### How to determine if slice is empty (dosen't have any bits, but may have refs) + +If we need to check only the `bits` and it does not matter if there are any `refs` in `slice`, then we should use `slice_data_empty?()`. + +```func +;; creating empty slice +slice empty_slice = ""; +;; `slice_data_empty?()` returns `true`, because slice dosen't have any `bits` +empty_slice.slice_data_empty?(); + +;; creating slice which contains bits only +slice slice_with_bits_only = "Hello, world!"; +;; `slice_data_empty?()` returns `false`, because slice have any `bits` +slice_with_bits_only.slice_data_empty?(); + +;; creating slice which contains refs only +slice slice_with_refs_only = begin_cell() + .store_ref(null()) + .end_cell() + .begin_parse(); +;; `slice_data_empty?()` returns `true`, because slice dosen't have any `bits` +slice_with_refs_only.slice_data_empty?(); + +;; creating slice which contains bits and refs +slice slice_with_bits_and_refs = begin_cell() + .store_slice("Hello, world!") + .store_ref(null()) + .end_cell() + .begin_parse(); +;; `slice_data_empty?()` returns `false`, because slice have any `bits` +slice_with_bits_and_refs.slice_data_empty?(); +``` + +> 💡 Useful links +> +> ["slice_data_empty?()" in docs](/develop/func/stdlib#slice_data_empty) +> +> ["store_slice()" in docs](/develop/func/stdlib#store_slice) +> +> ["store_ref()" in docs](/develop/func/stdlib#store_ref) +> +> ["begin_cell()" in docs](/develop/func/stdlib#begin_cell) +> +> ["end_cell()" in docs](/develop/func/stdlib#end_cell) +> +> ["begin_parse()" in docs](/develop/func/stdlib#begin_parse) + +### How to determine if slice is empty (dosen't have any refs, but may have bits) + +In case we are only interested in `refs`, we should check their presence using `slice_refs_empty?()`. + +```func +;; creating empty slice +slice empty_slice = ""; +;; `slice_refs_empty?()` returns `true`, because slice dosen't have any `refs` +empty_slice.slice_refs_empty?(); + +;; creating slice which contains bits only +slice slice_with_bits_only = "Hello, world!"; +;; `slice_refs_empty?()` returns `true`, because slice dosen't have any `refs` +slice_with_bits_only.slice_refs_empty?(); + +;; creating slice which contains refs only +slice slice_with_refs_only = begin_cell() + .store_ref(null()) + .end_cell() + .begin_parse(); +;; `slice_refs_empty?()` returns `false`, because slice have any `refs` +slice_with_refs_only.slice_refs_empty?(); + +;; creating slice which contains bits and refs +slice slice_with_bits_and_refs = begin_cell() + .store_slice("Hello, world!") + .store_ref(null()) + .end_cell() + .begin_parse(); +;; `slice_refs_empty?()` returns `false`, because slice have any `refs` +slice_with_bits_and_refs.slice_refs_empty?(); +``` + +> 💡 Useful links +> +> ["slice_refs_empty?()" in docs](/develop/func/stdlib#slice_refs_empty) +> +> ["store_slice()" in docs](/develop/func/stdlib#store_slice) +> +> ["store_ref()" in docs](/develop/func/stdlib#store_ref) +> +> ["begin_cell()" in docs](/develop/func/stdlib#begin_cell) +> +> ["end_cell()" in docs](/develop/func/stdlib#end_cell) +> +> ["begin_parse()" in docs](/develop/func/stdlib#begin_parse) + +### How to determine if cell is empty + +To check if there is any data in a `cell`, we should first convert it to `slice`. If we are only interested in having `bits`, we should use `slice_data_empty?()`, if only `refs` - `slice_refs_empty?()`. In case we want to check the presence of any data regardless of whether it is a `bit` or `ref`, we need to use `slice_empty?()`. + +```func +cell cell_with_bits_and_refs = begin_cell() + .store_uint(1337, 16) + .store_ref(null()) + .end_cell(); + +;; Change `cell` type to slice with `begin_parse()` +slice cs = cell_with_bits_and_refs.begin_parse(); + +;; determine if slice is empty +if (cs.slice_empty?()) { + ;; cell is empty +} +else { + ;; cell is not empty +} +``` + +> 💡 Useful links +> +> ["slice_empty?()" in docs](/develop/func/stdlib#slice_empty) +> +> ["begin_cell()" in docs](/develop/func/stdlib#begin_cell) +> +> ["store_uint()" in docs](/develop/func/stdlib#store_uint) +> +> ["end_cell()" in docs](/develop/func/stdlib#end_cell) +> +> ["begin_parse()" in docs](/develop/func/stdlib#begin_parse) + +### How to determine if dict is empty + +There is a method of `dict_empty?()` to check the date presence in dict. This method is the equivalent of `cell_null?()` because usually a `null`-cell is an empty dictionary. + +```func +cell d = new_dict(); +d~udict_set(256, 0, "hello"); +d~udict_set(256, 1, "world"); + +if (d.dict_empty?()) { ;; Determine if dict is empty + ;; dict is empty +} +else { + ;; dict is not empty +} +``` + +> 💡 Useful links +> +> ["dict_empty?()" in docs](/develop/func/stdlib#dict_empty) +> +> ["new_dict()" in docs](/develop/func/stdlib/#new_dict) creating an empty dict +> +> ["dict_set()" in docs](/develop/func/stdlib/#dict_set) adding some elements in dict d with function, so it is not empty + +### How to determine if tuple is empty + +When working with `tuples`, it is important always to know if any values are inside for extraction. If we try to extract value from an empty `tuple`, we get an error: "not a tuple of valid size" with `exit code 7`. + +```func +;; Declare tlen function because it's not presented in stdlib +(int) tlen (tuple t) asm "TLEN"; + +() main () { + tuple t = empty_tuple(); + t~tpush(13); + t~tpush(37); + + if (t.tlen() == 0) { + ;; tuple is empty + } + else { + ;; tuple is not empty + } +} +``` + +> 💡 Noted +> +> We are declaring tlen assembly function. You can read more [here](/develop/func/functions#assembler-function-body-definition) and see [list of all assembler commands](/learn/tvm-instructions/instructions). + +> 💡 Useful links +> +> ["empty_tuple?()" in docs](/develop/func/stdlib#empty_tuple) +> +> ["tpush()" in docs](/develop/func/stdlib/#tpush) +> +> ["Exit codes" in docs](/learn/tvm-instructions/tvm-exit-codes) + +### How to determine if lisp-style list is empty + +```func +tuple numbers = null(); +numbers = cons(100, numbers); + +if (numbers.null?()) { + ;; list-style list is empty +} else { + ;; list-style list is not empty +} +``` + +We are adding number 100 to our list-style list with [cons](/develop/func/stdlib/#cons) function, so it's not empty. + +### How to determine a state of the contract is empty + +Let’s say we have a `counter` that stores the number of transactions. This variable is not available during the first transaction in the smart contract state, because the state is empty, so it is necessary to process such a case. If the state is empty, we create a variable `counter` and save it. + +```func +;; `get_data()` will return the data cell from contract state +cell contract_data = get_data(); +slice cs = contract_data.begin_parse(); + +if (cs.slice_empty?()) { + ;; contract data is empty, so we create counter and save it + int counter = 1; + ;; create cell, add counter and save in contract state + set_data(begin_cell().store_uint(counter, 32).end_cell()); +} +else { + ;; contract data is not empty, so we get our counter, increase it and save + ;; we should specify correct length of our counter in bits + int counter = cs~load_uint(32) + 1; + set_data(begin_cell().store_uint(counter, 32).end_cell()); +} +``` + +> 💡 Noted +> +> We can determine that state of contract is empty by determining that [cell is empty](/develop/func/cookbook#how-to-determine-if-cell-is-empty). + +> 💡 Useful links +> +> ["get_data()" in docs](/develop/func/stdlib#get_data) +> +> ["begin_parse()" in docs](/develop/func/stdlib/#begin_parse) +> +> ["slice_empty?()" in docs](/develop/func/stdlib/#slice_empty) +> +> ["set_data?()" in docs](/develop/func/stdlib#set_data) + +### How to build an internal message cell + +If we want the contract to send an internal message, we should first properly create it as a cell, specifying the technical flags, the recipient address, and the rest data. + +```func +;; We use literal `a` to get valid address inside slice from string containing address +slice addr = "EQArzP5prfRJtDM5WrMNWyr9yUTAi0c9o6PfR4hkWy9UQXHx"a; +int amount = 1000000000; +;; we use `op` for identifying operations +int op = 0; + +cell msg = begin_cell() + .store_uint(0x18, 6) + .store_slice(addr) + .store_coins(amount) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) + .store_uint(op, 32) +.end_cell(); + +send_raw_message(msg, 3); ;; mode 3 - pay fees separately and ignore errors +``` + +> 💡 Noted +> +> In this example, we use literal `a` to get address. You can find more about string literals in [docs](/develop/func/literals_identifiers#string-literals) + +> 💡 Noted +> +> You can find more in [docs](/develop/smart-contracts/messages). Also, you can jump in [layout](/develop/smart-contracts/messages#message-layout) with this link. + +> 💡 Useful links +> +> ["begin_cell()" in docs](/develop/func/stdlib#begin_cell) +> +> ["store_uint()" in docs](/develop/func/stdlib#store_uint) +> +> ["store_slice()" in docs](/develop/func/stdlib#store_slice) +> +> ["store_coins()" in docs](/develop/func/stdlib#store_coins) +> +> ["end_cell()" in docs](/develop/func/stdlib/#end_cell) +> +> ["send_raw_message()" in docs](/develop/func/stdlib/#send_raw_message) + +### How to contain a body as ref to an internal message cell + +In the body of a message that follows flags and other technical data, we can send `int`, `slice`, and `cell`. In the case of the latter, it is necessary to set the bit to `1` before `store_ref()` to indicate that the `cell` will go on. + +We can also send the body of the message inside the same `cell` as header, if we are sure that we have enough space. In this case, we need to set the bit to `0`. + +```func +;; We use literal `a` to get valid address inside slice from string containing address +slice addr = "EQArzP5prfRJtDM5WrMNWyr9yUTAi0c9o6PfR4hkWy9UQXHx"a; +int amount = 1000000000; +int op = 0; +cell message_body = begin_cell() ;; Creating a cell with message + .store_uint(op, 32) + .store_slice("❤") +.end_cell(); + +cell msg = begin_cell() + .store_uint(0x18, 6) + .store_slice(addr) + .store_coins(amount) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1) ;; default message headers (see sending messages page) + .store_uint(1, 1) ;; set bit to 1 to indicate that the cell will go on + .store_ref(message_body) +.end_cell(); + +send_raw_message(msg, 3); ;; mode 3 - pay fees separately and ignore errors +``` + +> 💡 Noted +> +> In this example, we use literal `a` to get address. You can find more about string literals in [docs](/develop/func/literals_identifiers#string-literals) + +> 💡 Noted +> +> In this example, we used mode 3 to take the incoming tons and send exactly as much as specified (amount) while paying commission from the contract balance and ignoring the errors. Mode 64 is needed to return all the tons received, subtracting the commission, and mode 128 will send the entire balance. + +> 💡 Noted +> +> We are [building a message](/develop/func/cookbook#how-to-build-an-internal-message-cell) but adding message body separetly. + +> 💡 Useful links +> +> ["begin_cell()" in docs](/develop/func/stdlib#begin_cell) +> +> ["store_uint()" in docs](/develop/func/stdlib#store_uint) +> +> ["store_slice()" in docs](/develop/func/stdlib#store_slice) +> +> ["store_coins()" in docs](/develop/func/stdlib#store_coins) +> +> ["end_cell()" in docs](/develop/func/stdlib/#end_cell) +> +> ["send_raw_message()" in docs](/develop/func/stdlib/#send_raw_message) + +### How to contain a body as slice to an internal message cell + +When sending messages, the body message can be sent either as `cell` or as `slice`. In this example, we send the body of the message inside the `slice`. + +```func +;; We use literal `a` to get valid address inside slice from string containing address +slice addr = "EQArzP5prfRJtDM5WrMNWyr9yUTAi0c9o6PfR4hkWy9UQXHx"a; +int amount = 1000000000; +int op = 0; +slice message_body = "❤"; + +cell msg = begin_cell() + .store_uint(0x18, 6) + .store_slice(addr) + .store_coins(amount) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) + .store_uint(op, 32) + .store_slice(message_body) +.end_cell(); + +send_raw_message(msg, 3); ;; mode 3 - pay fees separately and ignore errors +``` + +> 💡 Noted +> +> In this example, we use literal `a` to get address. You can find more about string literals in [docs](/develop/func/literals_identifiers#string-literals) + +> 💡 Noted +> +> In this example, we used mode 3 to take the incoming tons and send exactly as much as specified (amount) while paying commission from the contract balance and ignoring the errors. Mode 64 is needed to return all the tons received, subtracting the commission, and mode 128 will send the entire balance. + +> 💡 Noted +> +> We are [building a message](/develop/func/cookbook#how-to-build-an-internal-message-cell) but adding message as a slice. + +### How to iterate tuples (in both directions) + +If we want to work with an array or stack in FunC, then tuple will be necessary there. And first of all we need to be able to iterate values to work with them. + +```func +(int) tlen (tuple t) asm "TLEN"; +forall X -> (tuple) to_tuple (X x) asm "NOP"; + +() main () { + tuple t = to_tuple([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + int len = t.tlen(); + + int i = 0; + while (i < len) { + int x = t.at(i); + ;; do something with x + i = i + 1; + } + + i = len - 1; + while (i >= 0) { + int x = t.at(i); + ;; do something with x + i = i - 1; + } +} +``` + +> 💡 Noted +> +> We are declaring `tlen` assembly function. You can read more [here](/develop/func/functions#assembler-function-body-definition) and see [list of all assembler commands](/learn/tvm-instructions/instructions). +> +> Also we declaring `to_tuple` function. It just changes data type of any input to tuple, so be careful while using it. + +### How to write own functions using `asm` keyword + +When using any features we actually use pre-prepared for us methods inside `stdlib.fc`. But in fact, we have many more opportunities available to us, and we need to learn to write them ourselves. + +For example, we have the method of `tpush`, which adds an element to `tuple`, but without `tpop`. In this case, we should do this: + +```func +;; ~ means it is modifying method +forall X -> (tuple, X) ~tpop (tuple t) asm "TPOP"; +``` + +If we want to know the length of `tuple` for iteration, we should write a new function with the `TLEN` asm instruction: + +```func +int tuple_length (tuple t) asm "TLEN"; +``` + +Some examples of functions already known to us from stdlib.fc: + +```func +slice begin_parse(cell c) asm "CTOS"; +builder begin_cell() asm "NEWC"; +cell end_cell(builder b) asm "ENDC"; +``` + +> 💡 Useful links: +> +> ["modifying method" in docs](/develop/func/statements#modifying-methods) +> +> ["stdlib" in docs](/develop/func/stdlib) +> +> ["TVM instructions" in docs](/learn/tvm-instructions/instructions) + +### Iterating n-nested tuples + +Sometimes we want to iterate nested tuples. The following example will iterate and print all of the items in a tuple of format `[[2,6],[1,[3,[3,5]]], 3]` starting from the head + +```func +int tuple_length (tuple t) asm "TLEN"; +forall X -> (tuple, X) ~tpop (tuple t) asm "TPOP"; +forall X -> int is_tuple (X x) asm "ISTUPLE"; +forall X -> tuple cast_to_tuple (X x) asm "NOP"; +forall X -> int cast_to_int (X x) asm "NOP"; +forall X -> (tuple) to_tuple (X x) asm "NOP"; + +;; define global variable +global int max_value; + +() iterate_tuple (tuple t) impure { + repeat (t.tuple_length()) { + var value = t~tpop(); + if (is_tuple(value)) { + tuple tuple_value = cast_to_tuple(value); + iterate_tuple(tuple_value); + } + else { + if(value > max_value) { + max_value = value; + } + } + } +} + +() main () { + tuple t = to_tuple([[2,6], [1, [3, [3, 5]]], 3]); + int len = t.tuple_length(); + max_value = 0; ;; reset max_value; + iterate_tuple(t); ;; iterate tuple and find max value + ~dump(max_value); ;; 6 +} +``` + +> 💡 Useful links +> +> ["Global variables" in docs](/develop/func/global_variables) +> +> ["~dump" in docs](/develop/func/builtins#dump-variable) +> +> ["TVM instructions" in docs](/learn/tvm-instructions/instructions) + +### Basic operations with tuples + +```func +(int) tlen (tuple t) asm "TLEN"; +forall X -> (tuple, X) ~tpop (tuple t) asm "TPOP"; + +() main () { + ;; creating an empty tuple + tuple names = empty_tuple(); + + ;; push new items + names~tpush("Naito Narihira"); + names~tpush("Shiraki Shinichi"); + names~tpush("Akamatsu Hachemon"); + names~tpush("Takaki Yuichi"); + + ;; pop last item + slice last_name = names~tpop(); + + ;; get first item + slice first_name = names.first(); + + ;; get an item by index + slice best_name = names.at(2); + + ;; getting the length of the list + int number_names = names.tlen(); +} +``` + +### Resolving type X + +The following example checks if some value is contained in a tuple, but tuple contains values X (cell, slice, int, tuple, int). We need to check the value and cast accordingly. + +```func +forall X -> int is_null (X x) asm "ISNULL"; +forall X -> int is_int (X x) asm "<{ TRY:<{ 0 PUSHINT ADD DROP -1 PUSHINT }>CATCH<{ 2DROP 0 PUSHINT }> }>CONT 1 1 CALLXARGS"; +forall X -> int is_cell (X x) asm "<{ TRY:<{ CTOS DROP -1 PUSHINT }>CATCH<{ 2DROP 0 PUSHINT }> }>CONT 1 1 CALLXARGS"; +forall X -> int is_slice (X x) asm "<{ TRY:<{ SBITS DROP -1 PUSHINT }>CATCH<{ 2DROP 0 PUSHINT }> }>CONT 1 1 CALLXARGS"; +forall X -> int is_tuple (X x) asm "ISTUPLE"; +forall X -> int cast_to_int (X x) asm "NOP"; +forall X -> cell cast_to_cell (X x) asm "NOP"; +forall X -> slice cast_to_slice (X x) asm "NOP"; +forall X -> tuple cast_to_tuple (X x) asm "NOP"; +forall X -> (tuple, X) ~tpop (tuple t) asm "TPOP"; + +forall X -> () resolve_type (X value) impure { + ;; value here is of type X, since we dont know what is the exact value - we would need to check what is the value and then cast it + + if (is_null(value)) { + ;; do something with the null + } + elseif (is_int(value)) { + int valueAsInt = cast_to_int(value); + ;; do something with the int + } + elseif (is_slice(value)) { + slice valueAsSlice = cast_to_slice(value); + ;; do something with the slice + } + elseif (is_cell(value)) { + cell valueAsCell = cast_to_cell(value); + ;; do something with the cell + } + elseif (is_tuple(value)) { + tuple valueAsTuple = cast_to_tuple(value); + ;; do something with the tuple + } +} + +() main () { + ;; creating an empty tuple + tuple stack = empty_tuple(); + ;; let's say we have tuple and do not know the exact types of them + stack~tpush("Some text"); + stack~tpush(4); + ;; we use var because we do not know type of value + var value = stack~tpop(); + resolve_type(value); +} +``` + +> 💡 Useful links +> +> ["TVM instructions" in docs](/learn/tvm-instructions/instructions) + +### How to get current time + +```func +int current_time = now(); + +if (current_time > 1672080143) { + ;; do some stuff +} +``` + +### How to generate random number + +:::caution draft + +TODO: add link to an article about generating random numbers +::: + +```func +randomize_lt(); ;; do this once + +int a = rand(10); +int b = rand(1000000); +int c = random(); +``` + +### Modulo operations + +As an example, lets say that we want to run the following calculation of all 256 numbers : `(xp + zp)*(xp-zp)`. Since most of those operations are used for cryptography, in the following example we are using the modulo operator for montogomery curves. +Note that xp+zp is a valid variable name ( without spaces between ). + +```func +(int) modulo_operations (int xp, int zp) { + ;; 2^255 - 19 is a prime number for montgomery curves, meaning all operations should be done against its prime + int prime = 57896044618658097711785492504343953926634992332820282019728792003956564819949; + + ;; muldivmod handles the next two lines itself + ;; int xp+zp = (xp + zp) % prime; + ;; int xp-zp = (xp - zp + prime) % prime; + (_, int xp+zp*xp-zp) = muldivmod(xp + zp, xp - zp, prime); + return xp+zp*xp-zp; +} +``` + +> 💡 Useful links +> +> ["muldivmod" in docs](/learn/tvm-instructions/instructions#52-division) + +### How to throw errors + +```func +int number = 198; + +throw_if(35, number > 50); ;; the error will be triggered only if the number is greater than 50 + +throw_unless(39, number == 198); ;; the error will be triggered only if the number is NOT EQUAL to 198 + +throw(36); ;; the error will be triggered anyway +``` + +[Standard tvm exception codes](/learn/tvm-instructions/tvm-exit-codes.md) + +### Reversing tuples + +Because tuple stores data as a stack, sometimes we have to reverse tuple to read data from the other end. + +```func +forall X -> (tuple, X) ~tpop (tuple t) asm "TPOP"; +int tuple_length (tuple t) asm "TLEN"; +forall X -> (tuple) to_tuple (X x) asm "NOP"; + +(tuple) reverse_tuple (tuple t1) { + tuple t2 = empty_tuple(); + repeat (t1.tuple_length()) { + var value = t1~tpop(); + t2~tpush(value); + } + return t2; +} + +() main () { + tuple t = to_tuple([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + tuple reversed_t = reverse_tuple(t); + ~dump(reversed_t); ;; [10 9 8 7 6 5 4 3 2 1] +} +``` + +> 💡 Useful links +> +> ["tpush()" in docs](/develop/func/stdlib/#tpush) + +### How to remove an item with a certain index from the list + +```func +int tlen (tuple t) asm "TLEN"; + +(tuple, ()) remove_item (tuple old_tuple, int place) { + tuple new_tuple = empty_tuple(); + + int i = 0; + while (i < old_tuple.tlen()) { + int el = old_tuple.at(i); + if (i != place) { + new_tuple~tpush(el); + } + i += 1; + } + return (new_tuple, ()); +} + +() main () { + tuple numbers = empty_tuple(); + + numbers~tpush(19); + numbers~tpush(999); + numbers~tpush(54); + + ~dump(numbers); ;; [19 999 54] + + numbers~remove_item(1); + + ~dump(numbers); ;; [19 54] +} +``` + +### Determine if slices are equal + +There are two different ways we can determine the equality. One is based on the slice hash, while the other one by using the SDEQ asm instruction. + +```func +int are_slices_equal_1? (slice a, slice b) { + return a.slice_hash() == b.slice_hash(); +} + +int are_slices_equal_2? (slice a, slice b) asm "SDEQ"; + +() main () { + slice a = "Some text"; + slice b = "Some text"; + ~dump(are_slices_equal_1?(a, b)); ;; -1 = true + + a = "Text"; + ;; We use literal `a` to get valid address inside slice from string containing address + b = "EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF"a; + ~dump(are_slices_equal_2?(a, b)); ;; 0 = false +} +``` + +#### 💡 Useful links + +- ["slice_hash()" in docs](/develop/func/stdlib/#slice_hash) +- ["SDEQ" in docs](/learn/tvm-instructions/instructions#62-other-comparison) + +### Determine if cells are equal + +We can easily determine cell equality based on their hash. + +```func +int are_cells_equal? (cell a, cell b) { + return a.cell_hash() == b.cell_hash(); +} + +() main () { + cell a = begin_cell() + .store_uint(123, 16) + .end_cell(); + + cell b = begin_cell() + .store_uint(123, 16) + .end_cell(); + + ~dump(are_cells_equal?(a, b)); ;; -1 = true +} +``` + +> 💡 Useful links +> +> ["cell_hash()" in docs](/develop/func/stdlib/#cell_hash) + +### Determine if tuples are equal + +A more advanced example would be to iterate and compare each of the tuple values. Since they are X we need to check and cast to the corresponding type and if it is tuple to iterate it recursively. + +```func +int tuple_length (tuple t) asm "TLEN"; +forall X -> (tuple, X) ~tpop (tuple t) asm "TPOP"; +forall X -> int cast_to_int (X x) asm "NOP"; +forall X -> cell cast_to_cell (X x) asm "NOP"; +forall X -> slice cast_to_slice (X x) asm "NOP"; +forall X -> tuple cast_to_tuple (X x) asm "NOP"; +forall X -> int is_null (X x) asm "ISNULL"; +forall X -> int is_int (X x) asm "<{ TRY:<{ 0 PUSHINT ADD DROP -1 PUSHINT }>CATCH<{ 2DROP 0 PUSHINT }> }>CONT 1 1 CALLXARGS"; +forall X -> int is_cell (X x) asm "<{ TRY:<{ CTOS DROP -1 PUSHINT }>CATCH<{ 2DROP 0 PUSHINT }> }>CONT 1 1 CALLXARGS"; +forall X -> int is_slice (X x) asm "<{ TRY:<{ SBITS DROP -1 PUSHINT }>CATCH<{ 2DROP 0 PUSHINT }> }>CONT 1 1 CALLXARGS"; +forall X -> int is_tuple (X x) asm "ISTUPLE"; +int are_slices_equal? (slice a, slice b) asm "SDEQ"; + +int are_cells_equal? (cell a, cell b) { + return a.cell_hash() == b.cell_hash(); +} + +(int) are_tuples_equal? (tuple t1, tuple t2) { + int equal? = -1; ;; initial value to true + + if (t1.tuple_length() != t2.tuple_length()) { + ;; if tuples are differ in length they cannot be equal + return 0; + } + + int i = t1.tuple_length(); + + while (i > 0 & equal?) { + var v1 = t1~tpop(); + var v2 = t2~tpop(); + + if (is_null(t1) & is_null(t2)) { + ;; nulls are always equal + } + elseif (is_int(v1) & is_int(v2)) { + if (cast_to_int(v1) != cast_to_int(v2)) { + equal? = 0; + } + } + elseif (is_slice(v1) & is_slice(v2)) { + if (~ are_slices_equal?(cast_to_slice(v1), cast_to_slice(v2))) { + equal? = 0; + } + } + elseif (is_cell(v1) & is_cell(v2)) { + if (~ are_cells_equal?(cast_to_cell(v1), cast_to_cell(v2))) { + equal? = 0; + } + } + elseif (is_tuple(v1) & is_tuple(v2)) { + ;; recursively determine nested tuples + if (~ are_tuples_equal?(cast_to_tuple(v1), cast_to_tuple(v2))) { + equal? = 0; + } + } + else { + equal? = 0; + } + + i -= 1; + } + + return equal?; +} + +() main () { + tuple t1 = cast_to_tuple([[2, 6], [1, [3, [3, 5]]], 3]); + tuple t2 = cast_to_tuple([[2, 6], [1, [3, [3, 5]]], 3]); + + ~dump(are_tuples_equal?(t1, t2)); ;; -1 +} +``` + +> 💡 Useful links +> +> ["cell_hash()" in docs](/develop/func/stdlib/#cell_hash) +> +> ["TVM instructions" in docs](/learn/tvm-instructions/instructions) + +### Generate internal address + +We need to generate an internal address when our contract should deploy a new contract, but do not know his address. Suppose we already have `state_init` - the code and data of the new contract. + +Creates an internal address for the corresponding MsgAddressInt TLB. + +```func +(slice) generate_internal_address (int workchain_id, cell state_init) { + ;; addr_std$10 anycast:(Maybe Anycast) workchain_id:int8 address:bits256 = MsgAddressInt; + + return begin_cell() + .store_uint(2, 2) ;; addr_std$10 + .store_uint(0, 1) ;; anycast nothing + .store_int(workchain_id, 8) ;; workchain_id: -1 + .store_uint(cell_hash(state_init), 256) + .end_cell().begin_parse(); +} + +() main () { + slice deploy_address = generate_internal_address(workchain(), state_init); + ;; then we can deploy new contract +} +``` + +> 💡 Noted +> +> In this example, we use `workchain()` to get id of workchain. You can find more about Workchain ID in [docs](/learn/overviews/addresses#workchain-id). + +> 💡 Useful links +> +> ["cell_hash()" in docs](/develop/func/stdlib/#cell_hash) + +### Generate external address + +We use the TL-B scheme from [block.tlb](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L101C1-L101C12) to understand how we need to create an address in this format. + +```func +(int) ubitsize (int a) asm "UBITSIZE"; + +slice generate_external_address (int address) { + ;; addr_extern$01 len:(## 9) external_address:(bits len) = MsgAddressExt; + + int address_length = ubitsize(address); + + return begin_cell() + .store_uint(1, 2) ;; addr_extern$01 + .store_uint(address_length, 9) + .store_uint(address, address_length) + .end_cell().begin_parse(); +} +``` + +Since we need to determine the number of bits occupied by the address, it is also necessary to [declare an asm function](#how-to-write-own-functions-using-asm-keyword) with the opcode `UBITSIZE`, which will return the minimum number of bits required to store the number. + +> 💡 Useful links +> +> ["TVM Instructions" in docs](/learn/tvm-instructions/instructions#53-shifts-logical-operations) + +### How to store and load dictionary in local storage + +The logic for loading the dictionary + +```func +slice local_storage = get_data().begin_parse(); +cell dictionary_cell = new_dict(); +if (~ slice_empty?(local_storage)) { + dictionary_cell = local_storage~load_dict(); +} +``` + +While the logic for storing the dictionary is like the following example: + +```func +set_data(begin_cell().store_dict(dictionary_cell).end_cell()); +``` + +> 💡 Useful links +> +> ["get_data()" in docs](/develop/func/stdlib/#get_data) +> +> ["new_dict()" in docs](/develop/func/stdlib/#new_dict) +> +> ["slice_empty?()" in docs](/develop/func/stdlib/#slice_empty) +> +> ["load_dict()" in docs](/develop/func/stdlib/#load_dict) +> +> ["~" in docs](/develop/func/statements#unary-operators) + +### How to send a simple message + +The usual way for us to send tons with a comment is actually a simple message. To specify that the body of the message is a `comment`, we should set `32 bits` before the message text to 0. + +```func +cell msg = begin_cell() + .store_uint(0x18, 6) ;; flags + .store_slice("EQBIhPuWmjT7fP-VomuTWseE8JNWv2q7QYfsVQ1IZwnMk8wL"a) ;; destination address + .store_coins(100) ;; amount of nanoTons to send + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) + .store_uint(0, 32) ;; zero opcode - means simple transfer message with comment + .store_slice("Hello from FunC!") ;; comment +.end_cell(); +send_raw_message(msg, 3); ;; mode 3 - pay fees separately, ignore errors +``` + +> 💡 Useful links +> +> ["Message layout" in docs](/develop/smart-contracts/messages) + +### How to send a message with an incoming account + +The contract example below is useful to us if we need to perform any actions between the user and the main contract, that is, we need a proxy contract. + +```func +() recv_internal (slice in_msg_body) { + {- + This is a simple example of a proxy-contract. + It will expect in_msg_body to contain message mode, body and destination address to be sent to. + -} + + int mode = in_msg_body~load_uint(8); ;; first byte will contain msg mode + slice addr = in_msg_body~load_msg_addr(); ;; then we parse the destination address + slice body = in_msg_body; ;; everything that is left in in_msg_body will be our new message's body + + cell msg = begin_cell() + .store_uint(0x18, 6) + .store_slice(addr) + .store_coins(100) ;; just for example + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) + .store_slice(body) + .end_cell(); + send_raw_message(msg, mode); +} +``` + +> 💡 Useful links +> +> ["Message layout" in docs](/develop/smart-contracts/messages) +> +> ["load_msg_addr()" in docs](/develop/func/stdlib/#load_msg_addr) + +### How to send a message with the entire balance + +If we need to send the entire balance of the smart contract, then, in this case, we need to use send `mode 128`. An example of such a case would be a proxy contract that accepts payments and forwards to the main contract. + +```func +cell msg = begin_cell() + .store_uint(0x18, 6) ;; flags + .store_slice("EQBIhPuWmjT7fP-VomuTWseE8JNWv2q7QYfsVQ1IZwnMk8wL"a) ;; destination address + .store_coins(0) ;; we don't care about this value right now + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) + .store_uint(0, 32) ;; zero opcode - means simple transfer message with comment + .store_slice("Hello from FunC!") ;; comment +.end_cell(); +send_raw_message(msg, 128); ;; mode = 128 is used for messages that are to carry all the remaining balance of the current smart contract +``` + +> 💡 Useful links +> +> ["Message layout" in docs](/develop/smart-contracts/messages) +> +> ["Message modes" in docs](/develop/func/stdlib/#send_raw_message) + +### How to send a message with a long text comment + +As we know, only 127 characters can fit into a single `cell` (<1023 bits). In case we need more - we need to organize a snake cells. + +```func +{- + If we want to send a message with really long comment, we should split the comment to several slices. + Each slice should have <1023 bits of data (127 chars). + Each slice should have a reference to the next one, forming a snake-like structure. +-} + +cell body = begin_cell() + .store_uint(0, 32) ;; zero opcode - simple message with comment + .store_slice("long long long message...") + .store_ref(begin_cell() + .store_slice(" you can store string of almost any length here.") + .store_ref(begin_cell() + .store_slice(" just don't forget about the 127 chars limit for each slice") + .end_cell()) + .end_cell()) +.end_cell(); + +cell msg = begin_cell() + .store_uint(0x18, 6) ;; flags + ;; We use literal `a` to get valid address inside slice from string containing address + .store_slice("EQBIhPuWmjT7fP-VomuTWseE8JNWv2q7QYfsVQ1IZwnMk8wL"a) ;; destination address + .store_coins(100) ;; amount of nanoTons to send + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1) ;; default message headers (see sending messages page) + .store_uint(1, 1) ;; we want to store body as a ref + .store_ref(body) +.end_cell(); +send_raw_message(msg, 3); ;; mode 3 - pay fees separately, ignore errors +``` + +> 💡 Useful links +> +> ["Internal messages" in docs](/develop/smart-contracts/guidelines/internal-messages) + +### How to get only data bits from a slice (without refs) + +If we are not interested in `refs` inside the `slice`, then we can get a separate date and work with it. + +```func +slice s = begin_cell() + .store_slice("Some data bits...") + .store_ref(begin_cell().end_cell()) ;; some references + .store_ref(begin_cell().end_cell()) ;; some references +.end_cell().begin_parse(); + +slice s_only_data = s.preload_bits(s.slice_bits()); +``` + +> 💡 Useful links +> +> ["Slice primitives" in docs](/develop/func/stdlib/#slice-primitives) +> +> ["preload_bits()" in docs](/develop/func/stdlib/#preload_bits) +> +> ["slice_bits()" in docs](/develop/func/stdlib/#slice_bits) + +### How to define your own modifying method + +Modifying methods allow data to be modified within the same variable. This can be compared to referencing in other programming languages. + +```func +(slice, (int)) load_digit (slice s) { + int x = s~load_uint(8); ;; load 8 bits (one char) from slice + x -= 48; ;; char '0' has code of 48, so we substract it to get the digit as a number + return (s, (x)); ;; return our modified slice and loaded digit +} + +() main () { + slice s = "258"; + int c1 = s~load_digit(); + int c2 = s~load_digit(); + int c3 = s~load_digit(); + ;; here s is equal to "", and c1 = 2, c2 = 5, c3 = 8 +} +``` + +> 💡 Useful links +> +> ["Modifying methods" in docs](/develop/func/statements#modifying-methods) + +### How to raise number to the power of n + +```func +;; Unoptimized variant +int pow (int a, int n) { + int i = 0; + int value = a; + while (i < n - 1) { + a *= value; + i += 1; + } + return a; +} + +;; Optimized variant +(int) binpow (int n, int e) { + if (e == 0) { + return 1; + } + if (e == 1) { + return n; + } + int p = binpow(n, e / 2); + p *= p; + if ((e % 2) == 1) { + p *= n; + } + return p; +} + +() main () { + int num = binpow(2, 3); + ~dump(num); ;; 8 +} +``` + +### How to convert string to int + +```func +slice string_number = "26052021"; +int number = 0; + +while (~ string_number.slice_empty?()) { + int char = string_number~load_uint(8); + number = (number * 10) + (char - 48); ;; we use ASCII table +} + +~dump(number); +``` + +### How to convert int to string + +```func +int n = 261119911; +builder string = begin_cell(); +tuple chars = null(); +do { + int r = n~divmod(10); + chars = cons(r + 48, chars); +} until (n == 0); +do { + int char = chars~list_next(); + string~store_uint(char, 8); +} until (null?(chars)); + +slice result = string.end_cell().begin_parse(); +~dump(result); +``` + +### How to iterate dictionaries + +Dictionaries are very useful when working with a lot of data. We can get minimum and maximum key values using the built-in methods `dict_get_min?` and `dict_get_max?` respectively. Additionally, we can use `dict_get_next?` to iterate the dictionary. + +```func +cell d = new_dict(); +d~udict_set(256, 1, "value 1"); +d~udict_set(256, 5, "value 2"); +d~udict_set(256, 12, "value 3"); + +;; iterate keys from small to big +(int key, slice val, int flag) = d.udict_get_min?(256); +while (flag) { + ;; do something with pair key->val + + (key, val, flag) = d.udict_get_next?(256, key); +} +``` + +> 💡 Useful links +> +> ["Dictonaries primitives" in docs](/develop/func/stdlib/#dictionaries-primitives) +> +> ["dict_get_max?()" in docs](/develop/func/stdlib/#dict_get_max) +> +> ["dict_get_min?()" in docs](/develop/func/stdlib/#dict_get_min) +> +> ["dict_get_next?()" in docs](/develop/func/stdlib/#dict_get_next) +> +> ["dict_set()" in docs](/develop/func/stdlib/#dict_set) + +### How to delete value from dictionaries + +```func +cell names = new_dict(); +names~udict_set(256, 27, "Alice"); +names~udict_set(256, 25, "Bob"); + +names~udict_delete?(256, 27); + +(slice val, int key) = names.udict_get?(256, 27); +~dump(val); ;; null() -> means that key was not found in a dictionary +``` + +### How to iterate cell tree recursively + +As we know, one `cell` can store up to `1023 bits` of data and up to `4 refs`. To get around this limit, we can use a tree of cells, but to do this we need to be able to iterate it for proper data processing. + +```func +forall X -> int is_null (X x) asm "ISNULL"; +forall X -> (tuple, ()) push_back (tuple tail, X head) asm "CONS"; +forall X -> (tuple, (X)) pop_back (tuple t) asm "UNCONS"; + +() main () { + ;; just some cell for example + cell c = begin_cell() + .store_uint(1, 16) + .store_ref(begin_cell() + .store_uint(2, 16) + .end_cell()) + .store_ref(begin_cell() + .store_uint(3, 16) + .store_ref(begin_cell() + .store_uint(4, 16) + .end_cell()) + .store_ref(begin_cell() + .store_uint(5, 16) + .end_cell()) + .end_cell()) + .end_cell(); + + ;; creating tuple with no data, which plays the role of stack + tuple stack = null(); + ;; bring the main cell into the stack to process it in the loop + stack~push_back(c); + ;; do it until stack is not null + while (~ stack.is_null()) { + ;; get the cell from the stack and convert it to a slice to be able to process it + slice s = stack~pop_back().begin_parse(); + + ;; do something with s data + + ;; if the current slice has any refs, add them to stack + repeat (s.slice_refs()) { + stack~push_back(s~load_ref()); + } + } +} +``` + +> 💡 Useful links +> +> ["Lisp-style lists" in docs](/develop/func/stdlib/#lisp-style-lists) +> +> ["null()" in docs](/develop/func/stdlib/#null) +> +> ["slice_refs()" in docs](/develop/func/stdlib/#slice_refs) + +### How to iterate through lisp-style list + +The data type tuple can hold up to 255 values. If this is not enough, then we should use a lisp-style list. We can put a tuple inside a tuple, thus bypassing the limit. + +```func +forall X -> int is_null (X x) asm "ISNULL"; +forall X -> (tuple, ()) push_back (tuple tail, X head) asm "CONS"; +forall X -> (tuple, (X)) pop_back (tuple t) asm "UNCONS"; + +() main () { + ;; some example list + tuple l = null(); + l~push_back(1); + l~push_back(2); + l~push_back(3); + + ;; iterating through elements + ;; note that this iteration is in reversed order + while (~ l.is_null()) { + var x = l~pop_back(); + + ;; do something with x + } +} +``` + +> 💡 Useful links +> +> ["Lisp-style lists" in docs](/develop/func/stdlib/#lisp-style-lists) +> +> ["null()" in docs](/develop/func/stdlib/#null) + +### How to send a deploy message (with stateInit only, with stateInit and body) + +```func +() deploy_with_stateinit(cell message_header, cell state_init) impure { + var msg = begin_cell() + .store_slice(begin_parse(msg_header)) + .store_uint(2 + 1, 2) ;; init:(Maybe (Either StateInit ^StateInit)) + .store_uint(0, 1) ;; body:(Either X ^X) + .store_ref(state_init) + .end_cell(); + + ;; mode 64 - carry the remaining value in the new message + send_raw_message(msg, 64); +} + +() deploy_with_stateinit_body(cell message_header, cell state_init, cell body) impure { + var msg = begin_cell() + .store_slice(begin_parse(msg_header)) + .store_uint(2 + 1, 2) ;; init:(Maybe (Either StateInit ^StateInit)) + .store_uint(1, 1) ;; body:(Either X ^X) + .store_ref(state_init) + .store_ref(body) + .end_cell(); + + ;; mode 64 - carry the remaining value in the new message + send_raw_message(msg, 64); +} +``` + +### How to build a stateInit cell + +```func +() build_stateinit(cell init_code, cell init_data) { + var state_init = begin_cell() + .store_uint(0, 1) ;; split_depth:(Maybe (## 5)) + .store_uint(0, 1) ;; special:(Maybe TickTock) + .store_uint(1, 1) ;; (Maybe ^Cell) + .store_uint(1, 1) ;; (Maybe ^Cell) + .store_uint(0, 1) ;; (HashmapE 256 SimpleLib) + .store_ref(init_code) + .store_ref(init_data) + .end_cell(); +} +``` + +### How to calculate a contract address (using stateInit) + +```func +() calc_address(cell state_init) { + var future_address = begin_cell() + .store_uint(2, 2) ;; addr_std$10 + .store_uint(0, 1) ;; anycast:(Maybe Anycast) + .store_uint(0, 8) ;; workchain_id:int8 + .store_uint(cell_hash(state_init), 256) ;; address:bits256 + .end_cell(); +} +``` From 81df62f6fd2454391a6a9642c1f64b0d08449fef Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:26 +0800 Subject: [PATCH 098/219] New translations functions.md (Chinese Simplified) --- .../current/develop/func/functions.md | 385 ++++++++++++++++++ 1 file changed, 385 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/func/functions.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/functions.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/functions.md new file mode 100644 index 0000000000..e4427438c4 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/functions.md @@ -0,0 +1,385 @@ +# Functions + +FunC program is essentially a list of function declarations/definitions and global variable declarations. This section covers the first topic. + +Any function declaration or definition starts with a common pattern and one of the three things goes next: + +- single `;`, which means that the function is declared but not defined yet. It may be defined later in the same file or in some other file which is passed before the current one to the FunC compiler. For example, + ```func + int add(int x, int y); + ``` + is a simple declaration of a function named `add` of type `(int, int) -> int`. + +- assembler function body definition. It is the way to define functions by low-level TVM primitives for later use in FunC program. For example, + ```func + int add(int x, int y) asm "ADD"; + ``` + is an assembler definition of the same function `add` of type `(int, int) -> int` which will translate to the TVM opcode `ADD`. + +- ordinary block statement function body definition. It is the usual way to define functions. For example, + ```func + int add(int x, int y) { + return x + y; + } + ``` + is an ordinary definition of the `add` function. + +## Function declaration + +As said before, any function declaration or definition starts with a common pattern. The following is the pattern: + +```func +[] () +``` + +where `[ ... ]` corresponds to an optional entry. + +### Function name + +Function name can be any [identifier](/develop/func/literals_identifiers#identifiers) and also it can start with `.` or `~` symbols. The meaning of those symbols is [explained](/develop/func/statements#methods-calls) in the statements section. + +For example, `udict_add_builder?`, `dict_set` and `~dict_set` are valid and different function names. (They are defined in [stdlib.fc](/develop/func/stdlib).) + +#### Special function names + +FunC (actually Fift assembler) has several reserved function names with predefined [ids](/develop/func/functions#method_id). + +- `main` and `recv_internal` have id = 0 +- `recv_external` has id = -1 +- `run_ticktock` has id = -2 + +Every program must have a function with id 0, that is, `main` or `recv_internal` function. +`run_ticktock` is called in ticktock transactions of special smart contracts. + +#### Receive internal + +`recv_internal` is called when a smart contract receives an inbound internal message. +There are some variables at the stack when [TVM initiates](/learn/tvm-instructions/tvm-overview#initialization-of-tvm), by setting arguments in `recv_internal` we give smart-contract code awareness about some of them. Those arguments about which code will not know, will just lie at the bottom of the stack never touched. + +So each of the following `recv_internal` declarations is correct, but those with less variables will spend slightly less gas (each unused argument adds additional `DROP` instructions) + +```func + +() recv_internal(int balance, int msg_value, cell in_msg_cell, slice in_msg) {} +() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) {} +() recv_internal(cell in_msg_cell, slice in_msg) {} +() recv_internal(slice in_msg) {} +``` + +#### Receive external + +`recv_external` is for inbound external messages. + +### Return type + +Return type can be any atomic or composite type as described in the [types](/develop/func/types.md) section. For example, + +```func +int foo(); +(int, int) foo'(); +[int, int] foo''(); +(int -> int) foo'''(); +() foo''''(); +``` + +are valid function declarations. + +Type inference is also allowed. For example, + +```func +_ pyth(int m, int n) { + return (m * m - n * n, 2 * m * n, m * m + n * n); +} +``` + +is a valid definition of function `pyth` of type `(int, int) -> (int, int, int)`, which computes Pythagorean triples. + +### Function arguments + +Function arguments are separated by commas. The valid declarations of an argument are following: + +- Ordinary declaration: type + name. For example, `int x` is a declaration of argument of type `int` and name `x` in the function declaration `() foo(int x);` +- Unused argument declaration: only type. For example, + ```func + int first(int x, int) { + return x; + } + ``` + is a valid function definition of type `(int, int) -> int` +- Argument with an inferred type declaration: only name. + For example, + ```func + int inc(x) { + return x + 1; + } + ``` + is a valid function definition of type `int -> int`. The `int` type of `x` is inferred by the type-checker. + +Note that although a function may look like a function of several arguments, it's actually a function of one [tensor-type](/develop/func/types#tensor-types) argument. To see the difference, please refer to [function application](/develop/func/statements#function-application). Nevertheless, the components of the argument tensor are conventionally called function arguments. + +### Function calls + +#### Non-modifying methods + +:::info +Non-modifying function supports short function call form with `.` +::: + +```func +example(a); +a.example(); +``` + +If a function has at least one argument, it can be called as a non-modifying method. For example, `store_uint` has type `(builder, int, int) -> builder` (the second argument is the value to store, and the third is the bit length). `begin_cell` is a function that creates a new builder. The following codes are equivalent: + +```func +builder b = begin_cell(); +b = store_uint(b, 239, 8); +``` + +```func +builder b = begin_cell(); +b = b.store_uint(239, 8); +``` + +So the first argument of a function can be passed to it being located before the function name, if separated by `.`. The code can be further simplified: + +```func +builder b = begin_cell().store_uint(239, 8); +``` + +Multiple calls of methods are also possible: + +```func +builder b = begin_cell().store_uint(239, 8) + .store_int(-1, 16) + .store_uint(0xff, 10); +``` + +#### Modifying functions + +:::info +Modifying function supports short form with `~` and `.` operators. +::: + +If the first argument of a function has type `A` and the return value of the function has the shape of `(A, B)` where `B` is some arbitrary type, then the function can be called as a modifying method. + +Modifying function calls may take some arguments and return some values, but they modify their first argument, that is, assign the first component of the returned value to the variable from the first argument. + +```func +a~example(); +a = example(a); +``` + +For example, suppose `cs` is a cell slice and `load_uint` has type `(slice, int) -> (slice, int)`: it takes a cell slice and number of bits to load and returns the remainder of the slice and the loaded value. The following codes are equivalent: + +```func +(cs, int x) = load_uint(cs, 8); +``` + +```func +(cs, int x) = cs.load_uint(8); +``` + +```func +int x = cs~load_uint(8); +``` + +In some cases we want to use a function as a modifying method that doesn't return any value and only modifies the first argument. It can be done using unit types as follows: Suppose we want to define function `inc` of type `int -> int`, which increments an integer, and use it as a modifying method. Then we should define `inc` as a function of type `int -> (int, ())`: + +```func +(int, ()) inc(int x) { + return (x + 1, ()); +} +``` + +When defined like that, it can be used as a modifying method. The following will increment `x`. + +```func +x~inc(); +``` + +#### `.` and `~` in function names + +Suppose we want to use `inc` as a non-modifying method too. We can write something like that: + +```func +(int y, _) = inc(x); +``` + +But it is possible to override the definition of `inc` as a modifying method. + +```func +int inc(int x) { + return x + 1; +} +(int, ()) ~inc(int x) { + return (x + 1, ()); +} +``` + +And then call it like that: + +```func +x~inc(); +int y = inc(x); +int z = x.inc(); +``` + +The first call will modify x; the second and third won't. + +In summary, when a function with the name `foo` is called as a non-modifying or modifying method (i.e. with `.foo` or `~foo` syntax), the FunC compiler uses the definition of `.foo` or `~foo` correspondingly if such a definition is presented, and if not, it uses the definition of `foo`. + +### Specifiers + +There are three types of specifiers: `impure`, `inline`/`inline_ref`, and `method_id`. One, several, or none of them can be put in a function declaration but currently they must be presented in the right order. For example, it is not allowed to put `impure` after `inline`. + +#### Impure specifier + +`impure` specifier means that the function can have some side effects which can't be ignored. For example, we should put `impure` specifier if the function can modify contract storage, send messages, or throw an exception when some data is invalid and the function is intended to validate this data. + +If `impure` is not specified and the result of the function call is not used, then the FunC compiler may and will delete this function call. + +For example, in the [stdlib.fc](/develop/func/stdlib) function + +```func +int random() impure asm "RANDU256"; +``` + +is defined. `impure` is used because `RANDU256` changes the internal state of the random number generator. + +#### Inline specifier + +If a function has `inline` specifier, its code is actually substituted in every place where the function is called. It goes without saying that recursive calls to inlined functions are not possible. + +For example, you can using `inline` like this way in this example: [ICO-Minter.fc](https://github.com/ton-blockchain/token-contract/blob/f2253cb0f0e1ae0974d7dc0cef3a62cb6e19f806/ft/jetton-minter-ICO.fc#L16) + +```func +() save_data(int total_supply, slice admin_address, cell content, cell jetton_wallet_code) impure inline { + set_data(begin_cell() + .store_coins(total_supply) + .store_slice(admin_address) + .store_ref(content) + .store_ref(jetton_wallet_code) + .end_cell() + ); +} +``` + +#### Inline_ref specifier + +The code of a function with the `inline_ref` specifier is put into a separate cell, and every time when the function is called, a `CALLREF` command is executed by TVM. So it's similar to `inline`, but because a cell can be reused in several places without duplicating it, it is almost always more efficient in terms of code size to use `inline_ref` specifier instead of `inline` unless the function is called exactly once. Recursive calls of `inline_ref`'ed functions are still impossible because there are no cyclic references in the TVM cells. + +#### method_id + +Every function in TVM program has an internal integer id by which it can be called. Ordinary functions are usually numbered by subsequent integers starting from 1, but get-methods of the contract are numbered by crc16 hashes of their names. `method_id()` specifier allows to set the id of a function to specified value, and `method_id` uses the default value `(crc16() & 0xffff) | 0x10000`. If a function has `method_id` specifier, then it can be called in lite-client or ton-explorer as a get-method by its name. + +For example, + +```func +(int, int) get_n_k() method_id { + (_, int n, int k, _, _, _, _) = unpack_state(); + return (n, k); +} +``` + +is a get-method of multisig contract. + +### Polymorphism with forall + +Before any function declaration or definition, there can be `forall` type variables declarator. It has the following syntax: + +```func +forall -> +``` + +where type variable name can be any [identifier](/develop/func/literals_identifiers#identifiers). Usually, they are named with capital letters. + +For example, + +```func +forall X, Y -> [Y, X] pair_swap([X, Y] pair) { + [X p1, Y p2] = pair; + return [p2, p1]; +} +``` + +is a function that takes a tuple of length exactly 2, but with values of any (single stack entry) types in components, and swaps them with each other. + +`pair_swap([2, 3])` will produce `[3, 2]` and `pair_swap([1, [2, 3, 4]])` will produce `[[2, 3, 4], 1]`. + +In this example `X` and `Y` are [type variables](/develop/func/types#polymorphism-with-type-variables). When the function is called, type variables are substituted with actual types, and the code of the function is executed. Note that although the function is polymorphic, the actual assembler code for it is the same for every type substitution. It is achieved essentially by the polymorphism of stack manipulation primitives. Currently, other forms of polymorphism (like ad-hoc polymorphism with type classes) are not supported. + +Also, it is worth noticing that the type width of `X` and `Y` is supposed to be equal to 1; that is, the values of `X` or `Y` must occupy a single stack entry. So you actually can't call the function `pair_swap` on a tuple of type `[(int, int), int]`, because type `(int, int)` has width 2, i.e., it occupies 2 stack entries. + +## Assembler function body definition + +As mentioned above, a function can be defined by the assembler code. The syntax is an `asm` keyword followed by one or several assembler commands, represented as strings. +For example, one can define: + +```func +int inc_then_negate(int x) asm "INC" "NEGATE"; +``` + +– a function that increments an integer and then negates it. Calls to this function will be translated to 2 assembler commands `INC` and `NEGATE`. Alternative way to define the function is: + +```func +int inc_then_negate'(int x) asm "INC NEGATE"; +``` + +`INC NEGATE` will be considered by FunC as one assembler command, but it is OK, because Fift assembler knows that it is 2 separate commands. + +:::info +The list of assembler commands can be found here: [TVM instructions](/learn/tvm-instructions/instructions). +::: + +### Rearranging stack entries + +In some cases, we want to pass arguments to the assembler function in a different order than the assembler command requires, or/and take the result in a different stack entry order than the command returns. We could manually rearrange the stack by adding corresponding stack primitives, but FunC can do it automatically. + +:::info +Note, that in case of manual rearranging, arguments will be computed in the rearranged order. To overwrite this behavior use `#pragma compute-asm-ltr`: [compute-asm-ltr](compiler_directives#pragma-compute-asm-ltr) +::: + +For example, suppose that the assembler command STUXQ takes an integer, builder, and integer; then it returns the builder, along with the integer flag, indicating the success or failure of the operation. +We may define the function: + +```func +(builder, int) store_uint_quite(int x, builder b, int len) asm "STUXQ"; +``` + +However, suppose we want to rearrange arguments. Then we can define: + +```func +(builder, int) store_uint_quite(builder b, int x, int len) asm(x b len) "STUXQ"; +``` + +So you can indicate the required order of arguments after the `asm` keyword. + +Also, we can rearrange return values like this: + +```func +(int, builder) store_uint_quite(int x, builder b, int len) asm( -> 1 0) "STUXQ"; +``` + +The numbers correspond to the indexes of returned values (0 is the deepest stack entry among returned values). + +Combining this techniques is also possible. + +```func +(int, builder) store_uint_quite(builder b, int x, int len) asm(x b len -> 1 0) "STUXQ"; +``` + +### Multiline asms + +Multiline assembler command or even Fift-code snippets can be defined via multiline strings which starts and ends with `"""`. + +```func +slice hello_world() asm """ + "Hello" + " " + "World" + $+ $+ $>s + PUSHSLICE +"""; +``` From a849c4adde57096db09c6c42ef3d7be7f322591a Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:27 +0800 Subject: [PATCH 099/219] New translations global_variables.md (Chinese Simplified) --- .../current/develop/func/global_variables.md | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/func/global_variables.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/global_variables.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/global_variables.md new file mode 100644 index 0000000000..433f583672 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/global_variables.md @@ -0,0 +1,73 @@ +# Global variables + +The FunC program is essentially a list of function declarations/definitions and global variable declarations. This section covers the second topic. + +A global variable can be declared with the `global` keyword followed by the variable type and the variable name. For example, + +```func +global ((int, int) -> int) op; + +int check_assoc(int a, int b, int c) { + return op(op(a, b), c) == op(a, op(b, c)); +} + +int main() { + op = _+_; + return check_assoc(2, 3, 9); +} +``` + +is a simple program that writes to a global functional variable `op` the addition operator `_+_` and checks the associativity of addition on three sample integers; 2, 3, and 9. + +Internally, global variables are stored in the c7 control register of TVM. + +The type of a global variable can be omitted. If so, it will be inferred from the usage of the variable. For example, we can rewrite the program as: + +```func +global op; + +int check_assoc(int a, int b, int c) { + return op(op(a, b), c) == op(a, op(b, c)); +} + +int main() { + op = _+_; + return check_assoc(2, 3, 9); +} +``` + +It is possible to declare several variables after the same `global` keyword. The following codes are equivalent: + +```func +global int A; +global cell B; +global C; +``` + +```func +global int A, cell B, C; +``` + +It is not allowed to declare a local variable with the same name as an already-declared global variable. For example, this code wouldn't compile: + +```func +global cell C; + +int main() { + int C = 3; + return C; +} +``` + +Note that the following code is correct: + +```func +global int C; + +int main() { + int C = 3; + return C; +} +``` + +but here `int C = 3;` is equivalent to `C = 3;`, i.e., that is an assignment to global variable `C`, not a declaration of local variable `C` (you can find an explanation of this effect in [statements](/develop/func/statements#variable-declaration)). From 693c392afa67993943856702925e957d98cb79f7 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:27 +0800 Subject: [PATCH 100/219] New translations literals_identifiers.md (Chinese Simplified) --- .../develop/func/literals_identifiers.md | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/func/literals_identifiers.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/literals_identifiers.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/literals_identifiers.md new file mode 100644 index 0000000000..8911b0f8b4 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/literals_identifiers.md @@ -0,0 +1,104 @@ +# Literals and Identifiers + +## Number literals + +FunC allows decimal and hexadecimal integer literals (leading zeros are allowed). + +For example, `0`, `123`, `-17`, `00987`, `0xef`, `0xEF`, `0x0`, `-0xfFAb`, `0x0001`, `-0`, `-0x0` are valid number literals. + +## String literals + +Strings in FunC are quoted in double quotes `"` like `"this is a string"`. Special symbols like `\n` and multi-line strings are not supported. +Optionally, string literals may specify a type after them, such as `"string"u`. + +The following string types are supported: + +- without type—used for asm function definitions and to define a slice const by ASCII string +- `s`—defines a raw slice const by its contents (hex-encoded and optionally bit-padded) +- `a`—creates a slice const containing `MsgAddressInt` structure from a specified address +- `u`—creates an int const that corresponds to the hex values of the provided ASCII string +- `h`—creates an int const that is the first 32 bits of the SHA256 hash of the string +- `H`—creates an int const that is all 256 bits of the SHA256 hash of the string +- `c`—creates an int const that is crc32 value of the string + +For example, the following values result in the corresponding consts: + +- `"string"` becomes `x{737472696e67}` slice const +- `"abcdef"s` becomes `x{abcdef}` slice const +- `"Ef8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM0vF"a` becomes `x{9FE6666666666666666666666666666666666666666666666666666666666666667_}` slice const (`addr_std$10 anycast:none$0 workchain_id:int8=0xFF address:bits256=0x33...33`) +- `"NstK"u` becomes `0x4e73744b` int const +- `"transfer(slice, int)"h` becomes `0x7a62e8a8` int const +- `"transfer(slice, int)"H` becomes `0x7a62e8a8ebac41bd6de16c65e7be363bc2d2cbc6a0873778dead4795c13db979` int const +- `"transfer(slice, int)"c` becomes `2235694568` int const + +## Identifiers + +FunC allows a really wide class of identifiers (functions and variable names). Namely, any (single-line) string which doesn't contain special symbols `;`, `,`, `(`, `)`, ` ` (space or tab), `~` and `.`, doesn't start as a comment or string literal (with `"`), isn't a number literal, isn't an underscore `_` and isn't a keyword is a valid identifier (with the only exception that if it starts with `` ` ``, it must end with the same `` ` `` and can't contain any other `` ` `` except for these two). + +Also, function names in function definitions may start with `.` or `~`. + +For example, those are valid identifiers: + +- `query`, `query'`, `query''` +- `elem0`, `elem1`, `elem2` +- `CHECK` +- `_internal_value` +- `message_found?` +- `get_pubkeys&signatures` +- `dict::udict_set_builder` +- `_+_` (the standard addition operator of type `(int, int) -> int` in prefix notation, although it is already defined) +- `fatal!` + +`'` at the end of the name of a variable is conventionally used when some modified version of the old value is introduced. For example, almost all modifying built-in primitives for hashmap manipulation (except ones with the prefix `~`) take a hashmap and return a new version of the hashmap along with some other data, if necessary. It is convenient to name those values with the same name suffixed by `'`. + +Suffix `?` is usually used for boolean variables (TVM doesn't have a built-in type bool; bools are represented by integers: 0 is false and -1 is true) or for functions that return some flag, usually indicating success of the operation (like `udict_get?` from [stdlib.fc](/develop/func/stdlib)). + +These are invalid identifiers: + +- `take(first)Entry` +- `"not_a_string` +- `msg.sender` +- `send_message,then_terminate` +- `_` + +Some more uncommon examples of valid identifiers: + +- `123validname` +- `2+2=2*2` +- `-alsovalidname` +- `0xefefefhahaha` +- `{hehehe}` +- ``pa{--}in"`aaa`"`` + +These are also invalid identifiers: + +- ``pa;;in"`aaa`"`` (because `;` is prohibited) +- `{-aaa-}` +- `aa(bb` +- `123` (it's a number) + +Also, FunC has a special type of identifiers which is quoted in back quotes `` ` ``. +In the quotes, any symbols are allowed except for `\n` and the quotes themselves. + +For example, `` `I'm a variable too` `` is a valid identifier, as well as `` `any symbols ; ~ () are allowed here...` `` + +## Constants + +FunC allows to define compile-time constants that are substituted and precalculated during compilation. + +Constants are defined as `const optional-type identifier = value-or-expression;` + +`optional-type` can be used to force a specific type of constant and for better readability. + +As of now, `int` and `slice` types are supported. + +`value-or-expression` can be a literal or a pre-computable expression of literals and constants. + +For example, constants can be defined as follows: + +- `const int101 = 101;` defines `int101` constant that is equivalent to the numeric literal `101` +- `const str1 = "const1", str2 = "aabbcc"s;` defines two constants that are equal to their corresponding strings +- `const int int240 = ((int1 + int2) * 10) << 3;` defines `int240` constant that equals the result of calculation +- `const slice str2r = str2;` defines `str2r` constant that is equal to the value of `str2` constant + +Since numeric constants are substituted during compilation, all optimization and pre-computations performed during the compilation are successfully performed (unlike the old method of defining constants via inline asm `PUSHINT`s). From 73266197fea3543522438613509e6eec068c9464 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:28 +0800 Subject: [PATCH 101/219] New translations overview.mdx (Chinese Simplified) --- .../current/develop/func/overview.mdx | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/func/overview.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/overview.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/overview.mdx new file mode 100644 index 0000000000..b31cac93e6 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/overview.mdx @@ -0,0 +1,109 @@ +import Button from '@site/src/components/button' + +# Overview + +A high-level language FunC is used to program smart contracts on TON. + +FunC is a domain-specific, C-like, statically typed language. +Here is a simple example method for sending money written in FunC: + +```func +() send_money(slice address, int amount) impure inline { + var msg = begin_cell() + .store_uint(0x10, 6) ;; nobounce + .store_slice(address) + .store_coins(amount) + .end_cell(); + + send_raw_message(msg, 64); +} +``` + +FunC programs are compiled into Fift assembler code, which generates corresponding bytecode for the [TON Virtual Machine](/learn/tvm-instructions/tvm-overview). + +Further this bytecode (actually a [tree of cells](/learn/overviews/cells), like any other data in TON Blockchain) can be used for creating smart contracts in the blockchain or can be run on a local instance of TVM. + + + + +## Compiler + +### Compile with JS + +Most convenient and quick way to begin develop and compile smart contracts is using Blueprint framework. Read more in [Blueprint](/develop/smart-contracts/sdk/javascript) section. + +```bash +npm create ton@latest +``` + +### Compile with original binaries + +If you want to use native TON compiler FunC locally you need binaries setup on your machine. FunC compiler binaries for Windows, MacOS (Intel/M1), and Ubuntu can be downloaded from: + +- [Environment Setup Page](/develop/smart-contracts/environment/installation) + +:::info +At the same time you can always make binaries from sources like:\ +[FunC compiler source code](https://github.com/ton-blockchain/ton/tree/master/crypto/func) (read [how to compile](/develop/howto/compile#func) a FunC compiler from sources). +::: + +## TON Course: FunC + +The [TON Blockchain Course](https://stepik.org/course/176754/) is a comprehensive guide to TON Blockchain development. + +Module 4 completely covers FunC language and smart contracts development. + + + +## Tutorials + +:::tip starter tip +The best place to start to develop using FunC: [INTRODUCTION](/develop/smart-contracts/) +::: + +Other materials gracefully provided by the experts from the community: + +- [TON Speed Run series](https://tonspeedrun.com/) + - [🚩 Challenge 1: Simple NFT Deploy](https://github.com/romanovichim/TONQuest1) + - [🚩 Challenge 2: Chatbot Contract](https://github.com/romanovichim/TONQuest2) + - [🚩 Challenge 3: Jetton Vending Machine](https://github.com/romanovichim/TONQuest3) + - [🚩 Challenge 4: Lottery/Raffle](https://github.com/romanovichim/TONQuest4) + - [🚩 Challenge 5: Create UI to interact with the contract in 5 minutes](https://github.com/romanovichim/TONQuest5) + - [🚩 Challenge 6: Analyzing NFT sales on the Getgems marketplace](https://github.com/romanovichim/TONQuest6) + + + +- [Func & Blueprint](https://www.youtube.com/watch?v=7omBDfSqGfA&list=PLtUBO1QNEKwtO_zSyLj-axPzc9O9rkmYa) by **@MarcoDaTr0p0je** +- [Learn FunC in Y Minutes](https://learnxinyminutes.com/docs/func/) by **@romanovichim** +- [TON Hello World: Step-by-step guide for writing your first smart contract](https://ton-community.github.io/tutorials/02-contract/) +- [TON Hello World: Step by step guide for testing your first smart contract](https://ton-community.github.io/tutorials/04-testing/) +- [10 FunC Lessons](https://github.com/romanovichim/TonFunClessons_Eng) by **@romanovichim**, using blueprint +- [10 FunC lessons (RU)](https://github.com/romanovichim/TonFunClessons_ru) by **@romanovichim**, using blueprint +- [FunC Quiz](https://t.me/toncontests/60) by **Vadim**—Good for selfcheck. It will take 10–15 minutes. The questions are mainly about FunС with a few general questions about TON +- [FunC Quiz (RU)](https://t.me/toncontests/58?comment=14888) by **Vadim**—FunC Quiz in Russian + +## Contests + +Participating in [contests](https://t.me/toncontests) is a great way to learn FunC. + +You can also study previous competitions for learning purposes. + +#### Contests Legacy + +| Contest Descr | Tasks | Solutions | +| ------------------------------------------- | -------------------------------------------------------- | ---------------------------------------------------------------------- | +| TSC #5 (December, 2023) | [Tasks](https://github.com/ton-community/tsc5) | | +| TSC #4 (September, 2023) | [Tasks](https://github.com/ton-community/tsc4) | [Solutions](/develop/smart-contracts/examples#ton-smart-challenge-4) | +| TSC #3 (December, 2022) | [Tasks](https://github.com/ton-blockchain/func-contest3) | [Solutions](https://github.com/nns2009/TON-FunC-contest-3) | +| TSC #2 (July, 2022) | [Tasks](https://github.com/ton-blockchain/func-contest2) | [Solutions](https://github.com/ton-blockchain/func-contest2-solutions) | +| TSC #1 (March, 2022) | [Tasks](https://github.com/ton-blockchain/func-contest1) | [Solutions](https://github.com/ton-blockchain/func-contest1-solutions) | + +## Smart contract examples + +Standard basic smart contracts like wallets, electors (manages validation on TON), multi-signature wallets, etc. can be a reference when studying. + +- [Smart contract examples](/develop/smart-contracts/examples) + +## Changelog + +[History of funC updates](/develop/func/changelog). From 5197b69573538eeb7d3e9ac8889434e191b17d3f Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:29 +0800 Subject: [PATCH 102/219] New translations statements.md (Chinese Simplified) --- .../current/develop/func/statements.md | 437 ++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/func/statements.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/statements.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/statements.md new file mode 100644 index 0000000000..6a8fa9bf85 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/statements.md @@ -0,0 +1,437 @@ +# Statements + +This section briefly discusses FunC statements, constituting the code of ordinary function bodies. + +## Expression statements + +The most common type of a statement is the expression statement. It's an expression followed by `;`. Expression's description would be quite complicated, so only a sketch is presented here. As a rule all sub-expressions are computed left to right with one exception of [asm stack rearrangement](functions#rearranging-stack-entries) which may define order manually. + +### Variable declaration + +It is not possible to declare a local variable without defining its initial value. + +Here are some examples of variables declarations: + +```func +int x = 2; +var x = 2; +(int, int) p = (1, 2); +(int, var) p = (1, 2); +(int, int, int) (x, y, z) = (1, 2, 3); +(int x, int y, int z) = (1, 2, 3); +var (x, y, z) = (1, 2, 3); +(int x = 1, int y = 2, int z = 3); +[int, int, int] [x, y, z] = [1, 2, 3]; +[int x, int y, int z] = [1, 2, 3]; +var [x, y, z] = [1, 2, 3]; +``` + +Variable can be "redeclared" in the same scope. For example, this is a correct code: + +```func +int x = 2; +int y = x + 1; +int x = 3; +``` + +In fact, the second occurrence of `int x` is not a declaration, but just a compile-time insurance that `x` has type `int`. So the third line is essentially equivalent to a simple assignment `x = 3;`. + +In nested scopes, a variable can be truly redeclared just like in the C language. For example, consider the code: + +```func +int x = 0; +int i = 0; +while (i < 10) { + (int, int) x = (i, i + 1); + ;; here x is a variable of type (int, int) + i += 1; +} +;; here x is a (different) variable of type int +``` + +But as mentioned in the global variables [section](/develop/func/global_variables.md), a global variable cannot be redeclared. + +Note that a variable declaration **is** an expression statement, so actually constructions like `int x = 2` are full-fledged expressions. For example, this is a correct code: + +```func +int y = (int x = 3) + 1; +``` + +It is a declaration of two variables `x` and `y` equal to `3` and `4` correspondingly. + +#### Underscore + +Underscore `_` is used when a value is not needed. For example, suppose a function `foo` has type `int -> (int, int, int)`. We can get the first returned value and ignore the second and third like this: + +```func +(int fst, _, _) = foo(42); +``` + +### Function application + +A call of a function looks like as such in a conventional language. The arguments of the function call are listed after the function name, separated by commas. + +```func +;; suppose foo has type (int, int, int) -> int +int x = foo(1, 2, 3); +``` + +But notice that `foo` is actually a function of **one** argument of type `(int, int, int)`. To see the difference, suppose `bar` is a function of type `int -> (int, int, int)`. Unlike in conventional languages, you can compose the functions like that: + +```func +int x = foo(bar(42)); +``` + +instead of the similar but longer form: + +```func +(int a, int b, int c) = bar(42); +int x = foo(a, b, c); +``` + +Also Haskell-style calls are possible, but not always (to be fixed later): + +```func +;; suppose foo has type int -> int -> int -> int +;; i.e. it's carried +(int a, int b, int c) = (1, 2, 3); +int x = foo a b c; ;; ok +;; int y = foo 1 2 3; wouldn't compile +int y = foo (1) (2) (3); ;; ok +``` + +### Lambda expressions + +Lambda expressions are not supported yet. + +### Methods calls + +#### Non-modifying methods + +If a function has at least one argument, it can be called as a non-modifying method. For example, `store_uint` has type `(builder, int, int) -> builder` (the second argument is the value to store, and the third is the bit length). `begin_cell` is a function that creates a new builder. The following codes are equivalent: + +```func +builder b = begin_cell(); +b = store_uint(b, 239, 8); +``` + +```func +builder b = begin_cell(); +b = b.store_uint(239, 8); +``` + +So the first argument of a function can be passed to it being located before the function name, if separated by `.`. The code can be further simplified: + +```func +builder b = begin_cell().store_uint(239, 8); +``` + +Multiple calls of methods are also possible: + +```func +builder b = begin_cell().store_uint(239, 8) + .store_int(-1, 16) + .store_uint(0xff, 10); +``` + +#### Modifying methods + +If the first argument of a function has type `A` and the return value of the function has the shape of `(A, B)` where `B` is some arbitrary type, then the function can be called as a modifying method. Modifying method calls may take some arguments and return some values, but they modify their first argument, that is, assign the first component of the returned value to the variable from the first argument. For example, suppose `cs` is a cell slice and `load_uint` has type `(slice, int) -> (slice, int)`: it takes a cell slice and number of bits to load and returns the remainder of the slice and the loaded value. The following codes are equivalent: + +```func +(cs, int x) = load_uint(cs, 8); +``` + +```func +(cs, int x) = cs.load_uint(8); +``` + +```func +int x = cs~load_uint(8); +``` + +In some cases we want to use a function as a modifying method that doesn't return any value and only modifies the first argument. It can be done using unit types as follows: Suppose we want to define function `inc` of type `int -> int`, which increments an integer, and use it as a modifying method. Then we should define `inc` as a function of type `int -> (int, ())`: + +```func +(int, ()) inc(int x) { + return (x + 1, ()); +} +``` + +When defined like that, it can be used as a modifying method. The following will increment `x`. + +```func +x~inc(); +``` + +#### `.` and `~` in function names + +Suppose we want to use `inc` as a non-modifying method too. We can write something like that: + +```func +(int y, _) = inc(x); +``` + +But it is possible to override the definition of `inc` as a modifying method. + +```func +int inc(int x) { + return x + 1; +} +(int, ()) ~inc(int x) { + return (x + 1, ()); +} +``` + +And then call it like that: + +```func +x~inc(); +int y = inc(x); +int z = x.inc(); +``` + +The first call will modify x; the second and third won't. + +In summary, when a function with the name `foo` is called as a non-modifying or modifying method (i.e. with `.foo` or `~foo` syntax), the FunC compiler uses the definition of `.foo` or `~foo` correspondingly if such a definition is presented, and if not, it uses the definition of `foo`. + +### Operators + +Note that currently all of the unary and binary operators are integer operators. Logical operators are represented as bitwise integer operators (cf. [absence of boolean type](/develop/func/types#absence-of-boolean-type)). + +#### Unary operators + +There are two unary operators: + +- `~` is bitwise not (priority 75) +- `-` is integer negation (priority 20) + +They should be separated from the argument: + +- `- x` is ok. +- `-x` is not ok (it's a single identifier) + +#### Binary operators + +With priority 30 (left-associative): + +- `*` is integer multiplication +- `/` is integer division (floor) +- `~/` is integer division (round) +- `^/` is integer division (ceil) +- `%` is integer reduction by modulo (floor) +- `~%` is integer reduction by modulo (round) +- `^%` is integer reduction by modulo (ceil) +- `/%` returns the quotient and the remainder +- `&` is bitwise AND + +With priority 20 (left-associative): + +- `+` is integer addition +- `-` is integer subtraction +- `|` is bitwise OR +- `^` is bitwise XOR + +With priority 17 (left-associative): + +- `<<` is bitwise left shift +- `>>` is bitwise right shift +- `~>>` is bitwise right shift (round) +- `^>>` is bitwise right shift (ceil) + +With priority 15 (left-associative): + +- `==` is integer equality check +- `!=` is integer inequality check +- `<` is integer comparison +- `<=` is integer comparison +- `>` is integer comparison +- `>=` is integer comparison +- `<=>` is integer comparison (returns -1, 0 or 1) + +They also should be separated from the argument: + +- `x + y` is ok +- `x+y` is not ok (it's a single identifier) + +#### Conditional operator + +It has the usual syntax. + +```func + ? : +``` + +For example: + +```func +x > 0 ? x * fac(x - 1) : 1; +``` + +It has priority 13. + +#### Assignments + +Priority 10. + +Simple assignment `=` and counterparts of the binary operations: `+=`, `-=`, `*=`, `/=`, `~/=`, `^/=`, `%=`, `~%=`, `^%=`, `<<=`, `>>=`, `~>>=`, `^>>=`, `&=`, `|=`, `^=`. + +## Loops + +FunC supports `repeat`, `while`, and `do { ... } until` loops. The `for` loop is not supported. + +### Repeat loop + +The syntax is a `repeat` keyword followed by an expression of type `int`. Repeats the code for the specified number of times. Examples: + +```func +int x = 1; +repeat(10) { + x *= 2; +} +;; x = 1024 +``` + +```func +int x = 1, y = 10; +repeat(y + 6) { + x *= 2; +} +;; x = 65536 +``` + +```func +int x = 1; +repeat(-1) { + x *= 2; +} +;; x = 1 +``` + +If the number of times is less than `-2^31` or greater than `2^31 - 1`, range check exception is thrown. + +### While loop + +Has the usual syntax. Example: + +```func +int x = 2; +while (x < 100) { + x = x * x; +} +;; x = 256 +``` + +Note that the truth value of condition `x < 100` is of type `int` (cf. [absence of boolean type](/develop/func/types#absence-of-boolean-type)). + +### Until loop + +Has the following syntax: + +```func +int x = 0; +do { + x += 3; +} until (x % 17 == 0); +;; x = 51 +``` + +## If statements + +Examples: + +```func +;; usual if +if (flag) { + do_something(); +} +``` + +```func +;; equivalent to if (~ flag) +ifnot (flag) { + do_something(); +} +``` + +```func +;; usual if-else +if (flag) { + do_something(); +} +else { + do_alternative(); +} +``` + +```func +;; Some specific features +if (flag1) { + do_something1(); +} else { + do_alternative4(); +} +``` + +The curly brackets are necessary. That code wouldn't be compiled: + +```func +if (flag1) + do_something(); +``` + +## Try-Catch statements + +_Available in func since v0.4.0_ + +Executes the code in `try` block. If it fails, completely rolls back changes made in `try` block and executes `catch` block instead; `catch` receives two arguments: the exception parameter of any type (`x`) and the error code (`n`, integer). + +Unlike many other languages in the FunC try-catch statement, the changes made in the try block, in particular the modification of local and global variables, all registers' changes (i.e. `c4` storage register, `c5` action/message register, `c7` context register and others) **are discarded** if there is an error in the try block and consequently all contract storage updates and message sending will be reverted. It is important to note that some TVM state parameters such as _codepage_ and gas counters will not be rolled back. This means, in particular, that all gas spent in the try block will be taken into account, and the effects of OPs that change the gas limit (`accept_message` and `set_gas_limit`) will be preserved. + +Note that exception parameter can be of any type (possibly different in case of different exceptions) and thus funC can not predict it on compile time. That means that developer need to "help" compiler by casting exception parameter to some type (see Example 2 below): + +Examples: + +```func +try { + do_something(); +} catch (x, n) { + handle_exception(); +} +``` + +```func +forall X -> int cast_to_int(X x) asm "NOP"; +... +try { + throw_arg(-1, 100); +} catch (x, n) { + x.cast_to_int(); + ;; x = -1, n = 100 + return x + 1; +} +``` + +```func +int x = 0; +try { + x += 1; + throw(100); +} catch (_, _) { +} +;; x = 0 (not 1) +``` + +## Block statements + +Block statements are also allowed. They open a new nested scope: + +```func +int x = 1; +builder b = begin_cell(); +{ + builder x = begin_cell().store_uint(0, 8); + b = x; +} +x += 1; +``` From 15f0615d4318426fcb006b007f355f13fbba940f Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:30 +0800 Subject: [PATCH 103/219] New translations stdlib.mdx (Chinese Simplified) --- .../current/develop/func/stdlib.mdx | 1345 +++++++++++++++++ 1 file changed, 1345 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/func/stdlib.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/stdlib.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/stdlib.mdx new file mode 100644 index 0000000000..648f9314e2 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/stdlib.mdx @@ -0,0 +1,1345 @@ +--- +toc_min_heading_level: 2 +toc_max_heading_level: 6 +--- + +# FunC standard library + +:::info +This section discusses the [stdlib.fc](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/stdlib.fc) library with standard functions used in FunC. +::: + +Currently, the library is just a wrapper for the most common assembler of the TVM commands which are not built-in. Each TVM command description used in the library can be found in the [TVM documentation](/learn/tvm-instructions/tvm-overview) section. Some descriptions were borrowed for this document. + +Some functions are commented out in the file. It means that they have already become built-ins for optimization purposes. However, the type signature and semantics remain the same. + +Note that some less common commands are not presented in the stdlib. Someday they will also be added. + +## Tuple manipulation primitives + +The names and the types are mostly self-explaining. See [polymorhism with forall](/develop/func/functions#polymorphism-with-forall) for more info on the polymorphic functions. + +Note that currently values of atomic type `tuple` cannot be cast into composite tuple types (e.g. `[int, cell]`) and vise versa. + +### Lisp-style lists + +Lists can be represented as nested 2-element tuples. Empty list is conventionally represented as TVM `null` value (it can be obtained by calling `null()`). For example, the tuple `(1, (2, (3, null)))` represents the list `[1, 2, 3]`. Elements of a list can be of different types. + +#### cons + +```func +forall X -> tuple cons(X head, tuple tail) asm "CONS"; +``` + +Adds an element to the beginning of a lisp-style list. + +#### uncons + +```func +forall X -> (X, tuple) uncons(tuple list) asm "UNCONS"; +``` + +Extracts the head and the tail of lisp-style list. + +#### list_next + +```func +forall X -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS"; +``` + +Extracts the head and tail of a lisp-style list. Can be used as a [(non-)modifying method](/develop/func/statements#methods-calls). + +```func +() foo(tuple xs) { + (_, int x) = xs.list_next(); ;; get the first element, `_` means do not use tail list + int y = xs~list_next(); ;; pop the first element + int z = xs~list_next(); ;; pop the second element +} +``` + +#### car + +```func +forall X -> X car(tuple list) asm "CAR"; +``` + +Returns the head of a lisp-style list. + +#### cdr + +```func +tuple cdr(tuple list) asm "CDR"; +``` + +Returns the tail of a lisp-style list. + +### Other tuple primitives + +#### empty_tuple + +```func +tuple empty_tuple() asm "NIL"; +``` + +Creates 0-element tuple. + +#### tpush + +```func +forall X -> tuple tpush(tuple t, X value) asm "TPUSH"; +forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH"; +``` + +Appends the value `x` to the `Tuple t = (x1, ..., xn)` but only if the resulting `Tuple t' = (x1, ..., xn, x)` is no longer than 255 characters. Otherwise, a type check exception is thrown. + +#### single + +```func +forall X -> [X] single(X x) asm "SINGLE"; +``` + +Creates a singleton, i.e., a tuple of length one. + +#### unsingle + +```func +forall X -> X unsingle([X] t) asm "UNSINGLE"; +``` + +Unpacks a singleton. + +#### pair + +```func +forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR"; +``` + +Creates a pair. + +#### unpair + +```func +forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR"; +``` + +Unpacks a pair. + +#### triple + +```func +forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE"; +``` + +Creates a triple. + +#### untriple + +```func +forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) asm "UNTRIPLE"; +``` + +Unpacks a triple. + +#### tuple4 + +```func +forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) asm "4 TUPLE"; +``` + +Creates 4-element tuple. + +#### untuple4 + +```func +forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) asm "4 UNTUPLE"; +``` + +Unpacks 4-element tuple. + +#### first + +```func +forall X -> X first(tuple t) asm "FIRST"; +``` + +Returns the first element of a tuple. + +#### second + +```func +forall X -> X second(tuple t) asm "SECOND"; +``` + +Returns the second element of a tuple. + +#### third + +```func +forall X -> X third(tuple t) asm "THIRD"; +``` + +Returns the third element of a tuple. + +#### fourth + +```func +forall X -> X fourth(tuple t) asm "3 INDEX"; +``` + +Returns the fourth element of a tuple. + +#### pair_first + +```func +forall X, Y -> X pair_first([X, Y] p) asm "FIRST"; +``` + +Returns the first element of a pair. + +#### pair_second + +```func +forall X, Y -> Y pair_second([X, Y] p) asm "SECOND"; +``` + +Returns the second element of a pair. + +#### triple_first + +```func +forall X, Y, Z -> X triple_first([X, Y, Z] p) asm "FIRST"; +``` + +Returns the first element of a triple. + +#### triple_second + +```func +forall X, Y, Z -> Y triple_second([X, Y, Z] p) asm "SECOND"; +``` + +Returns the second element of a triple. + +#### triple_third + +```func +forall X, Y, Z -> Z triple_third([X, Y, Z] p) asm "THIRD"; +``` + +Returns the third element of a triple. + +## Domain specific primitives + +### Extracting info from c7 + +Some useful information regarding smart contract invocation can be found in the [c7 special register](/learn/tvm-instructions/tvm-overview#control-registers). These primitives serve for convenient data extraction. + +#### now + +```func +int now() asm "NOW"; +``` + +Returns the current Unix time as an Integer + +#### my_address + +```func +slice my_address() asm "MYADDR"; +``` + +Returns the internal address of the current smart contract as a Slice with `MsgAddressInt`. If necessary, it can be parsed further using primitives such as `parse_std_addr`. + +#### get_balance + +```func +[int, cell] get_balance() asm "BALANCE"; +``` + +Returns the remaining balance of the smart contract as `tuple` consisting of `int` (the remaining balance in nanotoncoins) and `cell` (a dictionary with 32-bit keys representing the balance of “extra currencies”). Note that RAW primitives such as `send_raw_message` do not update this field. + +#### cur_lt + +```func +int cur_lt() asm "LTIME"; +``` + +Returns the logical time of the current transaction. + +#### block_lt + +```func +int block_lt() asm "BLOCKLT"; +``` + +Returns the starting logical time of the current +block. + +#### config_param + +```func +cell config_param(int x) asm "CONFIGOPTPARAM"; +``` + +Returns the value of the global configuration parameter with integer index `i` as `cell` or `null` value. + +### Hashes + +#### cell_hash + +```func +int cell_hash(cell c) asm "HASHCU"; +``` + +Computes the representation hash of `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. + +#### slice_hash + +```func +int slice_hash(slice s) asm "HASHSU"; +``` + +Computes the hash of `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 `cell_hash`. + +#### string_hash + +```func +int string_hash(slice s) asm "SHA256U"; +``` + +Computes sha256 of the data bits of `slice s`. If the bit length of `s` is not divisible by eight, it throws a cell underflow exception. The hash value is returned as a 256-bit unsigned integer `x`. + +### Signature checks + +#### check_signature + +```func +int check_signature(int hash, slice signature, int public_key) asm "CHKSIGNU"; +``` + +Checks the Ed25519 `signature` of `hash` (a 256-bit unsigned integer, usually computed as the hash of some data) using `public_key` (also represented by a 256-bit unsigned integer). The signature must contain at least 512 data bits; only the first 512 bits are used. If the signature is valid, the result is `-1`; otherwise, it is `0`. Note that `CHKSIGNU` creates a 256-bit slice with the hash and calls `CHKSIGNS`. That is, if `hash` is computed as the hash of some data, this data is hashed _twice_, the second hashing occurring inside `CHKSIGNS`. + +#### check_data_signature + +```func +int check_data_signature(slice data, slice signature, int public_key) asm "CHKSIGNS"; +``` + +Checks whether `signature` is a valid Ed25519 signature of the data portion of `slice data` using `public_key`, similarly to `check_signature`. If the bit length of `data` is not divisible by eight, it throws a cell underflow exception. The verification of Ed25519 signatures is a standard one, with sha256 used to reduce `data` to the 256-bit number that is actually signed. + +### Computation of boc size + +The primitives below may be useful for computing storage fees for user-provided data. + +#### compute_data_size? + +```func +(int, int, int, int) compute_data_size?(cell c, int max_cells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; +``` + +Returns `(x, y, z, -1)` or `(null, null, null, 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 `max_cells`; otherwise, the computation is aborted before visiting the `(max_cells + 1)`-st cell and a zero flag is returned to indicate failure. If `c` is `null`, it returns `x = y = z = 0`. + +#### slice_compute_data_size? + +```func +(int, int, int, int) slice_compute_data_size?(slice s, int max_cells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; +``` + +Similar to `compute_data_size?` but accepting `slice s` instead of `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`. + +#### compute_data_size + +```func +(int, int, int) compute_data_size(cell c, int max_cells) impure asm "CDATASIZE"; +``` + +A non-quiet version of `compute_data_size?` that throws a cell overflow exception (8) on failure. + +#### slice_compute_data_size + +```func +(int, int, int) slice_compute_data_size(slice s, int max_cells) impure asm "SDATASIZE"; +``` + +A non-quiet version of `slice_compute_data_size?` that throws a cell overflow exception (8) on failure. + +### Persistent storage save and load + +#### get_data + +```func +cell get_data() asm "c4 PUSH"; +``` + +Returns the persistent contract storage cell. It can be parsed or modified with slice and builder primitives later. + +#### set_data + +```func +() set_data(cell c) impure asm "c4 POP"; +``` + +Sets cell `c` as persistent contract data. You can update the persistent contract storage with this primitive. + +### Continuation primitives + +#### get_c3 + +```func +cont get_c3() impure asm "c3 PUSH"; +``` + +Usually `c3` has a continuation initialized by the whole code of the contract. It is used for function calls. The primitive returns the current value of `c3`. + +#### set_c3 + +```func +() set_c3(cont c) impure asm "c3 POP"; +``` + +Updates the current value of `c3`. Usually, it is used for updating smart contract code in runtime. Note that after execution of this primitive, the current code (and the stack of recursive function calls) won't change, but any other function call will use a function from the new code. + +#### bless + +```func +cont bless(slice s) impure asm "BLESS"; +``` + +Transforms `slice s` into a simple ordinary continuation `c` with `c.code = s`, and an empty stack, and savelist. + +### Gas related primitives + +#### accept_message + +```func +() accept_message() impure asm "ACCEPT"; +``` + +Sets the current gas limit `gl` to its maximum allowed value `gm` and resets the gas credit `gc` to zero, decreasing the value of `gr` by `gc` 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 that carry no value (hence no gas). + +For more details check [accept_message effects](/develop/smart-contracts/guidelines/accept) + +#### set_gas_limit + +```func +() set_gas_limit(int limit) impure asm "SETGASLIMIT"; +``` + +Sets the current gas limit `gl` to the minimum of `limit` and `gm`, and resets the gas credit `gc` to zero. At that point, if the amount of consumed gas (including the present instruction) exceeds the resulting value of `gl`, an (unhandled) out of gas exception is thrown before setting new gas limits. Notice that `set_gas_limit` with an argument `limit ≥ 2^63 − 1` is equivalent to `accept_message`. + +For more details check [accept_message effects](/develop/smart-contracts/guidelines/accept) + +#### commit + +```func +() commit() impure asm "COMMIT"; +``` + +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. + +#### buy_gas + +```func +() buy_gas(int gram) impure asm "BUYGAS"; +``` + +:::caution +`BUYGAS` opcode is currently not implemented +::: + +Computes the amount of gas that can be bought for `gram` nanotoncoins and sets `gl` accordingly in the same way as `set_gas_limit`. + +### Actions primitives + +#### raw_reserve + +```func +() raw_reserve(int amount, int mode) impure asm "RAWRESERVE"; +``` + +Creates an output action which would reserve exactly `amount` nanotoncoins (if `mode = 0`), at most `amount` nanotoncoins (if `mode = 2`), or all but `amount` nanotoncoins (if `mode = 1` or `mode = 3`) from the remaining balance of the account. It is roughly equivalent to creating an outbound message carrying `amount` nanotoncoins (or `b − amount` nanotoncoins, 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 `mode` means that the external action does not fail if the specified amount cannot be reserved; instead, all the remaining balance is reserved. Bit +8 in `mode` means `amount <- -amount` before performing any further actions. Bit +4 in `mode` means that `amount` 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, `amount` must be a non-negative integer, and `mode` must be in the range `0..15`. + +#### raw_reserve_extra + +```func +() raw_reserve_extra(int amount, cell extra_amount, int mode) impure asm "RAWRESERVEX"; +``` + +Similar to `raw_reserve` but also accepts a dictionary `extra_amount` (represented by `cell` or `null`) with extra currencies. In this way, currencies other than Toncoin can be reserved. + +#### send_raw_message + +```func +() send_raw_message(cell msg, int mode) impure asm "SENDRAWMSG"; +``` + +Sends a raw message contained in `msg`, which should contain a correctly serialized object Message X, with the only exception that the source address is allowed to have a 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). The integer parameter `mode` contains the flags. + +There are currently 3 Modes and 3 Flags for messages. You can combine a single mode with several (maybe none) flags to get a required `mode`. Combination simply means getting sum of their values. A table with descriptions of Modes and Flags is given below. + +| Mode | Description | +| :---- | :--------------------------------------------------------------------------------------------------------------------- | +| `0` | Ordinary message | +| `64` | Carry all the remaining value of the inbound message in addition to the value initially indicated in the new message | +| `128` | Carry all the remaining balance of the current smart contract instead of the value originally indicated in the message | + +| Flag | Description | +| :---- | :--------------------------------------------------------------------------------------------------------------- | +| `+1` | Pay transfer fees separately from the message value | +| `+2` | Ignore any errors arising while processing this message during the action phase | +| `+16` | In the case of action fail - bounce transaction. No effect if `+2` is used. | +| `+32` | Current account must be destroyed if its resulting balance is zero (often used with Mode 128) | + +For example, if you want to send a regular message and pay transfer fees separately, use the Mode `0` and Flag `+1` to get `mode = 1`. If you want to send the whole contract balance and destroy it immidiately, use the Mode `128` and Flag `+32` to get `mode = 160`. + +#### set_code + +```func +() set_code(cell new_code) impure asm "SETCODE"; +``` + +Creates an output action that would change this smart contract code to that given by cell `new_code`. Notice that this change will take effect only after the successful termination of the current run of the smart contract. (Cf. [set_c3](/develop/func/stdlib#set_c3.)) + +### Random number generator primitives + +The pseudo-random number generator uses the random seed, an unsigned 256-bit Integer, and (sometimes) other data kept in [c7](/learn/tvm-instructions/tvm-overview#control-registers). The initial value of the random seed before a smart contract is executed in TON Blockchain is a hash of the smart contract address and the global block random seed. If there are several runs of the same smart contract inside a block, then all of these runs will have the same random seed. This can be fixed, for example, by running `randomize_lt` before using the pseudo-random number generator for the first time. + +:::caution +Keep in mind that random numbers generated by the functions below can be predicted if you do not use additional tricks. + +- [Random number generation](/develop/smart-contracts/guidelines/random-number-generation) + ::: + +#### random + +```func +int random() impure asm "RANDU256"; +``` + +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 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 + +```func +int rand(int range) impure asm "RAND"; +``` + +Generates a new pseudo-random integer `z` in the range `0..range−1` (or `range..−1` if `range < 0`). More precisely, an unsigned random value `x` is generated as in `random`; then `z := x * range / 2^256` is +computed. + +#### get_seed + +```func +int get_seed() impure asm "RANDSEED"; +``` + +Returns the current random seed as an unsigned 256-bit integer. + +#### set_seed + +```func +int set_seed(int seed) impure asm "SETRAND"; +``` + +Sets a random seed to an unsigned 256-bit `seed`. + +#### randomize + +```func +() randomize(int x) impure asm "ADDRAND"; +``` + +Mixes an unsigned 256-bit integer `x` into a random seed `r` by setting the random seed to sha256 of the concatenation of two 32-byte strings: the first with a big-endian representation of the old seed `r`, and the second with a big-endian representation of `x`. + +#### randomize_lt + +```func +() randomize_lt() impure asm "LTIME" "ADDRAND"; +``` + +Equivalent to `randomize(cur_lt());`. + +### Address manipulation primitives + +The address manipulation primitives listed below serialize and deserialize values according to the following TL-B scheme. + +```func +addr_none$00 = MsgAddressExt; + +addr_extern$01 len:(## 8) external_address:(bits len) + = MsgAddressExt; + +anycast_info$_ depth:(#<= 30) { depth >= 1 } + rewrite_pfx:(bits depth) = Anycast; + +addr_std$10 anycast:(Maybe Anycast) + workchain_id:int8 address:bits256 = MsgAddressInt; + +addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9) + workchain_id:int32 address:(bits addr_len) = MsgAddressInt; +_ _:MsgAddressInt = MsgAddress; +_ _:MsgAddressExt = MsgAddress; + +int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool + src:MsgAddress dest:MsgAddressInt + value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams + created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed; + +ext_out_msg_info$11 src:MsgAddress dest:MsgAddressExt + created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed; +``` + +A deserialized `MsgAddress` is represented by the tuple `t` as follows: + +- `addr_none` is represented by `t = (0)`, i.e., a tuple containing exactly + one integer that equals zero +- `addr_extern` is represented by `t = (1, s)`, where slice `s` contains the + field `external_address`. In other words, `t` is a pair (a tuple consisting of two entries), containing an integer equal to one and slice `s` +- `addr_std` is represented by `t = (2, u, x, s)`, where `u` is either `null` (if `anycast` is absent) or a slice `s'` containing `rewrite_pfx` (if `anycast` is present). Next, integer `x` is the `workchain_id`, and slice `s` contains the address +- `addr_var` is represented by `t = (3, u, x, s)`, where `u`, `x`, and `s` have the same meaning as for `addr_std` + +#### load_msg_addr + +```func +(slice, slice) load_msg_addr(slice s) asm( -> 1 0) "LDMSGADDR"; +``` + +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. + +#### parse_addr + +```func +tuple parse_addr(slice s) asm "PARSEMSGADDR"; +``` + +Decomposes `slice s` containing a valid `MsgAddress` into `tuple t` with separate fields of this `MsgAddress`. If `s` is not a valid `MsgAddress`, a cell deserialization exception is thrown. + +#### parse_std_addr + +```func +(int, int) parse_std_addr(slice s) asm "REWRITESTDADDR"; +``` + +Parses slice `s` containing a valid `MsgAddressInt` (usually `msg_addr_std`), applies rewriting from the `anycast` (if present) to the same-length prefix of the address, and returns both the workchain and the 256-bit address as integers. If the address is not 256-bit or if `s` is not a valid serialization of `MsgAddressInt`, throws a cell `deserialization` exception. + +#### parse_var_addr + +```func +(int, slice) parse_var_addr(slice s) asm "REWRITEVARADDR"; +``` + +A variant of `parse_std_addr` that returns the (rewritten) address as a slice `s`, even if it is not exactly 256 bit long (represented by `msg_addr_var`). + +## Debug primitives + +Currently, only one function is available. + +#### dump_stack + +```func +() dump_stack() impure asm "DUMPSTK"; +``` + +Dumps the stack (at most the top 255 values) and shows the total stack depth. + +## Slice primitives + +It is said that a primitive _loads_ some data if it returns the data and the remainder of the slice (so it can also be used as a [modifying method](/develop/func/statements#modifying-methods)). + +It is said that a primitive _preloads_ some data if it returns only the data (it can be used as a [non-modifying method](/develop/func/statements#non-modifying-methods)). + +Unless otherwise stated, loading and preloading primitives read data from a prefix of the slice. + +#### begin_parse + +```func +slice begin_parse(cell c) asm "CTOS"; +``` + +Converts `cell` into `slice`. Notice that `c` must be either an ordinary cell or an exotic cell (see [TVM.pdf](https://ton.org/tvm.pdf), 3.1.2) which is automatically loaded to yield an ordinary cell `c'`converted into `slice` afterwards. + +#### end_parse + +```func +() end_parse(slice s) impure asm "ENDS"; +``` + +Checks if `s` is empty. If not, throws an exception. + +#### load_ref + +```func +(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF"; +``` + +Loads the first reference from a slice. + +#### preload_ref + +```func +cell preload_ref(slice s) asm "PLDREF"; +``` + +Preloads the first reference from a slice. + +#### load_int + +```func +;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX"; +``` + +Loads a signed `len`-bit integer from a slice. + +#### load_uint + +```func +;; (slice, int) ~load_uint(slice s, int len) asm( -> 1 0) "LDUX"; +``` + +Loads an unsigned `len`-bit integer from a slice. + +#### preload_int + +```func +;; int preload_int(slice s, int len) asm "PLDIX"; +``` + +Preloads a signed `len`-bit integer from a slice. + +#### preload_uint + +```func +;; int preload_uint(slice s, int len) asm "PLDUX"; +``` + +Preloads an unsigned `len`-bit integer from a slice. + +#### load_bits + +```func +;; (slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX"; +``` + +Loads the first `0 ≤ len ≤ 1023` bits from slice `s` into a separate slice `s''`. + +#### preload_bits + +```func +;; slice preload_bits(slice s, int len) asm "PLDSLICEX"; +``` + +Preloads the first `0 ≤ len ≤ 1023` bits from slice `s` into a separate slice `s''`. + +#### load_coins + +```func +(slice, int) load_coins(slice s) asm( -> 1 0) "LDGRAMS"; +``` + +Loads serialized amount of Toncoins (any unsigned integer up to `2^120 - 1`). + +#### skip_bits + +```func +slice skip_bits(slice s, int len) asm "SDSKIPFIRST"; +(slice, ()) ~skip_bits(slice s, int len) asm "SDSKIPFIRST"; +``` + +Returns all but the first `0 ≤ len ≤ 1023` bits of `s`. + +#### first_bits + +```func +slice first_bits(slice s, int len) asm "SDCUTFIRST"; +``` + +Returns the first `0 ≤ len ≤ 1023` bits of `s`. + +#### skip_last_bits + +```func +slice skip_last_bits(slice s, int len) asm "SDSKIPLAST"; +(slice, ()) ~skip_last_bits(slice s, int len) asm "SDSKIPLAST"; +``` + +Returns all but the last `0 ≤ len ≤ 1023` bits of `s`. + +#### slice_last + +```func +slice slice_last(slice s, int len) asm "SDCUTLAST"; +``` + +Returns the last `0 ≤ len ≤ 1023` bits of `s`. + +#### load_dict + +```func +(slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT"; +``` + +Loads a dictionary `D` from slice `s`. May be applied to dictionaries or to values of arbitrary `Maybe ^Y` types (returns `null` if `nothing` constructor is used). + +#### preload_dict + +```func +cell preload_dict(slice s) asm "PLDDICT"; +``` + +Preloads a dictionary `D` from slice `s`. + +#### skip_dict + +```func +slice skip_dict(slice s) asm "SKIPDICT"; +``` + +Loads a dictionary as `load_dict` but returns only the remainder of the slice. + +### Slice size primitives + +#### slice_refs + +```func +int slice_refs(slice s) asm "SREFS"; +``` + +Returns the number of references in slice `s`. + +#### slice_bits + +```func +int slice_bits(slice s) asm "SBITS"; +``` + +Returns the number of data bits in slice `s`. + +#### slice_bits_refs + +```func +(int, int) slice_bits_refs(slice s) asm "SBITREFS"; +``` + +Returns both the number of data bits and the number of references in `s`. + +#### slice_empty? + +```func +int slice_empty?(slice s) asm "SEMPTY"; +``` + +Checks whether slice `s` is empty (i.e., contains no bits of data and no cell references). + +#### slice_data_empty? + +```func +int slice_data_empty?(slice s) asm "SDEMPTY"; +``` + +Checks whether slice `s` has no bits of data. + +#### slice_refs_empty? + +```func +int slice_refs_empty?(slice s) asm "SREMPTY"; +``` + +Checks whether slice `s` has no references. + +#### slice_depth + +```func +int slice_depth(slice s) asm "SDEPTH"; +``` + +Returns the depth of slice `s`. If `s` has no references, then returns `0`; otherwise, the returned value is one plus the maximum of depths of cells referred to from `s`. + +## Builder primitives + +It is said that a primitive _stores_ a value `x` into a builder `b` if it returns a modified version of the builder `b'` with the value `x` stored at the end of it. It can be used as a [non-modifying method](/develop/func/statements#non-modifying-methods). + +All of the primitives listed below verify whether there is enough space in the `builder`first, and then the range of the value being serialized. + +#### begin_cell + +```func +builder begin_cell() asm "NEWC"; +``` + +Creates a new empty `builder`. + +#### end_cell + +```func +cell end_cell(builder b) asm "ENDC"; +``` + +Converts `builder` into an ordinary `cell`. + +#### store_ref + +```func +builder store_ref(builder b, cell c) asm(c b) "STREF"; +``` + +Stores a reference to cell `c` into builder `b`. + +#### store_uint + +```func +builder store_uint(builder b, int x, int len) asm(x b len) "STUX"; +``` + +Stores an unsigned `len`-bit integer `x` into `b` for `0 ≤ len ≤ 256`. + +#### store_int + +```func +builder store_int(builder b, int x, int len) asm(x b len) "STIX"; +``` + +Stores a signed `len`-bit integer `x` into `b` for `0 ≤ len ≤ 257`. + +#### store_slice + +```func +builder store_slice(builder b, slice s) asm "STSLICER"; +``` + +Stores slice `s` into builder `b`. + +#### store_grams + +```func +builder store_grams(builder b, int x) asm "STGRAMS"; +``` + +#### store_coins + +```func +builder store_coins(builder b, int x) asm "STGRAMS"; +``` + +Stores (serializes) an integer `x` in the range `0..2^120 − 1` into 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. + +It is the most common way of storing Toncoins. + +#### store_dict + +```func +builder store_dict(builder b, cell c) asm(c b) "STDICT"; +``` + +Stores dictionary `D` represented by cell `c` or `null` into builder `b`. In other words, stores `1`-bit and a reference to `c` if `c` is not `null` and `0`-bit otherwise. + +#### store_maybe_ref + +```func +builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF"; +``` + +Equivalent to `store_dict`. + +### Builder size primitives + +#### builder_refs + +```func +int builder_refs(builder b) asm "BREFS"; +``` + +Returns the number of cell references already stored in builder `b`. + +#### builder_bits + +```func +int builder_bits(builder b) asm "BBITS"; +``` + +Returns the number of data bits already stored in builder `b`. + +#### builder_depth + +```func +int builder_depth(builder b) asm "BDEPTH"; +``` + +Returns the depth of builder `b`. If no cell references are stored in `b`, then returns `0`; otherwise, the returned value is one plus the maximum of depths of cells referred to from `b`. + +## Cell primitives + +#### cell_depth + +```func +int cell_depth(cell c) asm "CDEPTH"; +``` + +Returns the depth of cell `c`. If `c` has no references, then return `0`; otherwise, the returned value is one plus the maximum of depths of cells referred to from `c`. If `c` is a `null` instead of a cell, it returns zero. + +#### cell_null? + +```func +int cell_null?(cell c) asm "ISNULL"; +``` + +Checks whether `c` is a `null`. Usually a `null`-cell represents an empty dictionary. FunC also has polymorphic `null?` built-in. (See [built-ins](/develop/func/builtins#other-primitives).) + +## Dictionaries primitives + +:::caution +The dictionary primitives below are low-level and do not check that the structure of the cell, they are applied to, matches the operation signature. Applying a dictionary operation to a "non-dictionary" or applying an operation corresponding to one key length/sign to a dictionary with a different kind of keys, for instance simultaneous writing to one dictionary key-values with 8bit-signed key and 7bit-unsigned key, is **Undefined Behavior**. Often in such cases an exception is thrown, but in rare cases the wrong value can be written / read. Developers are strongly encouraged to avoid such code. +::: + +As said in [TVM.pdf](https://ton.org/tvm.pdf): + +> Dictionaries admit two different representations as TVM stack values: +> +> - A slice `s` with a serialization of a TL-B value of type `HashmapE(n, X)`. In other words, `s` consists either of one bit equal to zero (if the dictionary is empty) or of one bit equal to one and a reference to a cell containing the root of the binary tree, i.e., a serialized value of type `Hashmap(n, X)`. +> - A “Maybe cell” `c^?`, i.e., a value that is either a cell (containing a serialized value of type `Hashmap(n, X)` as before) or a `null` (corresponding to an empty dictionary, cf. [null values](/develop/func/types#null-values)). When a “Maybe cell” `c^?` is used to represent a dictionary, we usually denote it by `D`. +> +> Most of the dictionary primitives listed below accept and return dictionaries in the second form, which is more convenient for stack manipulation. However, serialized dictionaries inside larger TL-B objects use the first representation. + +In FunC dictionaries are also represented by the `cell` type with the implicit assumption that it may be a `null` value. There are no separate types for dictionaries with different key lengths or value types (after all, it's FunC, not FunC++). + +### Taxonomy note + +A dictionary primitive may interpret the keys of the dictionary either as unsigned `l`-bit integers, as signed `l`-bit integers, or as `l`-bit slices. The primitives listed below differ by the prefix before the word `dict` in their names. `i` stands for signed integer keys, `u` stands for unsigned integer keys, and an empty prefix stands for slice keys. + +For example, `udict_set` is a set-by-key function for dictionaries with unsigned integer keys; `idict_set` is the corresponding function for dictionaries with signed integer keys; `dict_set` is the function for dictionaries with slice keys. + +An empty prefix is used in the titles. + +Also, some of the primitives have their counterparts prefixed with `~`. It makes it possible to use them as [modifying methods](/develop/func/statements#modifying-methods). + +### Dictionary's values + +Values within a dictionary can be stored either as a subslice within an inner dictionary cell or through a reference to a separate cell. In the former scenario, it is not assured that a value small enough to fit within a cell will also fit within the dictionary, as part of the inner cell's space may already be occupied by a portion of the corresponding key. Conversely, the latter method of storage is less efficient in terms of gas usage. Storing a value using the second method is tantamount to inserting a slice with no data bits and a single reference to the value in the first method​​. + +#### dict_set + +```func +cell udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET"; +cell idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET"; +cell dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET"; +(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET"; +(cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET"; +(cell, ()) ~dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET"; +``` + +Sets the value associated with `key_len`-bit key `index` in dictionary `dict` to `value` (a slice) and returns the resulting dictionary. + +#### dict_set_ref + +```func +cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF"; +cell udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF"; +(cell, ()) ~idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF"; +(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF"; +``` + +Similar to `dict_set` but with the value set to a reference to cell `value`. + +#### dict_get? + +```func +(slice, int) idict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGET" "NULLSWAPIFNOT"; +(slice, int) udict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT"; +``` + +Searches for the key `index` within the `dict` dictionary, which uses keys of `key_len` bits. If successful, it retrieves the associated value as a `slice` and returns a flag value of `-1` to indicate **success**. If the search fails, it returns `(null, 0)​​`. + +#### dict_get_ref? + +```func +(cell, int) idict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETREF"; +(cell, int) udict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETREF"; +``` + +Similar to `dict_get?` but returns the first reference of the found value. + +#### dict_get_ref + +```func +cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF"; +``` + +A variant of `dict_get_ref?` that returns `null` instead of the value if the key `index` is absent from the dictionary `dict`. + +#### dict_set_get_ref + +```func +(cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF"; +(cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETOPTREF"; +``` + +Sets the value associated with `index` to `value` (if `value` is `null`, then the key is deleted instead) and returns the old value (or `null` if the value was absent). + +#### dict_delete? + +```func +(cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL"; +(cell, int) udict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDEL"; +``` + +Deletes `key_len`-bit key `index` from the dictionary `dict`. If the key is present, returns the modified dictionary `dict'` and the success flag `−1`. Otherwise, returns the original dictionary `dict` and `0`. + +#### dict_delete_get? + +```func +(cell, slice, int) idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT"; +(cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT"; +``` + +Deletes `key_len`-bit key `index` from dictionary `dict`. If the key is present, returns the modified dictionary `dict'`, the original value `x` associated with the key k (represented by a Slice), and the success flag `−1`. Otherwise, returns `(dict, null, 0)`. + +#### dict_add? + +```func +(cell, int) udict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUADD"; +(cell, int) idict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIADD"; +``` + +An `add` counterpart of `dict_set` sets the value associated with key `index` in dictionary `dict` to `value` but only if it is not already present in `D`. Returns either modified version of the dictionary and `-1` flag or `(dict, 0)`. + +#### dict_replace? + +```func +(cell, int) udict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACE"; +(cell, int) idict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACE"; +``` + +A `replace` operation similar to `dict_set` but which sets the value of key `index` in dictionary `dict` to `value` only if the key was already present in `dict`. Returns either modified version of the dictionary and `-1` flag or `(dict, 0)`. + +### Builder counterparts + +The following primitives accept the new value as a builder instead of a slice, which often is more convenient if the value needs to be serialized from several components computed in the stack. The net effect is roughly equivalent to converting b into a slice and executing the corresponding primitive listed above. + +#### dict_set_builder + +```func +cell udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB"; +cell idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB"; +cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB"; +(cell, ()) ~idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB"; +(cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB"; +(cell, ()) ~dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB"; +``` + +Similar to `dict_set` but accepts a builder. + +#### dict_add_builder? + +```func +(cell, int) udict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUADDB"; +(cell, int) idict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIADDB"; +``` + +Similar to `dict_add?` but accepts a builder. + +#### dict_replace_builder? + +```func +(cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEB"; +(cell, int) idict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEB"; +``` + +Similar to `dict_replace?` but accepts a builder. + +#### dict_delete_get_min + +```func +(cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; +(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; +(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; +(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; +``` + +Computes the minimum key `k` in dictionary `dict`, removes it, and returns `(dict', k, x, -1)`, where `dict'` is the modified version of `dict` and `x` is the value associated with `k`. If the dict is empty, returns `(dict, null, null, 0)`. + +Note that the key returned by `idict_delete_get_min` may differ from the key returned by `dict_delete_get_min` and `udict_delete_get_min`. + +#### dict_delete_get_max + +```func +(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; +(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; +(cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; +(cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; +``` + +Computes the maximum key `k` in dictionary `dict`, removes it, and returns `(dict', k, x, -1)`, where `dict'` is the modified version of `dict` and `x` is the value associated with `k`. If the dict is empty, returns `(dict, null, null, 0)`. + +#### dict_get_min? + +```func +(int, slice, int) udict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2"; +``` + +Computes the minimum key `k` in dictionary `dict`, the associated value `x`, and returns `(k, x, -1)`. If the dictionary is empty, returns `(null, null, 0)`. + +#### dict_get_max? + +```func +(int, slice, int) udict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2"; +``` + +Computes the maximum key `k` in dictionary `dict`, the associated value `x`, and returns `(k, x, -1)`. If the dictionary is empty, returns `(null, null, 0)`. + +#### dict_get_min_ref? + +```func +(int, cell, int) udict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2"; +(int, cell, int) idict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2"; +``` + +Similar to `dict_get_min?` but returns the only reference in the value as a reference. + +#### dict_get_max_ref? + +```func +(int, cell, int) udict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2"; +(int, cell, int) idict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2"; +``` + +Similar to `dict_get_max?` but returns the only reference in the value as a reference. + +#### dict_get_next? + +```func +(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2"; +``` + +Computes the minimum key `k` in dictionary `dict` that is greater than `pivot`; returns `k`, associated value, and a flag indicating success. If the dictionary is empty, returns `(null, null, 0)`. + +#### dict_get_nexteq? + +```func +(int, slice, int) udict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2"; +``` + +Similar to `dict_get_next?` but computes the minimum key `k` that is greater than or equal to `pivot`. + +#### dict_get_prev? + +```func +(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2"; +``` + +Similar to `dict_get_next?` but computes the maximum key `k` smaller than `pivot`. + +#### dict_get_preveq? + +```func +(int, slice, int) udict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2"; +``` + +Similar to `dict_get_prev?` but computes the maximum key `k` smaller than or equal to `pivot`. + +#### new_dict + +```func +cell new_dict() asm "NEWDICT"; +``` + +Creates an empty dictionary, which is actually a `null` value. Special case of `null()`. + +#### dict_empty? + +```func +int dict_empty?(cell c) asm "DICTEMPTY"; +``` + +Checks whether a dictionary is empty. Equivalent to `cell_null?`. + +## Prefix dictionaries primitives + +TVM also supports dictionaries with non-fixed length keys which form a prefix code (i.e., there is no key that is a prefix to another key). Learn more about them in the [TVM Instruction](/learn/tvm-instructions/tvm-overview) section. + +#### pfxdict_get? + +```func +(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2"; +``` + +Returns `(s', x, s'', -1)` or `(null, null, s, 0)`. +Looks up the unique prefix of slice `key` present in the prefix code dictionary `dict`. 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 slice `s''`. If no prefix of `s` is key in prefix code dictionary `dict`, it returns the unchanged `s` and a zero flag to indicate failure. + +#### pfxdict_set? + +```func +(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) asm(value key dict key_len) "PFXDICTSET"; +``` + +Similar to `dict_set` but may fail if the key is a prefix of another key presented in the dictionary. Indicating success, returns a flag. + +#### pfxdict_delete? + +```func +(cell, int) pfxdict_delete?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTDEL"; +``` + +Similar to `dict_delete?`. + +## Special primitives + +#### null + +```func +forall X -> X null() asm "PUSHNULL"; +``` + +By the TVM type `Null`, FunC represents the absence of a value of some atomic type. So `null` can actually have any atomic type. + +#### ~impure_touch + +```func +forall X -> (X, ()) ~impure_touch(X x) impure asm "NOP"; +``` + +Mark a variable as used, such that the code which produced it won't be deleted even if it is not impure. (c.f. [impure specifier](/develop/func/functions#impure-specifier)) + +## Other primitives + +#### min + +```func +int min(int x, int y) asm "MIN"; +``` + +Computes the minimum of two integers `x` and `y`. + +#### max + +```func +int max(int x, int y) asm "MAX"; +``` + +Computes the maximum of two integers `x` and `y`. + +#### minmax + +```func +(int, int) minmax(int x, int y) asm "MINMAX"; +``` + +Sorts two integers. + +#### abs + +```func +int abs(int x) asm "ABS"; +``` + +Computes the absolute value of the integer `x`. From 50f449534a6c6af76389878b1ad077ffc15526e4 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:30 +0800 Subject: [PATCH 104/219] New translations types.md (Chinese Simplified) --- .../current/develop/func/types.md | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/func/types.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/types.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/types.md new file mode 100644 index 0000000000..d6b1c31de0 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/func/types.md @@ -0,0 +1,86 @@ +# Types + +:::info + +FunC documentation was initially written by [@akifoq](https://github.com/akifoq). + +::: + +FunC has the following built-in types. + +## Atomic types + +- `int` is the type of 257-bit signed integers. By default, overflow checks are enabled and lead to integer overflow exceptions. +- `cell` is the type of TVM cells. All persistent data in TON Blockchain is stored in trees of cells. Every cell has up to 1023 bits of arbitrary data and up to four references to other cells. Cells serve as memory in stack-based TVMs. +- `slice` is the type of cell slices. A cell can be transformed into a slice, and then the data bits and references to other cells from the cell can be obtained by loading them from the slice. +- `builder` is the type of cell builders. Data bits and references to other cells can be stored in a builder, and then the builder can be finalized to a new cell. +- `tuple` is the type of TVM tuples. Tuple is an ordered collection of up to 255 components with arbitrary value types that are possibly distinct. +- `cont` is the type of TVM continuations. Continuations are used for controlling the flow of the TVM program execution. It is rather low-level object from the perspective of FunC, although paradoxically quite general. + +Note that any of the types above occupy only a single entry in the TVM stack. + +### Absence of boolean type + +In FunC, booleans are represented as integers; `false` is represented as `0` and `true` is represented as `-1` (257 ones in binary notation). Logical operations are done as bitwise operations. When a condition is checked, every non-zero integer is considered a `true` value. + +### Null values + +By the value `null` of TVM type `Null`, FunC represents the absence of a value of some atomic type. Some primitives from the standard library may be typed as ones returning an atomic type and actually return `null`s in some cases. Others may be typed as ones excepting a value of an atomic type but work fine with `null` values too. Such behavior is explicitly stated in the primitive specification. By default, `null` values are prohibited and lead to a run-time exception. + +In such a way, the atomic type `A` may be implicitly transformed into type `A^?` a.k.a. `Maybe A` (the type checker is agnostic to such a transformation). + +## Hole type + +FunC has support for type inference. Types `_` and `var` represent type "holes" which can later be filled with some actual type during type checking. For example, `var x = 2;` is a definition of variable `x` equal to `2`. The type checker can infer that `x` has type `int`, because `2` has type `int`, and the left and right sides of the assignment must have equal types. + +## Composite types + +Types can be composed into more complex ones. + +### Functional type + +Types of the form `A -> B` represent functions with specified domain and codomain. For example, `int -> cell` is the type of function that takes one integer argument and returns a TVM cell. + +Internally, values of such types are represented as continuations. + +### Tensor types + +Types of the form `(A, B, ...)` essentially represent ordered collections of values of types `A`, `B`, `...`, which all together occupy more than one TVM stack entry. + +For example, if a function `foo` has type `int -> (int, int)`, it means that the function takes one integer and returns a pair of them. + +A call of this function may look like `(int a, int b) = foo(42);`. Internally, the function consumes one stack entry and leaves two of them. + +Note that from a low-level perspective, value `(2, (3, 9))` of type `(int, (int, int))` and value `(2, 3, 9)` of type `(int, int, int)`, are represented in the same way as three stack entries `2`, `3` and `9`. For the FunC type checker they are values of **different** types. For example, code `(int a, int b, int c) = (2, (3, 9));` wouldn't be compiled. + +A special case of the tensor type is the **unit type** `()`. It is usually used to represent the fact that a function doesn't return any value or has no arguments. For example, a function `print_int` would have type `int -> ()` and the function `random` has type `() -> int`. It has a unique inhabitant `()` which occupies 0 stack entries. + +Type of form `(A)` is considered by type checker as the same type as `A`. + +### Tuples types + +Types of the form `[A, B, ...]` represent TVM tuples with specific lengths and types of components known in compile time. For example, `[int, cell]` is the type of TVM tuple which length is exactly 2, and where the first component is an integer and the second is a cell. `[]` is the type of empty tuples (having the unique inhabitant—the empty tuple). Note that in contrast to the unit type `()`, the value of `[]` occupies one stack entry. + +## Polymorphism with type variables + +FunC has Miller-Rabin type system with support for polymorphic functions. For example, the following function: + +```func +forall X -> (X, X) duplicate(X value) { + return (value, value); +} +``` + +is a polymorphic function which takes a (single stack entry) value and returns two copies of this value. `duplicate(6)` will produce values `6 6`, and `duplicate([])` will produce two copies `[] []` of an empty tuple. + +In this example, `X` is a type variable. + +See more info on this topic in the [functions](/develop/func/functions#polymorphism-with-forall) section. + +## User-defined types + +Currently, FunC has no support for defining types except for the type constructions described above. + +## Type width + +As you may have noticed, every value of a type occupies some number of stack entries. If it is the same number for all values of the type, this number is called **type width**. Polymorphic functions can currently be defined only for types with fixed and known in advance type width. From 9c9ff2e8f709732dbd8e55a75721d393038d2cb0 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:31 +0800 Subject: [PATCH 105/219] New translations get-started-with-ton.mdx (Chinese Simplified) --- .../current/develop/get-started-with-ton.mdx | 711 ++++++++++++++++++ 1 file changed, 711 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/get-started-with-ton.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/get-started-with-ton.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/get-started-with-ton.mdx new file mode 100644 index 0000000000..d2bd9e48fb --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/get-started-with-ton.mdx @@ -0,0 +1,711 @@ +import ThemedImage from '@theme/ThemedImage'; +import ConceptImage from '@site/src/components/conceptImage' +import Player from '@site/src/components/player' + +# Get Started with TON + +Set up your first application on TON Blockchain from scratch and discover its speed, reliability and essential concepts of asynchronous way of thinking. + +:::tip newcomer-friendly guide +If you are completely new with programming, this guide is the best choice for you. +::: + +This learning path contains **5 modules** and should take you around **45 minutes**. + +## 🛳 What you will learn + +In this tutorial, you'll learn how to easily make blockchain transactions using JavaScript. You could learn to do it without this tutorial, but this approach is convenient and user-friendly. + +1. You will make your own TON Wallet with Tonkeeper +2. You will use a Testnet faucet to topup your wallet for testing +3. You will understand essential concepts of TON smart contracts (Addresses, Cells) +4. You will learn how to interact with TON using TypeScript SDK and API provider +5. You will compile your first transaction using NFT Miner console application + + _You're going to mine an NFT rocket achievement!!!_ + +As the first miners on TON, you will go through the Proof-of-Work smart contract and finally mine a secret reward for your TON wallet. Check it out: + +
+ +
+ +Our goal for today is to mine an NFT! This achievement will stay with you _forever_. + +Finally, you are able to mine this NFT achievement even in mainnet. (_it costs only 0,05 TON!_) + +### Video tutorial + +Check out this awesome video tutorial created by a member of TON developer community! With this helpful guide, you can complete the tutorial with ease: + + + +### Mining on TON Blockchain + +Today, we are going to teach our prospective builders how to mine on TON Blockchain. This experience will allow all of you to understand the significance of mining and why Bitcoin mining helped revolutionize the industry. + +Though the PoW Giver smart contract framework that defined the initial mining process that helped lay the foundation for TON was completed at launch, the last TON was mined in June of 2022 to culminate TON’s Proof of Work (PoW) token distribution mechanism. That said, with our recent transition to Proof of Stake (PoS), the era of staking on TON has just begun. + +- [Dive deeper into our economic model and mining on TON](https://ton.org/mining) + +Now, let’s focus on the first steps to becoming a **TVM Developer** and learn how to mine an NFT on TON! Provided below is an example of what we're aiming to create. + +
+ +
+ +It’s possible to create a miner in about half an hour if we keep focused on the task at hand. + +## 🦄 Getting started + +To get started, all developers will make use of the following components: + +- **Wallet**: You need a non-custodial wallet to store an NFT in testnet mode. +- **Repository**: We’ll use a ready-made template designed specifically for you. +- **Developer Environment**: Developers will need to determine whether they want to mine using a local or cloud environment. + +### Download and Create a Wallet + +To start off, you’ll need a non-custodial wallet that will allow you to receive and store your TON; for this purpose in this guide we are using Tonkeeper. You’ll need to enable Testnet mode within the wallet to be able to receive Testnet Toncoins. These tokens will be used later on to send a final minting transaction to the smart contract. + +:::info +With a non-custodial wallet, the user owns the wallet and holds its private key by themselves. +::: + +To download and create a TON wallet follow these simple steps: + +1. Install the Tonkeeper app on your smartphone. It can be downloaded [here](https://Tonkeeper.com/). +2. Next, you’ll need to [enable test mode](/participate/wallets/apps#Tonkeeper-test-environment) within Tonkeeper. + +Easy! Let's go to the development now. + +### Project setup + +To make your life easier and skip routine low-level stuff, we will use a boilerplate. + +:::tip +Note, you'll need to [sign in](https://github.com/login) to GitHub for further work. +::: + +Please, use the [ton-onboarding-challenge](https://github.com/ton-community/ton-onboarding-challenge) template to create your project by clicking on the “Use this template” button and selecting the “Create a new repository” tab as shown below: + +

+ +

+ +After completing this step, you’ll have access to a highly performant repository that can be used as your miner's core. Congratulations! ✨ + +### Development Environments + +The next step is to choose which developer environment is best suited to your needs, experience level, and overall skill-set. As you can see, it is possible to carry out this process by using either a cloud-based or local environment. Developing on the cloud is often considered simpler and easier to get started. Below, we’ll outline the steps required for both approaches. + +:::tip +Make sure you have opened the repository in your GitHub profile that was generated from the template in the previous step. +::: + +

+ +

+ +#### Local and Cloud Development Environments + +- For users who are not familiar with JavaScript, it can be challenging to use a JavaScript IDE, especially if your computer and tooling systems are not configured for this purpose. + +- However, if you're familiar with NodeJS and Git and know how to work with `npm`, it may be more comfortable for you to use a **local environment**. + +#### Cloud Codespaces + +If you choose the cloud development environment it's easy to get started by first selecting the _Code_ tab and then by clicking on the _Create codespace on master_ button within the GitHub repository like shown below: + +

+ +

+ +After completing this step, GitHub will create a special cloud workspace that allows you to access the VSCode Online IDE (Visual Code Online Integrated Development Environment). + +Once access is granted (the codespace typically starts in about 30 seconds), you'll have everything required to begin without the need to install Git, Node.js, or other developer tools. + +#### Local Development Environments + +To set up a local development environment, you'll require access to these three essential tools: + +- **Git**: Git is an essential tool that every developer needs to work with repositories. It can be downloaded [here](https://git-scm.com/downloads). +- **NodeJS**: Node.js is the JavaScript and TypeScript runtime environment typically used for application development on TON. It can be downloaded [here](https://nodejs.org/en/download/). +- **JavaScript IDE**. JavaScript IDE’s are typically used for development within local development environments. An example of this case is Visual Studio Code ([VSCode](https://code.visualstudio.com/download)). + +To get started, you’ll need to clone your GitHub repository boilerplate and open the correct repository in your Integrated Development Environment (IDE). + +#### Running Scripts + +In this guide, you'll need to run TypeScript scripts. All commands, such as running scripts or installing modules, are executed through the command line, which is located in the IDE's Terminal workspace. This workspace is typically found at the bottom of the IDE. + +For example, in the Cloud Codespaces, you should open the Terminal workspace (if it is not already open): + +

+ +



+ +Enter commands in this window and execute them with _Enter_: + +

+ +



+ +The Terminal is also available as a separate application. Please choose the appropriate version based on your IDE and OS. + +Great! After these steps you're ready to get deeper into TON Blockchain secrets. 👀 + +## 🎯 Connect to TON + +Okay, what do you need to connect to TON Blockchain? + +- **Smart contract address** as a point of destination. Our goal is to mine an NFT from the _proof-of-work smart contract_, so we need an address to get current mining complexity. +- **API provider** to make requests to TON Blockchain. TON has multiple [API types](/develop/dapps/apis/) for different purposes. We will use the testnet version of [toncenter.com](https://toncenter.com/) API. +- **JavaScript SDK**: A JavaScript SDK (recall that an SDK is a Software Development Kit) is needed to parse the smart contract address being used and prepare it to create an API request. To better understand TON addresses and why they need to be parsed to carry out this process, please see this [resource](/learn/overviews/addresses) to understand why should we parse it. To carry out this procedure, we'll use [ton.js](https://github.com/ton-core/ton). + +In the next section we’ll describe how users send their initial requests to TON Blockchain using the TONCenter API and ton.js to receive data from the PoW smart contract. + +### Smart Contract Addresses + +For the miner to work correctly, we need to add two different smart contract address types. These include: + +1. **Wallet address**: A wallet address is required, because it is necessary for the miner to receive their mining reward (in this case, we must use the [Tonkeeper Testnet mode](/participate/wallets/apps#Tonkeeper-test-environment)). +2. **Collection address**: A collection address is required to act as a smart contract to correctly mine an NFT (to carry out this process copy the NFT collection address, under the TON unboarding challenge collection name from the [Getgems website](https://testnet.getgems.io/collection/EQDk8N7xM5D669LC2YACrseBJtDyFqwtSPCNhRWXU7kjEptX)). + +Next, we’ll open the `index.ts` file in your miner and create a main function composed of initial constants as follows: + +```ts title="ton-onboarding-challenge/index.ts" +import {Address} from "ton" + +async function main () { + + const wallet = Address.parse('YOUR_WALLET_ADDRESS'); + const collection = Address.parse('COLLECTION_ADDRESS'); + +} + +main() +``` + +#### Using the async main() Function + +Later in the process of creating a TON NFT Miner, several requests will be executed to the public API to relay responses to the correct string of code in exchange for the desired instructions. By leveraging the Async/await function, code simplicity is improved dramatically. + +#### Address Parsing + +On TON, smart contract addresses come in different forms that employ the use of numerous flag types. In this context specifically, we’ll make use of the _user-friendly address form_. That said, if you are curious to learn more about the different smart contract address types, feel free to check out this additional [resource](https://ton.org/docs/learn/overviews/addresses) in our documentation. + +For the miner to work correctly, we need to add two different smart contract address types. These include: + +The `Address.parse()` command found in the `ton.js` SDK allows the developer to create an address object to convert addresses from one form to another in a simplified manner. + +### Connect to an API Provider + +In this step, we'll connect with TON via TONCenter (which is hosted on toncenter.com) API provider using specific commands in the script. + +The simplest way to learn how it works is by using @orbs-network/ton-access. + +:::tip +Remember, if you are adding new modules with `import`, that you may need to install them by executing the command `npm i @orbs-network/ton-access` in the [terminal](/develop/get-started-with-ton#running-scripts). +::: + +

+ +

+ +We are adding `client` and `endpoint` in the `index.ts` script using _TonClient_ and _getHttpEndpoint_ from `@orbs-network/ton-access`: + +```ts title="ton-onboarding-challenge/index.ts" +import {Address, TonClient} from "ton" +import {getHttpEndpoint} from "@orbs-network/ton-access"; + + // ... previous code + + // get the decentralized RPC endpoint in Testnet + const endpoint = await getHttpEndpoint({ + network: "testnet", + }); + + // initialize ton library + const client = new TonClient({ endpoint }); + +``` + +:::info what to do in production? +It's better to use an RPC node provider, or to run your own ton-http-api instance for that. Read more at the [TonCenter API page](/develop/dapps/apis/toncenter). +::: + +### Receiving Mining Data From TON Blockchain + +Finally, the next step in the process is to retrieve specific mining data from TON Blockchain. + +By consulting the [README file](https://github.com/ton-community/ton-onboarding-challenge#mining-process-advanced) needed to complete the ton-onboarding-challenge, the latest TON mining data is obtained by running the `get_mining_data` method. Once initiated, the result will be as follows: + +As a result we should receive an array with these fields: + +```bash +( + int pow_complexity, + int last_success, + int seed, + int target_delta, + int min_cpl, + int max_cpl +) +``` + +#### Running Smart Contract Get Methods on TON + +Using `ton.js` it is possible to run the callGetMethod (SMART_CONTRACT_ADDRESS, METHOD) function. +Running this code will result in the following console output: + +```ts title="ton-onboarding-challenge/index.ts" + // ... previous code + + const miningData = await client.callGetMethod(collection, 'get_mining_data') + + console.log(miningData) +``` + +Furthermore, to run the script, it is necessary to enter the following command [in the terminal](/develop/get-started-with-ton#running-scripts): + +```bash +npm run start +``` + +:::tip +To avoid unexpected issues, make sure you have finalized all previous steps, including inputting contract addresses. +::: + +Good! As long as the above processes were executed correctly, successful connection to the API will be achieved and the necessary data will be displayed in the console. The correct console output should be initiated as follows: + +```bash +{ + gas_used: 2374, + stack: [ + [ + 'num', + '0x2880000000000000000000000000000000000000000000000000000000000' + ], + [ 'num', '0x63984815' ], + [ 'num', '0x357401cf9b4f2386950faefd6b616264' ], + [ 'num', '0x1e' ], + [ 'num', '0xab' ], + [ 'num', '0xfc' ] + ] +} +``` + +Above it displays the gas amount used to execute the process, as well as the sum of numerical (_num_) values in hex format. At this time, this data is not too critical because it is necessary to convert the hex output data into something that can be used more easily. + +We need to convert the hex output to something _useful_. + +:::info GAS PARAMETERS ON TON: + +1. To obtain a better understanding of how TON Virtual Machine (TVM) operates and _how does TON process transactions_, check out [TVM overview section](/learn/tvm-instructions/tvm-overview). +2. Secondly, if you are interested in learning more about how transaction and gas fees work on TON, consider diving into [this section](/develop/smart-contracts/fees) of our documentation. +3. Finally, to get a better understanding of the exact gas values needed to carry out TVM instructions see [this section](/learn/tvm-instructions/instructions#gas-prices) of our documentation. + ::: + +Now, let's return to the tutorial! + +#### Numerical Mining Data in a User-Friendly Format + +In the section above where we discuss gas amounts and numerical (_num_) values in hex format needed to receive mining data, we note that the hex numbers present within the strings of code must be converted into a more easily understandable and usable format. + +As it is clear when examining the given output, hex numbers can be quite substantial in size. It's not possible to make a variable and make use of them, because of specific [JavaScript limitations](https://stackoverflow.com/a/307200) that are outlined here. + +We need a way to translate this, that’s where the `bn.js` (the big number implementation in JavaScript) library comes in. Bn.js is a library developers use to work with large numbers that are larger than the maximum JavaScript integer values. Let’s use this example to get a better idea of the _Mining Data_ required for this process: + +```ts title="ton-onboarding-challenge/index.ts" +import {BN} from 'bn.js' + + // ... previous code + + const parseStackNum = (sn: any) => new BN(sn[1].substring(2), 'hex'); + + const complexity = parseStackNum(miningData.stack[0]); + const last_success = parseStackNum(miningData.stack[1]); + const seed = parseStackNum(miningData.stack[2]); + const target_delta = parseStackNum(miningData.stack[3]); + const min_cpl = parseStackNum(miningData.stack[4]); + const max_cpl = parseStackNum(miningData.stack[5]); + + console.log('complexity', complexity); + console.log('last_success', last_success.toString()); + console.log('seed', seed); + console.log('target_delta', target_delta.toString()); + console.log('min_cpl', min_cpl.toString()); + console.log('max_cpl', max_cpl.toString()); +``` + +As shown above, the different components of _miningData_ use a stack-based array with hex numbers for different parameters (which will be introduced in the section below). To achieve the desired value outcome, we added the parseStackNum function to create a BN (Big Number) object from a hex number. + +After this process is complete, it is necessary to print values within the console. In this case, some values are printed as BN objects, which will make the process more user-friendly. Try to run the script again by running the command: + +```bash +npm run start +``` + +Here is an example output: + +```bash +complexity +last_success 1670924309 +seed +target_delta 30 +min_cpl 171 +max_cpl 252 +``` + +Let's cover the Mining Data command that is used to translate different data parameters when programming mining data into TON Blockchain. These include: + +- `complexity` is the most important number for miners. It's a Proof-of-Work complexity for the values. You're successful _if the final hash is less than complexity_. +- `last_success` is a [unix timestamp](https://www.unixtimestamp.com/) date and time representation that keeps track of the last mining transaction on TON. Each time the last_success metric changes, it's necessary to run the miner again because the seed also changes during this process. +- `seed` denotes a unique value generated by a smart contract to calculate the desired hash. To better understand this process and learn more about how the seed changes and why, have a look at the project files folder by using the ctx_seed keyword(Ctrl+F with keyword "ctx_seed"). +- `target_delta`, `min_cpl` and `max_cpl` won't be used in our tutorial. But you can always read more about how they are used in smart contracts to calculate proof-of-work complexity in the source files of the collection in your project. + +Now that we understand the different parameters discussed above, we have the values(`complexity`, `last_success`, `seed`) which we will use in our NFT Miner in the next chapter. + +## 🛠 Prepare a NFT Miner + +Hey, you're doing a great job! + +After connecting to TON and retrieving the necessary mining data from the blockchain to create an NFT Miner, let’s focus on the next steps in this process to achieve our goal. + +In this chapter you will _prepare a mining message_ and _calculate a hash_ of the message. After that, you will _find a hash that's less(`<`) than the complexity_ we got from the smart contract. + +That is what a miner is! Simple, isn't it? + +### Preparing Mining Messages + +First, we must prepare a mining message by ensuring the correct parameters to ensure the validity and data integrity of this process. + +Thankfully, the [README file](https://github.com/ton-community/ton-onboarding-challenge#mining-process-advanced) allows us to retrieve the correct guidelines needed to achieve this goal. As you can see, the above README file comprises a table with certain fields and Cell types (titled “Layout of Proof of Work Cell’) to help achieve our desired result. + +:::info WHAT ARE CELLS? +Cells are data storage structures on TON that fulfill numerous purposes, including increasing network scalable and smart contract transaction speeds. We won’t get into specifics here, but if you’re interested in understanding the complexity of cells and how they work, consider diving into [this](/learn/overviews/cells) section of our documentation. +::: + +Fortunately, all the data structures used in this tutorial are already written in TypeScript. Use the `MineMessageParams` object from _NftGiver.data.ts_ to build a transaction with _Queries_: + +```ts title="ton-onboarding-challenge/index.ts" + import {unixNow} from "./src/lib/utils"; + import {MineMessageParams, Queries} from "./src/giver/NftGiver.data"; + + // ... previous code + + const mineParams : MineMessageParams = { + expire: unixNow() + 300, // 5 min is enough to make a transaction + mintTo: wallet, // your wallet + data1: new BN(0), // temp variable to increment in the miner + seed // unique seed from get_mining_data + }; + + let msg = Queries.mine(mineParams); // transaction builder +``` + +Probably, you have a question: where are the _op_ and _data2_ from the [table](https://github.com/ton-community/ton-onboarding-challenge#mining-process-advanced)? + +- In the table, the numerical value of data1 must be equal to that of data2. In order to omit filling the data2 value, the transaction builder performs a low-level process (see Queries.mine() sources). +- Because the `op` classification is always constant, it is already implemented in transaction builder _Queries_ and in _OperationCodes_. You can find the op code by going to the source code of `mine()` method. + +:::tip +Though it may be interesting to check out the source code (`./src/giver/NftGiver.data`), it is not necessary. +::: + +### Creating TON NFT Miners + +Now that we have completed the process to prepare messages for our TON miner, let’s jump into the initial process to actually create a miner. First, let’s consider this line of code: + +```ts +let msg = Queries.mine(mineParams); +``` + +Above we compiled a `msg` value. The idea of mining is to find a hash `msg.hash()` that will be less than `complexity` from the last received _get_mining_data()_. We can increment `data1` as many times as we need. + +The pure miner will continue to run indefinitely, as long as `msg.hash()` is bigger than `complexity` (message hash is larger than PoW mining complexity). + +Here is an example of the code running as it relates to BigNumbers in TypeScript: + +```ts title="ton-onboarding-challenge/index.ts" + let msg = Queries.mine(mineParams); + + while (new BN(msg.hash(), 'be').gt(complexity)) { + mineParams.expire = unixNow() + 300 + mineParams.data1.iaddn(1) + msg = Queries.mine(mineParams) + } + + console.log('Yoo-hoo, you found something!') +``` + +A few important considerations regarding Big Number (_BN_) functions: + +- We create a _big-endian_ BN object from the `msg.hash()` with `'be'` attribute. +- `gt()`: denotes _greater than a variable_ for comparing _BigNumbers_. +- `iaddn(1)`: denotes an incremented value. + +Though the miner will work properly after the completion of the above steps, it will have a visually unappealing appearance (try `npm run start`). Therefore, we must address this issue. Let’s jump in. + +#### Improving TON Miner Appearance ✨ + +We want to make the miner look sexy now! How do we do it? + +Just follow me, my friend, follow me. + +To achieve our goal, we’ll add these commands: + +```ts title="ton-onboarding-challenge/index.ts" + let msg = Queries.mine(mineParams); + let progress = 0; + + while (new BN(msg.hash(), 'be').gt(complexity)) { + progress += 1 + console.clear() + console.log(`Mining started: please, wait for 30-60 seconds to mine your NFT!`) + console.log(' ') + console.log(`⛏ Mined ${progress} hashes! Last: `, new BN(msg.hash(), 'be').toString()) + + mineParams.expire = unixNow() + 300 + mineParams.data1.iaddn(1) + msg = Queries.mine(mineParams) + } + + console.log(' ') + console.log('💎 Mission completed: msg_hash less than pow_complexity found!'); + console.log(' ') + console.log('msg_hash: ', new BN(msg.hash(), 'be').toString()) + console.log('pow_complexity: ', complexity.toString()) + console.log('msg_hash < pow_complexity: ', new BN(msg.hash(), 'be').lt(complexity)) +``` + +Just check it out! Let’s execute the command: + +```bash +npm run start +``` + +

+ +

+ +_Cool, isn't it?_ 😏 + +After these commands are executed correctly, we’ll have a visually appealing NFT miner. In the next section, we’ll focus on connecting a wallet to the miner to create a payment channel that can accept and receive transactions from TON Blockchain. + +## 🎨 Prepare a Transaction + +Next, we’ll outline the steps to compile a message and send it to the blockchain with your [Tonkeeper wallet](https://Tonkeeper.com/). +The upcoming steps will guide you in completing the process of **mining an NFT** on TON. + +### Creating a Payment Link + +In order to ensure that the NFT mining process is carried out correctly, and that the user can store their NFT properly, we must create a payment link that is able to simultaneously interact with TON Blockchain and the Tonkeeper wallet. + +To achieve this goal, we’ll initiate the creation of a payment URL (a payment link) using the `ton://transfer/
` format that can be customized using different parameters. This will work perfectly because we want to create a message, and send it to the smart contract using our Tonkeeper wallet: + +```ts title="ton-onboarding-challenge/index.ts" +import {toNano} from "ton" + +// ... previous code + + console.log(' '); + console.log("💣 WARNING! As soon as you find the hash, you should quickly send the transaction."); + console.log("If someone else sends a transaction before you, the seed changes, and you'll have to find the hash again!"); + console.log(' '); + + // flags work only in user-friendly address form + const collectionAddr = collection.toFriendly({ + urlSafe: true, + bounceable: true, + }) + // we must convert TON to nanoTON + const amountToSend = toNano('0.05').toString() + // BOC means Bag Of Cells here + const preparedBodyCell = msg.toBoc().toString('base64url') + + // final method to build a payment URL + const tonDeepLink = (address: string, amount: string, body: string) => { + return `ton://transfer/${address}?amount=${amount}&bin=${body}`; + }; + + const link = tonDeepLink(collectionAddr, amountToSend, preparedBodyCell); + + console.log('🚀 Link to receive an NFT:') + console.log(link); +``` + +Let’s run the above script to launch the newly created payment link. Now it’s time to make the payment link running on your PC smartphone compatible. Let’s get started. + +### Creating A Smartphone Compatible Payment Link + +As a solution, the most brilliant minds in the world created a QR code generator specifically for use in terminal. With this tool, you can simply scan the payment link from your Tonkeeper wallet to send a transaction. + +```bash +npm install qrcode-terminal +``` + +Finally, we’ll need to encode the payment link(`link`) into the QR code and print it in the console. This is completed by following the steps below: + +```ts title="ton-onboarding-challenge/index.ts" + const qrcode = require('qrcode-terminal'); + + qrcode.generate(link, {small: true}, function (qrcode : any) { + console.log('🚀 Link to mine your NFT (use Tonkeeper in testnet mode):') + console.log(qrcode); + console.log('* If QR is still too big, please run script from the terminal. (or make the font smaller)') + }); +``` + +Do you sense the _experience_ in the air? That's you, on your way to becoming a TVM developer. + +Now that we have successfully encoded the QR code generator and payment link, we'll need to acquire some Testnet Toncoins to send our first transaction. This is accomplished by making use of a TON [token faucet](https://coinmarketcap.com/alexandria/glossary/faucet) (a Telegram Bot that shares Testnet Toncoins) on TON Blockchain, so we are in possession of some tokens that can be sent to the Tonkeeper wallet we created. + +## ⛏ Mine an NFT With a Wallet + +There are two main ways to mine an NFT on TON: + +- [Simple: NFT Testnet Mining](/develop/get-started-with-ton#simple-mine-in-testnet) +- [Genuine: NFT Mainnet Mining](/develop/get-started-with-ton#genuine-mine-in-mainnet) + +### Simple: NFT Testnet Mining + +Below are the steps needed to initiate your first Testnet transaction to mine your NFT: + +1. Activate [Testnet mode within your Tonkeeper wallet](/participate/wallets/apps#Tonkeeper-test-environment) +2. Input our testnet wallet address from Tonkeeper into `wallet` variable in the `index.ts` +3. Input address of the [NFT collection from Testnet](https://testnet.getgems.io/collection/EQDk8N7xM5D669LC2YACrseBJtDyFqwtSPCNhRWXU7kjEptX) into `collection` variable in the `index.ts` + +#### Top Up Wallet Balance via the Token Faucet + +To proceed to the next step, we need to acquire some TON testnet tokens. This can be achieved by making use of the testnet [faucet found here](https://t.me/testgiver_ton_bot). + +#### Mine A Testnet NFT Rocket + +In order to successfully mine an NFT rocket on Testnet, it is necessary to follow these steps: + +1. _Open_ Open the Tonkeeper wallet on your phone (it should hold some newly received TON Testnet tokens). +2. _Select_ scan mode in the wallet to scan the QR code. +3. _Run_ your miner to acquire the correct hash (this process takes between 30 and 60 seconds). +4. _Scan_ the generated QR code from the miner. + +:::tip final tip +Because there may be other developers carrying out the same process in an attempt to mine their own NFT, you may have to try the process a couple times to be successful (as another user could mine the next NFT available right before you). +::: + +Soon after initiating this process, you will have successfully mined your very first NFT on TON (it should appear in your Tonkeeper wallet). + +![](/img/tutorials/onboarding/8.svg) + +Welcome aboard, **a true TVM Developer**! You did it. 🛳 + +### Genuine: NFT Mainnet Mining + +Hey! For those who wish to mine an NFT on TON Mainnet, these instructions should be followed: + +1. You have activated _mainnet_ mode in your Tonkeeper (it should hold at least 0.1 TON). +2. Input our _mainnet_ wallet address from Tonkeeper into `wallet` variable in the `index.ts` +3. Input address of the [NFT collection from the Mainnet](https://getgems.io/collection/EQDk8N7xM5D669LC2YACrseBJtDyFqwtSPCNhRWXU7kjEptX) into `collection` variable in the `index.ts` +4. Replace **endpoint** to the _Mainnet_, for orbs-access it enables with excluding testnet argument in _getHttpEndpoint_: + +```ts title="ton-onboarding-challenge/index.ts" +// get the decentralized RPC endpoint in Mainnet +const endpoint = await getHttpEndpoint(); +``` + +#### Mine A Mainnet NFT Rocket + +Like we outlined in the testnet NFT rocket mining process, in order to successfully mine an NFT rocket on mainnet, it is necessary to follow these steps: + +1. _Open_ the Tonkeeper wallet on your phone (remember, it should hold some TON tokens). +2. _Select_ scan mode in the wallet to scan the QR code. +3. _Run_ your miner to acquire the correct hash (this process takes between 30 and 60 seconds). +4. _Scan_ the generated QR code from the miner. + +:::tip final tip +Because there may be other developers carrying out the same process in an attempt to mine their own NFT, you may have to try the process a couple times to be successful (as another user could mine the next NFT available right before you). +::: + +After some time, you will have **mined your NFT** and become a TVM Developer in TON Blockchain. The ritual is complete. Look at your NFT in Tonkeeper. + +
+ +
+ +Welcome aboard, **a TVM Developer**! You did it. 🛳 + +## 🧙 What's next? + +_First, take a rest! You did a big task! You are a TVM developer now. But it's only the beginning of the long way._ + +## See Also + +After finishing the TON Onboarding Challenge where we successfully mined an NFT, consider taking a look at some of these materials that detail different portions of TON's Ecosystem: + +- [What is blockchain? What is a smart contract? What is gas?](https://blog.ton.org/what-is-blockchain) +- [TON Hello World: Step-by-step guide for writing your first smart contract](https://ton-community.github.io/tutorials/02-contract/) +- [Develop Smart Contracts: Introduction](/develop/smart-contracts/) +- [\[YouTube\] Ton Dev Study - FunC & Blueprint](https://www.youtube.com/playlist?list=PLyDBPwv9EPsDjIMAF3XqNI2XGNwdcB3sg) +- [How to work with wallet smart contracts](/develop/smart-contracts/tutorials/wallet) +- [FunC Journey: Part 1](https://blog.ton.org/func-journey) +- [Bot for sales of dumplings](/develop/dapps/tutorials/accept-payments-in-a-telegram-bot-js) +- [Mint Your first Jetton](/develop/dapps/tutorials/jetton-minter) +- [Step by step NFT collection minting](/develop/dapps/tutorials/collection-minting) +- [How to run TON Site](/develop/dapps/tutorials/how-to-run-ton-site) + +:::info have some feedback? +You are one of the first explorers here. If you find any mistakes or feel stacked, please send feedback to [@SwiftAdviser](https://t.me/SwiftAdviser). I will fix it ASAP! :) +::: From 4de1a6eb7d407785ab6e6bd292e25072be17ca46 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:32 +0800 Subject: [PATCH 106/219] New translations blockchain-configs.md (Chinese Simplified) --- .../develop/howto/blockchain-configs.md | 557 ++++++++++++++++++ 1 file changed, 557 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/blockchain-configs.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/blockchain-configs.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/blockchain-configs.md new file mode 100644 index 0000000000..f7821748f3 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/blockchain-configs.md @@ -0,0 +1,557 @@ +# Config Parameters + +:::info +Read live values via [tonviewer](https://tonviewer.com/config) +::: + +## 👋 Introduction + +On this page, you can find a description of the configuration parameters used in the TON Blockchain. +TON has a complex configuration with many technical parameters: some are used by the blockchain itself, some by the ecosystem. However, only a few people understand what these parameters mean. This article is necessary to provide users with a simple way to understand the parameters and their purpose. + +## 💡 Prerequisites + +This material is intended to be read alongside the parameter list. +You can view the parameter values in the [current configuration](https://explorer.toncoin.org/config), and the way they are written into [cells](/learn/overviews/cells) is described in the [block.tlb](https://github.com/ton-blockchain/ton/blob/master/crypto/block/block.tlb) file in [TL-B](/develop/data-formats/tl-b-language) format. + +:::info +The binary encoding at the end of the TON Blockchain parameter is a serialized binary representation of its configuration, enabling efficient storage or transmission of the configuration. The precise details of serialization depend on the specific encoding scheme used by the TON Blockchain. +::: + +## 🚀 Let's get started! + +All parameters are in order, and you won't get lost. For your convenience, use the right sidebar for quick navigation. + +## Param 0 + +This parameter is the address of a special smart contract that stores the blockchain's configuration. The configuration is stored in the contract to simplify its loading and modification during validator voting. + +:::info +In the configuration parameter, only the hash portion of the address is recorded, as the contract always resides in the [masterchain](/learn/overviews/ton-blockchain#masterchain-blockchain-of-blockchains) (workchain -1). Therefore, the full address of the contract will be written as `-1:`. +::: + +## Param 1 + +This parameter is the address of the [Elector](/develop/smart-contracts/governance#elector) smart contract, responsible for appointing validators, distributing rewards, and voting on changes to blockchain parameters. + +## Param 2 + +This parameter represents the address of the System, on behalf of which new TONs are minted and sent as rewards for validating the blockchain. + +:::info +If parameter 2 is missing, parameter 0 is used instead (newly minted TONs come from the configuration smart contract). +::: + +## Param 3 + +This parameter is the address of the transaction fee collector. + +:::info +If parameter 3 is missing (as is the case at the time of writing), transaction fees are sent to the Elector smart contract (parameter 1). +::: + +## Param 4 + +This parameter is the address of the root DNS contract of the TON network. + +:::info +More detailed information can be found in the [TON DNS & Domains](/participate/web3/dns) article and in a more detailed original description [here](https://github.com/ton-blockchain/TEPs/blob/master/text/0081-dns-standard.md). +This contract is not responsible for selling .ton domains. +::: + +## Param 6 + +This parameter is responsible for minting fees of new currencies. + +:::info +Currently, minting additional currency is not implemented and does not work. The implementation and launch of the minter are planned. + +You can learn more about the issues and prospects in the [relevant article](/develop/research-and-development/minter-flow). +::: + +## Param 7 + +This parameter stores the volume of each of the additional currencies in circulation. Data is stored in the form of a [dictionary](/develop/data-formats/tl-b-types#hashmap-parsing-example) (binary tree; probably during TON's development this structure was mistakenly named hashmap) `extracurrency_id -> amount`, the amount is presented as `VarUint 32` - an integer from `0` to `2^248`. + +## Param 8 + +This parameter indicates the network version and additional capabilities supported by the validators. + +:::info +Validators are nodes in the blockchain network that are responsible for creating new blocks and verifying transactions. +::: + +- `version`: This field specifies the version. + +- `capabilities`: This field is a set of flags that are used to indicate the presence or absence of certain features or capabilities. + +Thus, when updating the network, validators will vote to change parameter 8. This way, the TON network can be updated without downtime. + +## Param 9 + +This parameter contains a list (binary tree) of mandatory parameters. It ensures that certain configuration parameters are always present and cannot be removed by a proposal to change the configuration until parameter 9 changes. + +## Param 10 + +This parameter represents a list (binary tree) of critical TON parameters, the change of which significantly affects the network, so more voting rounds are held. + +## Param 11 + +This parameter indicates under what conditions proposals to change the TON configuration are accepted. + +- `min_tot_rounds` - the minimum number of rounds before a proposal can be applied +- `max_tot_rounds` - the maximum number of rounds, upon reaching which the proposal will automatically be rejected +- `min_wins` - the required number of wins (3/4 of validators by the sum of the pledge must vote in favor) +- `max_losses` - the maximum number of losses, upon reaching which the proposal will automatically be rejected +- `min_store_sec` and `max_store_sec` determine the possible time interval during which the proposal will be stored +- `bit_price` and `cell_price` indicate the price of storing one bit or one cell of the proposal + +## Param 12 + +This parameter represents the configuration of a workchain in the TON Blockchain. Workchains in the TON Blockchain are designed as independent blockchains that can operate in parallel, allowing TON to scale and process a very large number of transactions and smart contracts. + +## Workchain configuration parameters + +- `enabled_since`: a UNIX timestamp of the moment this workchain was enabled; + +- `actual_min_split`: the minimum depth of the split (sharding) of this workchain, supported by validators; + +- `min_split`: the minimum depth of the split of this workchain, set by the configuration; + +- `max_split`: the maximum depth of the split of this workchain; + +- `basic`: a boolean flag (1 for true, 0 for false) indicating whether this workchain is basic (handles TON coins, smart contracts based on the TON Virtual Machine); + +- `active`: a boolean flag indicating whether this workchain is active at the moment; + +- `accept_msgs`: a boolean flag indicating whether this workchain is accepting messages at the moment; + +- `flags`: additional flags for the workchain (reserved, currently always 0); + +- `zerostate_root_hash` and `zerostate_file_hash`: hashes of the first block of the workchain; + +- `version`: version of the workchain; + +- `format`: the format of the workchain, which includes vm_version and vm_mode - the virtual machine used there. + +## Param 13 + +This parameter defines the cost of filing complaints about incorrect operation of validators in the [Elector](/develop/smart-contracts/governance#elector) contract. + +## Param 14 + +This parameter represents the reward for block creation in the TON Blockchain. Nanograms are nanoTON, thus, the reward for block creation in the masterchain equals 1.7 TON, and in the basic workchain - 1.0 TON (meanwhile, in the event of a workchain split, the block reward also splits: if there are two shardchains in the workchain, then the shard block reward will be 0.5 TON). + +## Param 15 + +This parameter contains the duration of different stages of elections and validators' work in the TON Blockchain. + +For each validation period, there is an `election_id` equal to the UNIX-format time at the start of the validation. +You can get the current `election_id` (if elections are ongoing) or the past one by invoking the Elector contract's respective get-methods `active_election_id` and `past_election_ids`. + +## Workchain configuration parameters + +- `validators_elected_for`: the number of seconds the elected set of validators perform their role (one round). + +- `elections_start_before`: how many seconds before the end of the current round the election process for the next period will start. + +- `elections_end_before`: how many seconds before the end of the current round the validators for the next round will be chosen. + +- `stake_held_for`: the period for which a validator's stake is held (for handling complaints) after the round expires. + +:::info +Each value in the arguments is determined by the `uint32` data type. +::: + +### Examples + +In the TON Blockchain, it is customary to conventionally divide validation periods into even and odd ones. These rounds follow one another. Since voting for the next round takes place during the previous one, a validator needs to divide funds into two pools to have the opportunity to participate in both rounds. + +#### Mainnet + +Current values: + +```python +constants = { + 'validators_elected_for': 65536, # 18.2 hours + 'elections_start_before': 32768, # 9.1 hours + 'elections_end_before': 8192, # 2.2 hours + 'stake_held_for': 32768 # 9.1 hours +} +``` + +Scheme: + +![image](/img/docs/blockchain-configs/config15-mainnet.png) + +#### How to calculate periods? + +Let `election_id = validation_start = 1600032768`. Then: + +```python +election_start = election_id - constants['elections_start_before'] = 1600032768 - 32768 = 1600000000 +election_end = delay_start = election_id - constants['elections_end_before'] = 1600032768 - 8192 = 1600024576 +hold_start = validation_end = election_id + constants['validators_elected_for'] = 1600032768 + 65536 = 1600098304 +hold_end = hold_start + constants['stake_held_for'] = 1600098304 + 32768 = 1600131072 +``` + +Thus, at the moment, the length of one round of one parity is `1600131072 - 1600000000 = 131072 seconds = 36.40888... hours` + +#### Testnet + +##### Current values: + +```python +constants = { + 'validators_elected_for': 7200, # 2 hours + 'elections_start_before': 2400, # 40 minutes + 'elections_end_before': 180, # 3 minutes + 'stake_held_for': 900 # 15 minutes +} +``` + +##### Scheme + +![image](/img/docs/blockchain-configs/config15-testnet.png) + +###### How to calculate periods? + +Let `election_id = validation_start = 160002400`. Then: + +```python +election_start = election_id - constants['elections_start_before'] = 160002400 - 2400 = 1600000000 +election_end = delay_start = election_id - constants['elections_end_before'] = 160002400 - 180 = 160002220 +hold_start = validation_end = election_id + constants['validators_elected_for'] = 160002400 + 7200 = 160009600 +hold_end = hold_start + constants['stake_held_for'] = 160009600 + 900 = 160010500 +``` + +Thus, at the moment, the length of one round of one parity is `160010500 - 1600000000 = 10500 seconds = 175 minutes = 2.91666... hours` + +## Param 16 + +This parameter represents the limits on the number of validators in the TON Blockchain. It is directly used by the Elector smart contract. + +### Configuration parameters for the number of validators for elections: + +- `max_validators`: This parameter represents the maximum number of validators that can participate in the network operation at any given time. + +- `max_main_validators`: This parameter represents the maximum number of masterchain validators. + +- `min_validators`: This parameter represents the minimum number of validators that must support the network operation. + +1. The maximum number of validators is greater than or equal to the maximum number of masterchain validators. +2. The maximum number of masterchain validators must be greater than or equal to the minimum number of validators. +3. The minimum number of validators must be no less than 1. + +## Param 17 + +This parameter represents the stake parameters configuration in the TON Blockchain. In many blockchain systems, especially those using the Proof-of-Stake or Delegated Proof-of-Stake consensus algorithm, cryptocurrency owners native to the network can "stake" their tokens to become validators and earn rewards. + +## Configuration parameters: + +- `min_stake`: This parameter represents the minimum amount of TONs that an interested party needs to stake to participate in the validation process. + +- `max_stake`: This parameter represents the maximum amount of TONs that an interested party can stake. + +- `min_total_stake`: This parameter represents the minimum total amount of TONs that must be held by the chosen set of validators. + +- `max_stake_factor`: This parameter is a multiplier indicating how many times the maximum effective stake (pledge) can exceed the minimum stake sent by any other validator. + +:::info +Each value in the arguments is determined by the `uint32` data type. +::: + +## Param 18 + +This parameter represents the configuration for determining the prices for data storage on the TON Blockchain. This serves as a measure to prevent spam and encourages network maintenance. + +### Dictionary of storage fee parameters: + +- `utime_since`: This parameter provides the initial Unix timestamp from which the specified prices apply. + +- `bit_price_ps` and `cell_price_ps`: These parameters represent the storage prices for one bit or one cell of information in the main workchains of the TON Blockchain for 65536 seconds + +- `mc_bit_price_ps` and `mc_cell_price_ps`: These parameters represent the prices for computational resources specifically in the TON masterchain for 65536 seconds + +:::info + +`utime_since` accepts values in the `uint32` data type. + +The rest accept values in the `uint64` data type. +::: + +## Param 20 and 21 + +These parameters define the cost of computations in the TON network. The complexity of any computation is estimated in gas units. + +- `flat_gas_limit` and `flat_gas_price`: A certain starting amount of gas is provided at a price of `flat_gas_price` (to offset the costs of launching the TON Virtual Machine). + +- `gas_price`: This parameter reflects the price of gas in the network, in nanotons per 65536 gas units. + +- `gas_limit`: This parameter represents the maximum amount of gas that can be consumed per transaction. + +- `special_gas_limit`: This parameter represents the limit on the amount of gas that can be consumed per transaction of a special (system) contract. + +- `gas_credit`: This parameter represents a credit in gas units that is provided to transactions for the purpose of checking an external message. + +- `block_gas_limit`: This parameter represents the maximum amount of gas that can be consumed within a single block. + +- `freeze_due_limit` and `delete_due_limit`: Limits of accumulated storage fees (in nanoTON) at which a contract is frozen and deleted, respectively. + +:::info +More about `gas_credit` and other parameters in the section with external messages [here](/develop/smart-contracts/guidelines/accept#external-messages). +::: + +## Param 22 and 23 + +These parameters set limits on the block, upon reaching which the block is finalized and the callback of the remaining messages (if any) is carried over to the next block. + +### Configuration parameters: + +- `bytes`: This section sets the limits on the block size in bytes. + +- `underload`: Underload is a state when the shard realizes that there is no load and is inclined to merge if a neighboring shard is willing. + +- `soft_limit`: Soft limit - when this limit is reached, internal messages stop being processed. + +- `hard_limit`: Hard limit - this is the absolute maximum size. + +- `gas`: This section sets the limits on the amount of gas that a block can consume. Gas in the context of blockchain is an indicator of computational work. The limits on underload, soft and hard limit work the same as for size in bytes. + +- `lt_delta`: This section sets the limits on the difference in logical time between the first and the last transaction. Logical time is a concept used in the TON Blockchain for ordering events. The limits on underload, soft and hard limit work the same as for size in bytes and gas. + +:::info +In case of insufficient load on the shard and, accordingly, the desire to merge with a neighbor, `soft_limit` defines a state above which internal (internal) messages stop being processed, but external (external) ones continue. External (external) messages are processed until a limit equal to `(soft_limit + hard_limit)/2 is reached`. +::: + +## Param 24 and 25 + +Parameter 24 represents the configuration for the cost of sending messages in the masterchain of the TON Blockchain. + +Parameter 25 represents the configuration for the cost of sending messages in all other cases. + +### Configuration parameters defining the costs of forwarding: + +- `lump_price`: This parameter means the base price for forwarding a message, regardless of its size or complexity. + +- `bit_price`: This parameter represents the cost per bit of message forwarding. + +- `cell_price`: This parameter reflects the cost of forwarding a message per cell. A cell is the basic unit of data storage on the TON Blockchain. + +- `ihr_price_factor`: This is a factor used to calculate the cost of immediate hypercube routing (IHR). + :::info + IHR is a method of message delivery in the TON Blockchain network, where messages are sent directly to the recipient's shard chain. + ::: + +- `first_frac`: This parameter defines the fraction of the remaining remainder that will be used for the first transition along the message route. + +- `next_frac`: This parameter defines the fraction of the remaining remainder that will be used for subsequent transitions along the message route. + +## Param 28 + +This parameter provides the configuration for the Catchain protocol in the TON Blockchain. Catchain is the lowest level consensus protocol used in TON to achieve agreement among validators. + +### Configuration parameters: + +- `flags`: A general field that can be used to set various binary parameters. In this case, it equals 0, which means that no specific flags are set. + +- `shuffle_mc_validators`: A Boolean value indicating whether to shuffle the masterchain validators or not. If this parameter is set to 1, the validators will be shuffled; otherwise, they will not. + +- `mc_catchain_lifetime`: The lifetime of masterchain catchain groups in seconds. + +- `shard_catchain_lifetime`: The lifetime of shardchain catchain groups in seconds. + +- `shard_validators_lifetime`: The lifetime of a shardchain validators group in seconds. + +- `shard_validators_num`: The number of validators in each shardchain validation group. + +## Param 29 + +This parameter provides the configuration for the consensus protocol above catchain ([Param 28](#param-28)) in the TON Blockchain. The consensus protocol is a crucial component of a blockchain network, and it ensures that all nodes agree on the state of the distributed ledger. + +### Configuration parameters: + +- `flags`: A general field that can be used to set various binary parameters. In this case, it equals 0, which means that no specific flags are set. + +- `new_catchain_ids`: A Boolean value indicating whether to generate new Catchain identifiers. If this parameter is set to 1, new identifiers will be generated. In this case, it is assigned the value of 1, which means that new identifiers will be generated. + +- `round_candidates`: The number of candidates to be considered in each round of the consensus protocol. Here, it is set to 3. + +- `next_candidate_delay_ms`: The delay in milliseconds before the right to generate a block candidate passes to the next validator. Here, it is set to 2000 ms (2 seconds). + +- `consensus_timeout_ms`: The timeout for block consensus in milliseconds. Here, it is set to 16000 ms (16 seconds). + +- `fast_attempts`: The number of "fast" attempts to reach consensus. Here, it is set to 3. + +- `attempt_duration`: The duration of each attempt at agreement. Here, it is set to 8. + +- `catchain_max_deps`: The maximum number of dependencies of a Catchain block. Here, it is set to 4. + +- `max_block_bytes`: The maximum size of a block in bytes. Here, it is set to 2097152 bytes (2 MB). + +- `max_collated_bytes`: The maximum size of serialized block correctness proofs in bytes. Here, it is set to 2097152 bytes (2 MB). + +- `proto_version`: The protocol version. Here, it is set to 2. + +- `catchain_max_blocks_coeff`: The coefficient limiting the rate of block generation in Catchain, [description](https://github.com/ton-blockchain/ton/blob/master/doc/catchain-dos.md). Here, it is set to 10000. + +## Param 31 + +This parameter represents the configuration of smart contract addresses from which no fees are charged for either gas or storage, and where tick-tok transactions can be created. The list usually includes governance contracts. The parameter is presented as a binary tree structure — a tree (HashMap 256), where the keys are a 256-bit representation of the address. Only addresses in the masterchain can be present in this list. + +## Param 32, 34 and 36 + +Lists of validators from the previous (32), current (34), and next (36) rounds. Parameter 36 is set from the end of the elections until the start of the round. + +### Configuration parameters: + +- `cur_validators`: This is the current list of validators. Validators are typically responsible for verifying transactions in a blockchain network. + +- `utime_since` and `utime_until`: These parameters provide the time period during which these validators are active. + +- `total` and `main`: These parameters provide the total number of validators and the number of validators validating the masterchain in the network. + +- `total_weight`: This adds up the weights of the validators. + +- `list`: A list of validators in the tree format `id->validator-data`: `validator_addr`, `public_key`, `weight`, `adnl_addr`: These parameters provide details about each validator - their 256 addresses in the masterchain, public key, weight, ADNL address (the address used at the network level of TON). + +## Param 40 + +This parameter defines the structure of the configuration for punishment for improper behavior (non-validation). In the absence of the parameter, the default fine size is 101 TON. + +## Configuration parameters: + +\*\* `MisbehaviourPunishmentConfig` \*\*: This data structure defines how improper behavior in the system is punished. + +It contains several fields: + +- `default_flat_fine`: This part of the fine does not depend on the stake size. + +- `default_proportional_fine`: This part of the fine is proportional to the validator's stake size. + +- `severity_flat_mult`: This is the multiplier applied to the `default_flat_fine` value for significant violations by the validator. + +- `severity_proportional_mult`: This is the multiplier applied to the `default_proportional_fine` value for significant violations by the validator. + +- `unpunishable_interval`: This parameter represents the period during which offenders are not punished to eliminate temporary network problems or other anomalies. + +- `long_interval`, `long_flat_mult`, `long_proportional_mult`: These parameters define a "long" period of time and multipliers for flat and proportional fines for improper behavior. + +- `medium_interval`, `medium_flat_mult`, `medium_proportional_mult`: Similarly, they define a "medium" period of time and multipliers for flat and proportional fines for improper behavior. + +## Param 43 + +This parameter pertains to various size limits and other characteristics of accounts and messages. + +### Configuration parameters: + +- `max_msg_bits`: maximum message size in bits. + +- `max_msg_cells`: maximum number of cells (a form of storage unit) a message can occupy. + +- `max_library_cells`: maximum number of cells that can be used for library cells. + +- `max_vm_data_depth`: maximum cell depth in messages and account state. + +- `max_ext_msg_size`: maximum external message size in bits. + +- `max_ext_msg_depth`: maximum external message depth. This could refer to the depth of the data structure within the message. + +- `max_acc_state_cells`: maximum number of cells that an account state can occupy. + +- `max_acc_state_bits`: maximum account state size in bits. + +If absent, the default parameters are taken: + +- `max_size` = 65535 +- `max_depth` = 512 +- `max_msg_bits` = 1 << 21 +- `max_msg_cells` = 1 << 13 +- `max_library_cells` = 1000 +- `max_vm_data_depth` = 512 +- `max_acc_state_cells` = 1 << 16 +- `max_acc_state_bits` = (1 << 16) \* 1023 + +:::info +You can view more details about the standard parameters [here](https://github.com/ton-blockchain/ton/blob/fc9542f5e223140fcca833c189f77b1a5ae2e184/crypto/block/mc-config.h#L379) in the source code. +::: + +## Param 44 + +This parameter defines the list of suspended addresses, which cannot be initialized until `suspended_until`. It only applies to yet uninitiated accounts. This is a measure for stabilizing the tokenomics (limiting early miners). If not set - there are no limitations. Each address is represented as an end node in this tree, and the tree-like structure allows to effectively check the presence or absence of an address in the list. + +:::info +The stabilization of the tokenomics is further described in the [official report](https://t.me/tonblockchain/178) of the "The Open Network" Telegram channel. +::: + +## Param 45 + +The list of precompiled contracts is stored in the masterchain config: + +``` +precompiled_smc#b0 gas_usage:uint64 = PrecompiledSmc; +precompiled_contracts_config#c0 list:(HashmapE 256 PrecompiledSmc) = PrecompiledContractsConfig; +_ PrecompiledContractsConfig = ConfigParam 45; +``` + +More details about precompiled contracts on [this page](develop/smart-contracts/core-contracts/precompiled). + +## Param 71 - 73 + +This parameter pertains to bridges for wrapping TON in other networks: + +- ETH-TON \*\* (71)\*\* +- BSC-TON \*\* (72) \*\* +- Polygon-TON \*\* (73) \*\* + +### Configuration parameters: + +- `bridge_address`: This is the bridge contract address that accepts TON to issue wrapped TON in other networks. + +- `oracle_multisig_address`: This is the bridge management wallet address. A multisig wallet is a type of digital wallet that requires signatures from multiple parties to authorize a transaction. It is often used to increase security. The oracles act as the parties. + +- `oracles`: list of oracles in the form of a tree `id->address` + +- `external_chain_address`: This is the bridge contract address in the corresponding external blockchain. + +## Param 79, 81 and 82 + +This parameter pertains to bridges for wrapping tokens from other networks into tokens on the TON network: + +- ETH-TON \*\* (79) \*\* +- BSC-TON \*\* (81) \*\* +- Polygon-TON \*\* (82) \*\* + +### Configuration parameters: + +- `bridge_address` and `oracles_address`: These are the blockchain addresses of the bridge and the bridge management contract (oracles multisig), respectively. + +- `oracles`: list of oracles in the form of a tree `id->address` + +- `state_flags`: State flag. This parameter is responsible for enabling/disabling separate bridge functions. + +- `prices`: This parameter contains a list or dictionary of prices for different operations or fees associated with the bridge, such as `bridge_burn_fee`, `bridge_mint_fee`, `wallet_min_tons_for_storage`, `wallet_gas_consumption`, `minter_min_tons_for_storage`, `discover_gas_consumption`. + +- `external_chain_address`: The bridge contract address in another blockchain. + +## Negative Parameters + +:::info +The difference between negative parameters and positive ones is the need for validators' verification; they usually do not have a specific assigned role. +::: + +## Next Steps + +After a deep dive into this article, it is strongly recommended that you take the time for a more detailed study of the following documents: + +- Original, but limited descriptions in [whitepaper.pdf](https://ton.org/whitepaper.pdf) and [tblkch.pdf](/tblkch.pdf). + +- [mc-config.h](https://github.com/ton-blockchain/ton/blob/fc9542f5e223140fcca833c189f77b1a5ae2e184/crypto/block/mc-config.h), [block.tlb](https://github.com/ton-blockchain/ton/blob/master/crypto/block/block.tlb) and [BlockMasterConfig Type](https://docs.evercloud.dev/reference/graphql-api/field_descriptions#blockmasterconfig-type). + +## 📖 See Also + +On this page, you can find active network configurations of the TON Blockchain: + +- Mainnet: https://ton.org/global-config.json +- Testnet: https://ton.org/testnet-global.config.json +- [Russian Version](https://github.com/delovoyhomie/description-config-for-TON-Blockchain/blob/main/Russian-version.md). From 907dfe1e31de2eb952de4cac6b6177f4df6fb450 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:33 +0800 Subject: [PATCH 107/219] New translations compile-swap.md (Chinese Simplified) --- .../current/develop/howto/compile-swap.md | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/compile-swap.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/compile-swap.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/compile-swap.md new file mode 100644 index 0000000000..b3ade6e9a8 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/compile-swap.md @@ -0,0 +1,50 @@ +# Compile TON on a low-memory machines + +:::caution +This section describes instructions and manuals for interacting with TON at a low level. +::: + +Creating a swap partitions to compile TON on a computer with low memory (less than 1GB). + +## Prerequisites + +During C++ compilation in the Linux system the following errors occur, resulting in a compilation abort: + +``` +C++: fatal error: Killed signal terminated program cc1plus compilation terminated. +``` + +## Solution + +This occurs due to a lack of memory and is solved by creating a swap partitions. + +```bash +# Create the partition path +sudo mkdir -p /var/cache/swap/ +# Set the size of the partition +# bs=64M is the block size, count=64 is the number of blocks, so the swap space size is bs*count=4096MB=4GB +sudo dd if=/dev/zero of=/var/cache/swap/swap0 bs=64M count=64 +# Set permissions for this directory +sudo chmod 0600 /var/cache/swap/swap0 +# Create the SWAP file +sudo mkswap /var/cache/swap/swap0 +# Activate the SWAP file +sudo swapon /var/cache/swap/swap0 +# Check if SWAP information is correct +sudo swapon -s +``` + +Command to delete swap partition: + +```bash +sudo swapoff /var/cache/swap/swap0 +sudo rm /var/cache/swap/swap0 +``` + +Free space command: + +```bash +sudo swapoff -a +#Detailed usage: swapoff --help +#View current memory usage: --swapoff: free -m +``` From a60a805ab0c253154bc4e77b13443762be646fbd Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:33 +0800 Subject: [PATCH 108/219] New translations compile.md (Chinese Simplified) --- .../current/develop/howto/compile.md | 233 ++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/compile.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/compile.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/compile.md new file mode 100644 index 0000000000..c073081198 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/compile.md @@ -0,0 +1,233 @@ +# Compile from Sources + +You can download prebuilt binaries [here](/develop/smart-contracts/environment/installation#1-download). + +If you still want to compile sources yourself, follow the instructions below. + +:::caution +This is a simplified quick build guide. + +If you are building for production and not for home use, it's better to use [autobuild scripts](https://github.com/ton-blockchain/ton/tree/master/.github/workflows). +::: + +## Common + +The software is likely to compile and work properly on most Linux systems. It should work on macOS and even Windows. + +1. Download the newest version of TON Blockchain sources available at the GitHub repository https://github.com/ton-blockchain/ton/: + +```bash +git clone --recurse-submodules https://github.com/ton-blockchain/ton.git +``` + +2. Install the newest versions of: + + - `make` + - `cmake` version 3.0.2 or later + - `g++` or `clang` (or another C++14-compatible compiler as appropriate for your operating system). + - OpenSSL (including C header files) version 1.1.1 or later + - `build-essential`, `zlib1g-dev`, `gperf`, `libreadline-dev`, `ccache`, `libmicrohttpd-dev`, `pkg-config`, `libsodium-dev`, `libsecp256k1-dev` + + On Ubuntu: + +```bash +apt update +sudo apt install build-essential cmake clang openssl libssl-dev zlib1g-dev gperf libreadline-dev ccache libmicrohttpd-dev pkg-config libsodium-dev libsecp256k1-dev +``` + +3. Suppose that you have fetched the source tree to directory `~/ton`, where `~` is your home directory, and that you have created an empty directory `~/ton-build`: + +```bash +mkdir ton-build +``` + +Then run the following in a terminal of Linux or MacOS: + +```bash +cd ton-build +export CC=clang +export CXX=clang++ +cmake -DCMAKE_BUILD_TYPE=Release ../ton && cmake --build . -j$(nproc) +``` + +:::warning +On MacOS Intel before next step we need maybe install `openssl@3` with `brew` or just link the lib: + +```zsh +brew install openssl@3 ninja libmicrohttpd pkg-config +``` + +Then need to inspect `/usr/local/opt`: + +```zsh +ls /usr/local/opt +``` + +Find `openssl@3` lib and export local variable: + +```zsh +export OPENSSL_ROOT_DIR=/usr/local/opt/openssl@3 +``` + +::: + +:::tip +If you are compiling on a computer with low memory (e.g., 1 Gb), don't forget to [create a swap partitions](/develop/howto/compile-swap). +::: + +## Download Global Config + +For tools like lite client you need to download the global network config. + +Download the newest configuration file from https://ton-blockchain.github.io/global.config.json for mainnet: + +```bash +wget https://ton-blockchain.github.io/global.config.json +``` + +or from https://ton-blockchain.github.io/testnet-global.config.json for testnet: + +```bash +wget https://ton-blockchain.github.io/testnet-global.config.json +``` + +## Lite Client + +To build a lite client, do [common part](/develop/howto/compile#common), [download the config](/develop/howto/compile#download-global-config), and then do: + +```bash +cmake --build . --target lite-client +``` + +Run the Lite Client with config: + +```bash +./lite-client/lite-client -C global.config.json +``` + +If everything was installed successfully, the Lite Client will connect to a special server (a full node for the TON Blockchain Network) and will send some queries to the server. +If you indicate a writeable "database" directory as an extra argument to the client, it will download and save the block and the state corresponding to the newest masterchain block: + +```bash +./lite-client/lite-client -C global.config.json -D ~/ton-db-dir +``` + +Basic help info can be obtained by typing `help` into the Lite Client. Type `quit` or press `Ctrl-C` to exit. + +## FunC + +To build FunC compiler from source code, do [common part](/develop/howto/compile#common) described above and then: + +```bash +cmake --build . --target func +``` + +To compile FunC smart contract: + +```bash +func -o output.fif -SPA source0.fc source1.fc ... +``` + +## Fift + +To build Fift compiler from source code, do [common part](/develop/howto/compile#common) described above and then: + +```bash +cmake --build . --target fift +``` + +To run Fift script: + +```bash +fift -s script.fif script_param0 script_param1 .. +``` + +## Tonlib-cli + +To build tonlib-cli, do [common part](/develop/howto/compile#common), [download the config](/develop/howto/compile#download-global-config) and then do: + +```bash +cmake --build . --target tonlib-cli +``` + +Run the tonlib-cli with config: + +```bash +./tonlib/tonlib-cli -C global.config.json +``` + +Basic help info can be obtained by typing `help` into the tonlib-cli. Type `quit` or press `Ctrl-C` to exit. + +## RLDP-HTTP-Proxy + +To build rldp-http-proxy, do [common part](/develop/howto/compile#common), [download the config](/develop/howto/compile#download-global-config) and then do: + +```bash +cmake --build . --target rldp-http-proxy +``` + +The Proxy binary will be located as: + +```bash +rldp-http-proxy/rldp-http-proxy +``` + +## generate-random-id + +To build generate-random-id, do [common part](/develop/howto/compile#common) and then do: + +```bash +cmake --build . --target generate-random-id +``` + +The binary will be located as: + +```bash +utils/generate-random-id +``` + +## storage-daemon + +To build storage-daemon and storage-daemon-cli, do [common part](/develop/howto/compile#common) and then do: + +```bash +cmake --build . --target storage-daemon storage-daemon-cli +``` + +The binary will be located at: + +```bash +storage/storage-daemon/ +``` + +# Compile old TON versions + +TON releases: https://github.com/ton-blockchain/ton/tags + +```bash +git clone https://github.com/ton-blockchain/ton.git +cd ton +# git checkout for example checkout func-0.2.0 +git checkout func-0.2.0 +git submodule update --init --recursive +cd .. +mkdir ton-build +cd ton-build +cmake ../ton +# build func 0.2.0 +cmake --build . --target func +``` + +## Compile old versions on Apple M1: + +TON supports Apple M1 from 11 Jun 2022 ([Add apple m1 support (#401)](https://github.com/ton-blockchain/ton/commit/c00302ced4bc4bf1ee0efd672e7c91e457652430) commit). + +To compile older TON revisions on Apple M1: + +1. Update RocksDb submodule to 6.27.3 + ```bash + cd ton/third-party/rocksdb/ + git checkout fcf3d75f3f022a6a55ff1222d6b06f8518d38c7c + ``` + +2. Replace root `CMakeLists.txt` by https://github.com/ton-blockchain/ton/blob/c00302ced4bc4bf1ee0efd672e7c91e457652430/CMakeLists.txt From fd49b986e6e8f435831ef9ba50643da7b55a7bfd Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:34 +0800 Subject: [PATCH 109/219] New translations config-params.md (Chinese Simplified) --- .../current/develop/howto/config-params.md | 338 ++++++++++++++++++ 1 file changed, 338 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/config-params.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/config-params.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/config-params.md new file mode 100644 index 0000000000..b2a3b71fb2 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/config-params.md @@ -0,0 +1,338 @@ +# Changing the Parameters + +The aim of this document is to provide a basic explanation of configuration parameters of TON Blockchain, and to give step-by-step instructions for changing these parameters by a consensus of a majority of validators. + +We assume that the reader is already familiar with [Fift](/develop/fift/overview) and the [Lite Client](/participate/nodes/lite-client), as explained in [FullNode-HOWTO (low-level)](/participate/nodes/full-node), and [Validator-HOWTO (low-level)](/participate/nodes/validator) in the sections where validators' voting for the configuration proposals is described. + +## 1. Configuration parameters + +The **configuration parameters** are certain values that affect the behavior of validators and/or fundamental smart contracts of TON Blockchain. The current values of all configuration parameters are stored as a special part of the masterchain state and are extracted from the current masterchain state when needed. Therefore, it makes sense to speak of the values of the configuration parameters with respect to a certain masterchain block. Each shardchain block contains a reference to the latest known masterchain block; the values from the corresponding masterchain state are assumed to be active for this shardchain block and are used during its generation and validation. For masterchain blocks, the state of the previous masterchain block is used to extract the active configuration parameters. Therefore, even if one tries to change some configuration parameters inside a masterchain block, the changes will become active only for the next masterchain block. + +Each configuration parameter is identified by a signed 32-bit integer index, called **configuration parameter index** or simply **index**. The value of a configuration parameter is always a Cell. Some configuration parameters may be missing; then it is sometimes assumed that the value of this parameter is `Null`. There also is a list of **mandatory** configuration parameters that must be always present; this list is stored in configuration parameter `#10`. + +All configuration parameters are combined into a **configuration dictionary** - a Hashmap with signed 32-bit keys (configuration parameter indices) and values consisting of exactly one cell reference. In other words, a configuration dictionary is a value of TL-B type (`HashmapE 32 ^Cell`). In fact, the collection of all configuration parameters is stored in the masterchain state as a value of TL-B type `ConfigParams`: + +``` +_ config_addr:bits256 config:^(Hashmap 32 ^Cell) = ConfigParams; +``` + +We see that, apart from the configuration dictionary, `ConfigParams` contains `config_addr`—256-bit address of the configuration smart contract in the masterchain. More details on the configuration smart contract will be provided later. + +The configuration dictionary containing the active values of all configuration parameters is available via special TVM register `c7` to all smart contracts when their code is executed in a transaction. More precisely, when a smart contract is executed, `c7` is initialized by a Tuple, the only element of which is a Tuple with several "context" values useful for the execution of the smart contract, such as the current Unix time (as registered in the block header). The tenth entry of this Tuple (i.e., the one with zero-based index 9) contains a Cell representing the configuration dictionary. Therefore, it can be accessed by means of TVM instructions `PUSH c7; FIRST; INDEX 9` or by the equivalent instruction `CONFIGROOT`. In fact, special TVM instructions `CONFIGPARAM` and `CONFIGOPTPARAM` combine the previous actions with a dictionary lookup, returning any configuration parameter by its index. We refer to the TVM documentation for more details on these instructions. What is relevant here is that all configuration parameters are easily accessible from all smart contracts (masterchain or shardchain), and smart contracts may inspect them and use them to perform specific checks. For instance, a smart contract might extract workchain data storage prices from a configuration parameter to compute the price for storing a chunk of user-provided data. + +The values of configuration parameters are not arbitrary. In fact, if the configuration parameter index `i` is non-negative, then the value of this parameter must be a valid value of TL-B type (`ConfigParam i`). This restriction is enforced by the validators, which will not accept changes to configuration parameters with non-negative indices unless they are valid values of the corresponding TL-B type. + +Therefore, the structure of such parameters is determined in source file `crypto/block/block.tlb`, where (`ConfigParam i`) is defined for different values of `i`. For instance, + +``` +_ config_addr:bits256 = ConfigParam 0; +_ elector_addr:bits256 = ConfigParam 1; +_ dns_root_addr:bits256 = ConfigParam 4; // root TON DNS resolver + +capabilities#c4 version:uint32 capabilities:uint64 = GlobalVersion; +_ GlobalVersion = ConfigParam 8; // all zero if absent +``` + +We see that the configuration parameter `#8` contains a Cell with no references and exactly 104 data bits. The first four bits must be `11000100`, then 32 bits with the currently enabled "global version" are stored, and a 64-bit integer with flags corresponding to currently enabled capabilities follows. A more detailed description of all configuration parameters will be provided in an appendix to the TON Blockchain documentation; for now, one can inspect the TL-B scheme in `crypto/block/block.tlb` and check how different parameters are used in the validator sources. + +In contrast with configuration parameters with non-negative indices, configuration parameters with negative indices can contain arbitrary values. At least, no restrictions on their values are enforced by the validators. Therefore, they can be used to store important information (such as the Unix time when certain smart contracts must start operating) that is not crucial for the block generation but is used by some of the fundamental smart contracts. + +## 2. Changing configuration parameters + +We have already explained that the current values of configuration parameters are stored in a special portion of the masterchain state. How do they ever get changed? + +In fact, there is a special smart contract residing in the masterchain called the **configuration smart contract**. Its address is determined by the `config_addr` field in `ConfigParams`, which we have described before. The first cell reference in its data must contain an up-to-date copy of all configuration parameters. When a new masterchain block is generated, the configuration smart contract is looked up by its address, `config_addr`, and the new configuration dictionary is extracted from the first cell reference of its data. After some validity checks (such as verifying that any value with a non-negative 32-bit index `i` is indeed a valid value of TL-B type (`ConfigParam i`)) the validator copies this new configuration dictionary into the portion of the masterchain containing ConfigParams. This is performed after all transactions have been created, so only the final version of the new configuration dictionary stored in the configuration smart contract is inspected. If the validity checks fail, then the "true" configuration dictionary is left unchanged. In this way, the configuration smart contract cannot install invalid values of configuration parameters. If the new configuration dictionary coincides with the current configuration dictionary, then no checks are performed and no changes are made. + +In this way, all changes to configuration parameters are performed by the configuration smart contract, and it is its code that determines the rules for changing configuration parameters. Currently, the configuration smart contract supports two modes for changing configuration parameters: + +1. By means of an external message signed by a specific private key, which corresponds to a public key stored in the data of the configuration smart contract. This is the method employed in the public testnet and probably in the smaller private test networks controlled by one entity, because it enables the operator to easily change the values of any configuration parameters. Note that this public key may be changed by a special external message signed by an old key, and that if it is changed to zero, then this mechanism is disabled. Therefore, one might use it for fine-tuning immediately after the launch and then disable it for good. +2. By means of creating "configuration proposals" that are subsequently voted for or against by validators. Typically, a configuration proposal has to collect votes from more than 3/4 of all validators (by weight), and not only in one round but in several rounds (i.e., several consecutive sets of validators have to confirm the proposed parameter change). This is the distributed governance mechanism to be used by the TON Blockchain Mainnet. + +We would like to describe the second way of changing configuration parameters in more detail. + +## 3. Creating configuration proposals + +A new **configuration proposal** contains the following data: + +- index of the configuration parameter to be changed +- new value of the configuration parameter (or Null, if it is to be deleted) +- expiration Unix time of the proposal +- flag indicating whether the proposal is **critical** or not +- optional **old value hash** with the cell hash of the current value (the proposal can be activated only if the current value has the indicated hash) + +Anybody with a wallet in the masterchain can create a new configuration proposal, provided he pays an adequate fee. However, only validators can vote for or against existing configuration proposals. + +Note that there are **critical** and **ordinary** configuration proposals. A critical configuration proposal can change any configuration parameter, including one of the so-called critical ones (the list of critical configuration parameters is stored in configuration parameter `#10`, which is itself critical). However, creating critical configuration proposals is more expensive, and they usually need to collect more validator votes in more rounds (the precise voting requirements for ordinary and critical configuration proposals are stored in critical configuration parameter `#11`). On the other hand, ordinary configuration proposals are cheaper, but they cannot change the critical configuration parameters. + +In order to create a new configuration proposal, one first has to generate a BoC (bag-of-cells) file containing the proposed new value. The exact way of doing this depends on the configuration parameter being changed. For instance, if we want to create a parameter `-239` containing UTF-8 string "TEST" (i.e., `0x54455354`), we could create `config-param-239.boc` as follows: invoke Fift and then type + +``` + 2 boc+>B "config-param-239.boc" B>file +bye +``` + +As a result, a 21-byte file `config-param-239.boc` will be created, containing the serialization of the required value. + +For more sophisticated cases, and especially for configuration parameters with non-negative indices this straightforward approach is not easily applicable. We recommend using `create-state` (available as `crypto/create-state` in the build directory) instead of `fift` and copying and editing suitable portions of the source files `crypto/smartcont/gen-zerostate.fif` and `crypto/smartcont/CreateState.fif`, which are usually employed to create the zero state (corresponding to the "genesis block" of other blockchain architectures) of TON Blockchain. + +Consider, for instance, configuration parameter `#8`, which contains the currently-enabled global blockchain version and capabilities: + +``` +capabilities#c4 version:uint32 capabilities:uint64 = GlobalVersion; +_ GlobalVersion = ConfigParam 8; +``` + +We can inspect its current value by running the lite client and typing `getconfig 8`: + +``` +> getconfig 8 +... +ConfigParam(8) = ( + (capabilities version:1 capabilities:6)) + +x{C4000000010000000000000006} +``` + +Now suppose that we want to enable the capability represented by bit `#3` (`+8`), which is `capReportVersion` (when enabled, this capability forces all collators to report their supported versions and capabilities in the block headers of the blocks they generate). Therefore, we want to have `version=1` and `capabilities=14`. In this example, we can still guess the correct serialization and create the BoC file directly by typing in Fift. + +``` +x{C400000001000000000000000E} s>c 2 boc+>B "config-param8.boc" B>file +``` + +(A 30-byte file `config-param8.boc` containing the desired value is created as a result.) + +However, in more complicated cases this might not be an option, so let's do this example differently. Namely, we can inspect the source files `crypto/smartcont/gen-zerostate.fif` and `crypto/smartcont/CreateState.fif` for relevant portions. + +``` +// version capabilities -- +{ 8 config! } : config.version! +1 constant capIhr +2 constant capCreateStats +4 constant capBounceMsgBody +8 constant capReportVersion +16 constant capSplitMergeTransactions +``` + +and + +``` +// version capabilities +1 capCreateStats capBounceMsgBody or capReportVersion or config.version! +``` + +We see that `config.version!` without the last `8 config!` essentially does what we need, so we can create a temporary Fift script, for example, `create-param8.fif`: + +``` +#!/usr/bin/fift -s +"TonUtil.fif" include + +1 constant capIhr +2 constant capCreateStats +4 constant capBounceMsgBody +8 constant capReportVersion +16 constant capSplitMergeTransactions +{ } : prepare-param8 + +// create new value for config param #8 +1 capCreateStats capBounceMsgBody or capReportVersion or prepare-param8 +// check the validity of this value +dup 8 is-valid-config? not abort"not a valid value for chosen configuration parameter" +// print +dup ."Serialized value = " B $1 tuck B>file +."(Saved into file " type .")" cr +``` + +Now if we run `fift -s create-param8.fif config-param8.boc` or, even better, `crypto/create-state -s create-param8.fif config-param8.boc` (from the build directory) we see the following output: + +``` +Serialized value = x{C400000001000000000000000E} +(Saved into file config-param8.boc) +``` + +and we obtain a 30-byte file `config-param8.boc` with the same content as before. + +Once we have a file with the desired value of the configuration parameter, we invoke the script `create-config-proposal.fif` that can be found in the directory `crypto/smartcont` of the source tree with suitable arguments. Again, we recommend using `create-state` (available as `crypto/create-state` from the build directory) instead of `fift`, because it is a special extended version of Fift that is able to do more blockchain-related validity checks: + +``` +$ crypto/create-state -s create-config-proposal.fif 8 config-param8.boc -x 1100000 + + +Loading new value of configuration parameter 8 from file config-param8.boc +x{C400000001000000000000000E} + +Non-critical configuration proposal will expire at 1586779536 (in 1100000 seconds) +Query id is 6810441749056454664 +resulting internal message body: x{6E5650525E838CB0000000085E9455904_} + x{F300000008A_} + x{C400000001000000000000000E} + +B5EE9C7241010301002C0001216E5650525E838CB0000000085E9455904001010BF300000008A002001AC400000001000000000000000ECD441C3C +(a total of 104 data bits, 0 cell references -> 59 BoC data bytes) +(Saved to file config-msg-body.boc) +``` + +We have obtained the body of an internal message to be sent to the configuration smart contract with a suitable amount of Toncoin from any (wallet) smart contract residing in the masterchain. The address of the configuration smart contract may be obtained by typing `getconfig 0` in the lite client: + +``` +> getconfig 0 +ConfigParam(0) = ( config_addr:x5555555555555555555555555555555555555555555555555555555555555555) +x{5555555555555555555555555555555555555555555555555555555555555555} +``` + +We see that the address of the configuration smart contract is `-1:5555...5555`. By running suitable get-methods of this smart contract, we can find out the required payment for creating this configuration proposal: + +``` +> runmethod -1:5555555555555555555555555555555555555555555555555555555555555555 proposal_storage_price 0 1100000 104 0 + +arguments: [ 0 1100000 104 0 75077 ] +result: [ 2340800000 ] +remote result (not to be trusted): [ 2340800000 ] +``` + +The parameters to get-method `proposal_storage_price` are the critical flag (0 in this case), the time interval during which this proposal will be active (1.1 Megaseconds), the total amount of bits (104) and cell references (0) in the data. The latter two quantities can be seen in the output of `create-config-proposal.fif`. + +We see that one has to pay 2.3408 Toncoin to create this proposal. It is better to add at least 1.5 Tonoin to the message to pay for the processing fees, so we are going to send 4 Toncoin along with the request (all excess Toncoin will be returned back). Now we use `wallet.fif` (or the corresponding Fift script for the wallet we are using) to create a transfer from our wallet to the configuration smart contract carrying 4 Toncoin and the body from `config-msg-body.boc`. This usually looks like: + +``` +$ fift -s wallet.fif my-wallet -1:5555555555555555555555555555555555555555555555555555555555555555 31 4. -B config-msg-body.boc + +Transferring GR$4. to account kf9VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQft = -1:5555555555555555555555555555555555555555555555555555555555555555 seqno=0x1c bounce=-1 +Body of transfer message is x{6E5650525E835154000000085E9293944_} + x{F300000008A_} + x{C400000001000000000000000E} + +signing message: x{0000001C03} + x{627FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA773594000000000000000000000000000006E5650525E835154000000085E9293944_} + x{F300000008A_} + x{C400000001000000000000000E} + +resulting external message: x{89FE000000000000000000000000000000000000000000000000000000000000000007F0BAA08B4161640FF1F5AA5A748E480AFD16871E0A089F0F017826CDC368C118653B6B0CEBF7D3FA610A798D66522AD0F756DAEECE37394617E876EFB64E9800000000E01C_} + x{627FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA773594000000000000000000000000000006E5650525E835154000000085E9293944_} + x{F300000008A_} + x{C400000001000000000000000E} + +B5EE9C724101040100CB0001CF89FE000000000000000000000000000000000000000000000000000000000000000007F0BAA08B4161640FF1F5AA5A748E480AFD16871E0A089F0F017826CDC368C118653B6B0CEBF7D3FA610A798D66522AD0F756DAEECE37394617E876EFB64E9800000000E01C010189627FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA773594000000000000000000000000000006E5650525E835154000000085E9293944002010BF300000008A003001AC400000001000000000000000EE1F80CD3 +(Saved to file wallet-query.boc) +``` + +Now we send the external message `wallet-query.boc` to the blockchain with the aid of the lite client. + +``` +> sendfile wallet-query.boc +.... +external message status is 1 +``` + +After waiting for some time, we can inspect the incoming messages of our wallet to check for response messages from the configuration smart contract, or, if we feel lucky, simply inspect the list of all active configuration proposals by means of method `list_proposals` of the configuration smart contract. + +``` +> runmethod -1:5555555555555555555555555555555555555555555555555555555555555555 list_proposals +... +arguments: [ 107394 ] +result: [ ([64654898543692093106630260209820256598623953458404398631153796624848083036321 [1586779536 0 [8 C{FDCD887EAF7ACB51DA592348E322BBC0BD3F40F9A801CB6792EFF655A7F43BBC} -1] 112474791597373109254579258586921297140142226044620228506108869216416853782998 () 864691128455135209 3 0 0]]) ] +remote result (not to be trusted): [ ([64654898543692093106630260209820256598623953458404398631153796624848083036321 [1586779536 0 [8 C{FDCD887EAF7ACB51DA592348E322BBC0BD3F40F9A801CB6792EFF655A7F43BBC} -1] 112474791597373109254579258586921297140142226044620228506108869216416853782998 () 864691128455135209 3 0 0]]) ] +... caching cell FDCD887EAF7ACB51DA592348E322BBC0BD3F40F9A801CB6792EFF655A7F43BBC +``` + +We see that the list of all active configuration proposals consists of exactly one entry represented by a pair. + +``` +[6465...6321 [1586779536 0 [8 C{FDCD...} -1] 1124...2998 () 8646...209 3 0 0]] +``` + +The first number `6465..6321` is the unique identifier of the configuration proposal, equal to its 256-bit hash. The second component of this pair is a Tuple describing the status of this configuration proposal. The first component of this Tuple is the expiration Unix time of the configuration proposal (`1586779546`). The second component (`0`) is the criticality flag. Next comes the configuration proposal proper, described by the triple `[8 C{FDCD...} -1]`, where `8` is the index of the configuration parameter to be modified, `C{FDCD...}` is the cell with the new value (represented by the hash of this cell), and `-1` is the optional hash of the old value of this parameter (`-1` means that this hash has not been specified). Next we see a large number `1124...2998` representing the identifier of the current validator set, then an empty list `()` representing the set of all currently active validators that have voted for this proposal so far, then `weight_remaining` equal to `8646...209` - a number that is positive if the proposal has not yet collected enough validator votes in this round, and negative otherwise. Then we see three numbers: `3 0 0`. These numbers are `rounds_remaining` (this proposal will survive at most three rounds, i.e., changes of the current validator set), `wins` (the count of rounds where the proposal collected votes of more than 3/4 of all validators by weight), and `losses` (the count of rounds where the proposal failed to collect 3/4 of all validator votes). + +We can inspect the proposed value for configuration parameter `#8` by asking the lite-client to expand cell `C{FDCD...}` using its hash `FDCD...` or a sufficiently long prefix of this hash to uniquely identify the cell in question: + +``` +> dumpcell FDC +C{FDCD887EAF7ACB51DA592348E322BBC0BD3F40F9A801CB6792EFF655A7F43BBC} = + x{C400000001000000000000000E} +``` + +We see that the value is `x{C400000001000000000000000E}`, which is indeed the value we have embedded into our configuration proposal. We can even ask the lite client to display this Cell as a value of TL-B type (`ConfigParam 8`). + +``` +> dumpcellas ConfigParam8 FDC +dumping cells as values of TLB type (ConfigParam 8) +C{FDCD887EAF7ACB51DA592348E322BBC0BD3F40F9A801CB6792EFF655A7F43BBC} = + x{C400000001000000000000000E} +( + (capabilities version:1 capabilities:14)) +``` + +This is especially useful when we consider configuration proposals created by other people. + +Note that the configuration proposal is henceforth identified by its 256-bit hash -- the huge decimal number `6465...6321`. We can inspect the current status of a specific configuration proposal by running the get-method `get_proposal` with the only argument equal to the identifier of the configuration proposal: + +``` +> runmethod -1:5555555555555555555555555555555555555555555555555555555555555555 get_proposal 64654898543692093106630260209820256598623953458404398631153796624848083036321 +... +arguments: [ 64654898543692093106630260209820256598623953458404398631153796624848083036321 94347 ] +result: [ [1586779536 0 [8 C{FDCD887EAF7ACB51DA592348E322BBC0BD3F40F9A801CB6792EFF655A7F43BBC} -1] 112474791597373109254579258586921297140142226044620228506108869216416853782998 () 864691128455135209 3 0 0] ] +``` + +We obtain essentially the same result as before, but for only one configuration proposal and without the identifier of the configuration proposal at the beginning. + +## 4. Voting for configuration proposals + +Once a configuration proposal is created, it is supposed to collect votes from more than 3/4 of all current validators (by weight, i.e., by stake) in the current round and maybe in several subsequent rounds (elected validator sets). In this way, the decision to change a configuration parameter must be approved by a significant majority not only of the current set of validators but also of several subsequent sets of validators. + +Voting for a configuration proposal is possible only for current validators, listed (with their permanent public keys) in configuration parameter `#34`. The process is approximately as follows: + +- The operator of a validator looks up `val-idx`, the (0-based) index of his validator in the current set of validators as stored in configuration parameter `#34`. +- The operator invokes a special Fift script `config-proposal-vote-req.fif` found in directory `crypto/smartcont` of the source tree, indicating `val-idx` and `config-proposal-id` as its arguments: + +``` + $ fift -s config-proposal-vote-req.fif -i 0 64654898543692093106630260209820256598623953458404398631153796624848083036321 + Creating a request to vote for configuration proposal 0x8ef1603180dad5b599fa854806991a7aa9f280dbdb81d67ce1bedff9d66128a1 on behalf of validator with index 0 + 566F744500008EF1603180DAD5B599FA854806991A7AA9F280DBDB81D67CE1BEDFF9D66128A1 + Vm90RQAAjvFgMYDa1bWZ-oVIBpkaeqnygNvbgdZ84b7f-dZhKKE= + Saved to file validator-to-sign.req +``` + +- After that, the vote request has to be signed by the current validator's private key, using `sign 566F744...28A1` in `validator-engine-console` connected to the validator. This process is similar to that described in [Validator-HOWTO](/participate/nodes/validator) for participating in validator elections, but this time the currently active key has to be used. +- Next, another script `config-proposal-signed.fif` has to be invoked. It has similar arguments to `config-proposal-req.fif`, but it expects two extra arguments: the base64 representation of the public key used to sign the vote request, and the base64 representation of the signature itself. Again, this is quite similar to the process described in [Validator-HOWTO](/participate/nodes/validator). +- In this way, the file `vote-msg-body.boc` containing the body of an internal message carrying a signed vote for this configuration proposal is created. +- After that, `vote-msg-body.boc` has to be carried in an internal message from any smart contract residing in the masterchain (typically, the controlling smart contract of the validator will be used) along with a small amount of Toncoin for processing (typically, 1.5 Toncoin should suffice). This is again completely similar to the procedure employed during validator elections. This is typically achieved by means of running: + +``` +$ fift -s wallet.fif my_wallet_id -1:5555555555555555555555555555555555555555555555555555555555555555 1 1.5 -B vote-msg-body.boc +``` + +(if a simple wallet is used to control the validator) and then sending the resulting file `wallet-query.boc` from the lite client: + +``` +> sendfile wallet-query.boc +``` + +You can monitor answer messages from the configuration smart contract to the controlling smart contract to learn the status of your voting queries. Alternatively, you can inspect the status of the configuration proposal by means of get-method `show_proposal` of the configuration smart contract: + +``` +> runmethod -1:5555555555555555555555555555555555555555555555555555555555555555 get_proposal 64654898543692093106630260209820256598623953458404398631153796624848083036321 +... +arguments: [ 64654898543692093106630260209820256598623953458404398631153796624848083036321 94347 ] +result: [ [1586779536 0 [8 C{FDCD887EAF7ACB51DA592348E322BBC0BD3F40F9A801CB6792EFF655A7F43BBC} -1] 112474791597373109254579258586921297140142226044620228506108869216416853782998 (0) 864691128455135209 3 0 0] ] +``` + +This time, the list of indices of validators that voted for this configuration proposal should be non-empty, and it should contain the index of your validator. In this example, this list is (`0`), meaning that only the validator with index `0` in configuration parameter `#34` has voted. If the list becomes large enough, the last-but-one integer (the first zero in `3 0 0`) in the proposal status will increase by one, indicating a new win by this proposal. If the number of wins becomes greater than or equal to the value indicated in configuration parameter `#11`, then the configuration proposal is automatically accepted and the proposed changes become effective immediately. On the other hand, when the validator set changes, then the list of validators that have already voted becomes empty, the value of `rounds_remaining` (three in `3 0 0`) is decreased by one, and if it becomes negative, the configuration proposal is destroyed. If it is not destroyed, and if it did not win in this round, then the number of losses (the second zero in `3 0 0`) is increased. If it becomes larger than a value specified in configuration parameter `#11`, then the configuration proposal is discarded. As a result, all validators who did not vote in a round implicitly voted against. + +## 5. An automated way for voting on configuration proposals + +Similarly to the automation provided by command `createelectionbid` of `validator-engine-console` for participating in validator elections, `validator-engine` and `validator-engine-console` offer an automated way of performing most of the steps explained in the previous section, producing a `vote-msg-body.boc` ready to be used with the controlling wallet. In order to use this method, you must install the Fift scripts `config-proposal-vote-req.fif` and `config-proposal-vote-signed.fif` into the same directory that the validator-engine uses to look up `validator-elect-req.fif` and `validator-elect-signed.fif` as explained in Section 5 of [Validator-HOWTO](/participate/nodes/validator). After that, you simply run + +``` + createproposalvote 64654898543692093106630260209820256598623953458404398631153796624848083036321 vote-msg-body.boc +``` + +in validator-engine-console to create `vote-msg-body.boc` with the body of the internal message to be sent to the configuration smart contract. + +## 6. Upgrading the code of configuration smart contract and the elector smart contract + +It may happen that the code of the configuration smart contract itself or the code of the elector smart contract has to be upgraded. To this end, the same mechanism as described above is used. The new code is to be stored in the only reference of a value cell, and this value cell has to be proposed as the new value of configuration parameter `-1000` (for upgrading the configuration smart contract) or `-1001` (for upgrading the elector smart contract). These parameters pretend to be critical, so a lot of validator votes are needed to change the configuration smart contract (this is akin to adopting a new constitution). We expect that such changes will involve first testing them in a test network, and discussing the proposed changes in public forums before each validator operator decides to vote for or against the proposed changes. + +Alternatively, critical configuration parameters `0` (the address of the configuration smart contract) or `1` (the address of the elector smart contract) can be changed to other values, that must correspond to already existing and correctly initialized smart contracts. In particular, the new configuration smart contract must contain a valid configuration dictionary in the first reference of its persistent data. Since it is not so easy to correctly transfer changing data (such as the list of active configuration proposals, or the previous and current participant lists of validator elections) between different smart contracts, in most cases it is better to upgrade the code of an existing smart contract rather than to change the configuration smart contract address. + +There are two auxiliary scripts used to create such configuration proposals to upgrade the code of the configuration or elector smart contract. Namely, `create-config-upgrade-proposal.fif` loads a Fift assembler source file (`auto/config-code.fif` by default, corresponding to the code automatically generated by FunC compiler from `crypto/smartcont/config-code.fc`) and creates the corresponding configuration proposal (for configuration parameter `-1000`). Similarly, `create-elector-upgrade-proposal.fif` loads a Fift assembler source file (`auto/elector-code.fif` by default) and uses it to create a configuration proposal for configuration parameter `-1001`. In this way, creating configuration proposals to upgrade one of these two smart contracts should be very simple. However, one should also publish the modified FunC source of the smart contract, the exact version of the FunC compiler used to compile it, so that all validators (or rather their operators) would be able to reproduce the code in the configuration proposal (and compare the hashes) and study and discuss the source code and the changes in this code before deciding to vote for or against the proposed changes. From c85f94c407a33f3b249c3c634f8964d553aaa7b1 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:35 +0800 Subject: [PATCH 110/219] New translations faq.md (Chinese Simplified) --- .../current/develop/howto/faq.md | 257 ++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/faq.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/faq.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/faq.md new file mode 100644 index 0000000000..35249ed558 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/faq.md @@ -0,0 +1,257 @@ +# Frequently Asked Questions + +This section covers the most popular questions about TON Blockchain. + +## Overview + +### Could you share a brief overview of TON? + +- [Introduction to The Open Network](/learn/introduction) +- [The TON Blockchain is based on PoS consensus](https://blog.ton.org/the-ton-blockchain-is-based-on-pos-consensus) +- [TON Whitepapers](/learn/docs) + +### What are some of the main similarities and differences to EVM blockchains? + +- [Ethereum to TON](/learn/introduction#ethereum-to-ton) +- [Comparison of TON, Solana and Ethereum 2.0](https://ton.org/comparison_of_blockchains.pdf) + +### Does TON have a test environment? + +- [Testnet](/develop/smart-contracts/environment/testnet) + +## Block + +### What is the RPC method used to retrieve block information? + +Blocks produced by Validators. Existing blocks available via Liteservers. Liteservers accessible via Lite Сlients. On top of Lite Сlient built 3rd-party tools like wallets, explorers, dapps, etc. + +- To access the Lite Client core check out this section of our GitHub: [ton-blockchain/tonlib](https://github.com/ton-blockchain/ton/tree/master/tonlib) + +Additionally, here are three high-level third-party block explorers: + +- https://explorer.toncoin.org/last +- https://toncenter.com/ +- https://tonwhales.com/explorer + +Read more in the [Explorers in TON](/participate/explorers) section of our documentation. + +### Block time + +_2-5s_ + +:::info +Compare TON's on-chain metrics, including block time and time-to-finality, to Solana and Ethereum by reading our analysis at [ton.org/analysis](https://ton.org/analysis). +::: + +### Time-to-finality + +_Under 6 sec._ + +:::info +Compare TON's on-chain metrics, including block time and time-to-finality, to Solana and Ethereum by reading our analysis at [ton.org/analysis](https://ton.org/analysis). +::: + +### Average block size + +```bash +max block size param 29 +max_block_bytes:2097152 +``` + +:::info +Find more actual params in [Network Configs](/develop/howto/network-configs). +::: + +### What is the layout of blocks on TON? + +Detailed explanations on each field of the layout: + +- [Block layout](/develop/data-formats/block-layout) + +## Transactions + +### RPC method to get transactions data + +- [see answer above](/develop/howto/faq#are-there-any-standardized-protocols-for-minting-burning-and-transferring-fungible-and-non-fungible-tokens-in-transactions) + +### Is TON transaction asynchronous or synchronous? Is it possible to access documentation that show how this system works? + +TON Blockchain messages asynchronous: + +- sender prepares the transaction body(message boc) and broadcasts it via Lite Client (or higher-level tool) +- Lite Client returns status of broadcast, not result of executing the Transaction +- sender checks desired result by listening target account(address) state or the whole blockchain state + +An explanation of how TON asynchronous messaging works is explained using an example related to Wallet smart contracts: + +- [How TON wallets work and how to access them using JavaScript](https://blog.ton.org/how-ton-wallets-work-and-how-to-access-them-from-javascript#1b-sending-a-transfer) + +Example for Wallet contract transfer (low-level): + +- https://github.com/xssnick/tonutils-go/blob/master/example/wallet/main.go + +### Is it possible to determine if a transaction is 100% finalized? Is querying the transaction level data sufficient to obtain this information? + +**Short answer:** To ensure the transaction is finalized, the receiver's account must be checked. + +To learn more about transaction verification, please see the following examples: + +- Go: [Wallet example](https://github.com/xssnick/tonutils-go/blob/master/example/wallet/main.go) +- Python: [Storefront bot with payments in TON](/develop/dapps/tutorials/accept-payments-in-a-telegram-bot) +- JavaScript: [Bot being used for dumpling sales](/develop/dapps/tutorials/accept-payments-in-a-telegram-bot-js) + +### What is the layout of a transaction in TON? + +Detailed explanations on each field of the layout: + +- [Transaction layout](/develop/data-formats/transaction-layout) + +### Is transaction batching possible? + +Yes, transaction batching on TON can be accomplished in two distinct ways: + +- By utilizing the asynchronous nature of TON, i.e. sending independent transactions to the network +- By making use of smart contracts which receive task and execute it as a batch + +Example of using batch-featured contract (high-load wallet): + +- https://github.com/tonuniverse/highload-wallet-api + +Default wallets (v3/v4) also support sending multiple messages (up to 4) in one transaction. + +## Standards + +### What accuracy of currencies is available for TON? + +_9 digits_ + +:::info +Number of decimal places supported by Mainnet : 9 digits. +::: + +### Are there any standardized protocols for minting, burning, and transferring fungible and non-fungible tokens in transactions? + +Non-fungible tokens (NFTs): + +- [TEP-62: NFT standard](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md) +- [NFT documentation](/develop/dapps/defi/tokens#nft) + +Jettons (tokens): + +- [TEP-74: Jettons standard](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md) +- [Distributed tokens overview](https://telegra.ph/Scalable-DeFi-in-TON-03-30) +- [Fungible token documentation(Jettons)](/develop/dapps/defi/tokens#jettons) + +Other Standards: + +- https://github.com/ton-blockchain/TEPs + +### Are there examples of parsing events with Jettons(Tokens) and NFT? + +On TON, all data is transmitted as boc-messages. This means that using NFTs in transactions is not an exceptional event. Rather, it is a regular message that is sent to or received from a (NFT- or Wallet-)contract, much like a transaction involving a standard wallet. + +However, certain indexed APIs allow you to view all messages sent to or from a contract, and filter them based on your specific requirements. + +- https://tonapi.io/swagger-ui + +To better understand how this process works, please refer [Payments Processing](/develop/dapps/asset-processing/) section. + +## Account Structure + +### What is the address format? + +- [Smart Contract Address](/learn/overviews/addresses) + +### Is it possible to have a named account similar to ENS + +Yes, use TON DNS: + +- [TON DNS & Domains](/participate/web3/dns) + +### How to distinguish between a normal account and a smart contract? + +- [Everything is a smart contract](/learn/overviews/addresses#everything-is-a-smart-contract) + +### How to tell if the address is a token address? + +For a **Jettons** contract must implement [standard's interface](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md) and return data on _get_wallet_data()_ or _get_jetton_data()_ methods. + +### Are there any special accounts (e.g. accounts owned by the network) that have different rules or methods from the rest? + +There is a special master blockchain inside a TON called Masterchain. It consists of network-wide contracts with network configuration, validator-related contracts, etc: + +:::info +Read more about masterchain, workchains and shardchains in TON Blockchain overview article: [Blockchain of Blockchains](/learn/overviews/ton-blockchain). +::: + +Good example is smart governance contract, which is a part of masterchain: + +- [Governance contracts](/develop/smart-contracts/governance) + +## Smart Contracts + +### Is it possible to detect contract deployment events on TON? + +[Everything in TON is a smart contract](/learn/overviews/addresses#everything-is-a-smart-contract). + +Account address is generated deterministically from its _initial state_, which includes _initial code_ and _initial data_ (for wallets, initial data includes public key among other parameters). +When any component changes, the address changes accordingly. + +Smart contract can exist in uninitialized state, meaning that its state is not available in blockchain but contract has non-zero balance. Initial state itself can be sent to the network later with an internal or external message, so those can be monitored to detect contract deployment. + +To protect message chains from being halted at non-existing contracts TON use "bounce" feature. Read more in these articles: + +- [Deploying wallet via TonLib](https://ton.org/docs/develop/dapps/asset-processing/#deploying-wallet) +- [Paying for processing queries and sending responses](https://ton.org/docs/develop/smart-contracts/guidelines/processing) + +### Is it possible to re-deploy code to an existing address or does it have to be deployed as a new contract? + +Yes, this is possible. If a smart contract carries out specific instructions (`set_code()`) its code can be updated and the address will remain the same. + +If the contract cannot initially execute `set_code()` (via its code or execution of other code coming from the outside), then its code cannot be changed ever. No one will be able to redeploy contract with other code at the same address. + +### Can smart contract be deleted? + +Yes, either as a result of storage fee accumulation (contract needs to reach -1 TON balance to be deleted) or by sending a message with [mode 160](https://docs.ton.org/develop/smart-contracts/messages#message-modes). + +### Are smart contract addresses case sensitive? + +Yes, smart contract addresses are case sensitive because they are generated using the [base64 algorithm](https://en.wikipedia.org/wiki/Base64). You can learn more about smart contract addresses [here](/learn/overviews/addresses). + +### Is the Ton Virtual Machine (TVM) EVM-compatible? + +The TVM is not compatible with the Ethereum Virtual Machine (EVM) because TON leverages a completely different architecture (TON is asynchronous, while Ethereum is synchronous). + +[Read more on asynchronous smart contracts](https://telegra.ph/Its-time-to-try-something-new-Asynchronous-smart-contracts-03-25). + +### Is it possible to write on Solidity for TON? + +Relatedly, the TON ecosystem does not support development in Ethereum’s Solidity programming language. + +But if you add asynchronous messages to the Solidity syntax and the ability to interact with data at a low level, then you get FunC. FunC features a syntax that is common to most modern programming languages and is designed specifically for development on TON. + +## Remote Procedure Calls (RPCs) + +### Recommended node providers for data extraction include: + +API types: + +- Read more about different [API Types](/develop/dapps/apis/) (Indexed, HTTP, and ADNL) + +Node providers partners: + +- https://toncenter.com/api/v2/ +- [getblock.io](https://getblock.io/) +- https://www.orbs.com/ton-access/ +- [toncenter/ton-http-api](https://github.com/toncenter/ton-http-api) +- [nownodes.io](https://nownodes.io/nodes) +- https://dton.io/graphql + +TON directory with projects from TON Community: + +- [ton.app](https://ton.app/) + +### Provided below are two main resources used to obtain information related to public node endpoints on TON Blockchain (for both TON Mainnet and TON Testnet). + +- [Network Configs](/develop/howto/network-configs) +- [Examples and tutorials](/develop/dapps/#examples) From e7e99bc0d88fd447504c0f868f2ab34ceea83d86 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:36 +0800 Subject: [PATCH 111/219] New translations fees-low-level.md (Chinese Simplified) --- .../current/develop/howto/fees-low-level.md | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/fees-low-level.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/fees-low-level.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/fees-low-level.md new file mode 100644 index 0000000000..9d2776c116 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/fees-low-level.md @@ -0,0 +1,196 @@ +# Low-level fees overview + +:::caution +This section describes instructions and manuals for interacting with TON at a low level. +::: + +This document provides a general idea of transaction fees on TON and particularly computation fees for the FunC code. There is also a [detailed specification in the TVM whitepaper](https://ton.org/tvm.pdf). + +## Transactions and phases + +As was described in the [TVM overview](/learn/tvm-instructions/tvm-overview), transaction execution consists of a few phases. During those phases, the corresponding fees may be deducted. + +Generally: + +```cpp +transaction_fee = storage_fees + + in_fwd_fees + + computation_fees + + action_fees + + out_fwd_fees +``` + +where: + +- `storage_fees`—fees corresponding to occupation of some space in chain state by contract +- `in_fwd_fees`—fees for importing to blockchain incoming message (it is only relevant for messages which were not previously on-chain, that is, `external` messages. For ordinary messages from contract to contract this fee is not applicable) +- `computation_fees`—fees corresponding to execution of TVM instructions +- `action_fees`—fees related to processing of action list (sending messages, setting libraries etc.) +- `out_fwd_fees`—fees related to importing to blockchain of outcoming message + +## Computation fees + +### Gas + +All computation costs are nominated in gas units. The price of gas units is determined by this chain config (Config 20 for masterchain and Config 21 for basechain) and may be changed only by consensus of the validator. Note that unlike in other systems, the user cannot set his own gas price, and there is no fee market. + +Current settings in basechain are as follows: 1 unit of gas costs 1000 nanotons. + +## TVM instructions cost + +On the lowest level (TVM instruction execution) the gas price for most primitives +equals the _basic gas price_, computed as `P_b := 10 + b + 5r`, +where `b` is the instruction length in bits and `r` is the +number of cell references included in the instruction. + +Apart from those basic fees, the following fees appear: + +| Instruction | GAS price | Description | +| ----------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Creation of cell | **500** | Operation of transforming builder to cell. | +| Parsing cell firstly | **100** | Operation of transforming cells into slices first time during current transaction. | +| Parsing cell repeatedly | **25** | Operation of transforming cells into slices, which already has parsed during same transaction. | +| Throwing exception | **50** | | +| Operation with tuple | **1** | This price will multiply by the quantity of tuple's elements. | +| Implicit Jump | **10** | It is paid when all instructions in the current continuation cell are executed. However, there are references in that continuation cell, and the execution flow jumps to the first reference. | +| Implicit Back Jump | **5** | It is paid when all instructions in the current continuation are executed and execution flow jumps back to the continuation from which the just finished continuation was called. | +| Moving stack elements | **1** | Price for moving stack elements between continuations. It will charge correspond gas price for every element. However, the first 32 elements moving is free. | + +## FunC constructions gas fees + +Almost all functions used in FunC are defined in [stdlib.func](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/stdlib.fc) which maps FunC functions to Fift assembler instructions. In turn, Fift assembler instructions are mapped to bit-sequence instructions in [asm.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/fift/lib/Asm.fif). So if you want to understand how much exactly the instruction call will cost you, you need to find `asm` representation in `stdlib.func`, then find bit-sequence in `asm.fif` and calculate instruction length in bits. + +However, generally, fees related to bit-lengths are minor in comparison with fees related to cell parsing and creation, as well as jumps and just number of executed instructions. + +So, if you try to optimize your code start with architecture optimization, the decreasing number of cell parsing/creation operations, and then with the decreasing number of jumps. + +### Operations with cells + +Just an example of how proper cell work may substantially decrease gas costs. + +Let's imagine that you want to add some encoded payload to the outgoing message. Straightforward implementation will be as follows: + +```cpp +slice payload_encoding(int a, int b, int c) { + return + begin_cell().store_uint(a,8) + .store_uint(b,8) + .store_uint(c,8) + .end_cell().begin_parse(); +} + +() send_message(slice destination) impure { + slice payload = payload_encoding(1, 7, 12); + var msg = begin_cell() + .store_uint(0x18, 6) + .store_slice(destination) + .store_coins(0) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) + .store_uint(0x33bbff77, 32) ;; op-code (see smart-contract guidelines) + .store_uint(cur_lt(), 64) ;; query_id (see smart-contract guidelines) + .store_slice(payload) + .end_cell(); + send_raw_message(msg, 64); +} +``` + +What is the problem with this code? `payload_encoding` to generate a slice bit-string, first create a cell via `end_cell()` (+500 gas units). Then parse it `begin_parse()` (+100 gas units). The same code can be written without those unnecessary operations by changing some commonly used types: + +```cpp +;; we add asm for function which stores one builder to the another, which is absent from stdlib +builder store_builder(builder to, builder what) asm(what to) "STB"; + +builder payload_encoding(int a, int b, int c) { + return + begin_cell().store_uint(a,8) + .store_uint(b,8) + .store_uint(c,8); +} + +() send_message(slice destination) impure { + builder payload = payload_encoding(1, 7, 12); + var msg = begin_cell() + .store_uint(0x18, 6) + .store_slice(destination) + .store_coins(0) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) + .store_uint(0x33bbff77, 32) ;; op-code (see smart-contract guidelines) + .store_uint(cur_lt(), 64) ;; query_id (see smart-contract guidelines) + .store_builder(payload) + .end_cell(); + send_raw_message(msg, 64); +} +``` + +By passing bit-string in the another form (builder instead of slice) we substantially decrease computation cost by very slight change in code. + +### Inline and inline_refs + +By default, when you have a FunC function, it gets its own `id`, stored in a separate leaf of id->function dictionary, and when you call it somewhere in the program, a search of the function in dictionary and subsequent jump occur. Such behavior is justified if your function is called from many places in the code and thus jumps allow to decrease the code size (by storing a function body once). However, if the function is only used once or twice, it is often much cheaper to declare this function as `inline` or `inline_ref`. `inline` modificator places the body of the function right into the code of the parent function, while `inline_ref` places the function code into the reference (jumping to the reference is still much cheaper than searching and jumping to the dictionary entry). + +### Dictionaries + +Dictionaries on TON are introduced as trees (DAGs to be precise) of cells. That means that if you search, read, or write to the dictionary, you need to parse all cells of the corresponding branch of the tree. That means that + +- a) dicts operations are not fixed in gas costs (since the size and number of nodes in the branch depend on the given dictionary and key) +- b) it is expedient to optimize dict usage by using special instructions like `replace` instead of `delete` and `add` +- c) developer should be aware of iteration operations (like next and prev) as well `min_key`/`max_key` operations to avoid unnecessary iteration through the whole dict + +### Stack operations + +Note that FunC manipulates stack entries under the hood. That means that the code: + +```cpp +(int a, int b, int c) = some_f(); +return (c, b, a); +``` + +will be translated into a few instructions which changes the order of elements on the stack. + +When the number of stack entries is substantial (10+), and they are actively used in different orders, stack operations fees may become non-negligible. + +## Fee's calculation Formulas + +### storage_fees + +```cpp +storage_fees = ceil( + (account.bits * bit_price + + account.cells * cell_price) + * period / 2 ^ 16) +``` + +### in_fwd_fees, out_fwd_fees + +```cpp +msg_fwd_fees = (lump_price + + ceil( + (bit_price * msg.bits + cell_price * msg.cells) / 2^16) + ) + +ihr_fwd_fees = ceil((msg_fwd_fees * ihr_price_factor) / 2^16) +``` + +// bits in the root cell of a message are not included in msg.bits (lump_price pays for them) + +### action_fees + +```cpp +action_fees = sum(out_ext_msg_fwd_fee) + sum(int_msg_mine_fee) +``` + +### Config file + +All fees are nominated for a certain gas amount and may be changed. The config file represents the current fee cost. + +- storage_fees = [p18](https://explorer.toncoin.org/config?workchain=-1\&shard=8000000000000000\&seqno=22185244\&roothash=165D55B3CFFC4043BFC43F81C1A3F2C41B69B33D6615D46FBFD2036256756382\&filehash=69C43394D872B02C334B75F59464B2848CD4E23031C03CA7F3B1F98E8A13EE05#configparam18) +- in_fwd_fees = [p24](https://explorer.toncoin.org/config?workchain=-1\&shard=8000000000000000\&seqno=22185244\&roothash=165D55B3CFFC4043BFC43F81C1A3F2C41B69B33D6615D46FBFD2036256756382\&filehash=69C43394D872B02C334B75F59464B2848CD4E23031C03CA7F3B1F98E8A13EE05#configparam24), [p25](https://explorer.toncoin.org/config?workchain=-1\&shard=8000000000000000\&seqno=22185244\&roothash=165D55B3CFFC4043BFC43F81C1A3F2C41B69B33D6615D46FBFD2036256756382\&filehash=69C43394D872B02C334B75F59464B2848CD4E23031C03CA7F3B1F98E8A13EE05#configparam25) +- computation_fees = [p20](https://explorer.toncoin.org/config?workchain=-1\&shard=8000000000000000\&seqno=22185244\&roothash=165D55B3CFFC4043BFC43F81C1A3F2C41B69B33D6615D46FBFD2036256756382\&filehash=69C43394D872B02C334B75F59464B2848CD4E23031C03CA7F3B1F98E8A13EE05#configparam20), [p21](https://explorer.toncoin.org/config?workchain=-1\&shard=8000000000000000\&seqno=22185244\&roothash=165D55B3CFFC4043BFC43F81C1A3F2C41B69B33D6615D46FBFD2036256756382\&filehash=69C43394D872B02C334B75F59464B2848CD4E23031C03CA7F3B1F98E8A13EE05#configparam21) +- action_fees = [p24](https://explorer.toncoin.org/config?workchain=-1\&shard=8000000000000000\&seqno=22185244\&roothash=165D55B3CFFC4043BFC43F81C1A3F2C41B69B33D6615D46FBFD2036256756382\&filehash=69C43394D872B02C334B75F59464B2848CD4E23031C03CA7F3B1F98E8A13EE05#configparam24), [p25](https://explorer.toncoin.org/config?workchain=-1\&shard=8000000000000000\&seqno=22185244\&roothash=165D55B3CFFC4043BFC43F81C1A3F2C41B69B33D6615D46FBFD2036256756382\&filehash=69C43394D872B02C334B75F59464B2848CD4E23031C03CA7F3B1F98E8A13EE05#configparam25) +- out_fwd_fees = [p24](https://explorer.toncoin.org/config?workchain=-1\&shard=8000000000000000\&seqno=22185244\&roothash=165D55B3CFFC4043BFC43F81C1A3F2C41B69B33D6615D46FBFD2036256756382\&filehash=69C43394D872B02C334B75F59464B2848CD4E23031C03CA7F3B1F98E8A13EE05#configparam24), [p25](https://explorer.toncoin.org/config?workchain=-1\&shard=8000000000000000\&seqno=22185244\&roothash=165D55B3CFFC4043BFC43F81C1A3F2C41B69B33D6615D46FBFD2036256756382\&filehash=69C43394D872B02C334B75F59464B2848CD4E23031C03CA7F3B1F98E8A13EE05#configparam25) + +:::info +[A direct link to the mainnet config file](https://explorer.toncoin.org/config?workchain=-1\&shard=8000000000000000\&seqno=22185244\&roothash=165D55B3CFFC4043BFC43F81C1A3F2C41B69B33D6615D46FBFD2036256756382\&filehash=69C43394D872B02C334B75F59464B2848CD4E23031C03CA7F3B1F98E8A13EE05) +::: + +_Based on @thedailyton [article](https://telegra.ph/Fees-calculation-on-the-TON-Blockchain-07-24) from 24.07_ From b63c61085dff0fee7f1cb87319564116b0f34839 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:36 +0800 Subject: [PATCH 112/219] New translations network-configs.md (Chinese Simplified) --- .../current/develop/howto/network-configs.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/network-configs.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/network-configs.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/network-configs.md new file mode 100644 index 0000000000..def28f69a0 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/network-configs.md @@ -0,0 +1,11 @@ +# Network Configs + +On this page you can find active network configs of TON Blockchain: + +- Mainnet: https://ton.org/global-config.json +- Testnet: https://ton.org/testnet-global.config.json + +## See Also + +- [Node Types](https://docs.ton.org/participate/nodes/node-types) +- [Blockchain Params Config](/develop/howto/blockchain-configs) From d0d996a3792ed532f0de39dc92d8a1b8100393ed Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:37 +0800 Subject: [PATCH 113/219] New translations step-by-step.md (Chinese Simplified) --- .../current/develop/howto/step-by-step.md | 610 ++++++++++++++++++ 1 file changed, 610 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/step-by-step.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/step-by-step.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/step-by-step.md new file mode 100644 index 0000000000..18e5706c46 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/step-by-step.md @@ -0,0 +1,610 @@ +# Step-by-Step Deep Dive + +:::caution +This section describes instructions and manuals for interacting with TON at a low level. +::: + +The aim of this document is to provide step-by-step instructions for compiling and creating a simple smart contract (a simple wallet) in TON Blockchain Test Network using the TON Blockchain Lite Client and associated software. + +We assume here that the Lite Client is already properly downloaded, compiled, and installed. + +> Note that this tutorial is for a testnet, so you must use this config: https://ton.org/testnet-global.config.json + +## 1. Smart contract addresses + +:::tip new version available +Please, read new version of [Smart contract addresses](/learn/overviews/addresses) article. +::: + +Smart contract addresses in the TON Network consist of two parts: + +(a) the workchain ID (a signed 32-bit integer) and + +(b) the address inside the workchain (64-512 bits depending on the workchain). + +Currently, only the masterchain (workchain_id=-1) and occasionally the basic workchain (workchain_id=0) are running in TON Blockchain Network. Both of them have 256-bit addresses, so we henceforth assume that workchain_id is either 0 or -1 and that the address inside the workchain is exactly 256-bit. + +Under the conditions stated above, the smart-contract address can be represented in the following forms: + +A) "Raw": < decimal workchain_id>:<64 hexadecimal digits with address> + +B) "User-friendly," which is obtained by first generating: + +- one tag byte (0x11 for "bounceable" addresses, 0x51 for "non-bounceable"; add +0x80 if the address should not be accepted by software running in the production network) +- one byte containing a signed 8-bit integer with the workchain_id (0x00 for the basic workchain, 0xff for the masterchain) +- 32 bytes containing 256 bits of the smart-contract address inside the workchain (big-endian) +- 2 bytes containing CRC16-CCITT of the previous 34 bytes + +In case B), the 36 bytes obtained are then encoded using either base64 (i.e., with digits, upper and lowercase Latin letters, '/' and '+') or base64url (with '_' and '-' instead of '/' and '+'), yielding 48 printable non-space characters. + +Example: + +The "test giver" (a special smart contract residing in the masterchain of the Test Network that gives up to 20 test coins to anybody who asks) has the address: + +`-1:fcb91a3a3816d0f7b8c2c76108b8a9bc5a6b7a55bd79f8ab101c52db29232260` + +in the "raw" form (notice that uppercase Latin letters 'A'..'F' may be used instead of 'a'..'f'), + +and: + +`kf/8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny` (base64) + +`kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny` (base64url) + +in the "user-friendly" form (to be displayed by user-friendly clients). Notice that both forms (base64 and base64url) are valid and must be accepted. + +Incidentally, other binary data related to TON Blockchain has similar "armored" base64 representations, differing by their first bytes. For example, the ubiquitous 256-bit Ed25519 public keys are represented by first creating a 36-byte sequence as follows: + +- one tag byte 0x3E, meaning that this is a public key +- one tag byte 0xE6, meaning that this is a Ed25519 public key +- 32 bytes containing the standard binary representation of the Ed25519 public key +- 2 bytes containing the big-endian representation of CRC16-CCITT of the previous 34 bytes + +The resulting 36-byte sequence is converted into a 48-character base64 or base64url string in the standard fashion. For example, the Ed25519 public key `E39ECDA0A7B0C60A7107EC43967829DBE8BC356A49B9DFC6186B3EAC74B5477D` (usually represented by a sequence of 32 bytes `0xE3, 0x9E, ..., 0x7D`) has the following "armored" representation: + +`Pubjns2gp7DGCnEH7EOWeCnb6Lw1akm538YYaz6sdLVHfRB2` + +## 2. Inspecting the state of a smart contract + +Inspecting the state of smart contracts with the aid of the TON Lite Client is easy. For the sample smart contract described above, you would run the Lite Client and enter the following commands: + +```func +> last +... +> getaccount -1:fcb91a3a3816d0f7b8c2c76108b8a9bc5a6b7a55bd79f8ab101c52db29232260 +or +> getaccount kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny +``` + +You will see something like this: + +:::info +Please note here and further that the code, comments, and/or documentation may contain parameters, methods, and definitions such as “gram”, “nanogram”, etc. That is a legacy of the original TON code, developed by Telegram. Gram cryptocurrency was never issued. The currency of TON is Toncoin and the currency of TON testnet is Test Toncoin. +::: + +```cpp +got account state for -1 : FCB91A3A3816D0F7B8C2C76108B8A9BC5A6B7A55BD79F8AB101C52DB29232260 with respect to blocks (-1,8000000000000000,2075):BFE876CE2085274FEDAF1BD80F3ACE50F42B5A027DF230AD66DCED1F09FB39A7:522C027A721FABCB32574E3A809ABFBEE6A71DE929C1FA2B1CD0DDECF3056505 +account state is (account + addr:(addr_std + anycast:nothing workchain_id:-1 address:xFCB91A3A3816D0F7B8C2C76108B8A9BC5A6B7A55BD79F8AB101C52DB29232260) + storage_stat:(storage_info + used:(storage_used + cells:(var_uint len:1 value:3) + bits:(var_uint len:2 value:707) + public_cells:(var_uint len:0 value:0)) last_paid:1568899526 + due_payment:nothing) + storage:(account_storage last_trans_lt:2310000003 + balance:(currencies + grams:(nanograms + amount:(var_uint len:6 value:9998859500889)) + other:(extra_currencies + dict:hme_empty)) + state:(account_active + ( + split_depth:nothing + special:nothing + code:(just + value:(raw@^Cell + x{} + x{FF0020DD2082014C97BA9730ED44D0D70B1FE0A4F260D31F01ED44D0D31FD166BAF2A1F8000120D74A8E11D307D459821804A817C80073FB0201FB00DED1A4C8CB1FC9ED54} + )) + data:(just + value:(raw@^Cell + x{} + x{00009A15} + )) + library:hme_empty)))) +x{CFFFCB91A3A3816D0F7B8C2C76108B8A9BC5A6B7A55BD79F8AB101C52DB2923226020680B0C2EC1C0E300000000226BF360D8246029DFF56534_} + x{FF0020DD2082014C97BA9730ED44D0D70B1FE0A4F260D31F01ED44D0D31FD166BAF2A1F8000120D74A8E11D307D459821804A817C80073FB0201FB00DED1A4C8CB1FC9ED54} + x{00000003} +last transaction lt = 2310000001 hash = 73F89C6F8910F598AD84504A777E5945C798AC8C847FF861C090109665EAC6BA +``` + +The first information line `got account state ... for ...` shows the account address and the masterchain block identifier with respect to which the account state has been dumped. Notice that even if the account state changes in a subsequent block, the `getaccount xxx` command will return the same result until the reference block is updated to a newer value by a `last` command. In this way, one can study the state of all accounts and obtain consistent results. + +The `account state is (account ... ` line begins the pretty-printed deserialized view of the account state. It is a deserialization of TL-B data type Account, used to represent account states in TON Blockchain as explained in the TON Blockchain documentation. (You can find the TL-B scheme used for deserialization in the source file `crypto/block/block.tlb`; notice that if the scheme is out of date, the deserialization may have a breakdown.) + +Finally, the last several lines beginning with `x{CFF...` (the "raw dump") contain the same information displayed as a tree of cells. In this case, we have one root cell containing the data bits `CFF...34_` (the underscore means that the last binary one and all subsequent binary zeroes are to be removed, so hexadecimal `4_` corresponds to binary `0`) and two cells that are its children (displayed with one-space indentation). + +We can see that `x{FF0020DD20...}` is the code of this smart contract. If we consult Appendix A of the TON Virtual Machine documentation, we can even disassemble this code: `FF00` is `SETCP 0`, `20` is `DUP`, `DD` is `IFNOTRET`, `20` is `DUP`, and so on. (Incidentally, you can find the source code of this smart contract in the source file `crypto/block/new-testgiver.fif`) + +We can also see that `x{00009A15}` (the actual value you see may be different) is the persistent data of this smart contract. It is actually an unsigned 32-bit integer, used by the smart contract as the counter of operations performed so far. Notice that this value is big-endian (i.e., 3 is encoded as `x{00000003}`, not as `x{03000000}`), as are all integers inside TON Blockchain. In this case, the counter is equal to `0x9A15` = `39445`. + +The current balance of the smart contract is easily seen in the pretty-printed portion of the output. In this case, we see `... balance:(currencies:(grams:(nanograms:(... value:1000000000000000...))))`, which is the balance of the account in (test) nanotons (a million test Toncoin in this example; the actual number you see may be smaller). If you study the TL-B scheme provided in `crypto/block/scheme.tlb`, you will be able to find this number (10^15) in binary big-endian form in the raw dump portion as well (it is located near the end of the data bits of the root cell). + +## 3. Compiling a new smart contract + +Before uploading a new smart contract into TON Blockchain, you need to determine its code and data and save them in serialized form into a file (called a "bag-of-cells" or BOC file, usually with a .boc suffix). Let's consider the case of a simple wallet smart contract, which stores a 32-bit operations counter and a 256-bit Ed25519 public key of its owner in its persistent data. + +Obviously, you'll need some tools for developing smart contracts, namely a TON smart contract compiler. Basically, a TON smart contract compiler is a program that reads the source of a smart contract in a specialized high-level programming language and creates a .boc file from this source. + +One such tool is the Fift interpreter, which is included in this distribution and can help create simple smart contracts. Larger smart contracts should be developed using more sophisticated tools (such as the FunC compiler included in this distribution that creates Fift assembler files from FunC source files; you can find some FunC smart-contract sources in the directory `crypto/smartcont`). However, Fift is sufficient for demonstration purposes. + +Consider the file `new-wallet.fif` (usually located at `crypto/smartcont/new-wallet.fif` with respect to the source directory) containing the source of a simple wallet smart contract: + +```cpp +#!/usr/bin/env fift -s +"TonUtil.fif" include +"Asm.fif" include + +{ ."usage: " @' $0 type ." []" cr + ."Creates a new wallet in specified workchain, with private key saved to or loaded from .pk" cr + ."('new-wallet.pk' by default)" cr 1 halt +} : usage +$# 1- -2 and ' usage if + +$1 parse-workchain-id =: wc // set workchain id from command line argument +def? $2 { @' $2 } { "new-wallet" } cond constant file-base + +."Creating new wallet in workchain " wc . cr + +// Create new simple wallet +<{ SETCP0 DUP IFNOTRET // return if recv_internal + DUP 85143 INT EQUAL IFJMP:<{ // "seqno" get-method + DROP c4 PUSHCTR CTOS 32 PLDU // cnt + }> + INC 32 THROWIF // fail unless recv_external + 512 INT LDSLICEX DUP 32 PLDU // sign cs cnt + c4 PUSHCTR CTOS 32 LDU 256 LDU ENDS // sign cs cnt cnt' pubk + s1 s2 XCPU // sign cs cnt pubk cnt' cnt + EQUAL 33 THROWIFNOT // ( seqno mismatch? ) + s2 PUSH HASHSU // sign cs cnt pubk hash + s0 s4 s4 XC2PU // pubk cs cnt hash sign pubk + CHKSIGNU // pubk cs cnt ? + 34 THROWIFNOT // signature mismatch + ACCEPT + SWAP 32 LDU NIP + DUP SREFS IF:<{ + // 3 INT 35 LSHIFT# 3 INT RAWRESERVE // reserve all but 103 coins from the balance + 8 LDU LDREF // pubk cnt mode msg cs + s0 s2 XCHG SENDRAWMSG // pubk cnt cs ; ( message sent ) + }> + ENDS + INC NEWC 32 STU 256 STU ENDC c4 POPCTR +}>c // >libref +// code + // data +null // no libraries +// Libs{ x{ABACABADABACABA} drop x{AAAA} s>c public_lib x{1234} x{5678} |_ s>c public_lib }Libs + // create StateInit +dup ."StateInit: " +dup ."signing message: " +dup ."External message for initialization is " B dup Bx. cr +file-base +"-query.boc" tuck B>file +."(Saved wallet creating query to file " type .")" cr +``` + +(The actual source file in your distribution may be slightly different.) Essentially, it is a complete Fift script for creating a new instance of this smart contract controlled by a newly-generated keypair. The script accepts command-line arguments, so you don't need to edit the source file each time you want to create a new wallet. + +Now, provided that you have compiled the Fift binary (usually located as `crypto/fift` with respect to the build directory), you can run: + +```bash +$ crypto/fift -I/crypto/fift/lib -s /crypto/smartcont/new-wallet.fif 0 my_wallet_name +``` + +where 0 is the workchain to contain the new wallet (0 = basechain, -1 = masterchain), `my_wallet_name` is any identifier you wish to be associated with this wallet. The address of the new wallet will be saved in the file `my_wallet_name.addr`, its newly-generated private key will be saved tin `my_wallet_name.pk` (unless this file already exists; then the key will be loaded from this file instead), and the external message will be saved in `my_wallet_name-query.boc`. If you do not specify a name of your wallet (`my_wallet_name` in the example above), the default name `new-wallet` is used. + +You may opt to set the `FIFTPATH` environment variable to `/crypto/fift/lib:/crypto/smartcont`, the directories containing `Fift.fif` and `Asm.fif` library files, and the sample smart contract sources respectively; then you can omit the `-I` argument to the Fift interpreter. If you install the Fift binary `crypto/fift` to a directory included in your `PATH` (e.g., `/usr/bin/fift`), you can simply invoke the following: + +```bash +$ fift -s new-wallet.fif 0 my_wallet_name +``` + +instead of indicating the complete search paths in the command line. + +If everything worked, you'll see something like this: + +```cpp +Creating new wallet in workchain 0 +Saved new private key to file my_wallet_name.pk +StateInit: x{34_} + x{FF0020DD2082014C97BA9730ED44D0D70B1FE0A4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED54} + x{00000000C59DC52962CC568AC5E72735EABB025C5BDF457D029AEEA6C2FFA5EB2A945446} + +new wallet address = 0:2ee9b4fd4f077c9b223280c35763df9edab0b41ac20d36f4009677df95c3afe2 +(Saving address to file my_wallet_name.addr) +Non-bounceable address (for init): 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb +Bounceable address (for later access): kQAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4rie +signing message: x{00000000} + +External message for initialization is x{88005DD369FA9E0EF93644650186AEC7BF3DB5616835841A6DE8012CEFBF2B875FC41190260D403E40B2EE8BEB2855D0F4447679D9B9519BE64BE421166ABA2C66BEAAAF4EBAF8E162886430243216DDA10FCE68C07B6D7DDAA3E372478D711E3E1041C00000001_} + x{FF0020DD2082014C97BA9730ED44D0D70B1FE0A4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED54} + x{00000000C59DC52962CC568AC5E72735EABB025C5BDF457D029AEEA6C2FFA5EB2A945446} + +B5EE9C724104030100000000E50002CF88005DD369FA9E0EF93644650186AEC7BF3DB5616835841A6DE8012CEFBF2B875FC41190260D403E40B2EE8BEB2855D0F4447679D9B9519BE64BE421166ABA2C66BEAAAF4EBAF8E162886430243216DDA10FCE68C07B6D7DDAA3E372478D711E3E1041C000000010010200A2FF0020DD2082014C97BA9730ED44D0D70B1FE0A4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED54004800000000C59DC52962CC568AC5E72735EABB025C5BDF457D029AEEA6C2FFA5EB2A945446BCF59C17 +(Saved wallet creating query to file my_wallet_name-query.boc) +``` + +In a nutshell, the Fift assembler (loaded by the `Asm.fif` include line) is used to compile the source code of the smart contract (contained in `<{ SETCP0 ... c4 POPCTR }>` lines) into its internal representation. The initial data of the smart contract is also created (by `` lines), containing a 32-bit sequence number (equal to zero), and a 256-bit public key from a newly-generated Ed25519 keypair. The corresponding private key is saved into the file `my_wallet_name.pk` unless it already exists (if you run this code twice in the same directory, the private key will be loaded from this file instead). + +The code and data for the new smart contract are combined into a `StateInit` structure (in the next lines), the address of the new smart contract (equal to the hash of this `StateInit` structure) is computed and output, and then an external message with a destination address equal to that of the new smart contract is created. This external message contains both the correct `StateInit` for the new smart contract and a non-trivial payload (signed by the correct private key). + +Finally, the external message is serialized into a bag of cells (represented by `B5EE...BE63`) and saved into the file `my_wallet_name-query.boc`. Essentially, this file is your compiled smart contract with all the additional information necessary to upload it to TON Blockchain. + +## 4. Transferring some funds to the new smart contract + +You might try to upload the new smart contract immediately by running the Lite Client and typing: + +``` +> sendfile new-wallet-query.boc +``` + +or + +``` +> sendfile my_wallet_name-query.boc +``` + +if you chose to name your wallet `my_wallet_name`. + +Unfortunately, this won't work because smart contracts must have a positive balance to be able to pay for storing and processing their data to the blockchain. So you have to transfer some funds to your new smart contract address, displayed during its generation as `-1:60c0...c0d0` (in raw form) and `0f9..EKD` (in a user-friendly form). + +In a real scenario, you would either transfer some Toncoin from your already existing wallet, ask a friend to do so, or buy some Toncoin at a cryptocurrency exchange, indicating `0f9...EKD` as the account to transfer the new Toncoin to. + +In the Test Network, you have another option; you can ask the "test giver" to give you some test Toncoin (up to 20). Let us explain how to do it. + +## 5. Using the test-giver smart contract + +You need to know the address of the test giver smart contract. We'll assume that it is `-1:fcb91a3a3816d0f7b8c2c76108b8a9bc5a6b7a55bd79f8ab101c52db29232260`, or, equivalently, `kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny`, as indicated in one of the previous examples. You can inspect the state of this smart contract in the Lite Client by typing: + +``` +> last +> getaccount kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny +``` + +as explained above in Section 2. The only number you need from the output is the 32-bit sequence number stored in the smart contract data (it is `0x9A15` in the example above, but generally it will be different). A simpler way of obtaining the current value of this sequence number is by typing: + +``` +> last +> runmethod kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny seqno +``` + +producing the correct value 39445 = 0x9A15: + +```cpp +got account state for -1 : FCB91A3A3816D0F7B8C2C76108B8A9BC5A6B7A55BD79F8AB101C52DB29232260 with respect to blocks (-1,8000000000000000,2240):18E6DA7707191E76C71EABBC5277650666B7E2CFA2AEF2CE607EAFE8657A3820:4EFA2540C5D1E4A1BA2B529EE0B65415DF46BFFBD27A8EB74C4C0E17770D03B1 +creating VM +starting VM to run method `seqno` (85143) of smart contract -1:FCB91A3A3816D0F7B8C2C76108B8A9BC5A6B7A55BD79F8AB101C52DB29232260 +... +arguments: [ 85143 ] +result: [ 39445 ] +``` + +Next, you create an external message to the test giver asking it to send another message to your (uninitialized) smart contract carrying a specified amount of test Toncoin. There is a special Fift script for generating this external message located at `crypto/smartcont/testgiver.fif`: + +```cpp +#!/usr/bin/env fift -s +"TonUtil.fif" include + +{ ."usage: " @' $0 type ." []" cr + ."Creates a request to TestGiver and saves it into .boc" cr + ."('testgiver-query.boc' by default)" cr 1 halt +} : usage + +$# 3 - -2 and ' usage if + +// "testgiver.addr" load-address +Masterchain 0xfcb91a3a3816d0f7b8c2c76108b8a9bc5a6b7a55bd79f8ab101c52db29232260 +2constant giver_addr + ."Test giver address = " giver_addr 2dup .addr cr 6 .Addr cr + +$1 true parse-load-address =: bounce 2=: dest_addr +$2 parse-int =: seqno +$3 $>GR =: amount +def? $4 { @' $4 } { "testgiver-query" } cond constant savefile + +."Requesting " amount .GR ."to account " +dest_addr 2dup bounce 7 + .Addr ." = " .addr +."seqno=0x" seqno x. ."bounce=" bounce . cr + +// create a message (NB: 01b00.., b = bounce) + + +dup ."enveloping message: " +dup ."resulting external message: " B dup Bx. cr +savefile +".boc" tuck B>file +."(Saved to file " type .")" cr +``` + +You can pass the required parameters as command-line arguments to this script. + +```bash +$ crypto/fift -I -s [] +``` + +For instance, + +```bash +$ crypto/fift -I/crypto/fift/lib:/crypto/smartcont -s testgiver.fif 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb 0x9A15 6.666 wallet-query +``` + +or simply: + +```bash +$ fift -s testgiver.fif 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb 0x9A15 6.666 wallet-query +``` + +provided you have set up the environment variable `FIFTPATH` to `/crypto/fift/lib:/crypto/smartcont` and installed the fift binary as `/usr/bin/fift` (or anywhere else in your `PATH`). + +The newly-created message to the new smart contract must have its bounce bit cleared; otherwise, the transfer will be bounced to its sender. This is the reason we have passed the non-bounceable address `0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb` of our new wallet smart contract. + +This Fift code creates an internal message from the test giver smart contract to the address of our new smart contract carrying 6.666 test TON (you can enter any other amount here up to approximately 20 TON). The message is then enveloped into an external message addressed to the test giver. This external message must also contain the correct sequence number of the test giver. When the test giver receives such an external message, it checks whether the sequence number matches the one stored in its persistent data, and if it does, sends the embedded internal message with the required amount of test TON to its destination (our smart contract in this case). + +The external message is serialized and saved into the file `wallet-query.boc`. Some output is generated in the process: + +```cpp +Test giver address = -1:fcb91a3a3816d0f7b8c2c76108b8a9bc5a6b7a55bd79f8ab101c52db29232260 +kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny +Requesting GR$6.666 to account 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb = 0:2ee9b4fd4f077c9b223280c35763df9edab0b41ac20d36f4009677df95c3afe2 seqno=0x9a15 bounce=0 +enveloping message: x{00009A1501} + x{42001774DA7EA783BE4D91194061ABB1EFCF6D585A0D61069B7A004B3BEFCAE1D7F1280C6A98B4000000000000000000000000000047494654} + +resulting external message: x{89FF02ACEEB6F264BCBAC5CE85B372D8616CA2B4B9A5E3EC98BB496327807E0E1C1A000004D0A80C_} + x{42001774DA7EA783BE4D91194061ABB1EFCF6D585A0D61069B7A004B3BEFCAE1D7F1280C6A98B4000000000000000000000000000047494654} + +B5EE9C7241040201000000006600014F89FF02ACEEB6F264BCBAC5CE85B372D8616CA2B4B9A5E3EC98BB496327807E0E1C1A000004D0A80C01007242001774DA7EA783BE4D91194061ABB1EFCF6D585A0D61069B7A004B3BEFCAE1D7F1280C6A98B4000000000000000000000000000047494654AFC17FA4 +(Saved to file wallet-query.boc) +``` + +## 6. Uploading the external message to the test giver smart contract + +Now we can invoke the Lite Client, check the state of the test giver (if the sequence number has changed, our external message will fail), and then type: + +```bash +> sendfile wallet-query.boc +``` + +We will see the output: + +```bash +... external message status is 1 +``` + +which means that the external message has been delivered to the collator pool. Afterwards, one of the collators might choose to include this external message in a block, creating a transaction for the test-giver smart contract to process this external message. We can check whether the state of the test giver has changed: + +```bash +> last +> getaccount kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny +``` + +(If you forget to type `last`, you are likely to see the unchanged state of the test giver smart contract.) The resulting output will be: + +```cpp +got account state for -1 : FCB91A3A3816D0F7B8C2C76108B8A9BC5A6B7A55BD79F8AB101C52DB29232260 with respect to blocks (-1,8000000000000000,2240):18E6DA7707191E76C71EABBC5277650666B7E2CFA2AEF2CE607EAFE8657A3820:4EFA2540C5D1E4A1BA2B529EE0B65415DF46BFFBD27A8EB74C4C0E17770D03B1 +account state is (account + addr:(addr_std + anycast:nothing workchain_id:-1 address:xFCB91A3A3816D0F7B8C2C76108B8A9BC5A6B7A55BD79F8AB101C52DB29232260) + storage_stat:(storage_info + used:(storage_used + cells:(var_uint len:1 value:3) + bits:(var_uint len:2 value:707) + public_cells:(var_uint len:0 value:0)) last_paid:0 + due_payment:nothing) + storage:(account_storage last_trans_lt:10697000003 + balance:(currencies + grams:(nanograms + amount:(var_uint len:7 value:999993280210000)) + other:(extra_currencies + dict:hme_empty)) + state:(account_active + ( + split_depth:nothing + special:nothing + code:(just + value:(raw@^Cell + x{} + x{FF0020DDA4F260D31F01ED44D0D31FD166BAF2A1F80001D307D4D1821804A817C80073FB0201FB00A4C8CB1FC9ED54} + )) + data:(just + value:(raw@^Cell + x{} + x{00009A16} + )) + library:hme_empty)))) +x{CFF8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D2068086C00000000000000009F65D110DC0E35F450FA914134_} + x{FF0020DDA4F260D31F01ED44D0D31FD166BAF2A1F80001D307D4D1821804A817C80073FB0201FB00A4C8CB1FC9ED54} + x{00000001} +``` + +You may notice that the sequence number stored in the persistent data has changed (in our example, to 0x9A16 = 39446), and the `last_trans_lt` field (the logical time of the last transaction of this account) has been increased. + +Now we can inspect the state of our new smart contract: + +```cpp +> getaccount 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb +or +> getaccount 0:2ee9b4fd4f077c9b223280c35763df9edab0b41ac20d36f4009677df95c3afe2 +``` + +Now we see: + +```cpp +got account state for 0:2EE9B4FD4F077C9B223280C35763DF9EDAB0B41AC20D36F4009677DF95C3AFE2 with respect to blocks (-1,8000000000000000,16481):890F4D549428B2929F5D5E0C5719FBCDA60B308BA4B907797C9E846E644ADF26:22387176928F7BCEF654411CA820D858D57A10BBF1A0E153E1F77DE2EFB2A3FB and (-1,8000000000000000,16481):890F4D549428B2929F5D5E0C5719FBCDA60B308BA4B907797C9E846E644ADF26:22387176928F7BCEF654411CA820D858D57A10BBF1A0E153E1F77DE2EFB2A3FB +account state is (account + addr:(addr_std + anycast:nothing workchain_id:0 address:x2EE9B4FD4F077C9B223280C35763DF9EDAB0B41AC20D36F4009677DF95C3AFE2) + storage_stat:(storage_info + used:(storage_used + cells:(var_uint len:1 value:1) + bits:(var_uint len:1 value:111) + public_cells:(var_uint len:0 value:0)) last_paid:1553210152 + due_payment:nothing) + storage:(account_storage last_trans_lt:16413000004 + balance:(currencies + grams:(nanograms + amount:(var_uint len:5 value:6666000000)) + other:(extra_currencies + dict:hme_empty)) + state:account_uninit)) +x{CFF60C04141C6A7B96D68615E7A91D265AD0F3A9A922E9AE9C901D4FA83F5D3C0D02025BC2E4A0D9400000000F492A0511406354C5A004_} +``` + +Our new smart contract has some positive balance (of 6.666 test Toncoin) but has no code or data (reflected by `state:account_uninit`). + +## 7. Uploading the code and data of new smart contract + +Now you can finally upload the external message with the `StateInit` of the new smart contract which contains its code and data: + +```cpp +> sendfile my_wallet_name-query.boc +... external message status is 1 +> last +... +> getaccount 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb +... +got account state for 0:2EE9B4FD4F077C9B223280C35763DF9EDAB0B41AC20D36F4009677DF95C3AFE2 with respect to blocks (-1,8000000000000000,16709):D223B25D8D68401B4AA19893C00221CF9AB6B4E5BFECC75FD6048C27E001E0E2:4C184191CE996CF6F91F59CAD9B99B2FD5F3AA6F55B0B6135069AB432264358E and (-1,8000000000000000,16709):D223B25D8D68401B4AA19893C00221CF9AB6B4E5BFECC75FD6048C27E001E0E2:4C184191CE996CF6F91F59CAD9B99B2FD5F3AA6F55B0B6135069AB432264358E +account state is (account + addr:(addr_std + anycast:nothing workchain_id:0 address:x2EE9B4FD4F077C9B223280C35763DF9EDAB0B41AC20D36F4009677DF95C3AFE2) + storage_stat:(storage_info + used:(storage_used + cells:(var_uint len:1 value:3) + bits:(var_uint len:2 value:963) + public_cells:(var_uint len:0 value:0)) last_paid:1553210725 + due_payment:nothing) + storage:(account_storage last_trans_lt:16625000002 + balance:(currencies + grams:(nanograms + amount:(var_uint len:5 value:5983177000)) + other:(extra_currencies + dict:hme_empty)) + state:(account_active + ( + split_depth:nothing + special:nothing + code:(just + value:(raw@^Cell + x{} + x{FF0020DDA4F260810200D71820D70B1FED44D0D7091FD709FFD15112BAF2A122F901541044F910F2A2F80001D7091F3120D74A97D70907D402FB00DED1A4C8CB1FCBFFC9ED54} + )) + data:(just + value:(raw@^Cell + x{} + x{00000001F61CF0BC8E891AD7636E0CD35229D579323AA2DA827EB85D8071407464DC2FA3} + )) + library:hme_empty)))) +x{CFF60C04141C6A7B96D68615E7A91D265AD0F3A9A922E9AE9C901D4FA83F5D3C0D020680F0C2E4A0EB280000000F7BB57909405928024A134_} + x{FF0020DDA4F260810200D71820D70B1FED44D0D7091FD709FFD15112BAF2A122F901541044F910F2A2F80001D7091F3120D74A97D70907D402FB00DED1A4C8CB1FCBFFC9ED54} + x{00000001F61CF0BC8E891AD7636E0CD35229D579323AA2DA827EB85D8071407464DC2FA3} +``` + +You will see that the smart contract has been initialized using code and data from the `StateInit` of the external message, and its balance has been slightly decreased because of the processing fees. Now it is up and running, and you can activate it by generating new external messages and uploading them to TON Blockchain using the `sendfile` command of the Lite Client. + +## 8. Using the simple wallet smart contract + +Actually, the simple wallet smart contract used in this example can be used to transfer test TON to any other account. It is in this respect similar to the test-giver smart contract discussed above, with the difference that it processes only external messages signed by the correct private key (of its owner). In our case, it is the private key saved into the file `my_wallet_name.pk` during the compilation of the smart contract (see Section 3). + +An example of how you might use this smart contract is provided in the sample file `crypto/smartcont/wallet.fif` : + +```cpp +#!/usr/bin/env fift -s +"TonUtil.fif" include + +{ ."usage: " @' $0 type ." [-B ] []" cr + ."Creates a request to simple wallet created by new-wallet.fif, with private key loaded from file .pk " + ."and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" cr 1 halt +} : usage +$# dup 4 < swap 5 > or ' usage if +def? $6 { @' $5 "-B" $= { @' $6 =: body-boc-file [forget] $6 def? $7 { @' $7 =: $5 [forget] $7 } { [forget] $5 } cond + @' $# 2- =: $# } if } if + +true constant bounce + +$1 =: file-base +$2 bounce parse-load-address =: bounce 2=: dest_addr +$3 parse-int =: seqno +$4 $>GR =: amount +def? $5 { @' $5 } { "wallet-query" } cond constant savefile + +file-base +".addr" load-address +2dup 2constant wallet_addr +."Source wallet address = " 2dup .addr cr 6 .Addr cr +file-base +".pk" load-keypair nip constant wallet_pk + +def? body-boc-file { @' body-boc-file file>B B>boc } { } cond +constant body-cell + +."Transferring " amount .GR ."to account " +dest_addr 2dup bounce 7 + .Addr ." = " .addr +."seqno=0x" seqno x. ."bounce=" bounce . cr +."Body of transfer message is " body-cell + +dup ."signing message: " +dup ."resulting external message: " B dup Bx. cr +savefile +".boc" tuck B>file +."(Saved to file " type .")" cr +``` + +You can invoke this script as follows: + +```bash +$ fift -I/crypto/fift/lib:/crypto/smartcont -s wallet.fif +``` + +or simply: + +```bash +$ fift -s wallet.fif +``` + +if you have correctly set up `PATH` and `FIFTPATH`. + +For example: + +```bash +$ fift -s wallet.fif my_wallet_name kf8Ty2EqAKfAksff0upF1gOptUWRukyI9x5wfgCbh58Pss9j 1 .666 +``` + +Here `my_wallet_name` is the identifier of your wallet used before with `new-wallet.fif`. The address and the private key of your test wallet will be loaded from files `my_wallet_name.addr` and `my_wallet_name.pk` in the current directory. + +When you run this code (by invoking the Fift interpreter), you create an external message with a destination equal to the address of your wallet smart contract, containing a correct Ed25519 signature, sequence number, and an enveloped internal message from your wallet smart contract to the smart contract indicated in `dest_addr` with an arbitrary value attached and an arbitrary payload. When your smart contract receives and processes this external message, it first checks the signature and sequence number. If they are correct, it accepts the external message, sends the embedded internal message from itself to the intended destination, and increases the sequence number in its persistent data (this is a simple measure to prevent replay attacks in case this sample wallet smart contract code ends up being used in a real wallet application). + +Of course, a true TON Blockchain wallet application would hide all the intermediate steps explained above. It would first communicate the address of the new smart contract to the user, asking them to transfer some funds to the indicated address (displayed in its non-bounceable, user-friendly form) from another wallet or a cryptocurrency exchange, and then would provide a simple interface to display the current balance and to transfer funds to whatever other addresses the user wants. (The aim of this document is to explain how to create new non-trivial smart contracts and experiment with the TON Blockchain Test Network, rather than to explain how one could use the Lite Client instead of a more user-friendly wallet application.) + +One final remark: The above examples used smart contracts in the basic workchain (workchain 0). They would function exactly the same way in the masterchain (workchain -1) if one passes workchain identifier -1 instead of 0 as the first argument to `new-wallet.fif`. The only difference is that the processing and storage fees in the basic workchain are 100-1,000 times lower than in the masterchain. Some smart contracts (such as the validator election smart contract) accept transfers only from masterchain smart contracts, so you'll need a wallet on the masterchain if you wish to make stakes on behalf of your own validator and participate in the elections. From 00b1e54950faa77da244ec770b7a414c2afda5d4 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:38 +0800 Subject: [PATCH 114/219] New translations subresolvers.md (Chinese Simplified) --- .../current/develop/howto/subresolvers.md | 446 ++++++++++++++++++ 1 file changed, 446 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/subresolvers.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/subresolvers.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/subresolvers.md new file mode 100644 index 0000000000..7039c2df2f --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/howto/subresolvers.md @@ -0,0 +1,446 @@ +# TON DNS resolvers + +## Introduction + +TON DNS is a powerful tool. It allows not only to assign TON Sites/Storage bags to domains, but also to set up subdomain resolving. + +## Relevant links + +1. [TON smart-contract address system](/learn/overviews/addresses) +2. [TEP-0081 - TON DNS Standard](https://github.com/ton-blockchain/TEPs/blob/master/text/0081-dns-standard.md) +3. [Source code of .ton DNS collection](https://tonscan.org/address/EQC3dNlesgVD8YbAazcauIrXBPfiVhMMr5YYk2in0Mtsz0Bz#source) +4. [Source code of .t.me DNS collection](https://tonscan.org/address/EQCA14o1-VWhS2efqoh_9M1b_A9DtKTuoqfmkn83AbJzwnPi#source) +5. [Domain contracts searcher](https://tonscan.org/address/EQDkAbAZNb4uk-6pzTPDO2s0tXZweN-2R08T2Wy6Z3qzH_Zp#source) +6. [Simple subdomain manager code](https://github.com/Gusarich/simple-subdomain/blob/198485bbc9f7f6632165b7ab943902d4e125d81a/contracts/subdomain-manager.fc) + +## Domain contracts searcher + +Subdomains have practical use. For example, blockchain explorers don't currently provide way to find domain contract by its name. Let's explore how to create contract that gives an opportunity to find such domains. + +:::info +This contract is deployed at [EQDkAbAZNb4uk-6pzTPDO2s0tXZweN-2R08T2Wy6Z3qzH\_Zp](https://tonscan.org/address/EQDkAbAZNb4uk-6pzTPDO2s0tXZweN-2R08T2Wy6Z3qzH_Zp#source) and linked to `resolve-contract.ton`. To test it, you may write `.resolve-contract.ton` in the address bar of your favourite TON explorer and get to the page of TON DNS domain contract. Subdomains and .t.me domains are supported as well. + +You can attempt to see the resolver code by going to `resolve-contract.ton.resolve-contract.ton`. Unfortunately, that will not show you the subresolver (that is a different smart-contract), you will see the page of domain contract itself. +::: + +### dnsresolve() code + +Some repeated parts are omitted. + +```func +(int, cell) dnsresolve(slice subdomain, int category) method_id { + int subdomain_bits = slice_bits(subdomain); + throw_unless(70, (subdomain_bits % 8) == 0); + + int starts_with_zero_byte = subdomain.preload_int(8) == 0; ;; assuming that 'subdomain' is not empty + if (starts_with_zero_byte) { + subdomain~load_uint(8); + if (subdomain.slice_bits() == 0) { ;; current contract has no DNS records by itself + return (8, null()); + } + } + + ;; we are loading some subdomain + ;; supported subdomains are "ton\\0", "me\\0t\\0" and "address\\0" + + slice subdomain_sfx = null(); + builder domain_nft_address = null(); + + if (subdomain.starts_with("746F6E00"s)) { + ;; we're resolving + ;; "ton" \\0 \\0 [subdomain_sfx] + subdomain~skip_bits(32); + + ;; reading domain name + subdomain_sfx = subdomain; + while (subdomain_sfx~load_uint(8)) { } + + subdomain~skip_last_bits(8 + slice_bits(subdomain_sfx)); + + domain_nft_address = get_ton_dns_nft_address_by_index(slice_hash(subdomain)); + } elseif (subdomain.starts_with("6164647265737300"s)) { + subdomain~skip_bits(64); + + domain_nft_address = subdomain~decode_base64_address_to(begin_cell()); + + subdomain_sfx = subdomain; + if (~ subdomain_sfx.slice_empty?()) { + throw_unless(71, subdomain_sfx~load_uint(8) == 0); + } + } else { + return (0, null()); + } + + if (slice_empty?(subdomain_sfx)) { + ;; example of domain being resolved: + ;; [initial, not accessible in this contract] "ton\\0resolve-contract\\0ton\\0ratelance\\0" + ;; [what is accessible by this contract] "ton\\0ratelance\\0" + ;; subdomain "ratelance" + ;; subdomain_sfx "" + + ;; we want the resolve result to point at contract of 'ratelance.ton', not its owner + ;; so we must answer that resolution is complete + "wallet"H is address of 'ratelance.ton' contract + + ;; dns_smc_address#9fd3 smc_addr:MsgAddressInt flags:(## 8) { flags <= 1 } cap_list:flags . 0?SmcCapList = DNSRecord; + ;; _ (HashmapE 256 ^DNSRecord) = DNS_RecordSet; + + cell wallet_record = begin_cell().store_uint(0x9fd3, 16).store_builder(domain_nft_address).store_uint(0, 8).end_cell(); + + if (category == 0) { + cell dns_dict = new_dict(); + dns_dict~udict_set_ref(256, "wallet"H, wallet_record); + return (subdomain_bits, dns_dict); + } elseif (category == "wallet"H) { + return (subdomain_bits, wallet_record); + } else { + return (subdomain_bits, null()); + } + } else { + ;; subdomain "resolve-contract" + ;; subdomain_sfx "ton\\0ratelance\\0" + ;; we want to pass \\0 further, so that next resolver has opportunity to process only one byte + + ;; next resolver is contract of 'resolve-contract<.ton>' + ;; dns_next_resolver#ba93 resolver:MsgAddressInt = DNSRecord; + cell resolver_record = begin_cell().store_uint(0xba93, 16).store_builder(domain_nft_address).end_cell(); + return (subdomain_bits - slice_bits(subdomain_sfx) - 8, resolver_record); + } +} +``` + +### Explanation of dnsresolve() + +- User requests `"stabletimer.ton.resolve-contract.ton"`. +- Application translates that into `"\0ton\0resolve-contract\0ton\0stabletimer\0"` (the first zero byte is optional). +- Root DNS resolver directs the request to TON DNS collection, remaining part is `"\0resolve-contract\0ton\0stabletimer\0"`. +- TON DNS collection delegates the request to the specific domain, leaving `"\0ton\0stabletimer\0"`. +- .TON DNS domain contract passes resolution to subresolver specified by editor, subdomain is `"ton\0stabletimer\0"`. + +**This is the point where dnsresolve() is invoked.** A step-by-step breakdown of how it works: + +1. It takes the subdomain and category as input. +2. If there is zero byte at the beginning, it is skipped. +3. It checks if the subdomain starts with `"ton\0"`. If so, + 1. it skips the first 32 bits (subdomain = `"resolve-contract\0"`) + 2. `subdomain_sfx` value is set to `subdomain`, and the function reads the bytes until zero byte + 3. (subdomain = `"resolve-contract\0"`, subdomain_sfx = `""`) + 4. Zero byte and subdomain_sfx are trimmed from the end of subdomain slice (subdomain = `"resolve-contract"`) + 5. Functions slice_hash and get_ton_dns_nft_address_by_index is used to convert domain name to the contract address. You can see them in [[Subresolvers#Appendix 1. Code of resolve-contract.ton|Appendix 1]]. +4. Otherwise, dnsresolve() checks if the subdomain starts with `"address\0"`. If so, it skips that prefix and reads base64 address. +5. If provided subdomain for resolution did not match any of these prefixes, function indicates failure by returning `(0, null())` (zero bytes prefix resolved with no DNS entries). +6. It then checks if the subdomain suffix is empty. An empty suffix indicates the request was fully satisfied. If the suffix is empty: + 1. dnsresolve() creates a DNS record for the "wallet" subsection of the domain, using the TON Domain contract address it retrieved. + 2. If category 0 (all DNS entries) is requested, the record is wrapped in the dictionary and returned. + 3. If category "wallet"H is requested, the record is returned as is. + 4. Otherwise, there is no DNS entry for the specified category, so function indicates that resolution was successful but didn't find any results. +7. If the suffix is not empty: + 1. The contract address obtained before is used as the next resolver. The function builds the next resolver record pointing at it. + 2. `"\0ton\0stabletimer\0"` is passed further to that contract: processed bits are bits of subdomain. + +So in summary, dnsresolve() either: + +- Fully resolves the subdomain to a DNS record +- Partially resolves it to a resolver record to pass resolution to another contract +- Returns a "domain not found" result for unknown subdomains + +:::warning +Actually, base64 addresses parsing does not work: if you attempt to enter `.address.resolve-contract.ton`, you will get an error saying that domain is misconfigured or does not exist. The reason for that is that domain names are case-insensitive (feature inherited from real DNS) and thus are converted to lowercase, taking you to some address of non-existent workchain. +::: + +### Binding the resolver + +Now that the subresolver contract is deployed, we need to point the domain to it, that is, to change domain `dns_next_resolver` record. We may do so by sending message with the following TL-B structure to the domain contract. + +1. `change_dns_record#4eb1f0f9 query_id:uint64 record_key#19f02441ee588fdb26ee24b2568dd035c3c9206e11ab979be62e55558a1d17ff record:^[dns_next_resolver#ba93 resolver:MsgAddressInt]` + +## Creating own subdomains manager + +Subdomains can be useful for regular users - for example, to link several projects to a single domain, or to link to friends' wallets. + +### Contract data + +We need to store owner's address and the _domain_->_record hash_->_record value_ dictionary in the contract data. + +```func +global slice owner; +global cell domains; + +() load_data() impure { + slice ds = get_data().begin_parse(); + owner = ds~load_msg_addr(); + domains = ds~load_dict(); +} +() save_data() impure { + set_data(begin_cell().store_slice(owner).store_dict(domains).end_cell()); +} +``` + +### Processing records update + +```func +const int op::update_record = 0x537a3491; +;; op::update_record#537a3491 domain_name:^Cell record_key:uint256 +;; value:(Maybe ^Cell) = InMsgBody; + +() recv_internal(cell in_msg, slice in_msg_body) { + if (in_msg_body.slice_empty?()) { return (); } ;; simple money transfer + + slice in_msg_full = in_msg.begin_parse(); + if (in_msg_full~load_uint(4) & 1) { return (); } ;; bounced message + + slice sender = in_msg_full~load_msg_addr(); + load_data(); + throw_unless(501, equal_slices(sender, owner)); + + int op = in_msg_body~load_uint(32); + if (op == op::update_record) { + slice domain = in_msg_body~load_ref().begin_parse(); + (cell records, _) = domains.udict_get_ref?(256, string_hash(domain)); + + int key = in_msg_body~load_uint(256); + throw_if(502, key == 0); ;; cannot update "all records" record + + if (in_msg_body~load_uint(1) == 1) { + cell value = in_msg_body~load_ref(); + records~udict_set_ref(256, key, value); + } else { + records~udict_delete?(256, key); + } + + domains~udict_set_ref(256, string_hash(domain), records); + save_data(); + } +} +``` + +We check that the incoming message contains some request, is not bounced, comes from the owner and that the request is `op::update_record`. + +Then we load domain name from the message. We can't store domains in dictionary as-is: they may have different lengths, but TVM non-prefix dictionaries can only contain keys of equal length. Thus, we calculate `string_hash(domain)` - SHA-256 of domain name; domain name is guaranteed to have an integer number of octets so that works. + +After that, we update the record for the specified domain and save new data into the contract storage. + +### Resolving domains + +```func +(slice, slice) ~parse_sd(slice subdomain) { + ;; "test\0qwerty\0" -> "test" "qwerty\0" + slice subdomain_sfx = subdomain; + while (subdomain_sfx~load_uint(8)) { } ;; searching zero byte + subdomain~skip_last_bits(slice_bits(subdomain_sfx)); + return (subdomain, subdomain_sfx); +} + +(int, cell) dnsresolve(slice subdomain, int category) method_id { + int subdomain_bits = slice_bits(subdomain); + throw_unless(70, subdomain_bits % 8 == 0); + if (subdomain.preload_uint(8) == 0) { subdomain~skip_bits(8); } + + slice subdomain_suffix = subdomain~parse_sd(); ;; "test\0" -> "test" "" + int subdomain_suffix_bits = slice_bits(subdomain_suffix); + + load_data(); + (cell records, _) = domains.udict_get_ref?(256, string_hash(subdomain)); + + if (subdomain_suffix_bits > 0) { ;; more than "\0" requested + category = "dns_next_resolver"H; + } + + int resolved = subdomain_bits - subdomain_suffix_bits; + + if (category == 0) { ;; all categories are requested + return (resolved, records); + } + + (cell value, int found) = records.udict_get_ref?(256, category); + return (resolved, value); +} +``` + +The `dnsresolve` function checks if the requested subdomain contains integer number of octets, skips optional zero byte in the beginning of the subdomain slice, then splits it into the topmost-level domain and everything other (`test\0qwerty\0` gets split into `test` and `qwerty\0`). The record dictionary corresponding to the requested domain is loaded. + +If there is a non-empty subdomain suffix, the function returns the number of bytes resolved and the next resolver record, found at `"dns_next_resolver"H` key. Otherwise, the function returns the number of bytes resolved (that is, the full slice length) and the record requested. + +There is a way to improve this function by handling errors more gracefully, but it is not strictly required. + +## Appendix 1. Code of resolve-contract.ton + +
+subresolver.fc + +```func showLineNumbers +(builder, ()) ~store_slice(builder to, slice s) asm "STSLICER"; +int starts_with(slice a, slice b) asm "SDPFXREV"; + +const slice ton_dns_minter = "EQC3dNlesgVD8YbAazcauIrXBPfiVhMMr5YYk2in0Mtsz0Bz"a; +cell ton_dns_domain_code() asm """ + B{} + B>boc + PUSHREF +"""; + +const slice tme_minter = "EQCA14o1-VWhS2efqoh_9M1b_A9DtKTuoqfmkn83AbJzwnPi"a; +cell tme_domain_code() asm """ + B{} + B>boc + PUSHREF +"""; + +cell calculate_ton_dns_nft_item_state_init(int item_index) inline { + cell data = begin_cell().store_uint(item_index, 256).store_slice(ton_dns_minter).end_cell(); + return begin_cell().store_uint(0, 2).store_dict(ton_dns_domain_code()).store_dict(data).store_uint(0, 1).end_cell(); +} + +cell calculate_tme_nft_item_state_init(int item_index) inline { + cell config = begin_cell().store_uint(item_index, 256).store_slice(tme_minter).end_cell(); + cell data = begin_cell().store_ref(config).store_maybe_ref(null()).end_cell(); + return begin_cell().store_uint(0, 2).store_dict(tme_domain_code()).store_dict(data).store_uint(0, 1).end_cell(); +} + +builder calculate_nft_item_address(int wc, cell state_init) inline { + return begin_cell() + .store_uint(4, 3) + .store_int(wc, 8) + .store_uint(cell_hash(state_init), 256); +} + +builder get_ton_dns_nft_address_by_index(int index) inline { + cell state_init = calculate_ton_dns_nft_item_state_init(index); + return calculate_nft_item_address(0, state_init); +} + +builder get_tme_nft_address_by_index(int index) inline { + cell state_init = calculate_tme_nft_item_state_init(index); + return calculate_nft_item_address(0, state_init); +} + +(slice, builder) decode_base64_address_to(slice readable, builder target) inline { + builder addr_with_flags = begin_cell(); + repeat(48) { + int char = readable~load_uint(8); + if (char >= "a"u) { + addr_with_flags~store_uint(char - "a"u + 26, 6); + } elseif ((char == "_"u) | (char == "/"u)) { + addr_with_flags~store_uint(63, 6); + } elseif (char >= "A"u) { + addr_with_flags~store_uint(char - "A"u, 6); + } elseif (char >= "0"u) { + addr_with_flags~store_uint(char - "0"u + 52, 6); + } else { + addr_with_flags~store_uint(62, 6); + } + } + + slice addr_with_flags = addr_with_flags.end_cell().begin_parse(); + addr_with_flags~skip_bits(8); + addr_with_flags~skip_last_bits(16); + + target~store_uint(4, 3); + target~store_slice(addr_with_flags); + return (readable, target); +} + +slice decode_base64_address(slice readable) method_id { + (slice _remaining, builder addr) = decode_base64_address_to(readable, begin_cell()); + return addr.end_cell().begin_parse(); +} + +(int, cell) dnsresolve(slice subdomain, int category) method_id { + int subdomain_bits = slice_bits(subdomain); + + throw_unless(70, (subdomain_bits % 8) == 0); + + int starts_with_zero_byte = subdomain.preload_int(8) == 0; ;; assuming that 'subdomain' is not empty + if (starts_with_zero_byte) { + subdomain~load_uint(8); + if (subdomain.slice_bits() == 0) { ;; current contract has no DNS records by itself + return (8, null()); + } + } + + ;; we are loading some subdomain + ;; supported subdomains are "ton\\0", "me\\0t\\0" and "address\\0" + + slice subdomain_sfx = null(); + builder domain_nft_address = null(); + + if (subdomain.starts_with("746F6E00"s)) { + ;; we're resolving + ;; "ton" \\0 \\0 [subdomain_sfx] + subdomain~skip_bits(32); + + ;; reading domain name + subdomain_sfx = subdomain; + while (subdomain_sfx~load_uint(8)) { } + + subdomain~skip_last_bits(8 + slice_bits(subdomain_sfx)); + + domain_nft_address = get_ton_dns_nft_address_by_index(slice_hash(subdomain)); + } elseif (subdomain.starts_with("6D65007400"s)) { + ;; "t" \\0 "me" \\0 \\0 [subdomain_sfx] + subdomain~skip_bits(40); + + ;; reading domain name + subdomain_sfx = subdomain; + while (subdomain_sfx~load_uint(8)) { } + + subdomain~skip_last_bits(8 + slice_bits(subdomain_sfx)); + + domain_nft_address = get_tme_nft_address_by_index(string_hash(subdomain)); + } elseif (subdomain.starts_with("6164647265737300"s)) { + subdomain~skip_bits(64); + + domain_nft_address = subdomain~decode_base64_address_to(begin_cell()); + + subdomain_sfx = subdomain; + if (~ subdomain_sfx.slice_empty?()) { + throw_unless(71, subdomain_sfx~load_uint(8) == 0); + } + } else { + return (0, null()); + } + + if (slice_empty?(subdomain_sfx)) { + ;; example of domain being resolved: + ;; [initial, not accessible in this contract] "ton\\0resolve-contract\\0ton\\0ratelance\\0" + ;; [what is accessible by this contract] "ton\\0ratelance\\0" + ;; subdomain "ratelance" + ;; subdomain_sfx "" + + ;; we want the resolve result to point at contract of 'ratelance.ton', not its owner + ;; so we must answer that resolution is complete + "wallet"H is address of 'ratelance.ton' contract + + ;; dns_smc_address#9fd3 smc_addr:MsgAddressInt flags:(## 8) { flags <= 1 } cap_list:flags . 0?SmcCapList = DNSRecord; + ;; _ (HashmapE 256 ^DNSRecord) = DNS_RecordSet; + + cell wallet_record = begin_cell().store_uint(0x9fd3, 16).store_builder(domain_nft_address).store_uint(0, 8).end_cell(); + + if (category == 0) { + cell dns_dict = new_dict(); + dns_dict~udict_set_ref(256, "wallet"H, wallet_record); + return (subdomain_bits, dns_dict); + } elseif (category == "wallet"H) { + return (subdomain_bits, wallet_record); + } else { + return (subdomain_bits, null()); + } + } else { + ;; example of domain being resolved: + ;; [initial, not accessible in this contract] "ton\\0resolve-contract\\0ton\\0resolve-contract\\0ton\\0ratelance\\0" + ;; [what is accessible by this contract] "ton\\0resolve-contract\\0ton\\0ratelance\\0" + ;; subdomain "resolve-contract" + ;; subdomain_sfx "ton\\0ratelance\\0" + ;; and we want to pass \\0 further, so that next resolver has opportunity to process only one byte + + ;; next resolver is contract of 'resolve-contract<.ton>' + ;; dns_next_resolver#ba93 resolver:MsgAddressInt = DNSRecord; + cell resolver_record = begin_cell().store_uint(0xba93, 16).store_builder(domain_nft_address).end_cell(); + return (subdomain_bits - slice_bits(subdomain_sfx) - 8, resolver_record); + } +} + +() recv_internal() { + return (); +} +``` + +
From 143cd7e4c7e80d2ef4839ba0d2842008345dc5ce Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:39 +0800 Subject: [PATCH 115/219] New translations adnl-tcp.md (Chinese Simplified) --- .../current/develop/network/adnl-tcp.md | 573 ++++++++++++++++++ 1 file changed, 573 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/network/adnl-tcp.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/network/adnl-tcp.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/network/adnl-tcp.md new file mode 100644 index 0000000000..986b61650f --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/network/adnl-tcp.md @@ -0,0 +1,573 @@ +# ADNL TCP - Liteserver + +This is the low level protocol on which all interaction in the TON network is built, it can work on top of any protocol, but is most often used on top of TCP and UDP. UDP is used for communication between nodes, and TCP is used for communication with lite servers. + +Now we will analyze ADNL running over TCP and learn how to interact with lite servers directly. + +In the TCP version of ADNL, network nodes use public keys ed25519 as addresses and establish a connection using a shared key obtained using the Elliptic Curve Diffie-Hellman procedure - ECDH. + +## Packet Structure + +Each ADNL TCP packet, except for the handshake, has the following structure: + +- 4 bytes of packet size in little endian (N) +- 32 bytes nonce [[?]](## "Random bytes to protect against checksum attacks") +- (N - 64) payload bytes +- 32 bytes SHA256 checksum from nonce and payload + +The entire packet, including the size, is **AES-CTR** encrypted. +After decryption, it is necessary to check whether the checksum matches the data, to check, you just need to calculate the checksum yourself and compare the result with what we have in the packet. + +The handshake packet is an exception, it is transmitted in a partially unencrypted form and is described in the next chapter. + +## Establishing a connection + +To establish a connection, we need to know the ip, port and public key of the server, and generate our own private and public key ed25519. + +Public server data such as ip, port and key can be obtained from the [global config](https://ton-blockchain.github.io/global.config.json). IP in the config in numerical form, it can be brought to normal form using, for example [this tool](https://www.browserling.com/tools/dec-to-ip). The public key in the config in base64 format. + +The client generates 160 random bytes, some of which will be used by the parties as the basis for AES encryption. + +Of these, 2 permanent AES-CTR ciphers are created, which will be used by the parties to encrypt/decrypt messages after the handshake. + +- Cipher A - key 0 - 31 bytes, iv 64 - 79 bytes +- Cipher B - key 32 - 63 bytes, iv 80 - 95 bytes + +The ciphers are applied in this order: + +- Cipher A is used by the server to encrypt the messages it sends. +- Cipher A is used by the client to decrypt received messages. +- Cipher B is used by the client to encrypt the messages it sends. +- Cipher B is used by the server to decrypt received messages. + +To establish a connection, the client must send a handshake packet containing: + +- [32 bytes] **Server key ID** [[Details]](#getting-key-id) +- [32 bytes] **Our public key is ed25519** +- [32 bytes] **SHA256 hash from our 160 bytes** +- [160 bytes] **Our 160 bytes encrypted** [[Details]](#handshake-packet-data-encryption) + +When receiving a handshake packet, the server will do the same actions, receive an ECDH key, decrypt 160 bytes and create 2 permanent keys. If everything works out, the server will respond with an empty ADNL packet, without payload, to decrypt which (as well as subsequent ones) we need to use one of the permanent ciphers. + +From this point on, the connection can be considered established. + +After we have established a connection, we can start receiving information; the TL language is used to serialize data. + +[More about TL](/develop/data-formats/tl) + +## Ping&Pong + +It is optimal to send a ping packet once every 5 seconds. This is necessary to maintain the connection while no data is being transmitted, otherwise the server may terminate the connection. + +The ping packet, like all the others, is built according to the standard schema described [above](#packet-structure), and carries the request ID and ping ID as payload data. + +Let's find the desired schema for the ping request [here](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/ton_api.tl#L35) and calculate the schema id as +`crc32_IEEE("tcp.ping random_id:long = tcp.Pong")`. When converted to little endian bytes, we get **9a2b084d**. + +Thus, our ADNL ping packet will look like this: + +- 4 bytes of packet size in little endian -> 64 + (4+8) = **76** +- 32 bytes nonce -> random 32 bytes +- 4 bytes of ID TL schema -> **9a2b084d** +- 8 bytes of request id -> random uint64 number +- 32 bytes of SHA256 checksum from nonce and payload + +We send our packet and wait for [tcp.pong](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/ton_api.tl#L23), `random_id` will be equal to the one we sent in ping packet. + +## Receiving information from a Liteserver + +All requests that are aimed at obtaining information from the blockchain are wrapped in [Liteserver Query](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/lite_api.tl#L83) schema, which in turn is wrapped in [ADNL Query](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/lite_api.tl#L22) schema. + +LiteQuery: +`liteServer.query data:bytes = Object`, id **df068c79** + +ADNLQuery: +`adnl.message.query query_id:int256 query:bytes = adnl.Message`, id **7af98bb4** + +LiteQuery is passed inside ADNLQuery, as `query:bytes`, and the final query is passed inside LiteQuery, as `data:bytes`. + +[Parsing encoding bytes in TL](/develop/data-formats/tl) + +### getMasterchainInfo + +Now, since we already know how to generate TL packets for the Lite API, we can request information about the current TON masterchain block. +The masterchain block is used in many further requests as an input parameter to indicate the state (moment) in which we need information. + +We are looking for the [TL schema we need](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/lite_api.tl#L60), calculate its ID and build the packet: + +- 4 bytes of packet size in little endian -> 64 + (4+32+(1+4+(1+4+3)+3)) = **116** +- 32 bytes nonce -> random 32 bytes +- 4 bytes of ID ADNLQuery schema -> **7af98bb4** +- 32 bytes `query_id:int256` -> random 32 bytes +- - 1 byte array size -> **12** +- - 4 byte of ID LiteQuery schema -> **df068c79** +- - - 1 byte array size -> **4** +- - - 4 bytes of ID getMasterchainInfo schema -> **2ee6b589** +- - - 3 zero bytes of padding (alignment to 8) +- - 3 zero bytes of padding (alignment to 16) +- 32 bytes of checksum SHA256 from nonce and payload + +Packet example in hex: + +``` +74000000 -> packet size (116) +5fb13e11977cb5cff0fbf7f23f674d734cb7c4bf01322c5e6b928c5d8ea09cfd -> nonce + 7af98bb4 -> ADNLQuery + 77c1545b96fa136b8e01cc08338bec47e8a43215492dda6d4d7e286382bb00c4 -> query_id + 0c -> array size + df068c79 -> LiteQuery + 04 -> array size + 2ee6b589 -> getMasterchainInfo + 000000 -> 3 bytes of padding + 000000 -> 3 bytes of padding +ac2253594c86bd308ed631d57a63db4ab21279e9382e416128b58ee95897e164 -> sha256 +``` + +In response, we expect to receive [liteServer.masterchainInfo](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/lite_api.tl#L30), consisting of last:[ton.blockIdExt](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/tonlib_api.tl#L51) state_root_hash:int256 and init:[tonNode.zeroStateIdExt](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/ton_api.tl#L359). + +The received packet is deserialized in the same way as the sent one - has same algorithm, but in the opposite direction, except that the response is wrapped only in [ADNLAnswer](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/lite_api.tl#L23). + +After decoding the response, we get a packet of the form: + +``` +20010000 -> packet size (288) +5558b3227092e39782bd4ff9ef74bee875ab2b0661cf17efdfcd4da4e53e78e6 -> nonce + 1684ac0f -> ADNLAnswer + 77c1545b96fa136b8e01cc08338bec47e8a43215492dda6d4d7e286382bb00c4 -> query_id (identical to request) + b8 -> array size + 81288385 -> liteServer.masterchainInfo + last:tonNode.blockIdExt + ffffffff -> workchain:int + 0000000000000080 -> shard:long + 27405801 -> seqno:int + e585a47bd5978f6a4fb2b56aa2082ec9deac33aaae19e78241b97522e1fb43d4 -> root_hash:int256 + 876851b60521311853f59c002d46b0bd80054af4bce340787a00bd04e0123517 -> file_hash:int256 + 8b4d3b38b06bb484015faf9821c3ba1c609a25b74f30e1e585b8c8e820ef0976 -> state_root_hash:int256 + init:tonNode.zeroStateIdExt + ffffffff -> workchain:int + 17a3a92992aabea785a7a090985a265cd31f323d849da51239737e321fb05569 -> root_hash:int256 + 5e994fcf4d425c0a6ce6a792594b7173205f740a39cd56f537defd28b48a0f6e -> file_hash:int256 + 000000 -> 3 bytes of padding +520c46d1ea4daccdf27ae21750ff4982d59a30672b3ce8674195e8a23e270d21 -> sha256 +``` + +### runSmcMethod + +We already know how to get the masterchain block, so now we can call any lite server methods. +Let's analyze **runSmcMethod** - this is a method that calls a function from a smart contract and returns a result. Here we need to understand some new data types such as [TL-B](/develop/data-formats/tl-b), [Cell](/develop/data-formats/cell-boc#cell) and [BoC](/develop/data-formats/cell-boc#bag-of-cells). + +To execute the smart contract method, we need to build and send a request using the TL schema: + +```tlb +liteServer.runSmcMethod mode:# id:tonNode.blockIdExt account:liteServer.accountId method_id:long params:bytes = liteServer.RunMethodResult +``` + +And wait for a response with schema: + +```tlb +liteServer.runMethodResult mode:# id:tonNode.blockIdExt shardblk:tonNode.blockIdExt shard_proof:mode.0?bytes proof:mode.0?bytes state_proof:mode.1?bytes init_c7:mode.3?bytes lib_extras:mode.4?bytes exit_code:int result:mode.2?bytes = liteServer.RunMethodResult; +``` + +In the request, we see the following fields: + +1. mode:# - uint32 bitmask of what we want to see in the response, for example, `result:mode.2?bytes` will only be present in the response if the bit with index 2 is set to one. +2. id:tonNode.blockIdExt - is our master block state that we got in the previous chapter. +3. account:[liteServer.accountId](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/lite_api.tl#L27) - workchain and smart contract address data. +4. method_id:long - 8 bytes, in which crc16 with the XMODEM table is written on behalf of the called method + bit 17 is set [[Calculation]](https://github.com/xssnick/tonutils-go/blob/88f83bc3554ca78453dd1a42e9e9ea82554e3dd2/ton/runmethod.go#L16) +5. params:bytes - [Stack](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/crypto/block/block.tlb#L783) serialized in [BoC](/develop/data-formats/cell-boc#bag-of-cells), containing arguments to call the method. [[Implementation example]](https://github.com/xssnick/tonutils-go/blob/88f83bc3554ca78453dd1a42e9e9ea82554e3dd2/tlb/stack.go) + +For example, we only need `result:mode.2?bytes`, then our mode will be equal to 0b100, that is 4. In response, we will get: + +1. mode:# -> what was sent - 4. +2. id:tonNode.blockIdExt -> our master block against which the method was executed +3. shardblk:tonNode.blockIdExt -> shard block where the contract account is located +4. exit_code:int -> 4 bytes which is the exit code when executing the method. If everything is successful, then = 0, if not, it is equal to the exception code. +5. result:mode.2?bytes -> [Stack](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/crypto/block/block.tlb#L783) serialized in [BoC](/develop/data-formats/cell-boc#bag-of-cells), containing the values returned by the method. + +Let's analyze the call and getting the result from the `a2` method of the contract `EQBL2_3lMiyywU17g-or8N7v9hDmPCpttzBPE2isF2GTzpK4`: + +Method code in FunC: + +```func +(cell, cell) a2() method_id { + cell a = begin_cell().store_uint(0xAABBCC8, 32).end_cell(); + cell b = begin_cell().store_uint(0xCCFFCC1, 32).end_cell(); + return (a, b); +} +``` + +Fill out our request: + +- `mode` = 4, we only need the result -> `04000000` +- `id` = result of execution getMasterchainInfo +- `account` = workchain 0 (4 bytes `00000000`), and int256 [obtained from our contract address](/develop/data-formats/tl-b#addresses), i.e. 32 bytes `4bdbfde5322cb2c14d7b83ea2bf0deeff610e63c2a6db7304f1368ac176193ce` +- `method_id` = [computed](https://github.com/xssnick/tonutils-go/blob/88f83bc3554ca78453dd1a42e9e9ea82554e3dd2/ton/runmethod.go#L16) id from `a2` -> `0a2e010000000000` +- `params:bytes` = Our method does not accept input parameters, so we need to pass it an empty stack (`000000`, cell 3 bytes - stack depth 0) serialized in [BoC](/develop/data-formats/cell-boc#bag-of-cells) -> `b5ee9c72010101010005000006000000` -> serialize in bytes and get `10b5ee9c72410101010005000006000000000000` 0x10 - size, 3 bytes in the end - padding. + +In response, we get: + +- `mode:#` -> not interesting +- `id:tonNode.blockIdExt` -> not interesting +- `shardblk:tonNode.blockIdExt` -> not interesting +- `exit_code:int` -> is 0 if execution was successful +- `result:mode.2?bytes` -> [Stack](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/crypto/block/block.tlb#L783) containing the data returned by the method in [BoC](/develop/data-formats/cell-boc#bag-of-cells) format, we will unpack it. + +Inside `result` we received `b5ee9c7201010501001b000208000002030102020203030400080ccffcc1000000080aabbcc8`, this is [BoC](/develop/data-formats/cell-boc#bag-of-cells) containing the data. When we deserialize it, we will get a cell: + +```json +32[00000203] -> { + 8[03] -> { + 0[], + 32[0AABBCC8] + }, + 32[0CCFFCC1] +} +``` + +If we parse it, we will get 2 values of the cell type, which our FunC method returns. +The first 3 bytes of the root cell `000002` - is the depth of the stack, that is 2. This means that the method returned 2 values. + +We continue parsing, the next 8 bits (1 byte) is the value type at the current stack level. For some types, it may take 2 bytes. Possible options can be seen in [schema](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/crypto/block/block.tlb#L766). +In our case, we have `03`, which means: + +```tlb +vm_stk_cell#03 cell:^Cell = VmStackValue; +``` + +So the type of our value is - cell, and, according to the schema, it stores the value itself as a reference. But, if we look at the stack element storage schema: + +```tlb +vm_stk_cons#_ {n:#} rest:^(VmStackList n) tos:VmStackValue = VmStackList (n + 1); +``` + +We will see that the first link `rest:^(VmStackList n)` - is the cell of the next value on the stack, and our value `tos:VmStackValue` comes second, so to get the value we need to read the second link, that is `32[0CCFFCC1]` - this is our first cell that the contract returned. + +Now we can go deeper and get the second element of the stack, we go through the first link, now we have: + +```json +8[03] -> { + 0[], + 32[0AABBCC8] + } +``` + +We repeat the same process. The first 8 bits = `03` - that is, again cell. The second reference is the value `32[0AABBCC8]` and since our stack depth is 2, we complete the pass. n total, we have 2 values returned by the contract - `32[0CCFFCC1]` and `32[0AABBCC8]`. + +Note that they are in reverse order. In the same way, you need to pass arguments when calling a function - in reverse order from what we see in the FunC code. + +[Implementation example](https://github.com/xssnick/tonutils-go/blob/46dbf5f820af066ab10c5639a508b4295e5aa0fb/ton/runmethod.go#L24) + +### getAccountState + +To get account state data such as balance, code and contract data, we can use [getAccountState](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/lite_api.tl#L68). For the request, we need a [fresh master block](#getmasterchaininfo) and account address. In response, we will receive the TL structure [AccountState](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/lite_api.tl#L38). + +Let's analyze the AccountState TL schema: + +```tlb +liteServer.accountState id:tonNode.blockIdExt shardblk:tonNode.blockIdExt shard_proof:bytes proof:bytes state:bytes = liteServer.AccountState; +``` + +1. `id` - is our master block, regarding which we got the data. +2. `shardblk` - workchain shard block where our account is located, regarding which we received data. +3. `shard_proof` - merkle proof of a shard block. +4. `proof` - merkle proof of account status. +5. `state` - [BoC](/develop/data-formats/cell-boc#bag-of-cells) TL-B [account state scheme](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/crypto/block/block.tlb#L232). + +Of all this data, what we need is in the state, we will analyze it. + +For example, let's get the status of account `EQAhE3sLxHZpsyZ_HecMuwzvXHKLjYx4kEUehhOy2JmCcHCT`, `state` in the response will be (at the moment of writing this article): + +```hex +b5ee9c720102350100051e000277c0021137b0bc47669b3267f1de70cbb0cef5c728b8d8c7890451e8613b2d899827026a886043179d3f6000006e233be8722201d7d239dba7d818134001020114ff00f4a413f4bcf2c80b0d021d0000000105036248628d00000000e003040201cb05060013a03128bb16000000002002012007080043d218d748bc4d4f4ff93481fd41c39945d5587b8e2aa2d8a35eaf99eee92d9ba96004020120090a0201200b0c00432c915453c736b7692b5b4c76f3a90e6aeec7a02de9876c8a5eee589c104723a18020004307776cd691fbe13e891ed6dbd15461c098b1b95c822af605be8dc331e7d45571002000433817dc8de305734b0c8a3ad05264e9765a04a39dbe03dd9973aa612a61f766d7c02000431f8c67147ceba1700d3503e54c0820f965f4f82e5210e9a3224a776c8f3fad1840200201200e0f020148101104daf220c7008e8330db3ce08308d71820f90101d307db3c22c00013a1537178f40e6fa1f29fdb3c541abaf910f2a006f40420f90101d31f5118baf2aad33f705301f00a01c20801830abcb1f26853158040f40e6fa120980ea420c20af2670edff823aa1f5340b9f2615423a3534e2a2d2b2c0202cc12130201201819020120141502016616170003d1840223f2980bc7a0737d0986d9e52ed9e013c7a21c2b2f002d00a908b5d244a824c8b5d2a5c0b5007404fc02ba1b04a0004f085ba44c78081ba44c3800740835d2b0c026b500bc02f21633c5b332781c75c8f20073c5bd0032600201201a1b02012020210115bbed96d5034705520db3c8340201481c1d0201201e1f0173b11d7420c235c6083e404074c1e08075313b50f614c81e3d039be87ca7f5c2ffd78c7e443ca82b807d01085ba4d6dc4cb83e405636cf0069006031003daeda80e800e800fa02017a0211fc8080fc80dd794ff805e47a0000e78b64c00015ae19574100d56676a1ec40020120222302014824250151b7255b678626466a4610081e81cdf431c24d845a4000331a61e62e005ae0261c0b6fee1c0b77746e102d0185b5599b6786abe06fedb1c68a2270081e8f8df4a411c4605a400031c34410021ae424bae064f613990039e2ca840090081e886052261c52261c52265c4036625ccd88302d02012026270203993828290111ac1a6d9e2f81b609402d0015adf94100cc9576a1ec1840010da936cf0557c1602d0015addc2ce0806ab33b50f6200220db3c02f265f8005043714313db3ced542d34000ad3ffd3073004a0db3c2fae5320b0f26212b102a425b3531cb9b0258100e1aa23a028bcb0f269820186a0f8010597021110023e3e308e8d11101fdb3c40d778f44310bd05e254165b5473e7561053dcdb3c54710a547abc2e2f32300020ed44d0d31fd307d307d33ff404f404d10048018e1a30d20001f2a3d307d3075003d70120f90105f90115baf2a45003e06c2170542013000c01c8cbffcb0704d6db3ced54f80f70256e5389beb198106e102d50c75f078f1b30542403504ddb3c5055a046501049103a4b0953b9db3c5054167fe2f800078325a18e2c268040f4966fa52094305303b9de208e1638393908d2000197d3073016f007059130e27f080705926c31e2b3e63006343132330060708e2903d08308d718d307f40430531678f40e6fa1f2a5d70bff544544f910f2a6ae5220b15203bd14a1236ee66c2232007e5230be8e205f03f8009322d74a9802d307d402fb0002e83270c8ca0040148040f44302f0078e1771c8cb0014cb0712cb0758cf0158cf1640138040f44301e201208e8a104510344300db3ced54925f06e234001cc8cb1fcb07cb07cb3ff400f400c9 +``` + +[Parse this BoC](/develop/data-formats/cell-boc#bag-of-cells) and get + +
+ large cell + +```json +473[C0021137B0BC47669B3267F1DE70CBB0CEF5C728B8D8C7890451E8613B2D899827026A886043179D3F6000006E233BE8722201D7D239DBA7D818130_] -> { + 80[FF00F4A413F4BCF2C80B] -> { + 2[0_] -> { + 4[4_] -> { + 8[CC] -> { + 2[0_] -> { + 13[D180], + 141[F2980BC7A0737D0986D9E52ED9E013C7A218] -> { + 40[D3FFD30730], + 48[01C8CBFFCB07] + } + }, + 6[64] -> { + 178[00A908B5D244A824C8B5D2A5C0B5007404FC02BA1B048_], + 314[085BA44C78081BA44C3800740835D2B0C026B500BC02F21633C5B332781C75C8F20073C5BD00324_] + } + }, + 2[0_] -> { + 2[0_] -> { + 84[BBED96D5034705520DB3C_] -> { + 112[C8CB1FCB07CB07CB3FF400F400C9] + }, + 4[4_] -> { + 2[0_] -> { + 241[AEDA80E800E800FA02017A0211FC8080FC80DD794FF805E47A0000E78B648_], + 81[AE19574100D56676A1EC0_] + }, + 458[B11D7420C235C6083E404074C1E08075313B50F614C81E3D039BE87CA7F5C2FFD78C7E443CA82B807D01085BA4D6DC4CB83E405636CF0069004_] -> { + 384[708E2903D08308D718D307F40430531678F40E6FA1F2A5D70BFF544544F910F2A6AE5220B15203BD14A1236EE66C2232] + } + } + }, + 2[0_] -> { + 2[0_] -> { + 323[B7255B678626466A4610081E81CDF431C24D845A4000331A61E62E005AE0261C0B6FEE1C0B77746E0_] -> { + 128[ED44D0D31FD307D307D33FF404F404D1] + }, + 531[B5599B6786ABE06FEDB1C68A2270081E8F8DF4A411C4605A400031C34410021AE424BAE064F613990039E2CA840090081E886052261C52261C52265C4036625CCD882_] -> { + 128[ED44D0D31FD307D307D33FF404F404D1] + } + }, + 4[4_] -> { + 2[0_] -> { + 65[AC1A6D9E2F81B6090_] -> { + 128[ED44D0D31FD307D307D33FF404F404D1] + }, + 81[ADF94100CC9576A1EC180_] + }, + 12[993_] -> { + 50[A936CF0557C14_] -> { + 128[ED44D0D31FD307D307D33FF404F404D1] + }, + 82[ADDC2CE0806AB33B50F60_] + } + } + } + } + }, + 872[F220C7008E8330DB3CE08308D71820F90101D307DB3C22C00013A1537178F40E6FA1F29FDB3C541ABAF910F2A006F40420F90101D31F5118BAF2AAD33F705301F00A01C20801830ABCB1F26853158040F40E6FA120980EA420C20AF2670EDFF823AA1F5340B9F2615423A3534E] -> { + 128[DB3C02F265F8005043714313DB3CED54] -> { + 128[ED44D0D31FD307D307D33FF404F404D1], + 112[C8CB1FCB07CB07CB3FF400F400C9] + }, + 128[ED44D0D31FD307D307D33FF404F404D1], + 40[D3FFD30730], + 640[DB3C2FAE5320B0F26212B102A425B3531CB9B0258100E1AA23A028BCB0F269820186A0F8010597021110023E3E308E8D11101FDB3C40D778F44310BD05E254165B5473E7561053DCDB3C54710A547ABC] -> { + 288[018E1A30D20001F2A3D307D3075003D70120F90105F90115BAF2A45003E06C2170542013], + 48[01C8CBFFCB07], + 504[5230BE8E205F03F8009322D74A9802D307D402FB0002E83270C8CA0040148040F44302F0078E1771C8CB0014CB0712CB0758CF0158CF1640138040F44301E2], + 856[DB3CED54F80F70256E5389BEB198106E102D50C75F078F1B30542403504DDB3C5055A046501049103A4B0953B9DB3C5054167FE2F800078325A18E2C268040F4966FA52094305303B9DE208E1638393908D2000197D3073016F007059130E27F080705926C31E2B3E63006] -> { + 112[C8CB1FCB07CB07CB3FF400F400C9], + 384[708E2903D08308D718D307F40430531678F40E6FA1F2A5D70BFF544544F910F2A6AE5220B15203BD14A1236EE66C2232], + 504[5230BE8E205F03F8009322D74A9802D307D402FB0002E83270C8CA0040148040F44302F0078E1771C8CB0014CB0712CB0758CF0158CF1640138040F44301E2], + 128[8E8A104510344300DB3CED54925F06E2] -> { + 112[C8CB1FCB07CB07CB3FF400F400C9] + } + } + } + } + } + }, + 114[0000000105036248628D00000000C_] -> { + 7[CA] -> { + 2[0_] -> { + 2[0_] -> { + 266[2C915453C736B7692B5B4C76F3A90E6AEEC7A02DE9876C8A5EEE589C104723A1800_], + 266[07776CD691FBE13E891ED6DBD15461C098B1B95C822AF605BE8DC331E7D45571000_] + }, + 2[0_] -> { + 266[3817DC8DE305734B0C8A3AD05264E9765A04A39DBE03DD9973AA612A61F766D7C00_], + 266[1F8C67147CEBA1700D3503E54C0820F965F4F82E5210E9A3224A776C8F3FAD18400_] + } + }, + 269[D218D748BC4D4F4FF93481FD41C39945D5587B8E2AA2D8A35EAF99EEE92D9BA96000] + }, + 74[A03128BB16000000000_] + } +} +``` + +
+ +Now we need to parse the cell according to the TL-B structure: + +```tlb +account_none$0 = Account; + +account$1 addr:MsgAddressInt storage_stat:StorageInfo + storage:AccountStorage = Account; +``` + +Our structure references other structures, such as: + +```tlb +anycast_info$_ depth:(#<= 30) { depth >= 1 } rewrite_pfx:(bits depth) = Anycast; +addr_std$10 anycast:(Maybe Anycast) workchain_id:int8 address:bits256 = MsgAddressInt; +addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9) workchain_id:int32 address:(bits addr_len) = MsgAddressInt; + +storage_info$_ used:StorageUsed last_paid:uint32 due_payment:(Maybe Grams) = StorageInfo; +storage_used$_ cells:(VarUInteger 7) bits:(VarUInteger 7) public_cells:(VarUInteger 7) = StorageUsed; + +account_storage$_ last_trans_lt:uint64 balance:CurrencyCollection state:AccountState = AccountStorage; + +currencies$_ grams:Grams other:ExtraCurrencyCollection = CurrencyCollection; + +var_uint$_ {n:#} len:(#< n) value:(uint (len * 8)) = VarUInteger n; +var_int$_ {n:#} len:(#< n) value:(int (len * 8)) = VarInteger n; +nanograms$_ amount:(VarUInteger 16) = Grams; + +account_uninit$00 = AccountState; +account_active$1 _:StateInit = AccountState; +account_frozen$01 state_hash:bits256 = AccountState; +``` + +As we can see, the cell contains a lot of data, but we will analyze the main cases and getting a balance. You can analyze the rest in a similar way. + +Let's start parsing. In the root cell data we have: + +``` +C0021137B0BC47669B3267F1DE70CBB0CEF5C728B8D8C7890451E8613B2D899827026A886043179D3F6000006E233BE8722201D7D239DBA7D818130_ +``` + +Convert it to binary form and get: + +``` +11000000000000100001000100110111101100001011110001000111011001101001101100110010011001111111000111011110011100001100101110110000110011101111010111000111001010001011100011011000110001111000100100000100010100011110100001100001001110110010110110001001100110000010011100000010011010101000100001100000010000110001011110011101001111110110000000000000000000000110111000100011001110111110100001110010001000100000000111010111110100100011100111011011101001111101100000011000000100110 +``` + +Let's look at our main TL-B structure, we see that we have 2 options for what can be there - `account_none$0` or `account$1`. We can understand which option we have by reading the prefix declared after the symbol $, in our case it is 1 bit. If there is 0, then we have `account_none`, or 1, then `account`. + +Our first bit from the data above = 1, so we are working with `account$1` and will use the schema: + +```tlb +account$1 addr:MsgAddressInt storage_stat:StorageInfo + storage:AccountStorage = Account; +``` + +Next we have `addr:MsgAddressInt`, we see that for MsgAddressInt we also have several options: + +```tlb +addr_std$10 anycast:(Maybe Anycast) workchain_id:int8 address:bits256 = MsgAddressInt; +addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9) workchain_id:int32 address:(bits addr_len) = MsgAddressInt; +``` + +To understand which one to work with, we, like last time, read the prefix bits, this time we read 2 bits. We cut off the already read bit, `1000000...` remains, we read the first 2 bits and get `10`, which means we are working with `addr_std$10`. + +Next we need to parse `anycast:(Maybe Anycast)`, Maybe means we should read 1 bit, and if it's one, read Anycast, otherwise skip. Our remaining bits are `00000...`, read 1 bit, it's 0, so we skip Anycast. + +Next, we have `workchain_id: int8`, everything is simple here, we read 8 bits, this will be the workchain ID. We read the next 8 bits, all zeros, so the workchain is 0. + +Next, we read `address:bits256`, this is 256 bits of the address, in the same way as with `workchain_id`. On reading, we get `21137B0BC47669B3267F1DE70CBB0CEF5C728B8D8C7890451E8613B2D8998270` in hex representation. + +We read the address `addr:MsgAddressInt`, then we have `storage_stat:StorageInfo` from the main structure, its schema is: + +```tlb +storage_info$_ used:StorageUsed last_paid:uint32 due_payment:(Maybe Grams) = StorageInfo; +``` + +First comes `used:StorageUsed`, with the schema: + +```tlb +storage_used$_ cells:(VarUInteger 7) bits:(VarUInteger 7) public_cells:(VarUInteger 7) = StorageUsed; +``` + +This is the number of cells and bits used to store account data. Each field is defined as `VarUInteger 7`, which means a uint of dynamic size, but a maximum of 7 bits. You can understand how it is arranged according to the scheme: + +```tlb +var_uint$_ {n:#} len:(#< n) value:(uint (len * 8)) = VarUInteger n; +``` + +In our case, n will be equal to 7. In len we will have `(#< 7)` which means the number of bits that can hold a number up to 7. You can determine it by translating 7-1=6 into binary form - `110`, we get 3 bits, so length len = 3 bits. And value is `(uint (len * 8))`. To determine it, we need to read 3 bits of the length, get a number and multiply by 8, this will be the size of `value`, that is, the number of bits that need to be read to get the value of VarUInteger. + +Read `cells:(VarUInteger 7)`, take our next bits from the root cell, look at the next 16 bits to understand, this is `0010011010101000`. We read the first 3 bits of len, this is `001`, i.e. 1, we get the size (uint (1 \* 8)), we get uint 8, we read 8 bits, it will be `cells`, `00110101`, i.e. 53 in decimal form. We do the same for `bits` and `public_cells`. + +We successfully read `used:StorageUsed`, next we have `last_paid:uint32`, we read 32 bits. Everything is just as simple with `due_payment:(Maybe Grams)` here Maybe, which will be 0, so we skip Grams. But, if Maybe is 1, we can look at the Grams `amount:(VarUInteger 16) = Grams` schema and immediately understand that we already know how to work with this. Like last time, only instead of 7 we have 16. + +Next we have `storage:AccountStorage` with a schema: + +```tlb +account_storage$_ last_trans_lt:uint64 balance:CurrencyCollection state:AccountState = AccountStorage; +``` + +We read `last_trans_lt:uint64`, this is 64 bits, storing lt of the last account transaction. And finally, the balance represented by the schema: + +```tlb +currencies$_ grams:Grams other:ExtraCurrencyCollection = CurrencyCollection; +``` + +From here we will read `grams:Grams` which will be the account balance in nano-tones. +`grams:Grams` is `VarUInteger 16`, to store 16 (in binary form `10000`, subtracting 1 we get `1111`), then we read the first 4 bits, and multiply the resulting value by 8, then we read the received number of bits, it is our balance. + +Let's analyze our remaining bits according to our data: + +``` +100000000111010111110100100011100111011011101001111101100000011000000100110 +``` + +Read first 4 bits - `1000`, this is 8. 8\*8=64, read next 64 bits = `0000011101011111010010001110011101101110100111110110000001100000`, removing extra zero bits, we get `11101011111010010001110011101101110100111110110000001100000`, that is equal to `531223439883591776`, and, translating from nano to TON, we get `531223439.883591776`. + +We will stop here, since we have already analyzed all the main cases, the rest can be obtained in a similar way with what we have analyzed. Also, additional information on parsing TL-B can be found in [official documentation](/develop/data-formats/tl-b-language) + +### Other methods + +Now, having studied all the information, you can call and process responses for other lite-server methods as well. Same principle :) + +## Additional technical details of the handshake + +### Getting key ID + +The key id is the SHA256 hash of the serialized TL schema. + +The most commonly used TL schemas for the keys are: + +```tlb +pub.ed25519 key:int256 = PublicKey -- ID c6b41348 +pub.aes key:int256 = PublicKey -- ID d4adbc2d +pub.overlay name:bytes = PublicKey -- ID cb45ba34 +pub.unenc data:bytes = PublicKey -- ID 0a451fb6 +pk.aes key:int256 = PrivateKey -- ID 3751e8a5 +``` + +As an example, for keys of type ED25519 that are used for handshake, the key ID will be the SHA256 hash from +**[0xC6, 0xB4, 0x13, 0x48]** and **public key**, (36 byte array, prefix + key) + +[Code example](https://github.com/xssnick/tonutils-go/blob/2b5e5a0e6ceaf3f28309b0833cb45de81c580acc/liteclient/crypto.go#L16) + +### Handshake packet data encryption + +The handshake packet is sent in a semi-open form, only 160 bytes are encrypted, containing information about permanent ciphers. + +To encrypt them, we need an AES-CTR cipher, to get it we need a SHA256 hash of 160 bytes and [ECDH shared key](#getting-a-shared-key-using-ecdh) + +The cipher is built like this: + +- key = (0 - 15 bytes of public key) + (16 - 31 bytes of hash) +- iv = (0 - 3 hash bytes) + (20 - 31 public key bytes) + +After the cipher is assembled, we encrypt our 160 bytes with it. + +[Code example](https://github.com/xssnick/tonutils-go/blob/2b5e5a0e6ceaf3f28309b0833cb45de81c580acc/liteclient/connection.go#L361) + +### Getting a shared key using ECDH + +To calculate the shared key, we need our private key and the server's public key. + +The essence of DH is to obtain a shared secret key, without disclosing private information. I will give an example of how this happens, in the most simplified form. Suppose we need to generate a shared key between us and the server, the process will look like this: + +1. We generate secret and public numbers like **6** and **7** +2. The server generates secret and public numbers like **5** and **15** +3. We exchange public numbers with the server, send **7** to the server, it sends us **15**. +4. We calculate: **7^6 mod 15 = 4** +5. The server calculates: **7^5 mod 15 = 7** +6. We exchange the received numbers, we give the server **4**, it gives us **7** +7. We calculate **7^6 mod 15 = 4** +8. The server calculates: **4^5 mod 15 = 4** +9. Shared key = **4** + +The details of the ECDH itself will be omitted for the sake of simplicity. It is calculated using 2 keys, private and public, by finding a common point on the curve. If interested, it is better to read about it separately. + +[Code example](https://github.com/xssnick/tonutils-go/blob/2b5e5a0e6ceaf3f28309b0833cb45de81c580acc/liteclient/crypto.go#L32) + +## References + +_Here a [link to the original article](https://github.com/xssnick/ton-deep-doc/blob/master/ADNL-TCP-Liteserver.md) by [Oleg Baranov](https://github.com/xssnick)._ From ce65129af336c0db21d05219a07f4fcba19e9642 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:39 +0800 Subject: [PATCH 116/219] New translations adnl-udp.md (Chinese Simplified) --- .../current/develop/network/adnl-udp.md | 362 ++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/network/adnl-udp.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/network/adnl-udp.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/network/adnl-udp.md new file mode 100644 index 0000000000..95526cf15d --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/network/adnl-udp.md @@ -0,0 +1,362 @@ +# ADNL UDP - Internode + +ADNL over UDP is used by nodes and TON components to communicate with each other. It is a low-level protocol on top of which other, higher-level TON protocols such as DHT and RLDP operate. +In this article, we will learn how ADNL over UDP works for basic communication between nodes. + +Unlike ADNL over TCP, in the UDP implementation, the handshake takes place in a different form, and an additional layer is used in the form of channels, but other principles are similar: +encryption keys are also generated based on our private key and the peer's public key, which is known in advance from the config or received from other network nodes. + +In the UDP version of ADNL, the connection is established in the same time with the receiving of initial data from the peer if the initiator sent 'create channel' message, the channel's key will be calculated and the channel creation will be confirmed. +When the channel is established, further communication will continue inside it. + +## Packet structure and communication + +### First packets + +Let's analyze the initialization of the connection with the DHT node and obtaining a signed list of its addresses in order to understand how the protocol works. + +Find the node you like in [global config](https://ton-blockchain.github.io/global.config.json), in the `dht.nodes` section. For example: + +```json +{ + "@type": "dht.node", + "id": { + "@type": "pub.ed25519", + "key": "fZnkoIAxrTd4xeBgVpZFRm5SvVvSx7eN3Vbe8c83YMk=" + }, + "addr_list": { + "@type": "adnl.addressList", + "addrs": [ + { + "@type": "adnl.address.udp", + "ip": 1091897261, + "port": 15813 + } + ], + "version": 0, + "reinit_date": 0, + "priority": 0, + "expire_at": 0 + }, + "version": -1, + "signature": "cmaMrV/9wuaHOOyXYjoxBnckJktJqrQZ2i+YaY3ehIyiL3LkW81OQ91vm8zzsx1kwwadGZNzgq4hI4PCB/U5Dw==" +} +``` + +1. Let's take its ED25519 key, `fZnkoIAxrTd4xeBgVpZFRm5SvVvSx7eN3Vbe8c83YMk`, and decode it from base64 +2. Take its IP address `1091897261` and translate it into an understandable format using [service](https://www.browserling.com/tools/dec-to-ip) or using conversion to little endian bytes, we will get `65.21.7.173` +3. Combine with the port, get `65.21.7.173:15813` and establish a UDP connection. + +We want to open a channel to communicate with node and get some information, and as the main task - to receive a list of signed addresses from it. To do this, we will generate 2 messages, the first - [create a channel](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/ton_api.tl#L129): + +```tlb +adnl.message.createChannel key:int256 date:int = adnl.Message +``` + +Here we have 2 parameters - key and date. As a date, we will specify the current unix timestamp. And for the key - we need to generate a new ED25519 private+public key pair specially for the channel, they will be used for initialization of [public encryption key](/develop/network/adnl-tcp#getting-a-shared-key-using-ecdh). We will use our generated public key in the `key` parameter of the message, and just save the private one for now. + +Serialize the filled TL structure and get: + +``` +bbc373e6 -- TL ID adnl.message.createChannel +d59d8e3991be20b54dde8b78b3af18b379a62fa30e64af361c75452f6af019d7 -- key +555c8763 -- date +``` + +Next, let's move to our main query - [get a list of addresses](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/ton_api.tl#L198). +To execute it, we first need to serialize its TL structure: + +```tlb +dht.getSignedAddressList = dht.Node +``` + +It has no parameters, so we just serialize it. It will be just its id - `ed4879a9`. + +Next, since this is a higher level request of the DHT protocol, we need to first wrap it in an `adnl.message.query` TL structure: + +```tlb +adnl.message.query query_id:int256 query:bytes = adnl.Message +``` + +As `query_id` we generate random 32 bytes, as `query` we use our main request, [wrapped as an array of bytes](/develop/data-formats/tl#encoding-bytes-array). +We will get: + +``` +7af98bb4 -- TL ID adnl.message.query +d7be82afbc80516ebca39784b8e2209886a69601251571444514b7f17fcd8875 -- query_id +04 ed4879a9 000000 -- query +``` + +### Building the packet + +All communication is carried out using packets, the content of which is [TL structure](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/ton_api.tl#L81): + +```tlb +adnl.packetContents + rand1:bytes -- random 7 or 15 bytes + flags:# -- bit flags, used to determine the presence of fields further + from:flags.0?PublicKey -- sender's public key + from_short:flags.1?adnl.id.short -- sender's ID + message:flags.2?adnl.Message -- message (used if there is only one message) + messages:flags.3?(vector adnl.Message) -- messages (if there are > 1) + address:flags.4?adnl.addressList -- list of our addresses + priority_address:flags.5?adnl.addressList -- priority list of our addresses + seqno:flags.6?long -- packet sequence number + confirm_seqno:flags.7?long -- sequence number of the last packet received + recv_addr_list_version:flags.8?int -- address version + recv_priority_addr_list_version:flags.9?int -- priority address version + reinit_date:flags.10?int -- connection reinitialization date (counter reset) + dst_reinit_date:flags.10?int -- connection reinitialization date from the last received packet + signature:flags.11?bytes -- signature + rand2:bytes -- random 7 or 15 bytes + = adnl.PacketContents + +``` + +Once we have serialized all the messages we want to send, we can start building the packet. +Packets to be sent to a channel differ in content from packets that are sent before the channel is initialized. +First, let's analyze the main packet, which is used for initialization. + +During the initial data exchange, outside the channel, the serialized content structure of the packet is prefixed with the public key of the peer - 32 bytes. +Our public key is 32 bytes, the sha256 hash of the serialized TL of the content structure of the packet - 32 bytes. +The content of the packet is encrypted using the [shared key](/develop/network/adnl-tcp#getting-a-shared-key-using-ecdh), obtained from our private key and the public key of the server. + +Serialize our packet content structure, and parse it byte by byte: + +``` +89cd42d1 -- TL ID adnl.packetContents +0f 4e0e7dd6d0c5646c204573bc47e567 -- rand1, 15 (0f) random bytes +d9050000 -- flags (0x05d9) -> 0b0000010111011001 + -- from (present because flag's zero bit = 1) +c6b41348 -- TL ID pub.ed25519 + afc46336dd352049b366c7fd3fc1b143a518f0d02d9faef896cb0155488915d6 -- key:int256 + -- messages (present because flag's third bit = 1) +02000000 -- vector adnl.Message, size = 2 messages + bbc373e6 -- TL ID adnl.message.createChannel + d59d8e3991be20b54dde8b78b3af18b379a62fa30e64af361c75452f6af019d7 -- key + 555c8763 -- date (date of creation) + + 7af98bb4 -- TL ID [adnl.message.query](/) + d7be82afbc80516ebca39784b8e2209886a69601251571444514b7f17fcd8875 -- query_id + 04 ed4879a9 000000 -- query (bytes size 4, padding 3) + -- address (present because flag's fourth bit = 1), without TL ID since it is specified explicitly +00000000 -- addrs (empty vector, because we are in client mode and do not have an address on wiretap) +555c8763 -- version (usually initialization date) +555c8763 -- reinit_date (usually initialization date) +00000000 -- priority +00000000 -- expire_at + +0100000000000000 -- seqno (present because flag's sixth bit = 1) +0000000000000000 -- confirm_seqno (present because flag's seventh bit = 1) +555c8763 -- recv_addr_list_version (present because flag's eighth bit = 1, usually initialization date) +555c8763 -- reinit_date (present because flag's tenth bit = 1, usually initialization date) +00000000 -- dst_reinit_date (present because flag's tenth bit = 1) +0f 2b6a8c0509f85da9f3c7e11c86ba22 -- rand2, 15 (0f) random bytes +``` + +After serialization - we need to sign the resulting byte array with our private client's (not channel's) ED25519 key, which we generated and saved before. +After we have generated the signature (64 bytes in size), we need to add it to the packet, serialize it again, but now add the 11th bit to the flag, which means the presence of the signature: + +``` +89cd42d1 -- TL ID adnl.packetContents +0f 4e0e7dd6d0c5646c204573bc47e567 -- rand1, 15 (0f) random bytes +d90d0000 -- flags (0x0dd9) -> 0b0000110111011001 + -- from (present because flag's zero bit = 1) +c6b41348 -- TL ID pub.ed25519 + afc46336dd352049b366c7fd3fc1b143a518f0d02d9faef896cb0155488915d6 -- key:int256 + -- messages (present because flag's third bit = 1) +02000000 -- vector adnl.Message, size = 2 message + bbc373e6 -- TL ID adnl.message.createChannel + d59d8e3991be20b54dde8b78b3af18b379a62fa30e64af361c75452f6af019d7 -- key + 555c8763 -- date (date of creation) + + 7af98bb4 -- TL ID adnl.message.query + d7be82afbc80516ebca39784b8e2209886a69601251571444514b7f17fcd8875 -- query_id + 04 ed4879a9 000000 -- query (bytes size 4, padding 3) + -- address (present because flag's fourth bit = 1), without TL ID since it is specified explicitly +00000000 -- addrs (empty vector, because we are in client mode and do not have an address on wiretap) +555c8763 -- version (usually initialization date) +555c8763 -- reinit_date (usually initialization date) +00000000 -- priority +00000000 -- expire_at + +0100000000000000 -- seqno (present because flag's sixth bit = 1) +0000000000000000 -- confirm_seqno (present because flag's seventh bit = 1) +555c8763 -- recv_addr_list_version (present because flag's eighth bit = 1, usually initialization date) +555c8763 -- reinit_date (present because flag's tenth bit = 1, usually initialization date) +00000000 -- dst_reinit_date (present because flag's tenth bit = 1) +40 b453fbcbd8e884586b464290fe07475ee0da9df0b8d191e41e44f8f42a63a710 -- signature (present because flag's eleventh bit = 1), (bytes size 64, padding 3) + 341eefe8ffdc56de73db50a25989816dda17a4ac6c2f72f49804a97ff41df502 -- + 000000 -- +0f 2b6a8c0509f85da9f3c7e11c86ba22 -- rand2, 15 (0f) random bytes +``` + +Now we have an assembled, signed and serialized packet, which is an array of bytes. +For subsequent verification of its integrity by the recipient, we need to calculate packet's sha256 hash. For example, let this be `408a2a4ed623b25a2e2ba8bbe92d01a3b5dbd22c97525092ac3203ce4044dcd2`. + +Now let's encrypt the content of our packet with the AES-CTR cipher, using [shared key](/develop/network/adnl-tcp#getting-a-shared-key-using-ecdh), obtained from our private key and the public key of the peer (not the channel's key). + +We are almost ready for sending, just remains to [calculate ID](/develop/network/adnl-tcp#getting-key-id) of ED25519 peer key and concat everything together: + +``` +daa76538d99c79ea097a67086ec05acca12d1fefdbc9c96a76ab5a12e66c7ebb -- server Key ID +afc46336dd352049b366c7fd3fc1b143a518f0d02d9faef896cb0155488915d6 -- our public key +408a2a4ed623b25a2e2ba8bbe92d01a3b5dbd22c97525092ac3203ce4044dcd2 -- sha256 content hash (before encryption) +... -- encrypted content of the packet +``` + +Now we can send our built packet to peer via UDP, and wait for a response. + +In response, we will receive a packet with similar structure, but with different messages. It will consist of: + +``` +68426d4906bafbd5fe25baf9e0608cf24fffa7eca0aece70765d64f61f82f005 -- ID of our key +2d11e4a08031ad3778c5e060569645466e52bd1bd2c7b78ddd56def1cf3760c9 -- server public key, for shared key +f32fa6286d8ae61c0588b5a03873a220a3163cad2293a5dace5f03f06681e88a -- sha256 content hash (before encryption) +... -- the encrypted content of the packet +``` + +The deserialization of the packet from the server is as follows: + +1. We check the ID of the key from the packet to understand that the packet is for us. +2. Using the public key of the server from the packet and our private key, we calculate a shared key and decrypt the content of the packet +3. Compare the sha256 hash sent to us with the hash received from the decrypted data, they must match +4. Start deserializing the packet content using the `adnl.packetContents` TL schema + +The content of the packet will look like this: + +``` +89cd42d1 -- TL ID adnl.packetContents +0f 985558683d58c9847b4013ec93ea28 -- rand1, 15 (0f) random bytes +ca0d0000 -- flags (0x0dca) -> 0b0000110111001010 +daa76538d99c79ea097a67086ec05acca12d1fefdbc9c96a76ab5a12e66c7ebb -- from_short (because flag's first bit = 1) +02000000 -- messages (present because flag's third bit = 1) + 691ddd60 -- TL ID adnl.message.confirmChannel + db19d5f297b2b0d76ef79be91ad3ae01d8d9f80fab8981d8ed0c9d67b92be4e3 -- key (server channel public key) + d59d8e3991be20b54dde8b78b3af18b379a62fa30e64af361c75452f6af019d7 -- peer_key (our public channel key) + 94848863 -- date + + 1684ac0f -- TL ID adnl.message.answer + d7be82afbc80516ebca39784b8e2209886a69601251571444514b7f17fcd8875 -- query_id + 90 48325384c6b413487d99e4a08031ad3778c5e060569645466e52bd5bd2c7b -- answer (the answer to our request, we will analyze its content in an article about DHT) + 78ddd56def1cf3760c901000000e7a60d67ad071541c53d0000ee354563ee -- + 35456300000000000000009484886340d46cc50450661a205ad47bacd318c -- + 65c8fd8e8f797a87884c1bad09a11c36669babb88f75eb83781c6957bc976 -- + 6a234f65b9f6e7cc9b53500fbe2c44f3b3790f000000 -- + 000000 -- +0100000000000000 -- seqno (present because flag's sixth bit = 1) +0100000000000000 -- confirm_seqno (present because flag's seventh bit = 1) +94848863 -- recv_addr_list_version (present because flag's eighth bit = 1, usually initialization date) +ee354563 -- reinit_date (present because flag's tenth bit = 1, usually initialization date) +94848863 -- dst_reinit_date (present because flag's tenth bit = 1) +40 5c26a2a05e584e9d20d11fb17538692137d1f7c0a1a3c97e609ee853ea9360ab6 -- signature (present because flag's eleventh bit = 1), (bytes size 64, padding 3) + d84263630fe02dfd41efb5cd965ce6496ac57f0e51281ab0fdce06e809c7901 -- + 000000 -- +0f c3354d35749ffd088411599101deb2 -- rand2, 15 (0f) random bytes +``` + +The server responded to us with two messages: `adnl.message.confirmChannel` and `adnl.message.answer`. +With `adnl.message.answer` everything is simple, this is the answer to our request `dht.getSignedAddressList`, we will analyze it in the article about DHT. + +Let's focus on `adnl.message.confirmChannel`, which means that the peer has confirmed the creation of the channel and sent us its public channel key. Now, having our private channel key and the peer's public channel key, we can calculate the [shared key](/develop/network/adnl-tcp#getting-a-shared-key-using-ecdh). + +Now when we have calculated the shared channel key, we need to make 2 keys out of it - one for encrypting outgoing messages, the other for decrypting incoming messages. +Making 2 keys out of it is quite simple, the second key is equal to the shared key written in reverse order. Example: + +``` +Shared key : AABB2233 + +First key: AABB2233 +Second key: 3322BBAA +``` + +It remains to determine which key to use for what, we can do this by comparing the id of our public channel key with the id of the public key of the server channel, converting them to a numerical form - uint256. This approach is used to ensure that both the server and the client determine which key to use for what. If the server uses the first key for encryption, then with this approach the client will always use it for decryption. + +The terms of use are: + +``` +The server id is smaller than our id: +Encryption: First Key +Decryption: Second Key + +The server id is larger than our id: +Encryption: Second Key +Decryption: First Key + +If the ids are equal (nearly impossible): +Encryption: First Key +Decryption: First Key +``` + +[[Implementation example]](https://github.com/xssnick/tonutils-go/blob/46dbf5f820af066ab10c5639a508b4295e5aa0fb/adnl/adnl.go#L502) + +### Communication in a channel + +All subsequent packet exchange will occur within the channel, and channel keys will be used for encryption. +Let's send the same `dht.getSignedAddressList` request inside a newly created channel to see the difference. + +Let's build the packet for the channel using the same `adnl.packetContents` structure: + +``` +89cd42d1 -- TL ID adnl.packetContents +0f c1fbe8c4ab8f8e733de83abac17915 -- rand1, 15 (0f) random bytes +c4000000 -- flags (0x00c4) -> 0b0000000011000100 + -- message (because second bit = 1) +7af98bb4 -- TL ID adnl.message.query +fe3c0f39a89917b7f393533d1d06b605b673ffae8bbfab210150fe9d29083c35 -- query_id +04 ed4879a9 000000 -- query (our dht.getSignedAddressList packed in bytes with padding 3) +0200000000000000 -- seqno (because flag's sixth bit = 1), 2 because it is our second message +0100000000000000 -- confirm_seqno (flag's seventh bit = 1), 1 because it is the last seqno received from the server +07 e4092842a8ae18 -- rand2, 7 (07) random bytes +``` + +The packets in a channel are quite simple and essentially consist of sequences (seqno) and the messages themselves. + +After serialization, like last time, we calculate the sha256 hash of the packet. Then we encrypt the packet using the key intended for the outgoing packets of the channel. +[Calculate](/develop/network/adnl-tcp#getting-key-id) `pub.aes` ID of encryption key of our outgoing messages, and we build our packet: + +``` +bcd1cf47b9e657200ba21d94b822052cf553a548f51f539423c8139a83162180 -- ID of encryption key of our outgoing messages +6185385aeee5faae7992eb350f26ba253e8c7c5fa1e3e1879d9a0666b9bd6080 -- sha256 content hash (before encryption) +... -- the encrypted content of the packet +``` + +We send a packet via UDP and wait for a response. In response, we will receive a packet of the same type as we sent (the same fields), but with the answer to our request `dht.getSignedAddressList`. + +## Other message types + +For basic communication, messages like `adnl.message.query` and `adnl.message.answer` are used, which we discussed above, but other types of messages are also used for some situations, which we will discuss in this section. + +### adnl.message.part + +This message type is a piece of one of the other possible message types, such as `adnl.message.answer`. This method of data transferring is used when the message is too large to be transmitted in a single UDP datagram. + +```tlb +adnl.message.part +hash:int256 -- sha256 hash of the original message +total_size:int -- original message size +offset:int -- offset according to the beginning of the original message +data:bytes -- piece of data of the original message + = adnl.Message; +``` + +Thus, in order to assemble the original message, we need to get several parts and, according to the offsets, concat them into a single bytes array. +And then process it as a message (according to the ID prefix in this bytes array). + +### adnl.message.custom + +```tlb +adnl.message.custom data:bytes = adnl.Message; +``` + +Such messages are used when the logic at the higher level does not correspond to the request-response format, messages of this type allow you to completely move the processing to the higher level, since the message carries only an array of bytes, without query_id and other fields. +Messages of this type are used, for example, in RLDP, since there can be only one response to many requests, this logic is controlled by RLDP itself. + +### Conclusion + +Further communication takes place on the basis of the logic described in this article, +but the content of the packets depends on higher level protocols such as DHT and RLDP. + +## References + +_Here a [link to the original article](https://github.com/xssnick/ton-deep-doc/blob/master/ADNL-UDP-Internal.md) by [Oleg Baranov](https://github.com/xssnick)._ From e8b85aa34c7eff756e7adbc30e66d59a49221101 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:40 +0800 Subject: [PATCH 117/219] New translations dht.md (Chinese Simplified) --- .../current/develop/network/dht.md | 193 ++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/network/dht.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/network/dht.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/network/dht.md new file mode 100644 index 0000000000..5c0fd96aee --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/network/dht.md @@ -0,0 +1,193 @@ +# DHT + +DHT stands for Distributed Hash Table and is essentially a distributed key-value database, +where each member of the network can store something, for example, information about themselves. + +The implementation of DHT in TON is inherently similar to the implementation of [Kademlia](https://codethechange.stanford.edu/guides/guide_kademlia.html), which is used in IPFS. +Any network member can run a DHT node, generate keys and store data. +To do this, he needs to generate a random ID and inform other nodes about himself. + +To determine which node to store the data on, an algorithm is used to determine the "distance" between the node and the key. +The algorithm is simple: we take the ID of the node and the ID of the key, we perform the XOR operation. The smaller the value, the closer the node. +The task is to store the key on the nodes as close as possible to the key, so that other network participants can, using +the same algorithm, find a node that can give data on this key. + +## Finding a value by key + +Let's look at an example with a search for a key, [connect to any DHT node and establish a connection via ADNL UDP](/develop/network/adnl-udp#packet-structure-and-communication). + +For example, we want to find the address and public key for connecting to the node that hosts foundation.ton site. +Let's say we have already obtained the ADNL address of this site by executing the Get method of the DNS contract. +The ADNL address in hex representation is `516618cf6cbe9004f6883e742c9a2e3ca53ed02e3e36f4cef62a98ee1e449174`. +Now our goal is to find the ip, port and public key of the node that has this address. + +To do this, we need to get the ID of the DHT key, first we will fill the DHT key schema: + +```tlb +dht.key id:int256 name:bytes idx:int = dht.Key +``` + +`name` is the type of key, for ADNL addresses the word `address` is used, and, for example, to search for shardchain nodes - `nodes`. But the key type can be any array of bytes, depending on the value you are looking for. + +Filling in this schema, we get: + +``` +8fde67f6 -- TL ID dht.key +516618cf6cbe9004f6883e742c9a2e3ca53ed02e3e36f4cef62a98ee1e449174 -- our searched ADNL address +07 61646472657373 -- key type, the word "address" as an TL array of bytes +00000000 -- index 0 because there is only 1 key +``` + +Next - get the key ID, sha256 hash from the bytes serialized above. It will be `b30af0538916421b46df4ce580bf3a29316831e0c3323a7f156df0236c5b2f75` + +Now we can start searching. To do this, we need to execute a query that has [schema](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/ton_api.tl#L197): + +```tlb +dht.findValue key:int256 k:int = dht.ValueResult +``` + +`key` is the id of our DHT key, and `k` is the "width" of the search, the smaller it is, the more accurate, but fewer potential nodes to query. The maximum k for nodes in a TON is 10, usually 6 is used. + +Let's populate this structure, serialize and send the request using the `adnl.message.query` schema. [You can read more about this in another article](/develop/network/adnl-udp#packet-structure-and-communication). + +In response, we can get: + +- `dht.valueNotFound` - if the value is not found. +- `dht.valueFound` - if the value is found on this node. + +##### dht.valueNotFound + +If we get `dht.valueNotFound`, the response will contain a list of nodes that are known to the node we requested and are as close as possible to the key we requested from the list of nodes known to it. In this case, we need to connect and add the received nodes to the list known to us. +After that, from the list of all nodes known to us, select the closest, accessible and not yet requested, and make the same request to it. And so on until we try all the nodes in the range we have chosen or until we stop receiving new nodes. + +Let's analyze the response fields in more detail, the schemas used: + +```tlb +adnl.address.udp ip:int port:int = adnl.Address; +adnl.addressList addrs:(vector adnl.Address) version:int reinit_date:int priority:int expire_at:int = adnl.AddressList; + +dht.node id:PublicKey addr_list:adnl.addressList version:int signature:bytes = dht.Node; +dht.nodes nodes:(vector dht.node) = dht.Nodes; + +dht.valueNotFound nodes:dht.nodes = dht.ValueResult; +``` + +`dht.nodes -> nodes` - list of DHT nodes (array). + +Each node has an `id` which is its public key, usually [pub.ed25519](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/ton_api.tl#L47), used as a server key to connect to the node via ADNL. Also, each node has a list of addresses `addr_list:adnl.addressList`, version and signature. + +We need to check the signature of each node, for this we read the value of `signature` and set the field to zero (we make it an empty bytes array). After - we serialize the TL structure `dht.node` with the empty signature and check the `signature` field that was before emptying. +We check on the received serialized bytes, using the public key from `id` field. [[Implementation example]](https://github.com/xssnick/tonutils-go/blob/46dbf5f820af066ab10c5639a508b4295e5aa0fb/adnl/dht/client.go#L91) + +From the list `addrs:(vector adnl.Address)` we take the address and try to establish an ADNL UDP connection, as the server key we use `id`, which is the public key. + +To find out the "distance" to this node - we need to take [key id](/develop/network/adnl-tcp#getting-key-id) from the key from the `id` field and check the distance by the XOR operation from the node's key id and the desired key. +If the distance is small enough, we can make the same request to this node. And so on, until we find a value or there are no more new nodes. + +##### dht.valueFound + +The response will contain the value itself, the full key information, and optionally a signature (depends on value type). + +Let's analyze the response fields in more detail, the schemas used: + +```tlb +adnl.address.udp ip:int port:int = adnl.Address; +adnl.addressList addrs:(vector adnl.Address) version:int reinit_date:int priority:int expire_at:int = adnl.AddressList; + +dht.key id:int256 name:bytes idx:int = dht.Key; + +dht.updateRule.signature = dht.UpdateRule; +dht.updateRule.anybody = dht.UpdateRule; +dht.updateRule.overlayNodes = dht.UpdateRule; + +dht.keyDescription key:dht.key id:PublicKey update_rule:dht.UpdateRule signature:bytes = dht.KeyDescription; + +dht.value key:dht.keyDescription value:bytes ttl:int signature:bytes = dht.Value; + +dht.valueFound value:dht.Value = dht.ValueResult; +``` + +First, let's analyze `key:dht.keyDescription`, it is a complete description of the key, the key itself and information about who and how can update the value. + +- `key:dht.key` - the key must match the one from which we took the key ID for the search. +- `id:PublicKey` - the public key of the record owner. +- `update_rule:dht.UpdateRule` - record update rule. +- - `dht.updateRule.signature` - only the owner of the private key can update the record, the `signature` of both the key and the value must be valid +- - `dht.updateRule.anybody` - everyone can update the record, `signature` is empty and not checked +- - `dht.updateRule.overlayNodes` - nodes from the same overlay can update the key, used to find nodes of the same overlay and add yourself + +###### dht.updateRule.signature + +After reading the description of the key, we act depending on the `updateRule`, for the ADNL address lookup case the type is always `dht.updateRule.signature`. +We check the key signature in the same way as last time, make the signature an empty byte array, serialize and check. After - we repeat the same for the value, i.e. for the entire `dht.value` object (while returning the key signature to its place). + +[[Implementation example]](https://github.com/xssnick/tonutils-go/blob/46dbf5f820af066ab10c5639a508b4295e5aa0fb/adnl/dht/client.go#L331) + +###### dht.updateRule.overlayNodes + +Used for keys containing information about other nodes-shards of the workchain in the network, the value always has the TL structure `overlay.nodes`. +The value field must be empty. + +```tlb +overlay.node id:PublicKey overlay:int256 version:int signature:bytes = overlay.Node; +overlay.nodes nodes:(vector overlay.node) = overlay.Nodes; +``` + +To check for validity, we must check all `nodes` and for each check `signature` against its `id` by serializing the TL structure: + +```tlb +overlay.node.toSign id:adnl.id.short overlay:int256 version:int = overlay.node.ToSign; +``` + +As we can see, id should be replaced with adnl.id.short, which is the key id (hash) of the `id` field from the original structure. After serialization - we check the signature with the data. + +As a result, we get a valid list of nodes that are able to give us information about the workchain shard we need. + +###### dht.updateRule.anybody + +There are no signatures, anyone can update. + +#### Using a value + +When everything is verified and the `ttl:int` value has not expired, we can start working with the value itself, i.e. `value:bytes`. For an ADNL address, there must be an `adnl.addressList` structure inside. +It will contain ip addresses and ports of servers corresponding to the requested ADNL address. In our case, there will most likely be 1 RLDP-HTTP address of the `foundation.ton` service. +We will use the public key `id:PublicKey` from the DHT key information as the server key. + +After the connection is established, we can request the pages of the site using the RLDP protocol. The task from the DHT side at this stage is completed. + +### Search for nodes that store the state of the blockchain + +DHT is also used to find information about the nodes that store the data of workchains and their shards. The process is the same as when searching for any key, the only difference is in the serialization of the key itself and the validation of the response, we will analyze these points in this section. + +In order to get data, for example, of the masterchain and its shards, we need to fill in the TL structure: + +``` +tonNode.shardPublicOverlayId workchain:int shard:long zero_state_file_hash:int256 = tonNode.ShardPublicOverlayId; +``` + +Where `workchain` in the case of a masterchain will be equal to -1, its shard will be equal to -922337203685477580 (0xFFFFFFFFFFFFFFFF), and `zero_state_file_hash` is the hash of the zero state of the chain (file_hash), like other data, it can be taken from the global network config, in the `"validator"` field + +```json +"zero_state": { + "workchain": -1, + "shard": -9223372036854775808, + "seqno": 0, + "root_hash": "F6OpKZKqvqeFp6CQmFomXNMfMj2EnaUSOXN+Mh+wVWk=", + "file_hash": "XplPz01CXAps5qeSWUtxcyBfdAo5zVb1N979KLSKD24=" +} +``` + +After we have filled in `tonNode.shardPublicOverlayId`, we serialize it and get the key id from it by hashing (as always). + +We need to use the resulting key ID as `name` to fill in the `pub.overlay name:bytes = PublicKey` structure, wrapping it in TL bytes array. Next, we serialize it, and we get the key ID now from it. + +The resulting id will be the key to use in `dht.findValue`, and the `name` field's value will be the word `nodes`. We repeat the process from the previous section, everything is the same as last time, but `updateRule` will be [dht.updateRule.overlayNodes](#dhtupdateruleoverlaynodes). + +After validation - we will get the public keys (`id`) of the nodes that have information about our workchain and shard. To get the ADNL addresses of the nodes, we need to make IDs from the keys (using the hashing method) and repeat the procedure described above for each of the ADNL addresses, as with the ADNL address of the `foundation.ton` domain. + +As a result, we will get the addresses of the nodes from which, if we want, we can find out the addresses of other nodes of this chain using [overlay.getRandomPeers](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/ton_api.tl#L237). +We will also be able to receive all the information about the blocks from these nodes. + +## References + +_Here a [link to the original article](https://github.com/xssnick/ton-deep-doc/blob/master/DHT.md) by [Oleg Baranov](https://github.com/xssnick)._ From 7b45b7730f0ba002fb7c5cb76c7394bea5afef51 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:41 +0800 Subject: [PATCH 118/219] New translations overlay.md (Chinese Simplified) --- .../current/develop/network/overlay.md | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/network/overlay.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/network/overlay.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/network/overlay.md new file mode 100644 index 0000000000..1788938820 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/network/overlay.md @@ -0,0 +1,81 @@ +# Overlay subnetworks + +The architecture of TON is built in such a way that a lot of chains can exist simultaneously and independently in it - they can be both private or public. +Nodes have the ability to choose which shards and chains they store and process. +At the same time, the communication protocol remains unchanged due to its universality. Protocols such as DHT, RLDP and Overlays allow this to be achieved. +We are already familiar with the first two, in this section we will learn what Overlay is. + +Overlays are responsible for dividing a single network into additional subnetworks. Overlays can be both public, to which anyone can connect, and private, where additional credentials is needed for entry, known only to a certain amount of people. + +All chains in TON, including the masterchain, communicate using their own overlay. +To join it, you need to find the nodes that are already in it, and start exchanging data with them. +For the public overlays you can find nodes using DHT. + +## Interaction with overlay nodes + +We have already analyzed an example with finding overlay nodes in an article about DHT, +in the section [Search for nodes that store the state of the blockchain](/develop/network/dht#search-for-nodes-that-store-the-state-of-the-blockchain). +In this section, we will focus on interacting with them. + +When querying the DHT, we will get the addresses of the overlay nodes, from which we can find out the addresses of other nodes of this overlay using [overlay.getRandomPeers](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/ton_api.tl#L237) query. +Once we connect to a sufficient number of nodes, we can receive all blocks information and other chain events from them, as well as send our transactions to them for processing. + +### Find more neighbors + +Let's look at an example of getting nodes in an overlay. + +To do this, send a request `overlay.getRandomPeers` to any known node of the overlay, serialize the TL schema: + +```tlb +overlay.node id:PublicKey overlay:int256 version:int signature:bytes = overlay.Node; +overlay.nodes nodes:(vector overlay.node) = overlay.Nodes; + +overlay.getRandomPeers peers:overlay.nodes = overlay.Nodes; +``` + +`peers` - should contain the peers we know, so we don't get them back, but since we don't know any yet, `peers.nodes` will be an empty array. + +In case if we want to not just get some information, but participate in overlay and get broadcasts, we should also add in `peers` information about our node, from which we're doing request. +When peers will get info about us - they will start to send us broadcasts using ADNL or RLDP. + +Each request inside the overlay must be prefixed with the TL schema: + +```tlb +overlay.query overlay:int256 = True; +``` + +The `overlay` should be the id of the overlay - the id of the `tonNode.ShardPublicOverlayId` schema key - the same one we used to search the DHT. + +We need to concat 2 serialized schemas by simply concatenating 2 serialized byte arrays, `overlay.query` will come first, `overlay.getRandomPeers` second. + +We wrap the resulting array in the `adnl.message.query` schema and send it via ADNL. In response, we are waiting for `overlay.nodes` - this will be a list of overlay nodes to which we can connect and, if necessary, repeat the same request to new of them until we get enough connections. + +### Functional requests + +Once the connection is established, we can access the overlay nodes using [requests](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/ton_api.tl#L413) `tonNode.*`. + +For requests of this kind, the RLDP protocol is used. And it's important not to forget the `overlay.query` prefix - it must be used for every query in the overlay. + +There is nothing unusual about the requests themselves, they are very similar to what we [did in the article about ADNL TCP](/develop/network/adnl-tcp#getmasterchaininfo). + +For example, the `downloadBlockFull` request uses the already familiar schema of block id: + +```tlb +tonNode.downloadBlockFull block:tonNode.blockIdExt = tonNode.DataFull; +``` + +By passing it, we will be able to download the full information about the block, in response we will receive: + +```tlb +tonNode.dataFull id:tonNode.blockIdExt proof:bytes block:bytes is_link:Bool = tonNode.DataFull; + or +tonNode.dataFullEmpty = tonNode.DataFull; +``` + +If present, the `block` field will contain data in TL-B format. + +Thus, we can receive information directly from the nodes. + +## References + +_Here a [link to the original article](https://github.com/xssnick/ton-deep-doc/blob/master/Overlay-Network.md) by [Oleg Baranov](https://github.com/xssnick)._ From bcc0f543e76f20eb013ecc715573f82ceef09920 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:42 +0800 Subject: [PATCH 119/219] New translations rldp.md (Chinese Simplified) --- .../current/develop/network/rldp.md | 200 ++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/network/rldp.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/network/rldp.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/network/rldp.md new file mode 100644 index 0000000000..301861ad52 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/network/rldp.md @@ -0,0 +1,200 @@ +# RLDP + +RLDP - Reliable Large Datagram Protocol - is a protocol that runs on top of ADNL UDP, which is used to transfer large data blocks and +includes Forward Error Correction (FEC) algorithms as a replacement of acknowledgment packets on the other side. +This makes it possible to transfer data between network components more efficiently, but with more traffic consumption. + +RLDP is used everywhere in TON infrastructure, for example, to download blocks from other nodes and transfer data to them, +to access TON websites and TON Storage. + +## Protocol + +RLDP uses the following TL structures for communication: + +```tlb +fec.raptorQ data_size:int symbol_size:int symbols_count:int = fec.Type; +fec.roundRobin data_size:int symbol_size:int symbols_count:int = fec.Type; +fec.online data_size:int symbol_size:int symbols_count:int = fec.Type; + +rldp.messagePart transfer_id:int256 fec_type:fec.Type part:int total_size:long seqno:int data:bytes = rldp.MessagePart; +rldp.confirm transfer_id:int256 part:int seqno:int = rldp.MessagePart; +rldp.complete transfer_id:int256 part:int = rldp.MessagePart; + +rldp.message id:int256 data:bytes = rldp.Message; +rldp.query query_id:int256 max_answer_size:long timeout:int data:bytes = rldp.Message; +rldp.answer query_id:int256 data:bytes = rldp.Message; +``` + +The serialized structure is wrapped in the `adnl.message.custom` TL schema and sent over ADNL UDP. +RLDP Transfers are used to transfer big data, a random `transfer_id` is generated, and the data itself is processed by the FEC algorithm. +The resulting pieces are wrapped in a `rldp.messagePart` structure and sent to the peer until the peer sends us `rldp.complete` or until timeout. + +When the receiver has collected the pieces of `rldp.messagePart` necessary to assemble a complete message, it concat them all together, decodes using FEC and +deserializes the resulting byte array into one of the `rldp.query` or `rldp.answer` structures, depending on the type (tl prefix id). + +### FEC + +Valid Forward Error Correction algorithms for use with RLDP are RoundRobin, Online, and RaptorQ. +Currently for data encoding [RaptorQ](https://www.qualcomm.com/media/documents/files/raptorq-technical-overview.pdf) is used. + +#### RaptorQ + +The essence of RaptorQ is that the data is divided into so-called symbols - blocks of the same, predetermined size. + +Matrices are created from blocks, and discrete mathematical operations are applied to them. This allows us to create an almost infinite number of symbols. +from the same data. All symbols are mixed, and it is possible to recover lost packets without requesting additional data from the server, while using fewer packets than it would be if we sent the same pieces in a loop. + +The generated symbols are sent to the peer until he reports that all data has been received and restored (decoded) by applying the same discrete operations. + +[[Implementation example of RaptorQ in Golang]](https://github.com/xssnick/tonutils-go/tree/46dbf5f820af066ab10c5639a508b4295e5aa0fb/adnl/rldp/raptorq) + +## RLDP-HTTP + +To interact with TON Sites, HTTP wrapped in RLDP is used. The hoster runs his site on any HTTP webserver and starts rldp-http-proxy next to it. +All requests from the TON network come via the RLDP protocol to the proxy, and the proxy reassembles the request into simple HTTP and calls the original web server locally. + +The user on his side launches the proxy, for example, [Tonutils Proxy](https://github.com/xssnick/TonUtils-Proxy), and uses the `.ton` sites, all traffic is wrapped in the reverse order, requests go to the local HTTP proxy, and it sends them via RLDP to the remote TON site. + +HTTP inside RLDP is implemented using TL structures: + +```tlb +http.header name:string value:string = http.Header; +http.payloadPart data:bytes trailer:(vector http.header) last:Bool = http.PayloadPart; +http.response http_version:string status_code:int reason:string headers:(vector http.header) no_payload:Bool = http.Response; + +http.request id:int256 method:string url:string http_version:string headers:(vector http.header) = http.Response; +http.getNextPayloadPart id:int256 seqno:int max_chunk_size:int = http.PayloadPart; +``` + +This is not pure HTTP in text form, everything is wrapped in binary TL and unwrapped back to be sent to the web server or browser by the proxy itself. + +The scheme of work is as follows: + +- Client sends `http.request` +- The server checks the `Content-Length` header when receiving a request +- - If not 0, sends a `http.getNextPayloadPart` request to the client +- - When receiving a request, the client sends `http.payloadPart` - the requested body piece depending on `seqno` and `max_chunk_size`. +- - The server repeats requests, incrementing `seqno`, until it receives all the chunks from the client, i.e. until the `last:Bool` field of the last chunk received is true. +- After processing the request, the server sends `http.response`, the client checks the `Content-Length` header +- - If it is not 0, then sends a `http.getNextPayloadPart` request to the server, and the operations are repeated, as in the case of the client but vice-versa. + +## Request the TON Site + +To understand how RLDP works, let's look at an example of getting data from the TON site `foundation.ton`. +Let's say we have already got its ADNL address by calling the Get method of the NFT-DNS contract, [determined the address and port of the RLDP service using DHT](https://github.com/xssnick/ton-deep-doc/blob/46dbf5f820af066ab10c5639a508b4295e5aa0fb/DHT.md), and [connected to it over ADNL UDP](https://github.com/xssnick/ton-deep-doc/blob/46dbf5f820af066ab10c5639a508b4295e5aa0fb/ADNL-UDP-Internal.md). + +### Send a GET request to `foundation.ton` + +To do this, fill in the structure: + +```tlb +http.request id:int256 method:string url:string http_version:string headers:(vector http.header) = http.Response; +``` + +Serialize `http.request` by filling in the fields: + +``` +e191b161 -- TL ID http.request +116505dac8a9a3cdb464f9b5dd9af78594f23f1c295099a9b50c8245de471194 -- id = {random} +03 474554 -- method = string `GET` +16 687474703a2f2f666f756e646174696f6e2e746f6e2f 00 -- url = string `http://foundation.ton/` +08 485454502f312e31 000000 -- http_version = string `HTTP/1.1` +01000000 -- headers (1) + 04 486f7374 000000 -- name = Host + 0e 666f756e646174696f6e2e746f6e 00 -- value = foundation.ton +``` + +Now let's wrap our serialized `http.request` into `rldp.query` and serialize it too: + +``` +694d798a -- TL ID rldp.query +184c01cb1a1e4dc9322e5cabe8aa2d2a0a4dd82011edaf59eb66f3d4d15b1c5c -- query_id = {random} +0004040000000000 -- max_answer_size = 257 KB, can be any sufficient size that we accept as headers +258f9063 -- timeout (unix) = 1670418213 +34 e191b161116505dac8a9a3cdb464f9b5dd9af78594f23f1c295099a9b50c8245 -- data (http.request) + de4711940347455416687474703a2f2f666f756e646174696f6e2e746f6e2f00 + 08485454502f312e310000000100000004486f73740000000e666f756e646174 + 696f6e2e746f6e00 000000 +``` + +### Encoding and sending packets + +Now we need to apply the FEC RaptorQ algorithm to this data. + +Let's create an encoder, for this we need to turn the resulting byte array into symbols of a fixed size. In TON, the symbol size is 768 bytes. +To do this, let's split the array into pieces of 768 bytes. In the last piece, if it comes out smaller than 768, it will need to be padded with zero bytes to the required size. + +Our array is 156 bytes in size, which means there will be only 1 piece, and we need to pad it with 612 zero bytes to a size of 768. + +Also, constants are selected for the encoder, depending on the size of the data and the symbol, you can learn more about this in the documentation of RaptorQ itself, but in order not to get into mathematical jungle, I recommend using a ready-made library that implements such encoding. +[[Example of creating an encoder]](https://github.com/xssnick/tonutils-go/blob/46dbf5f820af066ab10c5639a508b4295e5aa0fb/adnl/rldp/raptorq/encoder.go#L15) and [[Symbol encoding example]](https://github.com/xssnick/tonutils-go/blob/be3411cf412f23e6889bf0b648904306a15936e7/adnl/rldp/raptorq/solver.go#L26). + +Symbols are encoded and sent in a round-robin style: we initially define `seqno` which is 0, and increment it by 1 for each successive encoded packet. For example, if we have 2 symbols, then we encode and send the first one, increase seqno by 1, then the second and increase seqno by 1, then again the first one and increase seqno, which at this moment is already equal to 2, by another 1. +And so until we receive a message that the peer has accepted the data. + +Now, when we have created the encoder, we are ready to send data, for this we will fill in the TL schema: + +```tlb +fec.raptorQ data_size:int symbol_size:int symbols_count:int = fec.Type; + +rldp.messagePart transfer_id:int256 fec_type:fec.Type part:int total_size:long seqno:int data:bytes = rldp.MessagePart; +``` + +- `transfer_id` - random int256, the same for all messageParts within the same data transfer. +- `fec_type` is `fec.raptorQ`. +- - `data_size` = 156 +- - `symbol_size` = 768 +- - `symbols_count` = 1 +- `part` in our case always 0, can be used for transfers that hit the size limit. +- `total_size` = 156. The size of our transfer data. +- `seqno` - for the first packet will be equal to 0, and for each subsequent packet it will increase by 1, will be used as parameter to decode and encode symbol. +- `data` - our encoded symbol, 768 bytes in size. + +After serializing `rldp.messagePart`, wrap it in `adnl.message.custom` and send it over ADNL UDP. + +We send packets in a loop, increasing seqno all the time, until we wait for the `rldp.complete` message from the peer, or we stop on a timeout. After we have sent a number of packets equal to the number of our symbols, we can slow down and send an additional packet, for example, once every 10 milliseconds or fewer. +The extra packets are used for recovery in case of data loss, since UDP is a fast but unreliable protocol. + +[[Implementation example]](https://github.com/xssnick/tonutils-go/blob/be3411cf412f23e6889bf0b648904306a15936e7/adnl/rldp/rldp.go#L249) + +### Processing the response from `foundation.ton` + +During the sending, we can already expect a response from the server, in our case we are waiting for `rldp.answer` with `http.response` inside. +It will come to us in the same way, in the form of an RLDP transfer, as it was sent at the time of the request, but `transfer_id` will be inverted (each byte XOR 0xFF). +We will get `adnl.message.custom` messages containing `rldp.messagePart`. + +First we need to get the FEC information from the first received message of the transfer, specifically the `data_size`, `symbol_size` and `symbols_count` parameters from the `fec.raptorQ` messagePart structure. +We need them to initialize the RaptorQ decoder. [[Example]](https://github.com/xssnick/tonutils-go/blob/be3411cf412f23e6889bf0b648904306a15936e7/adnl/rldp/rldp.go#L137) + +After initialization, we add the received symbols with their `seqno` to our decoder, and once we have accumulated the minimum required number equal to `symbols_count`, we can try to decode the full message. On success, we will send `rldp.complete`. [[Example]](https://github.com/xssnick/tonutils-go/blob/be3411cf412f23e6889bf0b648904306a15936e7/adnl/rldp/rldp.go#L168) + +The result will be a `rldp.answer` message with the same query_id as in the `rldp.query` we sent. The data must contain `http.response`. + +```tlb +http.response http_version:string status_code:int reason:string headers:(vector http.header) no_payload:Bool = http.Response; +``` + +With the main fields, I think everything is clear, the essence is the same as in HTTP. +An interesting flag here is `no_payload`, if it is true, then there is no body in the response, (`Content-Length` = 0). +The response from the server can be considered received. + +If `no_payload` = false, then there is content in the response, and we need to get it. +To do this, we need to send a request with a TL schema `http.getNextPayloadPart` wrapped in `rldp.query`. + +```tlb +http.getNextPayloadPart id:int256 seqno:int max_chunk_size:int = http.PayloadPart; +``` + +`id` should be the same as we sent in `http.request`, `seqno` - 0, and +1 for each next part. `max_chunk_size` is the maximum chunk size we are ready to accept, typically 128 KB (131072 bytes) is used. + +In response, we will receive: + +```tlb +http.payloadPart data:bytes trailer:(vector http.header) last:Bool = http.PayloadPart; +``` + +If `last` = true, then we have reached the end, we can put all the pieces together and get a complete response body, for example, html. + +## References + +_Here a [link to the original article](https://github.com/xssnick/ton-deep-doc/blob/master/RLDP.md) by [Oleg Baranov](https://github.com/xssnick)._ From 5e231d7b23ceafd385710272ef77b02cf2422973 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:43 +0800 Subject: [PATCH 120/219] New translations overview.mdx (Chinese Simplified) --- .../current/develop/overview.mdx | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/overview.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/overview.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/overview.mdx new file mode 100644 index 0000000000..b0b0f8c01c --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/overview.mdx @@ -0,0 +1,101 @@ +import Button from '@site/src/components/button' +import Player from '@site/src/components/player' + +# TON Documentation + +Welcome to the official TON Blockchain development documentation! + +This resource aims to provide you with all the necessary information you'll need to build, test, and deploy applications on the TON Blockchain. + +This is a collaborative open-source initiative, and contributions are always welcome. All documentation can be edited via GitHub, just [follow these instructions](/contribute). + +- _Get Started with TON_ is a step-by-step guide to interacting with TON Blockchain. (video tutorial included) +- _TON Hello World_ series provides detailed step-by-step guides to wallets, smart contracts, mini apps, and testing & debugging smart contracts on TON. + + + + +### TON Course + +We're proud to present the **TON Blockchain Course**, which is a comprehensive guide to the TON Blockchain. The course is designed for developers who want to learn how to create smart contracts and decentralized applications on the TON Blockchain in engaging and interactive ways. + +It consists of **9 modules** and covers the basics of the TON Blockchain, the FunC programming language, and the TON Virtual Machine (TVM). + + + +## Development Modules + +If you're new to TON Blockchain development, it's recommended that you start from the beginning and work your way through these topics. + +### Foundational Concepts + +- [The Open Network](/learn/introduction) - A high-level overview of TON Blockchain. +- [Blockchain of Blockchains](/learn/overviews/ton-blockchain) - a down-to-earth explanation of TON Blockchain. +- [Smart Contract Addresses](/learn/overviews/addresses) - A detailed explanation of Addresses. +- [Cells as a Data Structure](/learn/overviews/cells) - A high-level explanation of data structures. +- [TON Networking](/learn/networking/overview) - A high-level overview of TON peer-to-peer protocols. +- [TON Virtual Machine (TVM)](/learn/tvm-instructions/tvm-overview) - A high-level overview of TON Virtual Machine. + - [Transactions and Phases](/learn/tvm-instructions/tvm-overview#transactions-and-phases) - A detailed explanation of transactions and phases. +- [Transaction Fees](/develop/smart-contracts/fees) - A high-level explanation of transaction fees. + +### Infrastructure + +- [Node Types](/participate/nodes/node-types) - A detailed explanation of node types. +- [Run a Full Node](/participate/run-nodes/full-node) - A detailed explanation of how to run a node. +- [TON DNS & Sites](/participate/web3/dns) - A detailed explanation of TON DNS & Sites. +- [TON Storage](/participate/ton-storage/storage-daemon) - A detailed explanation of TON Storage. + +### Additional Resources + +- [**FAQ**](/develop/howto/faq) - Frequently Asked Questions +- [FunC Documentation](/develop/func/overview) +- [Fift Documentation](/develop/fift/overview) + +## Smart Contracts Development + +Smart contracts are the building blocks of decentralized applications (DApps) on TON Blockchain. If you're looking to develop your own dApps, it's essential to understand how smart contracts work. + + + + +



+ +The following resources provide valuable information for TON smart contract development: + +- [TON Hello World: Step-by-step guide for writing your first smart contract](https://ton-community.github.io/tutorials/02-contract/) - An accessible and concise explanation of the fundamentals with JS. +- [How to work with wallet smart contracts](/develop/smart-contracts/tutorials/wallet) - Detailed and careful explanations of smart contract basics with the use of JS and GO. +- [Learn Smart Contracts by examples](/develop/smart-contracts/examples) (FunC, Fift) +- [Speed Run TON](/develop/smart-contracts/examples) - 6 interactive challenges and step-by-step tutorials to learn smart contracts development. + +## DApp Development + +Decentralized applications (DApps) are applications that run on a peer-to-peer network of computers rather than a single computer (TON Blockchain). They are similar to traditional web applications, but they are built on top of a blockchain network. This means that DApps are decentralized, meaning that no single entity controls them. + + + +### DeFi Development + +- [TON Connect](/develop/dapps/ton-connect/overview) — integration and authentication for DApps. +- [Off-chain Payments Processing](/develop/dapps/asset-processing) — examples and concepts for processing payments. +- [TON Jetton processing](/develop/dapps/asset-processing/jettons) — examples and concepts for processing Jettons. +- [Fungible (FT) / Non-fungible (NFT) tokens](/develop/dapps/defi/tokens) — smart contracts, examples, tools + +Take your first steps in DApps development with a comprehensive DApps building guide: + +- [TON Hello World: Step by step guide for building your first web client](https://ton-community.github.io/tutorials/03-client/) +- [Telegram bot integration via TON Connect](/develop/dapps/ton-connect/tg-bot-integration) + +### APIs and SDKs + +- [APIs](/develop/dapps/apis) +- [SDKs](/develop/dapps/apis/sdk) + +## Frequently Asked Questions + +Go to the [Frequently Asked Questions](/develop/howto/faq) section. From 7cdcdc4cc9b526074419dd9557ed310296331d51 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:43 +0800 Subject: [PATCH 121/219] New translations boc.md (Chinese Simplified) --- .../develop/research-and-development/boc.md | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/research-and-development/boc.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/research-and-development/boc.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/research-and-development/boc.md new file mode 100644 index 0000000000..28b1b0bcef --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/research-and-development/boc.md @@ -0,0 +1,47 @@ +# Canonical Cell Serialization + +## Cell weight + +`Weight` is a characteristic of each cell in tree of cells that defines by the following: + +- If cell is a leave node in a tree of cell: `weight = 1`; +- For ordinary cells (not leaves) weight is a sum: `cell weight = children weight + 1`; +- If cell is a _special_, its weight is set to zero. + +The algorithm below explains how and when we assign weights to each cell to create a weight balanced tree. + +## Weight reorder algorithm + +Each cell is weight balanced tree and [reorder_cells()](https://github.com/ton-blockchain/ton/blob/15088bb8784eb0555469d223cd8a71b4e2711202/crypto/vm/boc.cpp#L249) method +reassigns weights based on cumulative child weight. The traversal order is roots -> children. It's a breadth-first search, _probably_ used to preserve cache linearity. It also triggers hashes size recalculation and reindexes the bag (roots) and each tree, sets new indexes for empty references. Reindexing is depth-first though, probably there is something that depends on this order of indexing, as whitepaper states it is preferred. + +To follow the original node's bag of cells serialization you should: + +- First, if the cell's weights are not set (node does this on cell import), we set the weight for each cell to `1 + sum_child_weight`, where `sum_child_weight` is the sum of its child node's weights. We add one so that leaves have a weight of 1. + +- Iterate all roots, for each root cell: + - Check if each of its references has a weight less than `maximum_possible_weight - 1 + ref_index` divided by the number of root cell's refs so that they share parents weight uniformly, we do (+ index) to make sure if language casts towards 0 on division we always get a mathematically rounded number (like for 5 / 3, c++ would return 1, but we want 2 here) + + - If some references violate that rule, we add them to the list (or more efficiently create a bitmask, as the original node does) and then iterate again over those and clamp their weight to `weight_left / invalid_ref_count`, where `weight_left` is `maximum_possible_weight - 1 - sum_of_valid_refs_weights`. In the code it could be implemented as a decrement of a counter variable, which is first initialized to `maximum_possible_weight - 1` and then gets decremented as `counter -= valid_ref_weight`. So essentially we redistribute the remaining weight between these nodes (balance them) + +- Iterate over roots again, for each root: + - Make sure the new sum of its reference's weights is less than `maximum_possible_weight`, check if the new sum became less than the previous root cell's weight, and clamp its weight to the new sum. (if `new_sum < root_cell_weight` set `root_cell_weight` equal to `new_sum`) + - If the new sum is higher than the root's weight, then it should be a special node, which has 0 weight, set it. (Increment Internal hashes count here by the hashes count of the node) + +- Iterate over roots again, for each root: + If it's not a special node (if its weight > 0), increment the Top hashes count by the hashes count of the node. + +- Recursively reindex tree: + - First, we previsit all root cells. If we didn't previsit or visit this node before, check all its references recursively for special nodes. If we find a special node, we have to previsit and visit it before others, it does mean that the special node's children will come first in the list (their indexes will be the lowest ones). Then we add other node's children (order deepest -> highest). Roots come at the very end of the list (they have the biggest indexes). So in the end we get a sorted list where the deeper is node, the lower index it has. + +`maximum_possible_weight` is a constant 64 + +## Denotes + +- The special cell does not have weight (it's 0) + +- Make sure weight on import is fitting into 8 bits (weight <= 255) + +- Internal hashes count is the sum of hash counts of all special root nodes + +- The top hashes count is the sum of hash counts of all other (not special) root nodes From f3c91066c9f1ca627e661ff0890e29d280240d40 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:44 +0800 Subject: [PATCH 122/219] New translations minter-flow.md (Chinese Simplified) --- .../research-and-development/minter-flow.md | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/research-and-development/minter-flow.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/research-and-development/minter-flow.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/research-and-development/minter-flow.md new file mode 100644 index 0000000000..51966621b3 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/research-and-development/minter-flow.md @@ -0,0 +1,58 @@ +# Extra Currency Minting + +## Extracurrency + +According to [Ton Blockchain Whitepaper 3.1.6](https://ton-blockchain.github.io/docs/tblkch.pdf#page=55), TON Blockchain allows its users to define arbitrary cryptocurrencies or tokens apart from the Toncoin, provided some conditions are met. Such additional cryptocurrencies are identified by 32-bit _currency\_ids_. The list of defined additional cryptocurrencies is a part of the blockchain configuration, +stored in the masterchain. Each internal message as well as account balance contains a special field for `ExtraCurrencyCollection` (set of extracurrencies attached to a message or kept on balance): + +```tlb +extra_currencies$_ dict:(HashmapE 32 (VarUInteger 32)) = ExtraCurrencyCollection; +currencies$_ grams:Grams other:ExtraCurrencyCollection = CurrencyCollection; +``` + +## Extracurrency config + +A dictionary, or to be precise `ExtraCurrencyCollection`, of all currencies that should be minted is stored in `ConfigParam7`: + +```tlb +_ to_mint:ExtraCurrencyCollection = ConfigParam 7; +``` + +`ConfigParam 6` contains data related to minting: + +```tlb +_ mint_new_price:Grams mint_add_price:Grams = ConfigParam 6; +``` + +`ConfigParam2` contains address of _Minter_. + +## Low-level minting flow + +In each block, the collator compares the old global balance (global balance of all currencies at the end of prev block) with `ConfigParam7`. If any amount for any currency in `ConfigParam7` is less than it is in the global balance - the config is invalid. If any amount of any currency in `ConfigParam7` is higher than it is in the global balance a minting message will be created. + +This minting message has source `-1:0000000000000000000000000000000000000000000000000000000000000000` and _Minter_ from `ConfigParam2` as destination and contains excesses of extracurrencies in `ConfigParam7` over old global balance. + +The issue here is that the minting message contains extra currencies only and no TON coins. That means that even if _Minter_ is set as a fundamental smart contract (presented in `ConfigParam31`), a minting message will cause the aborted transaction: `compute_ph:(tr_phase_compute_skipped reason:cskip_no_gas)`. + +## High-level minting flow + +_Minter_ smart contract upon receiving a request for the creation of new extracurrencies or minting additional tokens for existing ones should: + +1. Check that fee determined in `ConfigParam6` can be deducted from the request message +2. 1. for existing tokens: check authorization for minting (only the _owner_ can mint new ones) + 2. for the creation of new currencies: check that id of the cryptocurrency is not occupied and store owner of the new currency +3. send message to config contract (such message should cause the addition to `ExtraCurrencyCollection` in `ConfigParam7`) +4. send message to `0:0000...0000` (which is guaranteed to bounce in the next or following blocks) with extra_currency id + +Upon receiving message from `0:0000...0000` + +1. read extra_currency id from the bounce message +2. if there are tokens with corresponding id on minter balance send them to this currency owner with `ok` message +3. otherwise send to currency owner `fail` message + +## Issues to be resolved + +1. Workaround with sending a message to `0:0000...0000` for postponement of request processing is quite dirty. +2. Cases, when minting failed, should be thought out. For now, it looks like the only possible situation is when a currency amount is 0 or such that the current balance plus a minted amount doesn't fit into `(VarUInteger 32)` +3. How to burn? At first glance, there are no ways. +4. Should minting fees be prohibitive? In other words, is it dangerous to have millions of extracurrencies (big config, potential DoS due to unbound number of dict operations on collation?) From 7dc55981c2115d4e88b22acb99f5d7817da9d8e5 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:45 +0800 Subject: [PATCH 123/219] New translations readme.mdx (Chinese Simplified) --- .../develop/smart-contracts/README.mdx | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/README.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/README.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/README.mdx new file mode 100644 index 0000000000..a8f04dfd84 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/README.mdx @@ -0,0 +1,125 @@ +import Button from '@site/src/components/button' + +# Introduction + +Smart contract creation, development, and deployment on TON Blockchain leverages the [FunC programming language](/develop/smart-contracts/#func-language) and [TON Virtual Machine (TVM)](/develop/smart-contracts/#ton-virtual-machine). + +## Quick Start: Your First Smart Contract + +Write and deploy your first smart contract with the _Blueprint_ framework. + +Blueprint is a development environment for writing, testing, and deploying smart contracts. +To create a new demo project use the following command: + +```bash +npm create ton@latest +``` + + + + + +## Getting Started + +### Fun and Easy Tutorials + +Kickstart your journey with our beginner-friendly guide: + +- [TON Hello World: Step-by-step guide for writing your first smart contract](https://ton-community.github.io/tutorials/02-contract/) +- [TON Speed Run tutorials](https://tonspeedrun.com/) + - [🚩 Challenge 1: Simple NFT Deploy](https://github.com/romanovichim/TONQuest1) + - [🚩 Challenge 2: Chatbot Contract](https://github.com/romanovichim/TONQuest2) + - [🚩 Challenge 3: Jetton Vending Machine](https://github.com/romanovichim/TONQuest3) + - [🚩 Challenge 4: Lottery/Raffle](https://github.com/romanovichim/TONQuest4) + - [🚩 Challenge 5: Create UI to interact with the contract in 5 minutes](https://github.com/romanovichim/TONQuest5) + - [🚩 Challenge 6: Analyzing NFT sales on the Getgems marketplace](https://github.com/romanovichim/TONQuest6) + +### TON Course + +We're proud to present the **TON Blockchain Course**, which is a comprehensive guide to the TON Blockchain. The course is designed for developers who want to learn how to create smart contracts and decentralized applications on the TON Blockchain. + +It consists of **9 modules** and covers the basics of the TON Blockchain, smart contract development lifecycle, the FunC programming, and the TON Virtual Machine (TVM). + + + +### Comprehensive Guides + +For those who prefer detail and nuance, visit: + +- [How to work with wallet smart contracts](/develop/smart-contracts/tutorials/wallet) + +## Examples of Smart Contracts + +Explore ready-made smart contract examples and tools provided by the TON community. + +:::info little tip +Feel free to focus on smart contracts written using _FunC_. Focusing on smart contracts written using FunC (_.fc) instead of the lower-level Fift (_.fif) language is often better. +::: + +Standard examples of smart contracts on TON include wallets, electors (which manage validation on TON), and multi-signature wallets, which can be a reference when studying. + + + +## Smart Contract Best Practices + +TON offers endless possibilities. Learn how to get the most out of them while adhering to recommended guidelines. + +- [Smart contract guidelines](/develop/smart-contracts/guidelines) + +## TON Virtual Machine (TVM) + +Discover the engine that runs your smart contracts. + +- [TVM Overview](/learn/tvm-instructions/tvm-overview) + +## Programming Languages + +### 📘 FunC + +The tailor-made language for TON smart contracts. + + + +### 📒 Tact + +The high-level language for TON smart contracts similar to TypeScript and Rust. + +:::caution +Developed by the community. Use with caution. +::: + + + + +### 📕 Fift (advanced) + +:::caution advanced level +Only for the brave! +::: + + + +## Community Tools + +- [disintar/toncli](/develop/smart-contracts/sdk/toncli) — The toncli is the command line interface used to build, deploy, and test FunC contracts. +- [MyLocalTON](/participate/run-nodes/local-ton) — MyLocalTON is used to run a private TON Blockchain in your local environment. +- [tonwhales.com/tools/boc](https://tonwhales.com/tools/boc) — BOC parser +- [tonwhales.com/tools/introspection-id](https://tonwhales.com/tools/introspection-id) — crc32 generator +- [@orbs-network/ton-access](https://www.orbs.com/ton-access/) — decentralized API gateway + +## Further Reading + +Enhance your skillset with these community-driven educational resources. + +- [TON FunC Learning Path](https://blog.ton.org/func-journey) ([RU version](https://github.com/romanovichim/TonFunClessons_ru)) +- [YouTube Tutorials](https://www.youtube.com/@TONDevStudy) [\[RU version\]](https://www.youtube.com/@WikiMar) + +## Additional Resources + +- [What is blockchain? What is a smart contract? What is gas?](https://blog.ton.org/what-is-blockchain) +- [Understanding Transaction Fees](/develop/smart-contracts/fees#how-to-calculate-fees) From 6acc1d128075e2375b0eb832697e1f2d6e5c426a Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:46 +0800 Subject: [PATCH 124/219] New translations readme.md (Chinese Simplified) --- .../develop/smart-contracts/compile/README.md | 201 ++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/compile/README.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/compile/README.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/compile/README.md new file mode 100644 index 0000000000..d65aff9a9c --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/compile/README.md @@ -0,0 +1,201 @@ +# Compile and Build smart contracts on TON + +Here is a list of libraries and repos to build your smart contract. + +**TLDR:** + +- In most cases, it's enough to use Blueprint SDK. +- If you need more low-level approach, you can use ton-compiler or func-js. + +## Blueprint + +### Overview + +A development environment for TON blockchain for writing, testing, and deploying smart contracts. Read more in [Blueprint git repository](https://github.com/ton-community/blueprint). + +### Installation + +Run the following in terminal to create a new project and follow the on-screen instructions: + +```bash +npm create ton@latest +``` + +  + +### Features + +- Streamlined workflow for building, testing and deploying smart contracts +- Dead simple deployment to mainnet/testnet using your favorite wallet (eg. Tonkeeper) +- Blazing fast testing of multiple smart contracts in an isolated blockchain running in-process + +### Tech stack + +1. Compiling FunC with https://github.com/ton-community/func-js (no CLI) +2. Testing smart contracts with https://github.com/ton-community/sandbox +3. Deploying smart contracts with [TON Connect 2](https://github.com/ton-connect), [Tonhub wallet](https://tonhub.com/) or a `ton://` deeplink + +### Requirements + +- [Node.js](https://nodejs.org) with a recent version like v18, verify version with `node -v` +- IDE with TypeScript and FunC support like [Visual Studio Code](https://code.visualstudio.com/) with the [FunC plugin](https://marketplace.visualstudio.com/items?itemName=tonwhales.func-vscode) + +### How to use? + +- [Watch DoraHacks presentation with demo of working with blueprint](https://www.youtube.com/watch?v=5ROXVM-Fojo). +- Read well detailed explanation in [Blueprint repo](https://github.com/ton-community/blueprint#create-a-new-project). + +## ton-compiler + +### Overview + +Packaged FunC compiler for TON smart contracts: + +- GitHub: [ton-community/ton-compiler](https://github.com/ton-community/ton-compiler) +- NPM: [ton-compiler](https://www.npmjs.com/package/ton-compiler) + +### Installation + +```bash npm2yarn +npm install ton-compiler +``` + +### Features + +- Multiple FunC compiler versions +- Doesn't need to install and compile TON +- Programmatic and CLI interfaces +- Ready to use in unit-testing + +### How to use + +This packages adds `ton-compiler` binary to a project. + +FunC compilation is a multi-stage process. One is compiling Func to Fift code that is then compiled to a binary representation. Fift compiler already has Asm.fif bundled. + +FunC stdlib is bundled but could be disabled at runtime. + +#### Console Use + +```bash +# Compile to binary form (for contract creation) +ton-compiler --input ./wallet.fc --output ./wallet.cell + +# Compile to fift (useful for debuging) +ton-compiler --input ./wallet.fc --output-fift ./wallet.fif + +# Compile to binary form and fift +ton-compiler --input ./wallet.fc --output ./wallet.cell --output-fift ./wallet.fif + +# Disable stdlib +ton-compiler --no-stdlib --input ./wallet.fc --output ./wallet.cell --output-fift ./wallet.fif + +# Pick version +ton-compiler --version "legacy" --input ./wallet.fc --output ./wallet.cell --output-fift ./wallet.fif +``` + +#### Programmatic Use + +```javascript +import { compileContract } from "ton-compiler"; +let result = await compileContract({ code: 'source code', stdlib: true, version: 'latest' }); +if (result.ok) { + console.log(result.fift); // Compiled Fift assembler + console.log(result.cell); // Compiled cell Buffer +} else { + console.warn(result.logs); // Output logs +} +``` + +## func-js + +### Overview + +_Cross-platform_ bindings for TON FunC compiler. + +It's more low-level than ton-compiler, so use it only if ton-compiler doesn't work for you. + +- GitHub: [ton-community/func-js](https://github.com/ton-community/func-js) +- NPM: [@ton-community/func-js](https://www.npmjs.com/package/@ton-community/func-js) + +### Installation + +```bash npm2yarn +npm install @ton-community/func-js +``` + +### Features + +- No need to compile of download FunC binaries +- Works both in Node.js & **WEB** (WASM support is required) +- Compiles straight to BOC with code Cell +- Assembly is returned fot debugging purposes +- Does not depend on file-system + +### How to use + +Internally, this package uses both FunC compiler and Fift interpreter combined to single lib compiled to WASM. + +Simple schema: + +```bash +(your code) -> WASM(FunC -> Fift -> BOC) +``` + +Sources to the internal lib could be found [here](https://github.com/ton-blockchain/ton/tree/testnet/crypto/funcfiftlib). + +### Usage example + +```javascript +import {compileFunc, compilerVersion} from '@ton-community/func-js'; +import {Cell} from 'ton'; + +async function main() { + // You can get compiler version + let version = await compilerVersion(); + + let result = await compileFunc({ + // Entry points of your project + entryPoints: ['main.fc'], + // Sources + sources: { + "stdlib.fc": "", + "main.fc": "", + // Rest of the files which are included in main.fc if some + } + }); + + if (result.status === 'error') { + console.error(result.message) + return; + } + + // result.codeBoc contains base64 encoded BOC with code cell + let codeCell = Cell.fromBoc(Buffer.from(result.codeBoc, "base64"))[0]; + + // result.fiftCode contains assembly version of your code (for debug purposes) + console.log(result.fiftCode) +} +``` + +Note that all FunC source file contents used in your project should be passed to `sources`, including: + +- entry points +- stdlib.fc (if you use it) +- all files included in entry points + +### Validated by TON Community + +- [ton-community/ton-compiler](/develop/smart-contracts/sdk/javascript#ton-compiler) — ready-to-use FunC compiler for TON smart contracts. +- [ton-community/func-js](/develop/smart-contracts/sdk/javascript#func-js) — cross-platform bindings for the TON FunC compiler. + +### Third-party contributors + +- [grozzzny/ton-compiler-groz](https://github.com/grozzzny/ton-compiler-groz) — TON FunC smart contract compiler. +- [Termina1/tonc](https://github.com/Termina1/tonc) — TONC (TON Compiler). Uses WASM, so perfect for Linux. + +## Other + +- [disintar/toncli](https://github.com/disintar/toncli) — one of the most popular approaches. You even can use it with Docker. +- [tonthemoon/ton](https://github.com/tonthemoon/ton) — _(closed beta)_ one-line TON binaries installer. +- [delab-team/tlbcrc](https://github.com/delab-team/tlbcrc) — Package & CLI to generate opcodes by TL-B scheme From 28ea6e98e4218ec09df2a72116938b9d34545fb8 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:47 +0800 Subject: [PATCH 125/219] New translations precompiled.md (Chinese Simplified) --- .../core-contracts/precompiled.md | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/core-contracts/precompiled.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/core-contracts/precompiled.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/core-contracts/precompiled.md new file mode 100644 index 0000000000..75846390fc --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/core-contracts/precompiled.md @@ -0,0 +1,46 @@ +# Precompiled Contracts + +_Precompiled smart contract_ is a contract with a C++ implementation in node. +When a validator runs a transaction on such smart contract, it can execute this implementation instead of TVM. +This improves performance and allows to reduce computation fees. + +## Config + +The list of precompiled contracts is stored in the masterchain config: + +``` +precompiled_smc#b0 gas_usage:uint64 = PrecompiledSmc; +precompiled_contracts_config#c0 list:(HashmapE 256 PrecompiledSmc) = PrecompiledContractsConfig; +_ PrecompiledContractsConfig = ConfigParam 45; +``` + +`list:(HashmapE 256 PrecompiledSmc)` is a map `(code_hash -> precomplied_smc)`. +If the code hash of a contract is found in this map then the contract is considered _precompiled_. + +## Contract execution + +Any transaction on a _precompiled smart contract_ (i.e. any contract with code hash found in `ConfigParam 45`) is executed in as follows: + +1. Get `gas_usage` from the masterchain config. +2. If the balance is not enough to pay for `gas_usage` gas then the compute phase fails with skip reason `cskip_no_gas`. +3. Code can be executed in two ways: +4. If the precompiled execution is disabled or the C++ implementation is not available in the current version of the node then TVM runs as usual. Gas limit for TVM is set to the transaction gas limit (1M gas). +5. If the precopmiled implementation is enabled and available then the C++ implementation is executed. +6. Override [compute phase values](https://github.com/ton-blockchain/ton/blob/dd5540d69e25f08a1c63760d3afb033208d9c99b/crypto/block/block.tlb#L308): set `gas_used` to `gas_usage`; set `vm_steps`, `vm_init_state_hash`, `vm_final_state_hash` to zero. +7. Computation fees are based on `gas_usage`, not the actual TVM gas usage. + +When precompiled contract is executed in TVM, the 17th element of `c7` is set to `gas_usage` and can be retrieved by `GETPRECOMPILEDGAS` instruction. For non-precompiled contracts this value is `null`. + +The execution of precompiled contracts is disabled by default. Run `validator-engine` with `--enable-precompiled-smc` flag to enable it. + +Note that both ways to execute a precompiled contract yield the same transaction. +Therefore, validators with and without C++ implemetation can safely co-exist in the network. +This allows adding new entries to `ConfigParam 45` without requiring all validators to update node software immediately. + +## Available implementaions + +Hic sunt dracones. + +## See Also + +- [Governance Contracts](/develop/smart-contracts/governance) From 2c0098be2e2a406da31a50350697e0bb80e3d352 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:47 +0800 Subject: [PATCH 126/219] New translations ide-plugins.md (Chinese Simplified) --- .../environment/ide-plugins.md | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/environment/ide-plugins.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/environment/ide-plugins.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/environment/ide-plugins.md new file mode 100644 index 0000000000..b16c2e655b --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/environment/ide-plugins.md @@ -0,0 +1,31 @@ +# IDE plugins + +## IntelliJ IDEs Plugin + +![](/img/docs/ton-jetbrains-plugin.png) + +:::info +This plugin can be used with any JetBrains product. +(IntelliJ IDEA, WebStorm, PyCharm, CLion, etc.) +::: + +There are several ways to install a plugin: + +- Find plugin directly in the IDE plugins section with "**TON**" keywords +- [Marketplace link](https://plugins.jetbrains.com/plugin/23382-ton) +- [GitHub repository](https://github.com/ton-blockchain/intellij-ton) + +## VS Code plugin + +Visual Studio Code is a free and popular IDE for developers. + +- [Marketplace link](https://marketplace.visualstudio.com/items?itemName=tonwhales.func-vscode) +- [GitHub repository](https://github.com/ton-foundation/vscode-func) + +## FunC Sublime Text plugin + +- [GitHub repository](https://github.com/savva425/func_plugin_sublimetext3) + +## Neovim + +To enable syntax highlighting in Neovim, follow the installation instructions in the [nvim-treesitter quickstart guide](https://github.com/nvim-treesitter/nvim-treesitter#quickstart). From 818441823716c0028e4aac8cec4b52f9cc64f360 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:48 +0800 Subject: [PATCH 127/219] New translations installation.md (Chinese Simplified) --- .../environment/installation.md | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/environment/installation.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/environment/installation.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/environment/installation.md new file mode 100644 index 0000000000..04b795af79 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/environment/installation.md @@ -0,0 +1,166 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import Button from '@site/src/components/button' + +# Precompiled binaries + +:::caution important +You no longer need to manually install binaries with Blueprint SDK. +::: + +All binaries for development and testing are provided with the Blueprint SDK. + +\ + +## Precompiled binaries + +If you don't use Blueprint SDK for smart contracts development, you can use precompiled binaries for your operating system and tool of choice. + +### Prerequisites + +For the local development of TON smart contracts _without Javascript_, you need to prepare binaries of `func`, `fift`, and `lite client` on your device. + +You can download and set them up below, or read this article from TON Society: + +- [Setting up TON Development Environment](https://blog.ton.org/setting-up-a-ton-development-environment) + +### 1. Download + +Download the binaries from the table below. Make sure to select the correct version for your operating system and to install any additional dependencies: + +| OS | TON binaries | fift | func | lite-client | Additional dependencies | +| ---------------------------------- | ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | +| MacOS x86-64 | [download](https://github.com/ton-blockchain/ton/releases/latest/download/ton-mac-x86-64.zip) | [download](https://github.com/ton-blockchain/ton/releases/latest/download/fift-mac-x86-64) | [download](https://github.com/ton-blockchain/ton/releases/latest/download/func-mac-x86-64) | [download](https://github.com/ton-blockchain/ton/releases/latest/download/lite-client-mac-x86-64) | | +| MacOS arm64 | [download](https://github.com/ton-blockchain/ton/releases/latest/download/ton-mac-arm64.zip) | | | | `brew install openssl ninja libmicrohttpd pkg-config` | +| Windows x86-64 | [download](https://github.com/ton-blockchain/ton/releases/latest/download/ton-win-x86-64.zip) | [download](https://github.com/ton-blockchain/ton/releases/latest/download/fift.exe) | [download](https://github.com/ton-blockchain/ton/releases/latest/download/func.exe) | [download](https://github.com/ton-blockchain/ton/releases/latest/download/lite-client.exe) | Install [OpenSSL 1.1.1](/ton-binaries/windows/Win64OpenSSL_Light-1_1_1q.msi) | +| Linux x86_64 | [download](https://github.com/ton-blockchain/ton/releases/latest/download/ton-linux-x86_64.zip) | [download](https://github.com/ton-blockchain/ton/releases/latest/download/fift-linux-x86_64) | [download](https://github.com/ton-blockchain/ton/releases/latest/download/func-linux-x86_64) | [download](https://github.com/ton-blockchain/ton/releases/latest/download/lite-client-linux-x86_64) | | +| Linux arm64 | [download](https://github.com/ton-blockchain/ton/releases/latest/download/ton-linux-arm64.zip) | | | | `sudo apt install libatomic1 libssl-dev` | + +### 2. Setup your binaries + +export const Highlight = ({children, color}) => ( +\ +{children} +); + + + + +1. After downloading, you need to `create` a new folder. For example: **`C:/Users/%USERNAME%/ton/bin`** and move the installed files there. + +2. To open the Windows environment variables, press the Win + R buttons on the keyboard, type `sysdm.cpl`, and press Enter. + +3. On the "_Advanced_" tab, click the "Environment Variables..." button. + +4. In the _"User variables"_ section, select the "_Path_" variable and click "Edit" (this is usually required). + +5. To add a new value `(path)` to the system variable in the next window, click the button "New". + In the new field, you need to specify the path to the folder where the previously installed files are stored: + +``` +C:\Users\%USERNAME%\ton\bin\ +``` + +6. To check whether everything was installed correctly, run in terminal (_cmd.exe_): + +```bash +fift -V -and func -V -and lite-client -V +``` + +7. If you plan to use fift, you need `FIFTPATH` environment variable with the necessary imports: + + 1. Download [fiftlib.zip](/ton-binaries/windows/fiftlib.zip) + 2. Open the zip in some directory on your machine (like **`C:/Users/%USERNAME%/ton/lib/fiftlib`**) + 3. Create a new (click button "New") environment variable `FIFTPATH` in "_User variables_" section. + 4. In the "_Variable value_" field, specify the path to the files: **`/%USERNAME%/ton/lib/fiftlib`** and click OK. Done. + +:::caution important +Instead of the `%USERNAME%` keyword, you must insert your own `username`.\ +::: +1. After downloading, make sure the downloaded binaries are executable by changing their permissions.```bash +chmod +x func +chmod +x fift +chmod +x lite-client +```2. It's also useful to add these binaries to your path (or copy them to `/usr/local/bin`) so you can access them from anywhere.```bash +cp ./func /usr/local/bin/func +cp ./fift /usr/local/bin/fift +cp ./lite-client /usr/local/bin/lite-client +```3. To check that everything was installed correctly, run in terminal.```bash +fift -V && func -V && lite-client -V +```4. If you plan to `use fift`, also download [fiftlib.zip](/ton-binaries/windows/fiftlib.zip), open the zip in some directory on your device (like `/usr/local/lib/fiftlib`), and set the environment variable `FIFTPATH` to point to this directory.``` +unzip fiftlib.zip +mkdir -p /usr/local/lib/fiftlib +cp fiftlib/* /usr/local/lib/fiftlib +```:::info Hey, you're almost finished :) +Remember to set the [environment variable](https://stackoverflow.com/questions/14637979/how-to-permanently-set-path-on-linux-unix) `FIFTPATH` to point to this directory. +::: + + + + +1. After downloading, make sure the downloaded binaries are executable by changing their permissions. + +```bash +chmod +x func +chmod +x fift +chmod +x lite-client +``` + +2. It's also useful to add these binaries to your path (or copy them to `/usr/local/bin`) so you can access them from anywhere. + +```bash +cp ./func /usr/local/bin/func +cp ./fift /usr/local/bin/fift +cp ./lite-client /usr/local/bin/lite-client +``` + +3. To check that everything was installed correctly, run in terminal. + +```bash +fift -V && func -V && lite-client -V +``` + +4. If you plan to `use fift`, also download [fiftlib.zip](/ton-binaries/windows/fiftlib.zip), open the zip in some directory on your device (like `/usr/local/lib/fiftlib`), and set the environment variable `FIFTPATH` to point to this directory. + +``` +unzip fiftlib.zip +mkdir -p /usr/local/lib/fiftlib +cp fiftlib/* /usr/local/lib/fiftlib +``` + +:::info Hey, you're almost finished :) +Remember to set the [environment variable](https://stackoverflow.com/questions/14637979/how-to-permanently-set-path-on-linux-unix) `FIFTPATH` to point to this directory. +::: + + + + +## Build from source + +If you don't want to rely on pre-compiled binaries and prefer to compile the binaries yourself, you can follow the [official instructions](/develop/howto/compile). + +The ready-to-use gist instructions are provided below: + +### Linux (Ubuntu / Debian) + +```bash +sudo apt update +sudo apt install git make cmake g++ libssl-dev zlib1g-dev wget +cd ~ && git clone https://github.com/ton-blockchain/ton.git +cd ~/ton && git submodule update --init +mkdir ~/ton/build && cd ~/ton/build && cmake .. -DCMAKE_BUILD_TYPE=Release && make -j 4 +``` + +## Other sources for binaries + +The core team provides automatic builds for several operating systems as [GitHub Actions](https://github.com/ton-blockchain/ton/releases/latest). + +Click on the link above, choose the workflow on the left relevant to your operating system, click on a recent green passing build, and download `ton-binaries` under "Artifacts". From 6fc1b215a9a6ab49e6c4532930b344113ea7e697 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:49 +0800 Subject: [PATCH 128/219] New translations testnet.md (Chinese Simplified) --- .../smart-contracts/environment/testnet.md | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/environment/testnet.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/environment/testnet.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/environment/testnet.md new file mode 100644 index 0000000000..ddb4da5e7f --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/environment/testnet.md @@ -0,0 +1,27 @@ +# Understanding Testnet + +Use the TON test network for development and testing purpose. + +:::info +Coins in test network have no value, the test network can be reset. +::: + +- Testnet global config: https://ton.org/testnet-global.config.json +- You can get free test coins in the [@test_giver_ton_bot](https://t.me/testgiver_ton_bot) +- Check the testnet status in this Telegram channel: [@testnetstatus](https://t.me/testnetstatus) + +## Services + +For convenience, almost the entire infrastructure of the mainnet (wallets, API, bridges, etc.) has been recreated in the test network. + +- Explorer: https://testnet.tonscan.org +- Web wallet: https://wallet.ton.org?testnet=true +- Browser extension: use [mainnet browser extension](https://chrome.google.com/webstore/detail/ton-wallet/nphplpgoakhhjchkkhmiggakijnkhfnd) and [do this](https://github.com/toncenter/ton-wallet#switch-between-mainnettestnet-in-extension). +- Testnet TON Center API: https://testnet.toncenter.com +- Testnet HTTP API: https://testnet.tonapi.io/ +- Testnet bridge: https://ton.org/bridge?testnet=true + +## Some third parties + +- To switch to [Tonkeeper's testnet](https://tonkeeper.com/), tap the version 5 times in the settings. +- Testnet CryptoBot: https://t.me/CryptoTestnetBot From ff95f681594c2cbab3dfda633503fe219b6ae439 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:49 +0800 Subject: [PATCH 129/219] New translations examples.md (Chinese Simplified) --- .../develop/smart-contracts/examples.md | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/examples.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/examples.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/examples.md new file mode 100644 index 0000000000..46c714be3b --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/examples.md @@ -0,0 +1,164 @@ +# Examples of Smart Contracts + +On this page, you can find TON smart contract references implemented for various program software. + +:::info +Make sure you have thoroughly tested contracts before using them in a production environment. This is a critical step to ensure the proper functioning and security of your software. +::: + +## FunC Smart Contracts + +### Production used Contracts + +| Contracts | Description | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [wallet-contract](https://github.com/ton-blockchain/wallet-contract)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ton-blockchain/wallet-contract\&name=wallet-contract) | Wallet v4 is proposed version of wallet to replace v3 or older wallets | +| [liquid-staking-contract](https://github.com/ton-blockchain/liquid-staking-contract/)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ton-blockchain/liquid-staking-contract/\&name=liquid-staking-contract) | Liquid Staking (LSt) is a protocol that connects TON holders of all caliber with hardware node operators to participate in TON Blockchain validation through assets pooling. | +| [modern_jetton](https://github.com/EmelyanenkoK/modern_jetton)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/EmelyanenkoK/modern_jetton\&name=modern_jetton) | Implementation of standard jetton with additional withdraw_tons and withdraw_jettons. | +| [highloadwallet-v3](https://github.com/ton-blockchain/highload-wallet-contract-v3) | This wallet is made for who need to send transactions at very high rates. For example, crypto exchanges. | +| [stablecoin-contract](https://github.com/ton-blockchain/stablecoin-contract) | Jetton-with-governance FunC smart contracts. Used for stablecoins such as USDt. | +| [governance-contract](https://github.com/ton-blockchain/governance-contract)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ton-blockchain/governance-contract\&name=governance-contract) | Core TON Blockchain contracts `elector-code.fc` and `config-code.fc`. | +| [bridge-func](https://github.com/ton-blockchain/bridge-func)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ton-blockchain/bridge-func\&name=bridge-func) | TON-EVM Toncoin Bridge. | +| [token-bridge-func](https://github.com/ton-blockchain/token-bridge-func)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ton-blockchain/token-bridge-func\&name=token-bridge-func) | TON-EVM token bridge - FunC smart contracts. | +| [lockup-wallet-contract/universal](https://github.com/ton-blockchain/lockup-wallet-contract/tree/main/universal)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ton-blockchain/lockup-wallet-contract/tree/main/universal\&name=lockup-wallet-contract/universal) | Universal lockup wallet is contract that can store locked and restricted coins. | +| [lockup-wallet-contract/vesting](https://github.com/ton-blockchain/lockup-wallet-contract/tree/main/vesting)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ton-blockchain/lockup-wallet-contract/tree/main/vesting\&name=lockup-wallet-contract/vesting) | Vesting wallet smart-contract | +| [multisig-contract](https://github.com/ton-blockchain/multisig-contract)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ton-blockchain/multisig-contract\&name=multisig-contract) | `(n, k)`-multisig wallet is a wallet with `n` private keys holders, which accepts requests to send messages if the request collects at least `k` signatures of the holders. | +| [token-contract](https://github.com/ton-blockchain/token-contract)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ton-blockchain/token-contract\&name=token-contract) | Fungible, Non-Fungible, Semi-Fungible Tokens Smart Contracts | +| [dns-contract](https://github.com/ton-blockchain/dns-contract)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ton-blockchain/dns-contract\&name=dns-contract) | Smart contracts of `.ton` zone. | +| [nominator-pool](https://github.com/ton-blockchain/nominator-pool)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ton-blockchain/nominator-pool\&name=nominator-pool) | Nominator pool smart contract | +| [single-nominator-pool](https://github.com/orbs-network/single-nominator)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ton-blockchain/nominator-pool\&name=nominator-pool) | Single Nominator Pool smart contract | +| [vesting-contract](https://github.com/ton-blockchain/vesting-contract)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ton-blockchain/nominator-pool\&name=nominator-pool) | Nominator pool smart contract | +| [storage](https://github.com/ton-blockchain/ton/tree/master/storage/storage-daemon/smartcont)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ton-blockchain/ton/tree/master/storage/storage-daemon/smartcont\&name=storage) | TON Storage provider and fabric contracts | + +### Ecosystem Contracts + +| Contracts | Description | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [telemint](https://github.com/TelegramMessenger/telemint)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/TelegramMessenger/telemint\&name=telemint) | Telegram Usenames(`nft-item.fc`) and Telegram Numbers(`nft-item-no-dns.fc`) contracts. | +| [capped-fungible-token](https://github.com/TonoxDeFi/capped-fungible-token)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/TonoxDeFi/capped-fungible-token\&name=capped-fungible-token) | Basic implementation of smart contracts for Jetton wallet and Jetton minter | +| [gusarich-airdrop](https://github.com/Gusarich/airdrop/tree/main/contracts) | Implementation of a Scalable Airdrop System for the TON blockchain. It can be used to distribute Jettons on-chain to any number of wallets. | +| [getgems-io/nft-contracts](https://github.com/getgems-io/nft-contracts/tree/main/packages/contracts/sources)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/getgems-io/nft-contracts/tree/main/packages/contracts/sources\&name=getgems-io/nft-contracts) | Getgems NFT Contracts | +| [lockup-wallet-deployment](https://github.com/ton-defi-org/lockup-wallet-deployment)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ton-defi-org/lockup-wallet-deployment\&name=lockup-wallet-deployment) | Deploy and run lockup Contract end to end | +| [WTON](https://github.com/TonoxDeFi/WTON)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/TonoxDeFi/WTON\&name=WTON) | This smart contract provides an implementation of wrapped toncoin, called WTON | +| [wton-contract](https://github.com/ton-community/wton-contract)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ton-community/wton-contract\&name=wton-contract) | wTON contracts | +| [contract-verifier-contracts](https://github.com/ton-community/contract-verifier-contracts)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ton-community/contract-verifier-contracts\&name=contract-verifier-contracts) | Sources registry contracts which stores an on-chain proof per code cell hash. | +| [vanity-contract](https://github.com/ton-community/vanity-contract)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ton-community/vanity-contract\&name=vanity-contract) | Smart contract that allows to "mine" any suitable address for any contract. | +| [ton-config-smc](https://github.com/ton-foundation/ton-config-smc)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ton-foundation/ton-config-smc\&name=ton-config-smc) | Simple contract for storing versioned data in TON Blockchain. | +| [ratelance](https://github.com/ProgramCrafter/ratelance/tree/main/contracts/func)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ProgramCrafter/ratelance/tree/main/contracts/func\&name=ratelance) | Ratelance is freelance platform that seeks to remove barriers between potential employers and workers. | +| [logger.fc](https://github.com/tonwhales/ton-contracts/blob/master/contracts/logger.fc)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/tonwhales/ton-contracts/blob/master/contracts/logger.fc\&name=logger.fc) | Contract that saves data in the local storage. | +| [ton-nominators](https://github.com/tonwhales/ton-nominators)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/tonwhales/ton-nominators\&name=ton-nominators) | Ton Whales Nominator pool source code. | +| [ton-link-contract-v3](https://github.com/ton-link/ton-link-contract-v3)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ton-link/ton-link-contract-v3\&name=ton-link-contract-v3) | Ton-link allows smart contracts to access data outside of the blockchain while maintaining data security. | +| [delab-team/fungible-token](https://github.com/delab-team/contracts/tree/main/fungible-token)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/delab-team/contracts/tree/main/fungible-token\&name=delab-team/fungible-token) | DeLab TON fungible-token implementation | +| [whitelisted-wallet.fc](https://github.com/tonwhales/ton-contracts/blob/master/contracts/whitelisted-wallet.fc)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/tonwhales/ton-contracts/blob/master/contracts/whitelisted-wallet.fc\&name=whitelisted-wallet.fc) | Simple Whitelisted Wallet Contract | +| [delab-team/jetton-pool](https://github.com/delab-team/contracts/tree/main/jetton-pool)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/delab-team/contracts/tree/main/jetton-pool\&name=delab-team/jetton-pool) | The Jetton Pool TON smart contract is designed to create farm pools. | +| [ston-fi/contracts](https://github.com/ston-fi/dex-core/tree/main/contracts)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ston-fi/dex-core/tree/main/contracts\&name=ston-fi/contracts) | Stonfi DEX core contracts | +| [onda-ton](https://github.com/0xknstntn/onda-ton)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/0xknstntn/onda-ton\&name=onda-ton) | Onda Lending Pool - Core smart contracts of the first lending protocol on TON | +| [ton-stable-timer](https://github.com/ProgramCrafter/ton-stable-timer)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ProgramCrafter/ton-stable-timer\&name=ton-stable-timer) | TON Stable Timer contract | +| [HipoFinance/contract](https://github.com/HipoFinance/contract)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/HipoFinance/contract\&name=HipoFinance) | hTON is a decentralized, permission-less, open-source liquid staking protocol on TON Blockchain | + +### Learning Contracts + +| Contracts | Description | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | +| [counter.fc](https://github.com/ton-community/blueprint/blob/main/example/contracts/counter.fc)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ton-community/blueprint/blob/main/example/contracts/counter.fc\&name=counter.fc) | Counter smart contract with comments. | +| [simple-distributor](https://github.com/ton-community/simple-distributor)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/ton-community/simple-distributor\&name=simple-distributor) | Simple TON distributor. | +| [ping-pong.fc](https://github.com/tonwhales/ton-nft/blob/main/packages/nft/ping-pong/ping-pong.fc)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/tonwhales/ton-nft/blob/main/packages/nft/ping-pong/ping-pong.fc\&name=ping-pong.fc) | Simple contract to test sending Toncoin in different modes. | +| [ton-random](https://github.com/puppycats/ton-random)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/puppycats/ton-random\&name=ton-random) | Two contracts that will help you in generating random numbers on-chain. | +| [Blueprint simple contract](https://github.com/liminalAngel/1-func-project/blob/master/contracts/main.fc)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/liminalAngel/1-func-project/blob/master/contracts/main.fc\&name=simple_contract) | Example smart contract | +| [Blueprint jetton_minter.fc](https://github.com/liminalAngel/func-blueprint-tutorial/blob/master/6/contracts/jetton_minter.fc)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/liminalAngel/func-blueprint-tutorial/blob/master/6/contracts/jetton_minter.fc\&name=jetton_minter.fc) | Smart contract example to mint Jettons on-chain. | +| [Simple TON DNS Subdomain manager](https://github.com/Gusarich/simple-subdomain)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/Gusarich/simple-subdomain\&name=Simple_TON_DNS_Subdomain_manager) | TON DNS subdomains manager. | +| [disintar/sale-dapp](https://github.com/disintar/sale-dapp/tree/master/func)
🪄 [Run in WebIDE](https://ide.nujan.io/?importURL=https://github.com/disintar/sale-dapp/tree/master/func\&name=disintar/sale-dapp) | React + NFT sale DApp with FunC | + +### TON Smart Challenges + +#### TON Smart Challenge 1 + +- https://github.com/nns2009/TON-FunC-contest-1/tree/main +- https://github.com/pyAndr3w/func-contest1-solutions +- https://github.com/crazyministr/TonContest-FunC/tree/master/func-contest1 + +#### TON Smart Challenge 2 + +- https://github.com/ton-blockchain/func-contest2-solutions +- https://github.com/nns2009/TON-FunC-contest-2 +- https://github.com/crazyministr/TonContest-FunC/tree/master/func-contest2 + +#### TON Smart Challenge 3 + +- https://github.com/nns2009/TON-FunC-contest-3 +- https://github.com/shuva10v/func-contest3-solutions +- https://github.com/crazyministr/TonContest-FunC/tree/master/func-contest3 + +#### TON Smart Challenge 4 + +- https://github.com/akifoq/tsc4 (TOP optimized) +- https://github.com/Gusarich/tsc4 +- https://github.com/Skydev0h/tsc4 +- https://github.com/aSpite/tsc4-contracts (FunC solution) +- [https://github.com/ProgramCrafter/tsc4](https://github.com/ProgramCrafter/tsc4/tree/c1616e12d1b449b01fdcb787a3aa8442e671371e/contracts) (FunC solution) + +## Fift Smart Contracts + +- [CreateState.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/CreateState.fif) +- [asm-to-cpp.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/asm-to-cpp.fif) +- [auto-dns.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/auto-dns.fif) +- [complaint-vote-req.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/complaint-vote-req.fif) +- [complaint-vote-signed.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/complaint-vote-signed.fif) +- [config-proposal-vote-req.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/config-proposal-vote-req.fif) +- [config-proposal-vote-signed.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/config-proposal-vote-signed.fif) +- [create-config-proposal.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/create-config-proposal.fif) +- [create-config-upgrade-proposal.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/create-config-upgrade-proposal.fif) +- [create-elector-upgrade-proposal.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/create-elector-upgrade-proposal.fif) +- [envelope-complaint.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/envelope-complaint.fif) +- [gen-zerostate-test.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/gen-zerostate-test.fif) +- [gen-zerostate.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/gen-zerostate.fif) +- [highload-wallet-v2-one.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/highload-wallet-v2-one.fif) +- [highload-wallet-v2.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/highload-wallet-v2.fif) +- [highload-wallet.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/highload-wallet.fif) +- [manual-dns-manage.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/manual-dns-manage.fif) +- [new-auto-dns.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/new-auto-dns.fif) +- [new-highload-wallet-v2.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/new-highload-wallet-v2.fif) +- [new-highload-wallet.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/new-highload-wallet.fif) +- [new-manual-dns.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/new-manual-dns.fif) +- [new-pinger.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/new-pinger.fif) +- [new-pow-testgiver.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/new-pow-testgiver.fif) +- [new-restricted-wallet.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/new-restricted-wallet.fif) +- [new-restricted-wallet2.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/new-restricted-wallet2.fif) +- [new-restricted-wallet3.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/new-restricted-wallet3.fif) +- [new-testgiver.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/new-testgiver.fif) +- [new-wallet-v2.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/new-wallet-v2.fif) +- [new-wallet-v3.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/new-wallet-v3.fif) +- [new-wallet.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/new-wallet.fif) +- [show-addr.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/show-addr.fif) +- [testgiver.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/testgiver.fif) +- [update-config-smc.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/update-config-smc.fif) +- [update-config.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/update-config.fif) +- [update-elector-smc.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/update-elector-smc.fif) +- [validator-elect-req.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/validator-elect-req.fif) +- [validator-elect-signed.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/validator-elect-signed.fif) +- [wallet-v2.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/wallet-v2.fif) +- [wallet-v3.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/wallet-v3.fif) +- [wallet.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/wallet.fif) +- [wallet-v3-code.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/wallet-v3-code.fif) + +## FunC Libraries and Helpers + +- https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/stdlib.fc +- https://github.com/TonoxDeFi/open-contracts/tree/main/contracts/crypto/elliptic-curves +- https://github.com/TonoxDeFi/open-contracts/tree/main/contracts/math +- https://github.com/TonoxDeFi/open-contracts/tree/main/contracts/messages +- https://github.com/TonoxDeFi/open-contracts/tree/main/contracts/slices +- https://github.com/TonoxDeFi/open-contracts/tree/main/contracts/strings +- https://github.com/TonoxDeFi/open-contracts/tree/main/contracts/tuples +- https://github.com/TonoxDeFi/open-contracts/tree/main/contracts/utils +- https://github.com/disintar/sale-dapp/tree/master/func + +## Add Reference + +If you want share new example smart contract, make your PR for this [page](https://github.com/ton-community/ton-docs/tree/main/docs/develop/smart-contracts/examples.md). + +## See Also + +- [Develop Smart Contracts Introduction](/develop/smart-contracts/) +- [How to work with wallet smart contracts](/develop/smart-contracts/tutorials/wallet) +- [[You Tube] Ton Dev Study FunC & BluePrint lessons](https://www.youtube.com/watch?v=7omBDfSqGfA\&list=PLtUBO1QNEKwtO_zSyLj-axPzc9O9rkmYa) From 3e15415dba2091d29055ab48575afa721a61e9e2 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:50 +0800 Subject: [PATCH 130/219] New translations fees.md (Chinese Simplified) --- .../current/develop/smart-contracts/fees.md | 178 ++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/fees.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/fees.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/fees.md new file mode 100644 index 0000000000..6b7a61170b --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/fees.md @@ -0,0 +1,178 @@ +# Transaction Fees + +Every TON user should keep in mind that _commission depends on many factors_. + +## Gas + +All fees are calculated in Gas. It's a special currency for fees in TON. + +All fees are nominated and fixed in a certain gas amount, but the gas price itself is not fixed. Today the price for gas is: + +```cpp +1 gas = 1000 nanotons = 0,000 001 TON +``` + +### Average transaction cost + +> **TLDR:** Today, every transaction costs around **~0.005 TON** + +Even if TON price increases 100 times, transactions will remain ultra-cheap; less than $0.01. Moreover, validators may lower this value if they see commissions have become expensive [read why they're interested](#gas-changing-voting-process). + +:::info +The current gas amount is written in the Network Config [param 20](https://explorer.toncoin.org/config?workchain=-1\&shard=8000000000000000\&seqno=22185244\&roothash=165D55B3CFFC4043BFC43F81C1A3F2C41B69B33D6615D46FBFD2036256756382\&filehash=69C43394D872B02C334B75F59464B2848CD4E23031C03CA7F3B1F98E8A13EE05#configparam20). +::: + +### Gas changing voting process + +The gas fee, like many other parameters of TON, is configurable and may be changed by a special vote made in the mainnet. + +Changing any parameter requires getting 66% of the validator votes. + +#### Could gas cost more? + +> _Does it mean that one day gas prices could rise by 1,000 times or even more?_ + +Technically, yes; but in fact, no. + +Validators receive a small fee for processing transactions, and charging higher commissions will lead to a decrease in the number of transactions which will make the validating process less beneficial. + +### How to calculate fees? + +Fees on TON are difficult to calculate in advance, as their amount depends on transaction run time, account status, message content and size, blockchain network settings, and a number of other variables that cannot be calculated until the transaction is sent. Read about [computation fees](/develop/howto/fees-low-level#computation-fees) in low-level article overview. + +That is why even NFT marketplaces usually take an extra amount of TON (_~1 TON_) and return (_`1 - transaction_fee`_) later. + +However, let's read more about how fees are supposed to function on TON. + +## Basic Fees Formula + +According to the [low-level fees overview](/develop/howto/fees-low-level), fees on TON are calculated by this formula: + +```cpp +transaction_fee = storage_fees + + in_fwd_fees + + computation_fees + + action_fees + + out_fwd_fees +``` + +## Elements of transaction fee + +- `storage_fees` is the amount you pay for storing a smart contract in the blockchain. In fact, you pay for every second the smart contract is stored on the blockchain. + - _Example_: your TON Wallet is also a smart contract, and it pays a storage fee every time you receive or send a transaction. Read more about [how storage fees are calculated](/develop/smart-contracts/fees#storage-fee). +- `in_fwd_fees` is a charge for importing messages from outside the blockchain. Every time you make a transaction, it must be delivered to the validators who will process it. + - _Example_: each transaction you make with your wallet app (like Tonkeeper) requires first to be distributed among validation nodes. +- `computation_fees` is the amount you pay for executing code in the virtual machine. The larger the code, the more fees must be paid. + - _Example_: each time you send a transaction with your wallet (which is a smart contract), you execute the code of your wallet contract and pay for it. +- `action_fees` is a charge for sending outgoing messages made by a smart contract. +- `out_fwd_fees` stands for a charge for sending messages outside from TON Blockchain to interact with off-chain services (e.g., logs) and external blockchains. + - Not used because it's not implemented. So today is equal to 0. + +## Storage fee + +TON validators collect storage fees from smart contracts. + +Storage fees are collected from the smart contract balance at the **Storage phase** of any transaction. Read more about phases and how TVM works [here](/learn/tvm-instructions/tvm-overview#transactions-and-phases). + +It’s important to keep in mind that on TON you pay for both the execution of a smart contract and for the **used storage**: + +```cpp +bytes * second +``` + +It means you have to pay a storage fee for having TON Wallet (even if it's very-very small). + +If you have not used your TON Wallet for a significant period of time (1 year), _you will have to pay a significantly larger commission than usual because the wallet pays commission on sending and receiving transactions_. + +### Formula + +You can approximately calculate storage fees for smart contracts using this formula: + +```cpp + storage_fee = (cells_count * cell_price + bits_count * bit_price) + / 2^16 * time_delta +``` + +Let's examine each value more closely: + +- `price`—price for storage for `time_delta` seconds +- `cells_count`—count of cells used by smart contract +- `bits_count`—count of bits used by smart contract +- `cell_price`—price of single cell +- `bit_price`—price of single bit + +Both `cell_price` and `bit_price` could be obtained from Network Config [param 18](https://explorer.toncoin.org/config?workchain=-1\&shard=8000000000000000\&seqno=22185244\&roothash=165D55B3CFFC4043BFC43F81C1A3F2C41B69B33D6615D46FBFD2036256756382\&filehash=69C43394D872B02C334B75F59464B2848CD4E23031C03CA7F3B1F98E8A13EE05#configparam18). + +Current values are: + +- Workchain. + ```cpp + bit_price_ps:1 + cell_price_ps:500 + ``` +- Masterchain. + ```cpp + mc_bit_price_ps:1000 + mc_cell_price_ps:500000 + ``` + +### Calculator Example + +You can use this JS script to calculate storage price for 1 MB in the workchain for 1 year + +```js live + +// Welcome to LIVE editor! +// feel free to change any variables + +function storageFeeCalculator() { + + const size = 1024 * 1024 * 8 // 1MB in bits + const duration = 60 * 60 * 24 * 365 // 1 Year in secs + + const bit_price_ps = 1 + const cell_price_ps = 500 + + const pricePerSec = size * bit_price_ps + + + Math.ceil(size / 1023) * cell_price_ps + + let fee = (pricePerSec * duration / 2**16 * 10**-9) + let mb = (size / 1024 / 1024 / 8).toFixed(2) + let days = Math.floor(duration / (3600 * 24)) + + let str = `Storage Fee: ${fee} TON (${mb} MB for ${days} days)` + + return str +} + + +``` + +## FAQ + +Here are the most frequently asked questions by visitors of TON: + +### Fees for sending TON? + +Average fee for sending any amount of TON is 0.0055 TON. + +### Fees for sending Jettons? + +Average fee for sending any amount of a custom Jettons is 0.037 TON. + +### Cost of minting NFTs? + +Average fee for minting one NFT is 0.08 TON. + +### Cost of saving data in TON? + +Saving 1 MB of data for one year on TON will cost you 6.01 TON. Note that you don't usually need to store big amounts of data on-chain. Consider [TON Storage](/participate/ton-storage/storage-daemon) if you need decentralized storage. + +### How to calculate fees in FunC? + +- [Smart contract function to calculate forward fees in FunC](https://github.com/ton-blockchain/token-contract/blob/main/misc/forward-fee-calc.fc) + +## References + +- ["Low-level fees overview"](/develop/howto/fees-low-level#fees-calculation-formulas)—read about the formulas for calculating commissions. +- _Based on the [@thedailyton article](https://telegra.ph/Commissions-on-TON-07-22) originally written by [menschee](https://github.com/menschee)_ From efa67f35e0645cfaca02cb3001256cc069046726 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:51 +0800 Subject: [PATCH 131/219] New translations governance.md (Chinese Simplified) --- .../develop/smart-contracts/governance.md | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/governance.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/governance.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/governance.md new file mode 100644 index 0000000000..19ff4ba690 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/governance.md @@ -0,0 +1,82 @@ +# Governance Contracts + +In TON, consensus parameters of node operation related to TVM, catchain, fees, and chain topology (as well as how those parameters are stored and updated) are controlled by a set of special smart contracts (in contrast to the old-fashioned and inflexible ways of hardcoding those parameters adopted by blockchains of previous generations). That way, TON implements comprehensive and transparent on-chain governance. The set of special contracts itself is governed by parameters and currently includes the Elector, Config, and DNS contracts and in future will be extended by extra-currency Minter and others. + +## Elector + +The Elector smart contract controls the way how rounds of validation change each other, who gets the duty to validate the blockchain, and how rewards for validation would be distributed. If you want to become a validator and interact with Elector, check [validator instrucitons](https://ton.org/validator). + +Elector stores data of Toncoin that is not withdrawn in `credits` hashmap, new applications in `elect` hashmap, and information about previous elections in _past\_elections_ hashmap (the latter is stored inside _complaints_ about validator misbehavior and _frozen_-stakes of validator for already finished rounds, which are withheld for `stake_held_for`(ConfigParam 15)). The Elector contract has three purposes: + +- Process applications for the election of validators +- Hold elections +- Process validator misbehaving reports +- Distribute validation rewards + +### Processing applications + +To create an application, a future validator needs to form a special message that contains the corresponding parameters (ADNL address, public key, `max_factor`, etc.), attach it to some sum of TON (called a stake), and send it to the Elector. In turn, the Elector checks those parameters and either registers an application or immediately returns the stake back to the sender. Note that applications are only accepted from addresses on the masterchain. + +### Conducting elections + +The Elector is a special smart contract that has the option to be forcedly invoked at the beginning and end of each block (so-called Tick and Tock transactions). The Elector, indeed, is invoked on each block and checks whether it is time to conduct a new election. + +The general concept of the election process is to consider all applications, in particular their TON amount and `max_factor` (the maximal ratio of validation work this applicant is agreed to do in comparison to the weakest validator), and set weights to each validator proportional to the TON amount but in such a way that all `max_factor` conditions are met. + +It is technically implemented as follows: + +1. Elector takes all applications with a stake amount above the current network minimum `min_stake` (ConfigParam 17). +2. It sorts them by stake in descending order. +3. If there are more participants than the maximum number of validators (`max_validators` ConfigParam 16), discard the tail of the list. +4. Cycle `i` from `1` to `N` (remaining number of participants). + +- Take the first `i` element from the list (sorted in descending order) +- Assume that _i_-th candidate will be the last accepted (and thus has the lowest weight) and calculate an effective stake (`true_stake` in code) with respect to `max_factor`. In other words, the effective stake of a _j_-th (`j Date: Tue, 30 Apr 2024 17:59:52 +0800 Subject: [PATCH 132/219] New translations guidelines.mdx (Chinese Simplified) --- .../develop/smart-contracts/guidelines.mdx | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines.mdx new file mode 100644 index 0000000000..0ac26777a4 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines.mdx @@ -0,0 +1,23 @@ +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](/develop/smart-contracts/guidelines/internal-messages) +- [External messages](/develop/smart-contracts/guidelines/external-messages) +- [Using non-bounceable messages](/develop/smart-contracts/guidelines/non-bouncable-messages) +- [Get-methods](/develop/smart-contracts/guidelines/get-methods) +- ["accept_message" effects](/develop/smart-contracts/guidelines/accept) +- [Paying for processing queries and sending responses](/develop/smart-contracts/guidelines/processing) +- [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) + +## TON Course: Contract Development + +The [TON Blockchain Course](https://stepik.org/course/176754/) is a comprehensive guide to TON Blockchain development. + +- Module 2 is dedicated to **TVM, transactions, scalability and business cases**. +- Module 3 is dedicated to **smart contract development lifecycle**. + + From 0e548bc7c09b44a189310e7847800215816a5cbc Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:53 +0800 Subject: [PATCH 133/219] New translations accept.md (Chinese Simplified) --- .../smart-contracts/guidelines/accept.md | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/accept.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/accept.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/accept.md new file mode 100644 index 0000000000..47a3a4b5e6 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/accept.md @@ -0,0 +1,27 @@ +# Accept Message Effects + +`accept_message` and `set_gas_limit` may cause not that straightforward effects when doing exactly what it's said in the [stdlib reference](/develop/func/stdlib#accept_message). + +## External messages + +External messages are processed as follows: + +- The `gas_limit` is set to `gas_credit` (ConfigParam 20 and ConfigParam 21), which is equal to 10k gas. +- During the spending of those credits, a contract should call `accept_message` to `set_gas_limit`, indicating that it is ready to pay fees for message processing. +- If `gas_credit` is reached or computation is finished, and `accept_message` is not called, the message will be completely discarded (as if it never existed at all). +- Otherwise, a new gas limit, equal to `contract_balance/gas_price` (in the case of `accept_message`) or a custom number (in the case of `set_gas_limit`), will be set; after the transaction ends, full computation fees will be deducted from the contract balance (in this way, `gas_credit` is indeed **credit**, not free gas). + +Note that if, after `accept_message`, some error is thrown (either in ComputePhase or ActionPhase), the transaction will be written to the blockchain, and fees will be deducted from the contract balance. However, storage will not be updated, and actions will not be applied, as is the case in any transaction with an error exit code. + +As a result, if the contract accepts an external message and then throws an exception due to an error in the message data or the sending of an incorrectly serialized message, it will pay for processing but will have no way of preventing message replay. **The same message will be accepted by the contract over and over until it consumes the entire balance.** + +## Internal message + +By default, when a contract receives an internal message, the gas limit is set to `message_balance`/`gas_price`. In other words, the message pays for its processing. By using `accept_message`/`set_gas_limit`, the contract may change the gas limit during execution. + +Note that manual settings of gas limits do not interfere with bouncing behavior; messages will be bounced if sent in bounceable mode and contain enough money to pay for their processing and the creation of bounce messages. + +:::info example + +If in the same example, the computation cost is 0.5 (instead of 0.005), there will be no bounce (the message balance would be `0.1 - 0.5 - 0.001 = -0.401`, thus no bounce), and the contract balance will be `1 + 0.1 - 0.5` = `0.6` TON. +::: From e37ab581e5e61257295915a60309d6ab5cc152d8 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:53 +0800 Subject: [PATCH 134/219] New translations external-messages.md (Chinese Simplified) --- .../guidelines/external-messages.md | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/external-messages.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/external-messages.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/external-messages.md new file mode 100644 index 0000000000..75512b5b4b --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/external-messages.md @@ -0,0 +1,28 @@ +# External messages + +External messages are `sent from the outside` to the smart contracts residing in TON Blockchain to make them perform certain actions. + +For instance, a wallet smart contract expects to receive external messages containing orders (e.g., internal messages to be sent from the wallet smart contract) signed by the wallet's owner. When such an external message is received by the wallet smart contract, it first checks the signature, then accepts the message (by running the TVM primitive `ACCEPT`), and then performs whatever actions are required. + +:::danger +Notice that all external messages `must be protected` against replay attacks. The validators normally remove an external message from the pool of suggested external messages (received from the network); however, in some situations `another validator` could process the same external message twice (thus creating a second transaction for the same external message, leading to the duplication of the original action). Even worse, a `malicious actor could extract` the external message from the block containing the processing transaction and re-send it later. This could force a wallet smart contract to repeat a payment, for example. +::: + +export const Highlight = ({children, color}) => ( +\ +{children} +); + +The simplest way to protect smart contracts from replay attacks related to external messages is to store a 32-bit counter `cur-seqno` in the persistent data of the smart contract, and to expect a `req-seqno` value in (the signed part of) any inbound external messages. Then an external message is accepted only if both the signature is valid and `req-seqno` equals `cur-seqno`. After successful processing, the `cur-seqno` value in the persistent data is increased by one, so the same external message will never be accepted again. + +And One could also include an `expire-at` field in the external message, and accept an external message only if the current Unix time is less than the value of this field. This approach can be used in conjunction with `seqno`; alternatively, the receiving smart contract could store the set of (the hashes of) all recent (not expired) accepted external messages in its persistent data, and reject a new external message if it is a duplicate of one of the stored messages. Some garbage collection of expired messages in this set should also be performed to avoid bloating the persistent data. + +:::note +In general, an external message begins with a 256-bit signature (if needed), a 32-bit `req-seqno` (if needed), a 32-bit `expire-at` (if needed), and possibly a 32-bit `op` and other required parameters depending on `op`. The layout of external messages does not need to be as standardized as that of internal messages because external messages are not used for interaction between different smart contracts (written by different developers and managed by different owners). +::: From bef222e8a2cca4b0e8d2ebedbb6a81b9ebda3a5a Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:54 +0800 Subject: [PATCH 135/219] New translations get-methods.md (Chinese Simplified) --- .../smart-contracts/guidelines/get-methods.md | 371 ++++++++++++++++++ 1 file changed, 371 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/get-methods.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/get-methods.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/get-methods.md new file mode 100644 index 0000000000..67a773a645 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/get-methods.md @@ -0,0 +1,371 @@ +# Get Methods + +:::note +Before proceeding, it is recommended that readers have a basic understanding of the [FunC programming language](/develop/func/overview) and [smart contract development](/develop/smart-contracts) on TON Blockchain. This will help you grasp the information provided here more effectively. +::: + +## 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. + +These functions are very common for most smart contracts. For example, the default [Wallet contract](/participate/wallets/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. + +## 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. + + Example: + + ```func + int get_balance() method_id { + return get_data().begin_parse().preload_uint(64); + } + ``` + +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: + + ```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. + + 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); + } + ``` + +2. **Conditional data retrieval**: Sometimes, the data that needs to be retrieved depends on certain conditions, such as current time. + + Example: + + ```func + (int) get_ready_to_be_used() method_id { + int ready? = now() >= 1686459600; + return ready?; + } + ``` + +## Most common get methods + +### Standard wallets + +#### seqno() + +```func +int seqno() method_id { + 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](/develop/smart-contracts/tutorials/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); +} +``` + +- [What is Subwallet ID?](/develop/smart-contracts/tutorials/wallet#what-is-subwallet-id) + +#### get_public_key() + +```func +int get_public_key() method_id { + var cs = get_data().begin_parse().skip_bits(64); + return cs.preload_uint(256); +} +``` + +Retrieves the public key associated with the wallet. + +### Jettons + +#### get_wallet_data() + +```func +(int, slice, slice, cell) get_wallet_data() method_id { + 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 + +#### 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); +} +``` + +Returns data of a jetton master, including its total supply, the address of its admin, the content of the jetton, and its wallet code. + +#### get_wallet_address(slice owner_address) + +```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); +} +``` + +Given the address of the owner, this method calculates and returns the address for the owner's jetton wallet contract. + +### NFTs + +#### get_nft_data() + +```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); +} +``` + +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. + +#### 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); +} +``` + +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. + +#### 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); +} +``` + +Given an index, this method calculates and returns the address of the corresponding NFT item contract of 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()); +} +``` + +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. + +#### 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()); +} +``` + +Given an index and [individual NFT content](#get_nft_data), this method fetches and returns the combined common and individual content of the NFT. + +## How to work with get methods + +### Calling get methods on popular explorers + +#### Tonviewer + +You can call get methods on the bottom of the page in the "Methods" tab. + +- https://tonviewer.com/EQAWrNGl875lXA6Fff7nIOwTIYuwiJMq0SmtJ5Txhgnz4tXI?section=Methods + +#### Ton.cx + +You can call get methods on the "Get methods" tab. + +- 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-core/ton) library +- [Blueprint](/develop/smart-contracts/sdk/javascript) SDK + +Let's say there is some contract with a 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 +} +``` + +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: + +```ts +import { Address, TonClient } from 'ton'; + +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); +} + +main(); +``` + +This code will result `Total: 123` output. The number can be different, 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. + +At first, you need to add a special method in 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: + +```ts +async getTotal(provider: ContractProvider) { + const result = (await provider.get('get_total', [])).stack; + return result.readNumber(); +} +``` + +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. + +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); +}); +``` + +Check it by running `npx blueprint test` in your terminal and if you did everything correct, 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. + +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. + +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. + +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. + +### 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. + +### 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](/develop/smart-contracts/guidelines/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. + +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. + +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. + +Let's consider a simple example: + +```func +#include "imports/stdlib.fc"; + +int get_total() method_id { + 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); + } +} +``` + +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. + +For the 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](/develop/data-formats/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. + +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. + +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. + +## 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. From 7236633fe3d34d155eb74132681cdbe9cefe32c1 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:55 +0800 Subject: [PATCH 136/219] New translations internal-messages.md (Chinese Simplified) --- .../guidelines/internal-messages.md | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/internal-messages.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/internal-messages.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/internal-messages.md new file mode 100644 index 0000000000..ddb56018e7 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/internal-messages.md @@ -0,0 +1,107 @@ +# Internal messages + +## Overview + +Smart contracts interact with each other by sending so-called **internal messages**. When an internal message reaches its intended destination, an ordinary transaction is created on behalf of the destination account, and the internal message is processed as specified by the code and the persistent data of this account (smart contract). + +:::info +In particular, the processing transaction can create one or several outbound internal messages, some of which could be addressed to the source address of the internal message being processed. This can be used to create simple "client-server applications" when a query is encapsulated in an internal message and sent to another smart contract, which processes the query and sends back a response again as an internal message. +::: + +This approach leads to the necessity of distinguishing whether an internal message is intended as a "query", "response", or doesn't require any additional processing (like a "simple money transfer"). Furthermore, when a response is received, there must be a means to understand to which query it corresponds. + +In order to achieve this goal, the following approaches for the internal message layout can be used (notice that TON Blockchain does not enforce any restrictions on the message body, so these are indeed just recommendations). + +### Internal Message Structure + +The body of the message can be embedded into the message itself, or be stored in a separate cell referred to from the message, as indicated by the TL-B scheme fragment: + +```tlb +message$_ {X:Type} ... body:(Either X ^X) = Message X; +``` + +The receiving smart contract should accept at least internal messages with embedded message bodies (whenever they fit into the cell containing the message). If it accepts message bodies in separate cells (using the `right` constructor of `(Either X ^X)`), the processing of the inbound message should not depend on the specific embedding option chosen for the message body. On the other hand, it is perfectly valid not to support message bodies in separate cells at all for simpler queries and responses. + +### Internal Message Body + +The message body typically begins with the following fields: + +``` +* A 32-bit (big-endian) unsigned integer `op`, identifying the `operation` to be performed, or the `method` of the smart contract to be invoked. +* A 64-bit (big-endian) unsigned integer `query_id`, used in all query-response internal messages to indicate that a response is related to a query (the `query_id` of a response must be equal to the `query_id` of the corresponding query). If `op` is not a query-response method (e.g., it invokes a method that is not expected to send an answer), then `query_id` may be omitted. +* The remainder of the message body is specific for each supported value of `op`. +``` + +### Simple Message with Comment + +If `op` is zero, then the message is a "simple transfer message with comment". The comment is contained in the remainder of the message body (without any `query_id` field, i.e., starting from the fifth byte). If it does not begin with the byte `0xff`, the comment is a text one; it can be displayed "as is" to the end user of a wallet (after filtering out invalid and control characters and checking that it is a valid UTF-8 string). + +When comment is long enough that it doesn't fit in a cell, non-fitting end of the line is put to the first reference of the cell. This process continues recursively to describe comments that doesn't fit in two or more cells: + +``` +root_cell("0x00000000" - 32 bit, "string" up to 123 bytes) + ↳1st_ref("string continuation" up to 127 bytes) + ↳1st_ref("string continuation" up to 127 bytes) + ↳.... +``` + +The same format is used for comments for NFT and [jetton](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#forward_payload-format) transfers. + +For instance, users may indicate the purpose of a simple transfer from their wallet to the wallet of another user in this text field. On the other hand, if the comment begins with the byte `0xff`, the remainder is a "binary comment", which should not be displayed to the end user as text (only as a hex dump if necessary). The intended use of "binary comments" is, e.g., to contain a purchase identifier for payments in a store, to be automatically generated and processed by the store's software. + +Most smart contracts should not perform non-trivial actions or reject the inbound message on receiving a "simple transfer message". In this way, once `op` is found to be zero, the smart contract function for processing inbound internal messages (usually called `recv_internal()`) should immediately terminate with a zero exit code indicating success (e.g., by throwing exception `0`, if no custom exception handler has been installed by the smart contract). This will lead to the receiving account being credited with the value transferred by the message without any further effect. + +### Messages with Encrypted Comments + +If `op` is `0x2167da4b`, then the message is a "transfer message with encrypted comment". This message is serialized as follows: + +Input: + +- `pub_1` and `priv_1` - Ed25519 public and private keys of the sender, 32 bytes each. +- `pub_2` - Ed25519 public key of the receiver, 32 bytes. +- `msg` - a message to be encrypted, arbitrary byte string. `len(msg) <= 960`. + +Encryption algo is as follows: + +1. Calculate `shared_secret` using `priv_1` and `pub_2`. +2. Let `salt` be the [bas64url representation](https://docs.ton.org/learn/overviews/addresses#user-friendly-address) of the sender wallet address with `isBounceable=1` and `isTestnetOnly=0`. +3. Select byte string `prefix` of length between 16 and 31 such that `len(prefix+msg)` is divisible by 16. The first byte of `prefix` is equal to `len(prefix)`, other bytes are random. Let `data = prefix + msg`. +4. Let `msg_key` be the first 16 bytes of `hmac_sha512(salt, data)`. +5. Calculate `x = hmac_sha512(shared_secret, msg_key)`. Let `key=x[0:32]` and `iv=x[32:48]`. +6. Encrypt `data` using AES-256 in CBC mode with `key` and `iv`. +7. Construct the encrypted comment: + 1. `pub_xor = pub_1 ^ pub_2` - 32 bytes. This allows each party to decrypt the message without looking up other's public key. + 2. `msg_key` - 16 bytes. + 3. Encrypted `data`. +8. The body of the message starts with the 4-byte tag `0x2167da4b`. Then this encrypted comment is stored: + 1. Byte string is divided into segments and is stored in a chain of cells `c_1,...,c_k` (`c_1` is the root of the body). Each cell (except for the last one) has a reference to the next. + 2. `c_1` contains up to 35 bytes (not including 4-byte tag), all other cells contain up to 127 bytes. + 3. This format has the following limitations: `k <= 16`, max string length is 1024. + +The same format is used for comments for NFT and jetton transfers, note that public key of sender address and receiver address (not jetton-wallet addresses) should be used. + +:::info +Learn from examples of the message encryption algorithm: + +- [encryption.js](https://github.com/toncenter/ton-wallet/blob/master/src/js/util/encryption.js) +- [SimpleEncryption.cpp](https://github.com/ton-blockchain/ton/blob/master/tonlib/tonlib/keys/SimpleEncryption.cpp) + ::: + +### Simple Transfer Messages Without Comments + +A "simple transfer message without comment" has an empty body (without even an `op` field). The above considerations apply to such messages as well. Note that such messages should have their bodies embedded into the message cell. + +### Distinction Between Query and Response Messages + +We expect "query" messages to have an `op` with the high-order bit clear, i.e., in the range `1 .. 2^31-1`, and "response" messages to have an `op` with the high-order bit set, i.e., in the range `2^31 .. 2^32-1`. If a method is neither a query nor a response (so that the corresponding message body does not contain a `query_id` field), it should use an `op` in the "query" range `1 .. 2^31 - 1`. + +### Handling of Standard Response Messages + +There are some "standard" response messages with the `op` equal to `0xffffffff` and `0xfffffffe`. In general, the values of `op` from `0xfffffff0` to `0xffffffff` are reserved for such standard responses. + +``` +* `op` = `0xffffffff` means "operation not supported". It is followed by the 64-bit `query_id` extracted from the original query, and the 32-bit `op` of the original query. All but the simplest smart contracts should return this error when they receive a query with an unknown `op` in the range `1 .. 2^31-1`. +* `op` = `0xfffffffe` means "operation not allowed". It is followed by the 64-bit `query_id` of the original query, followed by the 32-bit `op` extracted from the original query. +``` + +Notice that unknown "responses" (with an `op` in the range `2^31 .. 2^32-1`) should be ignored (in particular, no response with an `op` equal to `0xffffffff` should be generated in response to them), just as unexpected bounced messages (with the "bounced" flag set). From 8b58fbca86cf5b02dd51318a7c228fddcbdbce9e Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:56 +0800 Subject: [PATCH 137/219] New translations message-delivery-guarantees.mdx (Chinese Simplified) --- .../message-delivery-guarantees.mdx | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/message-delivery-guarantees.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/message-delivery-guarantees.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/message-delivery-guarantees.mdx new file mode 100644 index 0000000000..645ff81055 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/message-delivery-guarantees.mdx @@ -0,0 +1,159 @@ +import ConceptImage from '@site/src/components/conceptImage'; +import ThemedImage from '@theme/ThemedImage'; + +# Message Overview + +TON is an asynchronous blockchain with a complex structure very different from other blockchains. Because of this, new developers often have questions about low-level things in TON. In this article, we will have a look at one of these related to message delivery. + +## What is a message? + +A message is a packet of data sent between actors (users, applications, smart contracts). It typically contains information instructing the receiver on what action to perform, such as updating storage or sending a new message. + +

+
+ +
+

+ +Working with this type of communication is reminiscent of launching a satellite into space. We know the message we've formed, but after its launch, it is necessary to conduct separate observation to find out what results we will obtain. + +## What is a Transaction? + +A transaction in TON consists of the following: + +- the incoming message that initially triggers the contract (special ways to trigger exist) +- contract actions caused by the incoming message, such as an update to the contract's storage (optional) +- outgoing generated messages that are sent to other actors (optional) + +> Technically, a contract can be triggered through special functions such as [Tick-Tock](/develop/data-formats/transaction-layout#tick-tock), but this function more used for internal TON Blockchain core contracts. +> +> Not every transaction results in outgoing messages or updates to the contract's storage — this depends on the actions defined by the contract's code. + +

+ +

+ +If we look at Ethereum or almost any other synchronous blockchain, each transaction can contain several smart contract calls in it. For example, DEXs perform multiple exchanges in one transaction if there is no liquidity for the selected trading pair. + +In an asynchronous system you can't get a response from the destination smart contract in the same transaction. A contract call may take a few blocks to be processed, depending on the length of the route between source and destination. + +To achieve the infinite sharding paradigm, it is necessary to ensure full parallelization, which means that the execution of each transactions is independent of every other. Therefore, instead of transactions which affect and change the state of many contracts at one time, each transaction in TON is only executed on a single smart contract and smart contracts communicate through messages. That way, smart contracts can only interact with each other by calling their functions with special messages and getting a response to them via other messages later. + +:::info +More detailed and accurate description on the [Transaction Layout](/develop/data-formats/transaction-layout) page. +::: + +## What is a Logical time? + +In such a system with asynchronous and parallel smart contract calls, it can be hard to define the order of actions to process. That's why each message in TON has it's _Logical time_ or _Lamport time_ (later just _lt_). It is used to understand which event caused another and what a validator needs to process first. + +It is strictly guaranteed that the transaction resulting from a message will have a _lt_ greater than the _lt_ of the message. Likewise, the _lt_ of a message sent in some transaction is strictly greater than the _lt_ of the transaction that caused it. As well as this, messages that were sent from one account and transactions which happened on one account are strictly ordered as well. + +

+ +

+ +For the case in the image, it turns out: `in_msg_lt < tx0_lt < out_msg_lt` + +Thanks to this, for every account we always know the order of transactions, received messages and sent messages. + +Moreover, if account _A_ sent two messages to account _B_, it is guaranteed that the message with a lower _lt_ will be processed earlier: + +If `msg1_lt < msg2_lt` => `tx1_lt < tx2_lt`. + +

+
+ +
+

+ +Otherwise, an attempt to synchronize delivery would require the state of all the others to be known before processing one shard, thereby breaking parallelization and destroying efficient sharding. + +For each block, we can define the _lt_ span as starting from the first transaction and ending with the _lt_ of the last event in the block (message or transaction). Blocks are ordered the same way as other events in TON and so if one block depends on the other, it has a higher _lt_. The child block in a shard has a higher _lt_ than its parent. A masterchain block's _lt_ is higher that the _lts_ of shard blocks that it lists, since a master block depends on listed shard blocks. Each shard block contains an ordered reference to the latest (at the moment of shard block creation) master block and thus the shard block _lt_ is higher than the referenced master block _lt_. + +## Message delivery + +Fortunately, TON works in such a way that any internal message will definitely be received by the destination account. A message cannot be lost anywhere between the source and its destination. External messages are a little bit different since their acceptance to the block is at the validator's discretion however, once the message is accepted into the incoming message queue, it will be delivered. + +### Delivery Order + +It therefore seems like _lt_ solves the issue about message delivery order, because we know that a transaction with a lower _lt_ will be processed first. But this doesn't work in every scenario. + +Suppose that there are two contracts - _A_ and _B_. _A_ receives an external message which triggers it to send two internal messages to _B_, let's call these messages _1_ and _2_. In this simple case, we can be 100% sure that _1_ will be processed by _B_ before _2_ because it has a lower _lt_. + + + +But this is just a simple case when we only have two contracts. How does our system works in more complex cases? + +### Several Smart Contracts + +Suppose that we have three contracts - _A_, _B_ and _C_. In a transaction, _A_ sends two internal messages _1_ and _2_: one to _B_ and another to _C_. Even though they were created in an exact order (_1_, then _2_), we can't be sure that _1_ will be processed before _2_. This is the case because routes from _A_ to _B_ and from _A_ to _C_ can differ in length and validator sets. If these contracts are in different shard chains, one of the messages may require several blocks to reach the destination contract. + +For better clearness suppose our contracts send back messages `msg1'` and `msg2'` after `msg1` and `msg2` executed by `B` and `C` contracts. As a result it will be apply `tx2'` and `tx1'` on the contract `A`. +We have two possible trace for these transaction, + +1. The first possible order is `tx1'_lt < tx2'_lt`: + +

+
+ +
+

+ +2. The second possible order is `tx2'_lt < tx1'_lt`: + +

+
+ +
+

+ +The same thing happens in the reverse case, when two contracts _B_ and _C_ send a message to one contract _A_. Even if message `B -> A` was sent before `C -> A`, we can't know which one of them will be delivered first. The `B -> A` route may require more shard chain hops. + + + +There can be many possible scenarios of smart contract interactions, and in any scenario with more than 2 contracts, the order of messages delivery may be arbitrary. The only guarantee is that messages from any contract _A_ to any contract _B_ will be processed in order of their logical time. Some examples are below. + + + + + +## Conclusion + +The TON blockchain's asynchronous structure creates challenges for message delivery guarantees. Logical time helps to establish event and transaction order but doesn't guarantee message delivery order between multiple smart contracts due to varying routes in shard chains. Despite these complexities, TON ensures internal message delivery, maintaining network reliability. Developers must adapt to these nuances to harness TON's full potential in building innovative decentralized applications. From e4370161933e44a2017de3bed092bb2581b217c4 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:56 +0800 Subject: [PATCH 138/219] New translations non-bouncable-messages.md (Chinese Simplified) --- .../guidelines/non-bouncable-messages.md | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/non-bouncable-messages.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/non-bouncable-messages.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/non-bouncable-messages.md new file mode 100644 index 0000000000..98bb45fa0b --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/non-bouncable-messages.md @@ -0,0 +1,24 @@ +# Non-bounceable messages + +export const Highlight = ({children, color}) => ( +\ +{children} +); + +Almost all internal messages sent between smart contracts should be bounceable, i.e., should have their "bounce" bit set. Then, if the destination smart contract does not exist, or if it throws an unhandled exception while processing this message, the message will be "bounced" back carrying the remainder of the original value (minus all message transfer and gas fees). The body of the bounced message will contain 32 bit `0xffffffff` followed by 256 bit from original message, but with the "bounce" flag cleared and the "bounced" flag set. Therefore, all smart contracts should check the "bounced" flag of all inbound messages and either silently accept them (by immediately terminating with a zero exit code) or perform some special processing to detect which outbound query has failed. The query contained in the body of a bounced message should never be executed. + +:::info +The query contained in the body of a bounced message should never be executed. +::: + +On some occasions, `non-bounceable internal messages` must be used. For instance, new accounts cannot be created without at least one non-bounceable internal message being sent to them. Unless this message contains a `StateInit` with the code and data of the new smart contract, it does not make sense to have a non-empty body in a non-bounceable internal message. + +:::tip +It is a good idea `not to allow` the end user (e.g., of a wallet) to send unbounceable messages containing large amounts of value (e.g., more than five Toncoins) or to warn them if they do. It is a `better idea` to send a small amount first, then initialize the new smart contract, and then send a larger amount. +::: From 198880dc95842939c131f90bfae9356e631cb287 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:57 +0800 Subject: [PATCH 139/219] New translations processing.md (Chinese Simplified) --- .../smart-contracts/guidelines/processing.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/processing.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/processing.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/processing.md new file mode 100644 index 0000000000..7048c5fe53 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/processing.md @@ -0,0 +1,13 @@ +# Forward Fees + +In general, if a smart contract wants to send a query to another smart contract, it should pay for sending the internal message to the destination smart contract (message forwarding fees), processing this message at the destination (gas fees), and sending back the answer if required (message forwarding fees). + +:::note +In most cases, the sender will attach a small amount of Toncoin (e.g., one Toncoin) to the internal message (sufficient to pay for the processing of this message) and set its "bounce" flag (i.e., send a bounceable internal message); the receiver will return the unused portion of the received value with the answer (deducting message forwarding fees from it). This is normally accomplished by invoking `SENDRAWMSG` with `mode = 64` (cf. Appendix A of the TON VM documentation). +::: + +If the receiver is unable to parse the received message and terminates with a non-zero exit code (for example, because of an unhandled cell deserialization exception), the message will be automatically "bounced" back to its sender, with the "bounce" flag cleared and the "bounced" flag set. The body of the bounced message will contain 32 bit `0xffffffff` followed by 256 bit from original message. It is important to check the "bounced" flag of incoming internal messages before parsing the `op` field in the smart contract and processing the corresponding query (otherwise there is a risk that the query contained in a bounced message will be processed by its original sender as a new separate query). If the "bounced" flag is set, special code could find out which query has failed (e.g., by deserializing `op` and `query_id` from the bounced message) and take appropriate action. A simpler smart contract might simply ignore all bounced messages (terminate with zero exit code if "bounced" flag is set). Note, that "bounced" flag is rewritten during sending so it can not be forged and it is safe to assume that if message came with "bounced" flag it is result of bouncing of some message sent from receiver. + +On the other hand, the receiver might parse the incoming query successfully and find out that the requested method `op` is not supported, or that another error condition is met. Then a response with `op` equal to `0xffffffff` or another appropriate value should be sent back, using `SENDRAWMSG` with `mode = 64` as mentioned above. + +In some situations, the sender wants both to transfer some value to the sender and to receive either a confirmation or an error message. For instance, the validator elections smart contract receives an election participation request along with the stake as the attached value. In such cases, it makes sense to attach, say, one extra Toncoin to the intended value. If there is an error (e.g., the stake may not be accepted for any reason), the full amount received (minus the processing fees) should be returned to the sender along with an error message (e.g., by using `SENDRAWMSG` with `mode = 64` as explained before). In the case of success, the confirmation message is created and exactly one Toncoin is sent back (with the message transferring fees deducted from this value; this is `mode = 1` of `SENDRAWMSG`). From 6c9025c0cd087c6e2a1522f9775c57547c93f88c Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:58 +0800 Subject: [PATCH 140/219] New translations random-number-generation.md (Chinese Simplified) --- .../guidelines/random-number-generation.md | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/random-number-generation.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/random-number-generation.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/random-number-generation.md new file mode 100644 index 0000000000..151f02dcd7 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/guidelines/random-number-generation.md @@ -0,0 +1,106 @@ +# Random number generation + +Generating random numbers is a common task that you may need in many different projects. You might have already seen the `random()` function in FunC docs, but note that its result can be easily predicted unless you employ some additional tricks. + +## How can someone predict a random number? + +Computers are terrible at generating random information because all they do is follow the instructions of users. However, since people frequently need random numbers, they've devised various methods for generating _pseudo-random_ numbers. + +These algorithms typically require you to provide a _seed_ value that will be used to generate a sequence of _pseudo-random_ numbers. So, if you run the same program with the same _seed_ multiple times, you'll consistently get the same result. In TON, the _seed_ is different for each block. + +- [Generation of block random seed](/develop/smart-contracts/security/random) + +Therefore, to predict the result of the `random()` function in a smart contract, you just need to know the current `seed` of the block, which isn't possible if you're not a validator. + +## Simply use `randomize_lt()` + +To make the random number generation unpredictable, you can add the current [Logical Time](/develop/smart-contracts/guidelines/message-delivery-guarantees#what-is-a-logical-time) to the seed, so different transactions will have different seeds and results. + +Just add the `randomize_lt()` call before generating random numbers, and your random numbers will become unpredictable: + +```func +randomize_lt(); +int x = random(); ;; users can't predict this number +``` + +However, you should note that validators or collators may still affect the result of the random number, as they determine the seed of the current block. + +## Is there a way to protect against manipulation by validators? + +To prevent (or at least complicate) the substitution of the seed by validators, you can use more complex schemes. For instance, you could skip one block before generating a random number. If we skip a block, the seed will change in a less predictable manner. + +Skipping blocks isn't a complex task. You can do it by simply sending a message to the Masterchain and back to the workchain of your contract. Let's examine a simple example! + +:::caution +Do not use this example contract in real projects, write your own instead. +::: + +### Main contract in any workchain + +Let's write a simple lottery contract as an example. A user will send 1 TON to it, and with a 50% chance, will get 2 TON back. + +```func +;; set the echo-contract address +const echo_address = "Ef8Nb7157K5bVxNKAvIWreRcF0RcUlzcCA7lwmewWVNtqM3s"a; + +() recv_internal (int msg_value, cell in_msg_full, slice in_msg_body) impure { + var cs = in_msg_full.begin_parse(); + var flags = cs~load_uint(4); + if (flags & 1) { ;; ignore bounced messages + return (); + } + slice sender = cs~load_msg_addr(); + + int op = in_msg_body~load_uint(32); + if ((op == 0) & equal_slice_bits(in_msg_body, "bet")) { ;; bet from user + throw_unless(501, msg_value == 1000000000); ;; 1 TON + + send_raw_message( + begin_cell() + .store_uint(0x18, 6) + .store_slice(echo_address) + .store_coins(0) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) + .store_uint(1, 32) ;; let 1 be echo opcode in our contract + .store_slice(sender) ;; forward user address + .end_cell(), + 64 ;; send the remaining value of an incoming msg + ); + } + elseif (op == 1) { ;; echo + throw_unless(502, equal_slice_bits(sender, echo_address)); ;; only accept echoes from our echo-contract + + slice user = in_msg_body~load_msg_addr(); + + {- + at this point we have skipped 1+ blocks + so let's just generate the random number + -} + randomize_lt(); + int x = rand(2); ;; generate a random number (either 0 or 1) + if (x == 1) { ;; user won + send_raw_message( + begin_cell() + .store_uint(0x18, 6) + .store_slice(user) + .store_coins(2000000000) ;; 2 TON + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; default message headers (see sending messages page) + .end_cell(), + 3 ;; ignore errors & pay fees separately + ); + } + } +} +``` + +Deploy this contract in any workchain you need (probably Basechain) and you're done! + +## Is this method 100% secure? + +While it certainly helps, there's still a chance of manipulation if an intruder has control over several validators simultaneously. In this case, they might, with some probability, [affect](/develop/smart-contracts/security/random#conclusion) the _seed_, which the random number depends on. Even if this probability is extremely small, it's still worth considering. + +With the latest TVM upgrade, the introduction of new values to the `c7` register can further boost the security of random number generation. Specifically, the upgrade adds information about the last 16 masterchain blocks to the `c7` register. + +The masterchain block information, due to its constantly changing nature, can serve as an additional source of entropy for random number generation. By incorporating this data into your randomness algorithm, you can create numbers that are even harder for potential adversaries to predict. + +For more detailed information on this TVM upgrade, please refer to [TVM Upgrade](/learn/tvm-instructions/tvm-upgrade-2023-07). From 0f0be87fd45a571967bc520e920c25ca0665e8cb Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:58 +0800 Subject: [PATCH 141/219] New translations deployment.md (Chinese Simplified) --- .../develop/smart-contracts/learn/deployment.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/learn/deployment.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/learn/deployment.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/learn/deployment.md new file mode 100644 index 0000000000..0aec57e5c8 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/learn/deployment.md @@ -0,0 +1,10 @@ +# Deployment + +Steps to deploy a smart contract on TON blockchain: + +1. Compile func to fift. +2. Wrap the bytecode to BoC. +3. Execute the message address destination(address of the new contract) +4. Top up balance for this address. +5. Send BoC to destination address +6. Check success in the explorer From 6ea8cbdb83ae51e6b7bb9871c333b19d5d44b2ad Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:59:59 +0800 Subject: [PATCH 142/219] New translations libraries.md (Chinese Simplified) --- .../develop/smart-contracts/libraries.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/libraries.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/libraries.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/libraries.md new file mode 100644 index 0000000000..bdc995659b --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/libraries.md @@ -0,0 +1,17 @@ +# FunC SDK and libraries + +## Standard libraries + +- [stdlib](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/stdlib.fc) — Standard library for FunC +- [mathlib](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/mathlib.fc) — Fixed-point mathematical library + +## Libraries from Community + +- [continuation-team/openlib.func](https://github.com/continuation-team/openlib.func) - Reduces transaction fees in common scenarios. +- [open-contracts/utils](https://github.com/TonoxDeFi/open-contracts/tree/main/contracts/utils) — Utility library +- [open-contracts/strings](https://github.com/TonoxDeFi/open-contracts/tree/main/contracts/strings) — Provides operations on strings +- [open-contracts/math](https://github.com/TonoxDeFi/open-contracts/tree/main/contracts/math) — Math library that extends FunC arithmetic operations +- [open-contracts/tuples](https://github.com/TonoxDeFi/open-contracts/tree/main/contracts/tuples) — Collection of tuple-releated functions for FunC +- [open-contracts/crypto](https://github.com/TonoxDeFi/open-contracts/tree/main/contracts/crypto) — Provides operations on curve secp256k1 +- [toncli/test-libs](https://github.com/disintar/toncli/tree/master/src/toncli/lib/test-libs) - Operations on TLB, generation and parsing of typical messages and types +- [ston-fi/funcbox](https://github.com/ston-fi/funcbox) - Collection of FunC snippets and utilities. From eafe1438f44145cefe6b737cb2a51951c5fa8dbd Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:00 +0800 Subject: [PATCH 143/219] New translations messages.md (Chinese Simplified) --- .../develop/smart-contracts/messages.md | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/messages.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/messages.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/messages.md new file mode 100644 index 0000000000..d8895976b3 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/messages.md @@ -0,0 +1,182 @@ +# Sending messages + +Composition, parsing, and sending messages lie at the intersection of [TL-B schemas](/develop/data-formats/tl-b-language), [transaction phases and TVM](/learn/tvm-instructions/tvm-overview). + +Indeed, FunC exposes [send_raw_message](/develop/func/stdlib#send_raw_message) function which expects a serialized message as an argument. + +Since TON is a comprehensive system with wide functionality, messages which need to be able to support all of this functionality may look quite complicated. Still, most of that functionality is not used in common scenarios, and message serialization in most cases may be reduced to: + +```func + cell msg = begin_cell() + .store_uint(0x18, 6) + .store_slice(addr) + .store_coins(amount) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) + .store_slice(message_body) + .end_cell(); +``` + +Therefore, the developer should not be afraid, and if something in this document seems incomprehensible on first reading, it's okay. Just grasp the general idea. + +Let's dive in! + +## Types of messages + +There are three types of messages: + +- external—messages that are sent from outside of the blockchain to a smart contract inside the blockchain. Such messages should be explicitly accepted by smart contracts during so called `credit_gas`. If the message is not accepted, the node should not accept it into a block or relay it to other nodes. +- internal—messages that are sent from one blockchain entity to another. Such messages (in contrast to external) may carry some TON and pay for themselves. Thus, smart contracts that receive such messages may not accept it. In this case, gas will be deducted from the message value. +- logs—messages that are sent from a blockchain entity to the outer world. Generally speaking, there is no mechanism for sending such messages out of the blockchain. In fact, while all nodes in the network have consensus on whether a message was created or not, there are no rules on how to process them. Logs may be directly sent to `/dev/null`, logged to disk, saved an indexed database, or even sent by non-blockchain means (email/telegram/sms), all of these are at the sole discretion of the given node. + +## Message layout + +We will start with the internal message layout. + +TL-B scheme, which describes messages that can be sent by smart contracts, is as follows: + +```tlb +message$_ {X:Type} info:CommonMsgInfoRelaxed + init:(Maybe (Either StateInit ^StateInit)) + body:(Either X ^X) = MessageRelaxed X; +``` + +Let's put it into words. Serialization of any message consists of three fields: info (header of some sort which describes the source, destination, and other metadata), init (field which is only required for initialization of messages), and body (message payload). + +`Maybe` and `Either` and other types of expressions mean the following: + +- when we have the field `info:CommonMsgInfoRelaxed`, it means that the serialization of `CommonMsgInfoRelaxed` is injected directly to the serialization cell. +- when we have the field `body:(Either X ^X)`, it means that when we (de)serialize some type `X`, we first put one `either` bit, which is `0` if `X` is serialized to the same cell, or `1` if it is serialized to the separate cell. +- when we have the field `init:(Maybe (Either StateInit ^StateInit))`, it means that we first put `0` or `1` depending on whether this field is empty or not; and if it is not empty, we serialize `Either StateInit ^StateInit` (again, put one `either` bit which is `0` if `StateInit` is serialized to the same cell or `1` if it is serialized to a separate cell). + +`CommonMsgInfoRelaxed` layout is as follows: + +```tlb +int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool + src:MsgAddress dest:MsgAddressInt + value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams + created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed; + +ext_out_msg_info$11 src:MsgAddress dest:MsgAddressExt + created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed; +``` + +Let's focus on `int_msg_info` for now. +It starts with 1bit prefix `0`, then there are three 1-bit flags, namely whether Instant Hypercube Routing disabled (currently always true), whether message should be bounced if there are errors during it's processing, whether message itself is result of bounce. Then source and destination addresss are serialized, followed by the value of the message and four integers related to message forwarding fees and time. + +If a message is sent from the smart contract, some of those fields will be rewritten to the correct values. In particular, validator will rewrite `bounced`, `src`, `ihr_fee`, `fwd_fee`, `created_lt` and `created_at`. That means two things: first, another smart-contract during handling message may trust those fields (sender may not forge source address, `bounced` flag, etc); and second, that during serialization we may put to those fields any valid values (anyway those values will be overwritten). + +Straight-forward serialization of the message would be as follows: + +```func + var msg = begin_cell() + .store_uint(0, 1) ;; tag + .store_uint(1, 1) ;; ihr_disabled + .store_uint(1, 1) ;; allow bounces + .store_uint(0, 1) ;; not bounced itself + .store_slice(source) + .store_slice(destination) + ;; serialize CurrencyCollection (see below) + .store_coins(amount) + .store_dict(extra_currencies) + .store_coins(0) ;; ihr_fee + .store_coins(fwd_value) ;; fwd_fee + .store_uint(cur_lt(), 64) ;; lt of transaction + .store_uint(now(), 32) ;; unixtime of transaction + .store_uint(0, 1) ;; no init-field flag (Maybe) + .store_uint(0, 1) ;; inplace message body flag (Either) + .store_slice(msg_body) + .end_cell(); +``` + +However, instead of step-by-step serialization of all fields, usually developers use shortcuts. Thus, let's consider how messages can be sent from the smart contract using an example from [elector-code](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/elector-code.fc#L153). + +```func +() send_message_back(addr, ans_tag, query_id, body, grams, mode) impure inline_ref { + ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000 + var msg = begin_cell() + .store_uint(0x18, 6) + .store_slice(addr) + .store_coins(grams) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) + .store_uint(ans_tag, 32) + .store_uint(query_id, 64); + if (body >= 0) { + msg~store_uint(body, 32); + } + send_raw_message(msg.end_cell(), mode); +} +``` + +First, it put `0x18` value into 6 bits that is put `0b011000`. What is it? + +- First bit is `0`—1bit prefix which indicates that it is `int_msg_info`. + +- Then there are 3 bits `1`, `1` and `0`, meaning Instant Hypercube Routing is disabled, messages can be bounced, and that message is not the result of bouncing itself. + +- Then there should be sender address, however since it anyway will be rewritten with the same effect any valid address may be stored there. The shortest valid address serialization is that of `addr_none` and it serializes as a two-bit string `00`. + +Thus, `.store_uint(0x18, 6)` is the optimized way of serializing the tag and the first 4 fields. + +Next line serializes the destination address. + +Then we should serialize values. Generally, the message value is a `CurrencyCollection` object with the following scheme: + +```tlb +nanograms$_ amount:(VarUInteger 16) = Grams; + +extra_currencies$_ dict:(HashmapE 32 (VarUInteger 32)) + = ExtraCurrencyCollection; + +currencies$_ grams:Grams other:ExtraCurrencyCollection + = CurrencyCollection; +``` + +This scheme means that in addition to the TON value, message may carry the dictionary of additional _extra-currencies_. However, currently we may neglect it and just assume that the message value is serialized as "number of nanotons as variable integer" and "`0` - empty dictionary bit". + +Indeed, in the elector code above we serialize coins' amounts via `.store_coins(grams)` but then just put a string of zeros with length equal to `1 + 4 + 4 + 64 + 32 + 1 + 1`. What is it? + +- First bit stands for empty extra-currencies dictionary. +- Then we have two 4-bit long fields. They encode 0 as `VarUInteger 16`. In fact, since `ihr_fee` and `fwd_fee` will be overwritten, we may as well put there zeroes. +- Then we put zero to `created_lt` and `created_at` fields. Those fields will be overwritten as well; however, in contrast to fees, these fields have a fixed length and are thus encoded as 64- and 32-bit long strings. +- _(we had alredy serialized the message header and passed to init/body at that moment)_ +- Next zero-bit means that there is no `init` field. +- The last zero-bit means that msg_body will be serialized in-place. +- After that, message body (with arbitrary layout) is encoded. + +That way, instead of individual serialization of 14 parameters, we execute 4 serialization primitives. + +## Full scheme + +Full scheme of message layout and the layout of all constituting fields (as well as scheme of ALL objects in TON) are presented in [block.tlb](https://github.com/ton-blockchain/ton/blob/master/crypto/block/block.tlb). + +## Message size + +:::info cell size +Note that any [Cell](/learn/overviews/cells) may contain up to `1023` bits. If you need to store more data, you should split it into chunks and store in reference cells. +::: + +If, for instance, your message body size is 900 bits long, you can not store it in the same cell as the message header. +Indeed, in addition to message header fields, the total size of the cell will be more than 1023 bits, and during serialization there will be `cell overflow` exception. In this case, instead of `0` that stands for "inplace message body flag (Either)" there should be `1` and the message body should be stored in the reference cell. + +Those things should be handled carefully due to the fact that some fields have variable sizes. + +For instance, `MsgAddress` may be represented by four constructors: `addr_none`, `addr_std`, `addr_extern`, `addr_var` with length from 2 bits ( for `addr_none`) to 586 bits (for `addr_var` in the largest form). The same stands for nanotons' amounts which is serialized as `VarUInteger 16`. That means, 4 bits indicating the byte length of the integer and then indicated earlier bytes for integer itself. That way, 0 nanotons will be serialized as `0b0000` (4 bits which encode a zero-length byte string and then zero bytes), while 100.000.000 TON (or 100000000000000000 nanotons) will be serialized as `0b10000000000101100011010001010111100001011101100010100000000000000000` (`0b1000` stands for 8 bytes and then 8 bytes themselves). + +## Message modes + +As you might've noticed, we send messages with `send_raw_message` which, apart from consuming the message itself, also accepts the mode. To figure out the mode that best suits your needs, take a look at the following table: + +| Mode | Description | +| :---- | :--------------------------------------------------------------------------------------------------------------------- | +| `0` | Ordinary message | +| `64` | Carry all the remaining value of the inbound message in addition to the value initially indicated in the new message | +| `128` | Carry all the remaining balance of the current smart contract instead of the value originally indicated in the message | + +| Flag | Description | +| :---- | :--------------------------------------------------------------------------------------------------------------- | +| `+1` | Pay transfer fees separately from the message value | +| `+2` | Ignore any errors arising while processing this message during the action phase | +| `+16` | In the case of action fail - bounce transaction. No effect if `+2` is used. | +| `+32` | Current account must be destroyed if its resulting balance is zero (often used with Mode 128) | + +To build a mode for the `send_raw_message`, you just have to combine modes and flags by adding them together. For example, if you want to send a regular message and pay transfer fees separately, use the Mode `0` and Flag `+1` to get `mode = 1`. If you want to send the whole contract balance and destroy it immidiately, use the Mode `128` and Flag `+32` to get `mode = 160`. From 07adac59d2a314b87452748f70cb6e87ff96929f Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:01 +0800 Subject: [PATCH 144/219] New translations javascript.mdx (Chinese Simplified) --- .../smart-contracts/sdk/javascript.mdx | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/sdk/javascript.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/sdk/javascript.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/sdk/javascript.mdx new file mode 100644 index 0000000000..2778b8e2da --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/sdk/javascript.mdx @@ -0,0 +1,54 @@ +import Button from '@site/src/components/button' + +# Blueprint SDK + +![Blueprint](\img\blueprint\logo.svg) + +A development environment for TON for writing, testing, and deploying smart contracts. + +## Quick start 🚀 + +Run the following in terminal to create a new project and follow the on-screen instructions: + +```bash +npm create ton@latest +``` + + + +### Core features + +- Streamlined workflow for building, testing and deploying smart contracts +- Dead simple deployment to mainnet/testnet using your favorite wallet (eg. Tonkeeper) +- Blazing fast testing of multiple smart contracts in an isolated blockchain running in-process + +### Tech stack + +- Compiling FunC with https://github.com/ton-community/func-js (no cli) +- Testing smart contracts with https://github.com/ton-org/sandbox +- Deploying smart contracts with TON Connect 2.0 compatible wallets or a `ton://` deeplink + +### Requirements + +- [Node.js](https://nodejs.org/) with a recent version like v18, verify version with `node -v` +- IDE with TypeScript and FunC support like [Visual Studio Code](https://code.visualstudio.com/) with the [FunC plugin](https://marketplace.visualstudio.com/items?itemName=tonwhales.func-vscode) + +## References + +### GitHub + +- https://github.com/ton-org/blueprint + +### Materials + +- [Blueprint using on DoraHacks stream](https://www.youtube.com/watch?v=5ROXVM-Fojo) +- [Create a new project](https://github.com/ton-org/blueprint#create-a-new-project) +- [Develop a new smart contract](https://github.com/ton-org/blueprint#develop-a-new-contract) +- [\[YouTube\] Func with Blueprint EN](https://www.youtube.com/watch?v=7omBDfSqGfA&list=PLtUBO1QNEKwtO_zSyLj-axPzc9O9rkmYa) ([RU version](https://youtube.com/playlist?list=PLyDBPwv9EPsA5vcUM2vzjQOomf264IdUZ)) + +## See Also + +- [Develop Smart Contract Introduction](/develop/smart-contracts/) +- [How to work with wallet smart contracts](/develop/smart-contracts/tutorials/wallet) +- [Using toncli](/develop/smart-contracts/sdk/toncli) +- [SDKs](/develop/dapps/apis/sdk) From a2c6910a1e8210762177511ca075ff9d0020762d Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:02 +0800 Subject: [PATCH 145/219] New translations toncli.md (Chinese Simplified) --- .../develop/smart-contracts/sdk/toncli.md | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/sdk/toncli.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/sdk/toncli.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/sdk/toncli.md new file mode 100644 index 0000000000..ed0bd7afa2 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/sdk/toncli.md @@ -0,0 +1,125 @@ +# Using toncli + +_toncli—The Open Network cross-platform smart contract command line interface._ + +Easy to deploy and interact with TON smart contracts. + +Good solution for **Python** stack developers. + +- [GitHub repository](https://github.com/disintar/toncli) + +## Quick Start 📌 + +Here are tutorials made using a toncli library: + +- [Quick start guide](https://github.com/disintar/toncli/blob/master/docs/quick_start_guide.md)—simple steps for deploying an example smart contract to TON. +- [TON Learn: FunC journey overview. Part 1](https://blog.ton.org/func-journey) +- [TON Learn: FunC journey overview. Part 2](https://blog.ton.org/func-journey-2) +- [TON Learn: FunC journey overview. Part 3](https://blog.ton.org/func-journey-3) +- [TON Learn: 10 zero-to-hero lessons](https://github.com/romanovichim/TonFunClessons_Eng) ([Ru version](https://github.com/romanovichim/TonFunClessons_ru)) + +## Installation 💾 + +### Docker: linux / macOS (m1 supported) + +- Docker-hub pre-build images can be founded [here](https://hub.docker.com/r/trinketer22/func_docker/) +- Docker files with instructions can be founded [here](https://github.com/Trinketer22/func_docker) + +### Linux / macOS (intel) + +1. Download the necessary special pre-builds (use the latest build) + +- for Linux: [here](https://github.com/SpyCheese/ton/actions/workflows/ubuntu-compile.yml?query=branch%3Atoncli-local++) +- for Mac: [here](https://github.com/SpyCheese/ton/actions/workflows/macos-10.15-compile.yml?query=branch%3Atoncli-local) + +:::info Download special pre-builds tip +To download the necessary files, you must log-in to your account +::: + +2. Install [Python3.9](https://www.python.org/downloads/) or higher + +3. Run in terminal `pip install toncli` or `pip3 install toncli` + +:::tip Possible error +If you see `WARNING: The script toncli is installed in '/Python/3.9/bin' which is not on PATH`, then add the full path to bin to the PATH env +::: + +4. Run `toncli` and pass absolute path to `func/fift/lite-client` from first step + +### Windows + +1. Download the necessary special pre-builds from [here](https://github.com/SpyCheese/ton/actions/workflows/win-2019-compile.yml?query=branch%3Atoncli-local) (use the latest build) + +:::info Download special pre-builds tip +To download the necessary files, you must log-in to your account +::: + +2. Install [Python3.9](https://www.python.org/downloads/) or higher + +:::info Very important! +During installation, on the first screen, you need to click the `Add Python to PATH` checkbox +::: + +3. Open the terminal as an administrator and `pip install toncli` by installing `toncli` + +4. Unzip the downloaded archive and add [libcrypto-1_1-x64.dll](https://disk.yandex.ru/d/BJk7WPwr_JT0fw) to unziped files + +5. Open the folder in the console for windows users: + +**Windows 11**: + +- Right mouse button, open in the terminal + +**Windows 10**: + +- Copy the path in Explorer and run in Terminal `cd FULL PATH` + +## Create a project ✏️ + +These are simple steps to deploy an example smart contract in TON. +You can read the official documentation [here](https://github.com/disintar/toncli/blob/master/docs/quick_start_guide.md) + +### Step-by-step guide + +1. Open the terminal as an administrator and go to your project folder + +2. To create a project, run `toncli start YOUR-PROJECT-NAME` + +3. Go to the project folder `cd YOUR-PROJECT-NAME` + +:::info Result + +- build +- func +- fift +- test + ::: + +4. You can deploy it to testnet or mainnet: `toncli deploy -n testnet` + +## Examples + +Contributors have prepared sample projects very well covered by the new tests. For example, now two commands can be used to deploy an NFT collection or a Jetton. + +```bash +toncli start nft_colletion/jetton_minter/nft_item/jetton_wallet +``` + +All of these projects have a lot of interesting examples of toncli and blockchain interaction, as well as extremely tests that will help in developing custom smart contracts. + +## To test smart contracts using toncli, go to [testing](/develop/smart-contracts/testing/toncli) + +## Useful articles + +Other useful articles about using toncli in development: + +1. [All cli commands](https://github.com/disintar/toncli/blob/master/docs/advanced/commands.md) +2. [Run get-methods](https://github.com/disintar/toncli/blob/master/docs/advanced/get_methods.md) +3. [Multiple contracts](https://github.com/disintar/toncli/blob/master/docs/advanced/multiple_contracts.md) +4. [Send boc with fift](https://github.com/disintar/toncli/blob/master/docs/advanced/send_boc_with_fift.md) +5. [Project structure](https://github.com/disintar/toncli/blob/master/docs/advanced/project_structure.md) +6. [Interesting features](https://github.com/disintar/toncli/blob/master/docs/advanced/intresting_features.md) +7. [Send internal fift messages](https://github.com/disintar/toncli/blob/master/docs/advanced/send_fift_internal.md) +8. [How does the FunC test work?](https://github.com/disintar/toncli/blob/master/docs/advanced/func_tests_new.md) +9. [How to debug transactions with toncli?](https://github.com/disintar/toncli/blob/master/docs/advanced/transaction_debug.md) +10. [Dockerfile for FunC testing GitHub repository](https://github.com/Trinketer22/func_docker) From 9cdb97a6744f5ef9b782bf176c5764d9839adeb1 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:03 +0800 Subject: [PATCH 146/219] New translations tonstarter.md (Chinese Simplified) --- .../develop/smart-contracts/sdk/tonstarter.md | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/sdk/tonstarter.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/sdk/tonstarter.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/sdk/tonstarter.md new file mode 100644 index 0000000000..1350f56db9 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/sdk/tonstarter.md @@ -0,0 +1,41 @@ +# Using TypeScript + +## Blueprint + +Testing toolkit (usually, sandbox) already included to TypeScript SDK named Blueprint. + +- [Read more about Blueprint](develop/smart-contracts/sdk/javascript) + +Run tests in one line using: + +```bash npm2yarn +npm test +``` + +## Low-level libraries + +### sandbox + +This package allows you to emulate arbitrary TON smart contracts, send messages to them and run get methods on them as if they were deployed on a real network. + +The key difference of this package from ton-contract-executor is the fact that the latter only emulates the compute phase of the contract - it does not know about any other phases and thus does not know anything about fees and balances (in a sense that it does not know whether a contract's balance will be enough to process all the out messages that it produces). + +On the other hand, this package emulates all the phases of a contract, and as a result, the emulation is much closer to what would happen in a real network. + +- https://github.com/ton-community/sandbox + +### ton-contract-executor + +:::info deprecated +This library is deprecated. TON Community not developing it anymore. +::: + +This library allows you to run TON Virtual Machine locally and execute contract. That allows you to write & debug & fully test your contracts before launching them to the network. + +- https://github.com/ton-community/ton-contract-executor + +## Tutorials + +Read this article first to understand all approaches to testing on TON: + +- [TON Hello World part 4: Step by step guide for testing your first smart contract](https://ton-community.github.io/tutorials/04-testing/) From ab94e436a08bd3910b22e6a4995e22002d937c5c Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:04 +0800 Subject: [PATCH 147/219] New translations readme.mdx (Chinese Simplified) --- .../smart-contracts/security/README.mdx | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/security/README.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/security/README.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/security/README.mdx new file mode 100644 index 0000000000..815a21178f --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/security/README.mdx @@ -0,0 +1,22 @@ +import Button from '@site/src/components/button' + +# Overview + +:::info +This article needs an update. Please, help us to improve it. +::: + +**This page contains recommendations that can help to secure your smart contract.** + +If you are creating a smart contract, then here you can see some examples of what errors can lead you to losing funds: + +- [TON Hack Challenge #1](https://github.com/ton-blockchain/hack-challenge-1) + - [Drawing conclusions from TON Hack Challenge](/develop/smart-contracts/security/ton-hack-challenge-1) + +## TON Course: Security + +The [TON Blockchain Course](https://stepik.org/course/176754/) is a comprehensive guide to TON Blockchain development. + +Module 8 completely covers the security of smart contracts on TON Blockchain. + + From f7daad1611413ca18f58fa0221cc83f1eb6e46c0 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:05 +0800 Subject: [PATCH 148/219] New translations random.md (Chinese Simplified) --- .../smart-contracts/security/random.md | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/security/random.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/security/random.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/security/random.md new file mode 100644 index 0000000000..5cffcd4699 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/security/random.md @@ -0,0 +1,76 @@ +# Generation of block random seed + +:::caution +This information is up-to-date at the moment of writing. It may change at any network upgrade. +::: + +Once in a while, a lottery contract is created on TON. Usually it uses an unsafe way to handle randomness, so generated values can be predicted by user and lottery can be drained. + +But exploiting weaknesses in random numbers generation often involves using a proxy contract which forwards message if random value is correct. There exist proposals for wallet contracts which will be able to execute arbitrary code (specified and signed by user, of course) onchain, but most popular wallet versions don't support doing this. So, if lottery is checking whether gambler is participating through a wallet contract, is it safe? + +Or, this question can be written as follows. Can an external message be included in block where random value is exactly as needed by sender? + +Of course, sender doesn't affect randomness in any way. But validators generating blocks and including proposed external messages do. + +## How validators affect seed + +There is not much information about this even in whitepapers so most developers get confused. Here's the only mention of block random in [TON Whitepaper](https://docs.ton.org/ton.pdf): + +> The algorithm used to select validator task groups for each shard (w, s) is deterministic pseudorandom. **It uses pseudorandom numbers embedded by validators into each masterchain block (generated by a consensus using threshold signatures) to create a random seed**, and then computes for example Hash(code(w). code(s).validator_id.rand_seed) for each validator. + +However, the only thing that is guaranteed to be truthful and up-to-date is code. So let's look at [collator.cpp](https://github.com/ton-blockchain/ton/blob/f59c363ab942a5ddcacd670c97c6fbd023007799/validator/impl/collator.cpp#L1590): + +```cpp + { + // generate rand seed + prng::rand_gen().strong_rand_bytes(rand_seed->data(), 32); + LOG(DEBUG) << "block random seed set to " << rand_seed->to_hex(); + } +``` + +This is code that generates random seed for block. It's located in collator code because it's needed by party generating blocks (and is not required for lite validators). + +So, as we can see, seed is generated with block by single validator or collator. The next question is: + +## Can decision on including external message be made after seed is known? + +Yes, it can. The proof is as follows: if external message is imported, its execution must be successful. Execution can be dependent on random values so block seed is guaranteed to known beforehand. + +So, there **is** way to hack "unsafe" (let's call it single-block, because it doesn't use any information from blocks after sending message) random if sender can cooperate with validator. Even if `randomize_lt()` is used. The validator can either generate seed that is suitable for sender or include proposed external message in block that will satisfy all conditions. Validator doing so will still be considered fair. This is the essence of decentralization. + +And, to make this article cover randomness fully, here's one more question. + +## How does block seed affect random in contracts? + +Seed generated by validator isn't used directly in all contracts. Instead, it's [hashed with account address](https://github.com/ton-blockchain/ton/blob/f59c363ab942a5ddcacd670c97c6fbd023007799/crypto/block/transaction.cpp#L876). + +```cpp +bool Transaction::prepare_rand_seed(td::BitArray<256>& rand_seed, const ComputePhaseConfig& cfg) const { + // we might use SHA256(block_rand_seed . addr . trans_lt) + // instead, we use SHA256(block_rand_seed . addr) + // if the smart contract wants to randomize further, it can use RANDOMIZE instruction + td::BitArray<256 + 256> data; + data.bits().copy_from(cfg.block_rand_seed.cbits(), 256); + (data.bits() + 256).copy_from(account.addr_rewrite.cbits(), 256); + rand_seed.clear(); + data.compute_sha256(rand_seed); + return true; +} +``` + +Then pseudorandom numbers are generated with procedure described on [TVM instructions](/learn/tvm-instructions/instructions#112-pseudo-random-number-generator-primitives) page: + +> **x{F810} RANDU256**\ +> 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. + +We can confirm this by looking into code of [preparation of contract's c7](https://github.com/ton-blockchain/ton/blob/master/crypto/block/transaction.cpp#L903) (c7 is tuple for temporary data, that stores contract address, start balance, random seed, etc) and [generation of random values themselves](https://github.com/ton-blockchain/ton/blob/master/crypto/vm/tonops.cpp#L217-L268). + +## Conclusion + +No random in TON is completely safe in sense of unpredictability. This means **no perfect lottery can exist here**, nor can any lottery be believed in to be fair. + +Typical usage of PRNG may possibly include `randomize_lt()`, but it is possible to trick such a contract by choosing correct blocks to send messages to it. The proposed solution to that is sending messages to other workchain, receiving answer, thus skipping blocks, etc... but it only puts off the threat. In fact, any validator (that is, 1/250 of TON Blockchain) can choose correct time for sending a request to lottery contract so that answer from other workchain arrives in block generated by him, then he is free to choose any block seed he wishes. The danger will increase once collators appear in mainnet, as they can't ever be fined by standard complaints because they don't put any stake into Elector contract. + + + + From b983718ea78eb2a691886a049b26c1db8acf0b6f Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:06 +0800 Subject: [PATCH 149/219] New translations ton-hack-challenge-1.md (Chinese Simplified) --- .../security/ton-hack-challenge-1.md | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/security/ton-hack-challenge-1.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/security/ton-hack-challenge-1.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/security/ton-hack-challenge-1.md new file mode 100644 index 0000000000..a4c1714290 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/security/ton-hack-challenge-1.md @@ -0,0 +1,161 @@ +# Drawing conclusions from TON Hack Challenge + +The TON Hack Challenge was held on October 23. +There were several smart contracts deployed to the TON mainnet with synthetic security breaches. Every contract had a balance of 3000 or 5000 TON, allowing participant to hack it and get rewards immediately. + +Source code and contest rules were hosted on GitHub [here](https://github.com/ton-blockchain/hack-challenge-1). + +## Contracts + +### 1. Mutual fund + +:::note SECURITY RULE +Always check functions for [`impure`](/develop/func/functions#impure-specifier) modifier. +::: + +The first task was very simple. The attacker could find that `authorize` function was not `impure`. The absence of this modifier allows a compiler to skip calls to that function if it returns nothing or the return value is unused. + +```func +() authorize (sender) inline { + throw_unless(187, equal_slice_bits(sender, addr1) | equal_slice_bits(sender, addr2)); +} +``` + +### 2. Bank + +:::note SECURITY RULE +Always check for [modifying/non-modifying](/develop/func/statements#methods-calls) methods. +::: + +`udict_delete_get?` was called with `.` instead `~`, so the real dict was untouched. + +```func +(_, slice old_balance_slice, int found?) = accounts.udict_delete_get?(256, sender); +``` + +### 3. DAO + +:::note SECURITY RULE +Use signed integers if you really need it. +::: + +Voting power was stored in message as an integer. So the attacker could send a negative value during power transfer and get infinite voting power. + +```func +(cell,()) transfer_voting_power (cell votes, slice from, slice to, int amount) impure { + int from_votes = get_voting_power(votes, from); + int to_votes = get_voting_power(votes, to); + + from_votes -= amount; + to_votes += amount; + + ;; No need to check that result from_votes is positive: set_voting_power will throw for negative votes + ;; throw_unless(998, from_votes > 0); + + votes~set_voting_power(from, from_votes); + votes~set_voting_power(to, to_votes); + return (votes,()); +} +``` + +### 4. Lottery + +:::note SECURITY RULE +Always randomize seed before doing [`rand()`](/develop/func/stdlib#rand) +::: + +Seed was brought from logical time of the transaction, and a hacker can win by bruteforcing the logical time in the current block (cause lt is sequential in the borders of one block). + +```func +int seed = cur_lt(); +int seed_size = min(in_msg_body.slice_bits(), 128); + +if(in_msg_body.slice_bits() > 0) { + seed += in_msg_body~load_uint(seed_size); +} +set_seed(seed); +var balance = get_balance().pair_first(); +if(balance > 5000 * 1000000000) { + ;; forbid too large jackpot + raw_reserve( balance - 5000 * 1000000000, 0); +} +if(rand(10000) == 7777) { ...send reward... } +``` + +### 5. Wallet + +:::note SECURITY RULE +Remember that everything is stored in the blockchain. +::: + +The wallet was protected with password, it's hash was stored in contract data. However, the blockchain remembers everything—the password was in the transaction history. + +### 6. Vault + +:::note SECURITY RULE +Always check for [bounced](/develop/smart-contracts/guidelines/non-bouncable-messages) messages. +Don't forget about errors caused by [standard](/develop/func/stdlib/) functions. +Make your conditions as strict as possible. +::: + +The vault has the following code in the database message handler: + +```func +int mode = null(); +if (op == op_not_winner) { + mode = 64; ;; Refund remaining check-TONs + ;; addr_hash corresponds to check requester +} else { + mode = 128; ;; Award the prize + ;; addr_hash corresponds to the withdrawal address from the winning entry +} +``` + +Vault does not have a bounce handler or proxy message to the database if the user sends “check”. In the database we can set `msg_addr_none` as an award address because `load_msg_address` allows it. We are requesting a check from the vault, database tries to parse `msg_addr_none` using [`parse_std_addr`](/develop/func/stdlib#parse_std_addr), and fails. Message bounces to the vault from the database and op is not `op_not_winner`. + +### 7. Better bank + +:::note SECURITY RULE +Never destroy account for fun. +Make [`raw_reserve`](/develop/func/stdlib#raw_reserve) instead of sending money to yourself. +Think about possible race conditions. +Be careful with hashmap gas consumption. +::: + +There were race conditions in the contract: you could deposit money, then try to withdraw it twice in concurrent messages. There is no guarantee that a message with reserved money will be processed, so the bank can shut down after a second withdrawal. After that, the contract could be redeployed and anybody could withdraw unclaimed money. + +### 8. Dehasher + +:::note SECURITY RULE +Avoid executing third-party code in your contract. +::: + +```func +slice try_execute(int image, (int -> slice) dehasher) asm "<{ TRY:<{ EXECUTE DEPTH 2 THROWIFNOT }>CATCH<{ 2DROP NULL }> }>CONT" "2 1 CALLXARGS"; + +slice safe_execute(int image, (int -> slice) dehasher) inline { + cell c4 = get_data(); + + slice preimage = try_execute(image, dehasher); + + ;; restore c4 if dehasher spoiled it + set_data(c4); + ;; clean actions if dehasher spoiled them + set_c5(begin_cell().end_cell()); + + return preimage; +} +``` + +There is no way to safe execute a third-party code in the contract, because [`out of gas`](/learn/tvm-instructions/tvm-exit-codes#standard-exit-codes) exception cannot be handled by `CATCH`. The attacker simply can [`COMMIT`](/learn/tvm-instructions/instructions#11-application-specific-primitives) any state of contract and raise `out of gas`. + +## Conclusion + +Hope this article has shed some light on the non-obvious rules for FunC developers. + +## References + +Originally written by Dan Volkov + +- [dvlkv on GitHub](https://github.com/dvlkv) +- [Original article](https://dev.to/dvlkv/drawing-conclusions-from-ton-hack-challenge-1aep) From a77b46183c533362afa24a119564ae31083ea830 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:08 +0800 Subject: [PATCH 150/219] New translations overview.mdx (Chinese Simplified) --- .../smart-contracts/testing/overview.mdx | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/testing/overview.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/testing/overview.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/testing/overview.mdx new file mode 100644 index 0000000000..2e16e00bab --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/testing/overview.mdx @@ -0,0 +1,141 @@ +# Writing Tests with Blueprint + +## Overview + +Test toolkit (usually sandbox) is already included in the TypeScript SDK named [Blueprint](/develop/smart-contracts/sdk/javascript). You can create a demo project and launch the default test with two steps: + +1. Create a new Blueprint project: + +```bash +npm create ton@latest MyProject +``` + +2. Run a test: + +```bash +cd MyProject +npx blueprint test +``` + +As a result you'll see the corresponding output in the terminal window: + +```bash +% npx blueprint test + +> MyProject@0.0.1 test +> jest + + PASS tests/Main.spec.ts + Main + ✓ should deploy (127 ms) + +Test Suites: 1 passed, 1 total +Tests: 1 passed, 1 total +Snapshots: 0 total +Time: 1.224 s, estimated 2 s +Ran all test suites. +``` + +## Basic Usage + +Testing of smart contracts allows for the coverage of security, optimization of gas spending, and examination of edge cases. +Writing tests in Blueprint (based on [Sandbox](https://github.com/ton-org/sandbox)) works through defining arbitrary actions with the contract and comparing test results with the expected result, for example: + +```typescript +it('should execute with success', async () => { // description of the test case + const res = await main.sendMessage(sender.getSender(), toNano('0.05')); // performing an action with contract main and saving result in res + + expect(res.transactions).toHaveTransaction({ // configure the expected result with expect() function + from: main.address, // set expected sender for transaction we want to test matcher properties from + success: true // set the desirable result using matcher property success + }); + + printTransactionFees(res.transactions); // print table with details on spent fees +}); +``` + +### Writing Tests for Complex Assertion + +The basic workflow of creating a test is: + +1. Create a specific wrapped `Contract` entity using `blockchain.openContract()`. +2. Describe the actions your `Contract` should perform and save the execution result in `res` variable. +3. Verify the properties using the `expect()` function and the matcher `toHaveTransaction()`. + +The `toHaveTransaction` matcher expects an object with any combination of fields from the `FlatTransaction` type defined with the following properties + +| Name | Type | Description | +| -------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| from | Address? | Contract address of the message sender | +| on | Address | Contract address of the message destination (Alternative name of the property `to`). | +| value | bigint? | Amount of Toncoins in the message in nanotons | +| body | Cell | Message body defined as a Cell | +| op | number? | Op code is the operation identifier number (crc32 from TL-B usually). Expected in the first 32 bits of a message body. | +| success | boolean? | Custom Sandbox flag that defines the resulting status of a certain transaction. True - if both the compute and the action phase succeeded. Otherwise - False. | + +You can omit the fields that you're not interested in and pass functions accepting the types returning booleans (`true` meaning good) to check for example number ranges, message opcodes, etc. Please note that if a field is optional (like `from?: Address`), then the function needs to accept the optional type, too. + +:::tip +You can learn the whole list of matcher fields from the [Sandbox documentation](https://github.com/ton-org/sandbox#test-a-transaction-with-matcher). +::: + +### Specific Test Suite + +#### Extract SendMode + +To extract the send mode of a sent message you can use the following code: + +```ts + +const smc = await blockchain.getContract(addr); + +const re = blockchain.executor.runTransaction({ + config: blockchain.configBase64, libs: null, verbosity: 'full', + now: Math. floor (Date.now) / 1000), + lt: BigInt(Date.now()), + randomSeed: null, + ignoreChksig: false, + debugEnabled: true, + shardAccount: beginCell() + .store (storeShardAccount (smc.account)) + .endCell() + .toBoc() + .toString('base64'), + message: beginCell() + .store (storeMessageRelaxed (...)) + .endCell(), +}); + +if (!re.result. success || !re.result.actions) { + throw new Error('fail'); +} +const actions = loadoutList(Cell.fromBase64(re.result.actions).beginParse()); +actions[0].type === 'sendMsg' && actions[0].mode; + +``` + +## Tutorials + +Learn more about testing from the most valuable community tutorials on TON: + +- [Lesson 2: Testing FunC for a Smart Contract](https://github.com/romanovichim/TonFunClessons_Eng/blob/main/lessons/smartcontract/2lesson/secondlesson.md) +- [TON Hello World part 4: Step by step guide for testing your first smart contract](https://ton-community.github.io/tutorials/04-testing/) +- [TON Smart Contract Pipeline](https://dev.to/roma_i_m/ton-smart-contract-pipeline-write-simple-contract-and-compile-it-4pnh) +- [\[YouTube\]Sixth lesson FunC & Blueprint. Gas, fees, tests.](https://youtu.be/3XIpKZ6wNcg) + +## Examples + +Check test suites used for TON Ecosystem contracts and learn by examples. + +- [liquid-staking-contract sandbox tests](https://github.com/ton-blockchain/liquid-staking-contract/tree/main/tests) +- [governance_tests](https://github.com/Trinketer22/governance_tests/blob/master/config_tests/tests/) +- [JettonWallet.spec.ts](https://github.com/EmelyanenkoK/modern_jetton/blob/master/tests/JettonWallet.spec.ts) +- [governance_tests](https://github.com/Trinketer22/governance_tests/blob/master/elector_tests/tests/complaint-test.fc) +- [MassSender.spec.ts](https://github.com/Gusarich/ton-mass-sender/blob/main/tests/MassSender.spec.ts) +- [TonForwarder.spec.ts](https://github.com/TrueCarry/ton-contract-forwarder/blob/main/src/contracts/ton-forwarder/TonForwarder.spec.ts) +- [Assurer.spec.ts](https://github.com/aSpite/dominant-assurance-contract/blob/main/tests/Assurer.spec.ts) + +## See Also + +- [Blueprint](/develop/smart-contracts/sdk/javascript) +- [toncli](/develop/smart-contracts/testing/toncli) From 314cbe163f72448f8a01e4c84ef59f8c227e14b8 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:09 +0800 Subject: [PATCH 151/219] New translations toncli.md (Chinese Simplified) --- .../develop/smart-contracts/testing/toncli.md | 180 ++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/testing/toncli.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/testing/toncli.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/testing/toncli.md new file mode 100644 index 0000000000..f62370f3c8 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/testing/toncli.md @@ -0,0 +1,180 @@ +# Using toncli + +:::tip starter tip +If you didn't use toncli before, try [QUICK START GUIDE](https://github.com/disintar/toncli/blob/master/docs/quick_start_guide.md). +::: + +## Testing using toncli + +`toncli` uses FunC to test smart contracts. Also, the latest version supports Docker for quick environment setup. + +This tutorial will help you test the functions of our smart contract to make sure that the project is working correctly. + +The best tutorial describing testing using toncli is: + +- [How does the FunC test work?](https://github.com/disintar/toncli/blob/master/docs/advanced/func_tests_new.md) +- [How to debug transactions with toncli?](https://github.com/disintar/toncli/blob/master/docs/advanced/transaction_debug.md) + +## FunC test structure under toncli + +To test our smart contract, we will have to write 2 functions. One of which takes values, contains the desired result, gives an error if the function under test does not work correctly. + +### Let's create a file with tests + +Create in the `tests` folder, the file `example.func` in which we will write our tests. + +### Data function + +As a rule, the testing function does not accept arguments, but must return them. + +- **function selector** - id of the called function in the tested contract; +- **tuple** - (stack) values ​​that we will pass to the function that performs tests; +- **c4 cell** - "permanent data" in the control register c4; +- **c7 tuple** - "temporary data" in the control register c7; +- **gas limit integer** - gas limit (to understand the concept of gas, I advise you to first read about it in Ethereum); + +:::info About Gas + +And you can read in detail [here](https://ton-blockchain.github.io/docs/#/smart-contracts/fees). Well, in full detail here in [Appendix A](https://ton-blockchain.github.io/docs/tvm.pdf). + +More about registers c4 and c7 [here](https://ton-blockchain.github.io/docs/tvm.pdf) in 1.3.1 +::: + +## Let's start writing tests for our smart contract + +### Introduction + +In the new tests, testing occurs through two functions that allow you to call smart contract methods: + +- `invoke_method`, which assumes no exception will be thrown +- `invoke_method_expect_fail`, which assumes an exception will be thrown + +These are the functions inside the testing function, which can return any number of values, all of them will be displayed when the tests are run in the report. + +:::info Important! +It is worth noting that each test function name must begin with `_test`. +::: + +### Creating a test function + +Let's call our test function `__test_example()`, it will return the amount of gas consumed, so it will be `int`. + +```js +int __test_example() { + +} +``` + +### Register update c4 + +Since we will need to frequently update the `c4` register due to a large number of tests, so we will create a helper function that will write `c4` to zero + +```js +() set_default_initial_data() impure { + set_data(begin_cell().store_uint(0, 64).end_cell()); +} +``` + +- `begin_cell()` - will create a Builder for the future cell +- `store_uint()` - writes the value of total +- `end_cell()`- create Cell +- `set_data()` - writes the cell to register c4 + +`impure` is a keyword that indicates that the function changes the smart contract data. + +We got a function that we will use in the body of our testing function + +**Result:** + +```js +int __test_example() { + set_default_initial_data(); + +} +``` + +### Test Methods + +It is worth noting that in the new version of the tests, we have the ability to call several smart contract methods in the testing function. + +Inside our test, we will call the `recv_internal()` method and the Get method, so we will increment the value in c4 with the message and immediately check that the value has changed to the one sent. + +To call the `recv_internal()` method, we need to create a cell with a message. + +```js +int __test_example() { + set_default_initial_data(); + cell message = begin_cell().store_uint(10, 32).end_cell(); +} +``` + +After this step, we are going to use the `invoke_method` method. + +This method takes two arguments: + +- method name +- arguments to test as a `tuple` + +Two values ​​are returned: the gas used and the values ​​returned by the method (as a tuple). + +:::info It's worth noting +Two values ​​are returned: the gas used and the values ​​returned by the method (as a tuple). +::: + +In the first call, the arguments will be `recv_internal` and a tuple with a message transformed into a slice using `begin_parse()` + +```js +var (int gas_used1, _) = invoke_method(recv_internal, [message.begin_parse()]); +``` + +For the record, let's store the amount of gas used in int `gas_used1`. + +In the second call, the arguments will be the Get method `get_total()` and an empty tuple. + +```js +var (int gas_used2, stack) = invoke_method(get_total, []); +``` + +For the report, we also store the amount of gas used in `int gas_used2`, plus the values ​​that the method returns, to check later that everything worked correctly. + +We get: + +```js +int __test_example() { + set_default_initial_data(); + + cell message = begin_cell().store_uint(10, 32).end_cell(); + var (int gas_used1, _) = invoke_method(recv_internal, [message.begin_parse()]); + var (int gas_used2, stack) = invoke_method(get_total, []); + +} +``` + +Now, finally, the most important step. We have to check if our smart contract is working properly. + +That is, if it returns the **correct result**, we return `success` or `failure` and the gas used. + +```js +[int total] = stack; +throw_if(101, total != 10); +``` + +**Explanations:** + +- Passing a tuple +- In the first argument, the number of the error (101), which we will receive if the smart contract does not work correctly +- In the second argument is the correct answer + +```js +int __test_example() { + set_data(begin_cell().store_uint(0, 64).end_cell()); + cell message = begin_cell().store_uint(10, 32).end_cell(); + var (int gas_used1, _) = invoke_method(recv_internal, [message.begin_parse()]); + var (int gas_used2, stack) = invoke_method(get_total, []); + [int total] = stack; + throw_if(101, total != 10); + return gas_used1 + gas_used2; +} +``` + +This is the whole test, very convenient. From 13ea42310954c3d27ffad420d0e36bc1206f62ff Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:10 +0800 Subject: [PATCH 152/219] New translations tonstarter.md (Chinese Simplified) --- .../smart-contracts/testing/tonstarter.md | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/testing/tonstarter.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/testing/tonstarter.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/testing/tonstarter.md new file mode 100644 index 0000000000..098d0d76f2 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/testing/tonstarter.md @@ -0,0 +1,41 @@ +# Using TypeScript (deprecated) + +## Blueprint + +Testing toolkit (usually, sandbox) already included to TypeScript SDK named Blueprint. + +- [Read more about Blueprint](develop/smart-contracts/sdk/javascript) + +Run tests in one line using: + +```bash npm2yarn +npm test +``` + +## Low-level libraries + +### sandbox + +This package allows you to emulate arbitrary TON smart contracts, send messages to them and run get methods on them as if they were deployed on a real network. + +The key difference of this package from ton-contract-executor is the fact that the latter only emulates the compute phase of the contract - it does not know about any other phases and thus does not know anything about fees and balances (in a sense that it does not know whether a contract's balance will be enough to process all the out messages that it produces). + +On the other hand, this package emulates all the phases of a contract, and as a result, the emulation is much closer to what would happen in a real network. + +- https://github.com/ton-community/sandbox + +### ton-contract-executor + +:::info deprecated +This library is deprecated. TON Community not developing it anymore. +::: + +This library allows you to run TON Virtual Machine locally and execute contract. That allows you to write & debug & fully test your contracts before launching them to the network. + +- https://github.com/ton-community/ton-contract-executor + +## Tutorials + +Read this article first to understand all approaches to testing on TON: + +- [TON Hello World part 4: Step by step guide for testing your first smart contract](https://ton-community.github.io/tutorials/04-testing/) From 7d0397748dff06a34e812c336e148c6b42fac4a4 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:11 +0800 Subject: [PATCH 153/219] New translations writing-test-examples.mdx (Chinese Simplified) --- .../testing/writing-test-examples.mdx | 573 ++++++++++++++++++ 1 file changed, 573 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/testing/writing-test-examples.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/testing/writing-test-examples.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/testing/writing-test-examples.mdx new file mode 100644 index 0000000000..32c32732ea --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/testing/writing-test-examples.mdx @@ -0,0 +1,573 @@ +# Writing Tests Examples + +This page demonstrates how to write test for FunC contracts created in with [Blueprint SDK](https://github.com/ton-org/blueprint) ([Sandbox](https://github.com/ton-org/sandbox)). +Test suites built for demo contract [fireworks](https://github.com/ton-community/fireworks-func). The fireworks is a smart contract which initially run via `set_first` message. + +Once a new FunC project is created via `npm create ton@latest`, a test file `tests/contract.spec.ts` will be autogenerated in the project directory for testing the contract: + +```typescript +import ... + +describe('Fireworks', () => { +... + + + expect(deployResult.transactions).toHaveTransaction({ +... + }); + +}); + +it('should deploy', async () => { + // the check is done inside beforeEach + // blockchain and fireworks are ready to use +}); +``` + +Running tests using the following command: + +```bash +npx blueprint test +``` + +Additional options and vmLogs may be specified with `blockchain.verbosity`: + +```typescript +blockchain.verbosity = { + ...blockchain.verbosity, + blockchainLogs: true, + vmLogs: 'vm_logs_full', + debugLogs: true, + print: false, +} +``` + +## Direct Unit Tests + +Fireworks demonstrate different operating with sending messages in the TON Blockchain. + +![](/img/docs/writing-test-examples/test-examples-schemes.svg) + +Once you deploy this with message `set_first` with enough TON amount, it will be automatically executed with primary and usable combinations of send modes. + +Fireworks redeployed itself, as result it will be created 3 entities of Fireworks entities, while each of entity has own ID(keep it in storage) and, as a result, different Smart Contract Address. + +For clearness define different by ID Fireworks instances (different `state_init`) with the following names: + +- 1 - Fireworks setter - The entity that spread different launch op codes. Could be extended up to four different opcodes. +- 2 - Fireworks launcher-1 - The Fireworks instance, which launch first fireworks, means messages will be sent to the launcher. +- 3 - Fireworks launcher-2 - The Fireworks instance, which launch second fireworks, means messages will be sent launcher. + +
+ Expand details on transactions + +index - is an ID of a transaction in the `launchResult` array. + +- `0` - External request to the treasury (the Launcher) that resulted with a outbound message `op::set_first` with 2.5 to fireworks +- `1` - The transaction in Fireworks setter contract invoked with `op::set_first` and executed with two outbound messages to the Fireworks Launcher-1 and Fireworks Launcher-2 +- `2` - The transaction in the Fireworks launcher 1 invoked with `op::launch_first`, and executed with four outbound messages to the Launcher. +- `3` - The transaction in the Fireworks launcher 2 invoked with `op::launch_second`, and executed with a outbound message to the Launcher. +- `4` - Transaction in the Launcher with incoming message from the Fireworks launcher 1. This message sent with `send mode = 0`. +- `5` - Transaction in the Launcher with incoming message from the Fireworks launcher 1. This message sent with `send mode = 1` +- `6` - Transaction in the Launcher with incoming message from the Fireworks launcher 1. This message sent with `send mode = 2` +- `7` - Transaction in the Launcher with incoming message from the Fireworks launcher 1. This message sent with `send mode = 128 + 32` +- `8` - Transaction in the Launcher with incoming message from the Fireworks launcher 2. This message sent with `send mode = 64` + +
+ +Each 'firework' - is outbound message with a unique message body appears in transactions with ID:3 and ID:4. + +Bellow the list of test for each transaction expected successfully executed. Transaction[ID:0] External request to the treasury (the Launcher) that resulted with a outbound message `op::set_first` with 2.5 to fireworks. In case you will deploy Fireworks to the blockchain launcher is your wallet. + +### Transaction ID:1 Success Test + +[This test](https://github.com/ton-community/fireworks-func/blob/main/tests/Fireworks.spec.ts#L75) checks if the fireworks are successfully set by sending a transaction with a value of 2.5 TON. +This is the simplest case, the main purpose here to assert result of transaction success property to true. + +To filter certain transaction from the `launhcResult.transactions` array, we can use the most convince fields. +With +`from` (contract sender address), `to` (contract destination address), `op` (Op code value) - we will retrieve only one transaction for this combination. + +![](/img/docs/writing-test-examples/test-examples-schemes_id1.svg) + +The transaction[ID:1] in Fireworks Setter contract invoked with `op::set_first` and executed with two outbound messages to the Fireworks launcher-1 and Fireworks launcher-2 + +```typescript + + it('first transaction[ID:1] should set fireworks successfully', async () => { + + const launcher = await blockchain.treasury('launcher'); + + const launchResult = await fireworks.sendDeployLaunch(launcher.getSender(), toNano('2.5')); + + + expect(launchResult.transactions).toHaveTransaction({ + from: launcher.address, + to: fireworks.address, + success: true, + op: Opcodes.set_first + }) + + }); + +``` + +### Transaction ID:2 Success Test + +[This test](https://github.com/ton-community/fireworks-func/blob/main/tests/Fireworks.spec.ts#L92) checks if the transaction[ID:2] executed successfully. + +![](/img/docs/writing-test-examples/test-examples-schemes_id2.svg) + +The transaction in the Fireworks launcher 1 invoked with `op::launch_first`, and executed with four outbound messages to the Launcher. + +```typescript + it('should exist a transaction[ID:2] which launch first fireworks successfully', async () => { + + const launcher = await blockchain.treasury('launcher'); + + const launchResult = await fireworks.sendDeployLaunch(launcher.getSender(), toNano('2.5')); + + expect(launchResult.transactions).toHaveTransaction({ + from: fireworks.address, + to: launched_f1.address, + success: true, + op: Opcodes.launch_first, + outMessagesCount: 4, + destroyed: true, + endStatus: "non-existing", + }) + + printTransactionFees(launchResult.transactions); + + }); +``` + +In cases, when transaction should affect the state of contract, it is possible to specify this with `destroyed`, `endStatus` fields. + +The full list of Account Status related fields: + +- `destroyed` - `true` - if the existing contract was destroyed due to executing a certain transaction. Otherwise - `false`. +- `deploy` - Custom Sandbox flag that indicates whether the contract was deployed during this transaction. `true` if contract before this transaction was not initialized and after this transaction became initialized. Otherwise - `false`. +- `oldStatus` - AccountStatus before transaction execution. Values: `'uninitialized'`, `'frozen'`, `'active'`, `'non-existing'`. +- `endStatus` - AccountStatus after transaction execution. Values: `'uninitialized'`, `'frozen'`, `'active'`, `'non-existing'`. + +### Transaction ID:3 Success Test + +[This test](https://github.com/ton-community/fireworks-func/blob/main/tests/Fireworks.spec.ts#L113) checks if the transaction[ID:3] executed successfully. + +![](/img/docs/writing-test-examples/test-examples-schemes_id3.svg) + +The transaction[ID:3] carries out in the Fireworks launcher 1, invokes with `op::launch_first`, and executes with four outbound messages to the Launcher. + +```typescript + + it('should exist a transaction[ID:3] which launch second fireworks successfully', async () => { + + const launcher = await blockchain.treasury('launcher'); + + const launchResult = await fireworks.sendDeployLaunch(launcher.getSender(), toNano('2.5')); + + expect(launchResult.transactions).toHaveTransaction({ + from: fireworks.address, + to: launched_f2.address, + success: true, + op: Opcodes.launch_second, + outMessagesCount: 1 + }) + + printTransactionFees(launchResult.transactions); + + }); + + + + +``` + +### Transaction ID:4 Success Test + +[This test](https://github.com/ton-community/fireworks-func/blob/main/tests/Fireworks.spec.ts#L133) checks if the transaction[ID:4] executed successfully. + +![](/img/docs/writing-test-examples/test-examples-schemes_id4.svg) + +Transaction[ID:4] carries out in the Launcher(Deploy Wallet) with incoming message from the Fireworks launcher 1. This message sent with `send mode = 0` in the Transaction[ID:2]. + +```typescript + it('should exist a transaction[ID:4] with a comment send mode = 0', async() => { + + const launcher = await blockchain.treasury('launcher'); + + const launchResult = await fireworks.sendDeployLaunch( + launcher.getSender(), + toNano('2.5'), + ); + + expect(launchResult.transactions).toHaveTransaction({ + from: launched_f1.address, + to: launcher.address, + success: true, + body: beginCell().storeUint(0,32).storeStringTail("send mode = 0").endCell() // 0x00000000 comment opcode and encoded comment + + }); + }) +``` + +### Transaction ID:5 Success Test + +[This test](https://github.com/ton-community/fireworks-func/blob/main/tests/Fireworks.spec.ts#L152) checks if the transaction[ID:5] executed successfully. + +![](/img/docs/writing-test-examples/test-examples-schemes_id5.svg) + +Transaction[ID:5] carries out in the Launcher with incoming message from the Fireworks launcher 1. This message sent with `send mode = 1` + +```typescript + it('should exist a transaction[ID:5] with a comment send mode = 1', async() => { + + const launcher = await blockchain.treasury('launcher'); + + const launchResult = await fireworks.sendDeployLaunch( + launcher.getSender(), + toNano('2.5'), + ); + + expect(launchResult.transactions).toHaveTransaction({ + from: launched_f1.address, + to: launcher.address, + success: true, + body: beginCell().storeUint(0,32).storeStringTail("send mode = 1").endCell() // 0x00000000 comment opcode and encoded comment + }); + + }) + + +``` + +### Transaction ID:6 Success Test + +[This test](https://github.com/ton-community/fireworks-func/blob/main/tests/Fireworks.spec.ts#L170) checks if the transaction[ID:6] executed successfully. + +![](/img/docs/writing-test-examples/test-examples-schemes_id6.svg) + +The transaction[ID:6] carries out in the Launcher with incoming message from the Fireworks launcher 1. This message sent with `send mode = 2` + +```typescript + it('should exist a transaction[ID:6] with a comment send mode = 2', async() => { + + const launcher = await blockchain.treasury('launcher'); + + const launchResult = await fireworks.sendDeployLaunch( + launcher.getSender(), + toNano('2.5'), + ); + + expect(launchResult.transactions).toHaveTransaction({ + from: launched_f1.address, + to: launcher.address, + success: true, + body: beginCell().storeUint(0,32).storeStringTail("send mode = 2").endCell() // 0x00000000 comment opcode and encoded comment + }); + + }) +``` + +### Transaction ID:7 Success Test + +[This test](https://github.com/ton-community/fireworks-func/blob/main/tests/Fireworks.spec.ts#L188) checks if the transaction[ID:7] executed successfully. + +![](/img/docs/writing-test-examples/test-examples-schemes_id7.svg) + +The transaction[ID:7] carries out in the Launcher with incoming message from the Fireworks launcher 1. This message sent with `send mode = 128 + 32` + +```typescript + it('should exist a transaction[ID:7] with a comment send mode = 32 + 128', async() => { + + const launcher = await blockchain.treasury('launcher'); + + const launchResult = await fireworks.sendDeployLaunch( + launcher.getSender(), + toNano('2.5'), + ); + + expect(launchResult.transactions).toHaveTransaction({ + from: launched_f1.address, + to: launcher.address, + success: true, + body: beginCell().storeUint(0,32).storeStringTail("send mode = 32 + 128").endCell() // 0x00000000 comment opcode and encoded comment + }); + }) +``` + +### Transaction ID:8 Success Test + +[This test](https://github.com/ton-community/fireworks-func/blob/main/tests/Fireworks.spec.ts#L188) checks if the transaction[ID:8] executed successfully. + +![](/img/docs/writing-test-examples/test-examples-schemes_id8.svg) + +The transaction[ID:8] carries out in the Launcher with incoming message from the Fireworks launcher 2. This message sent with `send mode = 64` + +```typescript + it('should exist a transaction[ID:8] with a comment send mode = 64', async() => { + + const launcher = await blockchain.treasury('launcher'); + + const launchResult = await fireworks.sendDeployLaunch( + launcher.getSender(), + toNano('2.5'), + ); + + expect(launchResult.transactions).toHaveTransaction({ + from: launched_f2.address, + to: launcher.address, + success: true, + body: beginCell().storeUint(0,32).storeStringTail("send_mode = 64").endCell() // 0x00000000 comment opcode and encoded comment + + }); + + }) + +``` + +## Printing and Reading Transaction Fees + +During the test, reading the details about fees can be useful for optimizing the contract. The printTransactionFees function prints the entire transaction chain in a convenient manner." + +```typescript + + it('should be executed and print fees', async() => { + + const launcher = await blockchain.treasury('launcher'); + + const launchResult = await fireworks.sendDeployLaunch( + launcher.getSender(), + toNano('2.5'), + ); + + console.log(printTransactionFees(launchResult.transactions)); + + }); + +``` + +For instance, in case of `launchResult` the following table will be printed: + +| (index) | op | valueIn | valueOut | totalFees | outActions | +| -------------------------- | ------------ | -------------- | -------------- | -------------- | ---------- | +| 0 | 'N/A' | 'N/A' | '2.5 TON' | '0.010605 TON' | 1 | +| 1 | '0x5720cfeb' | '2.5 TON' | '2.185812 TON' | '0.015836 TON' | 2 | +| 2 | '0x6efe144b' | '1.092906 TON' | '1.081142 TON' | '0.009098 TON' | 4 | +| 3 | '0xa2e2c2dc' | '1.092906 TON' | '1.088638 TON' | '0.003602 TON' | 1 | +| 4 | '0x0' | '0.099 TON' | '0 TON' | '0.000309 TON' | 0 | +| 5 | '0x0' | '0.1 TON' | '0 TON' | '0.000309 TON' | 0 | +| 6 | '0x0' | '0.099 TON' | '0 TON' | '0.000309 TON' | 0 | +| 7 | '0x0' | '0.783142 TON' | '0 TON' | '0.000309 TON' | 0 | +| 8 | '0x0' | '1.088638 TON' | '0 TON' | '0.000309 TON' | 0 | + +![](/img/docs/writing-test-examples/fireworks_trace_tonviewer.png?=RAW) + +index - is an ID of a transaction in the `launchResult` array. + +- `0` - External request to the treasury (the Launcher) that resulted in a message `op::set_first` to Fireworks +- `1` - The Fireworks transaction that resulted in 4 messages to the Launcher +- `2` - Transaction on Launched Fireworks - 1 from the Launcher, message sent with `op::launch_first` op code. +- `2` - Transaction on Launched Fireworks - 2 from the Launcher, message sent with `op::launch_second` op code. +- `4` - Transaction on Launcher with incoming message from the Launched Fireworks - 1, message sent with `send mode = 0` +- `5` - Transaction on Launcher with incoming message from the Launched Fireworks - 1, message sent with `send mode = 1` +- `6` - Transaction on Launcher with incoming message from the Launched Fireworks - 1, message sent with `send mode = 2` +- `7` - Transaction on Launcher with incoming message from the Launched Fireworks - 1, message sent with `send mode = 128 + 32` +- `8` - Transaction on Launcher with incoming message from the Launched Fireworks - 2, message sent with `send mode = 64` + +## Transaction Fees Tests + +This test verifies whether the transaction fees for launching the fireworks are as expected. It is possible to define custom assertions for different parts of commission fees. + +```typescript + + it('should be executed with expected fees', async() => { + + const launcher = await blockchain.treasury('launcher'); + + const launchResult = await fireworks.sendDeployLaunch( + launcher.getSender(), + toNano('2.5'), + ); + + //totalFee + console.log('total fees = ', launchResult.transactions[1].totalFees); + + const tx1 = launchResult.transactions[1]; + if (tx1.description.type !== 'generic') { + throw new Error('Generic transaction expected'); + } + + //computeFee + const computeFee = tx1.description.computePhase.type === 'vm' ? tx1.description.computePhase.gasFees : undefined; + console.log('computeFee = ', computeFee); + + //actionFee + const actionFee = tx1.description.actionPhase?.totalActionFees; + console.log('actionFee = ', actionFee); + + + if ((computeFee == null || undefined) || + (actionFee == null || undefined)) { + throw new Error('undefined fees'); + } + + //The check, if Compute Phase and Action Phase fees exceed 1 TON + expect(computeFee + actionFee).toBeLessThan(toNano('1')); + + + }); + +``` + +## Edge Cases Tests + +In this section will be provided the test cases for TVM exit [codes](/learn/tvm-instructions/tvm-exit-codes) that can occur during transaction processing. These exit codes are in the blockchain code itself. At the same time, it is necessary to distinguish the exit code during the [Compute Phase](/learn/tvm-instructions/tvm-overview#compute-phase) and the exit code during the Action Phase. + +During the Compute Phase, the contract logic (its code) is executed. While processing, various actions can be created. These actions will be processed in the next phase - Action Phase. If the Compute Phase is unsuccessful, then the Action Phase does not start. However, if the Compute Phase was successful, this does not guarantee that the Action Phase will also end successfully. + +### Compute Phase | exit code = 0 + +This exit code means that the Compute Phase of the transaction was completed successfully. + +### Compute Phase | exit code = 1 + +A alternative exit code for denoting the success of the Compute Phase is `1`. To get this exit code, you need to use the [RETALT](https://github.com/ton-community/fireworks-func/blob/ef49b4da12d287a8f6c2b6f0c19d65814c1578fc/contracts/fireworks.fc#L20). + +It should be noted that this opcode should be called in the main function (for example, recv_internal). If you call in another function, then the exit from that function will be `1`, but the total exit code will be `0`. + +### Compute Phase | exit code = 2 + +TVM is a [stack machine](/learn/tvm-instructions/tvm-overview#tvm-is-a-stack-machine). When interacting with different values, they appear on the stack. If suddenly there are no elements on the stack, but some opcode requires them, then this error is thrown. + +This can happen when working with opcodes directly, since [stdlib.fc](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/stdlib.fc) (a library for FunC) assumes that there will be no such problem. + +### Compute Phase | exit code = 3 + +Any code before execution becomes a `continuation`. This is a special data type that contains a slice with code, a stack, registers, and other data necessary for the execution of the code. Such a continuation can be run later if necessary, passing the necessary parameters for the initial state of the stack. + +First, we [build](https://github.com/ton-community/fireworks-func/blob/ef49b4da12d287a8f6c2b6f0c19d65814c1578fc/contracts/fireworks.fc#L31-L32) such a continuation. In this case, this is just an empty continuation that does nothing. Next, using the opcode `0 SETNUMARGS`, we indicate that there should be no values ​​in the stack at the beginning of execution. Then, using the opcode `1 -1 SETCONTARGS`, we call the continuation, passing 1 value. Since there should have been no values, we get a StackOverflow error. + +### Compute Phase | exit code = 4 + +In TVM, `integer` can be in the range -2256 \< x \< 2256. If the value during the calculation went beyond this range, then 4 exit code is thrown. + +### Compute Phase | exit code = 5 + +If the `integer` value went beyond the expected range, then 5 exit code is thrown. For example, if a negative value was used in the `.store_uint()` function. + +### Compute Phase | exit code = 6 + +On a lower level, instead of the familiar functions names, opcodes are used, which can be seen in [this table](/learn/archive/tvm-instructions) in HEX form. In this example, we [use](https://github.com/ton-community/fireworks-func/blob/ef49b4da12d287a8f6c2b6f0c19d65814c1578fc/contracts/fireworks.fc#L25) `@addop`, which adds a non-existent opcode. + +The emulator, when trying to process this opcode, does not recognize it and throws 6. + +### Compute Phase | exit code = 7 + +It is a quite common error that occurs when receiving the wrong data type. In [example](https://github.com/ton-community/fireworks-func/blob/ef49b4da12d287a8f6c2b6f0c19d65814c1578fc/contracts/fireworks.fc#L79-L80) is the case when the `tuple` contained 3 elements, but when unpacking there was an attempt to get 4. + +There are many other cases when this error is thrown. Some of them: + +- [not a null](https://github.com/ton-blockchain/ton/blob/9728bc65b75defe4f9dcaaea0f62a22f198abe96/crypto/vm/stack.cpp#L433) +- [not an integer](https://github.com/ton-blockchain/ton/blob/9728bc65b75defe4f9dcaaea0f62a22f198abe96/crypto/vm/stack.cpp#L441) +- [not a cell](https://github.com/ton-blockchain/ton/blob/9728bc65b75defe4f9dcaaea0f62a22f198abe96/crypto/vm/stack.cpp#L478) +- [not a cell builder](https://github.com/ton-blockchain/ton/blob/9728bc65b75defe4f9dcaaea0f62a22f198abe96/crypto/vm/stack.cpp#L500) +- [not a cell slice](https://github.com/ton-blockchain/ton/blob/9728bc65b75defe4f9dcaaea0f62a22f198abe96/crypto/vm/stack.cpp#L509) +- [not a string](https://github.com/ton-blockchain/ton/blob/9728bc65b75defe4f9dcaaea0f62a22f198abe96/crypto/vm/stack.cpp#L518) +- [not a bytes chunk](https://github.com/ton-blockchain/ton/blob/9728bc65b75defe4f9dcaaea0f62a22f198abe96/crypto/vm/stack.cpp#L527) +- [not a continuation](https://github.com/ton-blockchain/ton/blob/9728bc65b75defe4f9dcaaea0f62a22f198abe96/crypto/vm/stack.cpp#L536) +- [not a box](https://github.com/ton-blockchain/ton/blob/9728bc65b75defe4f9dcaaea0f62a22f198abe96/crypto/vm/stack.cpp#L545) +- [not a tuple](https://github.com/ton-blockchain/ton/blob/9728bc65b75defe4f9dcaaea0f62a22f198abe96/crypto/vm/stack.cpp#L554) +- [not an atom](https://github.com/ton-blockchain/ton/blob/9728bc65b75defe4f9dcaaea0f62a22f198abe96/crypto/vm/stack.cpp#L598) + +### Compute Phase | exit code = 8 + +All data in TON is stored in [cells](/develop/data-formats/cell-boc#cell). A cell has the capacity to store 1023 bits of data and 4 references to other cells. If you try to write more than 1023 bits or more than 4 references, 8 exit code is thrown. + +### Compute Phase | exit code = 9 + +If you try to read more data from a slice (when reading data from a cell, it must be converted to a slice data type) than it contains, then 9 exit code is thrown. For example, if there were 10 bits in the slice, and 11 were read, or if there were no links to other references, but there was an attempt to load a reference. + +### Compute Phase | exit code = 10 + +This error is thrown when working with [dictionaries](/develop/func/stdlib/#dictionaries-primitives). As an example, the case when the value that belongs to the key [is stored in another cell](https://github.com/ton-community/fireworks-func/blob/ef49b4da12d287a8f6c2b6f0c19d65814c1578fc/contracts/fireworks.fc#L100-L110) as a reference. In this case, you need to use the `.udict_get_ref()` function to get such a value. + +However, the link to another cell [should be only 1](https://github.com/ton-blockchain/ton/blob/9728bc65b75defe4f9dcaaea0f62a22f198abe96/crypto/vm/dict.cpp#L454) and not 2, as in our example: + +``` +root_cell +├── key +│ ├── value +│ └── value - second reference for one key +└── key + └── value +``` + +That is why when trying to read the value, we get 10 exit code. + +**Additional:** You can also store the value next to the key in the dictionary: + +``` +root_cell +├── key-value +└── key-value +``` + +**Note:** In fact, the structure of the dictionary (how the data is located in the cells) is more complicated than indicated in the examples above. Therefore, they are simplified for understanding the example. + +### Compute Phase | exit code = 11 + +This error occurs when something unknown happens. For example, when using the [SENDMSG](/learn/tvm-instructions/tvm-upgrade-2023-07#sending-messages) opcode, if you pass the [wrong](https://github.com/ton-community/fireworks-func/blob/ef49b4da12d287a8f6c2b6f0c19d65814c1578fc/contracts/fireworks.fc#L116) (for example, empty) cell with a message, then such an error will occur. + +Also, it occurs when trying to call a non-existent method. Often, developers face this when calling a non-existent GET method. + +### Compute Phase | exit code = -14 (13) + +If there is not enough TON to handle Compute Phase, then this error is thrown. In the enum class `Excno`, where the exit codes for various errors in Compute Phase are indicated, [the value 13 is indicated](https://github.com/ton-blockchain/ton/blob/9728bc65b75defe4f9dcaaea0f62a22f198abe96/crypto/vm/excno.hpp#L39). + +However, during processing, the [NOT operation is applied](https://github.com/ton-blockchain/ton/blob/9728bc65b75defe4f9dcaaea0f62a22f198abe96/crypto/block/transaction.cpp#L1574) to this value, which changes this value to `-14`. This is done so that this exit code cannot be faked using, for example, the `throw` function, since all such functions accept only positive values for the exit code. + +### Action Phase | exit code = 32 + +Action Phase begins after Compute Phase and it processes actions that were written to [register c5](/learn/tvm-instructions/tvm-initialization#control-register-c5) during Compute Phase. If the data in this register is incorrectly written, then 32 exit code will be thrown. + +### Action Phase | exit code = 33 + +At the moment, a maximum of `255` actions can be in one transaction. If this value is exceeded, then the Action Phase will end with 33 exit code. + +### Action Phase | exit code = 34 + +This exit code is responsible for most of the errors when working with actions: invalid message, incorrect action, and so on. + +### Action Phase | exit code = 35 + +During the building of the [CommonMsgInfo](/develop/smart-contracts/tutorials/wallet#commonmsginfo) part of the message, you must specify the correct source address. It must be equal to either [addr_none](/develop/data-formats/msg-tlb#addr_none00) or the address of the account that sends the message. + +In the blockchain code, this is handled by the [check_replace_src_addr](https://github.com/ton-blockchain/ton/blob/9728bc65b75defe4f9dcaaea0f62a22f198abe96/crypto/block/transaction.cpp#L1985). + +### Action Phase | exit code = 36 + +If the destination address is invalid, then 36 exit code is thrown. Some possible reasons are a non-existent workchain or an incorrect address. All checks can be seen in the [check_rewrite_dest_addr](https://github.com/ton-blockchain/ton/blob/9728bc65b75defe4f9dcaaea0f62a22f198abe96/crypto/block/transaction.cpp#L2014-L2113). + +### Action Phase | exit code = 37 + +This exit code is similar to `-14` in Compute Phase. Here it means that there is not enough balance to send the specified amount of TON. + +### Action Phase | exit code = 38 + +The same as in `37` exit code, but refers to the lack of [ExtraCurrency](/develop/research-and-development/minter-flow#extracurrency) on the balance. + +### Action Phase | exit code = 40 + +In case there is enough TON to process a certain part of the message (let's say 5 cells), and there are 10 cells in the message, 40 exit code is thrown. + +### Action Phase | exit code = 43 + +May be occur, if the maximum number of cells in the library is exceeded or the maximum depth of the Merkle tree is exceeded. + +Library is a cell that is stored in [Masterchain](/learn/overviews/ton-blockchain#masterchain-blockchain-of-blockchains) and can be used by all smart contracts if it is [public](https://github.com/ton-blockchain/ton/blob/9728bc65b75defe4f9dcaaea0f62a22f198abe96/crypto/block/transaction.cpp#L1844). + +:::info +Since the order of lines may change when updating the code, some links become irrelevant. Therefore, all links will use the state of the code base at commit [9728bc65b75defe4f9dcaaea0f62a22f198abe96](https://github.com/ton-blockchain/ton/tree/9728bc65b75defe4f9dcaaea0f62a22f198abe96). +::: From 6d155355021c95caac9bdaac01fa61ced3e4332e Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:12 +0800 Subject: [PATCH 154/219] New translations multisig-js.md (Chinese Simplified) --- .../smart-contracts/tutorials/multisig-js.md | 197 ++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/tutorials/multisig-js.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/tutorials/multisig-js.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/tutorials/multisig-js.md new file mode 100644 index 0000000000..9c8c6aaf65 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/tutorials/multisig-js.md @@ -0,0 +1,197 @@ +--- +description: At the end of this guide you will deploy multisig wallet and send some transactions using ton library +--- + +# Interact with multisig wallets using TypeScript + +## Introduction + +If you don't know what is multisig wallet in TON, you can check it out [here](/develop/smart-contracts/tutorials/multisig) + +Following this steps you will learn how to: + +- Create and deploy multisig wallet +- Create, sign and send transactions with that wallet + +We will create a TypeScript project and use [ton](https://www.npmjs.com/package/ton) library, so you need to install it first. We will also use the [ton-access](https://www.orbs.com/ton-access/): + +```bash +yarn add typescript @types/node ton ton-crypto ton-core buffer @orbs-network/ton-access +yarn tsc --init -t es2022 +``` + +The full code of this guide is available here: + +- https://github.com/Gusarich/multisig-ts-example + +## Create and deploy multisig wallet + +Let's create a source file, `main.ts` for example. Open it in your favorite code editor and follow this guide! + +At first we need to import all important stuff + +```js +import { Address, beginCell, MessageRelaxed, toNano, TonClient, WalletContractV4, MultisigWallet, MultisigOrder, MultisigOrderBuilder } from "ton"; +import { KeyPair, mnemonicToPrivateKey } from 'ton-crypto'; +import { getHttpEndpoint } from "@orbs-network/ton-access"; +``` + +Create `TonClient` instance: + +```js +const endpoint = await getHttpEndpoint(); +const client = new TonClient({ endpoint }); +``` + +Than we need keypairs to work with: + +```js +let keyPairs: KeyPair[] = []; + +let mnemonics[] = [ + ['orbit', 'feature', ...], //this should be the seed phrase of 24 words + ['sing', 'pattern', ...], + ['piece', 'deputy', ...], + ['toss', 'shadow', ...], + ['guard', 'nurse', ...] +]; + +for (let i = 0; i < mnemonics.length; i++) keyPairs[i] = await mnemonicToPrivateKey(mnemonics[i]); +``` + +There are two ways to create `MultisigWallet` object: + +- Import existing one from address + +```js +let addr: Address = Address.parse('EQADBXugwmn4YvWsQizHdWGgfCTN_s3qFP0Ae0pzkU-jwzoE'); +let mw: MultisigWallet = await MultisigWallet.fromAddress(addr, { client }); +``` + +- Create a new one + +```js +let mw: MultisigWallet = new MultisigWallet([keyPairs[0].publicKey, keyPairs[1].publicKey], 0, 0, 1, { client }); +``` + +There are also two ways to deploy it + +- Via internal message + +```js +let wallet: WalletContractV4 = WalletContractV4.create({ workchain: 0, publicKey: keyPairs[4].publicKey }); +//wallet should be active and have some balance +await mw.deployInternal(wallet.sender(client.provider(wallet.address, null), keyPairs[4].secretKey), toNano('0.05')); +``` + +- Via external message + +```js +await mw.deployExternal(); +``` + +## Create, sign and send an order + +We need an `MultisigOrderBuilder` object to create a new order. + +```js +let order1: MultisigOrderBuilder = new MultisigOrderBuilder(0); +``` + +Then we can add some messages to it. + +```js +let msg: MessageRelaxed = { + body: beginCell().storeUint(0, 32).storeBuffer(Buffer.from('Hello, world!')).endCell(), + info: { + bounce: true, + bounced: false, + createdAt: 0, + createdLt: 0n, + dest: Address.parse('EQArzP5prfRJtDM5WrMNWyr9yUTAi0c9o6PfR4hkWy9UQXHx'), + forwardFee: 0n, + ihrDisabled: true, + ihrFee: 0n, + type: "internal", + value: { coins: toNano('0.01') } + } +}; + +order1.addMessage(msg, 3); +``` + +After you finish with adding messages, transform the `MultisigOrderBuilder` to `MultisigOrder` by calling `build()` method. + +```js +let order1b: MultisigOrder = order1.build(); +order1b.sign(0, keyPairs[0].secretKey); +``` + +Now let's create another order, add a message to it, sign it with some other set of keys and union the signatures of these orders. + +```js +let order2: MultisigOrderBuilder = new MultisigOrderBuilder(0); +order2.addMessage(msg, 3); +let order2b = order2.build(); +order2b.sign(1, keyPairs[1].secretKey); + +order1b.unionSignatures(order2b); //Now order1b have also have all signatures from order2b +``` + +And finally, send the signed order: + +```js +await mw.sendOrder(order1b, keyPairs[0].secretKey); +``` + +Now build the project + +```bash +yarn tsc +``` + +And run the compiled file + +```bash +node main.js +``` + +If it does not throw any errors, you made everything right! Now check if your transaction succeed with any explorer or wallet. + +## Other methods and properties + +You can easily clear messages from `MultisigOrderBuilder` objects: + +```js +order2.clearMessages(); +``` + +You also can clear signatures from `MultisigOrder` objects: + +```js +order2b.clearSignatures(); +``` + +And of course you can get public properties from `MultisigWallet`, `MultisigOrderBuilder` and `MultisigOrder` objects + +- MultisigWallet: + - `owners` - `Dictionary` of signatures _ownerId => signature_ + - `workchain` - workchain where the wallet is deployed + - `walletId` - wallet id + - `k` - number of signatures required to confirm a transaction + - `address` - wallet address + - `provider` - `ContractProvider` instance + +- MultisigOrderBuilder + - `messages` - array of `MessageWithMode` to be added to the order + - `querryId` - golbal time until which the order is valid + +- MultisigOrder + - `payload` - `Cell` with order payload + - `signatures` - `Dictionary` of signatures _ownerId => signature_ + +## References + +- [Low-level multisig guide](/develop/smart-contracts/tutorials/multisig) +- [ton.js Documentation](https://ton-community.github.io/ton/) +- [Multisig contract sources](https://github.com/ton-blockchain/multisig-contract) From ec17873bea6cb1f84169eed26e49c02dad88f73a Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:12 +0800 Subject: [PATCH 155/219] New translations multisig.md (Chinese Simplified) --- .../smart-contracts/tutorials/multisig.md | 271 ++++++++++++++++++ 1 file changed, 271 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/tutorials/multisig.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/tutorials/multisig.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/tutorials/multisig.md new file mode 100644 index 0000000000..29d5d46c40 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/tutorials/multisig.md @@ -0,0 +1,271 @@ +--- +description: At the end of the tutorial, you will have deployed multisig contract in TON Blockchain. +--- + +# How to make a simple multisig contract + +## 💡 Overview + +This tutorial help you learn how to deploy your multisig contract.\ +Recall, that (n, k)-multisig contract is a multisignature wallet with n private keys holders, which accepts requests to send messages if the request (aka order, query) collects at least k signatures of the holders. + +Based on original multisig contract code and updates by akifoq: + +- [original TON Blockchain multisig-code.fc](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/multisig-code.fc) +- [akifoq/multisig](https://github.com/akifoq/multisig) with fift libraries to work with multisig. + +:::tip starter tip +For anyone new with multisig: [What is Multisig Technology? (video)](https://www.youtube.com/watch?v=yeLqe_gg2u0) +::: + +## 📖 What you'll learn + +- How to create and customize a simple multisig wallet. +- How to deploy multisig wallet using lite-client. +- How to sign request and send it in message to blockchain. + +## ⚙ Set your environment + +Before we begin our journey, check and prepare your environment. + +- Install `func`, `fift`, `lite-client` binaries and `fiftlib` from the [Installation](/develop/smart-contracts/environment/installation) section. +- Clone [repository](https://github.com/akifoq/multisig) and open its directory in CLI. + +```cpp +https://github.com/akifoq/multisig.git +cd ~/multisig +``` + +## 🚀 Let's get started! + +1. Compile the code to fift. +2. Prepare multisig owners keys. +3. Deploy your contract. +4. Interact with deployed multisig wallet in blockchain. + +### Compile the contract + +Compile the contract to Fift with: + +```cpp +func -o multisig-code.fif -SPA stdlib.fc multisig-code.fc +``` + +### Prepare multisig owners keys + +#### Create participants keys + +To create a key you need to run: + +```cpp +fift -s new-key.fif $KEY_NAME$ +``` + +- Where `KEY_NAME` is the name of the file where the private key will be written. + +For example: + +```cpp +fift -s new-key.fif multisig_key +``` + +We'll receive a file `multisig_key.pk` with private key inside. + +#### Collect public keys + +Also, the script will issue a public key in the format: + +``` +Public key = Pub5XqPLwPgP8rtryoUDg2sadfuGjkT4DLRaVeIr08lb8CB5HW +``` + +Anything after `"Public key = "` needs to be saved somewhere! + +Let's store in file `keys.txt`. One Public Key per line, it's important. + +### Deploy your contract + +#### Deploy via lite-client + +After creating all the keys, you need to collect the public keys into a text file `keys.txt`. + +For example: + +```bash +PubExXl3MdwPVuffxRXkhKN1avcGYrm6QgJfsqdf4dUc0an7/IA +PubH821csswh8R1uO9rLYyP1laCpYWxhNkx+epOkqwdWXgzY4 +``` + +After that, you need to run: + +```cpp +fift -s new-multisig.fif 0 $WALLET_ID$ wallet $KEYS_COUNT$ ./keys.txt +``` + +- `$WALLET_ID$` - the wallet number assigned for current key. It is recommended to use a unique `$WALLET_ID$` for each new wallet with the same key. +- `$KEYS_COUNT$` - the number of keys needed for confirmation, usually equal to the number of public keys + +:::info wallet_id explained +It's possible to create many wallets with the same keys (Alice key, Bob key). What to do if Alice and Bob already have treasure? That's why `$WALLET_ID$` is crucial here. +::: + +The script will output something like: + +```bash +new wallet address = 0:4bbb2660097db5c72dd5e9086115010f0f8c8501e0b8fef1fe318d9de5d0e501 + +(Saving address to file wallet.addr) + +Non-bounceable address (for init): 0QBLuyZgCX21xy3V6QhhFQEPD4yFAeC4_vH-MY2d5dDlAbel + +Bounceable address (for later access): kQBLuyZgCX21xy3V6QhhFQEPD4yFAeC4_vH-MY2d5dDlAepg + +(Saved wallet creating query to file wallet-create.boc) +``` + +:::info +If you have "public key must be 48 characters long" error, please make sure your `keys.txt` has unix type word wrap - LF. For example, word wrap can be changed via Sublime text editor. +::: + +:::tip +Bounceable address is better to keep - this is the address of the wallet. +::: + +#### Activate your contract + +You need to send some TON to our newly generated _treasure_. For example 0.5 TON. + +After that, you need to run lite-client: + +```bash +lite-client -C global.config.json +``` + +:::info Where get `global.config.json`? +You can get fresh config file `global.config.json` for [mainnet](https://ton.org/global-config.json) or [testnet](https://ton.org/testnet-global.config.json). +::: + +After starting lite-client, it's best to run the `time` command in lite-client console to make sure the connection was successful: + +```bash +time +``` + +Okay, lite-client is works! + +After you need to deploy the wallet. run the command: + +``` +sendfile ./wallet-create.boc +``` + +After that, the wallet will be ready to work within a minute. + +### Interact with multisig wallet + +#### Create a request + +First you need to create a message request: + +```cpp +fift -s create-msg.fif $ADDRESS$ $AMOUNT$ $MESSAGE$ +``` + +- `$ADDRESS$` - address where to send coins +- `$AMOUNT$` - number of coins +- `$MESSAGE$` - name of file for compiled message. + +For example: + +```cpp +fift -s create-msg.fif EQApAj3rEnJJSxEjEHVKrH3QZgto_MQMOmk8l72azaXlY1zB 0.1 message +``` + +:::tip +To add comment for your transaction, use `-C comment` attribute. To get more information, run _create-msg.fif_ file without parameters. +::: + +#### Choose a wallet + +Next you need to choose a wallet to send a coins from: + +``` +fift -s create-order.fif $WALLET_ID$ $MESSAGE$ -t $AWAIT_TIME$ +``` + +Where + +- `$WALLET_ID$` — is an ID of wallet backed by this multisig contract. +- `$AWAIT_TIME$` — Time in seconds that smart contract will await signs from multisig wallet's owners for request. +- `$MESSAGE$` — here is a name of message boc-file created on the previous step. + +:::info +If time equals `$AWAIT_TIME$` passed before the request signs, the request becomes expired. As usual, $AWAIT_TIME$ equals a couple of hours (7200 seconds) +::: + +For example: + +``` +fift -s create-order.fif 0 message -t 7200 +``` + +Ready file will be saved in `order.boc` + +:::info +`order.boc` needs to be shared with key holders, they have to sign it. +::: + +#### Sign your part + +To sign, you need to do: + +```bash +fift -s add-signature.fif $KEY$ $KEY_INDEX$ +``` + +- `$KEY$` - name of the file containing the private key to sign, without extension. +- `$KEY_INDEX$` - index of the given key in `keys.txt` (zero-based) + +For example, for our `multisig_key.pk` file: + +``` +fift -s add-signature.fif multisig_key 0 +``` + +#### Create a message + +After everyone has signed the order, it needs to be turned into a message for the wallet and signed again with the following command: + +``` +fift -s create-external-message.fif wallet $KEY$ $KEY_INDEX$ +``` + +In this case, will be enough only one sign of wallet's owner. The idea is that you can't attack a contract with invalid signatures. + +For example: + +``` +fift -s create-external-message.fif wallet multisig_key 0 +``` + +#### Send sign to TON Blockchain + +After that, you need to start the light client again: + +```bash +lite-client -C global.config.json +``` + +And after finally, we want to send our sign! Just run: + +```bash +sendfile wallet-query.boc +``` + +If everyone else signed the request, it will be completed! + +You did it, ha-ha! 🚀🚀🚀 + +## What's next? + +- [Read more about multisig wallets in TON](https://github.com/akifoq/multisig) from akifoq From 7615d27e17d7d38f4292f0fd3c491f77a1a0e236 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:13 +0800 Subject: [PATCH 156/219] New translations wallet.md (Chinese Simplified) --- .../smart-contracts/tutorials/wallet.md | 2754 +++++++++++++++++ 1 file changed, 2754 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/tutorials/wallet.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/tutorials/wallet.md b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/tutorials/wallet.md new file mode 100644 index 0000000000..9552d25e37 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/smart-contracts/tutorials/wallet.md @@ -0,0 +1,2754 @@ +--- +description: In this tutorial, you will learn how to fully work with wallets, transactions and smart contracts. +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# 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, transactions, and smart contracts to implement specific development tasks. + +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. + +## 💡 Prerequisites + +This tutorial requires basic knowledge of Javascript, Typescript, and 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](/learn/overviews/cells), [addresses in TON](/learn/overviews/addresses), [blockchain of blockchains](/learn/overviews/ton-blockchain) 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. +::: + +## 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 + +- 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 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. + + + + +```bash +npm i --save @ton/ton @ton/core @ton/crypto +``` + + + + +```bash +go get github.com/xssnick/tonutils-go +go get github.com/xssnick/tonutils-go/adnl +go get github.com/xssnick/tonutils-go/address +``` + + + + +## ⚙ Set Your Environment + +In order to create a TypeScript project its 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 followings 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. +::: + +```json + "files": [ + "\\", + "\\" + ] +``` + +5. Then, create a `nodemon.json` config in your project root with the following content: + +```json +{ + "watch": ["src"], + "ext": ".ts,.js", + "ignore": [], + "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: + +```json +"start:dev": "npx nodemon" +``` + +7. Create `src` folder in the project root and `index.ts` file in this folder. +8. Next, the following code should be added: + +```ts +async function main() { + console.log("Hello, TON!"); +} + +main().finally(() => console.log("Exiting...")); +``` + +9. Run the code using 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. +::: + +\*\*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 it outdated): + +``` +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: + +```go +package main + +import ( + "log" +) + +func main() { + log.Println("Hello, TON!") +} +``` + +5. Change the name of the module in the `go.mod` to `main`. +6. Run the code above until the output in the terminal is displayed. + +:::info +It is also possible to use another IDE since GoLand isn’t free, but it is preferred. +::: + +:::warning IMPORTANT + +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.\ +::: + +## 🚀 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 transaction types on the TON platform to make it simpler to create transactions, send them to the blockchain, deploy wallets, and eventually, be able to work with high-load wallets. + +Our main task is to build transactions using various objects and functions for @ton/ton, @ton/core, @ton/crypto (ExternalMessage, InternalMessage, Signing etc.) to understand what transactions 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. + +:::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. + +**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. +::: + +## 💎 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. + +### Wallet Communication + +Generally, there are two transaction types on TON Blockchain: `internal` and `external`. External transactions 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 transactions. The function responsible for carrying out this process is as follows: + +```func +() recv_external(slice in_msg) impure { + ;; some code +} +``` + +Before we dive into more details concerning wallets, let’s look at how wallets accept external transactions. On TON, all wallets hold the owner’s `public key`, `seqno`, and `subwallet_id`. When receiving an external transaction, 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 transaction or not. This process is conducted 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 transaction + 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: +> +> ["load_bits()" in docs](/develop/func/stdlib/#load_bits) +> +> ["get_data()" in docs](/develop/func/stdlib/#load_bits) +> +> ["begin_parse()" in docs](/develop/func/stdlib/#load_bits) +> +> ["end_parse()" in docs](/develop/func/stdlib/#end_parse) +> +> ["load_int()" in docs](/develop/func/stdlib/#load_int) +> +> ["load_uint()" in docs](/develop/func/stdlib/#load_int) +> +> ["check_signature()" in docs](/develop/func/stdlib/#check_signature) +> +> ["slice_hash()" in docs](/develop/func/stdlib/#slice_hash) +> +> ["accept_message()" in docs](/develop/smart-contracts/guidelines/accept) + +Now let’s take a closer look. + +### Replay Protection - Seqno + +Transaction replay protection in the wallet smart contract is directly related to the transaction seqno (Sequence Number) which keeps track of which transactions are sent in which order. It is very important that a single transaction 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: + +```func +throw_unless(33, msg_seqno == stored_seqno); +``` + +This line of code above checks the `seqno`, which comes in the transaction 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 transaction sequence, and the contract protects against such cases. + +:::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. +::: + +### Signature + +As mentioned earlier, wallet smart contracts accept external transactions. However, these transactions 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 transaction signature when receiving an external transaction that the owner signed with the private key. This verifies that the transaction is actually 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: + +```func +var signature = in_msg~load_bits(512); +var ds = get_data().begin_parse(); +var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256)); +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: + +```func +accept_message(); +``` + +:::info accept_message() +Because the transaction 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](/develop/smart-contracts/guidelines/accept). +::: + +### Transaction Expiration + +Another step used to check the validity of external transactions is the `valid_until` field. As you can see from the variable name, this is the time in UNIX before the transaction is valid. If this verification process fails, the contract completes the processing of the transaction and returns the 32 exit code 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 transaction is no longer valid but was 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. + +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 + +As we discussed earlier, a wallet smart contract accepts external transactions, 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()) { + var mode = cs~load_uint(8); ;; load transaction 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. +::: + +Since a **maximum of 4 references** can be stored in one cell, we can send a maximum of 4 internal messages per external message. + +> 💡 Useful links: +> +> ["slice_refs()" in docs](/develop/func/stdlib/#slice_refs) +> +> ["send_raw_message() and transaction modes" in docs](/develop/func/stdlib/#send_raw_message) +> +> ["load_ref()" in docs](/develop/func/stdlib/#load_ref) + +## 📬 External and Internal Transactions + +In this section, we’ll learn more about `internal` and `external` transactions and we’ll create transactions 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](/participate/wallets/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 transaction 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. + +:::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). +::: + +### 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. + +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 transactions. + +:::info +Basic information will be provided within this guide. For further details, please refer to our TL-B [documentation](/develop/data-formats/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 transaction type, transaction 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 transaction 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 transaction type fields such as `src`, `created_lt`, `created_at`, and others are rewritten by validators during transaction handling. In this case, the `src` transaction type is most important because when transactions 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. + +:::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). +::: + +### Internal Transaction Creation + +Internal transactions 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: + +```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 +``` + +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: + +```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 +``` + +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](/learn/overviews/ton-blockchain#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 transactions, 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 transaction 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](/develop/smart-contracts/guidelines/non-bouncable-messages). | +| Bounced | Bounced transactions are transactions that are returned to the sender because an error occurred while processing the transaction with a smart contract. This option tells you whether the transaction 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: + +```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 +``` + +| 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](/develop/howto/fees-low-level#transactions-and-phases). | +| Logical time of creation | The time used to create the correct transaction queue. | +| UNIX tome of creation | The time the transaction 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. + +:::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`. +::: + +Next, we’ll begin preparing a transaction, 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](/develop/func/cookbook#how-to-send-a-simple-message)). + + + + +```js +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(); +``` + + + + +```go +import ( + "github.com/xssnick/tonutils-go/tvm/cell" +) + +internalMessageBody := cell.BeginCell(). + MustStoreUInt(0, 32). // write 32 zero bits to indicate that a text comment will follow + MustStoreStringSnake("Hello, TON!"). // write our text comment + EndCell() +``` + + + + +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](/develop/smart-contracts/guidelines/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. + +Next, the `InternalMessage` is created according to the information we have studied earlier as follows: + + + + +```js +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(); +``` + + + + +```go +import ( + "github.com/xssnick/tonutils-go/address" + "github.com/xssnick/tonutils-go/tlb" +) + +walletAddress := address.MustParseAddr("put your address") + +internalMessage := cell.BeginCell(). + MustStoreUInt(0, 1). // indicate that it is an internal message -> int_msg_info$0 + MustStoreBoolBit(true). // IHR Disabled + MustStoreBoolBit(true). // bounce + MustStoreBoolBit(false). // bounced + MustStoreUInt(0, 2). // src -> addr_none + MustStoreAddr(walletAddress). + MustStoreCoins(tlb.MustFromTON("0.2").NanoTON().Uint64()). // amount + MustStoreBoolBit(false). // Extra currency + MustStoreCoins(0). // IHR Fee + MustStoreCoins(0). // Forwarding Fee + MustStoreUInt(0, 64). // Logical time of creation + MustStoreUInt(0, 32). // UNIX time of creation + MustStoreBoolBit(false). // No State Init + MustStoreBoolBit(true). // We store Message Body as a reference + MustStoreRef(internalMessageBody). // Store Message Body as a reference + EndCell() +``` + + + + +### 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-transactions)) to sign our transaction via the following steps: + + + + +```js +import { TonClient } from '@ton/ton'; +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 +}); + +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 +``` + + + + +```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" +) + +mnemonic := strings.Split("put your mnemonic", " ") // get our mnemonic as array + +connection := liteclient.NewConnectionPool() +configUrl := "https://ton-blockchain.github.io/global.config.json" +err := connection.AddConnectionsFromConfigUrl(context.Background(), configUrl) +if err != nil { + panic(err) +} +client := ton.NewAPIClient(connection) // create client + +block, err := client.CurrentMasterchainInfo(context.Background()) // get current block, we will need it in requests to LiteServer +if err != nil { + log.Fatalln("CurrentMasterchainInfo err:", err.Error()) + return +} + +getMethodResult, err := client.RunGetMethod(context.Background(), block, walletAddress, "seqno") // run "seqno" GET method from your wallet contract +if err != nil { + log.Fatalln("RunGetMethod err:", err.Error()) + return +} +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 + +privateKey := ed25519.NewKeyFromSeed(k) +``` + + + + +Therefore, the `seqno`, `keys`, and `internal message` need to be sent. Now we need to create a [message](/develop/smart-contracts/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: + + + + +```js +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) // Transaction expiration time, +60 = 1 minute + .storeUint(seqno, 32) // store seqno + .storeUint(3, 8) // store mode of our internal transaction + .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 body = beginCell() + .storeBuffer(signature) // store signature + .storeBuilder(toSign) // store our message + .endCell(); +``` + + + + +```go +import ( + "time" +) + +toSign := cell.BeginCell(). + MustStoreUInt(698983191, 32). // subwallet_id | We consider this further + MustStoreUInt(uint64(time.Now().UTC().Unix()+60), 32). // Transaction expiration time, +60 = 1 minute + MustStoreUInt(seqno.Uint64(), 32). // store seqno + MustStoreUInt(uint64(3), 8). // store mode of our internal transaction + 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 + +body := cell.BeginCell(). + MustStoreSlice(signature, 512). // store signature + MustStoreBuilder(toSign). // store our message + EndCell() +``` + + + + +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 transaction 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 Transaction Creation + +To deliver any internal message to a blockchain from the outside world, it is necessary to send it within an external transaction. 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: + + + + +```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(); +``` + + + + +```go +externalMessage := cell.BeginCell(). + MustStoreUInt(0b10, 2). // 0b10 -> 10 in binary + MustStoreUInt(0, 2). // src -> addr_none + MustStoreAddr(walletAddress). // Destination address + MustStoreCoins(0). // Import Fee + MustStoreBoolBit(false). // No State Init + MustStoreBoolBit(true). // We store Message Body as a reference + MustStoreRef(body). // Store Message Body as a reference + EndCell() +``` + + + + +| 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`. +::: + +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](/develop/data-formats/cell-boc#bag-of-cells)), then be sent using the following code: + + + + +```js +console.log(externalMessage.toBoc().toString("base64")) + +client.sendFile(externalMessage.toBoc()); +``` + + + + +```go +import ( + "encoding/base64" + "github.com/xssnick/tonutils-go/tl" +) + +log.Println(base64.StdEncoding.EncodeToString(externalMessage.ToBOCWithFlags(false))) + +var resp tl.Serializable +err = client.Client().QueryLiteserver(context.Background(), ton.SendMessage{Body: externalMessage.ToBOCWithFlags(false)}, &resp) + +if err != nil { + log.Fatalln(err.Error()) + return +} +``` + + + + +> 💡 Useful link: +> +> [More about Bag of Cells](/develop/data-formats/cell-boc#bag-of-cells) + +As a result, we got the output of our BOC in the console and the transaction sent to our wallet. By copying the base64 encoded string, it is possible to [manually send our transaction and retrieve the hash using toncenter](https://toncenter.com/api/v2/#/send/send_boc_return_hash_sendBocReturnHash_post). + +## 👛 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. + +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 transactions 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. + +This is accomplished as follows: + + + + +```js +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 +``` + + + + +```go +import ( + "crypto/ed25519" + "crypto/hmac" + "crypto/sha512" + "log" + "github.com/xssnick/tonutils-go/ton/wallet" + "golang.org/x/crypto/pbkdf2" + "strings" +) + +// 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). +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 + +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(mnemonic) // if we want, we can print our mnemonic +``` + + + + +The private key is needed to sign transactions 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. +::: + +### 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. + +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: + +```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`. + +Each wallet contract checks the subwallet_id field for external transactions to avoid instances when requests were sent to wallet with another ID: + +```func +var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32)); +var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256)); +throw_unless(34, subwallet_id == stored_subwallet); +``` + +We will need to add the above value to the initial data of the contract, so the variable needs to be saved as follows: + + + + +```js +const subWallet = 698983191; +``` + + + + +```go +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. + +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: + +```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. + +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: + +``` +. +├── src/ +│ ├── main.ts +│ ├── wallet_v3.fc +│ └── stdlib.fc +├── nodemon.json +├── package-lock.json +├── package.json +└── tsconfig.json +``` + +:::info +It’s fine 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: + +```func +#include "stdlib.fc"; +``` + +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'; + +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' }), +} +}); + +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 +``` + +The result will be the following output in the terminal: + +```text +Code BOC: te6ccgEBCAEAhgABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQCW8oMI1xgg0x/TH9MfAvgju/Jj7UTQ0x/TH9P/0VEyuvKhUUS68qIE+QFUEFX5EPKj+ACTINdKltMH1AL7AOgwAaTIyx/LH8v/ye1UAATQMAIBSAYHABe7Oc7UTQ0z8x1wv/gAEbjJftRNDXCx+A== + +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: + + + + +```go +import ( + "encoding/base64" + "github.com/xssnick/tonutils-go/tvm/cell" +) + +base64BOC := "te6ccgEBCAEAhgABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQCW8oMI1xgg0x/TH9MfAvgju/Jj7UTQ0x/TH9P/0VEyuvKhUUS68qIE+QFUEFX5EPKj+ACTINdKltMH1AL7AOgwAaTIyx/LH8v/ye1UAATQMAIBSAYHABe7Oc7UTQ0z8x1wv/gAEbjJftRNDXCx+A==" // save our base64 encoded output from compiler to variable +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) +} + +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 +``` + + + + +The result will be the following output in the terminal: + +```text +idlku00WfSC36ujyK2JVT92sMBEpCNRUXOGO4sJVBPA= +``` + +After the above processes are complete it is confirmed that the correct code is being used within our cell because the hashes match. + +### Creating the State Init for Deployment + +Before building a transaction 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](/learn/overviews/ton-blockchain#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](/develop/data-formats/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](/learn/overviews/ton-blockchain#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: + + + + +```js +import { beginCell } from '@ton/core'; + +const dataCell = beginCell() + .storeUint(0, 32) // Seqno + .storeUint(698983191, 32) // Subwallet ID + .storeBuffer(keyPair.publicKey) // Public Key + .endCell(); +``` + + + + +```go +dataCell := cell.BeginCell(). + MustStoreUInt(0, 32). // Seqno + MustStoreUInt(698983191, 32). // Subwallet ID + MustStoreSlice(publicKey, 256). // Public Key + EndCell() +``` + + + + +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. + + + + +```js +import { Address } from '@ton/core'; + +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(); + +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 +``` + + + + +```go +import ( + "github.com/xssnick/tonutils-go/address" +) + +stateInit := cell.BeginCell(). + MustStoreBoolBit(false). // No split_depth + MustStoreBoolBit(false). // No special + MustStoreBoolBit(true). // We have code + MustStoreRef(codeCell). + MustStoreBoolBit(true). // We have data + MustStoreRef(dataCell). + MustStoreBoolBit(false). // No library + EndCell() + +contractAddress := address.NewAddress(0, 0, stateInit.Hash()) // get the hash of stateInit to get the address of our smart contract in workchain with ID 0 +log.Println("Contract address:", contractAddress.String()) // Output contract address to console +``` + + + + +Using the State Init, we can now build the transaction and send it to the blockchain. To carry out this process **a minimum wallet balance of 0.1 TON** (the balance can be less, but this amount is guaranteed to be sufficient) is required. To accomplish this, we’ll need to run the code mentioned earlier in the tutorial, get the correct wallet address and send 0.1 TON to this address. + +Let’s start with building the transaction similar to the one we built **in the previous section**: + + + + +```js +import { sign } from '@ton/crypto'; +import { toNano } from '@ton/core'; + +const internalMessageBody = beginCell() + .storeUint(0, 32) + .storeStringTail("Hello, TON!") + .endCell(); + +const internalMessage = beginCell() + .storeUint(0x10, 6) // no bounce + .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) + .endCell(); + +// transaction for our wallet +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(3, 8) + .storeRef(internalMessage); + +const signature = sign(toSign.endCell().hash(), keyPair.secretKey); +const body = beginCell() + .storeBuffer(signature) + .storeBuilder(toSign) + .endCell(); +``` + + + + +```go +import ( + "github.com/xssnick/tonutils-go/tlb" + "time" +) + +internalMessageBody := cell.BeginCell(). + MustStoreUInt(0, 32). + MustStoreStringSnake("Hello, TON!"). + EndCell() + +internalMessage := cell.BeginCell(). + MustStoreUInt(0x10, 6). // no bounce + MustStoreAddr(address.MustParseAddr("put your first wallet address from were 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 + MustStoreRef(internalMessageBody). + EndCell() + +// transaction for our wallet +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(3, 8). + MustStoreRef(internalMessage) + +signature := ed25519.Sign(privateKey, toSign.EndCell().Hash()) +body := cell.BeginCell(). + MustStoreSlice(signature, 512). + MustStoreBuilder(toSign). + EndCell() +``` + + + + +After this is completed the result is the correct State Init and Message Body. + +### Sending An External Transaction + +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: + + + + +```js +const externalMessage = beginCell() + .storeUint(0b10, 2) // indicate that it is an incoming external transaction + .storeUint(0, 2) // src -> addr_none + .storeAddress(contractAddress) + .storeCoins(0) // Import fee + .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(body) // Store Message Body as a reference + .endCell(); +``` + + + + +```go +externalMessage := cell.BeginCell(). + MustStoreUInt(0b10, 2). // indicate that it is an incoming external transaction + MustStoreUInt(0, 2). // src -> addr_none + MustStoreAddr(contractAddress). + MustStoreCoins(0). // Import fee + MustStoreBoolBit(true). // We have State Init + MustStoreBoolBit(true). // We store State Init as a reference + MustStoreRef(stateInit). // Store State Init as a reference + MustStoreBoolBit(true). // We store Message Body as a reference + MustStoreRef(body). // Store Message Body as a reference + EndCell() +``` + + + + +Finally, we can send our transaction to the blockchain to deploy our wallet and use it. + + + + +```js +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 +}); + +client.sendFile(externalMessage.toBoc()); +``` + + + + +```go +import ( + "context" + "github.com/xssnick/tonutils-go/liteclient" + "github.com/xssnick/tonutils-go/tl" + "github.com/xssnick/tonutils-go/ton" +) + +connection := liteclient.NewConnectionPool() +configUrl := "https://ton-blockchain.github.io/global.config.json" +err := connection.AddConnectionsFromConfigUrl(context.Background(), configUrl) +if err != nil { + panic(err) +} +client := ton.NewAPIClient(connection) + +var resp tl.Serializable +err = client.Client().QueryLiteserver(context.Background(), ton.SendMessage{Body: externalMessage.ToBOCWithFlags(false)}, &resp) +if err != nil { + log.Fatalln(err.Error()) + return +} +``` + + + + +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. + +It's important to note that for each new transaction the **seqno will need to be increased 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 + +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. + +### Sending Multiple Messages Simultaneously + +As you may already know, [one cell can store up to 1023 bits of data and up to 4 references](develop/data-formats/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 transactions to be sent at the same time. + +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. + + + + +```js +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 transaction without comment + "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 + +let internalMessages:Cell[] = []; // array for our internal messages +``` + + + + +```go +import ( + "github.com/xssnick/tonutils-go/tvm/cell" +) + +internalMessagesAmount := [4]string{"0.01", "0.02", "0.03", "0.04"} +internalMessagesComment := [4]string{ + "Hello, TON! #1", + "Hello, TON! #2", + "", // Let's leave the third transaction without comment + "Hello, TON! #4", +} +destinationAddresses := [4]string{ + "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 + +var internalMessages [len(internalMessagesAmount)]*cell.Cell // array for our internal messages +``` + + + + +[Sending mode](/develop/smart-contracts/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'; + +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); + + /* + 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. + */ + + if(internalMessagesComment[index] != "") { + internalMessage.storeBit(1) // we store Message Body as a reference + + let internalMessageBody = beginCell() + .storeUint(0, 32) + .storeStringTail(internalMessagesComment[index]) + .endCell(); + + internalMessage.storeRef(internalMessageBody); + } + 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. + */ + internalMessage.storeBit(0); + + internalMessages.push(internalMessage.endCell()); +} +``` + + + + +```go +import ( + "github.com/xssnick/tonutils-go/address" + "github.com/xssnick/tonutils-go/tlb" +) + +for i := 0; i < len(internalMessagesAmount); i++ { + amount := internalMessagesAmount[i] + + internalMessage := cell.BeginCell(). + MustStoreUInt(0x18, 6). // bounce + MustStoreAddr(address.MustParseAddr(destinationAddresses[i])). + MustStoreBigCoins(tlb.MustFromTON(amount).NanoTON()). + 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. + */ + + if internalMessagesComment[i] != "" { + internalMessage.MustStoreBoolBit(true) // we store Message Body as a reference + + internalMessageBody := cell.BeginCell(). + MustStoreUInt(0, 32). + MustStoreStringSnake(internalMessagesComment[i]). + EndCell() + + internalMessage.MustStoreRef(internalMessageBody) + } 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. + */ + internalMessage.MustStoreBoolBit(false) + } + internalMessages[i] = internalMessage.EndCell() +} +``` + + + + +Now let's use our knowledge from [chapter two](/develop/smart-contracts/tutorials/wallet#-deploying-our-wallet) to build a transaction for our wallet that can send 4 transactions simultaneously: + + + + +```js +import { TonClient } from '@ton/ton'; +import { mnemonicToWalletKey } from '@ton/crypto'; + +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 +}); + +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 + +let toSign = beginCell() + .storeUint(698983191, 32) // subwallet_id + .storeUint(Math.floor(Date.now() / 1e3) + 60, 32) // Transaction 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) +``` + + + + +```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" +) + +walletAddress := address.MustParseAddr("put your wallet address") + +connection := liteclient.NewConnectionPool() +configUrl := "https://ton-blockchain.github.io/global.config.json" +err := connection.AddConnectionsFromConfigUrl(context.Background(), configUrl) +if err != nil { + panic(err) +} +client := ton.NewAPIClient(connection) + +mnemonic := strings.Split("put your mnemonic", " ") // word1 word2 word3 +// The following three lines will extract the private key using the mnemonic phrase. +// We will not go into cryptographic details. In the library tonutils-go, it is all implemented, +// but it immediately returns the finished object of the wallet with the address and ready-made 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 +// 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 +if err != nil { + log.Fatalln("CurrentMasterchainInfo err:", err.Error()) + return +} + +getMethodResult, err := client.RunGetMethod(context.Background(), block, walletAddress, "seqno") // run "seqno" GET method from your wallet contract +if err != nil { + log.Fatalln("RunGetMethod err:", err.Error()) + return +} +seqno := getMethodResult.MustInt(0) // get seqno from response + +toSign := cell.BeginCell(). + MustStoreUInt(698983191, 32). // subwallet_id | We consider this further + MustStoreUInt(uint64(time.Now().UTC().Unix()+60), 32). // transaction 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). +``` + + + + +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 transaction + toSign.storeRef(internalMessage) // store our internalMessage as a reference +} +``` + + + + +```go +for i := 0; i < len(internalMessages); i++ { + internalMessage := internalMessages[i] + toSign.MustStoreUInt(3, 8) // store mode of our internal transaction + 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: + + + + +```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 body = beginCell() + .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(); + +client.sendFile(externalMessage.toBoc()); +``` + + + + +```go +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 + +body := cell.BeginCell(). + MustStoreSlice(signature, 512). // store signature + MustStoreBuilder(toSign). // store our message + EndCell() + +externalMessage := cell.BeginCell(). + MustStoreUInt(0b10, 2). // ext_in_msg_info$10 + MustStoreUInt(0, 2). // src -> addr_none + MustStoreAddr(walletAddress). // Destination address + MustStoreCoins(0). // Import Fee + MustStoreBoolBit(false). // No State Init + MustStoreBoolBit(true). // We store Message Body as a reference + MustStoreRef(body). // Store Message Body as a reference + EndCell() + +var resp tl.Serializable +err = client.Client().QueryLiteserver(context.Background(), ton.SendMessage{Body: externalMessage.ToBOCWithFlags(false)}, &resp) + +if err != nil { + log.Fatalln(err.Error()) + return +} +``` + + + + +:::info Connection error +If an error related to the lite-server connection (Golang) occurs, the code must be run until the transaction 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. +::: + +After this process is completed it is possible to use a TON blockchain explorer to verify that the wallet sent four transactions to the addresses previously specified. + +### NFT Transfers + +In addition to regular transactions, 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 transaction 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. + +- `query_id`: Query ID has no value in terms of transaction 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 transactions for identification purposes. Therefore, we will set it to 0. + +- `response_destination`: After processing the ownership change transaction there will be extra TON. They will be sent to this address, if specified, otherwise remain on the NFT balance. + +- `custom_payload`: The custom_payload is needed to carry out specific tasks and is not used with 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_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. + +Now let's build the transaction itself: + + + + +```js +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 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. +const forwardPayload = beginCell() + .storeUint(0, 32) + .storeStringTail("Hello, TON!") + .endCell(); + +const transferNftBody = beginCell() + .storeUint(0x5fcc3d14, 32) // Opcode for NFT transfer + .storeUint(0, 64) // query_id + .storeAddress(destinationAddress) // new_owner + .storeAddress(walletAddress) // response_destination for excesses + .storeBit(0) // we do not have custom_payload + .storeCoins(toNano("0.01")) // forward_amount + .storeBit(1) // we store forward_payload as a reference + .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(); +``` + + + + +```go +import ( + "github.com/xssnick/tonutils-go/address" + "github.com/xssnick/tonutils-go/tlb" + "github.com/xssnick/tonutils-go/tvm/cell" +) + +destinationAddress := address.MustParseAddr("put your wallet where you want to send NFT") +walletAddress := address.MustParseAddr("put your wallet which is the owner of NFT") +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. +forwardPayload := cell.BeginCell(). + MustStoreUInt(0, 32). + MustStoreStringSnake("Hello, TON!"). + EndCell() + +transferNftBody := cell.BeginCell(). + MustStoreUInt(0x5fcc3d14, 32). // Opcode for NFT transfer + MustStoreUInt(0, 64). // query_id + MustStoreAddr(destinationAddress). // new_owner + MustStoreAddr(walletAddress). // response_destination for excesses + MustStoreBoolBit(false). // we do not have custom_payload + MustStoreBigCoins(tlb.MustFromTON("0.01").NanoTON()). // forward_amount + MustStoreBoolBit(true). // we store forward_payload as a reference + MustStoreRef(forwardPayload). // store forward_payload as a reference + EndCell() + +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 + MustStoreRef(transferNftBody). + EndCell() +``` + + + + +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 transaction, as is laid out in the previous sections of this tutorial. The correct code needed to complete the transaction is found in the [GitHub repository](/develop/smart-contracts/tutorials/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. + +### Wallet v3 and Wallet v4 Get Methods + +Smart contracts often make use of [GET methods](/develop/smart-contracts/guidelines/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. + +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 transactions 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](/learn/overviews/ton-blockchain#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. + +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: + + + + +```js +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 +}); + +const walletAddress = Address.parse("EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF"); // my wallet address as an example +``` + + + + +```go +import ( + "context" + "github.com/xssnick/tonutils-go/address" + "github.com/xssnick/tonutils-go/liteclient" + "github.com/xssnick/tonutils-go/ton" + "log" +) + +connection := liteclient.NewConnectionPool() +configUrl := "https://ton-blockchain.github.io/global.config.json" +err := connection.AddConnectionsFromConfigUrl(context.Background(), configUrl) +if err != nil { + panic(err) +} +client := ton.NewAPIClient(connection) + +block, err := client.CurrentMasterchainInfo(context.Background()) // get current block, we will need it in requests to LiteServer +if err != nil { + log.Fatalln("CurrentMasterchainInfo err:", err.Error()) + return +} + +walletAddress := address.MustParseAddr("EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF") // my wallet address as an example +``` + + + + +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 +const publicKeyUInt = getResult.stack.readBigNumber(); // read answer that contains uint256 +const publicKey = publicKeyUInt.toString(16); // get hex string from bigint (uint256) +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 +} + +// We have a response as an array with values and should specify the index when reading it +// In the case of get_public_key, we have only one returned value that is stored at 0 index +publicKeyUInt := getResult.MustInt(0) // read answer that contains uint256 +publicKey := publicKeyUInt.Text(16) // get hex string from bigint (uint256) +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://tonapi.io/swagger-ui) (/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. + +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)): + + + + +```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 +``` + + + + +```go +oldWalletAddress := address.MustParseAddr("EQAM7M--HGyfxlErAIUODrxBA3yj5roBeYiTuy6BHgJ3Sx8k") +subscriptionAddress := address.MustParseAddr("EQBTKTis-SWYdupy99ozeOvnEBu8LRrQP_N9qwOTSAy3sQSZ") // subscription plugin address which is already installed on the wallet +``` + + + + +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. + + + + +```js +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 +]); +console.log(getResult.stack.readNumber()); // -1 +``` + + + + +```go +import ( + "math/big" +) + +hash := big.NewInt(0).SetBytes(subscriptionAddress.Data()) +// runGetMethod will automatically identify types of passed values +getResult, err = client.RunGetMethod(context.Background(), block, oldWalletAddress, + "is_plugin_installed", + 0, // pass workchain + hash) // pass plugin address +if err != nil { + log.Fatalln("RunGetMethod err:", err.Error()) + return +} + +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. + +### Contract Deployment via Wallet + +In chapter three, we deployed a wallet. To accomplish this, we initially sent some TON and then a transaction from the wallet to deploy a smart contract. However, this process is not broadly used with external transactions and is often primarily used for wallets only. 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](/develop/smart-contracts/tutorials/wallet#compiling-our-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): + + + + +```js +import { beginCell, Cell } from '@ton/core'; +import { mnemonicToWalletKey } from '@ton/crypto'; + +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 dataCell = beginCell() + .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(); +``` + + + + +```go +import ( + "crypto/ed25519" + "crypto/hmac" + "crypto/sha512" + "encoding/base64" + "github.com/xssnick/tonutils-go/tvm/cell" + "golang.org/x/crypto/pbkdf2" + "strings" +) + +mnemonicArray := strings.Split("put your mnemonic", " ") +// The following three lines will extract the private key using the mnemonic phrase. +// We will not go into cryptographic details. In the library tonutils-go, it is all implemented, +// but it immediately returns the finished object of the wallet with the address and ready-made 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(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 +// 32 is a key len +privateKey := ed25519.NewKeyFromSeed(k) // get private key +publicKey := privateKey.Public().(ed25519.PublicKey) // get public key from private key + +BOCBytes, _ := base64.StdEncoding.DecodeString("te6ccgEBCAEAhgABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQCW8oMI1xgg0x/TH9MfAvgju/Jj7UTQ0x/TH9P/0VEyuvKhUUS68qIE+QFUEFX5EPKj+ACTINdKltMH1AL7AOgwAaTIyx/LH8v/ye1UAATQMAIBSAYHABe7Oc7UTQ0z8x1wv/gAEbjJftRNDXCx+A==") +codeCell, _ := cell.FromBOC(BOCBytes) +dataCell := cell.BeginCell(). + MustStoreUInt(0, 32). // Seqno + MustStoreUInt(3, 32). // Subwallet ID + MustStoreSlice(publicKey, 256). // Public Key + EndCell() + +stateInit := cell.BeginCell(). + MustStoreBoolBit(false). // No split_depth + MustStoreBoolBit(false). // No special + MustStoreBoolBit(true). // We have code + MustStoreRef(codeCell). + MustStoreBoolBit(true). // We have data + MustStoreRef(dataCell). + MustStoreBoolBit(false). // No library + EndCell() +``` + + + + +Next we’ll retrieve the address from our contract and build the InternalMessage. Also we add the "Deploying..." comment to our transaction. + + + + +```js +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(); + +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(); +``` + + + + +```go +import ( + "github.com/xssnick/tonutils-go/address" + "github.com/xssnick/tonutils-go/tlb" + "log" +) + +contractAddress := address.NewAddress(0, 0, stateInit.Hash()) // get the hash of stateInit to get the address of our smart contract in workchain with ID 0 +log.Println("Contract address:", contractAddress.String()) // Output contract address to console + +internalMessageBody := cell.BeginCell(). + MustStoreUInt(0, 32). + MustStoreStringSnake("Deploying..."). + EndCell() + +internalMessage := cell.BeginCell(). + MustStoreUInt(0x10, 6). // no bounce + MustStoreAddr(contractAddress). + MustStoreBigCoins(tlb.MustFromTON("0.01").NanoTON()). + MustStoreUInt(0, 1+4+4+64+32). + MustStoreBoolBit(true). // We have State Init + MustStoreBoolBit(true). // We store State Init as a reference + MustStoreRef(stateInit). // Store State Init as a reference + MustStoreBoolBit(true). // We store Message Body as a reference + MustStoreRef(internalMessageBody). // Store Message Body Init as a reference + EndCell() +``` + + + + +:::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. +::: + +Next, we’ll prepare a message for our wallet and send it: + + + + +```js +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 +}); + +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 seqno = getMethodResult.stack.readNumber(); // get seqno from response + +// transaction for our wallet +const toSign = beginCell() + .storeUint(698983191, 32) // subwallet_id + .storeUint(Math.floor(Date.now() / 1e3) + 60, 32) // Transaction 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(); + +const external = beginCell() + .storeUint(0b10, 2) // indicate that it is an incoming external transaction + .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')); +client.sendFile(external.toBoc()); +``` + + + + +```go +import ( + "context" + "github.com/xssnick/tonutils-go/liteclient" + "github.com/xssnick/tonutils-go/tl" + "github.com/xssnick/tonutils-go/ton" + "time" +) + +connection := liteclient.NewConnectionPool() +configUrl := "https://ton-blockchain.github.io/global.config.json" +err := connection.AddConnectionsFromConfigUrl(context.Background(), configUrl) +if err != nil { + panic(err) +} +client := ton.NewAPIClient(connection) + +block, err := client.CurrentMasterchainInfo(context.Background()) // get current block, we will need it in requests to LiteServer +if err != nil { + log.Fatalln("CurrentMasterchainInfo err:", err.Error()) + return +} + +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 +// 32 is a key len +walletPrivateKey := ed25519.NewKeyFromSeed(k) // get private key +walletAddress := address.MustParseAddr("put your wallet address with which you will deploy") + +getMethodResult, err := client.RunGetMethod(context.Background(), block, walletAddress, "seqno") // run "seqno" GET method from your wallet contract +if err != nil { + log.Fatalln("RunGetMethod err:", err.Error()) + return +} +seqno := getMethodResult.MustInt(0) // get seqno from response + +toSign := cell.BeginCell(). + MustStoreUInt(698983191, 32). // subwallet_id | We consider this further + MustStoreUInt(uint64(time.Now().UTC().Unix()+60), 32). // transaction 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). + MustStoreUInt(3, 8). // store mode of our internal transaction + 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 + +body := cell.BeginCell(). + MustStoreSlice(signature, 512). // store signature + MustStoreBuilder(toSign). // store our message + EndCell() + +externalMessage := cell.BeginCell(). + MustStoreUInt(0b10, 2). // ext_in_msg_info$10 + MustStoreUInt(0, 2). // src -> addr_none + MustStoreAddr(walletAddress). // Destination address + MustStoreCoins(0). // Import Fee + MustStoreBoolBit(false). // No State Init + MustStoreBoolBit(true). // We store Message Body as a reference + MustStoreRef(body). // Store Message Body as a reference + EndCell() + +var resp tl.Serializable +err = client.Client().QueryLiteserver(context.Background(), ton.SendMessage{Body: externalMessage.ToBOCWithFlags(false)}, &resp) + +if err != nil { + log.Fatalln(err.Error()) + return +} +``` + + + + +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 transactions, and be able to use various library types. + +## 🔥 High-Load Wallets + +In some situations, sending a large number of transactions per message may be necessary. As previously mentioned, ordinary wallets support sending up to 4 transactions at a time by storing [a maximum of 4 references](/develop/data-formats/cell-boc#cell) in a single cell. High-load wallets only allow 255 transactions 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 transaction withdrawals 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/highload-wallet-v2-code.fc): + +```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 transaction 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: +> +> ["Bitwise operations" in docs](/develop/func/stdlib/#dict_get) +> +> ["load_dict()" in docs](/develop/func/stdlib/#load_dict) +> +> ["udict_get?()" in docs](/develop/func/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). + +### 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 transaction. +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 transactions very quickly). Therefore, high-load wallets on TON make use of the `query_id`. + +If the same transaction request already exists, the contract won’t accept it, as it has already been processed: + +```func +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 +``` + +This way, we are **being protected from repeat transactions**, which was the role of seqno in ordinary wallets. + +### Sending Transactions + +After the contract has accepted the external message, a loop starts, in which the `slices` stored in the dictionary are taken. These slices store transaction modes and the transactions themselves. Sending new transactions takes place 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 transaction mode + send_raw_message(cs~load_ref(), mode); ;; load transaction itself and send it + } +} until (~ f); ;; if any value was found continue +``` + +> 💡 Useful link: +> +> ["idict_get_next()" in docs](/develop/func/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 transactions, 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 transaction. This structure allows transactions to be sent in the correct sequential order. + +### Removing Expired Queries + +Typically, [smart contracts on TON pay for their own storage](develop/smart-contracts/fees#storage-fee). This means that the amount of data smart contracts can store is limited to prevent high network transaction fees. To allow the system to be more efficient, transactions 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 +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 + } +} until (~ f); +``` + +> 💡 Useful link: +> +> ["udict_delete_get_min()" in docs](/develop/func/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](learn/tvm-instructions/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 + +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: + +```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. + +Next, let’s consider the following line of code: + +```func +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: + +```func +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. + +### 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()); +} +``` + +### GET Methods + +The last thing we have to consider before we dive into wallet deployment and transaction 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. | + +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: + +```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); +} +``` + +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. + +### Deploying High-Load Wallets + +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 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/highload-wallet-v2-code.fc) 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](/develop/smart-contracts/tutorials/wallet#compiling-wallet-code): + + + + +```js +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' }), + } +}); + +if (result.status === 'error') { +console.error(result.message) +return; +} + +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 + +``` + + + + +The result will be the following output in the terminal: + +```text +Code BOC: te6ccgEBCQEA5QABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQHq8oMI1xgg0x/TP/gjqh9TILnyY+1E0NMf0z/T//QE0VNggED0Dm+hMfJgUXO68qIH+QFUEIf5EPKjAvQE0fgAf44WIYAQ9HhvpSCYAtMH1DAB+wCRMuIBs+ZbgyWhyEA0gED0Q4rmMQHIyx8Tyz/L//QAye1UCAAE0DACASAGBwAXvZznaiaGmvmOuF/8AEG+X5dqJoaY+Y6Z/p/5j6AmipEEAgegc30JjJLb/JXdHxQANCCAQPSWb6VsEiCUMFMDud4gkzM2AZJsIeKz + +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: + + + + +```go +import ( + "encoding/base64" + "github.com/xssnick/tonutils-go/tvm/cell" + "log" +) + +base64BOC := "te6ccgEBCQEA5QABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQHq8oMI1xgg0x/TP/gjqh9TILnyY+1E0NMf0z/T//QE0VNggED0Dm+hMfJgUXO68qIH+QFUEIf5EPKjAvQE0fgAf44WIYAQ9HhvpSCYAtMH1DAB+wCRMuIBs+ZbgyWhyEA0gED0Q4rmMQHIyx8Tyz/L//QAye1UCAAE0DACASAGBwAXvZznaiaGmvmOuF/8AEG+X5dqJoaY+Y6Z/p/5j6AmipEEAgegc30JjJLb/JXdHxQANCCAQPSWb6VsEiCUMFMDud4gkzM2AZJsIeKz" // save our base64 encoded output from compiler to variable +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) +} + +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: + + + + +```js +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 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(); + +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(); + +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 +``` + + + + +```go +import ( + "crypto/ed25519" + "crypto/hmac" + "crypto/sha512" + "github.com/xssnick/tonutils-go/address" + "golang.org/x/crypto/pbkdf2" + "strings" +) + +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 +// 32 is a key len +highloadPrivateKey := ed25519.NewKeyFromSeed(k) // get private key +highloadPublicKey := highloadPrivateKey.Public().(ed25519.PublicKey) // get public key from private key + +dataCell := cell.BeginCell(). + MustStoreUInt(698983191, 32). // Subwallet ID + MustStoreUInt(0, 64). // Last cleaned + MustStoreSlice(highloadPublicKey, 256). // Public Key + MustStoreBoolBit(false). // indicate that the dictionary is empty + EndCell() + +stateInit := cell.BeginCell(). + MustStoreBoolBit(false). // No split_depth + MustStoreBoolBit(false). // No special + MustStoreBoolBit(true). // We have code + MustStoreRef(codeCell). + MustStoreBoolBit(true). // We have data + MustStoreRef(dataCell). + MustStoreBoolBit(false). // No library + EndCell() + +contractAddress := address.NewAddress(0, 0, stateInit.Hash()) // get the hash of stateInit to get the address of our smart contract in workchain with ID 0 +log.Println("Contract address:", contractAddress.String()) // Output contract address to console +``` + + + + +:::caution +Everything we have detailed above follows the same steps as the contract [deployment via wallet](/develop/smart-contracts/tutorials/wallet#contract-deployment-via-wallet) section. To better understanding, read the entire [GitHub source code](\(https://github.com/aSpite/wallet-tutorial\)). +::: + +### Sending High-Load Wallet Transactions + +Now let’s program a high-load wallet to send several messages at the same time. For example, let's take 12 transactions per message 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. +::: + +Each message carry its own comment with code and the destination address will be the wallet from which we deployed: + + + + +```js +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'); + +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); +} +``` + + + + +```go +import ( + "fmt" + "github.com/xssnick/tonutils-go/address" + "github.com/xssnick/tonutils-go/tlb" + "github.com/xssnick/tonutils-go/tvm/cell" +) + +var internalMessages []*cell.Cell +wallletAddress := address.MustParseAddr("put your wallet address from which you deployed high-load wallet") + +for i := 0; i < 12; i++ { + comment := fmt.Sprintf("Hello, TON! #%d", i) + internalMessageBody := cell.BeginCell(). + MustStoreUInt(0, 32). + MustStoreBinarySnake([]byte(comment)). + EndCell() + + internalMessage := cell.BeginCell(). + MustStoreUInt(0x18, 6). // bounce + MustStoreAddr(wallletAddress). + MustStoreBigCoins(tlb.MustFromTON("0.001").NanoTON()). + MustStoreUInt(0, 1+4+4+64+32). + MustStoreBoolBit(false). // We do not have State Init + MustStoreBoolBit(true). // We store Message Body as a reference + MustStoreRef(internalMessageBody). // Store Message Body Init as a reference + EndCell() + + messageData := cell.BeginCell(). + MustStoreUInt(3, 8). // transaction mode + MustStoreRef(internalMessage). + EndCell() + + 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: + + + + +```js +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 +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 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 + +const toSign = beginCell() + .storeUint(698983191, 32) // subwallet_id + .storeUint(finalQueryID, 64) + // Here we create our own method that will save the + // transaction mode and a reference to the transaction + .storeDict(dictionary, Dictionary.Keys.Int(16), { + serialize: (src, buidler) => { + buidler.storeUint(3, 8); // save transaction mode, mode = 3 + buidler.storeRef(src); // save transaction as reference + }, + // We won't actually use this, but this method + // will help to read our dictionary that we saved + parse: (src) => { + let cell = beginCell() + .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 +``` + + + + +```go +import ( + "crypto/ed25519" + "crypto/hmac" + "crypto/sha512" + "golang.org/x/crypto/pbkdf2" + "log" + "math/big" + "math/rand" + "strings" + "time" +) + +dictionary := cell.NewDict(16) // create an empty dictionary with the key as a number and the value as a cell +for i := 0; i < len(internalMessages); i++ { + internalMessage := internalMessages[i] // get our message from an array + 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 + +toSign := cell.BeginCell(). + MustStoreUInt(698983191, 32). // subwallet_id + MustStoreUInt(finalQueryID, 64). + MustStoreDict(dictionary) + +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 +// 32 is a key len +highloadPrivateKey := ed25519.NewKeyFromSeed(k) // get private key +highloadWalletAddress := address.MustParseAddr("put your high-load wallet address") + +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 transaction mode after it saves the transaction 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. +::: + +Next we’ll create an external message and send it to the blockchain using the following code: + + + + +```js +import { TonClient } from '@ton/ton'; + +const body = beginCell() + .storeBuffer(signature) // store signature + .storeBuilder(toSign) // store our message + .endCell(); + +const externalMessage = beginCell() + .storeUint(0b10, 2) // indicate that it is an incoming external transaction + .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 +}); + +client.sendFile(externalMessage.toBoc()); +``` + + + + +```go +import ( + "context" + "github.com/xssnick/tonutils-go/liteclient" + "github.com/xssnick/tonutils-go/tl" + "github.com/xssnick/tonutils-go/ton" +) + +body := cell.BeginCell(). + MustStoreSlice(signature, 512). // store signature + MustStoreBuilder(toSign). // store our message + EndCell() + +externalMessage := cell.BeginCell(). + MustStoreUInt(0b10, 2). // ext_in_msg_info$10 + MustStoreUInt(0, 2). // src -> addr_none + MustStoreAddr(highloadWalletAddress). // Destination address + MustStoreCoins(0). // Import Fee + MustStoreBoolBit(false). // No State Init + MustStoreBoolBit(true). // We store Message Body as a reference + MustStoreRef(body). // Store Message Body as a reference + EndCell() + +connection := liteclient.NewConnectionPool() +configUrl := "https://ton-blockchain.github.io/global.config.json" +err := connection.AddConnectionsFromConfigUrl(context.Background(), configUrl) +if err != nil { + panic(err) +} +client := ton.NewAPIClient(connection) + +var resp tl.Serializable +err = client.Client().QueryLiteserver(context.Background(), ton.SendMessage{Body: externalMessage.ToBOCWithFlags(false)}, &resp) + +if err != nil { + log.Fatalln(err.Error()) + return +} +``` + + + + +After this process is completed it is possible to look up our wallet and verify that 12 outgoing transactions 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). + +## 🏁 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 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. + +## 🧩 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 about how to write smart contracts on TON by consulting the following resources: [FunC Overview](https://docs.ton.org/develop/func/overview), [Best Practices](https://docs.ton.org/develop/smart-contracts/guidelines), [Examples of Smart Contracts](https://docs.ton.org/develop/smart-contracts/examples), [FunC Cookbook](https://docs.ton.org/develop/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 + +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). + +## 📖 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/highload-wallet-v2-code.fc) + +- 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: + +- [@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](/develop/smart-contracts/guidelines/internal-messages) + +- [External messages](/develop/smart-contracts/guidelines/external-messages) + +- [Types of Wallet Contracts](/participate/wallets/contracts#wallet-v4) + +- [TL-B](/develop/data-formats/tl-b-language) + +- [Blockchain of Blockchains](https://docs.ton.org/learn/overviews/ton-blockchain) + +External references: + +- [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) From 8e6d544406eb2ec3557aab65fbb1499f9781e774 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:14 +0800 Subject: [PATCH 157/219] New translations docs.md (Chinese Simplified) --- .../current/learn/docs.md | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/learn/docs.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/learn/docs.md b/i18n/zh/docusaurus-plugin-content-docs/current/learn/docs.md new file mode 100644 index 0000000000..7bcbcf0745 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/learn/docs.md @@ -0,0 +1,35 @@ +# Whitepapers + +This section presents the original documentation written by Dr. Nikolai Durov, which fully describes all parts of TON. + +## Original documentation + +:::info +Please note that here and later the code, comments and/or documentation may contain parameters, methods and definitions of “gram”, “nanogram”, etc, that are a legacy of the original TON code, developed by Telegram. Gram cryptocurrency was never issued. The currency of TON is Toncoin and the currency of TON testnet is Test Toncoin. +::: + +- [TON Whitepaper](https://docs.ton.org/ton.pdf) + + TON Whitepaper – a general description of TON(The Open Network) Blockchain. + +- [TON Virtual Machine](https://docs.ton.org/tvm.pdf) + + TON Virtual Machine description(may include outdated information on OP Codes, actual list in [TVM Instruction](https://docs.ton.org/learn/tvm-instructions/tvm-overview) section). + +- [TON Blockchain](https://docs.ton.org/tblkch.pdf) + + Telegram Open Network Blockchain description (may include outdated information). + +- [Catchain Consensus Protocol](https://docs.ton.org/catchain.pdf) + + Description of the Byzantine Fault Tolerant (BFT) Consensus protocol employed by TON Blockchain while creating new blocks. + +- [Fift Documentation](https://docs.ton.org/fiftbase.pdf) + + Description of Fift language and how to use it in TON. + +## Translations + +- **\[RU]** [korolyow/ton_docs_ru](https://github.com/Korolyow/TON_docs_ru) — TON Whitepapers in Russian. (_this version made by community, TON Foundation can't guarantee quality of translation_) +- **\[Traditional CN]** [awesome-doge/the-open-network-whitepaper](https://github.com/awesome-doge/TON_Paper/blob/main/zh_ton.pdf) — TON Whitepapers in Traditional Chinese. (_made by community, TON Foundation can't guarantee quality of translation_) +- **\[Simplified CN]** [kojhliang/Ton_White_Paper_SC](https://github.com/kojhliang/Ton_White_Paper_SC/blob/main/Ton%E5%8C%BA%E5%9D%97%E9%93%BE%E7%99%BD%E7%9A%AE%E4%B9%A6_%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87%E7%89%88.pdf) — TON Whitepapers in Simplified Chinese. (_made by community, TON Foundation can't guarantee quality of translation_) From af6f72fee1f6c528b49047bfe0ba1d261cef0354 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:16 +0800 Subject: [PATCH 158/219] New translations glossary.md (Chinese Simplified) --- .../current/learn/glossary.md | 509 ++++++++++++++++++ 1 file changed, 509 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/learn/glossary.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/learn/glossary.md b/i18n/zh/docusaurus-plugin-content-docs/current/learn/glossary.md new file mode 100644 index 0000000000..5b6dfde55b --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/learn/glossary.md @@ -0,0 +1,509 @@ +# Glossary + +## Introduction + +In this glossary, you can find any TON- and crypto-related buzzwords. + +____________ + +## A + +### Airdrop + +**Airdrop** — a free distribution of tokens among specific participants. + +### Altcoin + +**Altcoin** — all cryptocurrencies, except Bitcoin, are called altcoins. + +### AMA + +**AMA** — an online Q&A called “ask me anything” where project leaders answer questions from the community about a product or service. + +### API + +**API** — Application Programming Interface, a mechanism that allows two programs to interact with each other through a series of protocols. + +### APY + +**APY** — Annual Percentage Yield, a calculated yearly interest rate for a given asset. + +____________ + +## B + +### Bearish + +**Bearish** — the term “bearish” is used when the price of an asset has declined due to investors selling. (The term is often used to describe the overall market sentiment.) + +### Binance + +**Binance** — the world’s biggest cryptocurrency exchange by daily trading volume. + +### Bitcoin (BTC) + +**Bitcoin (BTC)** — the preeminent cryptocurrency and the first decentralized network with open-source code, which laid the groundwork for the proliferation of blockchain technology. + +### Blockchain + +**Blockchain** — a distributed ledger of data in the form of a chain of blocks recording transaction information for every event on the network. + +### Bloodbath + +**Bloodbath** — a colloquial term often used to describe a severe and sustained market crash, which results in multiple deep red bars on price charts. + +### BoC + +**BoC** — Bag of Cells. Usually, used in code. + +### BoF + +**BoF** — Bag of Files. Usually, used in code. + +### Bot + +**Bot** — a program written for two ecosystems to interact with each other — e.g., The Open Network and the Telegram messenger. On Telegram, bots are accounts in the messenger operated by software. + +### Bridge + +**Bridge** — a program connecting various blockchains to transfer tokens and data from one network to another. TON Bridge is available at this [link](https://ton.org/bridge/). + +### Bullish + +**Bullish** — the term “bullish” is used to describe an asset whose value is appreciating. (“Bullish” is the opposite of “bearish” — i.e. when the market’s overall value is increasing.) + +### Burning + +**Burning** — the act of burning or removing a number of tokens from the circulating and total supply, which often results in increased demand. + +___________ + +## C + +### CEX + +**CEX** — a centralized cryptocurrency exchange to trade tokens. + +### CMC + +**CMC** — CoinMarketCap, a crypto information aggregator that closely follows changes in token prices and market capitalization. + +### Coinbase + +**Coinbase** — the biggest crypto exchange in the United States. + +### Cryptobot + +**Cryptobot** — a peer-to-peer (P2P) bot service for buying, trading, and selling Toncoin and other cryptocurrencies without Know Your Customer (KYC) verification. + +### Custodial + +**Custodial** — a type of crypto wallet where a third party stores cryptocurrencies, and not their true owner. + +___________ + +## D + +### DApps + +**DApps** — decentralized applications made for blockchains supported by a group of nodes instead of a centralized server. + +### DCA + +**DCA** — dollar-cost averaging, an investment strategy whereby investors buy crypto assets at a low yet fixed price to reduce exposure to risks. + +### Decentralization + +**Decentralization** — one of the main tenets behind TON and other blockchains. Without decentralization, Web3 would be impossible to achieve; therefore, every element of the TON Ecosystem revolves around maximizing decentralization. + +### DeFi + +**DeFi** — the decentralized analog to traditional finance, it includes accessible financial services and applications based on smart contracts. + +### DEX + +**DEX** — a decentralized exchange (DEX) where users can trade cryptocurrencies without any intermediaries. The online entity needed to guarantee safe transactions is the blockchain itself. + +### Diamond hands + +**Diamond hands** — a colloquial term describing an investor who has no intention of selling their assets regardless of the state of the market — even if there’s a crash or the market is bearish. + +### DNS + +**DNS** — Domain Name System, a technology that allows users to make transactions between human-readable domain names (ton.org) and machine-readable IP addresses (192.0.2.44). + +### Dolphin + +**Dolphin** — an investor who has low-level capital but has an influence on the community. + +### Donate + +**Donate** — a bot service on Telegram through which people can either donate money or content creators can monetize their channels and services on Telegram, with a payment option in Toncoin. + +### Dump + +**Dump** — manipulating the increase in value of a token or cryptocurrency and then cashing out. + +### Durov + +**Durov** — Pavel Durov, a Russian entrepreneur who is famous for having founded the VK social network and Telegram messenger. Nikolai Durov is Pavel's brother who participated in developing VK, Telegram and also TON. + +### DYOR + +**DYOR** — _Do Your Own Research_, the process by which you do research on a project, company or cryptocurrency before deciding to invest. + +___________ + +## E + +### EVM + +**EVM** — Ethereum Virtual Machine, a machine behaving like a decentralized computer, it computes the state of the Ethereum blockchain after each new block and executes smart contracts. + +### Exchange + +**Exchange** — a place for trading and using other market instruments. + +___________ + +## F + +### Farming + +**Farming** — lending your crypto assets to receive rewards. + +### Fiat + +**Fiat** — regular money issued by central banks or financial authorities. + +### FOMO + +**FOMO** — “fear of missing out,” a psychological state that consumes some investors when the idea of losing potential gains from an opportunity is present. It usually appears during a bull market and when traders don’t do their due diligence analyzing a particular project. + +### Fungible tokens + +**Fungible tokens** — cryptocurrencies that carry the same value as any other token of the same kind at any given moment. + +### FUD + +**FUD** — “Fear, uncertainty and doubt,” market sentiments based on many factors. + +### Full Node + +**Full Node** — a computer on TON Blockchain that synchronizes and copies the entire TON Blockchain. + +### FunC + +**FunC** — the smart contract language on TON. + +___________ + +## G + +### Gas + +**Gas** — the fee paid for transactions on the blockchain. + +### GitHub + +**GitHub** — a platform where developers gather to create base code for programs. + +___________ + +## H + +### Hackathon + +**Hackathon** — a gathering of programmers to develop software, programs, applications, etc. + +### Hash + +**Hash** — information about transaction data, created by a hashing algorithm. + +### Hash rate + +**Hash rate** — the indication of how much computational power is being used on a network for crypto mining. + +### Hold + +**Hold** — saving — i.e. not selling — an asset or assets from your portfolio. + +___________ + +## I + +### ICO + +**ICO** — initial coin offering, a method for crypto projects to attract capital in the early stages. + +### IDO + +**IDO** — initial decentralized exchange offering, another method of attracting capital when launching a cryptocurrency or token on a decentralized exchange. + +### Inflation + +**Inflation** — the process when the value of a currency — e.g., U.S. dollar or the euro — decreases. Toncoin (and other cryptocurrencies) are emitted with a high degree of transparency and a predictable issuance, which has deflationary properties. + +___________ + +## K + +### KYC + +**KYС** — _Know Your Customer_, the process by which a user verifies their identity when creating an account for a crypto service. + +___________ + +## L + +### Launchpad + +**Launchpad** — a platform for crypto startups that brings investors and projects together. The main launchpad in the TON Ecosystem is Tonstarter. + +### Liquidity pool + +**Liquidity pool** — grouping together crypto assets and freezing them in a smart contract. Liquidity pools are used for decentralized trading, loans, and other endeavors. + +__________ + +## M + +### Mainnet + +**Mainnet** — the main network of a blockchain. + +### Market cap (capitalization) + +**Market cap (capitalization)** — the total value of a cryptocurrency’s combined number of tokens. + +### Masterchain + +**Masterchain** — in a multi-layered blockchain, the masterchain is the most important. For TON, the masterchain is the main chain of the network. When operations happen on the blockchain, they do so on the masterchain. + +### Metaverse + +**Metaverse** — a digital universe similar to a videogame where users create avatars and interact with the digital representations of other people or users. + +### Moon + +**Moon** — a crypto term that describes a crypto asset’s vertical trajectory on a price chart — i.e. it quickly gains value. + +__________ + +## N + +### NFA + +**NFA** — not financial advice, this acronym is used as a disclaimer to avoid liability or responsibility when investors discuss cryptocurrencies or projects with other people. + +### NFT + +**NFT** — non-fungible token, a unique digital token on a blockchain that cannot be duplicated or minted more than once. + +### Nominator + +**Nominator** — those who provide financial resources to validators so the latter can confirm blocks on TON Blockchain. + +### Non-custodial + +**Non-custodial** — a kind of crypto wallet that gives full control over assets to the owner/user. + +__________ + +## O + +### Off-ramp + +**Off-ramp** — ways to convert cryptocurrencies into fiat money. + +### On-ramp + +**On-ramp** — ways to convert (buy) cryptocurrency by spending fiat money. + +### Onion routing + +**Onion routing** — a technology similar to TOR that allows anonymous interactions on a network. All messages are encrypted in various layers akin to an onion. TON Proxy applies such a technique. + +__________ + +## P + +### Paper hands + +**Paper hands** — an investor who’s inclined to panic-sell — an inexperienced investor. + +### Proof-of-stake + +**Proof-of-stake** — a consensus mechanism to process transactions in new blocks on the blockchain. + +### Proof-of-work + +**Proof-of-work** — a consensus algorithm where one party proves to another that a specific amount of computational work was spent. By expending a little energy, a party can verify this. + +### Proxy + +**Proxy** — a service on a computer network that allows clients to install indirect network connections with other network services. + +### Pump + +**Pump** — artificially inflating the price of a cryptocurrency or asset. + +### P2P + +**P2P** — _peer-to-peer_, transactions among users without the help of a third party or intermediary. + +__________ + +## R + +### Roadmap + +**Roadmap** — a project’s strategic plan that displays when its products, services, updates, etc. will be released. + +### ROI + +**ROI** — _return on investment_, the profits made from investments. + +_________ + +## S + +### SBT + +**SBT** — _Soulbound token_, an NFT that can never be transferred because it contains information about its owner and their accomplishments. + +### Scalability + +**Scalability** — the ability of a blockchain network to process complex transactions as well as a large number of them. + +### SEC + +**SEC** — Securities and Exchange Commission, a financial regulator in the United States. + +### Shard + +**Shard** — a mechanism that helps a blockchain network to scale by breaking into smaller blockchains to relieve network congestion — something which TON Blockchain does. + +### Smart contract + +**Smart contract** — self-executing code that oversees and enables operations with the help of mathematical algorithms and without human intervention. + +### Spot trading + +**Spot trading** — trading a financial asset for money. + +### Stablecoin + +**Stablecoin** — a cryptocurrency whose value is stable (usually pegged to a fiat currency) and does not crash. + +### Staking + +**Staking** — a way for users to earn a passive income by storing coins or tokens in a proof-of-stake algorithm, which, in turn, ensures the blockchain runs smoothly. For this, they earn rewards as an incentive. + +### Swap + +**Swap** — the exchange of two financial assets — e.g. Toncoin for USDT. + +________ + +## T + +### TEP + +**TEP** — [TON Enhancement Proposals](https://github.com/ton-blockchain/TEPs), a standard set of ways to interact with various parts of the TON ecosystem. + +### Testnet + +**Testnet** — a network for testing projects or services before launching on the mainnet. + +### Ticker + +**Ticker** — the short form of a cryptocurrency, asset, or token on exchanges, trading services, or other DeFi solutions — e.g. TON for Toncoin. + +### The Merge + +**The Merge** — the transition process of Ethereum switching from proof-of-work to proof-of-stake. + +### Token + +**Token** — a form of digital asset; it can have multiple functions. + +### Tokenomics + +**Tokenomics** — the economic plan and distribution strategy of a cryptocurrency (or token). + +### To the moon + +**To the moon** — a colloquial phrase used when people create FOMO. It refers to hopefuls wanting the value of a cryptocurrency rapidly gaining a lot of value — hence its trajectory to the moon. + +### Toncoin + +**Toncoin** — the native cryptocurrency of the TON Ecosystem, which is used to develop services and pay for fees and services. It can be bought, sold, and traded. + +### Trading + +**Trading** — buying and selling cryptocurrencies with the goal of making a profit. + +### TVL + +**TVL** (Total Value Locked) — Тotal value locked represents the number of assets currently being staked in a specific protocol. + +### TVM + +**TVM** — Ton Virtual Machine, a machine behaving like a decentralized computer, it computes the state of the Ton blockchain after each new block and executes smart contracts. + +___________ + +## V + +### Validator + +**Validator** — those who verify new blocks on TON Blockchain. + +___________ + +## W + +### WAGMI + +**WAGMI** — “we’re all gonna make it,” a sentence often used in the crypto community to express the aspirations of becoming rich one day by investing in cryptocurrencies. + +### Wallet + +**Wallet** — software that stores cryptocurrencies through a system of private keys needed to buy or sell cryptocurrencies and tokens. It is also a bot in the TON ecosystem for buying and selling Toncoin. + +### Web3 + +**Web3** — a new generation of the internet based on blockchain technology that includes decentralization and tokenomics. + +### Whale + +**Whale** — an investor who owns a large number of cryptocurrencies and tokens. + +### Whitelist + +**Whitelist** — a list awarding people special perks. + +### White paper + +**White paper** — the main document of a project written by its developers. It explains the technology and the project’s goals. + +### Watchlist + +**Wаtchlist** — a customizable list of cryptocurrencies whose price action an investor wishes to follow. + +### Workchain + +**Workchain** — secondary chains that connect to the masterchain. They can contain a massive number of different connected chains that have their own consensus rules. They can also contain address and transaction information and virtual machines for smart contracts. Additionally, they can be compatible with the masterchain and interact with one another. + +___________ + +## Y + +### Yield farming + +**Yield farming** — lending or placing cryptocurrencies or tokens in a smart contract to earn rewards in the form of transaction fees. + +### Yolo + +**Yolo** — “you only live once,” a slang acronym used as a call to live life to the fullest without taking into account the risk of the given endeavor. From 69e59ae90bfe4117c18d8523f59517367f191ce8 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:16 +0800 Subject: [PATCH 159/219] New translations introduction.mdx (Chinese Simplified) --- .../current/learn/introduction.mdx | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/learn/introduction.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/learn/introduction.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/learn/introduction.mdx new file mode 100644 index 0000000000..712e80d484 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/learn/introduction.mdx @@ -0,0 +1,44 @@ +import Player from '@site/src/components/player' +import Button from '@site/src/components/button' + +# The Open Network + +**The Open Network (TON)** is a decentralized and open internet platform made up of several components. These include: TON Blockchain, TON DNS, TON Storage, and TON Sites. TON Blockchain is the core protocol that connects TON’s underlying infrastructure together to form the greater TON Ecosystem. + +TON is focused on achieving widespread cross-chain interoperability, while operating in a highly scalable secure framework. TON is designed to process millions of transactions per second (TPS), with the goal of eventually reaching hundreds of millions of users moving forward. + +**TON Blockchain** is designed as a distributed supercomputer, or “_superserver_,” intended to provide a variety of products and services to contribute to the development of the decentralized vision for the new internet. + +- Discover which services TON provides for its users by reviewing this section: [Participate in TON](/participate/) +- To learn more about the technical aspects of TON Blockchain review the [Blockchain of Blockchains](/learn/overviews/ton-blockchain) +- Learn more about the development of all things TON by reviewing this section: [Getting Started](/develop/overview) + +## Bird's Eye Overview + +To understand the true vision for the decentralized internet and how TON contributes to this inevitability, consider taking a deep dive into the video below: + + + +## TON Blockchain Course + +We're proud to present the **TON Blockchain Course**, which is a comprehensive guide to the TON Blockchain. The course is designed for developers who want to learn how to create smart contracts and decentralized applications on the TON Blockchain. + +It consists of **9 modules** and covers the basics of the TON Blockchain, the FunC programming language, and the TON Virtual Machine (TVM). + + + +## New to Blockchain? + +If you're new to blockchain and don’t understand what makes the technology so revolutionary — consider taking a deep dive into these important resources: + +- [What is Blockchain? What is a Smart Contract? What is Gas?](https://blog.ton.org/what_is_blockchain) +- [How a Blockchain Can Help You on a Deserted Island](https://talkol.medium.com/why-decentralized-consensus-blockchain-is-good-for-business-5ff263468210) +- [\[YouTube\] Crypto Networks and Why They Matter](https://youtu.be/2wxtiNgXBaU) + +## TON’s Relationship With Ethereum + +For those familiar with Ethereum development, we wrote two introductory articles to help you understand what sets TON apart in this regard: + +- [Six unique aspects of TON Blockchain that will surprise Solidity developers](https://blog.ton.org/six-unique-aspects-of-ton-blockchain-that-will-surprise-solidity-developers) +- [It’s time to try something new: Asynchronous smart contracts](https://telegra.ph/Its-time-to-try-something-new-Asynchronous-smart-contracts-03-25) +- [Comparison of Blockchains](https://ton.org/comparison_of_blockchains.pdf) From 5742f9517352713398a5d921e9e7b05b85c4f4a9 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:17 +0800 Subject: [PATCH 160/219] New translations adnl.md (Chinese Simplified) --- .../current/learn/networking/adnl.md | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/adnl.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/adnl.md b/i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/adnl.md new file mode 100644 index 0000000000..2e9286b302 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/adnl.md @@ -0,0 +1,40 @@ +# ADNL Protocol + +Implementation: + +- https://github.com/ton-blockchain/ton/tree/master/adnl + +## Overview + +The cornerstone of TON is the Abstract Datagram Network Layer (ADNL). + +This is an overlay, peer-to-peer, unreliable (small-size) datagram protocol running on top of a **UDP** in **IPv4** (in the future, IPv6), with an optional **TCP fallback** if UDP is not available. + +## ADNL address + +Each participant has a 256-bit ADNL Address. + +The ADNL Protocol allows you to send (unreliable) and receive datagrams using only ADNL Addresses. IP Addresses and Ports are hidden by the ADNL Protocol. + +An ADNL Address is essentially equal to a 256-bit ECC public key. This public key can be arbitrarily generated, thus creating as many different network identities as the node needs. +However, one must know the corresponding private key in order to receive (and decrypt) messages intended for the recipient address. + +In reality, the ADNL Address is not the public key itself; instead, it is a 256-bit SHA256 hash of a serialized TL-object that can describe several types of public keys and addresses depending on its constructor. + +## Encryption & security + +Normally each datagram sent is signed by the sender and encrypted so that only the recipient can decrypt the message and verify its integrity by the signature. + +## Neighbor tables + +Normally, a TON ADNL node will have some “neighbor table”, which contains information about other known nodes, such as their abstract addresses, public keys, IP Addresses and UDP Ports. Over time, it will gradually +extend this table using information gathered from these known nodes. This new information can be in the form of answers to special queries or sometimes the removal of obsolete records. + +ADNL allows you to set up point-to-point channels and tunnels (a chain of proxies). + +A TCP-like stream protocol can be built over ADNL. + +## What's next? + +- Read more about ADNL in the [Low-Level ADNL article](/learn/networking/low-level-adnl) +- Chapter 3.1 of the [TON Whitepaper](https://docs.ton.org/ton.pdf). From 982677c1172bc07f0e02240e3cb0e12c471e01ca Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:18 +0800 Subject: [PATCH 161/219] New translations low-level-adnl.md (Chinese Simplified) --- .../learn/networking/low-level-adnl.md | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/low-level-adnl.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/low-level-adnl.md b/i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/low-level-adnl.md new file mode 100644 index 0000000000..de7a083edc --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/low-level-adnl.md @@ -0,0 +1,110 @@ +# Low-Level ADNL + +Abstract Datagram Network Layer (ADNL) is the core protocol of TON, which helps network peers to communicate with each other. + +## Peer identity + +Each peer must have at least one identity, it is possible but not necessary to use multiple. Each identity is a keypair, which is used to perform the Diffie-Hellman between peers. An abstract network address is derived from the public key in such way: `address = SHA-256(type_id || public_key)`. Note that type_id must be serialized as little-endian uint32. + +## Public-key cryptosystems list + +| type_id | cryptosystem | +| ---------------------------- | ------------------- | +| 0x4813b4c6 | ed255191 | + +_1. To perform x25519, the keypair must be generated in x25519 format. However, the public key is transmitted over the network in ed25519 format, so you have to convert the public key from x25519 to ed25519, examples of such conversions can be found [here](https://github.com/tonstack/adnl-rs/blob/master/src/integrations/dalek.rs#L10) for Rust and [here](https://github.com/andreypfau/curve25519-kotlin/blob/f008dbc2c0ebc3ed6ca5d3251ffb7cf48edc91e2/src/commonMain/kotlin/curve25519/MontgomeryPoint.kt#L39) for Kotlin._ + +## Client-server protocol (ADNL over TCP) + +The client connects to the server using TCP and sends an ADNL handshake packet, which contains a server abstract address, a client public key and encrypted AES-CTR session parameters, which are determined by the client. + +### Handshake + +First, the client must perform a key agreement protocol (for example, x25519) using their private key and server public key, taking into account the server key's `type_id`. As a result, the client will gain `secret`, which is used to encrypt session keys in future steps. + +Then, the client has to generate AES-CTR session parameters, a 16-byte nonce and 32-byte key, both for TX (client->server) and RX (server->client) directions and serialize it into a 160-byte buffer as follows: + +| Parameter | Size | +| ----------------------------- | -------- | +| rx_key | 32 bytes | +| tx_key | 32 bytes | +| rx_nonce | 16 bytes | +| tx_nonce | 16 bytes | +| padding | 64 bytes | + +The purpose of padding is unknown, it is not used by server implementations. It is recommended to fill the whole 160-byte buffer with random bytes, otherwise an attacker may perform an active MitM attack using compromised AES-CTR session parameters. + +The next step is to encrypt session parameters using `secret` via the key agreement protocol above. To do that, AES-256 must be initialized in CTR mode with a 128-bit big-endian counter using a (key, nonce) pair which is computed as such (`aes_params` is a 160-byte buffer which was built above): + +```cpp +hash = SHA-256(aes_params) +key = secret[0..16] || hash[16..32] +nonce = hash[0..4] || secret[20..32] +``` + +After the encryption of `aes_params` which is noted as `E(aes_params)`, AES should be removed because it is not needed anymore. + +Now we are ready to serialize all that information to the 256-bytes handshake packet and send it to the server: + +| Parameter | Size | Notes | +| ----------------------------------------------------------- | --------- | -------------------------------------------------------------- | +| receiver_address | 32 bytes | Server peer identity as described in the corresponding section | +| sender_public | 32 bytes | Client public key | +| SHA-256(aes_params) | 32 bytes | Integrity proof of session parameters | +| E(aes_params) | 160 bytes | Encrypted session parameters | + +The server must decrypt session parameters using a secret, derived from the key agreement protocol in the same way as the client. Then the server must perform the following checks to confirm the protocol's security properties: + +1. The server must have the corresponding private key for `receiver_address`, otherwise there is no way to perform the key agreement protocol. +2. `SHA-256(aes_params) == SHA-256(D(E(aes_params)))`, otherwise the key agreement protocol has failed and `secret` is not equal on both sides. + +If any of these checks fail, server will immediately drop the connection without responding to the client. If all checks pass, the server must issue an empty datagram (see the Datagram section) to the client in order to prove that it owns the private key for the specified `receiver_address`. + +### Datagram + +Both the client and server must initialize two AES-CTR instances each, for both TX and RX directions. AES-256 must be used in CTR mode with a 128-bit big-endian counter. Each AES instance is initialized using a (key, nonce) pair belonging to it, which can be taken from `aes_params` in the handshake. + +To send a datagram, a peer (the client or server) must build the following structure, encrypt it and send to the other peer: + +| Parameter | Size | Notes | +| --------- | ------------------------------- | ------------------------------------------------------ | +| length | 4 bytes (LE) | Length of the whole datagram, excluding `length` field | +| nonce | 32 bytes | Random value | +| buffer | `length - 64` bytes | Actual data to be sent to the other side | +| hash | 32 bytes | `SHA-256(nonce \\|\\| buffer)` to ensure integrity | + +The whole structure must be encrypted using the corresponding AES instance (TX for client -> server, RX for server -> client). + +The receiving peer must fetch the first 4 bytes, decrypt it into the `length` field and read exactly `length` bytes to get the full datagram. The receiving peer may start to decrypt and process `buffer` earlier, but it must take into account that it may be corrupted, intentionally or occasionally. Datagram `hash` must be checked to ensure the integrity of the `buffer`. In case of failure, no new datagrams can be issued and the connection must be dropped. + +The first datagram in the session always goes from the server to the client after a handshake packet was successfully accepted by the server and it's actual buffer is empty. The client should decrypt it and disconnect from the server in case of failure, because it means that the server has not followed the protocol properly and the actual session keys differs on the server and client side. + +### Communication details + +If you want to dive into communication details, you could check article [ADNL TCP - Liteserver](/develop/network/adnl-tcp) to see some examples. + +### Security considerations + +#### Handshake padding + +It is unknown why the initial TON team decided to include this field into the handshake. `aes_params` integrity is protected by a SHA-256 hash and confidentiality is protected by the key derived from the `secret` parameter. Probably, it was intended to migrate from AES-CTR at some point. To do this, specification may be extended to include a special magic value in `aes_params`, which will signal that the peer is ready to use the updated primitives. The response to such a handshake may be decrypted twice, with new and old schemes, to clarify which scheme the other peer is actually using. + +#### Session parameters encryption key derivation process + +If an encryption key is derived only from the `secret` parameter, it will be static because the secret is static. To derive a new encryption key for each session, developers also use `SHA-256(aes_params)`, which is random if `aes_params` is random. However, the actual key derivation algorithm with the concatenation of different subarrays is considered harmful. + +#### Datagram nonce + +It is not obvious why the `nonce` field in the datagram is present because, even without it, any two ciphertexts will differ because of the session-bounded keys for AES and encryption in CTR mode. However, the following attack can be performed in the case of an absent or predictable nonce. CTR encryption mode turns block ciphers, such as AES, into stream ciphers to make it possible to perform a bit-flipping attack. If the attacker knows the plaintext which belongs to encrypted datagram, they can obtain a pure keystream, XOR it with their own plaintext and efficiently replace the message which was sent by peer. The buffer integrity is protected by a SHA-256 hash, but an attacker can replace it too because having knowledge of a full plaintext means having knowledge of its hash. The nonce field is present to prevent such an attack, so no attacker can replace the SHA-256 without having knowledge of the nonce. + +## P2P protocol (ADNL over UDP) + +Detailed description can be found in article [ADNL UDP - Internode](/develop/network/adnl-udp). + +## References + +- [The Open Network, p. 80](https://ton.org/ton.pdf) +- [ADNL implementation in TON](https://github.com/ton-blockchain/ton/tree/master/adnl) + +_Thanks to the [hacker-volodya](https://github.com/hacker-volodya) for contributing to the community!_\ +_Here a [link to the original article](https://github.com/tonstack/ton-docs/tree/main/ADNL) on GitHub._ From 3bb6af6cc22df4dd3379c55a5ecc0bf034102c21 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:19 +0800 Subject: [PATCH 162/219] New translations overlay-subnetworks.md (Chinese Simplified) --- .../learn/networking/overlay-subnetworks.md | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/overlay-subnetworks.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/overlay-subnetworks.md b/i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/overlay-subnetworks.md new file mode 100644 index 0000000000..84009e8e71 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/overlay-subnetworks.md @@ -0,0 +1,33 @@ +# Overlay Subnetworks + +Implementation: + +- https://github.com/ton-blockchain/ton/tree/master/overlay + +## Overview + +In a multi-blockchain system like TON, even full nodes would usually be interested in obtaining updates (i.e., new blocks) only around +a few shardchains. To this end, a special overlay subnetwork has been built +inside the TON Network, on top of the ADNL Protocol, +for each shardchain. + +Also, overlay subnetworks are used for the operation of TON Storage, TON Proxy and so on. + +## ADNL vs Overlay networks + +In contrast to ADNL, the TON overlay networks usually do not support +sending datagrams to other arbitrary nodes. Instead, some “semi-permanent +links” are established between certain nodes (called “neighbors” with respect to +the overlay network under consideration) and messages are usually forwarded +along these links (i.e. from a node to one of its neighbors). + +Each overlay subnetwork has a 256-bit network identifier usually equal +to a SHA256 of the description of the overlay network—a TL-serialized object. + +Overlay subnetworks can be public or private. + +Overlay subnetworks work according to a special [gossip](https://en.wikipedia.org/wiki/Gossip_protocol) protocol. + +:::info +Read more about overlays in [Overlay subnetworks](/develop/network/overlay) article, or in Chapter 3.3 of the [TON Whitepaper](https://ton.org/docs/ton.pdf). +::: From 83a0523db28b22e8a80da974282b3e6b7d0d30c2 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:20 +0800 Subject: [PATCH 163/219] New translations overview.md (Chinese Simplified) --- .../current/learn/networking/overview.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/overview.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/overview.md b/i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/overview.md new file mode 100644 index 0000000000..1636cd3b10 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/overview.md @@ -0,0 +1,19 @@ +# TON Networking + +The TON Project uses its own peer-to-peer network protocols. + +- **TON Blockchain uses these protocols** to propagate new blocks, send and collect transaction candidates and so on. + + While the networking demands of single-blockchain projects, such as Bitcoin or Ethereum, can be met quite easily (one essentially needs to construct + a peer-to-peer overlay network and then propagate all new blocks and + transaction candidates via a [gossip](https://en.wikipedia.org/wiki/Gossip_protocol) protocol), whereas multi-blockchain projects, such + as TON, are much more demanding (e.g. one must be able to + subscribe to updates of only some shardchains, not necessarily all of them). + +- **TON Ecosystem services (e.g. TON Proxy, TON Sites, TON Storage) run on these protocols.** + + Once the more sophisticated network protocols needed + to support TON Blockchain are in place, it turns out that they can easily + be used for purposes not necessarily related to the immediate demands of the + blockchain itself, thus providing more possibilities and flexibility for creating + new services in the TON Ecosystem. From 67973642dfb63cd9843ebfcb5b0b3553ac3393c9 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:21 +0800 Subject: [PATCH 164/219] New translations rldp.md (Chinese Simplified) --- .../current/learn/networking/rldp.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/rldp.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/rldp.md b/i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/rldp.md new file mode 100644 index 0000000000..fd70ac286c --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/rldp.md @@ -0,0 +1,18 @@ +# RLDP Protocol + +Implementation: + +- https://github.com/ton-blockchain/ton/tree/master/rldp +- https://github.com/ton-blockchain/ton/tree/master/rldp2 +- https://github.com/ton-blockchain/ton/tree/master/rldp-http-proxy + +## Overview + +A reliable arbitrary-size datagram protocol built upon the ADNL, called RLDP, +is used instead of a TCP-like protocol. This reliable datagram protocol can +be employed, for instance, to send RPC queries to remote hosts and receive +answers from them. + +:::info +Detailed description with examples can be found in [RLDP](/develop/network/rldp) article of `develop` section. +::: From 42ba4b2162dd4535aa25cda862c57e8fc9318c0a Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:21 +0800 Subject: [PATCH 165/219] New translations ton-dht.md (Chinese Simplified) --- .../current/learn/networking/ton-dht.md | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/ton-dht.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/ton-dht.md b/i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/ton-dht.md new file mode 100644 index 0000000000..70f42e0311 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/learn/networking/ton-dht.md @@ -0,0 +1,69 @@ +# TON DHT Service + +Implementation: + +- https://github.com/ton-blockchain/ton/tree/master/dht +- https://github.com/ton-blockchain/ton/tree/master/dht-server + +## Overview + +The Kademlia-like Distributed Hash Table (DHT) plays a crucial role in the networking part of the TON project and is used to locate other nodes in the network. + +The keys of the TON DHT are simply 256-bit integers. In most cases, they are computed as a SHA256 of a TL-serialized object. + +The values assigned to these 256-bit keys are essentially arbitrary byte strings of limited length. The interpretation of +such byte strings is determined by the preimage of the corresponding key; it +is usually known both by the node that looks up the key and by the node +that stores the key. + +In the simplest case, the key represents an ADNL Address of some node and the value can be its IP address and port. + +The key-value mapping of the TON DHT is kept on the DHT nodes. + +## DHT nodes + +Each DHT Node has a 256-bit DHT address. Unlike an ADNL Address, a DHT Address should not change too often, otherwise other nodes would be unable to locate the keys they are looking for. + +It is expected that the value of key `K` will be stored on `S` Kademlia-nearest nodes to `K`. + +Kademlia distance = 256-bit key `XOR` 256-bit DHT node address (it has nothing to do with geographic location). + +`S` is a small parameter, for example `S = 7`, which is needed to improve reliability of +the DHT (if we would keep the key only on one node, the nearest one to `K`, +the value of that key would be lost if that single node goes offline). + +## Kademlia routing table + +Any node participating in a DHT usually maintains a Kademlia routing table. + +It consists of 256 buckets, numbered from 0 to 255. The `i`-th +bucket will contain information about some known nodes (a fixed number +of the “best” nodes and maybe some extra candidates) that lie at a Kademlia +distance from `2^i` to `2^(i+1) − 1` from the node’s address `a`. + +This information includes their DHT Addresses, IP Addresses and UDP Ports and +some availability information such as the time and the delay of the last ping. + +When a Kademlia node learns about any other Kademlia node as a result +of some query, it places it into a suitable bucket of its routing table, first +as a candidate. Then, if some of the “best” nodes in that bucket fail (e.g., do +not respond to ping queries for a long time), they can be replaced by some +of these candidates. In this way the Kademlia routing table stays populated. + +## Key-value pairs + +Key-value pairs can be added and updated in the TON DHT. + +The “update rules” can differ. In some cases, they simply +permit replacing the old value with the new value, provided that the new value +is signed by the owner/creator (the signature must be kept as part of the value, to +be checked later by any other nodes after they obtain the value of this key). +In other cases, the old value somehow affects the new value. For example, it +can contain a sequence number and the old value is overwritten only if the +new sequence number is larger (to prevent replay attacks). + +TON DHT is not only used to store the IP Addresses of ADNL Nodes, but is also used for other purposes - it can store a list of addresses of the nodes which are storing a specific torrent of TON Storage, a list of addresses of nodes included in an overlay subnetwork, ADNL Addresses of TON services or ADNL Addresses of accounts of TON Blockchain and so on. + +:::info +Read more about TON DHT in [DHT](/develop/network/dht) article, or in Chapter 3.2. of the [TON Whitepaper](https://docs.ton.org/ton.pdf). +::: From 1e6a908caef4303f2b5cdd2f100c4831fbe25fd5 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:22 +0800 Subject: [PATCH 166/219] New translations addresses.md (Chinese Simplified) --- .../current/learn/overviews/addresses.md | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/learn/overviews/addresses.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/learn/overviews/addresses.md b/i18n/zh/docusaurus-plugin-content-docs/current/learn/overviews/addresses.md new file mode 100644 index 0000000000..665255a855 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/learn/overviews/addresses.md @@ -0,0 +1,175 @@ +# Smart Contract Addresses + +This section will describe the specifics of smart contract addresses on TON Blockchain. It will also explain how actors are synonymous with smart contracts on TON. + +## Everything is a Smart Contract + +On TON, smart contracts are built using the [Actor model](/learn/overviews/ton-blockchain#single-actor). In fact, actors in TON are technically represented as smart contracts. This means that even your wallet is a simple actor (and a smart contract). + +Typically, actors process incoming messages, change their internal state, and generate outbound messages as a result. That's why every actor (i.e., smart contract) on TON Blockchain must have an address, so it is able to receive messages from other actors. + +:::info EVM EXPERIENCE +On the Ethereum Virtual Machine (EVM), addresses are completely separate from smart contracts. Feel free to learn more about the differences by reading our article ["Six unique aspects of TON Blockchain that will surprise Solidity developers"](https://blog.ton.org/six-unique-aspects-of-ton-blockchain-that-will-surprise-solidity-developers) by Tal Kol. +::: + +## Address of Smart Contract + +Smart contract addresses operating on TON typically consist of two main components: + +- **(workchain_id)**: denotes the workchain ID (a signed 32-bit integer) + +- **(account_id)** denotes the address of the account (64-512 bits, depending on the workchain) + +In the raw address overview section of this documentation, we'll discuss how **(workchain_id, account_id)** pairs present themselves. + +### Workchain ID and Account ID + +#### Workchain ID + +[As we've seen before](/learn/overviews/ton-blockchain#workchain-blockchain-with-your-own-rules), it is possible to create as many as `2^32` workchains operating on TON Blockchain. We also noted how 32-bit prefix smart contract addresses identify and are linked to smart contract addresses within different workchains. This allows smart contracts to send and receive messages to and from different workchains on TON Blockchain. + +Nowadays, only the Masterchain (workchain_id=-1) and occasionally the basic workchain (workchain_id=0) are running in TON Blockchain. + +Both of them have 256-bit addresses, therefore, we assume that the workchain_id is either 0 or -1, and the address within the workchain is precisely 256 bits. + +#### Account ID + +All account IDs on TON make use of 256-bit addresses on the Masterchain and Basechain (or basic workchain). + +In fact, Account ID’s **(account_id)** defined as hash functions for smart contract objects (particular, the SHA-256). Every smart contract operating on TON Blockchain stores two main components. These include: + +1. _Compiled code_. Logic of the smart contract compiled in the form of bytecode. +2. _Initial state_. The contract's values at the moment of its deployment on-chain. + +Finally, to accurately derive the contract's address, it is necessary to calculate the hash corresponding to the pair **(Initial code, Initial state)** object. At this time, we won't take a deep dive into how the [TVM](/learn/tvm-instructions/tvm-overview) works, but it's important to understand that account IDs on TON are determined using this formula: +: +**account_id = hash(initial code, initial state)** + +In time, throughout this documentation, we'll dive deeper into the technical specifications and overview of the TVM and TL-B scheme. Now that we are familiar with the generation of the **account_id** and their interaction with smart contract addresses on TON, let’s explain Raw and User-Friendly addresses. + +## Raw and User-Friendly Addresses + +After providing a brief overview of how smart contract addresses on TON leverage workchains and account IDs (for the Masterchain and Basechain specifically), it is important to understand that these addresses are expressed in two main formats: + +- **Raw addresses**: Original full representation of smart contract addresses. +- **User-friendly addresses**: User-friendly addresses are an enhanced format of raw address that employ better security and ease of use. + +Below, we’ll explain more about the differences between these two address types and dive deeper into why user-friendly addresses are used on TON. + +### Raw address + +Raw smart contract addresses consist of a workchain ID and account ID _(workchain_id, account_id)_ and are displayed in the following format: + +- [decimal workchain_id\]:[64 hexadecimal digits with account_id\] + +Provided below, is an example of a raw smart contract address using a workchain ID and account ID together (expressed as **workchain_id** and **account_id**): + +`-1:fcb91a3a3816d0f7b8c2c76108b8a9bc5a6b7a55bd79f8ab101c52db29232260` + +Notice the `-1` at the start of the address string, which denotes a _workchain_id_ that belongs to the Masterchain. + +:::note +Uppercase letters (such as 'A', ‘B’, ‘C’, ‘D’ etc.) may be used in address strings instead of their lower-case counterparts (such as 'a', ‘b’, ’c’ 'd' etc.). +::: + +#### Issues With Raw Addresses + +Using the Raw Address form presents two main issues: + +1. When using the raw address format, it's not possible to verify addresses to eliminate errors prior to sending a transaction. + This means that if you accidentally add or remove characters in the address string prior to sending the transaction, your transaction will be sent to the wrong destination, resulting in loss of funds. +2. When using the raw address format, it's impossible to add special flags like those used when sending transactions that employ user-friendly addresses. + To help you better understand this concept, we’ll explain which flags can be used below. + +### User-Friendly Address + +User-friendly addresses were developed to secure and simplify the experience for TON users who share addresses on the internet (for example, on public messaging platforms or via their email service providers), as well as in the real world. + +#### User-Friendly Address Structure + +User-friendly addresses are made up of 36 bytes in total and are obtained by generating the following components in order: + +1. _[flags - 1 byte]_ — Flags that are pinned to addresses change the way smart contracts react to the received message. + Flags types that employ the user-friendly address format include: + + - isBounceable. Denotes a bounceable or non-bounceable address type. (_0x11_ for "bounceable", _0x51_ for "non-bounceable") + - isTestnetOnly. Denotes an address type used for testnet purposes only. Addresses beginning with _0x80_ should not be accepted by software running on the production network + - isUrlSafe. Denotes a deprecated flag that is defined as URL-safe for an address. All addresses are then considered URL-safe. +2. _\[workchain_id - 1 byte]_ — The workchain ID (_workchain_id_) is defined by a signed 8-bit integer _workchain_id_.\ + (_0x00_ for the BaseChain, _0xff_ for the MasterChain) +3. _\[account_id - 32 byte]_ — The account ID is made up of a ([big-endian](https://www.freecodecamp.org/news/what-is-endianness-big-endian-vs-little-endian/)) 256-bit address in the workchain. +4. _\[address verification - 2 bytes]_ — In user-friendly addresses, address verification is composed of a CRC16-CCITT signature from the previous 34 bytes. ([Example](https://github.com/andreypfau/ton-kotlin/blob/ce9595ec9e2ad0eb311351c8a270ef1bd2f4363e/ton-kotlin-crypto/common/src/crc32.kt)) + In fact, the idea pertaining to verification for user-friendly addresses is quite similar to the [Luhn algorithm](https://en.wikipedia.org/wiki/Luhn_algorithm), which is used on all credit cards to prevent users from entering non-existing card numbers by mistake. + +The addition of these 4 main components means that: `1 + 1 + 32 + 2 = 36` bytes in total (per user-friendly address). + +To generate a user-friendly address, the developer must encode all 36 bytes using either: + +- _base64_ (i.e., with digits, upper and lowercase Latin letters, '/' and '+') +- _base64url_ (with '_' and '-' instead of '/' and '+') + +After this process is complete, the generation of a user-friendly address with a length of 48 non-spaced characters is finalized. + +:::info DNS ADDRESS FLAGS +On TON, DNS addresses such as mywallet.ton are sometimes used instead of raw and user-friendly addresses. In fact, DNS addresses are made up of user-friendly addresses and include all the required flags that allow developers to access all the flags from the DNS record within the TON domain. +::: + +#### User-Friendly Address Encoding Examples + +For example, the "test giver" smart contract (a special smart contract residing in the testnet masterchain that sends 2 test tokens to anyone who requests them) makes use of the following raw address: + +`-1:fcb91a3a3816d0f7b8c2c76108b8a9bc5a6b7a55bd79f8ab101c52db29232260` + +The above "test giver" raw address must be converted into the user-friendly address form. This is obtained using either the base64 or base64url forms (that we introduced previously) as follows: + +- `kf/8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15+KsQHFLbKSMiYIny` (base64) +- `kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny` (base64url) + +:::info +Notice that both forms (_base64_ and _base64url_) are valid and must be accepted! +::: + +#### Bounceable vs Non-Bounceable Addresses + +The core idea behind the bounceable address flag is sender's funds security. + +For example, if the destination smart contract does not exist, or if some issue happens during the transaction, the message will be "bounced" back to the sender and constitute the remainder of the original value of the transaction (minus all transfer and gas fees). This ensures the sender doesn't lose their funds that were sent by accident to an address that cannot accept the transaction. + +In relation to bounceable addresses specifically: + +1. The **bounceable=false** flag generally means the receiver is a wallet. +2. The **bounceable=true** flag typically denotes a custom smart contract with its own application logic (for example, a DEX). In this example, non-bounceable messages should not be sent because of security reasons. + +Feel free to read more on this topic in our documentation to gain a better understanding of [non-bounceable messages](/develop/smart-contracts/guidelines/non-bouncable-messages). + +#### Armored base64 Representations + +Additional binary data related to TON Blockchain employs similar "armored" base64 user-friendly address representations. These differentiate from one another depending on the first 4 characters of their byte tag. For example, 256-bit Ed25519 public keys are represented by first creating a 36-byte sequence using the below process in order: + +- A single byte tag using the _0x3E_ format denotes a public key +- A single byte tag using the _0xE6_ format denotes a Ed25519 public key +- 32 bytes containing the standard binary representation of the Ed25519 public key +- 2 bytes containing the big-endian representation of CRC16-CCITT of the previous 34 bytes + +The resulting 36-byte sequence is converted into a 48-character base64 or base64url string in the standard fashion. For example, the Ed25519 public key `E39ECDA0A7B0C60A7107EC43967829DBE8BC356A49B9DFC6186B3EAC74B5477D` (usually represented by a sequence of 32 bytes such as: `0xE3, 0x9E, ..., 0x7D`) presents itself through the "armored" representation as follows: + +`Pubjns2gp7DGCnEH7EOWeCnb6Lw1akm538YYaz6sdLVHfRB2` + +### Converting User-Friendly Addresses and Raw Addresses + +The simplest way to convert user-friendly and raw addresses is to use one of several TON APIs and other tools, including: + +- [ton.org/address](https://ton.org/address) +- [toncenter API methods in mainnet](https://toncenter.com/api/v2/#/accounts/pack_address_packAddress_get) +- [toncenter API methods in testnet](https://testnet.toncenter.com/api/v2/#/accounts/pack_address_packAddress_get) + +Additionally, there are two ways to convert user-friendly and raw addresses for wallets using JavaScript: + +- [Convert address from/to user-friendly or raw form using ton.js](https://github.com/ton-org/ton-core/blob/main/src/address/Address.spec.ts) +- [Convert address from/to user-friendly or raw form using tonweb](https://github.com/toncenter/tonweb/tree/master/src/utils#address-class) + +It's also possible to make use of similar mechanisms using [SDKs](/develop/dapps/apis/sdk). + +### Address Examples + +Learn more examples on TON Addresses in the [TON Cookbook](/develop/dapps/cookbook#working-with-contracts-addresses). From afc8fd43944e51618517166c14be096a35a903e2 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:23 +0800 Subject: [PATCH 167/219] New translations cells.md (Chinese Simplified) --- .../current/learn/overviews/cells.md | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/learn/overviews/cells.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/learn/overviews/cells.md b/i18n/zh/docusaurus-plugin-content-docs/current/learn/overviews/cells.md new file mode 100644 index 0000000000..b43d6ed648 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/learn/overviews/cells.md @@ -0,0 +1,52 @@ +# Cells as Data Storage + +Everything in TON is stored in cells. A cell is a data structure containing: + +- up to **1023 bits** of data (not bytes!) +- up to **4 references** to other cells + +Bits and references are not intermixed (they are stored separately). Circular references are forbidden: for any cell, none of its descendant cells can have this original cell as reference. + +Thus, all cells constitute a directed acyclic graph (DAG). Here is a good picture to illustrate: + +![Directed Acylic Graph](/img/docs/dag.png) + +## Cell types + +Currently, there are 5 types of cell: _ordinary_ and 4 _exotic_. +The exotic types are the following: + +- Pruned branch cell +- Library reference cell +- Merkle proof cell +- Merkle update cell + +:::tip +For more on exotic cells see: [**TVM Whitepaper, Section 3**](https://ton.org/tvm.pdf). +::: + +## Cell flavors + +A cell is an opaque object optimized for compact storage. + +In particular, it deduplicates data: if there are several equivalent sub-cells referenced in different branches, their content is only stored once. However, opaqueness means that a cell cannot be modified or read directly. Thus, there are 2 additional flavors of the cells: + +- _Builder_ for partially constructed cells, for which fast operations for appending bitstrings, integers, other cells and references to other cells can be defined. +- _Slice_ for 'dissected' cells representing either the remainder of a partially parsed cell or a value (subcell) residing inside such a cell and extracted from it via a parsing instruction. + +Another special cell flavor is used in TVM: + +- _Continuation_ for cells containing op-codes (instructions) for TON Virtual Machine, see [TVM bird's-eye overview](/learn/tvm-instructions/tvm-overview). + +## Serialization of data to cells + +Any object in TON (message, message queue, block, whole blockchain state, contract code and data) serializes to a cell. + +The process of serialization is described by a TL-B scheme: a formal description of how this object can be serialized into _Builder_ or how to parse an object of a given type from the _Slice_. +TL-B for cells is the same as TL or ProtoBuf for byte-streams. + +If you want to know more details about cell (de)serialization, you could read [Cell & Bag of Cells](/develop/data-formats/cell-boc) article. + +## See Also + +- [TL-B Language](/develop/data-formats/tl-b-language) From b4f3ae629533c9da06bf23b1d5e950c8d291f6b5 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:23 +0800 Subject: [PATCH 168/219] New translations ton-blockchain.md (Chinese Simplified) --- .../current/learn/overviews/ton-blockchain.md | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/learn/overviews/ton-blockchain.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/learn/overviews/ton-blockchain.md b/i18n/zh/docusaurus-plugin-content-docs/current/learn/overviews/ton-blockchain.md new file mode 100644 index 0000000000..a39f405a3b --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/learn/overviews/ton-blockchain.md @@ -0,0 +1,72 @@ +# Blockchain of Blockchains + +:::tip +Terms '**smart contract**', '**account**' and '**actor**' are used interchangeably in this document to describe a blockchain entity. +::: + +## Single actor + +Let's consider one smart contract. + +In TON, it is a _thing_ with properties like `address`, `code`, `data`, `balance` and others. In other words, it is an object which has some _storage_ and _behavior_. +That behavior has the following pattern: + +- something happens (the most common situation is that a contract gets a message) +- contract handles that event according to its own properties by executing its `code` in TON Virtual Machine. +- contract modifies its own properties (`code`, `data` and others) +- contract optionally generates outgoing messages +- contract goes into standby mode until the next event occurs + +A combination of these steps is called a **transaction**. It is important that events are handled one by one, thus _transactions_ are strictly ordered and cannot interrupt each other. + +This behavior pattern is well known and called 'Actor'. + +### The lowest level: Account Chain + +A sequence of _transactions_ `Tx1 -> Tx2 -> Tx3 -> ....` may be called a **chain**. And in the considered case it is called **AccountChain** to emphasize that it is _the chain_ of a single account of transactions. + +Now, since nodes that process transactions need from time to time to coordinate the state of the smart contract (to reach a _consensus_ about the state) those _transactions_ are batched: +`[Tx1 -> Tx2] -> [Tx3 -> Tx4 -> Tx5] -> [] -> [Tx6]`. +Batching does not intervene in sequencing, each transaction still has only one 'prev tx' and at most one 'next tx', but now this sequence is cut into the **blocks**. + +It is also expedient to include queues of incoming and outgoing messages to _blocks_. In that case, a _block_ will contain a full set of information which determines and describes what happened to the smart contract during that block. + +## Many AccountChains: Shards + +Now let's consider many accounts. We can get a few _AccountChains_ and store them together, such a set of _AccountChains_ is called a **ShardChain**. In the same way, we can cut **ShardChain** into **ShardBlocks**, which are an aggregation of individual _AccountBlocks_. + +### Dynamic splitting and merging of ShardChains + +Note that since a _ShardChain_ consists of easily distinguished _AccountChains_, we can easily split it. That way if we have 1 _ShardChain_ which describes events that happen with 1 million accounts and there are too many transactions per second to be processed and stored in one node, so we just divide (or **split**) that chain into two smaller _ShardChains_ with each chain accounting for half a million accounts and each chain processed on a separate subset of nodes. + +Analogously, if some shards became too unoccupied they can be **merged** into one bigger shard. + +There are obviously two limiting cases: when the shard contains only one account (and thus cannot be split further) and when the shard contains all accounts. + +Accounts can interact with each other by sending messages. There is a special mechanism of routing which move messages from outgoing queues to corresponding incoming queues and ensures that 1) all messages will be delivered 2) messages will be delivered consecutively (the message sent earlier will reach the destination earlier). + +:::info SIDE NOTE +To make splitting and merging deterministic, an aggregation of AccountChains into shards is based on the bit-representation of account addresses. For example, address looks like `(shard prefix, address)`. That way, all accounts in the shardchain will have exactly the same binary prefix (for instance all addresses will start with `0b00101`). +::: + +## Blockchain + +An aggregation of all shards which contains all accounts behaving by one set of rules is called a **Blockchain**. + +In TON there can be many sets of rules and thus many blockchains which operate simultaneously and can interact with each other by sending messages crosschain in the same way that accounts of one chain can interact with each other. + +### Workchain: Blockchain with your own rules + +If you want to customize rules of the group of Shardchains, you could create a **Workchain**. A good example is to make a workchain that works on the base of EVM to run Solidity smart contracts on it. + +Theoretically, everyone in community can create own workchain. In fact, it's pretty complicated task to build it, after that to pay (expensive) price of creating it and receive 2/3 of votes from validators to approve creation of your Workchain. + +TON allows creating up to `2^32` workchains, each subdivided to up to `2^60` shards. + +Nowadays, there are only 2 workchains in TON: MasterChain and BaseChain. + +BaseChain is used for everyday transactions between actors because it's pretty cheap, while MasterChain have a crucial function for TON, so let's cover what does it do! + +### Masterchain: Blockchain of Blockchains + +There is a necessity for the synchronization of message routing and transaction execution. In other words, nodes in the network need a way to fix some 'point' in a multichain state and reach a consensus about that state. In TON, a special chain called **MasterChain** is used for that purpose. Blocks of _masterchain_ contain additional information (latest block hashes) about all other chains in the system, thus any observer unambiguously determines the state of all multichain systems at a single masterchain block. From 0d1db0c8f970b94d7e60b1ae0759d68a288e19ca Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:24 +0800 Subject: [PATCH 169/219] New translations fee-calculation-instructions.md (Chinese Simplified) --- .../fee-calculation-instructions.md | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/fee-calculation-instructions.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/fee-calculation-instructions.md b/i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/fee-calculation-instructions.md new file mode 100644 index 0000000000..4c91ba03b5 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/fee-calculation-instructions.md @@ -0,0 +1,75 @@ +# TVM Upgrade 2024.04 + +## Introduction of new instructions for cheap fee calculation + +:::tip +This upgrade is active in mainnet since March 16 (see https://t.me/tonstatus/101). Preview of this update for blueprint is available in `@ton/sandbox@0.16.0-tvmbeta.3`, `@ton-community/func-js@0.6.3-tvmbeta.3` and `@ton-community/func-js-bin@0.4.5-tvmbeta.3` packages. +::: + +This update is activated by Config8 `version` >= 6. + +## c7 + +**c7** tuple extended from 14 to 16 elements: + +- **14**: tuple that contains some config parameters as cell slices. If the parameter is absent from the config, the value is null. + - **0**: `StoragePrices` from `ConfigParam 18`. Not the whole dict, but only the one StoragePrices entry that corresponds to the current time. + - **1**: `ConfigParam 19` (global id). + - **2**: `ConfigParam 20` (mc gas prices). + - **3**: `ConfigParam 21` (gas prices). + - **4**: `ConfigParam 24` (mc fwd fees). + - **5**: `ConfigParam 25` (fwd fees). + - **6**: `ConfigParam 43` (size limits). +- **15**: "[due payment](https://github.com/ton-blockchain/ton/blob/8a9ff339927b22b72819c5125428b70c406da631/crypto/block/block.tlb#L237)" - current debt for storage fee (nanotons). Asm opcode: `DUEPAYMENT`. +- **16**: "precompiled gas usage" - gas usage for the current contract if it is precompiled (see ConfigParam 45), null otherwise. Asm opcode: `GETPRECOMPILEDGAS`. + +The idea behind this extension of c7 by unpacked config parameters is the following: this data will be retrieved from global configuration by transaction executor, so it is already presented in memory of executor. However (before extension) smart-contract need to get all of these parameters one-by-one from configuration dictionary which is expensive and potentially unpredictable by gas (since cost depends on number of parameters). + +Due payment is needed so contract can properly assess storage fees: when message sent in default (bouncable) mode to smart-contract, storage fees are deducted (or added to due_payment field that contains storage fee related debt) prior message value is added to balance. Thus, if contract after processing message send gas excesses back with mode=64, that means that if contract balance hit 0, on next transactions storage fees start accruing in due_payment (and not deducted from incoming messages). That way debt will silently accumulate until account freezes. `DUEPAYMENT` allows developer explicitly account/withhold comission for storage and thus prevent any issues. + +## New opcodes + +### Opcodes to work with new c7 values + +26 gas for each, except for `SENDMSG` (because of cell operations). + +| xxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | +| :------------------------------------- | :------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `UNPACKEDCONFIGTUPLE` | _`- c`_ | Retrieves tuple of configs slices from c7 | +| `DUEPAYMENT` | _`- i`_ | Retrieves value of due payment from c7 | +| `GLOBALID` | _`- i`_ | Now retrieves `ConfigParam 19` from from c7, ton form config dict. | +| `SENDMSG` | _`msg mode - i`_ | Now retrieves `ConfigParam 24/25` (message prices) and `ConfigParam 43` (`max_msg_cells`) from c7, not from config dict. | + +### Opcodes to process config parameters + +The introduction of tuple of configurations slices in the TON transaction executor has made it more cost-effective to parse fee parameters. However, as new config parameters constructors may be introduced in the future, smart contracts may need to be updated to interpret these new parameters. To address this issue, special opcodes for fee calculation have been introduced. These opcodes read parameters from c7 and calculate fees in the same manner as the executor. With the introduction of new parameters constructors, these opcodes will be updated to accommodate the changes. This allows smart contracts to rely on these instructions for fee calculation without needing to interpret all types of constructors. + +26 gas for each. + +| xxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | +| :------------------------------------- | :----------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `GETGASFEE` | _`gas_used is_mc - price`_ | Calculates computation cost in nanotons for transaction that consumes _`gas_used`_ gas. | +| `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` | _`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` | _`- null`_ | reserved, currently returns `null`. Will return cost of contract execution in gas units if this contract is _precompiled_ | +| `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` | _`gas_used is_mc - price`_ | Calculates additional computation cost in nanotons for transaction that consumes additional _`gas_used`_. In other words, same as `GETGASFEE`, but without flat price (just `(gas_used * price) / 2^16`). | +| `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`). | + +`gas_used`, `cells`, `bits`, `time_delta` are integers in range `0..2^63-1`. + +### Cell level operations + +Operations for working with Merkle proofs, where cells can have non-zero level and multiple hashes. +26 gas for each. + +| xxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | +| :------------------------------------- | :-------------------- | :---------------------------------------------------- | +| `CLEVEL` | _`cell - level`_ | Returns level of the cell | +| `CLEVELMASK` | _`cell - level_mask`_ | Returns level mask of the cell | +| `i CHASHI` | _`cell - hash`_ | Returns `i`th hash of the cell | +| `i CDEPTHI` | _`cell - depth`_ | Returns `i`th depth of the cell | +| `CHASHIX` | _`cell i - depth`_ | Returns `i`th hash of the cell | +| `CDEPTHIX` | _`cell i - depth`_ | Returns `i`th depth of the cell | + +`i` is in range `0..3`. From cccb8c1af7658081608c858aa8839f6569ff5d24 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:25 +0800 Subject: [PATCH 170/219] New translations generate_md.py (Chinese Simplified) --- .../learn/tvm-instructions/generate_md.py | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/generate_md.py diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/generate_md.py b/i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/generate_md.py new file mode 100644 index 0000000000..84fa7fc0cc --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/generate_md.py @@ -0,0 +1,93 @@ +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) From c8ff0c292cf4706da54995c07d030d5461c4ef01 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:26 +0800 Subject: [PATCH 171/219] New translations instructions.csv (Chinese Simplified) --- .../learn/tvm-instructions/instructions.csv | 1026 +++++++++++++++++ 1 file changed, 1026 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/instructions.csv diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/instructions.csv b/i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/instructions.csv new file mode 100644 index 0000000000..06db926550 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/instructions.csv @@ -0,0 +1,1026 @@ +"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)","","","" From f1c9f5f3a1d7343e82b848f53d3049a6f063ff57 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:27 +0800 Subject: [PATCH 172/219] New translations tvm-exit-codes.md (Chinese Simplified) --- .../learn/tvm-instructions/tvm-exit-codes.md | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-exit-codes.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-exit-codes.md b/i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-exit-codes.md new file mode 100644 index 0000000000..32dbfc664b --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-exit-codes.md @@ -0,0 +1,43 @@ +# TVM Exit codes + +If TVM exits with an arbitrary 16-bit unsigned integer `exit_code`. `exit_code` higher than 1, it is considered to be an _error code_, therefore an exit with such a code may cause the transaction to revert/bounce. + +## Standard exit codes + +:::info +The list of standard exit codes contains all universal TVM exit codes defined for TON Blockchain. Alternative exit codes should be sought in the source code of corresponded contract. +::: + +| Exit Code | TVM Phase | Description | +| --------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `0` | Compute Phase | Standard successful execution exit code. | +| `1` | Compute Phase | Alternative successful execution exit code. | +| `2` | Compute Phase | Stack underflow. Last op-code consumed more elements than there are on the stacks. 1 | +| `3` | Compute Phase | Stack overflow. More values have been stored on a stack than allowed by this version of TVM. | +| `4` | Compute Phase | Integer overflow. Integer does not fit into −2256 ≤ x < 2256 or a division by zero has occurred. | +| `5` | Compute Phase | Integer out of expected range. | +| `6` | Compute Phase | Invalid opcode. Instruction is unknown in the current TVM version. | +| `7` | Compute Phase | Type check error. An argument to a primitive is of an incorrect value type. 1 | +| `8` | Compute Phase | Cell overflow. Writing to builder is not possible since after operation there would be more than 1023 bits or 4 references. | +| `9` | Compute Phase | Cell underflow. Read from slice primitive tried to read more bits or references than there are. | +| `10` | Compute Phase | Dictionary error. Error during manipulation with dictionary (hashmaps). | +| `11` | Compute Phase | Most often caused by trying to call get-method whose id wasn't found in the code (missing `method_id` modifier or wrong get-method name specified when trying to call it). In [TVM docs](https://ton.org/tvm.pdf) its described as "Unknown error, may be thrown by user programs". | +| `12` | Compute Phase | Thrown by TVM in situations deemed impossible. | +| `13` | Compute Phase | Out of gas error. Thrown by TVM when the remaining gas becomes negative. | +| `-14` | Compute Phase | It means out of gas error, same as `13`. Negative, because it [cannot be faked](https://github.com/ton-blockchain/ton/blob/20758d6bdd0c1327091287e8a620f660d1a9f4da/crypto/vm/vm.cpp#L492) | +| `32` | Action Phase | Action list is invalid. Set during action phase if c5 register after execution contains unparsable object. | +| `-32` | Action Phase | (the same as prev 32) - Method ID not found. Returned by TonLib during an attempt to execute non-existent get method. | +| `33` | Action Phase | Action list is too long. | +| `34` | Action Phase | Action is invalid or not supported. Set during action phase if current action cannot be applied. | +| `35` | Action Phase | Invalid Source address in outbound message. | +| `36` | Action Phase | Invalid Destination address in outbound message. | +| `37` | Action Phase | Not enough TON. Message sends too much TON (or there is not enough TON after deducting fees). | +| `38` | Action Phase | Not enough extra-currencies. | +| `40` | Action Phase | Not enough funds to process a message. This error is thrown when there is only enough gas to cover part of the message, but does not cover it completely. | +| `43` | Action Phase | The maximum number of cells in the library is exceeded or the maximum depth of the Merkle tree is exceeded. | + +1 If you encounter such exception in a func contract it probably means a type error in asm declarations. + +:::info +Often you can see the exit code `0xffff` (65535 in decimal form). This usually means that the received opcode is unknown to the contract. When writing contracts, this code is set by the developer himself. +::: From 61a5b58b2d9d372b7bcf64f5ed0b43289e4572c4 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:28 +0800 Subject: [PATCH 173/219] New translations tvm-initialization.md (Chinese Simplified) --- .../tvm-instructions/tvm-initialization.md | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-initialization.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-initialization.md b/i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-initialization.md new file mode 100644 index 0000000000..cdb5632ae2 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-initialization.md @@ -0,0 +1,196 @@ +# TVM Initialization + +:::info +To maximize your comprehension of this page, familiarizing yourself with the [TL-B language](/develop/data-formats/cell-boc) is highly recommended. +::: + +TVM is invoked during the computing phase of ordinary and/or other transactions. + +## Initial state + +A new instance of TVM is initialized prior to the execution of a smart contract as follows: + +- The original **cc** (current continuation) is initialized using the cell slice created from the `code` section of the smart contract. In case of a frozen or uninitialized state of the account, the code must be supplied in the `init` field of the incoming message. + +- The **cp** (current TVM codepage) is set to the default value, which is 0. If the smart contract wants to use another TVM codepage _x_, then it must switch to it by using `SETCODEPAGE` _x_ as the first instruction of its code. + +- The **gas** values (_gas limits_) are initialized in accordance to Credit phase results. + +- The **libraries** (_library context_) computation is [described below](#library-context). + +- The **stack** initialization process depends on the event which caused the transaction, and its contents are [described below](#stack). + +- Control register **c0** (return continuation) is initialized by extraordinary continuation `ec_quit` with parameter 0. When executed, this continuation leads to a termination of TVM with exit code 0. + +- Control register **c1** (alternative return continuation) is initialized by extraordinary continuation `ec_quit` with parameter 1. When invoked, it leads to a termination of TVM with exit code 1. Notice, that both exit codes 0 and 1 are considered a successful termination of TVM. + +- Control register **c2** (exception handler) is initialized by extraordinary continuation `ec_quit_exc`. When invoked, it takes the top integer from the stack (equal to the exception number) and terminates TVM with exit code equal to that integer. This way, by default all exceptions terminate the smart contract execution with exit code equal to the exception number. + +- Control register **c3** (code dictionary) is initialized by the cell with the smart contract code like **cc** (current continuation) described above. + +- Control register **c4** (root of persistent data) is initialized by the persistent data of the smart contract, stored in its `data` section. In case of a frozen or uninitialized state of the account, the data must be supplied in the `init` field of the incoming message. Notice, that the persistent data of the smart contract does not need to be loaded in its entirery for this to occur. The root is loaded instead, and TVM may load other cells by their references from the root only when they are accessed, thus providing a form of virtual memory. + +- Control register **c5** (root of actions) is initialized by an empty cell. The "output action" primitives of TVM, such as `SENDMSG`, accumulate _output actions_ (e.g., outbound messages) in this register, to be performed upon successful termination of the smart contract. The TL-B scheme for its serialization is [described below](#control-register-c5) + +- Control register **c7** (root of temporary data) is initialized as a tuple and its structure is [described below](#control-register-c7) + +## Library context + +The _library context_ (library environment) of a smart contract is a hashmap mapping 256-bit cell (representation) hashes into the corresponding cells themselves. When an external cell reference is accessed during the execution of the smart contract, the cell referred to is looked up in the library environment and the external cell reference is transparently replaced by the cell found. + +The library environment for an invocation of a smart contract is computed as follows: + +1. The global library environment for the current workchain is taken from the current state of the masterchain. +2. Then, it is augmented by the local library environment of the smart contract, stored in the `library` field of the smart contract's state. Only 256-bit keys equal to the hashes of the corresponding value cells are taken into account. If a key is present in both the global and local library environments, the local environment takes precedence in the merge. +3. Finally, it is augmented by the `library` field of the `init` field of the incoming message (if any). Notice, that if the account is frozen or uninitialized, the `library` field of the message would be used over the local library environment from the previous step. The message library has lower precedence than both the local and the global library environments. + +Most common way of creating shared libraries for TVM is to publish a reference to the root cell of the library in the masterchain. + +## Stack + +Initialization of the TVM stack comes after the formation of the initial state of the TVM, and it depends on the event which caused the transaction: + +- internal message +- external message +- tick-tock +- split prepare +- merge install + +The last item pushed to the stack is always the _function selector_, which is an _Integer_ that identifies the event that caused the transaction. + +### Internal message + +In case of internal message, the stack is initialized by pushing the arguments to the `main()` function of the smart contract as follows: + +- The balance _b_ of the smart contract (after crediting the value of the inbound message) is passed as an _Integer_ amount of nanotons. +- The balance _b_m of inbound message _m_ is passed as an _Integer_ amount of nanotons. +- The inbound message _m_ is passed as a cell, which contains a serialized value of type _Message X_, where _X_ is the type of the message body. +- The body _m_b of the inbound message, equal to the value of field body _m_ and passed as a cell slice. +- The function selector _s_, normally equal to 0. + +After that, the code of the smart contract, equal to its initial value of **c3**, is executed. It selects the correct function according to _s_, which is expected to process the remaining arguments to the function and terminate afterwards. + +### External message + +An inbound external message is processed similarly to the [internal message described above](#internal-message), with the following modifications: + +- The function selector _s_ is set to -1. +- The balance _b_m of inbound message is always 0. +- The initial current gas limit _g_l is always 0. However, the initial gas credit _g_c > 0. + +The smart contract must terminate with _g_c = 0 or _g_r ≥ _g_c; otherwise, the transaction and the block containing it are invalid. Validators or collators suggesting a block candidate must never include transactions processing inbound external messages that are invalid. + +### Tick and tock + +In case of tick and tock transactions, the stack is initialized by pushing the arguments to the `main()` function of the smart contract as follows: + +- The balance _b_ of the current account is passed as an _Integer_ amount of nanotons. +- The 256-bit address of the current account inside the masterchain as an unsigned _Integer_. +- An integer equal to 0 for tick transactions and to -1 for tock transactions. +- The function selector _s_, equal to -2. + +### Split prepare + +In case of split prepare transaction, the stack is initialized by pushing the arguments to the `main()` function of the smart contract as follows: + +- The balance _b_ of the current account is passed as an _Integer_ amount of nanotons. +- A _Slice_ containing _SplitMergeInfo_. +- The 256-bit address of the current account. +- The 256-bit address of the sibling account. +- An integer 0 ≤ _d_ ≤ 63, equal to the position of the only bit in which addresses of the current and sibling account differ. +- The function selector _s_, equal to -3. + +### Merge install + +In case of merge install transaction, the stack is initialized by pushing the arguments to the `main()` function of the smart contract as follows: + +- The balance _b_ of the current account (already combined with the nanoton balance of the sibling account) is passed as an _Integer_ amount of nanotons. +- The balance _b'_ of the sibling account, taken from the inbound message _m_ is passed as an _Integer_ amount of nanotons. +- The message _m_ from the sibling account, automatically generated by a merge prepare transaction. Its `init` field contains the final state of the sibling account. The message is passed as a cell, which contains a serialized value of type _Message X_, where _X_ is the type of the message body. +- The state of the sibling account, represented by a _StateInit_. +- A _Slice_ containing _SplitMergeInfo_. +- The 256-bit address of the current account. +- The 256-bit address of the sibling account. +- An integer 0 ≤ _d_ ≤ 63, equal to the position of the only bit in which addresses of the current and sibling account differ. +- The function selector _s_, equal to -4. + +## Control register c5 + +The _output actions_ of a smart contract are accumulated in cell stored in control register **c5**: the cell itself contains the last action in the list and a reference to the previous one, thus forming a linked list. + +The list can also be serialized as a value of type _OutList n_, where _n_ is the length of the list: + +```tlb +out_list_empty$_ = OutList 0; + +out_list$_ {n:#} + prev:^(OutList n) + action:OutAction + = OutList (n + 1); + +out_list_node$_ + prev:^Cell + action:OutAction = OutListNode; +``` + +The list of possible actions thereby consists of: + +- `action_send_msg` — for sending an outbound message +- `action_set_code` — for setting an opcode +- `action_reserve_currency` — for storing a currency collection +- `action_change_library` — for changing the library + +As described in the corresponding TL-B scheme: + +```tlb +action_send_msg#0ec3c86d + mode:(## 8) + out_msg:^(MessageRelaxed Any) = OutAction; + +action_set_code#ad4de08e + new_code:^Cell = OutAction; + +action_reserve_currency#36e6b809 + mode:(## 8) + currency:CurrencyCollection = OutAction; + +libref_hash$0 + lib_hash:bits256 = LibRef; +libref_ref$1 + library:^Cell = LibRef; +action_change_library#26fa1dd4 + mode:(## 7) { mode <= 2 } + libref:LibRef = OutAction; +``` + +## Control register c7 + +Control register **c7** contains the root of temporary data as a Tuple, formed by a _SmartContractInfo_ type, containing some basic blockchain context data, such as time, global config, etc. It is described by the following TL-B scheme: + +```tlb +smc_info#076ef1ea + actions:uint16 msgs_sent:uint16 + unixtime:uint32 block_lt:uint64 trans_lt:uint64 + rand_seed:bits256 balance_remaining:CurrencyCollection + myself:MsgAddressInt global_config:(Maybe Cell) = SmartContractInfo; +``` + +First component of this tuple is an _Integer_ value, which is always equal to 0x076ef1ea, after which 9 named fields follow: + +| Field | Type | Description | +| ------------------- | ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `actions` | uint16 | Originally initialized by 0, but incremented by one whenever an output action is installed by a non-RAW output action primitive | +| `msgs_sent` | uint16 | Number of messages sent | +| `unixtime` | uint32 | Unix timestamp in seconds | +| `block_lt` | uint64 | Represents _logical time_ of the previous block of this account. [More about logical time](https://docs.ton.org/develop/smart-contracts/guidelines/message-delivery-guarantees#what-is-a-logical-time) | +| `trans_lt` | uint64 | Represents _logical time_ of the previous transaction of this account | +| `rand_seed` | bits256 | Initialized deterministically starting from `rand_seed` of the block, the account address, the hash of the incoming message being processed (if any), and the transaction logical time `trans_lt` | +| `balance_remaining` | [CurrencyCollection](/develop/data-formats/msg-tlb#currencycollection) | Remaining balance of the smart contract | +| `myself` | [MsgAddressInt](/develop/data-formats/msg-tlb#msgaddressint-tl-b) | Address of this smart contract | +| `global_config` | (Maybe Cell) | Contains information about the global config | + +Notice, that in the upcoming upgrade to the TVM, the **c7** tuple was extended from 10 to 14 elements. Read more about it [here](/learn/tvm-instructions/tvm-upgrade-2023-07). + +## See also + +- Original description of [TVM Initialization](https://docs.ton.org/tblkch.pdf#page=89\&zoom=100) from the whitepaper From 8c3de04b3f2166191069bb7b9dbe7f1656a7cc46 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:28 +0800 Subject: [PATCH 174/219] New translations tvm-overview.mdx (Chinese Simplified) --- .../learn/tvm-instructions/tvm-overview.mdx | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-overview.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-overview.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-overview.mdx new file mode 100644 index 0000000000..69b5a3875e --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-overview.mdx @@ -0,0 +1,98 @@ +import Button from '@site/src/components/button' + +# TVM Overview + +All TON Smart Contracts are executed on their own TON Virtual Machine (TVM). TVM is built on the _stack principle_, which makes it efficient and easy to implement. + +This document provides a bird's-eye view of how TVM executes transactions. + +:::tip + +- TVM Source — [**TVM C++ implementation**](https://github.com/ton-blockchain/ton/tree/master/crypto/vm) + ::: + +## TON Course: TVM + +The [TON Blockchain Course](https://stepik.org/course/176754/) is a comprehensive guide to TON Blockchain development. + +Module 2 completely covers **TVM**, transactions, scalability and business cases. + + + +## Transactions and phases + +When some event happens on the account in one of the TON chains, it causes a **transaction**. The most common event is the "arrival of some message", but generally speaking there could be `tick-tock`, `merge`, `split` and other events. + +Each transaction consists of up to 5 phases: + +1. **Storage phase** - in this phase, storage fees accumulated by the contract due to the occupation of some space in the chain state are calculated. Read more in [Storage Fees](/develop/smart-contracts/fees#storage-fee). +2. **Credit phase** - in this phase, the balance of the contract with respect to a (possible) incoming message value and collected storage fee are calculated. +3. **Compute phase** - in this phase, TVM is executing the contract (see below) and the result of the contract execution is an aggregation of `exit_code`, `actions` (serialized list of actions), `gas_details`, `new_storage` and some others. +4. **Action phase** - if the compute phase was successful, in this phase, `actions` from the compute phase are processed. In particular, actions may include sending messages, updating the smart contract code, updating the libraries, etc. Note that some actions may fail during processing (for instance, if we try to send message with more TON than the contract has), in that case the whole transaction may revert or this action may be skipped (it depends on the mode of the actions, in other words, the contract may send a `send-or-revert` or `try-send-if-no-ignore` type of message). +5. **Bounce phase** - if the compute phase failed (it returned `exit_code >= 2`), in this phase, the _bounce message_ is formed for the transactions initiated by an incoming message. + +## Compute phase + +In this phase, the TVM execution occurs. + +### TVM state + +At any given moment, the TVM state is fully determined by 6 properties: + +- Stack (see below) +- Control registers - (see below) to put it simply, this means up to 16 variables which may be directly set and read during execution +- Current continuation - object which describes a currently executed sequence of instructions +- Current codepage - in simple terms, this means the version of TVM which is currently running +- Gas limits - a set of 4 integer values; the current gas limit gl, the maximal gas limit gm, the remaining gas gr and the gas credit gc +- Library context - the hashmap of libraries which can be called by TVM + +### TVM is a stack machine + +TVM is a last-input-first-output stack machine. In total, there are 7 types of variables which may be stored in stack — three non-cell types: + +- Integer - signed 257-bit integers +- Tuple - ordered collection of up to 255 elements having arbitrary value types, possibly distinct. +- Null + +And four distinct flavours of cells: + +- Cell - basic (possibly nested) opaque structure used by TON Blockchain for storing all data +- Slice - a special object which allows you to read from a cell +- Builder - a special object which allows you to create new cells +- Continuation - a special object which allows you to use a cell as source of TVM instructions + +### Control registers + +- `c0` — Contains the next continuation or return continuation (similar to the subroutine return address in conventional designs). This value must be a Continuation. +- `c1` — Contains the alternative (return) continuation; this value must be a Continuation. +- `c2` — Contains the exception handler. This value is a Continuation, invoked whenever an exception is triggered. +- `c3` — Supporting register, contains the current dictionary, essentially a hashmap containing the code of all functions used in the program. This value must be a Continuation. +- `c4` — Contains the root of persistent data, or simply the `data` section of the contract. This value is a Cell. +- `c5` — Contains the output actions. This value is a Cell. +- `c7` — Contains the root of temporary data. It is a Tuple. + +### Initialization of TVM + +TVM initializes when transaction execution gets to the Computation phase, and then executes commands (opcodes) from _Current continuation_ until there are no more commands to execute (and no continuation for return jumps). + +Detailed description of the initialization process can be found here: [TVM Initialization](/learn/tvm-instructions/tvm-initialization.md) + +## TVM instructions + +The list of TVM instructions can be found here: [TVM instructions](/learn/tvm-instructions/instructions). + +### Result of TVM execution + +Besides exit_code and consumed gas data, TVM indirectly outputs the following data: + +- c4 register - the cell which will be stored as new `data` of the smart-contract (if execution will not be reverted on this or later phases) +- c5 register - (list of output actions) the cell with last action in the list and reference to the cell with prev action (recursively) + +All other register values will be neglected. + +Note, that since there is a limit on max cell-depth `<1024`, and particularly the limit on c4 and c5 depth `<=512`, there will be a limit on the number of output actions in one tx `<=255`. If a contract needs to send more than that, it may send a message with the request `continue_sending` to itself and send all necessary messages in subsequent transactions. + +## See Also + +- [TVM Instructions](/learn/tvm-instructions/instructions) +- [TON TVM](https://ton.org/tvm.pdf) TVM Concepts(may include outdated information) From 9b36066f9160b5e5bd04390decb253ebd32f52e6 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:29 +0800 Subject: [PATCH 175/219] New translations tvm-upgrade-2023-07.md (Chinese Simplified) --- .../tvm-instructions/tvm-upgrade-2023-07.md | 299 ++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-upgrade-2023-07.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-upgrade-2023-07.md b/i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-upgrade-2023-07.md new file mode 100644 index 0000000000..0b7a00b663 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/learn/tvm-instructions/tvm-upgrade-2023-07.md @@ -0,0 +1,299 @@ +# TVM Upgrade 2023.07 + +:::tip +This upgrade launched [run](https://t.me/tonblockchain/223) on the Mainnet from December 2023. +::: + +# c7 + +**c7** is the register in which local context information needed for contract execution +(such as time, lt, network configs, etc) is stored. + +**c7** tuple extended from 10 to 14 elements: + +- **10**: `cell` with code of the smart contract itself. +- **11**: `[integer, maybe_dict]`: TON value of the incoming message, extracurrency. +- **12**: `integer`, fees collected in the storage phase. +- **13**: `tuple` with information about previous blocks. + +**10** Currently code of the smart contract is presented on TVM level only as executable continuation +and can not be transformed to cell. This code is often used to authorize a neighbour contract +of the same kind, for instance jetton-wallet authorizes jetton-wallet. For now we need +to explicitly store code cell in storage which make storage and init_wrapper more cumbersome than it could be. +Using **10** for code is compatible to Everscale update of tvm. + +**11** Currently value of the incoming message is presented on stack after TVM initiation, so if needed during execution, +one either need to store it to global variable or pass through local variables +(at funC level it looks like additional `msg_value` argument in all functions). +By putting it to **11** element we will repeat behavior of contract balance: it is presented both on stack and in c7. + +**12** Currently the only way to calculate storage fees is to store balance in the previous transaction, +somehow calculate gas usage in prev transaction and then compare to current balance minus message value. +Meanwhile, is often desired to account storage fees. + +**13** Currently there is no way to retrieve data on previous blocks. One of the kill features of TON is that every structure +is a Merkle-proof friendly bag (tree) of cells, moreover TVM is cell and merkle-proof friendly as well. +That way, if we include information on the blocks to TVM context it will be possible to make many trustless scenarios: contract A may check transactions on contract B (without B's cooperation), it is possible to recover broken chains of messages (when recover-contract gets and cheks proofs that some transaction occured but reverted), knowing masterchain block hashes is also required to make some validation fisherman functions onchain. + +Block ids are presented in the following format: + +``` +[ wc:Integer shard:Integer seqno:Integer root_hash:Integer file_hash:Integer ] = BlockId; +[ last_mc_blocks:[BlockId0, BlockId1, ..., BlockId15] + prev_key_block:BlockId ] : PrevBlocksInfo +``` + +Ids of the last 16 blocks of masterchain are included (or less if masterchain seqno is less than 16), as well as the last key block. +Inclusion of data on shardblocks may cause some data availability issues (due to merge/split events), +it is not necessarily required (since any event/data can by proven using masterchain blocks) and thus we decided not to include it. + +# New opcodes + +Rule of thumb when choosing gas cost on new opcodes is that it should not be less than normal (calculated from opcode length) and should take no more than 20 ns per gas unit. + +## Opcodes to work with new c7 values + +26 gas for each, except for `PREVMCBLOCKS` and `PREVKEYBLOCK` (34 gas). + +| xxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | +| :------------------------------------- | :------------------ | :---------------------------------------------------------------------------------- | +| `MYCODE` | _`- c`_ | Retrieves code of smart-contract from c7 | +| `INCOMINGVALUE` | _`- t`_ | Retrieves value of incoming message from c7 | +| `STORAGEFEES` | _`- i`_ | Retrieves value of storage phase fees from c7 | +| `PREVBLOCKSINFOTUPLE` | _`- t`_ | Retrives PrevBlocksInfo: `[last_mc_blocks, prev_key_block]` from c7 | +| `PREVMCBLOCKS` | _`- t`_ | Retrieves only `last_mc_blocks` | +| `PREVKEYBLOCK` | _`- t`_ | Retrieves only `prev_key_block` | +| `GLOBALID` | _`- i`_ | Retrieves `global_id` from 19 network config | + +## Gas + +| xxxxxxxxxxxxxx
Fift syntax | xxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | +| :----------------------------- | :----------------- | :-------------------------------------------------------------------------------------------------------------- | +| `GASCONSUMED` | _`- g_c`_ | Returns gas consumed by VM so far (including this instruction).
_26 gas_ | + +## Arithmetics + +New variants of [the division opcode](https://docs.ton.org/learn/tvm-instructions/instructions#52-division) (`A9mscdf`) are added: +`d=0` takes one additional integer from stack and adds it to the intermediate value before division/rshift. These operations return both the quotient and the remainder (just like `d=3`). + +Quiet variants are also available (e.g. `QMULADDDIVMOD` or `QUIET MULADDDIVMOD`). + +If return values don't fit into 257-bit integers or the divider is zero, non-quiet operation throws an integer overflow exception. Quiet operations return `NaN` instead of the value that doesn't fit (two `NaN`s if the divider is zero). + +Gas cost is equal to 10 plus opcode length: 26 for most opcodes, +8 for `LSHIFT#`/`RSHIFT#`, +8 for quiet. + +| xxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Stack | +| :------------------------------------- | :------------------------------------------------------- | +| `MULADDDIVMOD` | _`x y w z - q=floor((xy+w)/z) r=(xy+w)-zq`_ | +| `MULADDDIVMODR` | _`x y w z - q=round((xy+w)/z) r=(xy+w)-zq`_ | +| `MULADDDIVMODC` | _`x y w z - q=ceil((xy+w)/z) r=(xy+w)-zq`_ | +| `ADDDIVMOD` | _`x w z - q=floor((x+w)/z) r=(x+w)-zq`_ | +| `ADDDIVMODR` | _`x w z - q=round((x+w)/z) r=(x+w)-zq`_ | +| `ADDDIVMODC` | _`x w y - q=ceil((x+w)/z) r=(x+w)-zq`_ | +| `ADDRSHIFTMOD` | _`x w z - q=floor((x+w)/2^z) r=(x+w)-q*2^z`_ | +| `ADDRSHIFTMODR` | _`x w z - q=round((x+w)/2^z) r=(x+w)-q*2^z`_ | +| `ADDRSHIFTMODC` | _`x w z - q=ceil((x+w)/2^z) r=(x+w)-q*2^z`_ | +| `z ADDRSHIFT#MOD` | _`x w - q=floor((x+w)/2^z) r=(x+w)-q*2^z`_ | +| `z ADDRSHIFTR#MOD` | _`x w - q=round((x+w)/2^z) r=(x+w)-q*2^z`_ | +| `z ADDRSHIFTC#MOD` | _`x w - q=ceil((x+w)/2^z) r=(x+w)-q*2^z`_ | +| `MULADDRSHIFTMOD` | _`x y w z - q=floor((xy+w)/2^z) r=(xy+w)-q*2^z`_ | +| `MULADDRSHIFTRMOD` | _`x y w z - q=round((xy+w)/2^z) r=(xy+w)-q*2^z`_ | +| `MULADDRSHIFTCMOD` | _`x y w z - q=ceil((xy+w)/2^z) r=(xy+w)-q*2^z`_ | +| `z MULADDRSHIFT#MOD` | _`x y w - q=floor((xy+w)/2^z) r=(xy+w)-q*2^z`_ | +| `z MULADDRSHIFTR#MOD` | _`x y w - q=round((xy+w)/2^z) r=(xy+w)-q*2^z`_ | +| `z MULADDRSHIFTC#MOD` | _`x y w - q=ceil((xy+w)/2^z) r=(xy+w)-q*2^z`_ | +| `LSHIFTADDDIVMOD` | _`x w z y - q=floor((x*2^y+w)/z) r=(x*2^y+w)-zq`_ | +| `LSHIFTADDDIVMODR` | _`x w z y - q=round((x*2^y+w)/z) r=(x*2^y+w)-zq`_ | +| `LSHIFTADDDIVMODC` | _`x w z y - q=ceil((x*2^y+w)/z) r=(x*2^y+w)-zq`_ | +| `y LSHIFT#ADDDIVMOD` | _`x w z - q=floor((x*2^y+w)/z) r=(x*2^y+w)-zq`_ | +| `y LSHIFT#ADDDIVMODR` | _`x w z - q=round((x*2^y+w)/z) r=(x*2^y+w)-zq`_ | +| `y LSHIFT#ADDDIVMODC` | _`x w z - q=ceil((x*2^y+w)/z) r=(x*2^y+w)-zq`_ | + +## Stack operations + +Currently arguments of all stack operations are bounded by 256. +That means that if stack become deeper than 256 it becomes difficult to manage deep stack elements. +In most cases there are no safety reasons for that limit, i.e. arguments are not limited to prevent too expensive operations. +For some mass stack operations, such as `ROLLREV` (where computation time lineary depends on argument value) gas cost also lineary depends on argument value. + +- Arguments of `PICK`, `ROLL`, `ROLLREV`, `BLKSWX`, `REVX`, `DROPX`, `XCHGX`, `CHKDEPTH`, `ONLYTOPX`, `ONLYX` are now unlimited. +- `ROLL`, `ROLLREV`, `REVX`, `ONLYTOPX` consume more gas when arguments are big: additional gas cost is `max(arg-255,0)` (for argument less than 256 the gas consumption is constant and corresponds to the current behavior) +- For `BLKSWX`, additional cost is `max(arg1+arg2-255,0)` (this does not correspond to the current behavior, since currently both `arg1` and `arg2` are limited to 255). + +## Hashes + +Currently only two hash operations are available in TVM: calculation of representation hash of cell/slice, and sha256 of data, but only up to 127 bytes (only that much data fits into one cell). + +`HASHEXT[A][R]_(HASH)` family of operations is added: + +| xxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | +| :---------------------------------- | :------------------------------- | :----------------------------------------------------------------------------------------------------------------------- | +| `HASHEXT_(HASH)` | _`s_1 ... s_n n - h`_ | Calculates and returns hash of the concatenation of slices (or builders) `s_1...s_n`. | +| `HASHEXTR_(HASH)` | _`s_n ... s_1 n - h`_ | Same thing, but arguments are given in reverse order. | +| `HASHEXTA_(HASH)` | _`b s_1 ... s_n n - b'`_ | Appends the resulting hash to a builder `b` instead of pushing it to the stack. | +| `HASHEXTAR_(HASH)` | _`b s_n ... s_1 n - b'`_ | Arguments are given in reverse order, appends hash to builder. | + +Only the bits from root cells of `s_i` are used. + +Each chunk `s_i` may contain non-integer number of bytes. However, the sum of bits of all chunks should be divisible by 8. +Note that TON uses most-significant bit ordering, so when two slices with non-integer number of bytes are concatenated, bits from the first slice become most-significant bits. + +Gas consumption depends on the number of hashed bytes and the chosen algorithm. Additional 1 gas unit is consumed per chunk. + +If `[A]` is not enabled, the result of hashing will be returned as an unsigned integer if fits 256 bits or tuple of ints otherwise. + +The following algorithms are available: + +- `SHA256` - openssl implementation, 1/33 gas per byte, hash is 256 bits. +- `SHA512` - openssl implementation, 1/16 gas per byte, hash is 512 bits. +- `BLAKE2B` - openssl implementation, 1/19 gas per byte, hash is 512 bits. +- `KECCAK256` - [ethereum compatible implementation](http://keccak.noekeon.org/), 1/11 gas per byte, hash is 256 bits. +- `KECCAK512` - [ethereum compatible implementation](http://keccak.noekeon.org/), 1/6 gas per byte, hash is 512 bits. + +Gas usage is rounded down. + +## Crypto + +Currently the only cryptographic algorithm available is `CHKSIGN`: check the Ed25519-signature of a hash `h` for a public key `k`. + +- For compatibility with prev generation blockchains such as Bitcoin and Ethereum we also need checking `secp256k1` signatures. +- For modern cryptographic algorithms the bare minimum is curve addition and multiplication. +- For compatibility with Ethereum 2.0 PoS and some other modern cryptography we need BLS-signature scheme on bls12-381 curve. +- For some secure hardware secp256r1 == P256 == prime256v1 is needed. + +### secp256k1 + +Bitcoin/ethereum signatures. Uses [libsecp256k1 implementation](https://github.com/bitcoin-core/secp256k1). + +| xxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | +| :---------------------------- | :------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `ECRECOVER` | _`hash v r s - 0 or h x1 x2 -1`_ | Recovers public key from signature, identical to Bitcoin/Ethereum operations.
Takes 32-byte hash as uint256 `hash`; 65-byte signature as uint8 `v` and uint256 `r`, `s`.
Returns `0` on failure, public key and `-1` on success.
65-byte public key is returned as uint8 `h`, uint256 `x1`, `x2`.
_1526 gas_ | + +### secp256r1 + +Uses OpenSSL implementation. Interface is similar to `CHKSIGNS`/`CHKSIGNU`. Compatible with Apple Secure Enclave. + +| xxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | +| :---------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `P256_CHKSIGNS` | _`d sig k - ?`_ | Checks seck256r1-signature `sig` of data portion of slice `d` and public key `k`. Returns -1 on success, 0 on failure.
Public key is a 33-byte slice (encoded according to Sec. 2.3.4 point 2 of [SECG SEC 1](https://www.secg.org/sec1-v2.pdf)).
Signature `sig` is a 64-byte slice (two 256-bit unsigned integers `r` and `s`).
_3526 gas_ | +| `P256_CHKSIGNU` | _`h sig k - ?`_ | Same thing, but the signed data is 32-byte encoding of 256-bit unsigned integer `h`.
_3526 gas_ | + +### Ristretto + +Extended docs are [here](https://ristretto.group/). In short, Curve25519 was developed with performance in mind, however it exhibits symmetry due to which group elements have multiple representations. Simpler protocols such as Schnorr signatures or Diffie-Hellman apply tricks at the protocol level to mitigate some issues, but break key derivation and key blinding schemes. And those tricks do not scale to more complex protocols such as Bulletproofs. Ristretto is an arithmetic abstraction over Curve25519 such that each group element corresponds to a unique point, which is the requirement for most cryptographic protocols. Ristretto is essentially a compression/decompression protocol for Curve25519 that offers the required arithmetic abstraction. As a result, crypto protocols are easy to write correctly, while benefiting from the high performance of Curve25519. + +Ristretto operations allow calculating curve operations on Curve25519 (the reverse is not true), thus we can consider that we add both Ristretto and Curve25519 curve operation in one step. + +[libsodium](https://github.com/jedisct1/libsodium/) implementation is used. + +All ristretto-255 points are represented in TVM as 256-bit unsigned integers. +Non-quiet operations throw `range_chk` if arguments are not valid encoded points. +Zero point is represented as integer `0`. + +| xxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | +| :---------------------------- | :-------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | +| `RIST255_FROMHASH` | _`h1 h2 - x`_ | Deterministically generates a valid point `x` from a 512-bit hash (given as two 256-bit integers).
_626 gas_ | +| `RIST255_VALIDATE` | _`x -`_ | Checks that integer `x` is a valid representation of some curve point. Throws `range_chk` on error.
_226 gas_ | +| `RIST255_ADD` | _`x y - x+y`_ | Addition of two points on a curve.
_626 gas_ | +| `RIST255_SUB` | _`x y - x-y`_ | Subtraction of two points on curve.
_626 gas_ | +| `RIST255_MUL` | _`x n - x*n`_ | Multiplies point `x` by a scalar `n`.
Any `n` is valid, including negative.
_2026 gas_ | +| `RIST255_MULBASE` | _`n - g*n`_ | Multiplies the generator point `g` by a scalar `n`.
Any `n` is valid, including negative.
_776 gas_ | +| `RIST255_PUSHL` | _`- l`_ | Pushes integer `l=2^252+27742317777372353535851937790883648493`, which is the order of the group.
_26 gas_ | +| `RIST255_QVALIDATE` | _`x - 0 or -1`_ | Quiet version of `RIST255_VALIDATE`.
_234 gas_ | +| `RIST255_QADD` | _`x y - 0 or x+y -1`_ | Quiet version of `RIST255_ADD`.
_634 gas_ | +| `RIST255_QSUB` | _`x y - 0 or x-y -1`_ | Quiet version of `RIST255_SUB`.
_634 gas_ | +| `RIST255_QMUL` | _`x n - 0 or x*n -1`_ | Quiet version of `RIST255_MUL`.
_2034 gas_ | +| `RIST255_QMULBASE` | _`n - 0 or g*n -1`_ | Quiet version of `RIST255_MULBASE`.
_784 gas_ | + +### BLS12-381 + +Operations on a pairing friendly BLS12-381 curve. [BLST](https://github.com/supranational/blst) implementation is used. Also, ops for BLS signature scheme which is based on this curve. + +BLS values are represented in TVM in the following way: + +- G1-points and public keys: 48-byte slice. +- G2-points and signatures: 96-byte slice. +- Elements of field FP: 48-byte slice. +- Elements of field FP2: 96-byte slice. +- Messages: slice. Number of bits should be divisible by 8. + +When input value is a point or a field element, the slice may have more than 48/96 bytes. In this case only the first 48/96 bytes are taken. If the slice has less bytes (or if message size is not divisible by 8), cell underflow exception is thrown. + +#### High-level operations + +These are high-level operations for verifying BLS signatures. + +| xxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | +| :---------------------------- | :----------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `BLS_VERIFY` | _`pk msg sgn - bool`_ | Checks BLS signature, return true on success, false otherwise.
_61034 gas_ | +| `BLS_AGGREGATE` | _`sig_1 ... sig_n n - sig`_ | Aggregates signatures. `n>0`. Throw exception if `n=0` or if some `sig_i` is not a valid signature.
_`gas=n*4350-2616`_ | +| `BLS_FASTAGGREGATEVERIFY`- | _`pk_1 ... pk_n n msg sig - bool`_ | Checks aggregated BLS signature for keys `pk_1...pk_n` and message `msg`. Return true on success, false otherwise. Return false if `n=0`.
_`gas=58034+n*3000`_ | +| `BLS_AGGREGATEVERIFY` | _`pk_1 msg_1 ... pk_n msg_n n sgn - bool`_ | Checks aggregated BLS signature for key-message pairs `pk_1 msg_1...pk_n msg_n`. Return true on success, false otherwise. Return false if `n=0`.
_`gas=38534+n*22500`_ | + +`VERIFY` instructions don't throw exception on invalid signatures and public keys (except for cell underflow exceptions), they return false instead. + +#### Low-level operations + +These are arithmetic operations on group elements. + +| xxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | +| :---------------------------- | :---------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `BLS_G1_ADD` | _`x y - x+y`_ | Addition on G1.
_3934 gas_ | +| `BLS_G1_SUB` | _`x y - x-y`_ | Subtraction on G1.
_3934 gas_ | +| `BLS_G1_NEG` | _`x - -x`_ | Negation on G1.
_784 gas_ | +| `BLS_G1_MUL` | _`x s - x*s`_ | Multiplies G1 point `x` by scalar `s`.
Any `s` is valid, including negative.
_5234 gas_ | +| `BLS_G1_MULTIEXP` | _`x_1 s_1 ... x_n s_n n - x_1*s_1+...+x_n*s_n`_ | Calculates `x_1*s_1+...+x_n*s_n` for G1 points `x_i` and scalars `s_i`. Returns zero point if `n=0`.
Any `s_i` is valid, including negative.
_`gas=11409+n*630+n/floor(max(log2(n),4))*8820`_ | +| `BLS_G1_ZERO` | _`- zero`_ | Pushes zero point in G1.
_34 gas_ | +| `BLS_MAP_TO_G1` | _`f - x`_ | Converts FP element `f` to a G1 point.
_2384 gas_ | +| `BLS_G1_INGROUP` | _`x - bool`_ | Checks that slice `x` represents a valid element of G1.
_2984 gas_ | +| `BLS_G1_ISZERO` | _`x - bool`_ | Checks that G1 point `x` is equal to zero.
_34 gas_ | +| `BLS_G2_ADD` | _`x y - x+y`_ | Addition on G2.
_6134 gas_ | +| `BLS_G2_SUB` | _`x y - x-y`_ | Subtraction on G2.
_6134 gas_ | +| `BLS_G2_NEG` | _`x - -x`_ | Negation on G2.
_1584 gas_ | +| `BLS_G2_MUL` | _`x s - x*s`_ | Multiplies G2 point `x` by scalar `s`.
Any `s` is valid, including negative.
_10584 gas_ | +| `BLS_G2_MULTIEXP` | _`x_1 s_1 ... x_n s_n n - x_1*s_1+...+x_n*s_n`_ | Calculates `x_1*s_1+...+x_n*s_n` for G2 points `x_i` and scalars `s_i`. Returns zero point if `n=0`.
Any `s_i` is valid, including negative.
_`gas=30422+n*1280+n/floor(max(log2(n),4))*22840`_ | +| `BLS_G2_ZERO` | _`- zero`_ | Pushes zero point in G2.
_34 gas_ | +| `BLS_MAP_TO_G2` | _`f - x`_ | Converts FP2 element `f` to a G2 point.
_7984 gas_ | +| `BLS_G2_INGROUP` | _`x - bool`_ | Checks that slice `x` represents a valid element of G2.
_4284 gas_ | +| `BLS_G2_ISZERO` | _`x - bool`_ | Checks that G2 point `x` is equal to zero.
_34 gas_ | +| `BLS_PAIRING` | _`x_1 y_1 ... x_n y_n n - bool`_ | Given G1 points `x_i` and G2 points `y_i`, calculates and multiply pairings of `x_i,y_i`. Returns true if the result is the multiplicative identity in FP12, false otherwise. Returns false if `n=0`.
_`gas=20034+n*11800`_ | +| `BLS_PUSHR` | _`- r`_ | Pushes the order of G1 and G2 (approx. `2^255`).
_34 gas_ | + +`INGROUP`, `ISZERO` don't throw exception on invalid points (except for cell underflow exceptions), they return false instead. + +Other arithmetic operations throw exception on invalid curve points. Note that they don't check whether given curve points belong to group G1/G2. Use `INGROUP` instruction to check this. + +## RUNVM + +Currently there is no way for code in TVM to call external untrusted code "in sandbox". In other words, external code always can irreversibly update code, data of contract, or set actions (such as sending all money). +`RUNVM` instruction allows to spawn an independent VM instance, run desired code and get needed data (stack, registers, gas consumption etc) without risks of polluting caller's state. Running arbitrary code in a safe way may be useful for [v4-style plugins](/participate/wallets/contracts#wallet-v4), Tact's `init` style subcontract calculation etc. + +| xxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | +| :---------------------------- | :------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `flags RUNVM` | _`x_1 ... x_n n code [r] [c4] [c7] [g_l] [g_m] - x'_1 ... x'_m exitcode [data'] [c4'] [c5] [g_c]`_ | 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, see below. | +| `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]`_ | Same thing, but pops flags from stack. | + +Flags are similar to `runvmx` in fift: + +- `+1`: set c3 to code +- `+2`: push an implicit 0 before running the code +- `+4`: take `c4` from stack (persistent data), return its final value +- `+8`: take gas limit `g_l` from stack, return consumed gas `g_c` +- `+16`: take `c7` from stack (smart-contract context) +- `+32`: return final value of `c5` (actions) +- `+64`: pop hard gas limit (enabled by ACCEPT) `g_m` from stack +- `+128`: "isolated gas consumption". Child VM will have a separate set of visited cells and a separate chksgn counter. +- `+256`: pop integer `r`, return exactly `r` values from the top of the stack (only if `exitcode=0 or 1`; if not enough then `exitcode=stk_und`) + +Gas cost: + +- 66 gas +- 1 gas for every stack element given to the child VM (first 32 are free) +- 1 gas for every stack element returned from the child VM (first 32 are free) + +## Sending messages + +Currently it is difficult to calculate cost of sending message in contract (which leads to some approximations like in [jettons](https://github.com/ton-blockchain/token-contract/blob/main/ft/jetton-wallet.fc#L94)) and impossible to bounce request back if action phase is incorrect. It is also impossible to accurately subtract from incoming message sum of "constant fee for contract logic" and "gas expenses". + +- `SENDMSG` takes a cell and mode as input. Creates an output action and returns a fee for creating a message. Mode has the same effect as in the case of SENDRAWMSG. Additionally `+1024` means - do not create an action, only estimate fee. Other modes affect the fee calculation as follows: `+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), `+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). +- `SENDRAWMSG`, `RAWRESERVE`, `SETLIBCODE`, `CHANGELIB` - `+16` flag is added, that means in the case of action fail - bounce transaction. No effect if `+2` is used. From 67b3a3255e65890dafd8005080c0031977ee2a04 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:30 +0800 Subject: [PATCH 176/219] New translations readme.md (Chinese Simplified) --- .../current/participate/README.md | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/README.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/README.md b/i18n/zh/docusaurus-plugin-content-docs/current/participate/README.md new file mode 100644 index 0000000000..67b91a7a90 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/README.md @@ -0,0 +1,38 @@ +# Overview + +The participate section of our documentation is tailored to allow users to discover the capabilities of working with TON +It is also meant to offer essential frameworks (explorers, wallets, TEPs) that are necessary to participate in TON Ecosystem and the creation of TON’s vision for the TON World Wide Web (TWWW). + +## Participate in TON Ecosystem + +- [TON Explorers](/participate/explorers) +- [Wallet Apps (for Developers)](/participate/wallets/apps) +- [Types of Wallet Contracts](/participate/wallets/contracts) + +### Join TON Community + +- [TON Enhancement Proposals (TEPs)](https://github.com/ton-blockchain/TEPs) +- [Discover TON innovations at TON Research](https://tonresear.ch/) +- [Stake with TON Nominators](/participate/network-maintenance/nominators) + +### Bridges + +- [Bridges Overview](/participate/crosschain/overview) +- [Bridge Addresses](/participate/crosschain/bridge-addresses) + +### Run a Node + +- [Discover Node Types in TON](/participate/nodes/node-types) +- [Run your Full Node or Validator](/participate/run-nodes/full-node) +- [TON Validator maintenance & security](/participate/nodes/node-maintenance-and-security) + +## Participate in TON Web3 + +- [TON Web3 Overview](/participate/web3/overview) +- [Use TON DNS for your domains](/participate/web3/dns) +- [Site & Domain management](/participate/web3/site-management) +- [\[Tutorial\] How to run your own TON Site?](/develop/dapps/tutorials/how-to-run-ton-site) + +### TON Proxy + +- [How to open any TON Site?](/participate/web3/how-to-open-any-ton-site) From ce57febe2b2521a4d0532f16b5e6cefbdb9c3698 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:31 +0800 Subject: [PATCH 177/219] New translations bridge-addresses.md (Chinese Simplified) --- .../crosschain/bridge-addresses.md | 211 ++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/crosschain/bridge-addresses.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/crosschain/bridge-addresses.md b/i18n/zh/docusaurus-plugin-content-docs/current/participate/crosschain/bridge-addresses.md new file mode 100644 index 0000000000..89108e7e7d --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/crosschain/bridge-addresses.md @@ -0,0 +1,211 @@ +# Bridges Addresses + +:::caution +To accurately get the current addresses of bridge smart contracts, see them directly in the [blockchain config](/participate/crosschain/overview#blockchain-configs). It's the most secure way. +::: + +## Toncoin Mainnet + +### Mainnet TON-Ethereum Toncoin Bridge + +Wrapped TONCOIN Address - [0x582d872a1b094fc48f5de31d3b73f2d9be47def1](https://etherscan.io/token/0x582d872a1b094fc48f5de31d3b73f2d9be47def1) + +Bridge Address - [Ef_dJMSh8riPi3BTUTtcxsWjG8RLKnLctNjAM4rw8NN-xWdr](https://tonscan.org/address/Ef_dJMSh8riPi3BTUTtcxsWjG8RLKnLctNjAM4rw8NN-xWdr) + +Collector Address - [EQCuzvIOXLjH2tv35gY4tzhIvXCqZWDuK9kUhFGXKLImgxT5](https://tonscan.org/address/EQCuzvIOXLjH2tv35gY4tzhIvXCqZWDuK9kUhFGXKLImgxT5) + +Governance Address - [Ef87m7_QrVM4uXAPCDM4DuF9Rj5Rwa5nHubwiQG96JmyAjQY](https://tonscan.org/address/Ef87m7_QrVM4uXAPCDM4DuF9Rj5Rwa5nHubwiQG96JmyAjQY) + +### Mainnet TON-BSC Toncoin Bridge + +Wrapped TONCOIN Address - [0x76A797A59Ba2C17726896976B7B3747BfD1d220f](https://bscscan.com/token/0x76A797A59Ba2C17726896976B7B3747BfD1d220f) + +Bridge Address - [Ef9NXAIQs12t2qIZ-sRZ26D977H65Ol6DQeXc5_gUNaUys5r](https://tonscan.org/address/Ef9NXAIQs12t2qIZ-sRZ26D977H65Ol6DQeXc5_gUNaUys5r) + +Collector Address - [EQAHI1vGuw7d4WG-CtfDrWqEPNtmUuKjKFEFeJmZaqqfWTvW](https://tonscan.org/address/EQAHI1vGuw7d4WG-CtfDrWqEPNtmUuKjKFEFeJmZaqqfWTvW) + +Governance Address - [Ef8OvX_5ynDgbp4iqJIvWudSEanWo0qAlOjhWHtga9u2YjVp](https://tonscan.org/address/Ef8OvX_5ynDgbp4iqJIvWudSEanWo0qAlOjhWHtga9u2YjVp) + +### Mainnet Toncoin Oracles + +Oracle 0 - [Ef_P2CJw784O1qVd8Qbn8RCQc4EgxAs8Ra-M3bDhZn3OfzRb](https://tonscan.org/address/Ef_P2CJw784O1qVd8Qbn8RCQc4EgxAs8Ra-M3bDhZn3OfzRb) + +Oracle 1 - [Ef8DfObDUrNqz66pr_7xMbUYckUFbIIvRh1FSNeVSLWrvo1M](https://tonscan.org/address/Ef8DfObDUrNqz66pr_7xMbUYckUFbIIvRh1FSNeVSLWrvo1M) + +Oracle 2 - [Ef8JKqx4I-XECLuVhTqeY1WMgbgTp8Ld3mzN-JUogBF4ZEW-](https://tonscan.org/address/Ef8JKqx4I-XECLuVhTqeY1WMgbgTp8Ld3mzN-JUogBF4ZEW-) + +Oracle 3 - [Ef8voAFh-ByCeKD3SZhjMNzioqCmDOK6S6IaeefTwYmRhgsn](https://tonscan.org/address/Ef8voAFh-ByCeKD3SZhjMNzioqCmDOK6S6IaeefTwYmRhgsn) + +Oracle 4 - [Ef_uJVTTToU8b3o7-Jr5pcUqenxWzDNYpyklvhl73KSIA17M](https://tonscan.org/address/Ef_uJVTTToU8b3o7-Jr5pcUqenxWzDNYpyklvhl73KSIA17M) + +Oracle 5 - [Ef93olLWqh1OuBSTOnJKWZ4NwxNq_ELK55_h_laNPVwxcEro](https://tonscan.org/address/Ef93olLWqh1OuBSTOnJKWZ4NwxNq_ELK55_h_laNPVwxcEro) + +Oracle 6 - [Ef_iUPZdKLOCrqcNpDuFGNEmiuBwMB18TBXNjDimewpDExgn](https://tonscan.org/address/Ef_iUPZdKLOCrqcNpDuFGNEmiuBwMB18TBXNjDimewpDExgn) + +Oracle 7 - [Ef_tTGGToGmONePskH_Y6ZG-QLV9Kcg5DIXeKwBvCX4YifKa](https://tonscan.org/address/Ef_tTGGToGmONePskH_Y6ZG-QLV9Kcg5DIXeKwBvCX4YifKa) + +Oracle 8 - [Ef94L53akPw-4gOk2uQOenUyDYLOaif2g2uRoiu1nv0cWYMC](https://tonscan.org/address/Ef94L53akPw-4gOk2uQOenUyDYLOaif2g2uRoiu1nv0cWYMC) + +EVM Addresses: 0xC4c9bd836ab8b446519736166919e3d62491E041,0xCF4A7c26186aA41390E246FA04115A0495085Ab9,0x17DcaB1B1481610F6C7a7A98cf0370dC0EC704a6,0x32162CAaEd276E77EF63194820586C942009a962,0x039f4e886432bd4f3cb5062f9861EFef3F6aDA28,0xFf441F9889Aa475d9D3b1C638C59B84c5179846D,0x0933738699dc733C46A0D4CBEbDA2f842e1Ac7d9,0x7F2bbaaC14F0f1834E6D0219F8855A5F619Fe2C4,0xfc5c6A2d01A984ba9eab7CF87A6D169aA9720c0C. + +## Toncoin Testnet + +### Testnet TON-Ethereum Toncoin Bridge + +Wrapped TONCOIN Address - [0xDB15ffaf2c88F2d89Db9365a5160D5b8c9448Ea6](https://goerli.etherscan.io/token/0xDB15ffaf2c88F2d89Db9365a5160D5b8c9448Ea6) + +Bridge Address - [Ef-56ZiqKUbtp_Ax2Qg4Vwh7yXXJCO8cNJAb229J6XXe4-aC](https://testnet.tonscan.org/address/Ef-56ZiqKUbtp_Ax2Qg4Vwh7yXXJCO8cNJAb229J6XXe4-aC) + +Collector Address - [EQCA1W_I267-luVo9CzV7iCcrA1OO5vVeXD0QHACvBn1jIVU](https://testnet.tonscan.org/address/EQCA1W_I267-luVo9CzV7iCcrA1OO5vVeXD0QHACvBn1jIVU) + +Governance Address - [kf-OV1dpgFVEzEmyvAETT8gnhqZ1IqHn8RzT6dmEmvnze-9n](https://testnet.tonscan.org/address/kf-OV1dpgFVEzEmyvAETT8gnhqZ1IqHn8RzT6dmEmvnze-9n) + +### Testnet TON-BSC Toncoin Bridge + +Wrapped TONCOIN Address - [0xdb15ffaf2c88f2d89db9365a5160d5b8c9448ea6](https://testnet.bscscan.com/token/0xdb15ffaf2c88f2d89db9365a5160d5b8c9448ea6) + +Bridge Address - [Ef_GmJntTDokxfhLGF1jRvMGC8Jav2V5keoNj4El2jzhHsID](https://testnet.tonscan.org/address/Ef_GmJntTDokxfhLGF1jRvMGC8Jav2V5keoNj4El2jzhHsID) + +Collector Address - [EQDBNfV4DQzSyzNMw6BCTSZSoUi-CzWcYNsfhKxoDqfrwFtS](https://testnet.tonscan.org/address/EQDBNfV4DQzSyzNMw6BCTSZSoUi-CzWcYNsfhKxoDqfrwFtS) + +Governance Address - [kf83VnnXuaqQV1Ts2qvUr6agacM0ydOux5NNa1mcU-cEO693](https://testnet.tonscan.org/address/kf83VnnXuaqQV1Ts2qvUr6agacM0ydOux5NNa1mcU-cEO693) + +### Testnet Toncoin Oracles + +- Oracle 0 + + TON Address - [Ef9fwskZLEuGDfYTRAtvt9k-mEdkaIskkUOsEwPw1wzXk7zR](https://testnet.tonscan.org/address/Ef9fwskZLEuGDfYTRAtvt9k-mEdkaIskkUOsEwPw1wzXk7zR) + + EVM Address - 0xe54cd631c97be0767172ad16904688962d09d2fe + +- Oracle 1 + + TON Address - [Ef8jPzrhTYloKgTCsGgEFNx7OdH-sJ98etJnwrIVSsFxH9mu](https://testnet.tonscan.org/address/Ef8jPzrhTYloKgTCsGgEFNx7OdH-sJ98etJnwrIVSsFxH9mu) + + EVM Addresss - 0xeb05E1B6AC0d574eF2CF29FDf01cC0bA3D8F9Bf1 + +- Oracle 2 + + TON Address - [Ef-fxGCPuPKNR6T4GcFxNQuLU5TykLKEAtkxWEfA1wBWy6JE](https://testnet.tonscan.org/address/Ef-fxGCPuPKNR6T4GcFxNQuLU5TykLKEAtkxWEfA1wBWy6JE) + + EVM Address - 0xF636f40Ebe17Fb2A1343e5EEee9D13AA90888b51 + +## Token Mainnet + +### Mainnet TON-Ethereum Token Bridge + +Ethereum Bridge Address - [0xb323692b6d4DB96af1f52E4499a2bd0Ded9af3C5](https://etherscan.io/address/0xb323692b6d4DB96af1f52E4499a2bd0Ded9af3C5) + +Bridge Address - [Ef-1JetbPF9ubc1ga-57oHoOyDA1IShJt-BVlJnA9rrVTfrB](https://tonscan.org/address/Ef-1JetbPF9ubc1ga-57oHoOyDA1IShJt-BVlJnA9rrVTfrB) + +Collector Address - [EQDF6fj6ydJJX_ArwxINjP-0H8zx982W4XgbkKzGvceUWvXl](https://tonscan.org/address/EQDF6fj6ydJJX_ArwxINjP-0H8zx982W4XgbkKzGvceUWvXl) + +Governance Address - [Ef8hHxV0v2I9FHh3CMX91WXjKaJav6SQlemEQm8ZvPBJdLde](https://tonscan.org/address/Ef8hHxV0v2I9FHh3CMX91WXjKaJav6SQlemEQm8ZvPBJdLde) + +### Mainnet Token Oracles + +- Oracle 0 + + TON Public Key = a0993546fbeb4e8c90eeab0baa627659aee01726809707008e38d5742ea38aef + + TON Address - [Ef8WxwYOyAk-H0YGBc70gZFJc6oqUvcHywU-yJNBfSNh-GW9](https://tonscan.org/address/Ef8WxwYOyAk-H0YGBc70gZFJc6oqUvcHywU-yJNBfSNh-GW9) + + ETH Address - 0x3154E640c56D023a98890426A24D1A772f5A38B2 + +- Oracle 1 + + TON Public Key = fe0a78726a82754b62517e4b7a492e1b1a8d4c9014955d2fa8f1345f1a3eafba + + TON Address = [Ef8CbgwhUMYn2yHU343dcezKkvme3cyFJB7SHVY3FXhU9jqj](https://tonscan.org/address/Ef8CbgwhUMYn2yHU343dcezKkvme3cyFJB7SHVY3FXhU9jqj) + + ETH Address = 0x8B06A5D37625F41eE9D9F543482b6562C657EA6F + +- Oracle 2 + + TON Public Key = 00164233e111509b0486df85d2743defd6e2525820ee7d341c8ad92ee68d41a6 + + TON Address = [Ef-n3Vdme6nSe4FBDb3inTRF9B6lh3BbIwGlk0dDpUO5oFmH](https://tonscan.org/address/Ef-n3Vdme6nSe4FBDb3inTRF9B6lh3BbIwGlk0dDpUO5oFmH) + + ETH Address = 0x6D5E361F7E15ebA73e41904F4fB2A7d2ca045162 + +- Oracle 3 + + TON Public Key = 9af68ce3c030e8d21aae582a155a6f5c41ad006f9f3e4aacbb0ce579982b9ebb + + TON Address = [Ef9D1-FOb82pREFPgW7AlzNlZ7f0XnvmGakW23wpWeILAum9](https://tonscan.org/address/Ef9D1-FOb82pREFPgW7AlzNlZ7f0XnvmGakW23wpWeILAum9) + + ETH Address = 0x43931B8c29e34a8C16695408CD56327F511Cf086 + +- Oracle 4 + + TON Public Key = a4fef528b1e841f5fce752feeac0971f7df909e37ffeb3fab71c5ce8deb9f7d4 + + TON Address = [Ef8TBPHHIowG5pGgSVX8n4KmOaX-EEjvnOSBRlQvVsJWP_WJ](https://tonscan.org/address/Ef8TBPHHIowG5pGgSVX8n4KmOaX-EEjvnOSBRlQvVsJWP_WJ) + + ETH Address = 0x7a0d3C42f795BA2dB707D421Add31deda9F1fEc1 + +- Oracle 5 + + TON Public Key = 58a7ab3e3ff8281b668a86ad9fe8b72f2d14df5dcc711937915dacca1b94c07d + + TON Address = [Ef8ceN7cTemTe4ZV6AIbg5f8LsHZsYV1UaiGntvkME0KtP45](https://tonscan.org/address/Ef8ceN7cTemTe4ZV6AIbg5f8LsHZsYV1UaiGntvkME0KtP45) + + ETH Address = 0x88352632350690EF22F9a580e6B413c747c01FB2 + +- Oracle 6 + + TON Public Key = db60c3f50cb0302b516cd42833c7e8cad8097ad94306564b057b16ace486fb07 + + TON Address = [Ef8uDTu2WCcJdtuKmkDmC1yRKVxZrTp83ke5PnMECOccg3w4](https://tonscan.org/address/Ef8uDTu2WCcJdtuKmkDmC1yRKVxZrTp83ke5PnMECOccg3w4) + + ETH Address = 0xeB8975966dAF0C86721C14b8Bb7DFb89FCBB99cA + +- Oracle 7 + + TON Public Key = 98c037c6d3a92d9467dc62c0e3da9bb0ad08c6b3d1284d4a37c1c5c0c081c7df + + TON Address = [Ef905jDDX87nPDbTSMqFB9ILVGX1zWc66PPrNhkjHrWxAnZZ](https://tonscan.org/address/Ef905jDDX87nPDbTSMqFB9ILVGX1zWc66PPrNhkjHrWxAnZZ) + + ETH Address = 0x48Bf4a783ECFb7f9AACab68d28B06fDafF37ac43 + +- Oracle 8 + + TON Public Key = 5503c54a1b27525376e83d6fc326090c7d9d03079f400071b8bf05de5fbba48d + + TON Address = [Ef9Ubg96xQ8jVKbl7QQJ1k8pClQLmO1Ci68nuNfbLdm9uS-x](https://tonscan.org/address/Ef9Ubg96xQ8jVKbl7QQJ1k8pClQLmO1Ci68nuNfbLdm9uS-x) + + ETH Address = 0x954AE64BB0268b06ffEFbb6f454867a5F2CB3177 + +## Token Testnet + +### Testnet TON-Ethereum Token Bridge + +Ethereum Bridge Address - [0x4Efd8f04B6fb4CFAF0cfaAC11Fb489b97DBebB60](https://goerli.etherscan.io/address/0x4Efd8f04B6fb4CFAF0cfaAC11Fb489b97DBebB60) + +Bridge Address - [Ef-lJBALjXSSwSKiedKzriSHixwQUxJ1BxTE05Ur5AXwZVjp](https://testnet.tonscan.org/address/Ef-lJBALjXSSwSKiedKzriSHixwQUxJ1BxTE05Ur5AXwZVjp) + +Collector Address - [EQC1ZeKX1LNrlQ4bwi3je3KVM1AoZ3rkeyHM5hv9pYzmIh4v](https://testnet.tonscan.org/address/EQC1ZeKX1LNrlQ4bwi3je3KVM1AoZ3rkeyHM5hv9pYzmIh4v) + +Governance Address - [kf9NLH8CsGUkEKGYzCxaLd9Th6T5YkO-MXsCEU9Rw1fiRhf9](https://testnet.tonscan.org/address/kf9NLH8CsGUkEKGYzCxaLd9Th6T5YkO-MXsCEU9Rw1fiRhf9) + +### Testnet Token Oracles + +> Same with Toncoin Testnet Bridge + +- Oracle 0 + + TON Address - [Ef9fwskZLEuGDfYTRAtvt9k-mEdkaIskkUOsEwPw1wzXk7zR](https://testnet.tonscan.org/address/Ef9fwskZLEuGDfYTRAtvt9k-mEdkaIskkUOsEwPw1wzXk7zR) + + EVM Address - 0xe54cd631c97be0767172ad16904688962d09d2fe + +- Oracle 1 + + TON Address - [Ef8jPzrhTYloKgTCsGgEFNx7OdH-sJ98etJnwrIVSsFxH9mu](https://testnet.tonscan.org/address/Ef8jPzrhTYloKgTCsGgEFNx7OdH-sJ98etJnwrIVSsFxH9mu) + + EVM Addresss - 0xeb05E1B6AC0d574eF2CF29FDf01cC0bA3D8F9Bf1 + +- Oracle 2 + + TON Address - [Ef-fxGCPuPKNR6T4GcFxNQuLU5TykLKEAtkxWEfA1wBWy6JE](https://testnet.tonscan.org/address/Ef-fxGCPuPKNR6T4GcFxNQuLU5TykLKEAtkxWEfA1wBWy6JE) + + EVM Address - 0xF636f40Ebe17Fb2A1343e5EEee9D13AA90888b51 From 565cdbaa2da74fa214aa520cc420931084484c37 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:32 +0800 Subject: [PATCH 178/219] New translations overview.md (Chinese Simplified) --- .../participate/crosschain/overview.md | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/crosschain/overview.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/crosschain/overview.md b/i18n/zh/docusaurus-plugin-content-docs/current/participate/crosschain/overview.md new file mode 100644 index 0000000000..0621c3e7e8 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/crosschain/overview.md @@ -0,0 +1,57 @@ +# Cross-chain bridges + +Decentralized cross-chain bridges operate on TON Blockchain, allowing you to transfer assets from TON Blockchain to other blockchains and vice versa. + +## Toncoin Bridge + +The Toncoin bridge allows you to transfer Toncoin between TON Blockchain and the Ethereum blockchain, as well as between TON Blockchain and the BNB Smart Chain. + +The bridge is managed by [decentralized oracles](/participate/crosschain/bridge-addresses). + +### How to use it? + +Bridge frontend is hosted on https://ton.org/bridge. + +:::info +[Bridge frontend source code](https://github.com/ton-blockchain/bridge) +::: + +### TON-Ethereum smart contracts source codes + +- [FunC (TON side)](https://github.com/ton-blockchain/bridge-func) +- [Solidity (Ethereum side)](https://github.com/ton-blockchain/bridge-solidity/tree/eth_mainnet) + +### TON-BNB Smart Chain smart contracts source codes + +- [FunC (TON side)](https://github.com/ton-blockchain/bridge-func/tree/bsc) +- [Solidity (BSC side)](https://github.com/ton-blockchain/bridge-solidity/tree/bsc_mainnet) + +### Blockchain Configs + +You can get the actual bridge smart contract addresses and oracle addresses by inspecting the corresponding config: + +TON-Ethereum: [#71](https://github.com/ton-blockchain/ton/blob/35d17249e6b54d67a5781ebf26e4ee98e56c1e50/crypto/block/block.tlb#L738). + +TON-BSC: [#72](https://github.com/ton-blockchain/ton/blob/35d17249e6b54d67a5781ebf26e4ee98e56c1e50/crypto/block/block.tlb#L739). + +TON-Polygon: [#73](https://github.com/ton-blockchain/ton/blob/35d17249e6b54d67a5781ebf26e4ee98e56c1e50/crypto/block/block.tlb#L740). + +### Documentation + +- [How the bridge works](https://github.com/ton-blockchain/TIPs/issues/24) + +### Cross-chain roadmap + +- https://t.me/tonblockchain/146 + +## Tonana Bridge + +### How to participate? + +:::caution draft\ +This is a concept article. We're still looking for someone experienced to write it. +::: + +You can find the front-end here: https://tonana.org/ + +The source code is here: https://github.com/tonanadao From 14b6a4fa7ca642e4678a007579f3c1d05e5d462b Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:33 +0800 Subject: [PATCH 179/219] New translations explorers.mdx (Chinese Simplified) --- .../current/participate/explorers.mdx | 155 ++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/explorers.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/explorers.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/participate/explorers.mdx new file mode 100644 index 0000000000..694941fd3a --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/explorers.mdx @@ -0,0 +1,155 @@ +import Player from '@site/src/components/player' + +# Explorers in TON + +In this article, we will consider TON explorers, their capabilities and features from the point of view of the developer. + +## What is an explorer? + +An explorer is a website that allows you to view information in a blockchain, such as the account balance, transaction history, blocks, etc. + + + +## Which explorers exist? + +Among TON explorers, you can distinguish several categories: + +- For everyday use +- With extended information for developers +- Specialized + +This division into categories is largely conditional and one explorer can belong to several categories at the same time. So let's not pay too much attention to this. + +## Address Alias in Explorers + +Make your service address more user-friendly by using an address alias. Create Pull Requests (PRs) according to the provided guidelines: + +- [tonkeeper/ton-assets](https://github.com/tonkeeper/ton-assets) - Tonviewer.com alias +- [catchain/address-book](https://github.com/catchain/address-book)- Tonscan.org alias + +## General functionality + +Let's start with the general functionality that is present in all explorers. + +Almost all explorers have the ability to find out information about balances, transaction history and information about the smart contract, if deployed on the address. + +Next, we will consider several explorers that can be attributed to each of these categories. + +## TON Scan.org + +Good explorer for everyday use. It provides a comprehensive view of the TON Blockchain, allowing users to search for transactions, addresses, blocks, and more. Any search is performed against the public [address book](https://github.com/catchain/tonscan/blob/master/src/addrbook.json) (TON Foundation, OKX and etc.) + +### Features + +- **Convenient for everyday use** +- Convenient for developers +- TON DNS support +- Сontract types +- Contract disassembler + +### Links + +- URL: https://tonscan.org/ +- Testnet URL: https://testnet.tonscan.org/ + +## TON Scan.com + +### Features + +- Transaction aggregation analytics +- Transaction Traces + +### Links + +- URL: https://tonscan.com/ + +## Ton Whales Explorer + +This explorer is more oriented towards developers than ordinary users. + +### Features + +- **Convenient for developers** +- Сontract types +- Contract disassembler + +### Links + +- URL: https://tonwhales.com/explorer + +## Tonviewer Explorer + +This explorer is the newest and has its own unique features. +For example, Trace. This feature allows you to see the entire sequence of transactions between smart contracts, even if subsequent transactions do not contain your address. + +Transaction information is not as detailed as, for example, on TON Whales. + +### Features + +- Convenient for developers +- Convenient for everyday use +- Jetton transaction history +- **Trace** +- TON DNS support + +### Links + +- URL: https://tonviewer.com/ +- Testnet URL: https://testnet.tonviewer.com/ + +## TON NFT EXPLORER + +This explorer specializes in NFTs, but it can also be used as a regular explorer. + +When viewing the wallet address, you can find out which NFT it stores and, when viewing the NFT, you can find out the metadata, collection address, owner and transaction history. + +### Features + +- Convenient for developers +- Сontract types +- **Specialized in NFT** + +### Links + +- URL: https://explorer.tonnft.tools/ +- Testnet URL: https://testnet.explorer.tonnft.tools/ + +## DTON + +DTON is another explorer for developers. It provides a lot of information about transactions in a convenient form. + +Also, it has a feature that allows you to see the computation phase of the transaction step by step. + +### Features + +- Convenient for developers +- Extended information about the computation phase +- Сontract types +- Contract disassembler + +### Links + +- URL: https://dton.io/ + +## TON NFTscan + +This explorer is specifically designed for Non-Fungible Tokens (NFTs) on the TON Blockchain. It allows users to explore, track, and verify NFT transactions, contracts, and metadata. + +### Features + +- Convenient for regular users +- Useful information for traders, such as daily volume +- NFT collection ranking + +### Links + +- URL: https://ton.nftscan.com/ + +## Want to be in this list? + +Please, write to one of the [maintainers](/docs/contribute/maintainers.md). + +## References + +- [ton.app/explorers](https://ton.app/explorers) +- [Awesome TON repository](https://github.com/ton-community/awesome-ton) From cfccfe2c9efbe7647fbf1215df07a1c8df1373d3 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:34 +0800 Subject: [PATCH 180/219] New translations nominators.md (Chinese Simplified) --- .../network-maintenance/nominators.md | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/nominators.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/nominators.md b/i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/nominators.md new file mode 100644 index 0000000000..3d219b748e --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/nominators.md @@ -0,0 +1,45 @@ +# Staking with Nominator Pools + +## Overview + +With TON smart contracts, you can implement any staking and deposit mechanics you want. + +However, there is "native staking" in TON Blockchain - you can lend Toncoin to validators for staking and share the reward for validation. + +The one who lends to validator is called the **nominator**. + +A smart contract, called a **nominator pool**, provides the ability for one or more nominators to lend Toncoin in a validator stake, and ensures that the validator can use that Toncoin only for validation. Also, the smart contract guarantees the distribution of the reward. + +## Validators vs Nominators + +If you are familiar with cryptocurrencies, you must have heard about **validators** and **nominators**. These words often appear in crypto-related channels (our channel is no exception). Now, the time has come to find out what they are – the two major actors ruling the blockchain. + +### Validators + +First, let's speak about validators. A validator is a network node that helps keep the blockchain running by verifying (or validating) suggested blocks and recording them on the blockchain. + +To become a validator, you must meet two requirements: have a high-performance server and obtain a serious amount of TON (600,000) in order to make a stake. At the time of writing, there are 227 validators on TON. + +### Nominators + +:::info +New version of Nominator Pool available, read more in the Single Nominator and Vesting Contract pages. +::: + +It's evident that not everyone can afford to have 100,000s of Toncoin on their balance – here's where nominators come into play. Simply put, the nominator is a user who lends his TON to validators. Every time the validator earns a reward by validating blocks, it is distributed between the involved participants. + +Some time ago, Ton Whales ran the first staking pool on TON with a minimum deposit of 50 TON. Later, TON Foundation launched the first open nominator pool. Now, users may stake Toncoin in a fully-decentralized way, starting with **10,000 TON**. + +_From [TON Community post](https://t.me/toncoin/543)._ + +## How to participate? + +- [The list of the TON nominator pools](https://tonvalidators.org/) + +## Source code + +- [Nominator Pool smart contract source code](https://github.com/ton-blockchain/nominator-pool) + +:::info +The theory of nominators is described in [TON Whitepaper](https://docs.ton.org/ton.pdf), chapters 2.6.3, 2.6.25. +::: From 5d048105382af9e09d0efad02ca0e3961a867c85 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:35 +0800 Subject: [PATCH 181/219] New translations persistent-states.md (Chinese Simplified) --- .../network-maintenance/persistent-states.md | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/persistent-states.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/persistent-states.md b/i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/persistent-states.md new file mode 100644 index 0000000000..13e11c59fc --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/persistent-states.md @@ -0,0 +1,27 @@ +# Persistent States + +Nodes store snapshots of states of the blockchain periodically. Each state is created at some masterchain block and has some TTL. The block and TTL are chosen using the following algorithm: + +Only key blocks can be chosen. A block has some timestamp `ts`. There are periods of time of length `2^17` seconds (approximately up to 1.5 days). The period of a block with timestamp `ts` is `x = floor(ts / 2^17)`. The first key block from each period is chosen to create a persistent state. + +TTL of a state from period `x` is equal to `2^(18 + ctz(x))`, where `ctz(x)` is the number of trailing zeroes in the binary representation of `x` (i.e. the largest `y` such that `x` is divisible by `2^y`). + +That means that persistent states are created every 1.5 days, half of them have TTL of `2^18` seconds (3 days), half of the remaining states have TTL of `2^19` seconds (6 days) and so on. + +In 2022 there is the following long-term (at least 3 months) persistent states (times in the future are approximate): + +| Block seqno | Block time | TTL | Expires at | +| ----------: | --------------------------------------------------: | --------: | --------------------------------------------------: | +| 18155329 | 2022-02-07 01:31:53 | 777 days | 2024-03-24 18:52:57 | +| 19365422 | 2022-03-27 14:36:58 | 97 days | 2022-07-02 16:47:06 | +| | 2022-05-14 20:00:00 | 194 days | 2022-11-24 23:00:00 | +| | 2022-07-02 09:00:00 | 97 days | 2022-10-07 10:00:00 | +| | 2022-08-19 22:00:00 | 388 days | 2023-09-12 06:00:00 | +| | 2022-10-07 11:00:00 | 97 days | 2023-01-12 12:00:00 | +| | 2022-11-25 00:00:00 | 194 days | 2023-06-07 03:00:00 | +| | 2022-11-25 00:00:00 | 194 days | 2023-06-07 03:00:00 | +| 27747086 | 2022-03-02 05:08:11 | 1553 days | 2027-06-02 18:50:19 | + +When the node starts for the first time, it has to download a persistent state. This is implemented in [validator/manager-init.cpp](https://github.com/ton-blockchain/ton/blob/master/validator/manager-init.cpp). + +Starting from the init block, the node downloads all newer key blocks. It selects the most recent key block with a persistent state which still exists (using the formula above), and then downloads the corresponding masterchain state and states for all shards (or only the shards that are required for this node). From b1619271456840b0e4ef06e0856daf7bdce11e60 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:35 +0800 Subject: [PATCH 182/219] New translations single-nominator.mdx (Chinese Simplified) --- .../network-maintenance/single-nominator.mdx | 184 ++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/single-nominator.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/single-nominator.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/single-nominator.mdx new file mode 100644 index 0000000000..1dc5501c6a --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/single-nominator.mdx @@ -0,0 +1,184 @@ +# 单一提名者池 + +## Using mytonctrl + +Currently [mytonctrl](https://github.com/ton-blockchain/mytonctrl) supports `single_nominator` contracts, but firstly you need to install mytonctrl 2.0. + +### 准备验证者 + +If you already have installed mytonctrl just use command `update mytonctrl2`. If you have no mytonctrl installed, follow these steps: + +1. Download installation script: + +```bash +wget https://raw.githubusercontent.com/ton-blockchain/mytonctrl/mytonctrl2/scripts/install.sh +``` + +2. Run installation script: + +```bash +sudo bash ./install.sh -b mytonctrl2 +``` + +### Set up single-nominator + +After you have created and activated validator's wallet, follow these steps: + +1. 安装[nodejs](https://nodejs.org/en) v.16及以后版本和npm([详细说明](https://github.com/nodesource/distributions#debian-and-ubuntu-based-distributions)) + +```bash +MyTonCtrl> enable_mode single-nominator +``` + +2. 创建符号链接: + +```bash +MyTonCtrl> new_single_pool +``` + +3. 运行测试以确保一切设置正确: + +4. Send 1 TON to the pool and activate it + +```bash +MyTonCtrl> activate_single_pool +``` + +Now you can work with this pool via mytonctrl like with a standard nominator pool. + +## 创建single_nominator池 + +#### Prepare launched Validator + + If you have mytonctrl installed and validator running: + +1. 创建部署者地址: + +#### Prepare from the beginning + + If you had no Validator before, do the following: + +1. [Run a Validator](/participate/run-nodes/full-node) and make sure it's synced. +2. Stop validation and withdraw all funds. + +## Prepare single_nominator + +1. Install [nodejs](https://nodejs.org/en) v.16 and later and npm ([detailed instructions](https://github.com/nodesource/distributions#debian-and-ubuntu-based-distributions)) +2. Install `ts-node` and `arg` module + +```bash +$ sudo apt install ts-node +$ sudo npm i arg -g +``` + +4. 备份部署者私钥"./build/deploy.config.json"和"single-nominator.addr"文件 + +```bash +$ sudo ln -s /usr/bin/ton/crypto/fift /usr/local/bin/fift +$ sudo ln -s /usr/bin/ton/crypto/func /usr/local/bin/func +``` + +5. Run test to make sure everything is set up properly: + +```bash +$ npm run test +``` + +6. Replace mytonctrl nominator-pool scripts: https://raw.githubusercontent.com/orbs-network/single-nominator/main/mytonctrl-scripts/install-pool-scripts.sh + +## Create single_nominator Pool + +1. Get Toncenter API key from a Telegram [@tonapibot](https://t.me/tonapibot) +2. Set env variables: + +```bash +export OWNER_ADDRESS= +export VALIDATOR_ADDRESS= +export TON_ENDPOINT=https://toncenter.com/api/v2/jsonRPC +export TON_API_KEY= +``` + +2. Create deployer address: + +```bash +$ npm run init-deploy-wallet +Insufficient Deployer [EQAo5U...yGgbvR] funds 0 +``` + +3. Topup deployer address with 2.1 TON +4. Deploy pool contract, you will get pool address: `Ef-kC0..._WLqgs`: + +``` +$ npm run deploy +``` + +5. Convert address to .addr: + +``` +$ fift -s ./scripts/fift/str-to-addr.fif Ef-kC0..._WLqgs +``` + +b. + +6. Backup deployer private key "./build/deploy.config.json" and "single-nominator.addr" files +7. Copy "single-nominator.addr" to "mytoncore/pools/single-nominator-1.addr" +8. Send stake from owner address to single nominator address + +## Withdrawals from Single Nominator + +Using wallets to withdraw from single_nominator +Fift: + +1. Create "withdraw.boc" request with amount: + +```bash +$ fift -s ./scripts/fift/withdraw.fif +``` + +2. [Single Nominator 池合约](https://github.com/orbs-network/single-nominator) + +```bash +$ fift -s wallet-v3.fif -B withdraw.boc +``` + +3. Broadcast query: + +```bash +$ lite-client -C global.config.json -c 'sendfile wallet-query.boc' +tons +``` + +1. Create "withdraw.boc" request with amount: + +```bash +$ fift -s ./scripts/fift/withdraw.fif +``` + +2. Send request to single nominator address: + +a. + +```bash +$ tons wallet transfer --body withdraw.boc +tonkeeper +``` + +b. + +``` +npm link typescript +``` + +c. + +``` +npx ts-node scripts/ts/withdraw-deeplink.ts +``` + +d. Open deeplink on the owner's phone + +## See Also + +- [Single Nominator Pool contract](https://github.com/orbs-network/single-nominator) +- [Vesting Contract](/participate/network-maintenance/vesting-contract) +- [single-nominator-quick-how-to-09-25](https://telegra.ph/single-nominator-quick-how-to-09-25) From 1f4b9dae81ab48351e46a73d91792b542b241654 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:36 +0800 Subject: [PATCH 183/219] New translations staking-incentives.md (Chinese Simplified) --- .../network-maintenance/staking-incentives.md | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/staking-incentives.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/staking-incentives.md b/i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/staking-incentives.md new file mode 100644 index 0000000000..b8fb17449b --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/staking-incentives.md @@ -0,0 +1,92 @@ +# Staking Incentives + +## Election and Staking + +TON Blockchain makes use of the Proof of Stake (PoS) consensus algorithm which means, like all PoS networks, that the network’s security and stability is maintained by a set of network validators. In particular, validators propose candidates for new blocks (made up of transaction batches), while other validators _validate_ and approve them via digital signatures. + +Validators are chosen using special [Elector governance contract](/develop/smart-contracts/governance#elector). During each consensus round, validator candidates send an application for election along with their stake and desired _max_factor_ (a parameter which regulates the amount of maintenance the validator performs per consensus round). + +During the validator election process, the governance smart contract chooses the next round of validators and assigns a voting weight to each validator to maximize their total stake, while also taking into consideration the validator’s stake and _max_factor_. In this respect, the higher the stake and _max_factor_, the higher the voting weight of the validator and vice versa. + +Validators that are elected are chosen to secure the network by participating in the next consensus round. However, unlike many other blockchains, to achieve horizontal scalability, each validator validates only a portion of the network: + +For each shardchain and masterchain a dedicated set of validators exists. Sets of masterchain validators consist of up to 100 validators that exhibit the highest voting weight (defined as Network Parameter `Config16:max_main_validators`). + +In contrast, each shardchain is validated by a set of 23 validators (defined as Network Parameter `Config28:shard_validators_num`) and rotated randomly every 1000 seconds (Network Parameter `Config28:shard_validators_lifetime`). + +## Values of Stakes: Max Effective Stake + +The current `max_factor` in config is **3**, meaning the stake of the _smallest_ validator cannot be more than three times less than the stake of the _largest_ one. + +The formula with the config parameters: + +`max_factor` = [`max_stake_factor`](https://tonviewer.com/config#17) / [`validators_elected_for`](https://tonviewer.com/config#15) + +### (Simplified) Selection Algorithm + +This algorithm, run by the [Elector smart contract](/develop/smart-contracts/governance#elector), selects the best validator candidates based on the stake they have committed. Here's a breakdown of how it works: + +1. **Initial Selection**: Elector considers all candidates who have staked more than a set minimum amount (300K, as specified in the [configuration](https://tonviewer.com/config#17)). + +2. **Ordering Candidates**: These candidates are then arranged from highest to lowest based on their stake. + +3. **Narrowing Down**: + - If the number of candidates exceeds the maximum allowed number of validators ([see configuration](https://tonviewer.com/config#16)), those with the lowest stakes are excluded. + - The Elector then evaluates each potential group of candidates, starting from the largest group and moving to smaller ones: + - It examines the top candidates in the ordered list, increasing the number one by one. + - For each candidate, Elector calculates their 'effective stake'. If a candidate's stake is significantly higher than the minimum, it's adjusted down (e.g., if someone staked 310k and the minimum is 100k, but there's a rule capping at three times the minimum, their effective stake is considered as 300k). + - It sums up the effective stakes of all candidates in this group. + +4. **Final Selection**: The group of candidates with the highest total effective stake is chosen as the validators by the Elector. + +#### Validator Selection Algorithm + +Based on the available stakes of potential validators, optimal values for the minimum and maximum stake are determined, with the aim of maximizing the magnitude of the total stake: + +1. Elector takes all applicants who have a stake higher than the minimum ([300K in config](https://tonviewer.com/config#17)). +2. Elector sorts them in _descending_ order of stake. +3. If there are more participants than the [maximum number](https://tonviewer.com/config#16) of validators, Elector discards the tail of the list. Then Elector does the following: + + - For each cycle **i** from _1 to N_ (the remaining number of participants), it takes the first **i** applications from the sorted list. + - It calculates the effective stake, considering the `max_factor`. That is, if a person has put in 310k, but with a `max_factor` of 3, and the minimum stake in the list is 100k Toncoins, then the effective stake will be min(310k, 3\*100k) = 300k. + - It calculates the total effective stake of all **i** participants. + +Once Elector finds such an **i**, where the total effective stake is maximal, we declare these **i** participants as validators. + +## Positive Incentives + +Similarly to all blockchain networks, each transaction on TON requires a computation fee called [gas](https://blog.ton.org/what-is-blockchain) used to conduct network storage and the transaction processing on-chain. On TON, these fees are accumulated within the Elector contract in a reward pool. + +The network also subsidizes block creation by adding a subsidy to the reward pool equal to 1.7 TON for each masterchain block and 1 TON for each basechain block (Network Parameters `Config14:masterchain_block_fee` and `Config14:basechain_block_fee`). Note, that when splitting a basechain into more than one shardchain, the subsidy per shardchain block is split accordingly. This process allows the subsidy per unit of time to be kept near constant. + +:::info +TON Blockchain is planning to introduce a deflationary mechanism in Q2 of 2023. In particular, a portion of TON generated via network use will be burned instead of going to the rewards pool. +::: + +After a validation cycle round lasting 65536 seconds or ~18 hours (Network Parameter `Config15:validators_elected_for`), staked TON is not immediately released by each validator, but instead held for an additional 32768 seconds or ~9 hours (Network Parameter `Config15:stake_held_for`). During this period, slashing (a penalization mechanism for misbehaving validators) penalties can be deducted from the validator. After funds are released, validators can withdraw their stake along with a share of the reward pool accrued during the validation round proportional to their voting _weight_. + +As of April 2023, the total reward pool per consensus round for all validators on the network is approximately 40,000 TON, with the average reward per validator being ~ 120 TON (the maximum difference between voting weight and the accrued rewards is ~3 TON). + +Given the total supply of Toncoin (5 billion TON) has an inflation rate of approximately 0.3-0.6% annually. + +This inflation rate, however, is not always constant, and may deviate depending on the network’s current state. Eventually it will tend to deflation after Deflation mechanism activation and growth of network utilization. + +:::info +Learn current TON Blockchain stats [here](https://tontech.io/stats/). +::: + +## Negative Incentives + +On TON Blockchain, there are generally two ways validators can be penalized for misbehaving: idle and malicious misbehaving; both of which are prohibited and may result in being fined (in a process called slashing) for their actions. + +If a validator does not participate in block creation and transaction signing for a significant amount of time during a validation round, it is potentially fined using the _Standard fine_ parameter. As of April 2023, the Standard fine accrued is 101 TON (Network Parameter `ConfigParam40:MisbehaviourPunishmentConfig`). + +On TON, slashing penalties (fines given to validators) allow any network participant to file a complaint if they believe a validator is misbehaving. During this process, the participant issuing the complaint must attach cryptographic proofs of misbehavior for Elector submission. During the `stake_held_for` dispute resolution period, all validators operating on the network check the validity of complaints and vote whether they will pursue the complaint collectively (while determining the legitimacy of misbehaving proofs and fine allotment). + +Upon reaching 66% validator approval (measured by an equal voting weight), a slashing penalty is deducted from the validator and withdrawn from the validator’s total stake. The validation process for penalization and complaint resolution is typically conducted automatically using the MyTonCtrl. + +## See Also + +- [Running a Full Node (Validator)](/participate/run-nodes/full-node) +- [Transaction Fees](/develop/smart-contracts/fees) +- [What is blockchain? What is a smart contract? What is gas?](https://blog.ton.org/what-is-blockchain) From 5485d9553882dc64fae6011ce67389fa3fb9f4b3 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:37 +0800 Subject: [PATCH 184/219] New translations vesting-contract.mdx (Chinese Simplified) --- .../network-maintenance/vesting-contract.mdx | 156 ++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/vesting-contract.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/vesting-contract.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/vesting-contract.mdx new file mode 100644 index 0000000000..a71e5db1b2 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/vesting-contract.mdx @@ -0,0 +1,156 @@ +# Vesting Contract + +This contract allows you to lock a certain amount of Toncoin for a specified time and gradually unlock them. + +## Vesting Parameters + +Vesting parameters are unchanged and is set during deployment. + +`vesting_total_amount` - in nanotons, the total amount of locked Toncoins. + +`vesting_start_time` - unixtime, the starting point of the vesting period, until this moment the `vesting_total_amount` is locked, after that it starts to unlock according to other parameters. + +`vesting_total_duration` - total vesting duration in seconds (e.g. `31104000` for one year). + +`unlock_period` - unlock period in seconds (e.g. `2592000` for once a month). + +`cliff_duration` - starting cliff period in seconds (e.g. `5184000` for 2 months). + +`vesting_sender_address` - the address to which you can return the Toncoins (even if they are locked) at any time; also this address can append the whitelist. + +`owner_address` - the one to whom the vesting was issued, from this address, he can initiate the sending of Toncoins from the vesting contract. + +You can get this parameters by `get_vesting_data()` get-method. + +The parameters must satisfy the following conditions: + +``` +vesting_total_duration > 0 +vesting_total_duration <= 135 years (2^32 seconds) +unlock_period > 0 +unlock_period <= vesting_total_duration +cliff_duration >= 0 +cliff_duration < vesting_total_duration +vesting_total_duration mod unlock_period == 0 +cliff_duration mod unlock_period == 0 +``` + +Although the smart contract does not check for compliance with these conditions, after the contract is deployed and before sending Toncoins to it, the user can verify that all parameters are OK by get-method. + +## Lock + +Before the `vesting_start_time`, all `vesting_total_amount` are locked. + +Starting from `vesting_start_time` the amount starts to unlock proportionately. + +For example if `vesting_total_duration` is 10 months, and `unlock_period` is 1 month, and `vesting_total_amount` is 500 TON then every month will unlock 500\*(10/100)=50 TON, and in 10 months all 500 TON will be unlocked. + +If there is a cliff period, nothing is unlocked during this cliff period, and after it has passed, the amount is unlocked according to the formula above. + +For example if `cliff_period` is 3 months, and the other parameters are the same as in the previous example, then first 3 months nothing will be unlocked and on 3 months 150 TON will be unlocked at once (and then 50 TON every month). + +Get-method `get_locked_amount(int at_time)` allows you to calculate how much will be locked at a certain point in time. + +You can only send the locked Toncoins to the whitelist addresses or `vesting_sender_address`. + +You can send the unlocked Toncoins whenever and wherever you like. + +## Whitelist + +Whitelist is a list of addresses to which you can send Toncoins, even if coins are still locked. + +Get-method `get_whitelist()` returns all whitelist addresses as list of (wc, hash_part) tuples. + +Get-method `is_whitelisted(slice address)` checks to see if this address is on the whitelist. + +`vesting_sender_address` can append new addresses to whitelist at any time by `op::add_whitelist` message. + +Unable to remove an address from the whitelist. + +Also, locked coins can always be sent to the `vesting_sender_address` (it doesn't need to be added to the whitelist separately). + +## Top-up + +You can send Toncoins to vesting contract from any address. + +## Wallet smart contract + +This contract is designed similar to the [standard wallet V3 smart contract](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/wallet3-code.fc). + +In his data, he keeps `seqno`, `subwallet_id`, `public_key` and accepts external messages of the same format. + +Get-methods `seqno()`, `get_subwallet_id()` and `get_public_key()` are available. + +Unlike a standard wallet, vesting contract allows you to send only one message at a time. + +## Send + +The owner of the public key can initiate the sending of Toncoins from the vesting contract by an external message, just like in standard wallets. + +The Toncoin sending can also be initiated by an `op::send` internal message sent from the `owner_address`. + +In practice, both the public key and the `owner_address` are owned by the same user. + +## Whitelist restrictions + +Messages that can be sent to the `vesting_sender_address` have the following restrictions: + +- only `send_mode == 3` allowed; + +In most cases, addresses are added to the whitelist to allow the user to validate using locked coins or to stake locked coins into the pools. + +To avoid theft of Toncoins, messages that can be sent to the whitelist have the following restrictions: + +- only `send_mode == 3` allowed; + +- only bounceable messages allowed; + +- no `state_init` attachment allowed; + +If destination is system elector address: + +- only `op::elector_new_stake`, `op::elector_recover_stake`, `op::vote_for_complaint`, `op::vote_for_proposal` operations allowed; + +If destination is system config address: + +- only `op::vote_for_proposal` operation allowed; + +For other destinations: + +- allowed empty messages and empty text messages; +- allowed text messages starts only with "d", "w", "D", "W"; +- allowed `op::single_nominator_pool_withdraw`, `op::single_nominator_pool_change_validator`, `op::ton_stakers_deposit`, `op::jetton_burn`, `op::ton_stakers_vote`, `op::vote_for_proposal`, `op::vote_for_complaint` operations; + +There are no restrictions on addresses not included in the whitelist. + +No restrictions apply when sending unlocked Toncoins, even if we send to whitelist or `vesting_sender_address`. + +## Project structure + +- `contracts` - source code of all the smart contracts of the project and their dependencies. +- `wrappers` - wrapper classes (implementing `Contract` from ton-core) for the contracts, including any [de]serialization primitives and compilation functions. +- `tests` - tests for the contracts. +- `scripts` - scripts used by the project, mainly the deployment scripts. + +## How to use + +### Build + +`npx blueprint build` or `yarn blueprint build` + +### Test + +`npx blueprint test` or `yarn blueprint test` + +### Deploy or run another script + +`npx blueprint run` or `yarn blueprint run` + +### Add a new contract + +`npx blueprint create ContractName` or `yarn blueprint create ContractName` + +## See Also + +- [Single Nominator](/participate/network-maintenance/single-nominator) +- [vesting-contract](https://github.com/ton-blockchain/vesting-contract) From ebe59d07a7e458a2d5f11d1127702c3da32932b4 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:38 +0800 Subject: [PATCH 185/219] New translations collators.md (Chinese Simplified) --- .../current/participate/nodes/collators.md | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/nodes/collators.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/nodes/collators.md b/i18n/zh/docusaurus-plugin-content-docs/current/participate/nodes/collators.md new file mode 100644 index 0000000000..7c0f37a97b --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/nodes/collators.md @@ -0,0 +1,125 @@ +# Validator/Collator separation + +:::caution in development +This feature is testnet only right now! Participate on your own risk. +::: + +The key feature of TON blockchain is the ability to distribute transaction processing over network nodes, and switching from "everybody checks all transactions" to "every transaction is checked by secure validator subset". This ability to infinitely horizontally scale throughput over shards when one workchain splits to required number of _shardchains_ distinguishes TON from other L1 networks. + +However it is necessary to regularly rotate validator subsets which process one or another shard to prevent collusion. At the same time to process transactions validators obiviously should know state of the shard prior transaction. The simplest approach is to require all validators to know state of all shards. + +This approach works well while number of TON users is within range of a few millions and TPS (transactions per second) is under hundred. However, in the future, when TON will process many thousands transactions per second and server hundred millions or billions of people, no single server would be able to keep actual state of whole network. Fortunately, TON was designed with such loads in mind and supports sharding both throughput and state update. + +This is achieived through separation of two roles: + +- _Collator_ - actor which watch for only part of the network, know actual state and _collate_ (generate) next blocks +- _Validator_ - actor which gets new blocks from _Collator_, checks it's validity and signs it effectively guaranteeing correctness at the risk of losing the stake. + +At the same time architecture of TON allows _Validator_ effectively validate new blocks without actually storing state of blockchain, by cheking specially crafted proofs. + +That way, when throughput of TON will be to heavy to be processed by single machine, network will consist of subnetwork of collators each of which will process only part of the chains it is capable to process and subnetwork of validators which will form many secure sets for commiting new transactions. + +Currently, TON testnet is used for testing this _Validator_/_Collator_ separation, where some validators works as usual, and some validators do not collate blocks for themselves and receive them from collators. + +# Join with "lite validator" + +New node software is available in [block-generation](https://github.com/SpyCheese/ton/tree/block-generation) branch. + +## Collator + +To create new collator you need to setup TON node; you can use flag `-M` to force node not to keep eye on shardchains it doesn't process. + +In `validator-engine-console` create new key for collator, set adnl category `0` to this key and add collation entity through command: + +```bash +addcollator +``` + +For example: + +```bash +newkey +addadnl 0 +addcollator 0 -9223372036854775808 +``` + +Collator which is configured to shard wc:shard_pfx can collate blocks in shard wc:shard_pfx, its ancestors and its descendants; it also will monitor all shese shards because this is required for collation. + +Collator can be stopped with command: + +```bash +delcollator 0 -9223372036854775808 +``` + +:::info +Currently there is one collator in the Network and config **-41** is used to announce it's adnl address. +::: + +## Validator + +To run validator you need to setup TON node, use flag `--lite-validator` to force validator to request new blocks from collators instead of generating them, and set up staking process. Validator in lite mode takes collator nodes from `-41` config. + +The easiest way is the following: + +- setup MyTonCtrl for testnet +- Stop validator `sudo systemctl stop validator` +- Update service file `sudo nano /etc/systemd/system/validator.service`: add `--lite-validator` flag +- Reload systemctl `sudo systemctl daemon-reload` +- Start validator `sudo systemctl start validator` + +## Liteserver + +Just like Collators, Liteservers can be configured to only monitor some part of the blockchain. It can be done by running a node with option `-M` and adding shards in `validator-engine-console`: + +```bash +addshard 0 -9223372036854775808 +``` + +Masterchain is always monitored by default. Shards can be removed using `delshard 0 -9223372036854775808`. + +### Lite Client + +Global config should contain at least one of two secions: `liteservers` and `liteservers_v2`. First section contains "full" Liteservers which have data about all shard states. Second section contains "partial" liteservers which contain data about some part of the blockchain. + +"Partial" Liteservers are described as following: + +```json +"liteservers_v2": [ + { + "ip": ..., + "port": ..., + "id": { + "@type": "pub.ed25519", + "key": "..." + }, + "shards": [ + { + "workchain": 0, + "shard": -9223372036854775808 + } + ] + } + ... +] +``` + +Lite Client and Tonlib support this config and can choose a suitable Liteserver for each query. Note that each Liteserver monitors masterchain by default, and each server in `liteservers_v2` is implicitly configured to accept queries about masterchain. Shard `wc:shard_pfx` in the config means that the server accepts queries about shard `wc:shard_pfx`, its ancestors and its descendsnts (just like configuration of collators). + +## Full collated data + +By default validators proposing new block in validator set do not attach data that proves "prior to block" state. This data should be obtained by other validators from locally stored state. That way old (from master branch) and new nodes may reach consensus, but new validators should keep eye on all network state. + +Upgrade to new protocol when validators will share blocks with collated data attached can be done by + +- Upgrading all validators to new node version +- Setting [full_collated_data](https://github.com/spycheese/ton/blob/block-generation/crypto/block/block.tlb#L737) to true + +# Next steps + +The practical ability to separate _Validator_ and _Collator_ roles is the main milestone on the road to limitless throughput, but to create truly decentralised and censorship-resistant network it necessary to + +- ensure independence and redundancy of _Collators_ +- ensure stable and secure way to interaction of Validators and Collators +- ensure suitable financial model for Collators which incentivize durable collation of new blocks + +Currently, these tasks are out of the scope. From 5a4ba19529ade2e1d38b2e05c6da2ec13c2b0695 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:39 +0800 Subject: [PATCH 186/219] New translations node-maintenance-and-security.md (Chinese Simplified) --- .../nodes/node-maintenance-and-security.md | 217 ++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/nodes/node-maintenance-and-security.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/nodes/node-maintenance-and-security.md b/i18n/zh/docusaurus-plugin-content-docs/current/participate/nodes/node-maintenance-and-security.md new file mode 100644 index 0000000000..05ffe855e5 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/nodes/node-maintenance-and-security.md @@ -0,0 +1,217 @@ +# Maintenance and Security + +## Introduction + +This guide provides some basic information on maintaining and securing TON Validator nodes. + +This document assumes that a validator is installed using the configuration and tools **[recommended by TON Foundation](/participate/run-nodes/full-node)** but general concepts apply to other scenarios as well and can be useful for savvy sysadmins. + +## Maintenance + +### Database grooming + +TON Node/Validator keeps it's database within the path specified by `--db` flag of `validator-engine`, usually `/var/ton-work/db`, this directory is created and managed by the node but it is recommended to perform a database grooming/cleanup task once a month to remove some artefacts. + +**Important**: You **must** stop the validator process before performing the steps outlined below, failure to do that will likely cause database corruption. + +The procedure takes ~5 minutes to complete and will not cause major service disruption. + +#### Switch to root + +```sh +sudo -s +``` + +#### Stop validator service + +```sh +systemctl stop validator +``` + +#### Verify that validator is not running + +```sh +systemctl status validator +``` + +#### Perform database cleanup + +```sh +find /var/ton-work/db -name 'LOG.old*' -exec rm {} + +``` + +#### Start validator service + +```sh +systemctl start validator +``` + +Verify that the validator process is running by analysing the processes and log. Validator should re-sync with the network within a few minutes. + +### Backups + +The easiest and most efficient way to backup the validator is to copy crucial node configuration files, keys and mytonctrl settings: + +- Node configuration file: `/var/ton-work/db/config.json` +- Node private keyring: `/var/ton-work/db/keyring` +- Node public keys: `/var/ton-work/keys` +- mytonctrl configuration and wallets: `$HOME/.local/share/myton*` where $HOME is the home directory of the user who started the installation of mytonctrl **OR** `/usr/local/bin/mytoncore` if you installed mytonctrl as root. + +This set is everything you need to perform a recovery of your node from scratch. + +#### Snapshots + +Modern file systems such as ZFS offer snapshot functionality, most cloud providers also allow their customers to make snapshots of their machines during which the entire disk is preserved for future use. + +The problem with both methods is that you must stop node before performing a snapshot, failure to do so will most likely result in a corrupt database with unexpected consequences. Many cloud providers also require you power down the machine before performing a snapshot. + +Such stops should not be performed often, if you snapshot your node once a week then in the worst case scenario after recovery you will have a node with a week-old database and it will take your node more time to catch up with the network then to perform a new installation using mytonctrl "install from dump" feature (-d flag added during invocation of `install.sh` script). + +### Disaster recovery + +To perform recovery of your node on a new machine: + +#### Install mytonctrl / node + +For fastest node initialization add `-d` switch to invocation of installation script. + +#### Switch to root user + +```sh +sudo -s +``` + +#### Stop mytoncore and validator processes + +```sh +systemctl stop validator +systemctl stop mytoncore +``` + +#### Apply backed up node configuration files + +- Node configuration file: `/var/ton-work/db/config.json` +- Node private keyring: `/var/ton-work/db/keyring` +- Node public keys: `/var/ton-work/keys` + +#### Set node IP address + +If your new node has a different IP address then you must edit the node configuration file `/var/ton-work/db/config.json` and set the leaf `.addrs[0].ip` to **decimal** representation of new IP address. You can use **[this](https://github.com/sonofmom/ton-tools/blob/master/node/ip2dec.py)** python script to convert your IP to decimal. + +#### Ensure proper database permissions + +```sh +chown -R validator:validator /var/ton-work/db +``` + +#### Apply backed up mytonctrl configuration files + +Replace `$HOME/.local/share/myton*` where $HOME is home directory of user who started the installation of mytonctrl with backed-up content, make sure that the user is the owner of all files you copy. + +#### Start mytoncore and validator processes + +```sh +systemctl start validator +systemctl start mytoncore +``` + +## Security + +### Host level security + +Host level security is a huge topic which lies outside the scope of this document, we do however advise that you never install mytonctrl under root user, use a service account to ensure privilege separation. + +### Network level security + +TON Validators are high value assets that should be protected against external threats, one of the first steps you should take is to make your node as invisible as possible, this means locking down all network connections. On a validator node, only a UDP Port used for node operations should be exposed to the internet. + +#### Tools + +We will use a **[ufw](https://help.ubuntu.com/community/UFW)** firewall interface as well as a **[jq](https://github.com/stedolan/jq)** JSON command line processor. + +#### Management Networks + +As a node operator, you need to retain full control and access to the machine, in order to do this you need at least one fixed IP address or range. + +We also advise you to setup a small "jumpstation" VPS with a fixed IP Address that can be used by you to access your locked down machine(s) if you do not have a fixed IP at your home/office or to add an alternative way to access secured machines should you lose your primary IP address. + +#### Install ufw and jq1 + +```sh +sudo apt install -y ufw jq +``` + +#### Basic lockdown of ufw ruleset + +```sh +sudo ufw default deny incoming; sudo ufw default allow outgoing +``` + +#### Disable automated ICMP echo request accept + +```sh +sudo sed -i 's/-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT/#-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT/g' /etc/ufw/before.rules +``` + +#### Enable all access from management network(s) + +```sh +sudo ufw insert 1 allow from +``` + +repeat the above command for each management network / address. + +#### Expose node / validator UDP port to public + +```sh +sudo ufw allow proto udp from any to any port `sudo jq -r '.addrs[0].port' /var/ton-work/db/config.json` +``` + +#### Doublecheck your management networks + +Important: before enabling firewall, please double-check that you added the correct management addresses! + +#### Enable ufw firewall + +```sh +sudo ufw enable +``` + +#### Checking status + +To check the firewall status use the following command: + +```sh + sudo ufw status numbered +``` + +Here is an example output of a locked-down node with two management networks / addresses: + +``` +Status: active + + To Action From + -- ------ ---- +[ 1] Anywhere ALLOW IN /28 +[ 2] Anywhere ALLOW IN /32 +[ 3] /udp ALLOW IN Anywhere +[ 4] /udp (v6) ALLOW IN Anywhere (v6) +``` + +#### Expose LiteServer port + +```sh +sudo ufw allow proto tcp from any to any port `sudo jq -r '.liteservers[0].port' /var/ton-work/db/config.json` +``` + +Please note that the LiteServer port should not be exposed publicly on a validator. + +#### More information on UFW + +See this excellent **[ufw tutorial](https://www.digitalocean.com/community/tutorials/ufw-essentials-common-firewall-rules-and-commands)** from Digital Ocean for more ufw magic. + +### IP Switch + +If you believe that your node is under attack then you should consider switching IP Address. The way to achieve the switch depends on your hosting provider; you might pre-order a second address, clone your **stopped** VM into another instance or setup a new instance by performing a **[disaster recovery](#disaster-recovery)** process. + +In any case, please do make sure that you **[set your new IP Address](#set-node-ip)** in the node configuration file! From 9b360e64f161e35025cba9ec2135584bc75401d6 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:40 +0800 Subject: [PATCH 187/219] New translations node-types.md (Chinese Simplified) --- .../current/participate/nodes/node-types.md | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/nodes/node-types.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/nodes/node-types.md b/i18n/zh/docusaurus-plugin-content-docs/current/participate/nodes/node-types.md new file mode 100644 index 0000000000..c10c04c04c --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/nodes/node-types.md @@ -0,0 +1,80 @@ +import Button from '@site/src/components/button' + +# TON Node Types + +When diving into the world of The Open Network (TON), understanding the distinct node types and their functionalities is crucial. This article breaks down each node type to provide clarity for developers wishing to engage with the TON blockchain. + +## Full Node + +A **Full Node** in TON is a node that maintains synchronization with the blockchain. + +It retains the _current state_ of the blockchain and can house either the entire block history or parts of it. This makes it the backbone of the TON blockchain, facilitating the network's decentralization and security. + +\ + +## Validator Node + +A **Validator Node** is activated when it holds a necessary amount of Toncoin as a stake. Validator nodes are vital for the network's operability, participating in the validation of new network blocks. + +TON operates on a Proof-of-Stake mechanism, where validators are pivotal in maintaining network functionality. Validators are [rewarded in Toncoin](/participate/network-maintenance/staking-incentives) for their contributions, incentivizing network participation and ensuring network security. + +[Running a Full Node as a Validator](/participate/run-nodes/full-node#become-a-validator) + +## Full Node + Liteserver + +When an endpoint is activated on a full node, the node assumes the role of a **Liteserver**. This node type can field and respond to requests from Lite Clients, allowing to seamlessly interract with the TON Blockchain. + +### Lite Clients: the SDKs to interact with TON + +Liteservers enable swift communication with Lite Clients, facilitating tasks like retrieving balance or submitting transactions without necessitating the full block history. + +Each SDK which supports ADNL protocol can be used as a Lite Client with `config.json` file. The `config.json` file contains a list of endpoints that can be used to connect to the TON Blockchain. + +[Choose a TON SDK](/develop/dapps/apis/sdk) + +Each SDK without ADNL support usually uses HTTP middleware to connect to the TON Blockchain. It's less secure and slower than ADNL, but it's easier to use. + +### Interaction with TON: Public Liteservers (endpoints) + +The TON Foundation provides several public Liteservers, integrated into the global config, which are accessible for universal use. These endpoints, such as those used by standard wallets, ensure that even without setting up a personal liteserver, interaction with the TON Blockchain remains possible. + +- [Public Liteserver Configurations - mainnet](https://ton.org/global-config.json) +- [Public Liteserver Configurations - testnet](https://ton.org/testnet-global.config.json) + +Use the downloaded `config.json` file in your application with TON SDK. + +[Choose a TON SDK](/develop/dapps/apis/sdk) + +#### Troubleshooting + +##### Timed out after 3 seconds + +If you see this error this means that the liteserver you are trying to connect to is not available. The correct way to solve this issue for public liteservers is as follows: + +1. Download the config.json file from the tontech link: + +```bash +wget https://api.tontech.io/ton/wallet-mainnet.autoconf.json -O /usr/bin/ton/global.config.json +``` + +It removes slow liteservers from the configuration file. + +2. Use the downloaded config.json file in your application with TON SDK. + +[Choose a TON SDK](/develop/dapps/apis/sdk) + +### Running a Full Node as a Liteserver + +If your project requires a high level of _security_, you can run your own Liteserver. To run a full node as a Liteserver, simply enable the Liteserver mode in your node's configuration file: + +[Enable Liteserver in your Node](/participate/run-nodes/full-node#enable-liteserver-mode) + +## Archive Node + +An **Archive Node** is essentially a full node that archives the entire block history. + +Such nodes are indispensable for creating blockchain explorers or other tools that necessitate a full blockchain history. + +[Running an Archive Node](/participate/run-nodes/archive-node) From 483e284b1f309c6a8d8524ad4c9839c9cdb611ea Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:41 +0800 Subject: [PATCH 188/219] New translations nodes-faq.md (Chinese Simplified) --- .../current/participate/nodes/nodes-faq.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/nodes/nodes-faq.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/nodes/nodes-faq.md b/i18n/zh/docusaurus-plugin-content-docs/current/participate/nodes/nodes-faq.md new file mode 100644 index 0000000000..5fc65094ac --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/nodes/nodes-faq.md @@ -0,0 +1,3 @@ +# Nodes FAQ + +Moved to [Troubleshooting](/participate/run-nodes/full-node#troubleshooting) section. From af0c5dde487ae78c0deaf9a99b6aa95a960fd8a9 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:41 +0800 Subject: [PATCH 189/219] New translations archive-node.md (Chinese Simplified) --- .../participate/run-nodes/archive-node.md | 225 ++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/run-nodes/archive-node.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/run-nodes/archive-node.md b/i18n/zh/docusaurus-plugin-content-docs/current/participate/run-nodes/archive-node.md new file mode 100644 index 0000000000..b968cc6069 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/run-nodes/archive-node.md @@ -0,0 +1,225 @@ +# Running an Archive Node + +## Overview + +:::caution System Administrator Required +For running nodes basic knowledge of Linux/Ubuntu system administration is required. +::: + +An Archive Node is a type of Full Node that stores extended historical data of a blockchain. If you are creating a blockchain explorer or a similar application that requires access to historical data, using an Archive Node as an indexer is recommended. + +## Prerequisites + +We highly recommend install mytonctrl using the supported operating systems: + +- Ubuntu 20.04 +- Ubuntu 22.04 +- Debian 11 + +Please, use a [non-root user with sudo privileges](/participate/run-nodes/full-node#prerequisites-1) to install and run mytonctrl. + +## Hardware requirements + +- 16 x Cores CPU +- 128GB ECC Memory +- 4TB SSD _OR_ Provisioned 32+k IOPS storage +- 1 Gbit/s network connectivity +- 16 TB/month traffic on peak load +- a public IP address (fixed IP address) + +**Note**: 4TB assumes usage of zfs volume with compression enabled + +## Installation + +In general, you need the following steps to run an Archive Node: + +1. Install ZFS and Prepare Volume +2. Install MyTonCtrl +3. Run a Full Node on your server and stop validator process +4. Download and restore dump data from https://archival-dump.ton.org +5. Run Full Node with Configuring DB specs for Archive Node + +### Install ZFS and Prepare Volume + +Dumps come in form of ZFS Snapshots compressed using plzip, you need to install zfs on your host and restore the dump, see [Oracle Documentation](https://docs.oracle.com/cd/E23824_01/html/821-1448/gavvx.html#scrolltoc) for more details. + +Usually, it's a good idea to create a separate ZFS pool for your node on a _dedicated SSD drive_, this will allow you to easily manage storage space and backup your node. + +1. Install [zfs](https://ubuntu.com/tutorials/setup-zfs-storage-pool#1-overview) + +```shell +sudo apt install zfsutils-linux +``` + +2. [Create pool](https://ubuntu.com/tutorials/setup-zfs-storage-pool#3-creating-a-zfs-pool) on your dedicated 4TB `` and name it `data` + +```shell +sudo zpool create data +``` + +3. Before restoring we highly recommend to enable compression on parent ZFS filesystem, this will save you a [lot of space](https://www.servethehome.com/the-case-for-using-zfs-compression/). To enable compression for the `data` volume enter using root account: + +```shell +sudo zfs set compression=lz4 data +``` + +### Install MyTonCtrl + +Please, use a [Running Full Node](/participate/run-nodes/full-node) to install mytonctrl. + +### Run an Archive Node + +#### Prepare the node + +1. Before performing a restore, you must stop the validator using root account: + +```shell +sudo -s +systemctl stop validator.service +``` + +2. Make a backup of `ton-work` config files (we will need the `/var/ton-work/db/config.json`, `/var/ton-work/keys`, and `/var/ton-work/db/keyring`). + +```shell +mv /var/ton-work /var/ton-work.bak +``` + +#### Download the dump + +1. Request `user` and `password` credentials to gain access for downloading dumps in the [@TONBaseChatEn](https://t.me/TONBaseChatEn) Telegram chat. +2. Here is an example command to download & restore the dump from the ton.org server: + +```shell +wget --user --password -c https://archival-dump.ton.org/dumps/latest.zfs.lz | pv | plzip -d -n | zfs recv data/ton-work +``` + +Size of the dump is **~1.5TB**, so it will take some time to download and restore it. + +Prepare and run the command: + +1. Install the tools if necessary (`pv`, `plzip`) +2. Replace `` and `` with your credentials +3. Tell `plzip` to use as many cores as your machine allows to speed up extraction (`-n`) + +#### Mount the dump + +1. Mount zfs: + +```shell +zfs set mountpoint=/var/ton-work data/ton-work && zfs mount data/ton-work +``` + +2. Restore `db/config.json`, `keys` and `db/keyring` from backup to `/var/ton-work` + +```shell +cp /var/ton-work.bak/db/config.json /var/ton-work/db/config.json +cp -r /var/ton-work.bak/keys /var/ton-work/keys +cp -r /var/ton-work.bak/db/keyring /var/ton-work/db/keyring +``` + +3. Make sure that permissions for `/var/ton-work` and `/var/ton-work/keys` dirs promoted correctly: + +- The owner for the `/var/ton-work/db` dir should be `validator` user: + +```shell +chown -R validator:validator /var/ton-work/db +``` + +- The owner for the `/var/ton-work/keys` dir should be `ubuntu` user: + +```shell +chown -R ubuntu:ubuntu /var/ton-work/keys +``` + +#### Update Configuration + +Update node configuration for the archive node. + +1. Open the node config file `/etc/systemd/system/validator.service` + +```shell +nano /etc/systemd/system/validator.service +``` + +2. Add storage settings for the node in the `ExecStart` line: + +```shell +--state-ttl 315360000 --archive-ttl 315360000 --block-ttl 315360000 +``` + +:::info +Please be patient once you start the node and observe the logs. Dumps come without DHT caches, so it will take your node some time to find other nodes and then sync with them. Depending on the age of the snapshot, your node might take from a few hours to several days to catch up with the network. This is normal. +::: + +#### Start the node + +1. Start the validator by running the command: + +```shell +systemctl start validator.service +``` + +2. Open `mytonctrl` from _local user_ and check the node status using the `status`. + +## Node maintenance + +Node database requires cleansing from time to time (we advise once a week), to do so please perform following steps as root: + +1. Stop validator process (Never skip this!) + +```shell +sudo -s +systemctl stop validator.service +``` + +2. Remove old logs + +```shell +find /var/ton-work -name 'LOG.old*' -exec rm {} + +``` + +4. Remove temp files + +```shell +rm -r /var/ton-work/db/files/packages/temp.archive.* +``` + +5. Start validator process + +```shell +systemctl start validator.service +``` + +## Troubleshooting and backups + +If for some reason something does not work / breaks you can always [roll back](https://docs.oracle.com/cd/E23824_01/html/821-1448/gbciq.html#gbcxk) to @archstate snapshot on your ZFS filesystem, this is the original state from dump. + +1. Stop validator process (Never skip this!) + +```shell +sudo -s +systemctl stop validator.service +``` + +2. Check the snapshot name + +```shell +zfs list -t snapshot +``` + +3. Rollback to the snapshot + +```shell +zfs rollback data/ton-work@dumpstate +``` + +If your Node works well then you can remove this snapshot to save storage space, but we do recommend to regularly snapshot your filesystem for rollback purposes because validator node has been known to corrupt data as well as config.json in some cases. [zfsnap](https://www.zfsnap.org/docs.html) is a nice tool to automate snapshot rotation. + +:::tip Need help? +Have question or need help? Please ask in the [TON dev chat](https://t.me/tondev_eng) to get help from the community. MyTonCtrl developers also hang out there. +::: + +## See Also + +- [TON Node Types](/participate/nodes/node-types) +- [Run a Full Node](/participate/run-nodes/full-node) From 13e3b83eee6f2985dec8eba520c374c0d5bc67c7 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:42 +0800 Subject: [PATCH 190/219] New translations full-node.mdx (Chinese Simplified) --- .../participate/run-nodes/full-node.mdx | 708 ++++++++++++++++++ 1 file changed, 708 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/run-nodes/full-node.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/run-nodes/full-node.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/participate/run-nodes/full-node.mdx new file mode 100644 index 0000000000..53cc665e5d --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/run-nodes/full-node.mdx @@ -0,0 +1,708 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Running a Full Node + +:::caution System Administrator Required +For running nodes basic knowledge of Linux/Ubuntu system administration is required. +::: + +To install and manage your own node, use the **MyTonCtrl** open-source tool developed by the TON Foundation. The majority of TON Nodes are reliable and tested by **MyTonCtrl**. + +[MyTonCtrl](https://github.com/ton-blockchain/mytonctrl) is a console application that is a convenient wrapper for fift, lite-client, and validator-engine-console. It has been specifically developed to streamline wallet, domain, and validator management tasks on the Linux operating system. + +We are actively seeking feedback about the installation process. If you have any questions or suggestions, please [contact us](https://t.me/Alexgton). + +## Prerequisites + +We highly recommend installing MyTonCtrl using the supported operating systems: + +- Ubuntu 20.04 +- Ubuntu 22.04 +- Debian 11 + +Please, [non-root user with sudo privileges](/participate/run-nodes/full-node#prerequisites-1) to install and run MyTonCtrl. + +## Hardware requirements + +These requirements are for a **full node with a validator**. If you want to run a full node without a validator (e.g. [liteserver](/participate/run-nodes/full-node#hardware-requirements-1)), you can use a less powerful machine. + +- 16 cores CPU +- 128 GB RAM +- 1TB NVME SSD _OR_ Provisioned 64+k IOPS storage +- 1 Gbit/s network connectivity +- public IP address (_fixed IP address_) +- 16 TB/month traffic on peak load + +You need a machine with a **fixed IP address** and a **high-bandwidth network connection** to run a TON Blockchain Full Node. + +Typically you'll need a sufficiently powerful server in a data center with good network connectivity, using at least a 1 Gbit/s connection to reliably accommodate peak loads (the average load is expected to be approximately 100 Mbit/s). + +### Recommended Providers + +The TON Foundation recommends the following providers for running a Validator: + +#### GCP (Google Cloud Platform) + +- **Machine Type:** `n2-standard-16` +- **CPU:** `32 vCPUs` +- **RAM:** `128 GB` +- **Storage:** `1 TB NVMe SSD persistent disk` +- **Network:** `16 Gbps` +- **Public IP:** `Reserve a static external IP address.` +- **Traffic:** `16 TB/month` + +#### Alibaba Cloud + +- **Instance Type:** `ecs.g6.4xlarge` +- **CPU:** `32 vCPUs` +- **RAM:** `128 GB` +- **Storage:** `1 TB NVMe SSD disk` +- **Network:** `Up to 10 Gbps` +- **Public IP:** `Bind an Elastic IP for a fixed IP address.` +- **Traffic:** `16 TB/month` + +#### Tencent Cloud + +- **Instance Type:** `M5.4XLARGE` +- **CPU:** `32 vCPUs` +- **RAM:** `128 GB` +- **Storage:** `1 TB NVMe SSD cloud disk` +- **Network:** `Up to 10 Gbps` +- **Public IP:** `Associate an Elastic IP for a fixed IP address.` +- **Traffic:** `16 TB/month` + +#### Vultr + +- **Instance Type:** `bare metal Intel E-2388G` +- **CPU:** `16 Cores / 32 Threads` +- **RAM:** `128 GB` +- **Storage:** `1.92TB NVMe SSD` +- **Network:** `10 Gbps` +- **Public IP:** `Fixed IP address included with instance.` +- **Traffic:** `16 TB/month` + +#### DigitalOcean + +- **Instance Type:** `general purpose premium Intel` +- **CPU:** `32 vCPUs` +- **RAM:** `128 GB` +- **Storage:** `1TB NVMe SSD` +- **Network:** `10 Gbps` +- **Public IP:** `Fixed IP address included with instance.` +- **Traffic:** `16 TB/month` + +#### Latitude + +- **Instance Type:** `c3.medium.x86` +- **CPU:** `16 Cores / 32 Threads` +- **RAM:** `128 GB` +- **Storage:** `1.9TB NVMe SSD` +- **Network:** `10 Gbps` +- **Public IP:** `Fixed IP address included with instance.` +- **Traffic:** `16 TB/month` + +:::info +**Note:** Prices, configurations, and availability may vary. It is advisable to always check the official documentation and pricing pages of the respective cloud provider before making any decisions. +::: + +## How to run a Node? (video) + +[//]: # "" + +Please, check this video step-by-step tutorial to start promptly: + + + +## Step-by-step instructions + +### Prerequisites + +#### Login with non-root sudo user + +Login to your server as a **non-root** user with **sudo** privileges. + +#### Create and login non-root sudo user + +If you don't have **non-root** user, you can create this with the following steps steps. + +1. Login as root and create new user: + +```bash +sudo adduser +``` + +2. Add your user to the sudo group: + +```bash +sudo usermod -aG sudo +``` + +3. Log into the server as a new user using ssh + +```bash +ssh @ +``` + +### Install the MyTonCtrl + +Download and run the installation script from the **non-root** user account with **sudo** privileges. Choose your Linux distributive: + + + + +```bash +wget https://raw.githubusercontent.com/ton-blockchain/mytonctrl/master/scripts/install.sh +sudo bash install.sh -m full -d +``` + + + + +```bash +wget https://raw.githubusercontent.com/ton-blockchain/mytonctrl/master/scripts/install.sh +su root -c 'bash install.sh -m full -d' +``` + + + + +- `-m full` - Full node installation mode. +- `-d` - **mytonctrl** will download a [dump](https://dump.ton.org/) of the latest blockchain state. + This will reduce synchronization time by several times. +- `-c ` - If you want to use not public liteservers for synchronization. _(not required)_ + +### Run the mytonctrl + +1. Run **MyTonCtrl** console from the local user account used for installation in [step 1](/participate/run-nodes/full-node#prerequisites-1): + +```sh +mytonctrl +``` + +2. Check the MyTonCtrl status using the `status` command: + +```sh +status +``` + +The following statuses should be displayed: + +- **mytoncore status**: Should be in green. +- **Local validator status**: Should also be in green. +- **Local validator out of sync**: Initially, a large number is displayed. As soon as the newly created validator connects with other validators, the number will be around 250k. As synchronization progresses, this number decreases. When it falls below 20, the validator is synchronized. + +Example of the **status** command output: + +![status](https://raw.githubusercontent.com/ton-blockchain/mytonctrl/master/screens/mytonctrl-status.png) + +:::caution Make sure you have same output for status +For all nodes type **Local Validator status** section should appear. +Otherwise, [check troubleshooting section](/participate/run-nodes/full-node#status-command-displays-without-local-node-section) and [check node logs](/participate/run-nodes/full-node#check-the-node-logs). +::: + +Wait until `Local validator out of sync` becomes less than 20 seconds. + +## Become a Validator + +### View the List of Wallets + +Check out the list of available wallets in the **MyTonCtrl** console using the `wl` command: + +```sh +wl +``` + +During the installation of **mytonctrl**, the **validator_wallet_001** wallet is created: + +![wallet list](https://raw.githubusercontent.com/ton-blockchain/mytonctrl/master/screens/manual-ubuntu_mytonctrl-wl_ru.png) + +### Activate the Wallets + +1. Send the necessary number of coins to the wallet and activate it. + +Recently (_at the end of 2023_), the approximate figures have been a minimum stake of around **340K TON** and a maximum of about **1M TON**. + +Check current stakes with [tonscan.com](https://tonscan.com/validation) to understand necessary amount of coins. + +Read more [how maximum and minimum stakes calculated](/participate/network-maintenance/staking-incentives#values-of-stakes-max-effective-stake). + +2. Use the `vas` command to display the history of transfers: + +```sh +vas +``` + +3. Activate the wallet using the `aw` command + +```sh +aw [wallet name] +``` + +![account history](https://raw.githubusercontent.com/ton-blockchain/mytonctrl/master/screens/manual-ubuntu_mytonctrl-vas-aw_ru.png) + +### Your Validator is Now Ready + +**mytoncore** will automatically join the elections. It divides the wallet balance into two parts and uses them as a stake to participate in the elections. You can also manually set the stake size: + +```sh +set stake 50000 +``` + +`set stake 50000` — this sets the stake size to 50k coins. If the bet is accepted and our node becomes a validator, the bet can only be withdrawn in the second election (according to the rules of the electorate). + +![setting stake](https://raw.githubusercontent.com/ton-blockchain/mytonctrl/master/screens/manual-ubuntu_mytonctrl-set_ru.png) + +## Enable Liteserver mode + +When an endpoint is activated in a full node, the node assumes the role of a **Liteserver**. This node type can field and respond to requests from Lite Clients, allowing for seamless interaction with the TON Blockchain. + +### Hardware requirements + +Compared to a [validator](/participate/run-nodes/full-node#hardware-requirements), a liteserver mode requires less resources. However, it is still recommended to use a powerful machine to run a liteserver. + +- at least 16 cores CPU +- at least 128 GB RAM +- at least 1TB GB NVMe SSD _OR_ Provisioned 32+k IOPS storage +- 1 Gbit/s network connectivity +- 16 TB/month traffic on peak load +- public IP address (_fixed IP address_) + +#### Recommended Providers + +Feel free to use cloud providers listed in the [Recommended Providers](/participate/run-nodes/full-node#recommended-providers) section. + +Hetzner and OVH are forbidden to run a validator, but you can use them to run a liteserver: + +- **Hetzner**: EX101, AX102 +- **OVH**: RISE-4 + +### Installation of liteserver + +1. Complete the previous steps to install [MyTonCtrl](/participate/run-nodes/full-node#step-by-step-instructions). + +2. Create a config file + +```bash +MyTonCtrl> installer +MyTonInstaller> clcf + +Local config file created: /usr/bin/ton/local.config.json +``` + +3. This file will help you to connect to your liteserver. Copy the config file located on the specified path to your home to save it. + +```bash +cp /usr/bin/ton/local.config.json ~/config.json +``` + +4. Create an empty `config.json` file on your local machine. + +5. Copy the content from the console to your local machine `config.json` file. + +```bash +cat ~/config.json +``` + +### Check the firewall settings + +First, verify the Liteserver port specified in your `config.json` file. This port changes with each new installation of `MyTonCtrl`. It is located in the `port` field: + +```json +{ + ... + "liteservers": [ + { + "ip": 1605600994, + "port": LITESERVER_PORT + ... + } + ] +} +``` + +If you are using a cloud provider, you need to open this port in the firewall settings. For example, if you are using AWS, you need to open the port in the [security group](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-security-groups.html). + +Below is an example of opening a port in the bare metal server firewall. + +#### Opening a port in the firewall + +We will use the `ufw` utility ([cheatsheet](https://www.cyberciti.biz/faq/ufw-allow-incoming-ssh-connections-from-a-specific-ip-address-subnet-on-ubuntu-debian/)). You can use the one you prefer. + +1. Install `ufw` if it is not installed: + +```bash +sudo apt update +sudo apt install ufw +``` + +2. Allow ssh connections: + +```bash +sudo ufw allow ssh +``` + +3. Allow the port specified in the `config.json` file: + +```bash +sudo ufw allow +``` + +4. Check the firewall status. After running this command, you can verify that the rules have been added successfully by checking the UFW status: + +```bash +sudo ufw status +``` + +5. Enable the firewall: + +```bash +sudo ufw enable +``` + +This way, you can open the port in the firewall settings of your server. + +### Interaction with Liteserver + +0. Create an empty project on your machine and paste `config.js` in the project directory. + +1. Install libraries. + + + + +```bash +npm i --save ton-core ton-lite-client +``` + + + + +```bash +pip install pytonlib +``` + + + + +```bash +go get github.com/xssnick/tonutils-go +go get github.com/xssnick/tonutils-go/liteclient +go get github.com/xssnick/tonutils-go/ton +``` + + + + +2. Initialize a client and request masterchain info to ensure the liteserver is running. + + + + + Change project type to `module` in your `package.json` file: + +```json +{ + "type": "module" +} +``` + + Create `index.js` file with the following content: + +```js +import { LiteSingleEngine } from 'ton-lite-client/dist/engines/single.js' +import { LiteRoundRobinEngine } from 'ton-lite-client/dist/engines/roundRobin.js' +import { LiteClient } from 'ton-lite-client/dist/client.js' +import config from './config.json' assert {type: 'json'}; + + +function intToIP(int ) { + var part1 = int & 255; + var part2 = ((int >> 8) & 255); + var part3 = ((int >> 16) & 255); + var part4 = ((int >> 24) & 255); + + return part4 + "." + part3 + "." + part2 + "." + part1; +} + +let server = config.liteservers[0]; + +async function main() { + const engines = []; + engines.push(new LiteSingleEngine({ + host: `tcp://${intToIP(server.ip)}:${server.port}`, + publicKey: Buffer.from(server.id.key, 'base64'), + })); + + const engine = new LiteRoundRobinEngine(engines); + const client = new LiteClient({ engine }); + const master = await client.getMasterchainInfo() + console.log('master', master) + +} + +main() + +``` + + + + +```python + import asyncio + from pytonlib import TonlibClient + from pathlib import Path + import json + + + async def get_client() -> TonlibClient: + with open('config.json', 'r') as f: + config = json.loads(f.read()) + + keystore_dir = '/tmp/ton_keystore' + Path(keystore_dir).mkdir(parents=True, exist_ok=True) + + client = TonlibClient(ls_index=0, config=config, keystore=keystore_dir, tonlib_timeout=10) + await client.init() + + return client + + + async def test_client(): + client = await get_client() + + print(await client.get_masterchain_info()) + + await client.close() + + + if __name__ == '__main__': + asyncio.run(test_client()) +``` + + + + +```go +package main + +import ( + "context" + "encoding/json" + "io/ioutil" + "log" + "github.com/xssnick/tonutils-go/liteclient" + "github.com/xssnick/tonutils-go/ton" +) + +func main() { + client := liteclient.NewConnectionPool() + + content, err := ioutil.ReadFile("./config.json") + if err != nil { + log.Fatal("Error when opening file: ", err) + } + + config := liteclient.GlobalConfig{} + err = json.Unmarshal(content, &config) + if err != nil { + log.Fatal("Error during Unmarshal(): ", err) + } + + err = client.AddConnectionsFromConfig(context.Background(), &config) + if err != nil { + log.Fatalln("connection err: ", err.Error()) + return + } + + // initialize ton API lite connection wrapper + api := ton.NewAPIClient(client) + + master, err := api.GetMasterchainInfo(context.Background()) + if err != nil { + log.Fatalln("get masterchain info err: ", err.Error()) + return + } + + log.Println(master) +} + +``` + + + + +3. Now you can interact with your own liteserver. + +### See also + +- [\[YouTube\]Tutorial how to launch liteserver](https://youtu.be/p5zPMkSZzPc) + +## Tips & Tricks + +### List of available commands + +- You can use `help` to get a list of available commands: + +![Help command](/img/docs/full-node/help.jpg) + +### Check the mytonctrl logs + +- To check **mytoncrl** logs, open `~/.local/share/mytoncore/mytoncore.log` for a local user or `/usr/local/bin/mytoncore/mytoncore.log` for Root. + +![logs](https://raw.githubusercontent.com/ton-blockchain/mytonctrl/master/screens/manual-ubuntu_mytoncore-log.png) + +### Check the node logs + +Check the node logs upon failure: + +```bash +tail -f /var/ton-work/log.thread* +``` + +## Troubleshooting + +This section contains answers to the most frequently asked questions about running nodes. + +### Failed to get account state + +``` +Failed to get account state +``` + +This error means that there are issues during search for this account in shard state. +Most probably it means that liteserver node is syncing too slow, in particular the Masterchain synchronisation overtook shardchains (Basechain) synchronisation. In this case node knows the recent Masterchain block but can not check account state in recent shardchain block and returns Failed to get account state. + +### Failed to unpack account state + +``` +Failed to unpack account state +``` + +This error means that requested account doesn't exist in current state. That means that this account is simultaneously is not deployed AND has zero balance + +### Cannot apply external message to current state : External message was not accepted + +``` +Cannot apply external message to current state : External message was not accepted +``` + +This error means that contract didn't accepted external message. You need to find exitcode in trace. -13 means that account doesn't have enough TON to accept message (or it requires more than gas_credit). In case of wallet contracts exitcode=33 means wrong seqno (probably seqno data you use is outdatd), exitcode=34 means wrong subwallet_id (for old wallets v1/v2 it means wrong signature), exitcode=35 means that either message is expired or signature is wrong. + +### What does Error 651 mean? + +`[Error : 651 : no nodes]` indicates that your node cannot locate another node within the TON Blockchain. + +Sometimes, this process can take up to 24 hours. However, if you've been receiving this error for several days, that means that your node cannot synchronize via a current network connection. + +:::tip Solution + +It should allow incoming connections on one specific port and outgoing connections from any port. +::: + +### Validator console is not settings + +If you encounter the `Validator console is not settings` error, it indicates that you are running `MyTonCtrl` from a user other than the one you used for the installation. + +:::tip Solution + +```bash +mytonctrl +``` + +::: + +### What does "block is not applied" mean? + +**Q:** Sometimes we get `block is not applied` or `block is not ready` for various requests - is this normal? + +**A:** This is normal, typically this means you tried to retrieve block, which does not reach the node you asked. + +**Q:** If comparative frequency appears, does it mean there is a problem somewhere? + +**A:** No. You need to check "Local validator out of sync" value in mytonctrl. If it's less than 60 seconds, then everything is fine. + +But you need to keep in mind that the node is constantly synchronizing. Sometimes, you may try to receive a block that has not reached the node you requested. + +You need to repeat the request with a slight delay. + +### Out of Sync Issue with -d Flag + +If you encounter an issue where the `out of sync` equals the timestamp after downloading `MyTonCtrl` with the `-d` flag, it's possible that the dump wasn't installed correctly (or it's already outdated). + +:::tip Solution +The recommended solution is to reinstall `MyTonCtrl` again with the new dump. +::: + +If syncing takes an unusually long time, there may have been issues with the dump. Please [contact us](https://t.me/SwiftAdviser) for assistance. + +Please, run `mytonctrl` from the user you've installed it. + +### Error command\<...> timed out after 3 seconds + +This error means that the local node is not yet synchronized(out of sync lesser then 20 sec) and public nodes are being used. +Public nodes do not always respond and end up with a timeout error. + +:::tip Solution +The solution to the problem is to wait for the local node to synchronize or execute the same command several times before execution. +::: + +### Status command displays without local node section + +![](\img\docs\full-node\local-validator-status-absent.png) + +If there is no local node section in the node status, typically this means, something went wrong during installation and the step of creating/assigning a validator wallet was skipped. +Also check that the validator wallet is specified. + +Check directly the following: + +```bash +mytonctrl> get validatorWalletName +``` + +If validatorWalletName is null then execute the following: + +```bash +mytonctrl> set validatorWalletName validator_wallet_001 +``` + +### Transfer a Validator on the new Server + +:::info +Transfer all keys and configs from the old to the working node and start it. In case something goes wrong on the new one, there is still the source where everything is set up. +::: + +The best way (while the penalty for temporary non-validation is small, it can be done without interruption): + +1. Perform a clean installation on the new server using `mytonctrl`, and wait until everything is synchronized. + +2. Stop the `mytoncore` and validator `services` on both machines, make backups on the source and on the new one: + +- 2.1 `/usr/local/bin/mytoncore/...` +- 2.2 `/home/${user}/.local/share/mytoncore/...` +- 2.3 `/var/ton-work/db/config.json` +- 2.4 `/var/ton-work/db/config.json.backup` +- 2.5 `/var/ton-work/db/keyring` +- 2.6 `/var/ton-work/keys` + +3. Transfer from the source to the new one (replace the contents): + +- 3.1 `/usr/local/bin/mytoncore/...` +- 3.2 `/home/${user}/.local/share/mytoncore/...` +- 3.3 `/var/ton-work/db/config.json` +- 3.4 `/var/ton-work/db/keyring` +- 3.5 `/var/ton-work/keys` + +4. In `/var/ton-work/db/config.json` edit `addrs[0].ip` to the current one, which was after installation (can be seen in the backup `/ton-work/db/config.json.backup`) + +5. Check the permissions on all replaced files + +6. On the new one, start the `mytoncore` and `validator` services, check that the node synchronizes and then validates + +7. On the new one, make a backup: + +```bash +cp var/ton-work/db/config.json var/ton-work/db/config.json.backup +``` From 73e5d239524556b1829f0773cb7d5b3bc35ee6e5 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:43 +0800 Subject: [PATCH 191/219] New translations liteserver.mdx (Chinese Simplified) --- .../current/participate/run-nodes/liteserver.mdx | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/run-nodes/liteserver.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/run-nodes/liteserver.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/participate/run-nodes/liteserver.mdx new file mode 100644 index 0000000000..468d4d73dc --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/run-nodes/liteserver.mdx @@ -0,0 +1,7 @@ +# Running Liteserver + +:::info Deprecated +This article was merged with Running a Full Node article. +::: + +[Enable Liteserver mode](/participate/run-nodes/full-node#enable-liteserver-mode) From 9873bfdbc677376801964e0ed0b720b53fac4d58 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:44 +0800 Subject: [PATCH 192/219] New translations local-ton.md (Chinese Simplified) --- .../current/participate/run-nodes/local-ton.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/run-nodes/local-ton.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/run-nodes/local-ton.md b/i18n/zh/docusaurus-plugin-content-docs/current/participate/run-nodes/local-ton.md new file mode 100644 index 0000000000..58bfbefd03 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/run-nodes/local-ton.md @@ -0,0 +1,14 @@ +# Running a local TON + +## MyLocalTon + +Using **MyLocalTon** you can run your own TON Blockchain even on your laptop. + +![MyLocalTon](/img/docs/mylocalton.jpeg) + +## Resources + +MyLocalTon has a convenient UI and is cross-platform compatible. + +- [MyLocalTon binaries](https://github.com/neodiX42/MyLocalTon/releases) +- [MyLocalTon source code](https://github.com/neodiX42/MyLocalTon) From 2d25827561f945514949a5596dffd6ff4aacb1a8 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:45 +0800 Subject: [PATCH 193/219] New translations storage-daemon.md (Chinese Simplified) --- .../participate/ton-storage/storage-daemon.md | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/ton-storage/storage-daemon.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/ton-storage/storage-daemon.md b/i18n/zh/docusaurus-plugin-content-docs/current/participate/ton-storage/storage-daemon.md new file mode 100644 index 0000000000..e6b27420df --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/ton-storage/storage-daemon.md @@ -0,0 +1,159 @@ +# Storage daemon + +_A storage daemon is a program used to download and share files in the TON network. The `storage-daemon-cli` console program, is used to manage a running storage daemon._ + +The current version of the storage daemon can be found in the [Testnet](https://github.com/ton-blockchain/ton/tree/testnet) branch. + +## Hardware requirements + +- at least 1GHz and 2 cores CPU +- at least 2 GB RAM +- at least 2 GB SSD (Without taking into account space for torrents) +- 10 Mb/s network bandwidth with static IP + +## Binaries + +You can download `storage-daemon` and `storage-daemon-cli` for Linux/Windows/MacOS binaries from [TON Auto Builds](https://github.com/ton-blockchain/ton/releases/latest). + +## Compile from sources + +You can compile `storage-daemon` and `storage-damon-cli` from sources using this [instruction](/develop/howto/compile#storage-daemon). + +## Key concepts + +- _Bag of files_ or _Bag_ - a collection of files distributed through TON Storage +- TON Storage's network part is based on technology similar to torrents, so the terms _Torrent_, _Bag of files_, and _Bag_ will be used interchangeably. It's important to note some differences, however: TON Storage transfers data over [ADNL](/learn/networking/adnl) by [RLDP](/learn/networking/rldp) protocol, each _Bag_ is distributed through its own network overlay, the merkle structure can exist in two versions - with large chunks for efficient downloading and small ones for efficient ownership proof, and [TON DHT](/learn/networking/ton-dht) network is used for finding peers. +- A _Bag of files_ consists of _torrent info_ and a data block. +- The data block starts with a _torrent header_ - a structure that contains a list of files with their names and sizes. The files themselves follow in the data block. +- The data block is divided into chunks (128 KB by default), and a _merkle tree_ (made of TVM cells) is built on the SHA256 hashes of these chunks. This allows building and verifying _merkle proofs_ of individual chunks, as well as efficiently recreating the _Bag_ by exchanging only the proof of the modified chunk. +- _Torrent info_ contains the _merkle root_ of the + - Chunk size (data block) + - the list of chunks' sizes + - Hash _merkle tree_ + - Description - any text specified by the creator of the torrent +- _Torrent info_ is serialized to a TVM cell. The hash of this cell is called _BagID_, and it uniquely identifies _Bag_. +- _Bag meta_ is a file containing _torrent info_ and _torrent header_.\* This is an analog `.torrent` files. + +## Starting the storage daemon and storage-daemon-cli + +### An example command for starting the storage-daemon: + +`storage-daemon -v 3 -C global.config.json -I :3333 -p 5555 -D storage-db` + +- `-v` - verbosity level (INFO) +- `-C` - global network config ([download global config](/develop/howto/compile#download-global-config)) +- `-I` - IP address and port for adnl +- `-p` - TCP port for console interface +- `-D` - directory for the storage daemon database + +### storage-daemon-cli management + +It's started like this: + +``` +storage-daemon-cli -I 127.0.0.1:5555 -k storage-db/cli-keys/client -p storage-db/cli-keys/server.pub +``` + +- `-I` - this is the IP address and port of the daemon (the port is the same one specified in the `-p` parameter above) +- `-k` and `-p` - these are the client's private key and the server's public key (similar to `validator-engine-console`). These keys are generated on the first run of the daemon and are placed in `/cli-keys/`. + +### List of commands + +The list of `storage-daemon-cli` commands can be obtained with the `help` command. + +Commands have positional parameters and flags. Parameters with spaces should be enclosed in quotes (`'` or `"`), also spaces can be escaped. Other escapes are available, for example: + +``` +create filename\ with\ spaces.txt -d "Description\nSecond line of \"description\"\nBackslash: \\" +``` + +All parameters after flag `--` are positional parameters. It can be used to specify filenames that start with a dash: + +``` +create -d "Description" -- -filename.txt +``` + +`storage-daemon-cli` can be run in non-interactive mode by passing it commands to execute: + +``` +storage-daemon-cli ... -c "add-by-meta m" -c "list --hashes" +``` + +## Adding a Bag of Files + +To download a _Bag of Files_, you need to know its `BagID` or have a meta-file. The following commands can be used to add a _Bag_ for download: + +``` +add-by-hash -d directory +add-by-meta -d directory +``` + +The _Bag_ will be downloaded to the specified directory. You can omit it, then it will be saved to the storage daemon directory. + +:::info +The hash is specified in hexadecimal form (length - 64 characters). +::: + +When adding a _Bag_ by a meta-file, information about the _Bag_ will be immediately available: size, description, list of files. When adding by hash, you will have to wait for this information to be loaded. + +## Managing Added Bags + +- The `list` command outputs a list of _Bags_. +- `list --hashes` outputs a list with full hashes. + +In all subsequent commands, `` is either a hash (hexadecimal) or an ordinal number of the _Bag_ within the session (a number that can be seen in the list by the `list` command). Ordinal numbers of _Bags_ are not saved between restarts of storage-daemon-cli and are not available in non-interactive mode. + +### Methods + +- `get ` - outputs detailed information about the _Bag_: description, size, download speed, list of files. +- `get-peers ` - outputs a list of peers. +- `download-pause `, `download-resume ` - pauses or resumes downloading. +- `upload-pause `, `upload-resume ` - pauses or resumes uploading. +- `remove ` - removes the _Bag_. `remove --remove-files` also deletes all files of the _Bag_. Note that if the _Bag_ is saved in the internal storage daemon directory, the files will be deleted in any case. + +## Partial Download, Priorities + +:::info +When adding a _Bag_ you can specify which files you want to download from it: +::: + +``` +add-by-hash -d dir --partial file1 file2 file3 +add-by-meta -d dir --partial file1 file2 file3 +``` + +### Priorities + +Each file in the _Bag of Files_ has a priority, a number from 0 to 255. Priority 0 means the file won't be downloaded. The `--partial` flag sets the specified files to priority 1, the others to 0. + +You can change the priorities of an already added _Bag_ with the following commands: + +- `priority-all ` - for all files. +- `priority-idx ` - for one file by number (see with `get` command). +- `priority-name ` - for one file by name. + Priorities can be set even before the list of files is downloaded. + +## Creating a Bag of Files + +To create a _Bag_ and start distributing it, use the `create` command: + +``` +create +``` + +`` can point to either a single file or a directory. When creating a _Bag_, you can specify a description: + +``` +create -d "Bag of Files description" +``` + +After the _Bag_ is created, the console will display detailed information about it (including the hash, which is the `BagID` by which the _Bag_ will be identified), and the daemon will start distributing the torrent. Extra options for `create`: + +- `--no-upload` - daemon will not distribute files to peers. Upload can be started using `upload-resume`. +- `--copy` - files will be copied to an internal directory of storage daemon. + +To download the _Bag_, other users just need to know its hash. You can also save the torrent meta file: + +``` +get-meta +``` From ffec2740042e4e1a6025a4a586ae0806abce599f Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:46 +0800 Subject: [PATCH 194/219] New translations storage-faq.md (Chinese Simplified) --- .../participate/ton-storage/storage-faq.md | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/ton-storage/storage-faq.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/ton-storage/storage-faq.md b/i18n/zh/docusaurus-plugin-content-docs/current/participate/ton-storage/storage-faq.md new file mode 100644 index 0000000000..f3f57d0678 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/ton-storage/storage-faq.md @@ -0,0 +1,55 @@ +# TON Storage FAQ + +## How to assign a TON domain to a TON Storage bag of files + +1. [Upload](/participate/ton-storage/storage-daemon#creating-a-bag-of-files) the bag of files to the network and get the Bag ID + +2. Open the Google Chrome browser on your computer. + +3. Install [TON extension](https://chrome.google.com/webstore/detail/ton-wallet/nphplpgoakhhjchkkhmiggakijnkhfnd) for Google Chrome. + You can also use [MyTonWallet](https://chrome.google.com/webstore/detail/mytonwallet/fldfpgipfncgndfolcbkdeeknbbbnhcc). + +4. Open the extension, click "Import wallet" and import the wallet that owns the domain, using the recovery phrase. + +5. Now open your domain at https://dns.ton.org and click "Edit". + +6. Copy your Bag ID into the "Storage" field and click "Save". + +## How to host static TON site in TON Storage + +1. [Create](/participate/ton-storage/storage-daemon#creating-a-bag-of-files) the Bag from folder with website files, upload it to the network and get the Bag ID. The folder must contain `index.html` file. + +2. Open the Google Chrome browser on your computer. + +3. Install [TON extension](https://chrome.google.com/webstore/detail/ton-wallet/nphplpgoakhhjchkkhmiggakijnkhfnd) for Google Chrome. + You can also use [MyTonWallet](https://chrome.google.com/webstore/detail/mytonwallet/fldfpgipfncgndfolcbkdeeknbbbnhcc). + +4. Open the extension, click "Import wallet" and import the wallet that owns the domain, using the recovery phrase. + +5. Now open your domain at https://dns.ton.org and click "Edit". + +6. Copy your Bag ID into the "Site" field, select "Host in TON Storage" checkbox and click "Save". + +## How to migrate TON NFT content to TON Storage + +If you used a [standard NFT smart contract](https://github.com/ton-blockchain/token-contract/blob/main/nft/nft-collection-editable.fc) for your collection, you need to send a [message](https://github.com/ton-blockchain/token-contract/blob/2d411595a4f25fba43997a2e140a203c140c728a/nft/nft-collection-editable.fc#L132) to the collection smart contract from the collection owner's wallet with a new URL prefix. + +As an example, if the url prefix used to be `https://mysite/my_collection/`, the new prefix will be `tonstorage://my_bag_id/`. + +## How to assign a TON domain to a TON Storage bag (Low Level) + +You need to assign the following value to the sha256("storage") DNS Record of your TON domain: + +``` +dns_storage_address#7473 bag_id:uint256 = DNSRecord; +``` + +## How to host static TON site in TON Storage (Low Level) + +[Create](/participate/ton-storage/storage-daemon#creating-a-bag-of-files) the Bag from folder with website files, upload it to the network and get the Bag ID. The folder must contain `index.html` file. + +You need to assign the following value to the sha256("site") DNS Record of your TON domain: + +``` +dns_storage_address#7473 bag_id:uint256 = DNSRecord; +``` From 5d11e32d96b3ee61b3f49a5f5a0a13b4cf0f5a66 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:47 +0800 Subject: [PATCH 195/219] New translations storage-provider.md (Chinese Simplified) --- .../ton-storage/storage-provider.md | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/ton-storage/storage-provider.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/ton-storage/storage-provider.md b/i18n/zh/docusaurus-plugin-content-docs/current/participate/ton-storage/storage-provider.md new file mode 100644 index 0000000000..173b7eb0a2 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/ton-storage/storage-provider.md @@ -0,0 +1,150 @@ +# Storage provider + +_A storage provider_ is a service that stores files for a fee. + +## Binaries + +You can download `storage-daemon` and `storage-daemon-cli` for Linux/Windows/MacOS binaries from [TON Auto Builds](https://github.com/ton-blockchain/ton/releases/latest). + +## Compile from sources + +You can compile `storage-daemon` and `storage-damon-cli` from sources using this [instruction](/develop/howto/compile#storage-daemon). + +## Key concepts + +It consists of a smart contract that accepts storage requests and manages payment from clients, and an application that uploads and serves the files to clients. Here's how it works: + +1. The owner of the provider launches the `storage-daemon`, deploys the main smart contract, and sets up the parameters. The contract's address is shared with potential clients. +2. Using the `storage-daemon`, the client creates a Bag from their files and sends a special internal message to the provider's smart contract. +3. The provider's smart contract creates a storage contract to handle this specific Bag. +4. The provider, upon finding the request in the blockchain, downloads the Bag and activates the storage contract. +5. The client can then transfer payment for storage to the storage contract. To receive the payment, the provider regularly presents the contract with proof that they are still storing the Bag. +6. If the funds on the storage contract run out, the contract is considered inactive and the provider is no longer required to store the Bag. The client can either refill the contract or retrieve their files. + +:::info +The client can also retrieve their files at any time by providing proof of ownership to the storage contract. The contract will then release the files to the client and deactivate itself. +::: + +## Smart contract + +[Smart Contract Source Code](https://github.com/ton-blockchain/ton/tree/master/storage/storage-daemon/smartcont). + +## Using a Provider by Clients + +In order to use a storage provider, you need to know the address of its smart contract. The client can obtain the provider's parameters with the following command in `storage-daemon-cli`: + +``` +get-provider-params
+``` + +### The provider's parameters: + +- Whether new storage contracts are accepted. +- Minimum and maximum _Bag_ size (in bytes). +- Rate - the cost of storage. Specified in nanoTON per megabyte per day. +- Max span - how often the provider should provide proofs of _Bag_ storage. + +### A request to store + +You need to create a _Bag_ and generate a message with the following command: + +``` +new-contract-message --query-id 0 --provider
+``` + +### Info: + +Executing this command may take some time for large _Bags_. The message body will be saved to `` (not the entire internal message). Query id can be any number from 0 to `2^64-1`. The message contains the provider's parameters (rate and max span). These parameters will be printed out after executing the command, so they should be double checked before sending. If the provider's owner changes the parameters, the message will be rejected, so the conditions of the new storage contract will be exactly what the client expects. + +The client must then send the message with this body to the provider's address. In case of an error the message will come back to the sender (bounce). Otherwise, a new storage contract will be created and the client will receive a message from it with [`op=0xbf7bd0c1`](https://github.com/ton-blockchain/ton/tree/testnet/storage/storage-daemon/smartcont/constants.fc#L3) and the same query id. + +At this point the contract is not yet active. Once the provider downloads the _Bag_, it will activate the storage contract and the client will receive a message with [`op=0xd4caedcd`](https://github.com/SpyCheese/ton/blob/tonstorage/storage/storage-daemon/smartcont/constants.fc#L4) (also from the storage contract). + +The storage contract has a "client balance" - these are the funds that the client transferred to the contract and which have not yet been paid to the provider. Funds are gradually debited from this balance (at a rate equal to the rate per megabyte per day). The initial balance is what the client transferred with the request to create the storage contract. The client can then top up the balance by making simple transfers to the storage contract (this can be done from any address). The remaining client balance is returned by the [`get_storage_contract_data`](https://github.com/ton-blockchain/ton/tree/testnet/storage/storage-daemon/smartcont/storage-contract.fc#L222) get method as the second value (`balance`). + +### The contract may be closed for the following cases: + +:::info +In case of the storage contract being closed, the client receives a message with the remaining balance and [`op=0xb6236d63`](https://github.com/ton-blockchain/ton/tree/testnet/storage/storage-daemon/smartcont/constants.fc#L6). +::: + +- Immediately after creation, before activation, if the provider refuses to accept the contract (the provider's limit is exceeded or other errors). +- The client balance reaches 0. +- The provider can voluntarily close the contract. +- The client can voluntarily close the contract by sending a message with [`op=0x79f937ea`](https://github.com/ton-blockchain/ton/tree/testnet/storage/storage-daemon/smartcont/constants.fc#L2) from its own address and any query id. + +## Running and Configuring a Provider + +The Storage Provider is part of the `storage-daemon`, and is managed by the `storage-daemon-cli`. `storage-daemon` needs to be started with the `-P` flag. + +### Create a main smart contract + +You can do this from `storage-daemon-cli`: + +``` +deploy-provider +``` + +:::info IMPORTANT! +You will be asked to send a non-bounceable message with 1 TON to the specified address in order to initialize the provider. You can check that the contract has been created using the `get-provider-info` command. +::: + +By default, the contract is set to not accept new storage contracts. Before activating it, you need to configure the provider. The provider's settings consist of a configuration (stored in `storage-daemon`) and contract parameters (stored in the blockchain). + +### Configuration: + +- `max contracts` - maximum number of storage contracts that can exist at the same time. +- `max total size` - maximum total size of _Bags_ in storage contracts. + You can view the configuration values using `get-provider-info`, and change them with: + +``` +set-provider-config --max-contracts 100 --max-total-size 100000000000 +``` + +### Contract parameters: + +- `accept` - whether to accept new storage contracts. +- `max file size`, `min file size` - size limits for one _Bag_. +- `rate` - storage cost (specified in nanoTONs per megabyte per day). +- `max span` - how often the provider will have to submit storage proofs. + +You can view the parameters using `get-provider-info`, and change them with: + +``` +set-provider-params --accept 1 --rate 1000000000 --max-span 86400 --min-file-size 1024 --max-file-size 1000000000 +``` + +### It is worth paying attention + +Note: in the `set-provider-params` command, you can specify only some of the parameters. The others will be taken from the current parameters. Since the data in the blockchain is not updated instantly, several consecutive `set-provider-params` commands can lead to unexpected results. + +It is recommended to initially put more than 1 TON on the provider's balance, so that there are enough funds to cover the commissions for working with storage contracts. However, do not send too many TONs with the first non-bounceable message. + +After setting the `accept` parameter to `1`, the smart contract will start accepting requests from clients and creating storage contracts, while the storage daemon will automatically process them: downloading and distributing _Bags_, generating storage proofs. + +## Further Work With the Provider + +### List of existing storage contracts + +``` +get-provider-info --contracts --balances +``` + +Each storage contract has a `Client$` and `Contract$` balance listed; the difference between them can be withdrawn to the main provider contract with the command `withdraw
`. + +The command `withdraw-all` will withdraw funds from all contracts that have at least `1 TON` available. + +Any storage contract can be closed with the command `close-contract
`. This will also transfer the funds to the main contract. The same will happen automatically when the client's balance runs out. The _Bag_ files will be deleted in this case (unless there are other contracts using the same _Bag_). + +### Transfer + +You can transfer funds from the main smart contract to any address (the amount is specified in nanoTON): + +``` +send-coins
+send-coins
--message "Some message" +``` + +:::info +All _Bags_ stored by the provider are available with the command `list`, and can be used as usual. To prevent disrupting the provider's operations, do not delete them or use this storage daemon to work with any other _Bags_. +::: From 91458d1181fb55e86b5faef55bb9b4062bc1f531 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:48 +0800 Subject: [PATCH 196/219] New translations apps.mdx (Chinese Simplified) --- .../current/participate/wallets/apps.mdx | 245 ++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/wallets/apps.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/wallets/apps.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/participate/wallets/apps.mdx new file mode 100644 index 0000000000..8ddfe25820 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/wallets/apps.mdx @@ -0,0 +1,245 @@ +import Player from '@site/src/components/player' + +# Wallet Apps (for Devs) + +## Overview + +This article describes wallets from a developmental perspective. The end goal is to create wallet applications that support TON mass adoption. + +

+ +

+ +If you want to find a wallet to install, open [ton.org/wallets](https://ton.org/wallets). + +## Non-Custodial Software (Hot) Wallets + +:::info +A Software wallet, more often known as a hot wallet, operates as software on a host device and they store your private keys within its interface. Mostly, these wallets are non-custodial, meaning they give you custody of your keys. +::: + +Here are some non-custodial wallets for TON Blockchain: + +- [TON Wallet](https://chrome.google.com/webstore/detail/ton-wallet/nphplpgoakhhjchkkhmiggakijnkhfnd) — Multiplatform (iOS, Android, macOS, Linux, Windows) classic wallets of TON Ecosystem developed by TON Foundation. +- [Tonkeeper](https://tonkeeper.com/) — an open-source multi-platform (iOS, Android, Firefox, Chrome) wallet with a great user base. +- [Tonhub](https://tonhub.com/) — an open-source (iOS, Android) alternative mobile phone wallet with advanced features (TON Whales Staking UI). +- [OpenMask](https://www.openmask.app/) — an open-source Chrome extension wallet with biometric authentication. +- [MyTonWallet](https://mytonwallet.io/) — an open-source browser web wallet and a browser extension wallet for TON. + +### Basics features + +| Wallet | Jetton & NFT transfers | Ton Connect 2.0 | Authorization | Wallet contract | +| ----------- | ----------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | +| TON Wallet | Not implemented | - | - | [wallet v3](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/wallet3-code.fc) | +| Tonkeeper | Supported | [Supported](https://github.com/ton-society/ton-footsteps/issues/161) | [TON Connect 2.0](/develop/dapps/ton-connect) | [wallet v4](https://github.com/ton-blockchain/wallet-contract/tree/3fd1d7ae39f1c46ec1f2be54c4040d8d87505e0f) | +| Tonhub | Supported\* | [Supported](https://github.com/ton-society/ton-footsteps/issues/108) | [TON Connect 2.0](/develop/dapps/ton-connect) | [wallet v4](https://github.com/ton-blockchain/wallet-contract/tree/3fd1d7ae39f1c46ec1f2be54c4040d8d87505e0f) | +| OpenMask | Supported | [Supported](https://github.com/ton-society/ton-footsteps/issues/107) | [TON Connect 2.0](/develop/dapps/ton-connect) | [wallet v4](https://github.com/ton-blockchain/wallet-contract/tree/3fd1d7ae39f1c46ec1f2be54c4040d8d87505e0f) | +| MyTonWallet | Supported | [Supported](https://github.com/ton-society/ton-footsteps/issues/149) | [TON Connect 2.0](/develop/dapps/ton-connect) | [wallet v4](https://github.com/ton-blockchain/wallet-contract/tree/3fd1d7ae39f1c46ec1f2be54c4040d8d87505e0f) | + +> \*Tonhub extension opens a built-in browser, which allows NFT market placements. Jetton is fully supported. + +### TON Wallet + +TON Wallet was the first step in mass-adoption blockchain technology available to ordinary users. It demonstrates how a wallet should work on TON Blockchain. + +| | +| --------------------------------------------------------------------------------------------------------- | +|               ![TON wallet](/img/docs/wallet-apps/TonWallet.png?raw=true)                                 | + +#### Pros and Cons + +- ✅ The original wallet developed by TON Foundation. TON Wallet works according to the vision of TON Blockchain's core developers. +- ✅ Multi-platform architecture support. It works in Linux, Windows, macOS, iOS, Android and as well as a Chrome plugin. +- ✅ [Bug bounty program](https://github.com/ton-blockchain/bug-bounty) +- ❌ Rarely updates. This wallet does not have not all up-to-date features (TON DNS addresses, and the wallet-v4 contract isn't supported). +- ❌ The current UI is outdated and is worse than alternative wallets. + +#### Ton Wallet test environment + +To switch TON classic wallet to test environment, you should to open in browser with testnet parameter: + +#### Links + +- [GitHub\*](https://github.com/ton-blockchain/wallet-ios) + + > \*TON Wallet clients for every supported OS placed in nearby repositories. + +### Tonkeeper + +[Tonkeeper](https://tonkeeper.com/) - is the most downloaded and popular wallet, developed by the Tonkeeper team, and has active support from both users and developers. + +| | +| -------------------------------------------------------------------------------------------------------- | +|               ![Tonkeeper](/img/docs/wallet-apps/Tonkeeper.png?raw=true)                                 | + +#### Pros and Cons + +- ✅ This app is most popular with users. +- ✅ Supports all up-to-date features including native NFT transfer between user wallets. +- ✅ Supports all major platforms, such as iOS and Android, but also works in popular browsers, such as Firefox or Chrome. +- ❌ Requires advanced skills to contribute to its source code. A lot of the job is already done, and it will be hard for newcomers to add something significant or useful. + +#### Tonkeeper test environment + +To switch Tonkeeper application between Mainnet and Testnet: in settings, tap 5 times on the Tonkeeper icon at the bottom and switch the network in the Dev menu. In the Dev menu tap `Switch to Testnet` or `Switch to Mainnet` according to the network you need. + +| | +| --------------------------------------------------------------------------------------------------------------- | +|               ![TestMode](/img/docs/wallet-apps/Tonkeeper-testnet.png?raw=true)                                 | + +#### Links + +- [GitHub](https://github.com/tonkeeper/wallet) +- [Tonkeeper Wallet API](https://github.com/tonkeeper/wallet-api) + +### Tonhub + +[Tonhub](https://tonhub.com/) - is another fully-fledged TON wallet, that has basic up-to-date features support. Ton Whales are rapidly increasing the capabilities of the wallet. + +| | +| -------------------------------------------------------------------------------------------------- | +|               ![Tonhub](/img/docs/wallet-apps/Tonhub.png?raw=true)                                 | + +#### Pros and Cons + +- ✅ Has own custom [Ton Nominator](https://github.com/tonwhales/ton-nominators) contract supported with Tonhub UI. +- ✅ Open-source wallet from the beginning of the existing application. +- ✅ [Bug bounty program](https://tonwhales.com/bounty). +- ❌ Doesn't have any kind of support for desktop platforms. +- ❌ Requires advanced skills to contribute to its source code. + +#### Tonhub test environment + +You will need to download separate application to connect to the Testnet. + +#### Links + +- [GitHub](https://github.com/tonwhales/wallet) +- [Sandbox iOS](https://apps.apple.com/app/ton-development-wallet/id1607857373) +- [Sandbox Android](https://play.google.com/store/apps/details?id=com.tonhub.wallet.testnet) + +### OpenMask + +[OpenMask](https://www.openmask.app/) - is the trailblazing tool enabling user interactions and experience in Web3 as a browser extension. + +| | +| ------------------------------------------------------------------------------------------------------ | +|               ![OpenMask](/img/docs/wallet-apps/OpenMask.png?raw=true)                                 | + +#### Pros and Cons + +- ✅ Convenient for developers to learn and create dApps via desktop without mobile devices. +- ✅ Unique functions such as multiple wallets, with detailed descriptions and examples in its documentation. +- ❌ Has almost no integration with dApps at the moment. +- ❌ Supports only browser extension platform. + +#### OpenMask test environment + +To switch OpenMask between Mainnet and Testnet: you need to click on "mainnet/testnet" button on the top of the OpenMask's main screen and choose the network you need. + +#### Links + +- [GitHub](https://github.com/OpenProduct/openmask-extension) +- [Documentation](https://www.openmask.app/docs/introduction) + +### MyTonWallet + +[MyTonWallet](https://mytonwallet.io/) - is the most feature-rich web wallet and browser extension for TON – with support of tokens, NFT, TON DNS, TON Sites, TON Proxy, and TON Magic. + +| | +| ------------------------------------------------------------------------------------------------------------ | +|               ![MyTonWallet](/img/docs/wallet-apps/MyTonWallet.png?raw=true)                                 | + +#### Pros and Cons + +- ✅ Has all basic features implemented. +- ✅ Unique feature - management of the official [Nominator Pool contract](https://github.com/ton-blockchain/nominator-pool) from the wallet UI. +- ✅ Supports all major platforms (such as macOS, Linux and Windows) and also runs in Chrome as an extension. +- ❌ Doesn't work in Firefox as an extension. + +#### MyTonWallet test environment + +To switch MyTonWallet to Testnet: open Settings and by clicking 5 times on the app version open the network switcher. + +#### Links + +- [GitHub](https://github.com/mytonwalletorg/mytonwallet) +- [MyTonWallet Telegram](https://t.me/MyTonWalletRu) + +## Non-Custodial Hardware (Cold) Wallets + +:::info +A hardware wallet is a physical device that stores the private keys to your cryptocurrency funds away from the internet. Even if you make transactions with it, the wallet confirms the transactions in an offline environment. This process helps keep your private keys away from the risks of the internet at all times. +::: + +### Ledger + +[Ledger](https://www.ledger.com/) hardware wallets with Ledger Live app. + +#### Links + +- [Ledger TON Blogpost](https://blog.ton.org/ton-is-coming-to-ledger-hardware-wallets) user manual for TON in the Ledger wallets. +- [Ledger](https://www.ledger.com/) official site. + +### SafePal + +[SafePal](https://www.safepal.com/en/) is your gateway to the rapidly expanding galaxy of decentralized applications. + +#### Links + +- [SafePal](https://www.safepal.com/en/) official site + +## Custodial wallets + +:::info +With a custodial wallet, user trusts somebody else to hold the wallet's private key. +::: + +### @wallet + +[@wallet](https://t.me/wallet) — a bot to send and receive or trade TON for real money using P2P in Telegram. Supports Telegram Mini App UI. + +| | +| -------------------------------------------------------------------------------------------------- | +|               ![wallet](/img/docs/wallet-apps/Wallet.png?raw=true)                                 | + +### @cryptobot + +[@cryptobot](https://t.me/cryptobot) — A Telegram bot wallet for storing, sending and exchanging TON. + +| | +| -------------------------------------------------------------------------------------------------------- | +|               ![CryptoBot](/img/docs/wallet-apps/CryptoBot.png?raw=true)                                 | + +#### Links for 3d-party integrations + +- [Crypto Pay API](https://help.crypt.bot/crypto-pay-api) + +### @tonRocketBot + +[@tonRocketBot](https://t.me/tonRocketBot) - A Telegram bot wallet for storing, sending and exchanging TON. Supports Jetton trading as well. + +| | +| -------------------------------------------------------------------------------------------------------------- | +|               ![tonrocketbot](/img/docs/wallet-apps/tonrocketbot.png?raw=true)                                 | + +#### Links for 3d-party integrations + +- [Rocket exchange](https://trade.ton-rocket.com/api/) +- [Rocket pay docs](https://pay.ton-rocket.com/api/) + +## Multi signature Wallets + +### Tonkey + +Tonkey is an advanced project that introduces multi-signature functionality to TON Blockchain. + +### Links + +- https://tonkey.app/ +- [GitHub](https://github.com/tonkey-app) + +## See Also + +- [What is blockchain? What is a smart contract? What is gas?](https://blog.ton.org/what-is-blockchain) +- [Types of Wallet Contracts](/participate/wallets/contracts) From 5aea267b899928c1edcce8c305e3afa8db585c3c Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:49 +0800 Subject: [PATCH 197/219] New translations contracts.md (Chinese Simplified) --- .../current/participate/wallets/contracts.md | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/wallets/contracts.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/wallets/contracts.md b/i18n/zh/docusaurus-plugin-content-docs/current/participate/wallets/contracts.md new file mode 100644 index 0000000000..d862764e36 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/wallets/contracts.md @@ -0,0 +1,162 @@ +# Types of Wallet Contracts + +You have probably heard somewhere about different versions of wallets in TON Blockchain. But what do these versions really mean and how do they differ? + +In this article, we'll look at all versions and modifications of TON wallets. + +## How can wallets be different? + +Before we start, we need to understand how wallets can differ at all. + +If we look at Ethereum, Solana or almost any other blockchain, there are not different types or versions of wallets. But why do they exist in TON? It's because wallets in TON are made by smart contracts. Basically, any wallet (even yours) is a smart contract running on TON Blockchain which can accept and send transactions to other wallets which are also smart contracts. + +These smart contracts can be set up in different ways and can have different features. That's why there are several versions of wallets in TON. + +## Basic wallets + +### Wallet V1 + +This is the simplest one. It only allows you to send one transaction at the time and it doesn't check anything besides your signature and seqno. + +This version isn't even used in regular apps because it has some major issues: + +- No easy way to retrieve the seqno and public key from the contract +- No `valid_until` check, so you can't be sure that the transaction won't be confirmed too late. + +The first issue is fixed in `V1R2` and `V1R3`. That `R` letter means `revision`. Usually revisions are just small updates which only add get-methods which allows you to retrieve seqno and public key from the contract. +But this version also has a second issue, which is fixed in the next version. + +Wallet source code: + +- [ton/crypto/smartcont/wallet-code.fc](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/wallet-code.fc) + +### Wallet V2 + +This version introduces the `valid_until` parameter which is used to set a time limit for a transaction in case you don't want it to be confirmed too late. This version also doesn't have the get-method for public key, which is added in `V2R2`. + +It can be used in most cases, but it misses one cool feature, which was added in `V3`. + +Wallet source code: + +- [ton/crypto/smartcont/new-wallet-v2.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/new-wallet-v2.fif) + +### Wallet V3 + +This version introduces the `subwallet_id` parameter, which allows you to create multiple wallets using the same public key (so you can have only one seed phrase and lots of wallets). And, as before, `V3R2` only adds the get-method for public key. + +Basically, `subwallet_id` is just a number which is added to the contract state when it is deployed. And since the contract address in TON is a hash of its state and code, the wallet address will change with a different `subwallet_id`. + +This version is the most used right now. It covers most use-cases and remains clean and simple. + +Wallet source code: + +- [ton/crypto/smartcont/wallet-v3-code.fif](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/wallet-v3-code.fif) + +### Wallet V4 + +It is the most modern wallet version at the moment. It still has all the functionality of the previous versions, but also introduces something very powerful — `plugins`. + +This feature allows developers to implement complex logic that will work in tandem with a user's wallet. For example, some DApp may require a user to pay a small amount of coins every day to use some features, so the user will need to install the plugin on their wallet by signing a transaction. This plugin will send coins to the destination address every day when it will be reqested by an external message. + +This is a very customizable feature which is unique to TON Blockchain. + +Wallet source code: + +- [ton-blockchain/wallet-contract](https://github.com/ton-blockchain/wallet-contract) + +## Special wallets + +Sometimes the functionality of basic wallets isn't enough. That's why there are several types of specialized wallet: `high-load`, `lockup` and `restricted`. + +Let's have a look at them. + +### Highload Wallet v3 + +This wallet is made for who need to send transactions at very high rates. For example, crypto exchanges. + +- [Source code](https://github.com/ton-blockchain/highload-wallet-contract-v3) + +Any given external message (transfer request) to a highload v3 contains: + +- a signature (512 bits) in the top level cell - the other parameters are in the ref of that cell +- subwallet ID (32 bits) +- message to send as a ref (the serialized internal message that will be sent) +- send mode for the message (8 bits) +- composite query ID - 13 bits of "shift" and 10 bits of "bit number", however the 10 bits of bit number can only go up to 1022, not 1023, and also the last such usable query ID (8388605) is reserved for emergencies and should not be normally used +- created at, or message timestamp +- timeout + +Timeout is stored in highload as a parameter and is checked against the timeout in all requests - so the timeout for all requests is equal. The message should be not older than timeout at the time of arrival to the highload wallet, or in code it is required that `created_at > now() - timeout`. Query IDs are stored for the purposes of replay protection for at least timeout and possibly up to 2 \* timeout, however one should not expect them to be stored for longer than timeout. Subwallet ID is checked against the one stored in the wallet. Inner ref's hash is checked along with the signature against the public key of the wallet. + +Highload v3 can only send 1 message from any given external message, however it can send that message to itself with a special op code, allowing one to set any action cell on that internal message invocation, effectively making it possible to send up to 254 messages per 1 external message (possibly more if another message is sent to highload wallet again among these 254). + +Highload v3 will always store the query ID (replay protection) once all the checks pass, however a message may not be sent due to some conditions, including but not limited to: + +- **containing state init** (such messages, if required, may be sent using the special op code to set the action cell after an internal message from highload wallet to itself) +- not enough balance +- invalid message structure (that includes external out messages - only internal messages may be sent straight from the external message) + +Highload v3 will never execute multiple externals containing the same `query_id` **and** `created_at` - by the time it forgets any given `query_id`, the `created_at` condition will prevent such a message from executing. This effectively makes `query_id` **and** `created_at` together the "primary key" of a transfer request for highload v3. + +When iterating (incrementing) query ID, it is cheaper (in terms of TON spent on fees) to iterate through bit number first, and then the shift, like when incrementing a regular number. After you've reached the last query ID (remember about the emergency query ID - see above), you can reset query ID to 0, but if highload's timeout period has not passed yet, then the replay protection dictionary will be full and you will have to wait for the timeout period to pass. + +### Highload wallet v2 + +:::danger +Legacy contract, it is suggest to use High-load wallet v3. +::: + +This wallet is made for those who need to send hundreds of transactions in a short period of time. For example, crypto exchanges. + +It allows you to send up to `254` transactions in one smart contract call. It also uses a slightly different approach to solve replay attacks instead of seqno, so you can call this wallet several times at once to send even thousands of transactions in a second. + +:::caution Limitations +Note, when dealing with highload-wallet the following limits need to be checked and taken into account. +::: + +1. **Storage size limit.** Currently, size of contract storage should be less than 65535 cells. If size of + old_queries will grow above this limit, exception in ActionPhase will be thrown and transaction will fail. + Failed transaction may be replayed. +2. **Gas limit.** Currently, gas limit is 1'000'000 GAS units, that means that there is a limit of how much + old queries may be cleaned in one tx. If number of expired queries will be higher, contract will stuck. + +That means that it is not recommended to set too high expiration date: +number of queries during expiration timespan should not exceed 1000. + +Also, number of expired queries cleaned in one transaction should be below 100. + +Wallet source code: + +- [ton/crypto/smartcont/highload-wallet-v2-code.fc](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/highload-wallet-v2-code.fc) + +### Lockup wallet + +If you, for some reason, need to lock coins in a wallet for some time without the possibility to withdraw them before that time passes, have a look at the lockup wallet. + +It allows you to set the time until which you won't be able to withdraw anything from the wallet. You can also customize it by setting unlock periods so that you will be able to spend some coins during these set periods. + +For example: you can create a wallet which will hold 1 million coins with total vesting time of 10 years. Set the cliff duration to one year, so the funds will be locked for the first year after the wallet is created. Then, you can set the unlock period to one month, so `1'000'000 TON / 120 months = ~8333 TON` will unlock every month. + +Wallet source code: + +- [ton-blockchain/lockup-wallet-contract](https://github.com/ton-blockchain/lockup-wallet-contract) + +### Restricted wallet + +This wallet's function is to act like a regular wallet, but restrict transfers to only one pre-defined destination address. You can set the destination when you create this wallet and then you'll be only able to transfer funds from it to that address. But note that you can still transfer funds to validation contracts so you can run a validator with this wallet. + +Wallet source code: + +- [EmelyanenkoK/nomination-contract/restricted-wallet](https://github.com/EmelyanenkoK/nomination-contract/tree/master/restricted-wallet) + +## Conclusion + +As you see, there are many different versions of wallets in TON. But in most cases, you only need `V3R2` or `V4R2`. You can also use one of the special wallets if you want to have some additional functionality like a periodic unlocking of funds. + +## See Also + +- [Sources of basic wallets](https://github.com/ton-blockchain/ton/tree/master/crypto/smartcont) +- [More technical description of versions](https://github.com/toncenter/tonweb/blob/master/src/contract/wallet/WalletSources.md) +- [Wallet V4 sources and detailed description](https://github.com/ton-blockchain/wallet-contract) +- [Lockup wallet sources and detailed description](https://github.com/ton-blockchain/lockup-wallet-contract) +- [Restricted wallet sources](https://github.com/EmelyanenkoK/nomination-contract/tree/master/restricted-wallet) From 0ddbc2a03d4a89d525abbe9916561648f3216b2f Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:49 +0800 Subject: [PATCH 198/219] New translations dns.md (Chinese Simplified) --- .../current/participate/web3/dns.md | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/dns.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/dns.md b/i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/dns.md new file mode 100644 index 0000000000..177113b6d0 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/dns.md @@ -0,0 +1,45 @@ +# TON DNS & Domains + +TON DNS is a service for translating human-readable domain names (such as `test.ton` or `mysite.temp.ton`) into TON Smart Contract Addresses, ADNL Addresses employed by services running on the TON Network (such as TON Sites) and so on. + +## Standard + +[TON DNS Standard](https://github.com/ton-blockchain/TIPs/issues/81) describes the format of domain names, the process of resolving a domain, the interface of DNS smart contracts and the format of DNS records. + +## SDK + +Working with TON DNS is implemented in JavaScript SDK [TonWeb](https://github.com/toncenter/tonweb) and [TonLib](https://ton.org/#/apis/?id=_2-ton-api). + +```js +const address: Address = await tonweb.dns.getWalletAddress('test.ton'); + +// or + +const address: Address = await tonweb.dns.resolve('test.ton', TonWeb.dns.DNS_CATEGORY_WALLET); +``` + +Also `lite-client` and `tonlib-cli` is supported by DNS queries. + +## First-level domain + +Currently, only domains ending in `.ton` are recognized as valid TON DNS domains. + +Root DNS smart contract source code - https://github.com/ton-blockchain/dns-contract/blob/main/func/root-dns.fc. + +This could change in the future. Adding a new first-level domain will require new root smart contract and general vote to change the [network config #4](https://ton.org/#/smart-contracts/governance?id=config). + +## \*.ton domains + +\*.ton domains are implemented in the form of an NFT. Since they implement the NFT standard, they are compatible with regular NFT services (e.g. NFT marketplaces) and wallets that can display NFT. + +\*.ton domains source code - https://github.com/ton-blockchain/dns-contract. + +.ton domains resolver implements an NFT collection interface and .ton domain implements an NFT item interface. + +The primary sale of \*.ton domains happens via a decentralized open auction at https://dns.ton.org. Source code - https://github.com/ton-blockchain/dns. + +## Subdomains + +The domain owner can make subdomains by setting the address of the smart contract responsible for resolving subdomains in the DNS record `sha256("dns_next_resolver")`. + +It can be any smart contract that implements the DNS standard. From 3457d474acabb987dac272f42a5ee0b7a275e41a Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:50 +0800 Subject: [PATCH 199/219] New translations how-to-open-any-ton-site.md (Chinese Simplified) --- .../web3/how-to-open-any-ton-site.md | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/how-to-open-any-ton-site.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/how-to-open-any-ton-site.md b/i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/how-to-open-any-ton-site.md new file mode 100644 index 0000000000..4e2ee02528 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/how-to-open-any-ton-site.md @@ -0,0 +1,47 @@ +# How to open any TON Site? + +In this article we'll have a look at most common ways to visit TON Sites from different devices. + +Each method have it's own pros and cons which we will analyze here. + +we will start with the simplest methods and finish with the most advanced ones. + +## 😄 Easy methods + +### Browse through the ton.run + +The simplest way to open a TON Site is through [ton.run](https://ton.run). You don't need to install or set up anything on your device - just open the **ton.run** and you're ready to explore TON Sites. + +This method may be suitable for casual browsing of TON Sites or for some checks, but not for regular use, because it also has its drawbacks: + +- You trust your internet traffic to the **ton.run** +- It can go offline or break at any moment +- It can be blocked by your internet provider + +### TON Wallet and MyTonWallet extensions + +A bit harder but better method is to use some browser extension which will connect you to the TON Proxy and allow to browse TON Sites without any intermediate services such as ton.run. + +Currently, TON Proxy is already available in [MyTonWallet](https://mytonwallet.io/) extension and also will be available in [TON Wallet](https://chrome.google.com/webstore/detail/ton-wallet/nphplpgoakhhjchkkhmiggakijnkhfnd) extension soon. + +This method is also pretty easy, but you need to install an extension in your browser to make it work. It will be suitable for most users. + +### Connect to a public proxy + +If you don't want to install any extensions or if you are using mobile device, you can use this method. You'll have to configure something on your device to connect to the proxy. + +This method is described here: + +- [Connect with TON Proxy](/participate/web3/setting-proxy/) + +## 🤓 Advanced methods + +### Using Tonutils-Proxy + +This is the most secure way of accessing TON Sites. + +1. Download the latest version [from here](https://github.com/xssnick/Tonutils-Proxy#download-precompiled-version) + +2. Launch it and press "Start Gateway" + +3. Done! From f926aa0ee3e4178934e8b62046f22e8e3e1e3cee Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:51 +0800 Subject: [PATCH 200/219] New translations overview.mdx (Chinese Simplified) --- .../current/participate/web3/overview.mdx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/overview.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/overview.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/overview.mdx new file mode 100644 index 0000000000..ddd855a16c --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/overview.mdx @@ -0,0 +1,17 @@ +# Overview + +## Concepts + +Read more about ideas in: + +- [Payments on TON](https://blog.ton.org/ton-payments) +- [TON DNS & Domains](/participate/web3/dns) +- [TON Sites, TON WWW and TON Proxy](https://blog.ton.org/ton-sites) + +## Use-cases + +- [\*.ton user-friendly domains for any smart contract](/participate/web3/dns) +- [Connect to the TON Sites using TON Proxy](/participate/web3/setting-proxy) +- [Run own TON Proxy to connect to TON Sites](/participate/web3/sites-and-proxy) +- [Link your TON Wallet or TON Site to a domain](/participate/web3/site-management) +- [How to make a subdomain using TON DNS smart contracts](/participate/web3/site-management#how-to-set-up-subdomains) From 93ed5c0ad96685cd084f1d62b683774fd330aa4a Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:52 +0800 Subject: [PATCH 201/219] New translations setting-proxy.md (Chinese Simplified) --- .../current/participate/web3/setting-proxy.md | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/setting-proxy.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/setting-proxy.md b/i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/setting-proxy.md new file mode 100644 index 0000000000..bd59b91eee --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/setting-proxy.md @@ -0,0 +1,71 @@ +# Connect with TON Proxy + +## Public entry TON Proxies + +You can use one of the public entry TON Proxies: + +- `in1.ton.org` port `8080` +- `in2.ton.org` port `8080` +- `in3.ton.org` port `8080` + +TON Proxy is compatible with a regular HTTP proxy, so you can use it directly in your browser or operating system settings. + +## Google Chrome + +Follow the instructions for Windows, macOS, Linux, iOS, or Android, depending on your operating system. + +## Firefox + +Settings -> General -> Network Settings -> Configure -> Manual proxy settings -> HTTP Proxy + +In the "HTTP proxy" field, enter the address of one of the public entry proxies, in the "Port" field, enter "8080" without quotes. + +Click "OK". + +## Safari + +Follow the instructions for Windows, macOS, Linux, iOS, or Android, depending on your operating system. + +## iOS + +Settings -> WiFi -> Click on currently connected network -> Proxy setting -> Manual + +In the "Server" field, enter the address of one of the public entry proxies, in the "Port" field, enter "8080" without quotes. + +Click "Save". + +## Android + +Settings -> WiFi -> Tap and hold the Wi-Fi Network Name -> Modify Network -> Advanced Options -> Manual + +In the "Server" field, enter the address of one of the public entry proxies, in the "Port" field, enter "8080" without quotes. + +Click "Save". + +## Windows + +"Start" button, then select Settings > Network & internet > Proxy. + +Under "Manual proxy setup", next to "Use a proxy server", select "Set up". + +In the "Edit proxy server dialog" box, do the following: + +Turn on "Use a proxy server". + +Enter the address of one of the public entry proxies, in the "Port" field, enter "8080" without quotes. + +Click "Save". + +## macOS + +Settings -> Network -> Advanced -> Proxy -> Web proxy (HTTP). + +In the "Web proxy server" field, enter the address of one of the public entry proxies, after the colon, enter "8080" without quotes. + +Click "OK". + +## Ubuntu + +Settings -> Network -> Network Proxy Button -> Manual + +In the "HTTP Proxy" field, enter the address of one of the public entry proxies, for the port, enter "8080" without quotes. From b018565152e56f30262f7e38eaece5fb1db1eac7 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:53 +0800 Subject: [PATCH 202/219] New translations site-management.md (Chinese Simplified) --- .../participate/web3/site-management.md | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/site-management.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/site-management.md b/i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/site-management.md new file mode 100644 index 0000000000..70940f16a9 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/site-management.md @@ -0,0 +1,49 @@ +# Site & Domain Management + +## How to open a domain for editing + +1. Open the Google Chrome browser on your computer. + +2. Install the Google Chrome TON extension from this [link](https://chrome.google.com/webstore/detail/ton-wallet/nphplpgoakhhjchkkhmiggakijnkhfnd). + +3. Open the extension, click "Import wallet" and import the wallet where the domain is stored. + +> Recovery Phrases +> +> Your recovery phrase is the 24 words you wrote down when you created your wallet. +> +> If you lost your phrase, you can restore it using any TON Wallet. +> In Tonkeeper: Settings > Wallet protection > Your private key. +> +> Be sure to write down these 24 words and keep them in a safe place. In case of emergency, you will be able to restore access to the wallet only by recovery phrases. +> Keep your recovery phrases strictly confidential. Anyone who gains access to your recovery phrases will have full access to your funds. + +4. Now open your domain on https://dns.ton.org and click the "Edit" button. + +## How to link a wallet to a domain + +You can link a wallet to a domain, in which case users will be able to send coins to that wallet by entering the domain as the recipient address instead of the wallet address. + +1. Open the domain for editing as described above. + +2. Copy your wallet address into the "Wallet address" field and click "Save". + +3. Confirm sending the transaction in the extension. + +## How to link a TON Site to a domain + +1. Open the domain for editing as described above. + +2. Copy the ADNL Address of your TON Site in HEX format into the "Site" field and click "Save". + +3. Confirm sending the transaction in the extension. + +## How to set up subdomains + +1. Create a smart contract on the network that will manage the subdomains of your website or service. You can use ready-made [manual-dns](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/dns-manual-code.fc) or [auto-dns](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/dns-auto-code.fc) smart contracts, or any other smart contract that implements the TON DNS interface. + +2. Open the domain for editing as described above. + +3. Copy the smart contract address of the subdomains in the "Subdomains" field and click "Save". + +4. Confirm sending the transaction in the extension. From 1c262f867dc314b999fa31a27f39f283f7bce7ab Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:54 +0800 Subject: [PATCH 203/219] New translations sites-and-proxy.md (Chinese Simplified) --- .../participate/web3/sites-and-proxy.md | 204 ++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/sites-and-proxy.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/sites-and-proxy.md b/i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/sites-and-proxy.md new file mode 100644 index 0000000000..f0c51b832b --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/web3/sites-and-proxy.md @@ -0,0 +1,204 @@ +# Running your own TON proxy + +The aim of this document is to provide a gentle introduction into TON Sites, which are websites accessed through the TON Network. TON Sites may be used as a convenient entry point for other TON Services. In particular, HTML pages downloaded from TON Sites may contain links to `ton://...` URIs representing payments that can be performed by the user by clicking on the link, provided a TON Wallet is installed on the user's device. + +From the technical perspective, TON Sites are very much like standard websites, but they are accessed through the [TON Network](/learn/networking/overview) (which is an overlay network inside the Internet) instead of the Internet. More specifically, they have an [ADNL](/learn/networking/adnl) Address (instead of a more customary IPv4 or IPv6 address) and they accept HTTP queries via a [RLDP](/learn/networking/rldp) protocol (which is a higher-level RPC protocol built upon ADNL, the main protocol of TON Network) instead of the usual TCP/IP. All encryption is handled by ADNL, so there is no need to use HTTPS (i.e. TLS) in case the entry proxy is hosted locally on the user's device. + +In order to access existing sites and create new TON, Sites one needs special gateways between the "ordinary" internet and the TON Network. Essentially, TON Sites are accessed with the aid of a HTTP->RLDP proxy running locally on the client's machine and they are created by means of a reverse RLDP->HTTP proxy running on a remote web server. + +[Read more about TON Sites, WWW, and Proxy](https://blog.ton.org/ton-sites) + +## Running an entry proxy + +In order to access existing TON Sites, you need to run a RLDP-HTTP Proxy on your computer. + +1. Download **rldp-http-proxy** from [TON Auto Builds](https://github.com/ton-blockchain/ton/releases/latest). + + Or you can compile the **rldp-http-proxy** yourself by following these [instructions](/develop/howto/compile#rldp-http-proxy). + +2. [Download](/develop/howto/compile#download-global-config) TON global config. + +3. Run **rldp-http-proxy** + + ```bash + rldp-http-proxy/rldp-http-proxy -p 8080 -c 3333 -C global.config.json + ``` + +In the above example, `8080` is the TCP port that will be listened to at localhost for incoming HTTP queries, and `3333` is the UDP port that will be used for all outbound and inbound RLDP and ADNL activity (i.e. for connecting to TON Sites via the TON Network). `global.config.json` is the filename of TON global config. + +If you have done everything correctly, the entry proxy will not terminate, but it will continue running in the terminal. It can now be used for accessing TON Sites. When you don't need it anymore, you can terminate it by pressing `Ctrl-C`, or simply by closing the terminal window. + +Your entry proxy will be available by HTTP on `localhost` port `8080`. + +## Running an entry proxy on a remote computer + +1. Download **rldp-http-proxy** from [TON Auto Builds](https://github.com/ton-blockchain/ton/releases/latest). + + Or you can compile the **rldp-http-proxy** yourself by following these [instructions](/develop/howto/compile#rldp-http-proxy). + +2. [Download](/develop/howto/compile#download-global-config) TON global config. + +3. Download **generate-random-id** from [TON Auto Builds](https://github.com/ton-blockchain/ton/releases/latest). + + Or you can compile the **generate-random-id** yourself by following these [instructions](/develop/howto/compile#generate-random-id). + +4. Generate a persistent ANDL Address for your entry proxy + + ```bash + mkdir keyring + + utils/generate-random-id -m adnlid + ``` + + You will see something like + + ``` + 45061C1D4EC44A937D0318589E13C73D151D1CEF5D3C0E53AFBCF56A6C2FE2BD vcqmha5j3ceve35ammfrhqty46rkhi455otydstv66pk2tmf7rl25f3 + ``` + + This is your newly-generated persistent ADNL Address, in hexadecimal and user-friendly form. The corresponding private key is saved into file `45061...2DB` in the current directory. Move key into the keyring directory + + ```bash + mv 45061C1* keyring/ + ``` + +5. Run **rldp-http-proxy** + + ``` + rldp-http-proxy/rldp-http-proxy -p 8080 -a :3333 -C global.config.json -A + ``` + + where `` is your public IPv4 address and `` is the ADNL Address generated in the previous step. + + Example: + + ``` + rldp-http-proxy/rldp-http-proxy -p 8080 -a 777.777.777.777:3333 -C global.config.json -A vcqmha5j3ceve35ammfrhqty46rkhi455otydstv66pk2tmf7rl25f3 + ``` + + In the above example, `8080` is the TCP port that will be listened to at localhost for incoming HTTP queries, and `3333` is the UDP port that will be used for all outbound and inbound RLDP and ADNL activity (i.e. for connecting to TON Sites via the TON Network). `global.config.json` is the filename of TON global config. + +If you have done everything correctly, the Proxy will not terminate, but it will continue running in the terminal. It can be used now for accessing TON Sites. When you don't need it anymore, you can terminate it by pressing `Ctrl-C`, or simply by closing the terminal window. You can run this as a unix service to run permanently. + +Your entry proxy will be available by HTTP on `` port `8080`. + +## Accessing TON Sites + +Now suppose that you have a running instance of the RLDP-HTTP Proxy running on your computer and listening on `localhost:8080` for inbound TCP connections, as explained [above](#running-entry-proxy). + +A simple test that everything is working properly may be performed using programs such as `curl` or `wget`. For example, + +``` +curl -x 127.0.0.1:8080 http://just-for-test.ton +``` + +attempts to download the main page of (TON) Site `just-for-test.ton` using the proxy at `127.0.0.1:8080`. If the proxy is up and running, you'll see something like + +```html + + + +TON Site + + +

TON Proxy Works!

+ + + +``` + +You can also access TON Sites by means of their ADNL Addresses by using a fake domain `.adnl` + +```bash +curl -x 127.0.0.1:8080 http://utoljjye6y4ixazesjofidlkrhyiakiwrmes3m5hthlc6ie2h72gllt.adnl/ +``` + +currently fetches the same TON Web page. + +Alternatively, you can set up `localhost:8080` as a HTTP proxy in your browser. For example, if you use Firefox, visit [Setup] -> General -> Network Settings -> Settings -> Configure Proxy Access -> Manual Proxy configuration, and type "127.0.0.1" into the field "HTTP Proxy", and "8080" into the field "Port". + +Once you have set up `localhost:8080` as the HTTP proxy to be used in your browser, you can simply type the required URI, such as `http://just-for-test.ton` or `http://utoljjye6y4ixazesjofidlkrhyiakiwrmes3m5hthlc6ie2h72gllt.adnl/`, in the navigation bar of your browser, and interact with the TON Site in the same way as with the usual Web Sites. + +## Running TON Site + +:::tip tutorial found! +Hey! Don't want to start from beginner-friendly tutorial [How to run TON Site?](/develop/dapps/tutorials/how-to-run-ton-site) +::: + +Most people will need just to access existing TON Sites, not to create new ones. However, if you want to create one, you'll need to run RLDP-HTTP Proxy on your server, along with the usual web server software such as Apache or Nginx. + +We suppose that you know already how to set up an ordinary website, and that you have already configured one on your server, are accepting incoming HTTP connections on TCP port `:80`, and have defined the required TON Network domain name (e.g. `example.ton`) as the main domain name or an alias for your website in the configuration of your web server. + +1. Download **rldp-http-proxy** from [TON Auto Builds](https://github.com/ton-blockchain/ton/releases/latest). + + Or you can compile the **rldp-http-proxy** yourself by this [instruction](/develop/howto/compile#rldp-http-proxy). + +2. [Download](/develop/howto/compile#download-global-config) TON global config. + +3. Download **generate-random-id** from [TON Auto Builds](https://github.com/ton-blockchain/ton/releases/latest). + + Or you can compile the **generate-random-id** yourself by following these [instructions](/develop/howto/compile#generate-random-id). + +4. Generate a persistent ANDL Address for your server + + ```bash + mkdir keyring + + utils/generate-random-id -m adnlid + ``` + + You will see something like + + ```bash + 45061C1D4EC44A937D0318589E13C73D151D1CEF5D3C0E53AFBCF56A6C2FE2BD vcqmha5j3ceve35ammfrhqty46rkhi455otydstv66pk2tmf7rl25f3 + ``` + + This is your newly-generated persistent ADNL Address, in hexadecimal and user-friendly form. The corresponding private key is saved into file `45061...2DB` in the current directory. Move it into the keyring directory + + ```bash + mv 45061C1* keyring/ + ``` + +5. Make sure your webserver accepts HTTP requests with `.ton` and `.adnl` domains. + + For example if you use nginx with config `server_name example.com;`, you need to change it to `server_name _;` or `server_name example.com example.ton vcqmha5j3ceve35ammfrhqty46rkhi455otydstv66pk2tmf7rl25f3.adnl;`. + +6. Run the proxy in reverse mode + + ```bash + rldp-http-proxy/rldp-http-proxy -a :3333 -L '*' -C global.config.json -A -d -l + ``` + + where `` is your server public IPv4 address and `` is the ADNL Address generated in the previous step. + +If you want your TON Site to run permanently, you'll have to use options `-d` and `-l `. + +Example: + +```bash +rldp-http-proxy/rldp-http-proxy -a 777.777.777.777:3333 -L '*' -C global.config.json -A vcqmha5j3ceve35ammfrhqty46rkhi455otydstv66pk2tmf7rl25f3 -d -l tonsite.log +``` + +If all works properly, the RLDP-HTTP proxy will accept incoming HTTP queries from the TON Network via RLDP/ADNL running on UDP port 3333 (of course, you can use any other UDP port if you want to) of IPv4 address `` (in particular, if you are using a firewall, don't forget to allow `rldp-http-proxy` to receive and send UDP packets from this port), and it will forward these HTTP queries addressed to all hosts (if you want to forward only specific hosts, change `-L '*'` to `-L `) to TCP port `80` at `127.0.0.1` (i.e. to your ordinary Web server). + +You can visit TON Site `http://.adnl` (`http://vcqmha5j3ceve35ammfrhqty46rkhi455otydstv66pk2tmf7rl25f3.adnl` in this example) from a browser running on a client machine as explained in the "Accessing TON Sites" Section and check whether your TON Site is actually available to the public. + +If you want to, you can [register](/participate/web3/site-management) a TON DNS domain, such as 'example.ton', and create a `site` record for this domain pointing to the persistent ADNL Address of your TON Site. Then the RLDP-HTTP proxies running in client mode would resolve http://example.ton as pointing to your ADNL Address and will access your TON Site. + +You can also run a reverse proxy on a separate server and set your webserver as a remote address. In this case use `-R '*'@:` instead of `-L '*'`. + +Example: + +```bash +rldp-http-proxy/rldp-http-proxy -a 777.777.777.777:3333 -R '*'@333.333.333.333:80 -C global.config.json -A vcqmha5j3ceve35ammfrhqty46rkhi455otydstv66pk2tmf7rl25f3 -d -l tonsite.log +``` + +In this case your regular webserver should be available on http://333.333.333.333:80 (this IP will not be exposed to the outside). + +### Recommendations + +Since anonymity will only be available in TON Proxy 2.0, if you do not want to disclose the IP address of your web server, you can do it in two ways: + +- Run a reverse proxy on a separate server with `-R` flag as described above. + +- Make a duplicate server with copy of your website and run reverse proxy locally. From e6a36ef1d99c2fd2ef5fdef6abcf7a8ce795b203 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:55 +0800 Subject: [PATCH 204/219] New translations tvm-instructions.mdx (Chinese Simplified) --- .../learn/archive/tvm-instructions.mdx | 1077 +++++++++++++++++ 1 file changed, 1077 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-pages/learn/archive/tvm-instructions.mdx diff --git a/i18n/zh/docusaurus-plugin-content-pages/learn/archive/tvm-instructions.mdx b/i18n/zh/docusaurus-plugin-content-pages/learn/archive/tvm-instructions.mdx new file mode 100644 index 0000000000..94895cc7c8 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-pages/learn/archive/tvm-instructions.mdx @@ -0,0 +1,1077 @@ +--- +hide_table_of_contents: true +wrapperClassName: bootstrap-wrapper +--- + +import { SearchField } from '@site/src/components/SearchField'; +import { opcodes } from '@site/src/data/opcodes'; + +# TVM Instructions + +:::caution advanced level +This information is **very low-level** and could be hard to understand for newcomers. +::: + +## Introduction + +This document provides a list of TVM instructions along with their opcodes and mnemonics. + +:::info +[**TVM.pdf**](https://ton.org/tvm.pdf) original concept TON Virtual Machine (may include outdated information). +::: + +Fift is a stack-based programming language designed to manage TON smart contracts. The Fift assembler is a Fift library that converts mnemonics of TVM instructions into their binary representation. + +A description of Fift, including an introduction to the Fift assembler, can be found [here](https://github.com/Piterden/TON-docs/blob/master/Fift.%20A%20Brief%20Introduction.md). + +This document specifies the corresponding mnemonic for each instruction. + +Note the following: + +1. Fift is a stack-based language, therefore all the arguments of any instruction are written before it (e.g. [`5 PUSHINT`](#instr-pushint-4), [`s0 s4 XCHG`](#instr-xchg-ij)). +2. Stack registers are denoted by `s0, s1, ..., s15`. Other stack registers (up to 255) are denoted by `i s()` (e.g. `100 s()`). +3. Control registers are denoted by `c0, c1, ..., c15`. + +### Gas prices + +The gas price of each instruction is specified in this document. The basic gas price of an instruction is `10 + b`, where `b` is the instruction length in bits. Some operations have additional fees: + +1. _Parsing cells_: Transforming a cell into a slice costs **100 gas units** if the cell is loading for the first time and **25** for subsequent loads during the same transaction. For such instructions, two gas prices are specified (e.g. [`CTOS`](#instr-ctos): `118/43`). +2. _Cell creation_: **500 gas units**. +3. _Throwing exceptions_: **50 gas units**. In this document the exception fee is only specified for an instruction if its primary purpose is to throw (e.g. [`THROWIF`](#instr-throwif-short), [`FITS`](#instr-fits)). If the instruction only throws in some cases, two gas prices are specified (e.g. [`FITS`](#instr-fits): `26/76`). +4. _Tuple creation_: **1 gas unit** for every tuple element. +5. _Implicit jumps_: **10 gas units** for an implicit jump, **5 gas units** for an implicit back jump. This fee is not a part of any instruction. +6. _Moving stack elements between continuations_: **1 gas unit** per element, however moving the first 32 elements is free. + +### Quick search + +:::info + +A full machine-readable list of TVM instructions is available [here](https://github.com/ton-community/ton-docs/blob/main/docs/learn/tvm-instructions/instructions.csv). +::: + +Feel free to use the search field below to find a specific instruction: + + + +## 2 Stack manipulation primitives + +Here `0 <= i,j,k <= 15` if not stated otherwise. + +### 2.1 Basic stack manipulation primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :-------------------------------------------------------------------------------------------------------------- | :----------- | +| **`00`** | `NOP` | _`-`_ | Does nothing. | `18` | +| **`01`** | `SWAP` | _`x y - y x`_ | Same as [`s1 XCHG0`](#instr-xchg-0i). | `18` | +| **`0i`** | `s[i] XCHG0` | | Interchanges `s0` with `s[i]`, `1 <= i <= 15`. | `18` | +| **`10ij`** | `s[i] s[j] XCHG` | | Interchanges `s[i]` with `s[j]`, `1 <= i < j <= 15`. | `26` | +| **`11ii`** | `s0 [ii] s() XCHG` | | Interchanges `s0` with `s[ii]`, `0 <= ii <= 255`. | `26` | +| **`1i`** | `s1 s[i] XCHG` | | Interchanges `s1` with `s[i]`, `2 <= i <= 15`. | `18` | +| **`2i`** | `s[i] PUSH` | | Pushes a copy of the old `s[i]` into the stack. | `18` | +| **`20`** | `DUP` | _`x - x x`_ | Same as [`s0 PUSH`](#instr-push). | `18` | +| **`21`** | `OVER` | _`x y - x y x`_ | Same as [`s1 PUSH`](#instr-push). | `18` | +| **`3i`** | `s[i] POP` | | Pops the old `s0` value into the old `s[i]`. Equivalent to [`s[i] XCHG0`](#instr-xchg-0i) [`DROP`](#instr-drop) | `18` | +| **`30`** | `DROP` | _`x -`_ | Same as [`s0 POP`](#instr-pop), discards the top-of-stack value. | `18` | +| **`31`** | `NIP` | _`x y - y`_ | Same as [`s1 POP`](#instr-pop). | `18` | + +### 2.2 Complex stack manipulation primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :---------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`4ijk`** | `s[i] s[j] s[k] XCHG3` | | Equivalent to [`s2 s[i] XCHG`](#instr-xchg-ij) [`s1 s[j] XCHG`](#instr-xchg-ij) [`s[k] XCHG0`](#instr-xchg-0i). | `26` | +| **`50ij`** | `s[i] s[j] XCHG2` | | Equivalent to [`s1 s[i] XCHG`](#instr-xchg-ij) [`s[j] XCHG0`](#instr-xchg-0i). | `26` | +| **`51ij`** | `s[i] s[j] XCPU` | | Equivalent to [`s[i] XCHG0`](#instr-xchg-0i) [`s[j] PUSH`](#instr-push). | `26` | +| **`52ij`** | `s[i] s[j-1] PUXC` | | Equivalent to [`s[i] PUSH`](#instr-push) [`SWAP`](#instr-swap) [`s[j] XCHG0`](#instr-xchg-0i). | `26` | +| **`53ij`** | `s[i] s[j] PUSH2` | | Equivalent to [`s[i] PUSH`](#instr-push) [`s[j+1] PUSH`](#instr-push). | `26` | +| **`540ijk`** | `s[i] s[j] s[k] XCHG3_l` | | Long form of [`XCHG3`](#instr-xchg3). | `34` | +| 541ijk | `s[i] s[j] s[k] XC2PU` | | Equivalent to [`s[i] s[j] XCHG2`](#instr-xchg2) [`s[k] PUSH`](#instr-push). | `34` | +| **`542ijk`** | `s[i] s[j] s[k-1] XCPUXC` | | Equivalent to [`s1 s[i] XCHG`](#instr-xchg-ij) [`s[j] s[k-1] PUXC`](#instr-puxc). | `34` | +| **`543ijk`** | `s[i] s[j] s[k] XCPU2` | | Equivalent to [`s[i] XCHG0`](#instr-xchg-0i) [`s[j] s[k] PUSH2`](#instr-push2). | `34` | +| **`544ijk`** | `s[i] s[j-1] s[k-1] PUXC2` | | Equivalent to [`s[i] PUSH`](#instr-push) [`s2 XCHG0`](#instr-xchg-0i) [`s[j] s[k] XCHG2`](#instr-xchg2). | `34` | +| **`545ijk`** | `s[i] s[j-1] s[k-1] PUXCPU` | | Equivalent to [`s[i] s[j-1] PUXC`](#instr-puxc) [`s[k] PUSH`](#instr-push). | `34` | +| **`546ijk`** | `s[i] s[j-1] s[k-2] PU2XC` | | Equivalent to [`s[i] PUSH`](#instr-push) [`SWAP`](#instr-swap) [`s[j] s[k-1] PUXC`](#instr-puxc). | `34` | +| **`547ijk`** | `s[i] s[j] s[k] PUSH3` | | Equivalent to [`s[i] PUSH`](#instr-push) [`s[j+1] s[k+1] PUSH2`](#instr-push2). | `34` | +| **`55ij`** | `[i+1] [j+1] BLKSWAP` | | 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`](#instr-reverse) [`[j+1] 0 REVERSE`](#instr-reverse) [`[i+j+2] 0 REVERSE`](#instr-reverse). | `26` | +| **`5513`** | `ROT2`
`2ROT` | _`a b c d e f - c d e f a b`_ | Rotates the three topmost pairs of stack entries. | `26` | +| **`550i`** | `[i+1] ROLL` | | Rotates the top `i+1` stack entries.
Equivalent to [`1 [i+1] BLKSWAP`](#instr-blkswap). | `26` | +| **`55i0`** | `[i+1] -ROLL`
`[i+1] ROLLREV` | | Rotates the top `i+1` stack entries in the other direction.
Equivalent to [`[i+1] 1 BLKSWAP`](#instr-blkswap). | `26` | +| **`56ii`** | `[ii] s() PUSH` | | Pushes a copy of the old `s[ii]` into the stack.
`0 <= ii <= 255` | `26` | +| **`57ii`** | `[ii] s() POP` | | Pops the old `s0` value into the old `s[ii]`.
`0 <= ii <= 255` | `26` | +| **`58`** | `ROT` | _`a b c - b c a`_ | Equivalent to [`1 2 BLKSWAP`](#instr-blkswap) or to [`s2 s1 XCHG2`](#instr-xchg2). | `18` | +| **`59`** | `ROTREV`
`-ROT` | _`a b c - c a b`_ | Equivalent to [`2 1 BLKSWAP`](#instr-blkswap) or to [`s2 s2 XCHG2`](#instr-xchg2). | `18` | +| **`5A`** | `SWAP2`
`2SWAP` | _`a b c d - c d a b`_ | Equivalent to [`2 2 BLKSWAP`](#instr-blkswap) or to [`s3 s2 XCHG2`](#instr-xchg2). | `18` | +| **`5B`** | `DROP2`
`2DROP` | _`a b - `_ | Equivalent to [`DROP`](#instr-drop) [`DROP`](#instr-drop). | `18` | +| **`5C`** | `DUP2`
`2DUP` | _`a b - a b a b`_ | Equivalent to [`s1 s0 PUSH2`](#instr-push2). | `18` | +| **`5D`** | `OVER2`
`2OVER` | _`a b c d - a b c d a b`_ | Equivalent to [`s3 s2 PUSH2`](#instr-push2). | `18` | +| **`5Eij`** | `[i+2] [j] REVERSE` | | Reverses the order of `s[j+i+1] … s[j]`. | `26` | +| **`5F0i`** | `[i] BLKDROP` | | Equivalent to [`DROP`](#instr-drop) performed `i` times. | `26` | +| **`5Fij`** | `[i] [j] BLKPUSH` | | Equivalent to `PUSH s(j)` performed `i` times.
`1 <= i <= 15`, `0 <= j <= 15`. | `26` | +| **`60`** | `PICK`
`PUSHX` | | Pops integer `i` from the stack, then performs [`s[i] PUSH`](#instr-push). | `18` | +| **`61`** | `ROLLX` | | Pops integer `i` from the stack, then performs [`1 [i] BLKSWAP`](#instr-blkswap). | `18` | +| **`62`** | `-ROLLX`
`ROLLREVX` | | Pops integer `i` from the stack, then performs [`[i] 1 BLKSWAP`](#instr-blkswap). | `18` | +| **`63`** | `BLKSWX` | | Pops integers `i`,`j` from the stack, then performs [`[i] [j] BLKSWAP`](#instr-blkswap). | `18` | +| **`64`** | `REVX` | | Pops integers `i`,`j` from the stack, then performs [`[i] [j] REVERSE`](#instr-reverse). | `18` | +| **`65`** | `DROPX` | | Pops integer `i` from the stack, then performs [`[i] BLKDROP`](#instr-blkdrop). | `18` | +| **`66`** | `TUCK` | _`a b - b a b`_ | Equivalent to [`SWAP`](#instr-swap) [`OVER`](#instr-over) or to [`s1 s1 XCPU`](#instr-xcpu). | `18` | +| **`67`** | `XCHGX` | | Pops integer `i` from the stack, then performs [`s[i] XCHG`](#instr-xchg-ij). | `18` | +| **`68`** | `DEPTH` | _`- depth`_ | Pushes the current depth of the stack. | `18` | +| **`69`** | `CHKDEPTH` | _`i -`_ | Pops integer `i` from the stack, then checks whether there are at least `i` elements, generating a stack underflow exception otherwise. | `18/58` | +| **`6A`** | `ONLYTOPX` | | Pops integer `i` from the stack, then removes all but the top `i` elements. | `18` | +| **`6B`** | `ONLYX` | | Pops integer `i` from the stack, then leaves only the bottom `i` elements. Approximately equivalent to [`DEPTH`](#instr-depth) [`SWAP`](#instr-swap) [`SUB`](#instr-sub) [`DROPX`](#instr-dropx). | `18` | +| **`6Cij`** | `[i] [j] BLKDROP2` | | Drops `i` stack elements under the top `j` elements.
`1 <= i <= 15`, `0 <= j <= 15`
Equivalent to [`[i+j] 0 REVERSE`](#instr-reverse) [`[i] BLKDROP`](#instr-blkdrop) [`[j] 0 REVERSE`](#instr-reverse). | `26` | + +## 3 Tuple, List, and Null primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :----------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`6D`** | `NULL`
`PUSHNULL` | _` - null`_ | Pushes the only value of type _Null_. | `18` | +| **`6E`** | `ISNULL` | _`x - ?`_ | Checks whether `x` is a _Null_, and returns `-1` or `0` accordingly. | `18` | +| **`6F0n`** | `[n] TUPLE` | _`x_1 ... x_n - t`_ | Creates a new _Tuple_ `t=(x_1, … ,x_n)` containing `n` values `x_1`,..., `x_n`.
`0 <= n <= 15` | `26+n` | +| **`6F00`** | `NIL` | _`- t`_ | Pushes the only _Tuple_ `t=()` of length zero. | `26` | +| **`6F01`** | `SINGLE` | _`x - t`_ | Creates a singleton `t:=(x)`, i.e., a _Tuple_ of length one. | `27` | +| **`6F02`** | `PAIR`
`CONS` | _`x y - t`_ | Creates pair `t:=(x,y)`. | `28` | +| **`6F03`** | `TRIPLE` | _`x y z - t`_ | Creates triple `t:=(x,y,z)`. | `29` | +| **`6F1k`** | `[k] INDEX` | _`t - x`_ | Returns the `k`-th element of a _Tuple_ `t`.
`0 <= k <= 15`. | `26` | +| **`6F10`** | `FIRST`
`CAR` | _`t - x`_ | Returns the first element of a _Tuple_. | `26` | +| **`6F11`** | `SECOND`
`CDR` | _`t - y`_ | Returns the second element of a _Tuple_. | `26` | +| **`6F12`** | `THIRD` | _`t - z`_ | Returns the third element of a _Tuple_. | `26` | +| **`6F2n`** | `[n] UNTUPLE` | _`t - x_1 ... x_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. | `26+n` | +| **`6F21`** | `UNSINGLE` | _`t - x`_ | Unpacks a singleton `t=(x)`. | `27` | +| **`6F22`** | `UNPAIR`
`UNCONS` | _`t - x y`_ | Unpacks a pair `t=(x,y)`. | `28` | +| **`6F23`** | `UNTRIPLE` | _`t - x y z`_ | Unpacks a triple `t=(x,y,z)`. | `29` | +| **`6F3k`** | `[k] UNPACKFIRST` | _`t - x_1 ... x_k`_ | Unpacks first `0 <= k <= 15` elements of a _Tuple_ `t`.
If `\|t\|`0 <= k <= 15`
If `k >= \|t\|`, throws a range check exception. | `26+\|t\|` | +| **`6F50`** | `SETFIRST` | _`t x - t'`_ | Sets the first component of _Tuple_ `t` to `x` and returns the resulting _Tuple_ `t'`. | `26+\|t\|` | +| **`6F51`** | `SETSECOND` | _`t x - t'`_ | Sets the second component of _Tuple_ `t` to `x` and returns the resulting _Tuple_ `t'`. | `26+\|t\|` | +| **`6F52`** | `SETTHIRD` | _`t x - t'`_ | Sets the third component of _Tuple_ `t` to `x` and returns the resulting _Tuple_ `t'`. | `26+\|t\|` | +| **`6F6k`** | `[k] INDEXQ` | _`t - x`_ | 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`. | `26` | +| **`6F60`** | `FIRSTQ`
`CARQ` | _`t - x`_ | Returns the first element of a _Tuple_. | `26` | +| **`6F61`** | `SECONDQ`
`CDRQ` | _`t - y`_ | Returns the second element of a _Tuple_. | `26` | +| **`6F62`** | `THIRDQ` | _`t - z`_ | Returns the third element of a _Tuple_. | `26` | +| **`6F7k`** | `[k] SETINDEXQ` | _`t x - 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). | `26+\|t’\|` | +| **`6F70`** | `SETFIRSTQ` | _`t x - t'`_ | Sets the first component of _Tuple_ `t` to `x` and returns the resulting _Tuple_ `t'`. | `26+\|t’\|` | +| **`6F71`** | `SETSECONDQ` | _`t x - t'`_ | Sets the second component of _Tuple_ `t` to `x` and returns the resulting _Tuple_ `t'`. | `26+\|t’\|` | +| **`6F72`** | `SETTHIRDQ` | _`t x - t'`_ | Sets the third component of _Tuple_ `t` to `x` and returns the resulting _Tuple_ `t'`. | `26+\|t’\|` | +| **`6F80`** | `TUPLEVAR` | _`x_1 ... x_n n - t`_ | Creates a new _Tuple_ `t` of length `n` similarly to [`TUPLE`](#instr-tuple), but with `0 <= n <= 255` taken from the stack. | `26+n` | +| **`6F81`** | `INDEXVAR` | _`t k - x`_ | Similar to [`k INDEX`](#instr-index), but with `0 <= k <= 254` taken from the stack. | `26` | +| **`6F82`** | `UNTUPLEVAR` | _`t n - x_1 ... x_n`_ | Similar to [`n UNTUPLE`](#instr-untuple), but with `0 <= n <= 255` taken from the stack. | `26+n` | +| **`6F83`** | `UNPACKFIRSTVAR` | _`t n - x_1 ... x_n`_ | Similar to [`n UNPACKFIRST`](#instr-unpackfirst), but with `0 <= n <= 255` taken from the stack. | `26+n` | +| **`6F84`** | `EXPLODEVAR` | _`t n - x_1 ... x_m m`_ | Similar to [`n EXPLODE`](#instr-explode), but with `0 <= n <= 255` taken from the stack. | `26+m` | +| **`6F85`** | `SETINDEXVAR` | _`t x k - t'`_ | Similar to [`k SETINDEX`](#instr-setindex), but with `0 <= k <= 254` taken from the stack. | `26+\|t’\|` | +| **`6F86`** | `INDEXVARQ` | _`t k - x`_ | Similar to [`n INDEXQ`](#instr-indexq), but with `0 <= k <= 254` taken from the stack. | `26` | +| **`6F87`** | `SETINDEXVARQ` | _`t x k - t'`_ | Similar to [`k SETINDEXQ`](#instr-setindexq), but with `0 <= k <= 254` taken from the stack. | `26+\|t’\|` | +| **`6F88`** | `TLEN` | _`t - n`_ | Returns the length of a _Tuple_. | `26` | +| **`6F89`** | `QTLEN` | _`t - n or -1`_ | Similar to [`TLEN`](#instr-tlen), but returns `-1` if `t` is not a _Tuple_. | `26` | +| **`6F8A`** | `ISTUPLE` | _`t - ?`_ | Returns `-1` or `0` depending on whether `t` is a _Tuple_. | `26` | +| **`6F8B`** | `LAST` | _`t - x`_ | Returns the last element of a non-empty _Tuple_ `t`. | `26` | +| **`6F8C`** | `TPUSH`
`COMMA` | _`t x - 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. | `26+\|t’\|` | +| **`6F8D`** | `TPOP` | _`t - t' x`_ | 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`. | `26+\|t’\|` | +| **`6FA0`** | `NULLSWAPIF` | _`x - x or null x`_ | Pushes a _Null_ under the topmost _Integer_ `x`, but only if `x!=0`. | `26` | +| **`6FA1`** | `NULLSWAPIFNOT` | _`x - x or null x`_ | 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`](#instr-plduxq). | `26` | +| **`6FA2`** | `NULLROTRIF` | _`x y - x y or null x y`_ | Pushes a _Null_ under the second stack entry from the top, but only if the topmost _Integer_ `y` is non-zero. | `26` | +| **`6FA3`** | `NULLROTRIFNOT` | _`x y - x y or null x y`_ | 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`](#instr-lduxq). | `26` | +| **`6FA4`** | `NULLSWAPIF2` | _`x - x or null null x`_ | Pushes two nulls under the topmost _Integer_ `x`, but only if `x!=0`.
Equivalent to [`NULLSWAPIF`](#instr-nullswapif) [`NULLSWAPIF`](#instr-nullswapif). | `26` | +| **`6FA5`** | `NULLSWAPIFNOT2` | _`x - x or null null x`_ | Pushes two nulls under the topmost _Integer_ `x`, but only if `x=0`.
Equivalent to [`NULLSWAPIFNOT`](#instr-nullswapifnot) [`NULLSWAPIFNOT`](#instr-nullswapifnot). | `26` | +| **`6FA6`** | `NULLROTRIF2` | _`x y - x y or null null x y`_ | Pushes two nulls under the second stack entry from the top, but only if the topmost _Integer_ `y` is non-zero.
Equivalent to [`NULLROTRIF`](#instr-nullrotrif) [`NULLROTRIF`](#instr-nullrotrif). | `26` | +| **`6FA7`** | `NULLROTRIFNOT2` | _`x y - x y or null null x y`_ | Pushes two nulls under the second stack entry from the top, but only if the topmost _Integer_ `y` is zero.
Equivalent to [`NULLROTRIFNOT`](#instr-nullrotrifnot) [`NULLROTRIFNOT`](#instr-nullrotrifnot). | `26` | +| **`6FBij`** | `[i] [j] INDEX2` | _`t - x`_ | Recovers `x=(t_{i+1})_{j+1}` for `0 <= i,j <= 3`.
Equivalent to [`[i] INDEX`](#instr-index) [`[j] INDEX`](#instr-index). | `26` | +| **`6FB4`** | `CADR` | _`t - x`_ | Recovers `x=(t_2)_1`. | `26` | +| **`6FB5`** | `CDDR` | _`t - x`_ | Recovers `x=(t_2)_2`. | `26` | +| **`6FE_ijk`** | `[i] [j] [k] INDEX3` | _`t - x`_ | Recovers `x=t_{i+1}_{j+1}_{k+1}`.
`0 <= i,j,k <= 3`
Equivalent to [`[i] [j] INDEX2`](#instr-index2) [`[k] INDEX`](#instr-index). | `26` | +| **`6FD4`** | `CADDR` | _`t - x`_ | Recovers `x=t_2_2_1`. | `26` | +| **`6FD5`** | `CDDDR` | _`t - x`_ | Recovers `x=t_2_2_2`. | `26` | + +## 4 Constant or literal primitives + +### 4.1 Integer and boolean constants + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`7i`** | `[x] PUSHINT`
`[x] INT` | _`- x`_ | Pushes integer `x` into the stack. `-5 <= x <= 10`.
Here `i` equals four lower-order bits of `x` (`i=x mod 16`). | `18` | +| **`70`** | `ZERO`
`FALSE` | _`- 0`_ | | `18` | +| **`71`** | `ONE` | _`- 1`_ | | `18` | +| **`72`** | `TWO` | _`- 2`_ | | `18` | +| **`7A`** | `TEN` | _`- 10`_ | | `18` | +| **`7F`** | `TRUE` | _`- -1`_ | | `18` | +| **`80xx`** | `[xx] PUSHINT`
`[xx] INT` | _`- xx`_ | Pushes integer `xx`. `-128 <= xx <= 127`. | `26` | +| **`81xxxx`** | `[xxxx] PUSHINT`
`[xxxx] INT` | _`- xxxx`_ | Pushes integer `xxxx`. `-2^15 <= xx < 2^15`. | `34` | +| **`82lxxx`** | `[xxx] PUSHINT`
`[xxx] INT` | _`- xxx`_ | 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. | `23` | +| **`83xx`** | `[xx+1] PUSHPOW2` | _`- 2^(xx+1)`_ | (Quietly) pushes `2^(xx+1)` for `0 <= xx <= 255`.
`2^256` is a `NaN`. | `26` | +| **`83FF`** | `PUSHNAN` | _`- NaN`_ | Pushes a `NaN`. | `26` | +| **`84xx`** | `[xx+1] PUSHPOW2DEC` | _`- 2^(xx+1)-1`_ | Pushes `2^(xx+1)-1` for `0 <= xx <= 255`. | `26` | +| **`85xx`** | `[xx+1] PUSHNEGPOW2` | _`- -2^(xx+1)`_ | Pushes `-2^(xx+1)` for `0 <= xx <= 255`. | `26` | + +### 4.2 Constant slices, continuations, cells, and references + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------------------------------- | :-------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`88`** | `[ref] PUSHREF` | _`- c`_ | 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). | `18` | +| **`89`** | `[ref] PUSHREFSLICE` | _`- s`_ | Similar to [`PUSHREF`](#instr-pushref), but converts the cell into a _Slice_. | `118/43` | +| **`8A`** | `[ref] PUSHREFCONT` | _`- cont`_ | Similar to [`PUSHREFSLICE`](#instr-pushrefslice), but makes a simple ordinary _Continuation_ out of the cell. | `118/43` | +| **`8Bxsss`** | `[slice] PUSHSLICE`
`[slice] SLICE` | _`- s`_ | 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. | `22` | +| **`8Crxxssss`** | `[slice] PUSHSLICE`
`[slice] SLICE` | _`- s`_ | 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. | `25` | +| **`8Drxxsssss`** | `[slice] PUSHSLICE`
`[slice] SLICE` | _`- s`_ | 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. | `28` | +| | `x{} PUSHSLICE`
`x{ABCD1234} PUSHSLICE`
`b{01101} PUSHSLICE` | _`- s`_ | Examples of [`PUSHSLICE`](#instr-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`](#instr-pushslice) with [`PUSHREFSLICE`](#instr-pushrefslice) in certain situations (e.g. if there’s not enough space in the current continuation). | | +| | ` PUSHREF`
` PUSHREFSLICE` | _`- c/s`_ | Examples of [`PUSHREF`](#instr-pushref) and [`PUSHREFSLICE`](#instr-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). | | +| **`8F_rxxcccc`** | `[builder] PUSHCONT`
`[builder] CONT` | _`- c`_ | 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`. | `26` | +| **`9xccc`** | `[builder] PUSHCONT`
`[builder] CONT` | _`- c`_ | Pushes a continuation made from `builder`.
_Details:_ Pushes an `x`-byte continuation for `0 <= x <= 15`. | `18` | +| | `<{ code }> PUSHCONT`
`<{ code }> CONT`
`CONT:<{ code }>` | _`- c`_ | Pushes a continuation with code `code`.
Note that the assembler can replace [`PUSHCONT`](#instr-pushcont) with [`PUSHREFCONT`](#instr-pushrefcont) in certain situations (e.g. if there’s not enough space in the current continuation). | | + +## 5 Arithmetic primitives + +### 5.1 Addition, subtraction, multiplication + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------------------------------------ | :-------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`A0`** | `ADD` | _`x y - x+y`_ | | `18` | +| **`A1`** | `SUB` | _`x y - x-y`_ | | `18` | +| **`A2`** | `SUBR` | _`x y - y-x`_ | Equivalent to [`SWAP`](#instr-swap) [`SUB`](#instr-sub). | `18` | +| **`A3`** | `NEGATE` | _`x - -x`_ | Equivalent to [`-1 MULCONST`](#instr-mulconst) or to [`ZERO SUBR`](#instr-subr).
Notice that it triggers an integer overflow exception if `x=-2^256`. | `18` | +| **`A4`** | `INC` | _`x - x+1`_ | Equivalent to [`1 ADDCONST`](#instr-addconst). | `18` | +| **`A5`** | `DEC` | _`x - x-1`_ | Equivalent to [`-1 ADDCONST`](#instr-addconst). | `18` | +| **`A6cc`** | `[cc] ADDCONST`
`[cc] ADDINT`
`[-cc] SUBCONST`
`[-cc] SUBINT` | _`x - x+cc`_ | `-128 <= cc <= 127`. | `26` | +| **`A7cc`** | `[cc] MULCONST`
`[cc] MULINT` | _`x - x*cc`_ | `-128 <= cc <= 127`. | `26` | +| **`A8`** | `MUL` | _`x y - x*y`_ | | `18` | + +### 5.2 Division + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :---------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`A9mscdf`** | | | 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`](#instr-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. | `26` | +| **`A904`** | `DIV` | _`x y - q`_ | `q=floor(x/y)`, `r=x-y*q` | `26` | +| **`A905`** | `DIVR` | _`x y - q’`_ | `q’=round(x/y)`, `r’=x-y*q’` | `26` | +| **`A906`** | `DIVC` | _`x y - q''`_ | `q’’=ceil(x/y)`, `r’’=x-y*q’’` | `26` | +| **`A908`** | `MOD` | _`x y - r`_ | | `26` | +| **`A90C`** | `DIVMOD` | _`x y - q r`_ | | `26` | +| **`A90D`** | `DIVMODR` | _`x y - q' r'`_ | | `26` | +| **`A90E`** | `DIVMODC` | _`x y - q'' r''`_ | | `26` | +| **`A925`** | `RSHIFTR` | _`x y - round(x/2^y)`_ | | `26` | +| **`A926`** | `RSHIFTC` | _`x y - ceil(x/2^y)`_ | | `34` | +| **`A935tt`** | `[tt+1] RSHIFTR#` | _`x y - round(x/2^(tt+1))`_ | | `34` | +| **`A936tt`** | `[tt+1] RSHIFTC#` | _`x y - ceil(x/2^(tt+1))`_ | | `34` | +| **`A938tt`** | `[tt+1] MODPOW2#` | _`x - x mod 2^(tt+1)`_ | | `34` | +| **`A98`** | `MULDIV` | _`x y z - q`_ | `q=floor(x*y/z)` | `26` | +| **`A985`** | `MULDIVR` | _`x y z - q'`_ | `q'=round(x*y/z)` | `26` | +| **`A98C`** | `MULDIVMOD` | _`x y z - q r`_ | `q=floor(x*y/z)`, `r=x*y-z*q` | `26` | +| **`A9A4`** | `MULRSHIFT` | _`x y z - floor(x*y/2^z)`_ | `0 <= z <= 256` | `26` | +| **`A9A5`** | `MULRSHIFTR` | _`x y z - round(x*y/2^z)`_ | `0 <= z <= 256` | `26` | +| **`A9A6`** | `MULRSHIFTC` | _`x y z - ceil(x*y/2^z)`_ | `0 <= z <= 256` | `34` | +| **`A9B4tt`** | `[tt+1] MULRSHIFT#` | _`x y - floor(x*y/2^(tt+1))`_ | | `34` | +| **`A9B5tt`** | `[tt+1] MULRSHIFTR#` | _`x y - round(x*y/2^(tt+1))`_ | | `34` | +| **`A9B6tt`** | `[tt+1] MULRSHIFTC#` | _`x y - ceil(x*y/2^(tt+1))`_ | | `26` | +| **`A9C4`** | `LSHIFTDIV` | _`x y z - floor(2^z*x/y)`_ | `0 <= z <= 256` | `26` | +| **`A9C5`** | `LSHIFTDIVR` | _`x y z - round(2^z*x/y)`_ | `0 <= z <= 256` | `26` | +| **`A9C6`** | `LSHIFTDIVC` | _`x y z - ceil(2^z*x/y)`_ | `0 <= z <= 256` | `34` | +| **`A9D4tt`** | `[tt+1] LSHIFT#DIV` | _`x y - floor(2^(tt+1)*x/y)`_ | | `34` | +| **`A9D5tt`** | `[tt+1] LSHIFT#DIVR` | _`x y - round(2^(tt+1)*x/y)`_ | | `34` | +| **`A9D6tt`** | `[tt+1] LSHIFT#DIVC` | _`x y - ceil(2^(tt+1)*x/y)`_ | | `26` | + +### 5.3 Shifts, logical operations + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`AAcc`** | `[cc+1] LSHIFT#` | _`x - x*2^(cc+1)`_ | `0 <= cc <= 255` | `26` | +| **`ABcc`** | `[cc+1] RSHIFT#` | _`x - floor(x/2^(cc+1))`_ | `0 <= cc <= 255` | `18` | +| **`AC`** | `LSHIFT` | _`x y - x*2^y`_ | `0 <= y <= 1023` | `18` | +| **`AD`** | `RSHIFT` | _`x y - floor(x/2^y)`_ | `0 <= y <= 1023` | `18` | +| **`AE`** | `POW2` | _`y - 2^y`_ | `0 <= y <= 1023`
Equivalent to [`ONE`](#instr-one) [`SWAP`](#instr-swap) [`LSHIFT`](#instr-lshift-var). | `18` | +| **`B0`** | `AND` | _`x y - x&y`_ | Bitwise and of two signed integers `x` and `y`, sign-extended to infinity. | `18` | +| **`B1`** | `OR` | _`x y - x\|y`_ | Bitwise or of two integers. | `18` | +| **`B2`** | `XOR` | _`x y - x xor y`_ | Bitwise xor of two integers. | `18` | +| **`B3`** | `NOT` | _`x - ~x`_ | Bitwise not of an integer. | `26` | +| **`B4cc`** | `[cc+1] FITS` | _`x - x`_ | 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). | `26/76` | +| **`B400`** | `CHKBOOL` | _`x - x`_ | Checks whether `x` is a “boolean value'' (i.e., either 0 or -1). | `26/76` | +| **`B5cc`** | `[cc+1] UFITS` | _`x - x`_ | Checks whether `x` is a `cc+1`-bit unsigned integer for `0 <= cc <= 255` (i.e., whether `0 <= x < 2^(cc+1)`). | `26/76` | +| **`B500`** | `CHKBIT` | _`x - x`_ | Checks whether `x` is a binary digit (i.e., zero or one). | `26/76` | +| **`B600`** | `FITSX` | _`x c - x`_ | Checks whether `x` is a `c`-bit signed integer for `0 <= c <= 1023`. | `26/76` | +| **`B601`** | `UFITSX` | _`x c - x`_ | Checks whether `x` is a `c`-bit unsigned integer for `0 <= c <= 1023`. | `26/76` | +| **`B602`** | `BITSIZE` | _`x - c`_ | Computes smallest `c >= 0` such that `x` fits into a `c`-bit signed integer (`-2^(c-1) <= c < 2^(c-1)`). | `26` | +| **`B603`** | `UBITSIZE` | _`x - c`_ | Computes smallest `c >= 0` such that `x` fits into a `c`-bit unsigned integer (`0 <= x < 2^c`), or throws a range check exception. | `26` | +| **`B608`** | `MIN` | _`x y - x or y`_ | Computes the minimum of two integers `x` and `y`. | `26` | +| **`B609`** | `MAX` | _`x y - x or y`_ | Computes the maximum of two integers `x` and `y`. | `26` | +| **`B60A`** | `MINMAX`
`INTSORT2` | _`x y - x y or y x`_ | Sorts two integers. Quiet version of this operation returns two `NaN`s if any of the arguments are `NaN`s. | `26` | +| **`B60B`** | `ABS` | _`x - \|x\|`_ | Computes the absolute value of an integer `x`. | `26` | + +### 5.4 Quiet arithmetic primitives + +Quiet operations return `NaN` instead of throwing exceptions if one of their arguments is a `NaN` or in the case of an integer overflow. +Quiet operations have a prefix `Q` as shown below. Another way to make an operation quiet is to add `QUIET` before it (i.e. one can write [`QUIET ADD`](#instr-add) instead of [`QADD`](#instr-qadd)). +Quiet versions of integer comparison primitives are also available ([`QUIET SGN`](#instr-sgn), [`QUIET LESS`](#instr-less) etc). + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :----------------------------------------------------------------------------------------------- | :----------- | +| **`B7A0`** | `QADD` | _`x y - x+y`_ | | `26` | +| **`B7A1`** | `QSUB` | _`x y - x-y`_ | | `26` | +| **`B7A2`** | `QSUBR` | _`x y - y-x`_ | | `26` | +| **`B7A3`** | `QNEGATE` | _`x - -x`_ | | `26` | +| **`B7A4`** | `QINC` | _`x - x+1`_ | | `26` | +| **`B7A5`** | `QDEC` | _`x - x-1`_ | | `26` | +| **`B7A8`** | `QMUL` | _`x y - x*y`_ | | `26` | +| **`B7A904`** | `QDIV` | _`x y - q`_ | Division returns `NaN` if `y=0`. | `34` | +| **`B7A905`** | `QDIVR` | _`x y - q’`_ | | `34` | +| **`B7A906`** | `QDIVC` | _`x y - q''`_ | | `34` | +| **`B7A908`** | `QMOD` | _`x y - r`_ | | `34` | +| **`B7A90C`** | `QDIVMOD` | _`x y - q r`_ | | `34` | +| **`B7A90D`** | `QDIVMODR` | _`x y - q' r'`_ | | `34` | +| **`B7A90E`** | `QDIVMODC` | _`x y - q'' r''`_ | | `34` | +| **`B7A985`** | `QMULDIVR` | _`x y z - q'`_ | | `34` | +| **`B7A98C`** | `QMULDIVMOD` | _`x y z - q r`_ | | `34` | +| **`B7AC`** | `QLSHIFT` | _`x y - x*2^y`_ | | `26` | +| **`B7AD`** | `QRSHIFT` | _`x y - floor(x/2^y)`_ | | `26` | +| **`B7AE`** | `QPOW2` | _`y - 2^y`_ | | `26` | +| **`B7B0`** | `QAND` | _`x y - x&y`_ | | `26` | +| **`B7B1`** | `QOR` | _`x y - x\|y`_ | | `26` | +| **`B7B2`** | `QXOR` | _`x y - x xor y`_ | | `26` | +| **`B7B3`** | `QNOT` | _`x - ~x`_ | | `26` | +| **`B7B4cc`** | `[cc+1] QFITS` | _`x - x`_ | Replaces `x` with a `NaN` if x is not a `cc+1`-bit signed integer, leaves it intact otherwise. | `34` | +| **`B7B5cc`** | `[cc+1] QUFITS` | _`x - x`_ | Replaces `x` with a `NaN` if x is not a `cc+1`-bit unsigned integer, leaves it intact otherwise. | `34` | +| **`B7B600`** | `QFITSX` | _`x c - x`_ | Replaces `x` with a `NaN` if x is not a c-bit signed integer, leaves it intact otherwise. | `34` | +| **`B7B601`** | `QUFITSX` | _`x c - x`_ | Replaces `x` with a `NaN` if x is not a c-bit unsigned integer, leaves it intact otherwise. | `34` | + +## 6 Comparison primitives + +### 6.1 Integer comparison + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`B8`** | `SGN` | _`x - sgn(x)`_ | Computes the sign of an integer `x`:
`-1` if `x<0`, `0` if `x=0`, `1` if `x>0`. | `18` | +| **`B9`** | `LESS` | _`x y - xy`_ | | `18` | +| **`BD`** | `NEQ` | _`x y - x!=y`_ | Equivalent to [`EQUAL`](#instr-equal) [`NOT`](#instr-not). | `18` | +| **`BE`** | `GEQ` | _`x y - x>=y`_ | Equivalent to [`LESS`](#instr-less) [`NOT`](#instr-not). | `18` | +| **`BF`** | `CMP` | _`x y - sgn(x-y)`_ | Computes the sign of `x-y`:
`-1` if `xy`.
No integer overflow can occur here unless `x` or `y` is a `NaN`. | `18` | +| **`C0yy`** | `[yy] EQINT` | _`x - x=yy`_ | Returns `-1` if `x=yy`, `0` otherwise.
`-2^7 <= yy < 2^7`. | `26` | +| **`C000`** | `ISZERO` | _`x - x=0`_ | Checks whether an integer is zero. Corresponds to Forth's `0=`. | `26` | +| **`C1yy`** | `[yy] LESSINT`
`[yy-1] LEQINT` | _`x - x`-2^7 <= yy < 2^7`. | `26` | +| **`C100`** | `ISNEG` | _`x - x<0`_ | Checks whether an integer is negative. Corresponds to Forth's `0<`. | `26` | +| **`C101`** | `ISNPOS` | _`x - x<=0`_ | Checks whether an integer is non-positive. | `26` | +| **`C2yy`** | `[yy] GTINT`
`[yy+1] GEQINT` | _`x - x>yy`_ | Returns `-1` if `x>yy`, `0` otherwise.
`-2^7 <= yy < 2^7`. | `26` | +| **`C200`** | `ISPOS` | _`x - x>0`_ | Checks whether an integer is positive. Corresponds to Forth's `0>`. | `26` | +| **`C2FF`** | `ISNNEG` | _`x - x >=0`_ | Checks whether an integer is non-negative. | `26` | +| **`C3yy`** | `[yy] NEQINT` | _`x - x!=yy`_ | Returns `-1` if `x!=yy`, `0` otherwise.
`-2^7 <= yy < 2^7`. | `26` | +| **`C4`** | `ISNAN` | _`x - x=NaN`_ | Checks whether `x` is a `NaN`. | `18` | +| **`C5`** | `CHKNAN` | _`x - x`_ | Throws an arithmetic overflow exception if `x` is a `NaN`. | `18/68` | + +### 6.2 Other comparison + +Most of these "other comparison" primitives actually compare the data portions of _Slices_ as bitstrings (ignoring references if not stated otherwise). + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :----------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`C700`** | `SEMPTY` | _`s - ?`_ | Checks whether a _Slice_ `s` is empty (i.e., contains no bits of data and no cell references). | `26` | +| **`C701`** | `SDEMPTY` | _`s - ?`_ | Checks whether _Slice_ `s` has no bits of data. | `26` | +| **`C702`** | `SREMPTY` | _`s - ?`_ | Checks whether _Slice_ `s` has no references. | `26` | +| **`C703`** | `SDFIRST` | _`s - ?`_ | Checks whether the first bit of _Slice_ `s` is a one. | `26` | +| **`C704`** | `SDLEXCMP` | _`s s' - x`_ | Compares the data of `s` lexicographically with the data of `s'`, returning `-1`, 0, or 1 depending on the result. | `26` | +| **`C705`** | `SDEQ` | _`s s' - ?`_ | Checks whether the data parts of `s` and `s'` coincide, equivalent to [`SDLEXCMP`](#instr-sdlexcmp) [`ISZERO`](#instr-iszero). | `26` | +| **`C708`** | `SDPFX` | _`s s' - ?`_ | Checks whether `s` is a prefix of `s'`. | `26` | +| **`C709`** | `SDPFXREV` | _`s s' - ?`_ | Checks whether `s'` is a prefix of `s`, equivalent to [`SWAP`](#instr-swap) [`SDPFX`](#instr-sdpfx). | `26` | +| **`C70A`** | `SDPPFX` | _`s s' - ?`_ | Checks whether `s` is a proper prefix of `s'` (i.e., a prefix distinct from `s'`). | `26` | +| **`C70B`** | `SDPPFXREV` | _`s s' - ?`_ | Checks whether `s'` is a proper prefix of `s`. | `26` | +| **`C70C`** | `SDSFX` | _`s s' - ?`_ | Checks whether `s` is a suffix of `s'`. | `26` | +| **`C70D`** | `SDSFXREV` | _`s s' - ?`_ | Checks whether `s'` is a suffix of `s`. | `26` | +| **`C70E`** | `SDPSFX` | _`s s' - ?`_ | Checks whether `s` is a proper suffix of `s'`. | `26` | +| **`C70F`** | `SDPSFXREV` | _`s s' - ?`_ | Checks whether `s'` is a proper suffix of `s`. | `26` | +| **`C710`** | `SDCNTLEAD0` | _`s - n`_ | Returns the number of leading zeroes in `s`. | `26` | +| **`C711`** | `SDCNTLEAD1` | _`s - n`_ | Returns the number of leading ones in `s`. | `26` | +| **`C712`** | `SDCNTTRAIL0` | _`s - n`_ | Returns the number of trailing zeroes in `s`. | `26` | +| **`C713`** | `SDCNTTRAIL1` | _`s - n`_ | Returns the number of trailing ones in `s`. | `26` | + +## 7 Cell primitives + +### 7.1 Cell serialization primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`C8`** | `NEWC` | _`- b`_ | Creates a new empty _Builder_. | `18` | +| **`C9`** | `ENDC` | _`b - c`_ | Converts a _Builder_ into an ordinary _Cell_. | `518` | +| **`CAcc`** | `[cc+1] STI` | _`x b - b'`_ | 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. | `26` | +| **`CBcc`** | `[cc+1] STU` | _`x b - b'`_ | Stores an unsigned `cc+1`-bit integer `x` into _Builder_ `b`. In all other respects it is similar to [`STI`](#instr-sti). | `26` | +| **`CC`** | `STREF` | _`c b - b'`_ | Stores a reference to _Cell_ `c` into _Builder_ `b`. | `18` | +| **`CD`** | `STBREFR`
`ENDCST` | _`b b'' - b`_ | Equivalent to [`ENDC`](#instr-endc) [`SWAP`](#instr-swap) [`STREF`](#instr-stref). | `518` | +| **`CE`** | `STSLICE` | _`s b - b'`_ | Stores _Slice_ `s` into _Builder_ `b`. | `18` | +| **`CF00`** | `STIX` | _`x b l - b'`_ | Stores a signed `l`-bit integer `x` into `b` for `0 <= l <= 257`. | `26` | +| **`CF01`** | `STUX` | _`x b l - b'`_ | Stores an unsigned `l`-bit integer `x` into `b` for `0 <= l <= 256`. | `26` | +| **`CF02`** | `STIXR` | _`b x l - b'`_ | Similar to [`STIX`](#instr-stix), but with arguments in a different order. | `26` | +| **`CF03`** | `STUXR` | _`b x l - b'`_ | Similar to [`STUX`](#instr-stux), but with arguments in a different order. | `26` | +| **`CF04`** | `STIXQ` | _`x b l - x b f or b' 0`_ | A quiet version of [`STIX`](#instr-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. | `26` | +| **`CF05`** | `STUXQ` | _`x b l - x b f or b' 0`_ | A quiet version of [`STUX`](#instr-stux). | `26` | +| **`CF06`** | `STIXRQ` | _`b x l - b x f or b' 0`_ | A quiet version of [`STIXR`](#instr-stixr). | `26` | +| **`CF07`** | `STUXRQ` | _`b x l - b x f or b' 0`_ | A quiet version of [`STUXR`](#instr-stuxr). | `26` | +| **`CF08cc`** | `[cc+1] STI_l` | _`x b - b'`_ | A longer version of [`[cc+1] STI`](#instr-sti). | `34` | +| **`CF09cc`** | `[cc+1] STU_l` | _`x b - b'`_ | A longer version of [`[cc+1] STU`](#instr-stu). | `34` | +| **`CF0Acc`** | `[cc+1] STIR` | _`b x - b'`_ | Equivalent to [`SWAP`](#instr-swap) [`[cc+1] STI`](#instr-sti). | `34` | +| **`CF0Bcc`** | `[cc+1] STUR` | _`b x - b'`_ | Equivalent to [`SWAP`](#instr-swap) [`[cc+1] STU`](#instr-stu). | `34` | +| **`CF0Ccc`** | `[cc+1] STIQ` | _`x b - x b f or b' 0`_ | A quiet version of [`STI`](#instr-sti). | `34` | +| **`CF0Dcc`** | `[cc+1] STUQ` | _`x b - x b f or b' 0`_ | A quiet version of [`STU`](#instr-stu). | `34` | +| **`CF0Ecc`** | `[cc+1] STIRQ` | _`b x - b x f or b' 0`_ | A quiet version of [`STIR`](#instr-stir). | `34` | +| **`CF0Fcc`** | `[cc+1] STURQ` | _`b x - b x f or b' 0`_ | A quiet version of [`STUR`](#instr-stur). | `34` | +| **`CF10`** | `STREF_l` | _`c b - b'`_ | A longer version of [`STREF`](#instr-stref). | `26` | +| **`CF11`** | `STBREF` | _`b' b - b''`_ | Equivalent to [`SWAP`](#instr-swap) [`STBREFR`](#instr-stbrefr). | `526` | +| **`CF12`** | `STSLICE_l` | _`s b - b'`_ | A longer version of [`STSLICE`](#instr-stslice). | `26` | +| **`CF13`** | `STB` | _`b' b - b''`_ | Appends all data from _Builder_ `b'` to _Builder_ `b`. | `26` | +| **`CF14`** | `STREFR` | _`b c - b'`_ | Equivalent to [`SWAP`](#instr-swap) [`STREF`](#instr-stref). | `26` | +| **`CF15`** | `STBREFR_l` | _`b b' - b''`_ | A longer encoding of [`STBREFR`](#instr-stbrefr). | `526` | +| **`CF16`** | `STSLICER` | _`b s - b'`_ | Equivalent to [`SWAP`](#instr-swap) [`STSLICE`](#instr-stslice). | `26` | +| **`CF17`** | `STBR`
`BCONCAT` | _`b b' - b''`_ | Concatenates two builders.
Equivalent to [`SWAP`](#instr-swap) [`STB`](#instr-stb). | `26` | +| **`CF18`** | `STREFQ` | _`c b - c b -1 or b' 0`_ | Quiet version of [`STREF`](#instr-stref). | `26` | +| **`CF19`** | `STBREFQ` | _`b' b - b' b -1 or b'' 0`_ | Quiet version of [`STBREF`](#instr-stbref). | `526` | +| **`CF1A`** | `STSLICEQ` | _`s b - s b -1 or b' 0`_ | Quiet version of [`STSLICE`](#instr-stslice). | `26` | +| **`CF1B`** | `STBQ` | _`b' b - b' b -1 or b'' 0`_ | Quiet version of [`STB`](#instr-stb). | `26` | +| **`CF1C`** | `STREFRQ` | _`b c - b c -1 or b' 0`_ | Quiet version of [`STREFR`](#instr-strefr). | `26` | +| **`CF1D`** | `STBREFRQ` | _`b b' - b b' -1 or b'' 0`_ | Quiet version of [`STBREFR`](#instr-stbrefr). | `526` | +| **`CF1E`** | `STSLICERQ` | _`b s - b s -1 or b'' 0`_ | Quiet version of [`STSLICER`](#instr-stslicer). | `26` | +| **`CF1F`** | `STBRQ`
`BCONCATQ` | _`b b' - b b' -1 or b'' 0`_ | Quiet version of [`STBR`](#instr-stbr). | `26` | +| **`CF20`** | `[ref] STREFCONST` | _`b - b’`_ | Equivalent to [`PUSHREF`](#instr-pushref) [`STREFR`](#instr-strefr). | `26` | +| **`CF21`** | `[ref] [ref] STREF2CONST` | _`b - b’`_ | Equivalent to [`STREFCONST`](#instr-strefconst) [`STREFCONST`](#instr-strefconst). | `26` | +| **`CF23`** | | _`b x - c`_ | 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`](#instr-endc). Otherwise some validity checks on the data and references of `b` are performed before creating the exotic cell. | `526` | +| **`CF28`** | `STILE4` | _`x b - b'`_ | Stores a little-endian signed 32-bit integer. | `26` | +| **`CF29`** | `STULE4` | _`x b - b'`_ | Stores a little-endian unsigned 32-bit integer. | `26` | +| **`CF2A`** | `STILE8` | _`x b - b'`_ | Stores a little-endian signed 64-bit integer. | `26` | +| **`CF2B`** | `STULE8` | _`x b - b'`_ | Stores a little-endian unsigned 64-bit integer. | `26` | +| **`CF30`** | `BDEPTH` | _`b - x`_ | 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`. | `26` | +| **`CF31`** | `BBITS` | _`b - x`_ | Returns the number of data bits already stored in _Builder_ `b`. | `26` | +| **`CF32`** | `BREFS` | _`b - y`_ | Returns the number of cell references already stored in `b`. | `26` | +| **`CF33`** | `BBITREFS` | _`b - x y`_ | Returns the numbers of both data bits and cell references in `b`. | `26` | +| **`CF35`** | `BREMBITS` | _`b - x'`_ | Returns the number of data bits that can still be stored in `b`. | `26` | +| **`CF36`** | `BREMREFS` | _`b - y'`_ | Returns the number of references that can still be stored in `b`. | `26` | +| **`CF37`** | `BREMBITREFS` | _`b - x' y'`_ | Returns the numbers of both data bits and references that can still be stored in `b`. | `26` | +| **`CF38cc`** | `[cc+1] BCHKBITS#` | _`b -`_ | Checks whether `cc+1` bits can be stored into `b`, where `0 <= cc <= 255`. | `34/84` | +| **`CF39`** | `BCHKBITS` | _`b x - `_ | 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. | `26/76` | +| **`CF3A`** | `BCHKREFS` | _`b y - `_ | Checks whether `y` references can be stored into `b`, `0 <= y <= 7`. | `26/76` | +| **`CF3B`** | `BCHKBITREFS` | _`b x y - `_ | Checks whether `x` bits and `y` references can be stored into `b`, `0 <= x <= 1023`, `0 <= y <= 7`. | `26/76` | +| **`CF3Ccc`** | `[cc+1] BCHKBITSQ#` | _`b - ?`_ | Checks whether `cc+1` bits can be stored into `b`, where `0 <= cc <= 255`. | `34` | +| **`CF3D`** | `BCHKBITSQ` | _`b x - ?`_ | Checks whether `x` bits can be stored into `b`, `0 <= x <= 1023`. | `26` | +| **`CF3E`** | `BCHKREFSQ` | _`b y - ?`_ | Checks whether `y` references can be stored into `b`, `0 <= y <= 7`. | `26` | +| **`CF3F`** | `BCHKBITREFSQ` | _`b x y - ?`_ | Checks whether `x` bits and `y` references can be stored into `b`, `0 <= x <= 1023`, `0 <= y <= 7`. | `26` | +| **`CF40`** | `STZEROES` | _`b n - b'`_ | Stores `n` binary zeroes into _Builder_ `b`. | `26` | +| **`CF41`** | `STONES` | _`b n - b'`_ | Stores `n` binary ones into _Builder_ `b`. | `26` | +| **`CF42`** | `STSAME` | _`b n x - b'`_ | Stores `n` binary `x`es (`0 <= x <= 1`) into _Builder_ `b`. | `26` | +| **`CFC0_xysss`** | `[slice] STSLICECONST` | _`b - b'`_ | 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`](#instr-stsliceconst) with [`PUSHSLICE`](#instr-pushslice) [`STSLICER`](#instr-stslicer) if the slice is too big. | `24` | +| **`CF81`** | `STZERO` | _`b - b'`_ | Stores one binary zero. | `24` | +| **`CF83`** | `STONE` | _`b - b'`_ | Stores one binary one. | `24` | + +### 7.2 Cell deserialization primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :--------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------- | +| **`D0`** | `CTOS` | _`c - s`_ | 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. | `118/43` | +| **`D1`** | `ENDS` | _`s - `_ | Removes a _Slice_ `s` from the stack, and throws an exception if it is not empty. | `18/68` | +| **`D2cc`** | `[cc+1] LDI` | _`s - x s'`_ | Loads (i.e., parses) a signed `cc+1`-bit integer `x` from _Slice_ `s`, and returns the remainder of `s` as `s'`. | `26` | +| **`D3cc`** | `[cc+1] LDU` | _`s - x s'`_ | Loads an unsigned `cc+1`-bit integer `x` from _Slice_ `s`. | `26` | +| **`D4`** | `LDREF` | _`s - c s'`_ | Loads a cell reference `c` from `s`. | `18` | +| **`D5`** | `LDREFRTOS` | _`s - s' s''`_ | Equivalent to [`LDREF`](#instr-ldref) [`SWAP`](#instr-swap) [`CTOS`](#instr-ctos). | `118/43` | +| **`D6cc`** | `[cc+1] LDSLICE` | _`s - s'' s'`_ | Cuts the next `cc+1` bits of `s` into a separate _Slice_ `s''`. | `26` | +| **`D700`** | `LDIX` | _`s l - x s'`_ | Loads a signed `l`-bit (`0 <= l <= 257`) integer `x` from _Slice_ `s`, and returns the remainder of `s` as `s'`. | `26` | +| **`D701`** | `LDUX` | _`s l - x s'`_ | Loads an unsigned `l`-bit integer `x` from (the first `l` bits of) `s`, with `0 <= l <= 256`. | `26` | +| **`D702`** | `PLDIX` | _`s l - x`_ | Preloads a signed `l`-bit integer from _Slice_ `s`, for `0 <= l <= 257`. | `26` | +| **`D703`** | `PLDUX` | _`s l - x`_ | Preloads an unsigned `l`-bit integer from `s`, for `0 <= l <= 256`. | `26` | +| **`D704`** | `LDIXQ` | _`s l - x s' -1 or s 0`_ | Quiet version of [`LDIX`](#instr-ldix): loads a signed `l`-bit integer from `s` similarly to [`LDIX`](#instr-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. | `26` | +| **`D705`** | `LDUXQ` | _`s l - x s' -1 or s 0`_ | Quiet version of [`LDUX`](#instr-ldux). | `26` | +| **`D706`** | `PLDIXQ` | _`s l - x -1 or 0`_ | Quiet version of [`PLDIX`](#instr-pldix). | `26` | +| **`D707`** | `PLDUXQ` | _`s l - x -1 or 0`_ | Quiet version of [`PLDUX`](#instr-pldux). | `26` | +| **`D708cc`** | `[cc+1] LDI_l` | _`s - x s'`_ | A longer encoding for [`LDI`](#instr-ldi). | `34` | +| **`D709cc`** | `[cc+1] LDU_l` | _`s - x s'`_ | A longer encoding for [`LDU`](#instr-ldu). | `34` | +| **`D70Acc`** | `[cc+1] PLDI` | _`s - x`_ | Preloads a signed `cc+1`-bit integer from _Slice_ `s`. | `34` | +| **`D70Bcc`** | `[cc+1] PLDU` | _`s - x`_ | Preloads an unsigned `cc+1`-bit integer from `s`. | `34` | +| **`D70Ccc`** | `[cc+1] LDIQ` | _`s - x s' -1 or s 0`_ | A quiet version of [`LDI`](#instr-ldi). | `34` | +| **`D70Dcc`** | `[cc+1] LDUQ` | _`s - x s' -1 or s 0`_ | A quiet version of [`LDU`](#instr-ldu). | `34` | +| **`D70Ecc`** | `[cc+1] PLDIQ` | _`s - x -1 or 0`_ | A quiet version of [`PLDI`](#instr-pldi). | `34` | +| **`D70Fcc`** | `[cc+1] PLDUQ` | _`s - x -1 or 0`_ | A quiet version of [`PLDU`](#instr-pldu). | `34` | +| **`D714_c`** | `[32(c+1)] PLDUZ` | _`s - s x`_ | 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`](#instr-ifbitjmp) and similar instructions. | `26` | +| **`D718`** | `LDSLICEX` | _`s l - s'' s'`_ | Loads the first `0 <= l <= 1023` bits from _Slice_ `s` into a separate _Slice_ `s''`, returning the remainder of `s` as `s'`. | `26` | +| **`D719`** | `PLDSLICEX` | _`s l - s''`_ | Returns the first `0 <= l <= 1023` bits of `s` as `s''`. | `26` | +| **`D71A`** | `LDSLICEXQ` | _`s l - s'' s' -1 or s 0`_ | A quiet version of [`LDSLICEX`](#instr-ldslicex). | `26` | +| **`D71B`** | `PLDSLICEXQ` | _`s l - s' -1 or 0`_ | A quiet version of [`LDSLICEXQ`](#instr-ldslicexq). | `26` | +| **`D71Ccc`** | `[cc+1] LDSLICE_l` | _`s - s'' s'`_ | A longer encoding for [`LDSLICE`](#instr-ldslice). | `34` | +| **`D71Dcc`** | `[cc+1] PLDSLICE` | _`s - s''`_ | Returns the first `0 < cc+1 <= 256` bits of `s` as `s''`. | `34` | +| **`D71Ecc`** | `[cc+1] LDSLICEQ` | _`s - s'' s' -1 or s 0`_ | A quiet version of [`LDSLICE`](#instr-ldslice). | `34` | +| **`D71Fcc`** | `[cc+1] PLDSLICEQ` | _`s - s'' -1 or 0`_ | A quiet version of [`PLDSLICE`](#instr-pldslice). | `34` | +| **`D720`** | `SDCUTFIRST` | _`s l - s'`_ | Returns the first `0 <= l <= 1023` bits of `s`. It is equivalent to [`PLDSLICEX`](#instr-pldslicex). | `26` | +| **`D721`** | `SDSKIPFIRST` | _`s l - s'`_ | Returns all but the first `0 <= l <= 1023` bits of `s`. It is equivalent to [`LDSLICEX`](#instr-ldslicex) [`NIP`](#instr-nip). | `26` | +| **`D722`** | `SDCUTLAST` | _`s l - s'`_ | Returns the last `0 <= l <= 1023` bits of `s`. | `26` | +| **`D723`** | `SDSKIPLAST` | _`s l - s'`_ | Returns all but the last `0 <= l <= 1023` bits of `s`. | `26` | +| **`D724`** | `SDSUBSTR` | _`s l l' - s'`_ | Returns `0 <= l' <= 1023` bits of `s` starting from offset `0 <= l <= 1023`, thus extracting a bit substring out of the data of `s`. | `26` | +| **`D726`** | `SDBEGINSX` | _`s s' - s''`_ | 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`](#instr-sdpfxrev) can be considered a quiet version of [`SDBEGINSX`](#instr-sdbeginsx). | `26` | +| **`D727`** | `SDBEGINSXQ` | _`s s' - s'' -1 or s 0`_ | A quiet version of [`SDBEGINSX`](#instr-sdbeginsx). | `26` | +| **`D72A_xsss`** | `[slice] SDBEGINS` | _`s - s''`_ | 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. | `31` | +| **`D72E_xsss`** | `[slice] SDBEGINSQ` | _`s - s'' -1 or s 0`_ | A quiet version of [`SDBEGINS`](#instr-sdbegins). | `31` | +| **`D730`** | `SCUTFIRST` | _`s l r - s'`_ | Returns the first `0 <= l <= 1023` bits and first `0 <= r <= 4` references of `s`. | `26` | +| **`D731`** | `SSKIPFIRST` | _`s l r - s'`_ | Returns all but the first `l` bits of `s` and `r` references of `s`. | `26` | +| **`D732`** | `SCUTLAST` | _`s l r - s'`_ | Returns the last `0 <= l <= 1023` data bits and last `0 <= r <= 4` references of `s`. | `26` | +| **`D733`** | `SSKIPLAST` | _`s l r - s'`_ | Returns all but the last `l` bits of `s` and `r` references of `s`. | `26` | +| **`D734`** | `SUBSLICE` | _`s l r l' r' - s'`_ | 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. | `26` | +| **`D736`** | `SPLIT` | _`s l r - s' s''`_ | 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''`. | `26` | +| **`D737`** | `SPLITQ` | _`s l r - s' s'' -1 or s 0`_ | A quiet version of [`SPLIT`](#instr-split). | `26` | +| **`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`. | | +| **`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. | | +| **`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. | | +| **`D741`** | `SCHKBITS` | _`s l - `_ | 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. | `26/76` | +| **`D742`** | `SCHKREFS` | _`s r - `_ | Checks whether there are at least `r` references in _Slice_ `s`. | `26/76` | +| **`D743`** | `SCHKBITREFS` | _`s l r - `_ | Checks whether there are at least `l` data bits and `r` references in _Slice_ `s`. | `26/76` | +| **`D745`** | `SCHKBITSQ` | _`s l - ?`_ | Checks whether there are at least `l` data bits in _Slice_ `s`. | `26` | +| **`D746`** | `SCHKREFSQ` | _`s r - ?`_ | Checks whether there are at least `r` references in _Slice_ `s`. | `26` | +| **`D747`** | `SCHKBITREFSQ` | _`s l r - ?`_ | Checks whether there are at least `l` data bits and `r` references in _Slice_ `s`. | `26` | +| **`D748`** | `PLDREFVAR` | _`s n - c`_ | Returns the `n`-th cell reference of _Slice_ `s` for `0 <= n <= 3`. | `26` | +| **`D749`** | `SBITS` | _`s - l`_ | Returns the number of data bits in _Slice_ `s`. | `26` | +| **`D74A`** | `SREFS` | _`s - r`_ | Returns the number of references in _Slice_ `s`. | `26` | +| **`D74B`** | `SBITREFS` | _`s - l r`_ | Returns both the number of data bits and the number of references in `s`. | `26` | +| **`D74E_n`** | `[n] PLDREFIDX` | _`s - c`_ | Returns the `n`-th cell reference of _Slice_ `s`, where `0 <= n <= 3`. | `26` | +| **`D74C`** | `PLDREF` | _`s - c`_ | Preloads the first cell reference of a _Slice_. | `26` | +| **`D750`** | `LDILE4` | _`s - x s'`_ | Loads a little-endian signed 32-bit integer. | `26` | +| **`D751`** | `LDULE4` | _`s - x s'`_ | Loads a little-endian unsigned 32-bit integer. | `26` | +| **`D752`** | `LDILE8` | _`s - x s'`_ | Loads a little-endian signed 64-bit integer. | `26` | +| **`D753`** | `LDULE8` | _`s - x s'`_ | Loads a little-endian unsigned 64-bit integer. | `26` | +| **`D754`** | `PLDILE4` | _`s - x`_ | Preloads a little-endian signed 32-bit integer. | `26` | +| **`D755`** | `PLDULE4` | _`s - x`_ | Preloads a little-endian unsigned 32-bit integer. | `26` | +| **`D756`** | `PLDILE8` | _`s - x`_ | Preloads a little-endian signed 64-bit integer. | `26` | +| **`D757`** | `PLDULE8` | _`s - x`_ | Preloads a little-endian unsigned 64-bit integer. | `26` | +| **`D758`** | `LDILE4Q` | _`s - x s' -1 or s 0`_ | Quietly loads a little-endian signed 32-bit integer. | `26` | +| **`D759`** | `LDULE4Q` | _`s - x s' -1 or s 0`_ | Quietly loads a little-endian unsigned 32-bit integer. | `26` | +| **`D75A`** | `LDILE8Q` | _`s - x s' -1 or s 0`_ | Quietly loads a little-endian signed 64-bit integer. | `26` | +| **`D75B`** | `LDULE8Q` | _`s - x s' -1 or s 0`_ | Quietly loads a little-endian unsigned 64-bit integer. | `26` | +| **`D75C`** | `PLDILE4Q` | _`s - x -1 or 0`_ | Quietly preloads a little-endian signed 32-bit integer. | `26` | +| **`D75D`** | `PLDULE4Q` | _`s - x -1 or 0`_ | Quietly preloads a little-endian unsigned 32-bit integer. | `26` | +| **`D75E`** | `PLDILE8Q` | _`s - x -1 or 0`_ | Quietly preloads a little-endian signed 64-bit integer. | `26` | +| **`D75F`** | `PLDULE8Q` | _`s - x -1 or 0`_ | Quietly preloads a little-endian unsigned 64-bit integer. | `26` | +| **`D760`** | `LDZEROES` | _`s - n s'`_ | Returns the count `n` of leading zero bits in `s`, and removes these bits from `s`. | `26` | +| **`D761`** | `LDONES` | _`s - n s'`_ | Returns the count `n` of leading one bits in `s`, and removes these bits from `s`. | `26` | +| **`D762`** | `LDSAME` | _`s x - n s'`_ | Returns the count `n` of leading bits equal to `0 <= x <= 1` in `s`, and removes these bits from `s`. | `26` | +| **`D764`** | `SDEPTH` | _`s - x`_ | 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`. | `26` | +| **`D765`** | `CDEPTH` | _`c - x`_ | 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. | `26` | + +## 8 Continuation and control flow primitives + +### 8.1 Unconditional control flow primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`D8`** | `EXECUTE`
`CALLX` | _`c - `_ | _Calls_, or _executes_, continuation `c`. | `18` | +| **`D9`** | `JMPX` | _`c - `_ | _Jumps_, or transfers control, to continuation `c`.
The remainder of the previous current continuation `cc` is discarded. | `18` | +| **`DApr`** | `[p] [r] CALLXARGS` | _`c - `_ | _Calls_ continuation `c` with `p` parameters and expecting `r` return values
`0 <= p <= 15`, `0 <= r <= 15` | `26` | +| **`DB0p`** | `[p] -1 CALLXARGS` | _`c - `_ | _Calls_ continuation `c` with `0 <= p <= 15` parameters, expecting an arbitrary number of return values. | `26` | +| **`DB1p`** | `[p] JMPXARGS` | _`c - `_ | _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). | `26` | +| **`DB2r`** | `[r] RETARGS` | | _Returns_ to `c0`, with `0 <= r <= 15` return values taken from the current stack. | `26` | +| **`DB30`** | `RET`
`RETTRUE` | | _Returns_ to the continuation at `c0`. The remainder of the current continuation `cc` is discarded.
Approximately equivalent to [`c0 PUSHCTR`](#instr-pushctr) [`JMPX`](#instr-jmpx). | `26` | +| **`DB31`** | `RETALT`
`RETFALSE` | | _Returns_ to the continuation at `c1`.
Approximately equivalent to [`c1 PUSHCTR`](#instr-pushctr) [`JMPX`](#instr-jmpx). | `26` | +| **`DB32`** | `BRANCH`
`RETBOOL` | _`f - `_ | Performs [`RETTRUE`](#instr-ret) if integer `f!=0`, or [`RETFALSE`](#instr-retalt) if `f=0`. | `26` | +| **`DB34`** | `CALLCC` | _`c - `_ | _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`). | `26` | +| **`DB35`** | `JMPXDATA` | _`c - `_ | Similar to [`CALLCC`](#instr-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`. | `26` | +| **`DB36pr`** | `[p] [r] CALLCCARGS` | _`c - `_ | Similar to [`CALLXARGS`](#instr-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`. | `34` | +| **`DB38`** | `CALLXVARARGS` | _`c p r - `_ | Similar to [`CALLXARGS`](#instr-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`. | `26` | +| **`DB39`** | `RETVARARGS` | _`p r - `_ | Similar to [`RETARGS`](#instr-retargs). | `26` | +| **`DB3A`** | `JMPXVARARGS` | _`c p r - `_ | Similar to [`JMPXARGS`](#instr-jmpxargs). | `26` | +| **`DB3B`** | `CALLCCVARARGS` | _`c p r - `_ | Similar to [`CALLCCARGS`](#instr-callccargs). | `26` | +| **`DB3C`** | `[ref] CALLREF` | | Equivalent to [`PUSHREFCONT`](#instr-pushrefcont) [`CALLX`](#instr-execute). | `126/51` | +| **`DB3D`** | `[ref] JMPREF` | | Equivalent to [`PUSHREFCONT`](#instr-pushrefcont) [`JMPX`](#instr-jmpx). | `126/51` | +| **`DB3E`** | `[ref] JMPREFDATA` | | Equivalent to [`PUSHREFCONT`](#instr-pushrefcont) [`JMPXDATA`](#instr-jmpxdata). | `126/51` | +| **`DB3F`** | `RETDATA` | | Equivalent to [`c0 PUSHCTR`](#instr-pushctr) [`JMPXDATA`](#instr-jmpxdata). In this way, the remainder of the current continuation is converted into a _Slice_ and returned to the caller. | `26` | + +### 8.2 Conditional control flow primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`DC`** | `IFRET`
`IFNOT:` | _`f - `_ | Performs a [`RET`](#instr-ret), but only if integer `f` is non-zero. If `f` is a `NaN`, throws an integer overflow exception. | `18` | +| **`DD`** | `IFNOTRET`
`IF:` | _`f - `_ | Performs a [`RET`](#instr-ret), but only if integer `f` is zero. | `18` | +| **`DE`** | `IF` | _`f c - `_ | Performs [`EXECUTE`](#instr-execute) for `c` (i.e., _executes_ `c`), but only if integer `f` is non-zero. Otherwise simply discards both values. | `18` | +| **`DE`** | `IF:<{ code }>`
`<{ code }>IF` | _`f -`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`IF`](#instr-if). | | +| **`DF`** | `IFNOT` | _`f c - `_ | Executes continuation `c`, but only if integer `f` is zero. Otherwise simply discards both values. | `18` | +| **`DF`** | `IFNOT:<{ code }>`
`<{ code }>IFNOT` | _`f -`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`IFNOT`](#instr-ifnot). | | +| **`E0`** | `IFJMP` | _`f c - `_ | Jumps to `c` (similarly to [`JMPX`](#instr-jmpx)), but only if `f` is non-zero. | `18` | +| **`E0`** | `IFJMP:<{ code }>` | _`f -`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`IFJMP`](#instr-ifjmp). | | +| **`E1`** | `IFNOTJMP` | _`f c - `_ | Jumps to `c` (similarly to [`JMPX`](#instr-jmpx)), but only if `f` is zero. | `18` | +| **`E1`** | `IFNOTJMP:<{ code }>` | _`f -`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`IFNOTJMP`](#instr-ifnotjmp). | | +| **`E2`** | `IFELSE` | _`f c c' - `_ | If integer `f` is non-zero, executes `c`, otherwise executes `c'`. Equivalent to [`CONDSELCHK`](#instr-condselchk) [`EXECUTE`](#instr-execute). | `18` | +| **`E2`** | `IF:<{ code1 }>ELSE<{ code2 }>` | _`f -`_ | Equivalent to [`<{ code1 }> CONT`](#instr-pushcont) [`<{ code2 }> CONT`](#instr-pushcont) [`IFELSE`](#instr-ifelse). | | +| **`E300`** | `[ref] IFREF` | _`f - `_ | Equivalent to [`PUSHREFCONT`](#instr-pushrefcont) [`IF`](#instr-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. | `26/126/51` | +| **`E301`** | `[ref] IFNOTREF` | _`f - `_ | Equivalent to [`PUSHREFCONT`](#instr-pushrefcont) [`IFNOT`](#instr-ifnot). | `26/126/51` | +| **`E302`** | `[ref] IFJMPREF` | _`f - `_ | Equivalent to [`PUSHREFCONT`](#instr-pushrefcont) [`IFJMP`](#instr-ifjmp). | `26/126/51` | +| **`E303`** | `[ref] IFNOTJMPREF` | _`f - `_ | Equivalent to [`PUSHREFCONT`](#instr-pushrefcont) [`IFNOTJMP`](#instr-ifnotjmp). | `26/126/51` | +| **`E304`** | `CONDSEL` | _`f x y - x or y`_ | 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`](#instr-rot) [`ISZERO`](#instr-iszero) [`INC`](#instr-inc) [`ROLLX`](#instr-rollx) [`NIP`](#instr-nip). | `26` | +| **`E305`** | `CONDSELCHK` | _`f x y - x or y`_ | Same as [`CONDSEL`](#instr-condsel), but first checks whether `x` and `y` have the same type. | `26` | +| **`E308`** | `IFRETALT` | _`f -`_ | Performs [`RETALT`](#instr-retalt) if integer `f!=0`. | `26` | +| **`E309`** | `IFNOTRETALT` | _`f -`_ | Performs [`RETALT`](#instr-retalt) if integer `f=0`. | `26` | +| **`E30D`** | `[ref] IFREFELSE` | _`f c -`_ | Equivalent to [`PUSHREFCONT`](#instr-pushrefcont) [`SWAP`](#instr-swap) [`IFELSE`](#instr-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. | `26/126/51` | +| **`E30E`** | `[ref] IFELSEREF` | _`f c -`_ | Equivalent to [`PUSHREFCONT`](#instr-pushrefcont) [`IFELSE`](#instr-ifelse). | `26/126/51` | +| **`E30F`** | `[ref] [ref] IFREFELSEREF` | _`f -`_ | Equivalent to [`PUSHREFCONT`](#instr-pushrefcont) [`PUSHREFCONT`](#instr-pushrefcont) [`IFELSE`](#instr-ifelse). | `126/51` | +| **`E39_n`** | `[n] IFBITJMP` | _`x c - x`_ | Checks whether bit `0 <= n <= 31` is set in integer `x`, and if so, performs [`JMPX`](#instr-jmpx) to continuation `c`. Value `x` is left in the stack. | `26` | +| **`E3B_n`** | `[n] IFNBITJMP` | _`x c - x`_ | Jumps to `c` if bit `0 <= n <= 31` is not set in integer `x`. | `26` | +| **`E3D_n`** | `[ref] [n] IFBITJMPREF` | _`x - x`_ | Performs a [`JMPREF`](#instr-jmpref) if bit `0 <= n <= 31` is set in integer `x`. | `126/51` | +| **`E3F_n`** | `[ref] [n] IFNBITJMPREF` | _`x - x`_ | Performs a [`JMPREF`](#instr-jmpref) if bit `0 <= n <= 31` is not set in integer `x`. | `126/51` | + +### 8.3 Control flow primitives: loops + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :------------------ | :----------------------------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------- | +| **`E4`** | `REPEAT` | _`n c - `_ | 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`](#instr-ret) inside the code of `c` works as a `continue`, not as a `break`. One should use either alternative (experimental) loops or alternative [`RETALT`](#instr-retalt) (along with a [`SETEXITALT`](#instr-setexitalt) before the loop) to `break` out of a loop. | `18` | +| **`E4`** | `REPEAT:<{ code }>`
`<{ code }>REPEAT` | _`n -`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`REPEAT`](#instr-repeat). | | +| **`E5`** | `REPEATEND`
`REPEAT:` | _`n - `_ | Similar to [`REPEAT`](#instr-repeat), but it is applied to the current continuation `cc`. | `18` | +| **`E6`** | `UNTIL` | _`c - `_ | 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. | `18` | +| **`E6`** | `UNTIL:<{ code }>`
`<{ code }>UNTIL` | _`-`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`UNTIL`](#instr-until). | | +| **`E7`** | `UNTILEND`
`UNTIL:` | _`-`_ | Similar to [`UNTIL`](#instr-until), but executes the current continuation `cc` in a loop. When the loop exit condition is satisfied, performs a [`RET`](#instr-ret). | `18` | +| **`E8`** | `WHILE` | _`c' c - `_ | 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. | `18` | +| **`E8`** | `WHILE:<{ cond }>DO<{ code }>` | _`-`_ | Equivalent to [`<{ cond }> CONT`](#instr-pushcont) [`<{ code }> CONT`](#instr-pushcont) [`WHILE`](#instr-while). | | +| **`E9`** | `WHILEEND` | _`c' - `_ | Similar to [`WHILE`](#instr-while), but uses the current continuation `cc` as the loop body. | `18` | +| **`EA`** | `AGAIN` | _`c - `_ | Similar to [`REPEAT`](#instr-repeat), but executes `c` infinitely many times. A [`RET`](#instr-ret) only begins a new iteration of the infinite loop, which can be exited only by an exception, or a [`RETALT`](#instr-retalt) (or an explicit [`JMPX`](#instr-jmpx)). | `18` | +| **`EA`** | `AGAIN:<{ code }>`
`<{ code }>AGAIN` | _`-`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`AGAIN`](#instr-again). | | +| **`EB`** | `AGAINEND`
`AGAIN:` | _`-`_ | Similar to [`AGAIN`](#instr-again), but performed with respect to the current continuation `cc`. | `18` | +| **`E314`** | `REPEATBRK` | _`n c -`_ | Similar to [`REPEAT`](#instr-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`](#instr-retalt) could be used to break out of the loop body. | `26` | +| **`E314`** | `REPEATBRK:<{ code }>`
`<{ code }>REPEATBRK` | _`n -`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`REPEATBRK`](#instr-repeatbrk). | | +| **`E315`** | `REPEATENDBRK` | _`n -`_ | Similar to [`REPEATEND`](#instr-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`](#instr-samealtsave) [`REPEATEND`](#instr-repeatend). | `26` | +| **`E316`** | `UNTILBRK` | _`c -`_ | Similar to [`UNTIL`](#instr-until), but also modifies `c1` in the same way as [`REPEATBRK`](#instr-repeatbrk). | `26` | +| **`E316`** | `UNTILBRK:<{ code }>` | _`-`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`UNTILBRK`](#instr-untilbrk). | | +| **`E317`** | `UNTILENDBRK`
`UNTILBRK:` | _`-`_ | Equivalent to [`SAMEALTSAVE`](#instr-samealtsave) [`UNTILEND`](#instr-untilend). | `26` | +| **`E318`** | `WHILEBRK` | _`c' c -`_ | Similar to [`WHILE`](#instr-while), but also modifies `c1` in the same way as [`REPEATBRK`](#instr-repeatbrk). | `26` | +| **`E318`** | `WHILEBRK:<{ cond }>DO<{ code }>` | _`-`_ | Equivalent to [`<{ cond }> CONT`](#instr-pushcont) [`<{ code }> CONT`](#instr-pushcont) [`WHILEBRK`](#instr-whilebrk). | | +| **`E319`** | `WHILEENDBRK` | _`c -`_ | Equivalent to [`SAMEALTSAVE`](#instr-samealtsave) [`WHILEEND`](#instr-whileend). | `26` | +| **`E31A`** | `AGAINBRK` | _`c -`_ | Similar to [`AGAIN`](#instr-again), but also modifies `c1` in the same way as [`REPEATBRK`](#instr-repeatbrk). | `26` | +| **`E31A`** | `AGAINBRK:<{ code }>` | _`-`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`AGAINBRK`](#instr-againbrk). | | +| **`E31B`** | `AGAINENDBRK`
`AGAINBRK:` | _`-`_ | Equivalent to [`SAMEALTSAVE`](#instr-samealtsave) [`AGAINEND`](#instr-againend). | `26` | + +### 8.4 Manipulating the stack of continuations + +Here `s"` is the [fee for moving stack elements between continuations](#11-gas-prices). It is equal to the size of the resulting stack minus 32 (or 0 if the stack is smaller than 32). + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :--------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`ECrn`** | `[r] [n] SETCONTARGS` | _`x_1 x_2...x_r c - c'`_ | Similar to [`[r] -1 SETCONTARGS`](#instr-setcontargs-n), 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. | `26+s”` | +| **`EC0n`** | `[n] SETNUMARGS` | _`c - c'`_ | 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. | `26` | +| **`ECrF`** | `[r] -1 SETCONTARGS` | _`x_1 x_2...x_r c - c'`_ | 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. | `26+s”` | +| **`ED0p`** | `[p] RETURNARGS` | _`-`_ | Leaves only the top `0 <= p <= 15` values in the current stack (somewhat similarly to [`ONLYTOPX`](#instr-onlytopx)), with all the unused bottom values not discarded, but saved into continuation `c0` in the same way as [`SETCONTARGS`](#instr-setcontargs-n) does. | `26+s”` | +| **`ED10`** | `RETURNVARARGS` | _`p -`_ | Similar to [`RETURNARGS`](#instr-returnargs), but with Integer `0 <= p <= 255` taken from the stack. | `26+s”` | +| **`ED11`** | `SETCONTVARARGS` | _`x_1 x_2...x_r c r n - c'`_ | Similar to [`SETCONTARGS`](#instr-setcontargs-n), but with `0 <= r <= 255` and `-1 <= n <= 255` taken from the stack. | `26+s”` | +| **`ED12`** | `SETNUMVARARGS` | _`c n - c'`_ | `-1 <= n <= 255`
If `n=-1`, this operation does nothing (`c'=c`).
Otherwise its action is similar to [`[n] SETNUMARGS`](#instr-setnumargs), but with `n` taken from the stack. | `26` | + +### 8.5 Creating simple continuations and closures + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`ED1E`** | `BLESS` | _`s - c`_ | Transforms a _Slice_ `s` into a simple ordinary continuation `c`, with `c.code=s` and an empty stack and savelist. | `26` | +| **`ED1F`** | `BLESSVARARGS` | _`x_1...x_r s r n - c`_ | Equivalent to [`ROT`](#instr-rot) [`BLESS`](#instr-bless) [`ROTREV`](#instr-rotrev) [`SETCONTVARARGS`](#instr-setcontvarargs). | `26+s”` | +| **`EErn`** | `[r] [n] BLESSARGS` | _`x_1...x_r s - c`_ | `0 <= r <= 15`, `-1 <= n <= 14`
Equivalent to [`BLESS`](#instr-bless) [`[r] [n] SETCONTARGS`](#instr-setcontargs-n).
The value of `n` is represented inside the instruction by the 4-bit integer `n mod 16`. | `26` | +| **`EE0n`** | `[n] BLESSNUMARGS` | _`s - c`_ | Also transforms a _Slice_ `s` into a _Continuation_ `c`, but sets `c.nargs` to `0 <= n <= 14`. | `26` | + +### 8.6 Operations with continuation savelists and control registers + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :----------------------------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------- | +| **`ED4i`** | `c[i] PUSHCTR`
`c[i] PUSH` | _`- x`_ | 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. | `26` | +| **`ED44`** | `c4 PUSHCTR`
`c4 PUSH` | _`- x`_ | Pushes the “global data root'' cell reference, thus enabling access to persistent smart-contract data. | `26` | +| **`ED5i`** | `c[i] POPCTR`
`c[i] POP` | _`x - `_ | 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. | `26` | +| **`ED54`** | `c4 POPCTR`
`c4 POP` | _`x -`_ | Sets the “global data root'' cell reference, thus allowing modification of persistent smart-contract data. | `26` | +| **`ED6i`** | `c[i] SETCONT`
`c[i] SETCONTCTR` | _`x c - c'`_ | 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`](#instr-setcontctr), [`POPCTR`](#instr-popctr), and [`PUSHCTR`](#instr-pushctr). | `26` | +| **`ED7i`** | `c[i] SETRETCTR` | _`x - `_ | Equivalent to [`c0 PUSHCTR`](#instr-pushctr) [`c[i] SETCONTCTR`](#instr-setcontctr) [`c0 POPCTR`](#instr-popctr). | `26` | +| **`ED8i`** | `c[i] SETALTCTR` | _`x - `_ | Equivalent to [`c1 PUSHCTR`](#instr-pushctr) [`c[i] SETCONTCTR`](#instr-setcontctr) [`c1 POPCTR`](#instr-popctr). | `26` | +| **`ED9i`** | `c[i] POPSAVE`
`c[i] POPCTRSAVE` | _`x -`_ | Similar to [`c[i] POPCTR`](#instr-popctr), but also saves the old value of `c[i]` into continuation `c0`.
Equivalent (up to exceptions) to [`c[i] SAVECTR`](#instr-save) [`c[i] POPCTR`](#instr-popctr). | `26` | +| **`EDAi`** | `c[i] SAVE`
`c[i] SAVECTR` | | 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`](#instr-pushctr) [`c[i] SETRETCTR`](#instr-setretctr). | `26` | +| **`EDBi`** | `c[i] SAVEALT`
`c[i] SAVEALTCTR` | | Similar to [`c[i] SAVE`](#instr-save), but saves the current value of `c[i]` into the savelist of `c1`, not `c0`. | `26` | +| **`EDCi`** | `c[i] SAVEBOTH`
`c[i] SAVEBOTHCTR` | | Equivalent to [`c[i] SAVE`](#instr-save) [`c[i] SAVEALT`](#instr-savealt). | `26` | +| **`EDE0`** | `PUSHCTRX` | _`i - x`_ | Similar to [`c[i] PUSHCTR`](#instr-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`. | `26` | +| **`EDE1`** | `POPCTRX` | _`x i - `_ | Similar to [`c[i] POPCTR`](#instr-popctr), but with `0 <= i <= 255` from the stack. | `26` | +| **`EDE2`** | `SETCONTCTRX` | _`x c i - c'`_ | Similar to [`c[i] SETCONTCTR`](#instr-setcontctr), but with `0 <= i <= 255` from the stack. | `26` | +| **`EDF0`** | `COMPOS`
`BOOLAND` | _`c c' - c''`_ | 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`](#instr-swap) [`c0 SETCONT`](#instr-setcontctr). | `26` | +| **`EDF1`** | `COMPOSALT`
`BOOLOR` | _`c c' - c''`_ | 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`](#instr-swap) [`c1 SETCONT`](#instr-setcontctr). | `26` | +| **`EDF2`** | `COMPOSBOTH` | _`c c' - c''`_ | 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`''. | `26` | +| **`EDF3`** | `ATEXIT` | _`c - `_ | Sets `c0` to `compose0(c, c0)`. In other words, `c` will be executed before exiting current subroutine. | `26` | +| **`EDF3`** | `ATEXIT:<{ code }>`
`<{ code }>ATEXIT` | _`-`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`ATEXIT`](#instr-atexit). | | +| **`EDF4`** | `ATEXITALT` | _`c - `_ | Sets `c1` to `compose1(c, c1)`. In other words, `c` will be executed before exiting current subroutine by its alternative return path. | `26` | +| **`EDF4`** | `ATEXITALT:<{ code }>`
`<{ code }>ATEXITALT` | _`-`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`ATEXITALT`](#instr-atexitalt). | | +| **`EDF5`** | `SETEXITALT` | _`c - `_ | Sets `c1` to `compose1(compose0(c, c0), c1)`,
In this way, a subsequent [`RETALT`](#instr-retalt) will first execute `c`, then transfer control to the original `c0`. This can be used, for instance, to exit from nested loops. | `26` | +| **`EDF6`** | `THENRET` | _`c - c'`_ | Computes `compose0(c, c0)`. | `26` | +| **`EDF7`** | `THENRETALT` | _`c - c'`_ | Computes `compose0(c, c1)` | `26` | +| **`EDF8`** | `INVERT` | _`-`_ | Interchanges `c0` and `c1`. | `26` | +| **`EDF9`** | `BOOLEVAL` | _`c - ?`_ | 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. | `26` | +| **`EDFA`** | `SAMEALT` | _`-`_ | Sets `c1` to `c0`. Equivalent to [`c0 PUSHCTR`](#instr-pushctr) [`c1 POPCTR`](#instr-popctr). | `26` | +| **`EDFB`** | `SAMEALTSAVE` | _`-`_ | Sets `c1` to `c0`, but first saves the old value of `c1` into the savelist of `c0`.
Equivalent to [`c1 SAVE`](#instr-save) [`SAMEALT`](#instr-samealt). | `26` | + +### 8.7 Dictionary subroutine calls and jumps + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`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`](#instr-pushint-4) [`c3 PUSHCTR`](#instr-pushctr) [`EXECUTE`](#instr-execute). | | +| **`F12_n`** | `[n] CALL`
`[n] CALLDICT` | _`- n`_ | For `0 <= n < 2^14`, an encoding of [`[n] CALL`](#instr-calldict) for larger values of `n`. | | +| **`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`](#instr-pushint-4) [`c3 PUSHCTR`](#instr-pushctr) [`JMPX`](#instr-jmpx). | | +| **`F1A_n`** | `[n] PREPARE`
`[n] PREPAREDICT` | _` - n c`_ | Equivalent to [`n PUSHINT`](#instr-pushint-4) [`c3 PUSHCTR`](#instr-pushctr), for `0 <= n < 2^14`.
In this way, [`[n] CALL`](#instr-calldict) is approximately equivalent to [`[n] PREPARE`](#instr-preparedict) [`EXECUTE`](#instr-execute), and [`[n] JMP`](#instr-jmpdict) is approximately equivalent to [`[n] PREPARE`](#instr-preparedict) [`JMPX`](#instr-jmpx).
One might use, for instance, [`CALLXARGS`](#instr-callxargs) or [`CALLCC`](#instr-callcc) instead of [`EXECUTE`](#instr-execute) here. | | + +## 9 Exception generating and handling primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------- | +| **`F22_n`** | `[n] THROW` | _` - 0 n`_ | 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. | `76` | +| **`F26_n`** | `[n] THROWIF` | _`f - `_ | Throws exception `0 <= n <= 63` with parameter zero only if integer `f!=0`. | `26/76` | +| **`F2A_n`** | `[n] THROWIFNOT` | _`f - `_ | Throws exception `0 <= n <= 63` with parameter zero only if integer `f=0`. | `26/76` | +| **`F2C4_n`** | `[n] THROW` | _`- 0 nn`_ | For `0 <= n < 2^11`, an encoding of [`[n] THROW`](#instr-throw-short) for larger values of `n`. | `84` | +| **`F2CC_n`** | `[n] THROWARG` | _`x - x nn`_ | Throws exception `0 <= n < 2^11` with parameter `x`, by copying `x` and `n` into the stack of `c2` and transferring control to `c2`. | `84` | +| **`F2D4_n`** | `[n] THROWIF` | _`f - `_ | For `0 <= n < 2^11`, an encoding of [`[n] THROWIF`](#instr-throwif-short) for larger values of `n`. | `34/84` | +| **`F2DC_n`** | `[n] THROWARGIF` | _`x f - `_ | Throws exception `0 <= nn < 2^11` with parameter `x` only if integer `f!=0`. | `34/84` | +| **`F2E4_n`** | `[n] THROWIFNOT` | _`f - `_ | For `0 <= n < 2^11`, an encoding of [`[n] THROWIFNOT`](#instr-throwifnot-short) for larger values of `n`. | `34/84` | +| **`F2EC_n`** | `[n] THROWARGIFNOT` | _`x f - `_ | Throws exception `0 <= n < 2^11` with parameter `x` only if integer `f=0`. | `34/84` | +| **`F2F0`** | `THROWANY` | _`n - 0 n`_ | Throws exception `0 <= n < 2^16` with parameter zero.
Approximately equivalent to [`ZERO`](#instr-zero) [`SWAP`](#instr-swap) [`THROWARGANY`](#instr-throwargany). | `76` | +| **`F2F1`** | `THROWARGANY` | _`x n - x n`_ | Throws exception `0 <= n < 2^16` with parameter `x`, transferring control to the continuation in `c2`.
Approximately equivalent to [`c2 PUSHCTR`](#instr-pushctr) [`2 JMPXARGS`](#instr-jmpxargs). | `76` | +| **`F2F2`** | `THROWANYIF` | _`n f - `_ | Throws exception `0 <= n < 2^16` with parameter zero only if `f!=0`. | `26/76` | +| **`F2F3`** | `THROWARGANYIF` | _`x n f - `_ | Throws exception `0 <= n<2^16` with parameter `x` only if `f!=0`. | `26/76` | +| **`F2F4`** | `THROWANYIFNOT` | _`n f - `_ | Throws exception `0 <= n<2^16` with parameter zero only if `f=0`. | `26/76` | +| **`F2F5`** | `THROWARGANYIFNOT` | _`x n f - `_ | Throws exception `0 <= n<2^16` with parameter `x` only if `f=0`. | `26/76` | +| **`F2FF`** | `TRY` | _`c c' - `_ | 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`](#instr-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`](#instr-throwany) if it cannot handle it by itself. | `26` | +| **`F2FF`** | `TRY:<{ code1 }>CATCH<{ code2 }>` | _`-`_ | Equivalent to [`<{ code1 }> CONT`](#instr-pushcont) [`<{ code2 }> CONT`](#instr-pushcont) [`TRY`](#instr-try). | | +| **`F3pr`** | `[p] [r] TRYARGS` | _`c c' - `_ | Similar to [`TRY`](#instr-try), but with [`[p] [r] CALLXARGS`](#instr-callxargs) internally used instead of [`EXECUTE`](#instr-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. | `26` | + +## 10 Dictionary manipulation primitives + +The gas consumption of most dictionary operations is not fixed, it depends on the contents of the given dictionary. + +### 10.1 Dictionary creation + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`6D`** | `NEWDICT` | _` - D`_ | Returns a new empty dictionary.
It is an alternative mnemonics for [`PUSHNULL`](#instr-null). | `18` | +| **`6E`** | `DICTEMPTY` | _`D - ?`_ | Checks whether dictionary `D` is empty, and returns `-1` or `0` accordingly.
It is an alternative mnemonics for [`ISNULL`](#instr-isnull). | `18` | + +### 10.2 Dictionary serialization and deserialization + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`CE`** | `STDICTS`
\`\` | _`s b - b'`_ | Stores a _Slice_-represented dictionary `s` into _Builder_ `b`.
It is actually a synonym for [`STSLICE`](#instr-stslice). | `18` | +| **`F400`** | `STDICT`
`STOPTREF` | _`D b - b'`_ | Stores dictionary `D` into _Builder_ `b`, returing the resulting _Builder_ `b'`.
In other words, if `D` is a cell, performs [`STONE`](#instr-stone) and [`STREF`](#instr-stref); if `D` is _Null_, performs [`NIP`](#instr-nip) and [`STZERO`](#instr-stzero); otherwise throws a type checking exception. | `26` | +| **`F401`** | `SKIPDICT`
`SKIPOPTREF` | _`s - s'`_ | Equivalent to [`LDDICT`](#instr-lddict) [`NIP`](#instr-nip). | `26` | +| **`F402`** | `LDDICTS` | _`s - s' s''`_ | 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. | `26` | +| **`F403`** | `PLDDICTS` | _`s - s'`_ | Preloads a (_Slice_-represented) dictionary `s'` from _Slice_ `s`.
Approximately equivalent to [`LDDICTS`](#instr-lddicts) [`DROP`](#instr-drop). | `26` | +| **`F404`** | `LDDICT`
`LDOPTREF` | _`s - D s'`_ | 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. | `26` | +| **`F405`** | `PLDDICT`
`PLDOPTREF` | _`s - D`_ | Preloads a dictionary `D` from _Slice_ `s`.
Approximately equivalent to [`LDDICT`](#instr-lddict) [`DROP`](#instr-drop). | `26` | +| **`F406`** | `LDDICTQ` | _`s - D s' -1 or s 0`_ | A quiet version of [`LDDICT`](#instr-lddict). | `26` | +| **`F407`** | `PLDDICTQ` | _`s - D -1 or 0`_ | A quiet version of [`PLDDICT`](#instr-plddict). | `26` | + +### 10.3 Get dictionary operations + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`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`. | | +| **`F40B`** | `DICTGETREF` | _`k D n - c -1 or 0`_ | Similar to [`DICTGET`](#instr-dictget), but with a [`LDREF`](#instr-ldref) [`ENDS`](#instr-ends) applied to `x` on success.
This operation is useful for dictionaries of type `HashmapE(n,^Y)`. | | +| **`F40C`** | `DICTIGET` | _`i D n - x -1 or 0`_ | Similar to [`DICTGET`](#instr-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. | | +| **`F40D`** | `DICTIGETREF` | _`i D n - c -1 or 0`_ | Combines [`DICTIGET`](#instr-dictiget) with [`DICTGETREF`](#instr-dictgetref): it uses signed `n`-bit _Integer_ `i` as a key and returns a _Cell_ instead of a _Slice_ on success. | | +| **`F40E`** | `DICTUGET` | _`i D n - x -1 or 0`_ | Similar to [`DICTIGET`](#instr-dictiget), but with _unsigned_ (big-endian) `n`-bit _Integer_ `i` used as a key. | | +| **`F40F`** | `DICTUGETREF` | _`i D n - c -1 or 0`_ | Similar to [`DICTIGETREF`](#instr-dictigetref), but with an unsigned `n`-bit _Integer_ key `i`. | | + +### 10.4 Set/Replace/Add dictionary operations + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :----------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`F412`** | `DICTSET` | _`x k D n - D'`_ | Sets the value associated with `n`-bit key `k` (represented by a _Slice_ as in [`DICTGET`](#instr-dictget)) in dictionary `D` (also represented by a _Slice_) to value `x` (again a _Slice_), and returns the resulting dictionary as `D'`. | | +| **`F413`** | `DICTSETREF` | _`c k D n - D'`_ | Similar to [`DICTSET`](#instr-dictset), but with the value set to a reference to _Cell_ `c`. | | +| **`F414`** | `DICTISET` | _`x i D n - D'`_ | Similar to [`DICTSET`](#instr-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. | | +| **`F415`** | `DICTISETREF` | _`c i D n - D'`_ | Similar to [`DICTSETREF`](#instr-dictsetref), but with the key a signed `n`-bit integer as in [`DICTISET`](#instr-dictiset). | | +| **`F416`** | `DICTUSET` | _`x i D n - D'`_ | Similar to [`DICTISET`](#instr-dictiset), but with `i` an _unsigned_ `n`-bit integer. | | +| **`F417`** | `DICTUSETREF` | _`c i D n - D'`_ | Similar to [`DICTISETREF`](#instr-dictisetref), but with `i` unsigned. | | +| **`F41A`** | `DICTSETGET` | _`x k D n - D' y -1 or D' 0`_ | Combines [`DICTSET`](#instr-dictset) with [`DICTGET`](#instr-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. | | +| **`F41B`** | `DICTSETGETREF` | _`c k D n - D' c' -1 or D' 0`_ | Combines [`DICTSETREF`](#instr-dictsetref) with [`DICTGETREF`](#instr-dictgetref) similarly to [`DICTSETGET`](#instr-dictsetget). | | +| **`F41C`** | `DICTISETGET` | _`x i D n - D' y -1 or D' 0`_ | [`DICTISETGET`](#instr-dictisetget), but with `i` a signed `n`-bit integer. | | +| **`F41D`** | `DICTISETGETREF` | _`c i D n - D' c' -1 or D' 0`_ | [`DICTISETGETREF`](#instr-dictisetgetref), but with `i` a signed `n`-bit integer. | | +| **`F41E`** | `DICTUSETGET` | _`x i D n - D' y -1 or D' 0`_ | [`DICTISETGET`](#instr-dictisetget), but with `i` an unsigned `n`-bit integer. | | +| **`F41F`** | `DICTUSETGETREF` | _`c i D n - D' c' -1 or D' 0`_ | [`DICTISETGETREF`](#instr-dictisetgetref), but with `i` an unsigned `n`-bit integer. | | +| **`F422`** | `DICTREPLACE` | _`x k D n - D' -1 or D 0`_ | A _Replace_ operation, which is similar to [`DICTSET`](#instr-dictset), but sets the value of key `k` in dictionary `D` to `x` only if the key `k` was already present in `D`. | | +| **`F423`** | `DICTREPLACEREF` | _`c k D n - D' -1 or D 0`_ | A _Replace_ counterpart of [`DICTSETREF`](#instr-dictsetref). | | +| **`F424`** | `DICTIREPLACE` | _`x i D n - D' -1 or D 0`_ | [`DICTREPLACE`](#instr-dictreplace), but with `i` a signed `n`-bit integer. | | +| **`F425`** | `DICTIREPLACEREF` | _`c i D n - D' -1 or D 0`_ | [`DICTREPLACEREF`](#instr-dictreplaceref), but with `i` a signed `n`-bit integer. | | +| **`F426`** | `DICTUREPLACE` | _`x i D n - D' -1 or D 0`_ | [`DICTREPLACE`](#instr-dictreplace), but with `i` an unsigned `n`-bit integer. | | +| **`F427`** | `DICTUREPLACEREF` | _`c i D n - D' -1 or D 0`_ | [`DICTREPLACEREF`](#instr-dictreplaceref), but with `i` an unsigned `n`-bit integer. | | +| **`F42A`** | `DICTREPLACEGET` | _`x k D n - D' y -1 or D 0`_ | A _Replace_ counterpart of [`DICTSETGET`](#instr-dictsetget): on success, also returns the old value associated with the key in question. | | +| **`F42B`** | `DICTREPLACEGETREF` | _`c k D n - D' c' -1 or D 0`_ | A _Replace_ counterpart of [`DICTSETGETREF`](#instr-dictsetgetref). | | +| **`F42C`** | `DICTIREPLACEGET` | _`x i D n - D' y -1 or D 0`_ | [`DICTREPLACEGET`](#instr-dictreplaceget), but with `i` a signed `n`-bit integer. | | +| **`F42D`** | `DICTIREPLACEGETREF` | _`c i D n - D' c' -1 or D 0`_ | [`DICTREPLACEGETREF`](#instr-dictreplacegetref), but with `i` a signed `n`-bit integer. | | +| **`F42E`** | `DICTUREPLACEGET` | _`x i D n - D' y -1 or D 0`_ | [`DICTREPLACEGET`](#instr-dictreplaceget), but with `i` an unsigned `n`-bit integer. | | +| **`F42F`** | `DICTUREPLACEGETREF` | _`c i D n - D' c' -1 or D 0`_ | [`DICTREPLACEGETREF`](#instr-dictreplacegetref), but with `i` an unsigned `n`-bit integer. | | +| **`F432`** | `DICTADD` | _`x k D n - D' -1 or D 0`_ | An _Add_ counterpart of [`DICTSET`](#instr-dictset): sets the value associated with key `k` in dictionary `D` to `x`, but only if it is not already present in `D`. | | +| **`F433`** | `DICTADDREF` | _`c k D n - D' -1 or D 0`_ | An _Add_ counterpart of [`DICTSETREF`](#instr-dictsetref). | | +| **`F434`** | `DICTIADD` | _`x i D n - D' -1 or D 0`_ | [`DICTADD`](#instr-dictadd), but with `i` a signed `n`-bit integer. | | +| **`F435`** | `DICTIADDREF` | _`c i D n - D' -1 or D 0`_ | [`DICTADDREF`](#instr-dictaddref), but with `i` a signed `n`-bit integer. | | +| **`F436`** | `DICTUADD` | _`x i D n - D' -1 or D 0`_ | [`DICTADD`](#instr-dictadd), but with `i` an unsigned `n`-bit integer. | | +| **`F437`** | `DICTUADDREF` | _`c i D n - D' -1 or D 0`_ | [`DICTADDREF`](#instr-dictaddref), but with `i` an unsigned `n`-bit integer. | | +| **`F43A`** | `DICTADDGET` | _`x k D n - D' -1 or D y 0`_ | An _Add_ counterpart of [`DICTSETGET`](#instr-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. | | +| **`F43B`** | `DICTADDGETREF` | _`c k D n - D' -1 or D c' 0`_ | An _Add_ counterpart of [`DICTSETGETREF`](#instr-dictsetgetref). | | +| **`F43C`** | `DICTIADDGET` | _`x i D n - D' -1 or D y 0`_ | [`DICTADDGET`](#instr-dictaddget), but with `i` a signed `n`-bit integer. | | +| **`F43D`** | `DICTIADDGETREF` | _`c i D n - D' -1 or D c' 0`_ | [`DICTADDGETREF`](#instr-dictaddgetref), but with `i` a signed `n`-bit integer. | | +| **`F43E`** | `DICTUADDGET` | _`x i D n - D' -1 or D y 0`_ | [`DICTADDGET`](#instr-dictaddget), but with `i` an unsigned `n`-bit integer. | | +| **`F43F`** | `DICTUADDGETREF` | _`c i D n - D' -1 or D c' 0`_ | [`DICTADDGETREF`](#instr-dictaddgetref), but with `i` an unsigned `n`-bit integer. | | + +### 10.5 Builder-accepting variants of Set dictionary operations + +The following primitives accept the new value as a _Builder_ `b` instead of a _Slice_ `x`. + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :---------------------------- | :----------------------------------------------------- | :----------- | +| **`F441`** | `DICTSETB` | _`b k D n - D'`_ | | | +| **`F442`** | `DICTISETB` | _`b i D n - D'`_ | | | +| **`F443`** | `DICTUSETB` | _`b i D n - D'`_ | | | +| **`F445`** | `DICTSETGETB` | _`b k D n - D' y -1 or D' 0`_ | | | +| **`F446`** | `DICTISETGETB` | _`b i D n - D' y -1 or D' 0`_ | | | +| **`F447`** | `DICTUSETGETB` | _`b i D n - D' y -1 or D' 0`_ | | | +| **`F449`** | `DICTREPLACEB` | _`b k D n - D' -1 or D 0`_ | | | +| **`F44A`** | `DICTIREPLACEB` | _`b i D n - D' -1 or D 0`_ | | | +| **`F44B`** | `DICTUREPLACEB` | _`b i D n - D' -1 or D 0`_ | | | +| **`F44D`** | `DICTREPLACEGETB` | _`b k D n - D' y -1 or D 0`_ | | | +| **`F44E`** | `DICTIREPLACEGETB` | _`b i D n - D' y -1 or D 0`_ | | | +| **`F44F`** | `DICTUREPLACEGETB` | _`b i D n - D' y -1 or D 0`_ | | | +| **`F451`** | `DICTADDB` | _`b k D n - D' -1 or D 0`_ | | | +| **`F452`** | `DICTIADDB` | _`b i D n - D' -1 or D 0`_ | | | +| **`F453`** | `DICTUADDB` | _`b i D n - D' -1 or D 0`_ | | | +| **`F455`** | `DICTADDGETB` | _`b k D n - D' -1 or D y 0`_ | | | +| **`F456`** | `DICTIADDGETB` | _`b i D n - D' -1 or D y 0`_ | | | +| **`F457`** | `DICTUADDGETB` | _`b i D n - D' -1 or D y 0`_ | | | + +### 10.6 Delete dictionary operations + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------- | +| **`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`. | | +| **`F45A`** | `DICTIDEL` | _`i D n - D' ?`_ | A version of [`DICTDEL`](#instr-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''). | | +| **`F45B`** | `DICTUDEL` | _`i D n - D' ?`_ | Similar to [`DICTIDEL`](#instr-dictidel), but with `i` an unsigned `n`-bit integer. | | +| **`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`. | | +| **`F463`** | `DICTDELGETREF` | _`k D n - D' c -1 or D 0`_ | Similar to [`DICTDELGET`](#instr-dictdelget), but with [`LDREF`](#instr-ldref) [`ENDS`](#instr-ends) applied to `x` on success, so that the value returned `c` is a _Cell_. | | +| **`F464`** | `DICTIDELGET` | _`i D n - D' x -1 or D 0`_ | [`DICTDELGET`](#instr-dictdelget), but with `i` a signed `n`-bit integer. | | +| **`F465`** | `DICTIDELGETREF` | _`i D n - D' c -1 or D 0`_ | [`DICTDELGETREF`](#instr-dictdelgetref), but with `i` a signed `n`-bit integer. | | +| **`F466`** | `DICTUDELGET` | _`i D n - D' x -1 or D 0`_ | [`DICTDELGET`](#instr-dictdelget), but with `i` an unsigned `n`-bit integer. | | +| **`F467`** | `DICTUDELGETREF` | _`i D n - D' c -1 or D 0`_ | [`DICTDELGETREF`](#instr-dictdelgetref), but with `i` an unsigned `n`-bit integer. | | + +### 10.7 "Maybe reference" dictionary operations + +The following operations assume that a dictionary is used to store values `c?` of type _Maybe Cell_. The representation is as follows: if `c?` is a _Cell_ , it is stored as a value with no data bits and exactly one reference to this _Cell_. If `c?` is _Null_, then the corresponding key must be absent from the dictionary. + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`F469`** | `DICTGETOPTREF` | _`k D n - c^?`_ | A variant of [`DICTGETREF`](#instr-dictgetref) that returns _Null_ instead of the value `c^?` if the key `k` is absent from dictionary `D`. | | +| **`F46A`** | `DICTIGETOPTREF` | _`i D n - c^?`_ | [`DICTGETOPTREF`](#instr-dictgetoptref), but with `i` a signed `n`-bit integer. If the key `i` is out of range, also returns _Null_. | | +| **`F46B`** | `DICTUGETOPTREF` | _`i D n - c^?`_ | [`DICTGETOPTREF`](#instr-dictgetoptref), but with `i` an unsigned `n`-bit integer. If the key `i` is out of range, also returns _Null_. | | +| **`F46D`** | `DICTSETGETOPTREF` | _`c^? k D n - D' ~c^?`_ | A variant of both [`DICTGETOPTREF`](#instr-dictgetoptref) and [`DICTSETGETREF`](#instr-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). | | +| **`F46E`** | `DICTISETGETOPTREF` | _`c^? i D n - D' ~c^?`_ | Similar to primitive [`DICTSETGETOPTREF`](#instr-dictsetgetoptref), but using signed `n`-bit _Integer_ `i` as a key. If `i` does not fit into `n` bits, throws a range checking exception. | | +| **`F46F`** | `DICTUSETGETOPTREF` | _`c^? i D n - D' ~c^?`_ | Similar to primitive [`DICTSETGETOPTREF`](#instr-dictsetgetoptref), but using unsigned `n`-bit _Integer_ `i` as a key. | | + +### 10.8 Prefix code dictionary operations + +These are some basic operations for constructing prefix code dictionaries. +These primitives are completely similar to their non-prefix code counterparts ([`DICTSET`](#instr-dictset) etc), with the obvious difference that even a _Set_ may fail in a prefix code dictionary, so a success flag must be returned by [`PFXDICTSET`](#instr-pfxdictset) as well. + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :----------------------------------------------------- | :----------- | +| **`F470`** | `PFXDICTSET` | _`x k D n - D' -1 or D 0`_ | | | +| **`F471`** | `PFXDICTREPLACE` | _`x k D n - D' -1 or D 0`_ | | | +| **`F472`** | `PFXDICTADD` | _`x k D n - D' -1 or D 0`_ | | | +| **`F473`** | `PFXDICTDEL` | _`k D n - D' -1 or D 0`_ | | | + +### 10.9 Variants of GetNext and GetPrev operations + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`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_). | | +| **`F475`** | `DICTGETNEXTEQ` | _`k D n - x' k' -1 or 0`_ | Similar to [`DICTGETNEXT`](#instr-dictgetnext), but computes the minimal key `k'` that is lexicographically greater than or equal to `k`. | | +| **`F476`** | `DICTGETPREV` | _`k D n - x' k' -1 or 0`_ | Similar to [`DICTGETNEXT`](#instr-dictgetnext), but computes the maximal key `k'` lexicographically smaller than `k`. | | +| **`F477`** | `DICTGETPREVEQ` | _`k D n - x' k' -1 or 0`_ | Similar to [`DICTGETPREV`](#instr-dictgetprev), but computes the maximal key `k'` lexicographically smaller than or equal to `k`. | | +| **`F478`** | `DICTIGETNEXT` | _`i D n - x' i' -1 or 0`_ | Similar to [`DICTGETNEXT`](#instr-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). | | +| **`F479`** | `DICTIGETNEXTEQ` | _`i D n - x' i' -1 or 0`_ | Similar to [`DICTGETNEXTEQ`](#instr-dictgetnexteq), but interprets keys as signed `n`-bit integers. | | +| **`F47A`** | `DICTIGETPREV` | _`i D n - x' i' -1 or 0`_ | Similar to [`DICTGETPREV`](#instr-dictgetprev), but interprets keys as signed `n`-bit integers. | | +| **`F47B`** | `DICTIGETPREVEQ` | _`i D n - x' i' -1 or 0`_ | Similar to [`DICTGETPREVEQ`](#instr-dictgetpreveq), but interprets keys as signed `n`-bit integers. | | +| **`F47C`** | `DICTUGETNEXT` | _`i D n - x' i' -1 or 0`_ | Similar to [`DICTGETNEXT`](#instr-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). | | +| **`F47D`** | `DICTUGETNEXTEQ` | _`i D n - x' i' -1 or 0`_ | Similar to [`DICTGETNEXTEQ`](#instr-dictgetnexteq), but interprets keys as unsigned `n`-bit integers. | | +| **`F47E`** | `DICTUGETPREV` | _`i D n - x' i' -1 or 0`_ | Similar to [`DICTGETPREV`](#instr-dictgetprev), but interprets keys as unsigned `n`-bit integers. | | +| **`F47F`** | `DICTUGETPREVEQ` | _`i D n - x' i' -1 or 0`_ | Similar to [`DICTGETPREVEQ`](#instr-dictgetpreveq), but interprets keys a unsigned `n`-bit integers. | | + +### 10.10 GetMin, GetMax, RemoveMin, RemoveMax operations + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------- | +| **`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`. | | +| **`F483`** | `DICTMINREF` | _`D n - c k -1 or 0`_ | Similar to [`DICTMIN`](#instr-dictmin), but returns the only reference in the value as a _Cell_ `c`. | | +| **`F484`** | `DICTIMIN` | _`D n - x i -1 or 0`_ | Similar to [`DICTMIN`](#instr-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`](#instr-dictmin) and [`DICTUMIN`](#instr-dictumin). | | +| **`F485`** | `DICTIMINREF` | _`D n - c i -1 or 0`_ | Similar to [`DICTIMIN`](#instr-dictimin), but returns the only reference in the value. | | +| **`F486`** | `DICTUMIN` | _`D n - x i -1 or 0`_ | Similar to [`DICTMIN`](#instr-dictmin), but returns the key as an unsigned `n`-bit _Integer_ `i`. | | +| **`F487`** | `DICTUMINREF` | _`D n - c i -1 or 0`_ | Similar to [`DICTUMIN`](#instr-dictumin), but returns the only reference in the value. | | +| **`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`. | | +| **`F48B`** | `DICTMAXREF` | _`D n - c k -1 or 0`_ | Similar to [`DICTMAX`](#instr-dictmax), but returns the only reference in the value. | | +| **`F48C`** | `DICTIMAX` | _`D n - x i -1 or 0`_ | Similar to [`DICTMAX`](#instr-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`](#instr-dictmax) and [`DICTUMAX`](#instr-dictumax). | | +| **`F48D`** | `DICTIMAXREF` | _`D n - c i -1 or 0`_ | Similar to [`DICTIMAX`](#instr-dictimax), but returns the only reference in the value. | | +| **`F48E`** | `DICTUMAX` | _`D n - x i -1 or 0`_ | Similar to [`DICTMAX`](#instr-dictmax), but returns the key as an unsigned `n`-bit _Integer_ `i`. | | +| **`F48F`** | `DICTUMAXREF` | _`D n - c i -1 or 0`_ | Similar to [`DICTUMAX`](#instr-dictumax), but returns the only reference in the value. | | +| **`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'`. | | +| **`F493`** | `DICTREMMINREF` | _`D n - D' c k -1 or D 0`_ | Similar to [`DICTREMMIN`](#instr-dictremmin), but returns the only reference in the value as a _Cell_ `c`. | | +| **`F494`** | `DICTIREMMIN` | _`D n - D' x i -1 or D 0`_ | Similar to [`DICTREMMIN`](#instr-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`](#instr-dictremmin) and [`DICTUREMMIN`](#instr-dicturemmin). | | +| **`F495`** | `DICTIREMMINREF` | _`D n - D' c i -1 or D 0`_ | Similar to [`DICTIREMMIN`](#instr-dictiremmin), but returns the only reference in the value. | | +| **`F496`** | `DICTUREMMIN` | _`D n - D' x i -1 or D 0`_ | Similar to [`DICTREMMIN`](#instr-dictremmin), but returns the key as an unsigned `n`-bit _Integer_ `i`. | | +| **`F497`** | `DICTUREMMINREF` | _`D n - D' c i -1 or D 0`_ | Similar to [`DICTUREMMIN`](#instr-dicturemmin), but returns the only reference in the value. | | +| **`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'`. | | +| **`F49B`** | `DICTREMMAXREF` | _`D n - D' c k -1 or D 0`_ | Similar to [`DICTREMMAX`](#instr-dictremmax), but returns the only reference in the value as a _Cell_ `c`. | | +| **`F49C`** | `DICTIREMMAX` | _`D n - D' x i -1 or D 0`_ | Similar to [`DICTREMMAX`](#instr-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`](#instr-dictremmax) and [`DICTUREMMAX`](#instr-dicturemmax). | | +| **`F49D`** | `DICTIREMMAXREF` | _`D n - D' c i -1 or D 0`_ | Similar to [`DICTIREMMAX`](#instr-dictiremmax), but returns the only reference in the value. | | +| **`F49E`** | `DICTUREMMAX` | _`D n - D' x i -1 or D 0`_ | Similar to [`DICTREMMAX`](#instr-dictremmax), but returns the key as an unsigned `n`-bit _Integer_ `i`. | | +| **`F49F`** | `DICTUREMMAXREF` | _`D n - D' c i -1 or D 0`_ | Similar to [`DICTUREMMAX`](#instr-dicturemmax), but returns the only reference in the value. | | + +### 10.11 Special Get dictionary and prefix code dictionary operations and constant dictionaries + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :----------------------------------------------------------- | :----------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`F4A0`** | `DICTIGETJMP` | _`i D n - `_ | Similar to [`DICTIGET`](#instr-dictiget), but with `x` [`BLESS`](#instr-bless)ed into a continuation with a subsequent [`JMPX`](#instr-jmpx) to it on success. On failure, does nothing. This is useful for implementing `switch`/`case` constructions. | | +| **`F4A1`** | `DICTUGETJMP` | _`i D n - `_ | Similar to [`DICTIGETJMP`](#instr-dictigetjmp), but performs [`DICTUGET`](#instr-dictuget) instead of [`DICTIGET`](#instr-dictiget). | | +| **`F4A2`** | `DICTIGETEXEC` | _`i D n - `_ | Similar to [`DICTIGETJMP`](#instr-dictigetjmp), but with [`EXECUTE`](#instr-execute) instead of [`JMPX`](#instr-jmpx). | | +| **`F4A3`** | `DICTUGETEXEC` | _`i D n - `_ | Similar to [`DICTUGETJMP`](#instr-dictugetjmp), but with [`EXECUTE`](#instr-execute) instead of [`JMPX`](#instr-jmpx). | | +| **`F4A6_n`** | `[ref] [n] DICTPUSHCONST` | _` - D n`_ | 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`](#instr-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`](#instr-newdict) primitive instead. | `34` | +| **`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. | | +| **`F4A9`** | `PFXDICTGET` | _`s D n - s' x s''`_ | Similar to [`PFXDICTGET`](#instr-pfxdictget), but throws a cell deserialization failure exception on failure. | | +| **`F4AA`** | `PFXDICTGETJMP` | _`s D n - s' s'' or s`_ | Similar to [`PFXDICTGETQ`](#instr-pfxdictgetq), but on success [`BLESS`](#instr-bless)es the value `x` into a _Continuation_ and transfers control to it as if by a [`JMPX`](#instr-jmpx). On failure, returns `s` unchanged and continues execution. | | +| **`F4AB`** | `PFXDICTGETEXEC` | _`s D n - s' s''`_ | Similar to [`PFXDICTGETJMP`](#instr-pfxdictgetjmp), but `EXEC`utes the continuation found instead of jumping to it. On failure, throws a cell deserialization exception. | | +| **`F4AE_n`** | `[ref] [n] PFXDICTCONSTGETJMP`
`[ref] [n] PFXDICTSWITCH` | _`s - s' s'' or s`_ | Combines [`[n] DICTPUSHCONST`](#instr-dictpushconst) for `0 <= n <= 1023` with [`PFXDICTGETJMP`](#instr-pfxdictgetjmp). | | +| **`F4BC`** | `DICTIGETJMPZ` | _`i D n - i or nothing`_ | A variant of [`DICTIGETJMP`](#instr-dictigetjmp) that returns index `i` on failure. | | +| **`F4BD`** | `DICTUGETJMPZ` | _`i D n - i or nothing`_ | A variant of [`DICTUGETJMP`](#instr-dictugetjmp) that returns index `i` on failure. | | +| **`F4BE`** | `DICTIGETEXECZ` | _`i D n - i or nothing`_ | A variant of [`DICTIGETEXEC`](#instr-dictigetexec) that returns index `i` on failure. | | +| **`F4BF`** | `DICTUGETEXECZ` | _`i D n - i or nothing`_ | A variant of [`DICTUGETEXEC`](#instr-dictugetexec) that returns index `i` on failure. | | + +### 10.12 SubDict dictionary operations + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`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'`. | | +| **`F4B2`** | `SUBDICTIGET` | _`x l D n - D'`_ | Variant of [`SUBDICTGET`](#instr-subdictget) with the prefix represented by a signed big-endian `l`-bit _Integer_ `x`, where necessarily `l <= 257`. | | +| **`F4B3`** | `SUBDICTUGET` | _`x l D n - D'`_ | Variant of [`SUBDICTGET`](#instr-subdictget) with the prefix represented by an unsigned big-endian `l`-bit _Integer_ `x`, where necessarily `l <= 256`. | | +| **`F4B5`** | `SUBDICTRPGET` | _`k l D n - D'`_ | Similar to [`SUBDICTGET`](#instr-subdictget), but removes the common prefix `k` from all keys of the new dictionary `D'`, which becomes of type `HashmapE(n-l,X)`. | | +| **`F4B6`** | `SUBDICTIRPGET` | _`x l D n - D'`_ | Variant of [`SUBDICTRPGET`](#instr-subdictrpget) with the prefix represented by a signed big-endian `l`-bit _Integer_ `x`, where necessarily `l <= 257`. | | +| **`F4B7`** | `SUBDICTURPGET` | _`x l D n - D'`_ | Variant of [`SUBDICTRPGET`](#instr-subdictrpget) with the prefix represented by an unsigned big-endian `l`-bit _Integer_ `x`, where necessarily `l <= 256`. | | + +## 11 Application-specific primitives + +### 11.1 Gas-related primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`F800`** | `ACCEPT` | _`-`_ | 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. | `26` | +| **`F801`** | `SETGASLIMIT` | _`g - `_ | 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`](#instr-setgaslimit) with an argument `g >= 2^63-1` is equivalent to [`ACCEPT`](#instr-accept). | `26` | +| **`F80F`** | `COMMIT` | _`-`_ | 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. | `26` | + +### 11.2 Pseudo-random number generator primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------- | +| **`F810`** | `RANDU256` | _`- x`_ | 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`. | `26+\|c7\|+\|c1_1\|` | +| **`F811`** | `RAND` | _`y - z`_ | 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`](#instr-randu256) [`256 MULRSHIFT`](#instr-mulrshift-var). | `26+\|c7\|+\|c1_1\|` | +| **`F814`** | `SETRAND` | _`x - `_ | Sets the random seed to unsigned 256-bit _Integer_ `x`. | `26+\|c7\|+\|c1_1\|` | +| **`F815`** | `ADDRAND`
`RANDOMIZE` | _`x - `_ | 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`. | `26` | + +### 11.3 Configuration primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------- | +| **`F82i`** | `[i] GETPARAM` | _` - x`_ | Returns the `i`-th parameter from the _Tuple_ provided at `c7` for `0 <= i <= 15`. Equivalent to [`c7 PUSHCTR`](#instr-pushctr) [`FIRST`](#instr-first) [`[i] INDEX`](#instr-index).
If one of these internal operations fails, throws an appropriate type checking or range checking exception. | `26` | +| **`F823`** | `NOW` | _` - x`_ | 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`](#instr-getparam). | `26` | +| **`F824`** | `BLOCKLT` | _` - x`_ | Returns the starting logical time of the current block.
Equivalent to [`4 GETPARAM`](#instr-getparam). | `26` | +| **`F825`** | `LTIME` | _` - x`_ | Returns the logical time of the current transaction.
Equivalent to [`5 GETPARAM`](#instr-getparam). | `26` | +| **`F826`** | `RANDSEED` | _` - x`_ | Returns the current random seed as an unsigned 256-bit _Integer_.
Equivalent to [`6 GETPARAM`](#instr-getparam). | `26` | +| **`F827`** | `BALANCE` | _` - t`_ | 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`](#instr-getparam).
Note that `RAW` primitives such as [`SENDRAWMSG`](#instr-sendrawmsg) do not update this field. | `26` | +| **`F828`** | `MYADDR` | _` - s`_ | 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`](#instr-parsemsgaddr) or [`REWRITESTDADDR`](#instr-rewritestdaddr).
Equivalent to [`8 GETPARAM`](#instr-getparam). | `26` | +| **`F829`** | `CONFIGROOT` | _` - D`_ | Returns the _Maybe Cell_ `D` with the current global configuration dictionary. Equivalent to `9 GETPARAM `. | `26` | +| **`F830`** | `CONFIGDICT` | _` - D 32`_ | Returns the global configuration dictionary along with its key length (32).
Equivalent to [`CONFIGROOT`](#instr-configroot) [`32 PUSHINT`](#instr-pushint-4). | `26` | +| **`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`](#instr-configdict) [`DICTIGETREF`](#instr-dictigetref). | | +| **`F833`** | `CONFIGOPTPARAM` | _`i - c^?`_ | Returns the value of the global configuration parameter with integer index `i` as a _Maybe Cell_ `c^?`.
Equivalent to [`CONFIGDICT`](#instr-configdict) [`DICTIGETOPTREF`](#instr-dictigetoptref). | | + +### 11.4 Global variable primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`F840`** | `GETGLOBVAR` | _`k - x`_ | Returns the `k`-th global variable for `0 <= k < 255`.
Equivalent to [`c7 PUSHCTR`](#instr-pushctr) [`SWAP`](#instr-swap) [`INDEXVARQ`](#instr-indexvarq). | `26` | +| **`F85_k`** | `[k] GETGLOB` | _` - x`_ | Returns the `k`-th global variable for `1 <= k <= 31`.
Equivalent to [`c7 PUSHCTR`](#instr-pushctr) [`[k] INDEXQ`](#instr-indexq). | `26` | +| **`F860`** | `SETGLOBVAR` | _`x k - `_ | Assigns `x` to the `k`-th global variable for `0 <= k < 255`.
Equivalent to [`c7 PUSHCTR`](#instr-pushctr) [`ROTREV`](#instr-rotrev) [`SETINDEXVARQ`](#instr-setindexvarq) [`c7 POPCTR`](#instr-popctr). | `26+\|c7’\|` | +| **`F87_k`** | `[k] SETGLOB` | _`x - `_ | Assigns `x` to the `k`-th global variable for `1 <= k <= 31`.
Equivalent to [`c7 PUSHCTR`](#instr-pushctr) [`SWAP`](#instr-swap) [`k SETINDEXQ`](#instr-setindexq) [`c7 POPCTR`](#instr-popctr). | `26+\|c7’\|` | + +### 11.5 Hashing and cryptography primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------- | +| **`F900`** | `HASHCU` | _`c - x`_ | 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. | `26` | +| **`F901`** | `HASHSU` | _`s - x`_ | 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`](#instr-hashcu). | `526` | +| **`F902`** | `SHA256U` | _`s - x`_ | 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`. | `26` | +| **`F910`** | `CHKSIGNU` | _`h s k - ?`_ | 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`](#instr-chksignu) is equivalent to [`ROT`](#instr-rot) [`NEWC`](#instr-newc) [`256 STU`](#instr-stu) [`ENDC`](#instr-endc) [`ROTREV`](#instr-rotrev) [`CHKSIGNS`](#instr-chksigns), i.e., to [`CHKSIGNS`](#instr-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`](#instr-chksigns). | `26` | +| **`F911`** | `CHKSIGNS` | _`d s k - ?`_ | Checks whether `s` is a valid Ed25519-signature of the data portion of _Slice_ `d` using public key `k`, similarly to [`CHKSIGNU`](#instr-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. | `26` | + +### 11.6 Miscellaneous primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`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`. | | +| **`F941`** | `CDATASIZE` | _`c n - x y z`_ | A non-quiet version of [`CDATASIZEQ`](#instr-cdatasizeq) that throws a cell overflow exception (8) on failure. | | +| **`F942`** | `SDATASIZEQ` | _`s n - x y z -1 or 0`_ | Similar to [`CDATASIZEQ`](#instr-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`. | | +| **`F943`** | `SDATASIZE` | _`s n - x y z`_ | A non-quiet version of [`SDATASIZEQ`](#instr-sdatasizeq) that throws a cell overflow exception (8) on failure. | | + +### 11.7 Currency manipulation primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`FA00`** | `LDGRAMS`
`LDVARUINT16` | _`s - x s'`_ | 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`](#instr-ldu) [`SWAP`](#instr-swap) [`3 LSHIFT#`](#instr-lshift) [`LDUX`](#instr-ldux). | `26` | +| **`FA01`** | `LDVARINT16` | _`s - x s'`_ | Similar to [`LDVARUINT16`](#instr-ldgrams), but loads a _signed_ _Integer_ `x`.
Approximately equivalent to [`4 LDU`](#instr-ldu) [`SWAP`](#instr-swap) [`3 LSHIFT#`](#instr-lshift) [`LDIX`](#instr-ldix). | `26` | +| **`FA02`** | `STGRAMS`
`STVARUINT16` | _`b x - b'`_ | 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. | `26` | +| **`FA03`** | `STVARINT16` | _`b x - b'`_ | Similar to [`STVARUINT16`](#instr-stgrams), but serializes a _signed_ _Integer_ `x` in the range `-2^119...2^119-1`. | `26` | + +### 11.8 Message and address manipulation primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`FA40`** | `LDMSGADDR` | _`s - s' s''`_ | 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. | `26` | +| **`FA41`** | `LDMSGADDRQ` | _`s - s' s'' -1 or s 0`_ | A quiet version of [`LDMSGADDR`](#instr-ldmsgaddr): on success, pushes an extra `-1`; on failure, pushes the original `s` and a zero. | `26` | +| **`FA42`** | `PARSEMSGADDR` | _`s - t`_ | 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. | `26` | +| **`FA43`** | `PARSEMSGADDRQ` | _`s - t -1 or 0`_ | A quiet version of [`PARSEMSGADDR`](#instr-parsemsgaddr): returns a zero on error instead of throwing an exception. | `26` | +| **`FA44`** | `REWRITESTDADDR` | _`s - x y`_ | 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. | `26` | +| **`FA45`** | `REWRITESTDADDRQ` | _`s - x y -1 or 0`_ | A quiet version of primitive [`REWRITESTDADDR`](#instr-rewritestdaddr). | `26` | +| **`FA46`** | `REWRITEVARADDR` | _`s - x s'`_ | A variant of [`REWRITESTDADDR`](#instr-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`). | `26` | +| **`FA47`** | `REWRITEVARADDRQ` | _`s - x s' -1 or 0`_ | A quiet version of primitive [`REWRITEVARADDR`](#instr-rewritevaraddr). | `26` | + +### 11.9 Outbound message and output action primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`FB00`** | `SENDRAWMSG` | _`c x - `_ | 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`. | `526` | +| **`FB02`** | `RAWRESERVE` | _`x y - `_ | 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`. | `526` | +| **`FB03`** | `RAWRESERVEX` | _`x D y - `_ | Similar to [`RAWRESERVE`](#instr-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. | `526` | +| **`FB04`** | `SETCODE` | _`c - `_ | 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. | `526` | +| **`FB06`** | `SETLIBCODE` | _`c x - `_ | 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. | `526` | +| **`FB07`** | `CHANGELIB` | _`h x - `_ | Creates an output action similarly to [`SETLIBCODE`](#instr-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. | `526` | + +## 12 Debug primitives + +Opcodes beginning with `FE` are reserved for the debug primitives. These primitives have known fixed operation length and behave as (multibyte) [`NOP`](#instr-nop) operations. + +However, when invoked in a TVM instance with debug mode enabled, these primitives can produce a specific output into the text debug log of the TVM instance, never affecting the TVM state. + +[`DEBUG`](#instr-debug) and [`DEBUGSTR`](#instr-debugstr) are the two debug primitives, they cover all opcodes that start with `FE`. +Other primitives listed here have opcodes from the same set. When debug is enabled, they have their specified effects. When debug is disabled, they behave as [`NOP`](#instr-nop). + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :----------------------------------------------- | :-------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`FEnn`** | `{nn} DEBUG` | _`-`_ | `0 <= nn < 240` | `26` | +| **`FEFnssss`** | `{string} DEBUGSTR`
`{string} {x} DEBUGSTRI` | _`-`_ | `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`](#instr-debugstr): `ssss` is the given string.
[`DEBUGSTRI`](#instr-debugstr): `ssss` is one-byte integer `0 <= x <= 255` followed by the given string. | `26` | +| **`FE00`** | `DUMPSTK` | _`-`_ | Dumps the stack (at most the top 255 values) and shows the total stack depth. | `26` | +| **`FE2i`** | `s[i] DUMP` | _`-`_ | Dumps `s[i]`. | `26` | + +## 13 Codepage primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`FFnn`** | `[nn] SETCP` | _`-`_ | Selects TVM codepage `0 <= nn < 240`. If the codepage is not supported, throws an invalid opcode exception. | `26` | +| **`FF00`** | `SETCP0` | _`-`_ | Selects TVM (test) codepage zero as described in this document. | `26` | +| **`FFFz`** | `[z-16] SETCP` | _`-`_ | 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. | `26` | +| **`FFF0`** | `SETCPX` | _`c - `_ | Selects codepage `c` with `-2^15 <= c < 2^15` passed in the top of the stack. | `26` | + +## See Also + +- [TVM Overview](/learn/tvm-instructions/tvm-overview) +- [TVM Instructions](/learn/tvm-instructions/instructions) +- [TON TVM](https://ton.org/tvm.pdf) TVM Concepts(may include outdated information) From 71e4d798560c0ec6a01ef17545d5f257d65c83cb Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:56 +0800 Subject: [PATCH 205/219] New translations instructions.mdx (Chinese Simplified) --- .../learn/tvm-instructions/instructions.mdx | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions.mdx diff --git a/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions.mdx b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions.mdx new file mode 100644 index 0000000000..7bb705dc36 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions.mdx @@ -0,0 +1,98 @@ +--- +hide_table_of_contents: true +wrapperClassName: bootstrap-wrapper +--- + +import { SearchField } from '@site/src/components/SearchField'; +import { opcodes } from '@site/src/data/opcodes'; + +# TVM Instructions + +:::caution +Due to limitations in Docusaurus, the single-page version moved [here](/learn/archive/tvm-instructions). +::: + +... + +- [Exit From TVM Instruction Full Screen Mode](/learn/tvm-instructions/tvm-overview) + +## Introduction + +This document provides a list of TVM instructions along with their opcodes and mnemonics. + +:::info +[**TVM.pdf**](https://ton.org/tvm.pdf) concept document for TON Virtual Machine (may include outdated information). +::: + +Fift is a stack-based programming language designed to manage TON smart contracts. The Fift assembler is a Fift library that converts mnemonics of TVM instructions into their binary representation. + +A description of Fift, including an introduction to the Fift assembler, can be found [here](https://github.com/Piterden/TON-docs/blob/master/Fift.%20A%20Brief%20Introduction.md). + +This document specifies the corresponding mnemonic for each instruction. + +Note the following: + +1. Fift is a stack-based language, therefore all the arguments of any instruction are written before it (e.g. [`5 PUSHINT`](#instr-pushint-4), [`s0 s4 XCHG`](#instr-xchg-ij)). +2. Stack registers are denoted by `s0, s1, ..., s15`. Other stack registers (up to 255) are denoted by `i s()` (e.g. `100 s()`). +3. Control registers are denoted by `c0, c1, ..., c15`. + +### Gas prices + +The gas price of each instruction is specified in this document. The basic gas price of an instruction is `10 + b`, where `b` is the instruction length in bits. Some operations have additional fees: + +1. _Parsing cells_: Transforming a cell into a slice costs **100 gas units** if the cell is loading for the first time and **25** for subsequent loads during the same transaction. For such instructions, two gas prices are specified (e.g. [`CTOS`](#instr-ctos): `118/43`). +2. _Cell creation_: **500 gas units**. +3. _Throwing exceptions_: **50 gas units**. In this document the exception fee is only specified for an instruction if its primary purpose is to throw (e.g. [`THROWIF`](#instr-throwif-short), [`FITS`](#instr-fits)). If the instruction only throws in some cases, two gas prices are specified (e.g. [`FITS`](#instr-fits): `26/76`). +4. _Tuple creation_: **1 gas unit** for every tuple element. +5. _Implicit jumps_: **10 gas units** for an implicit jump, **5 gas units** for an implicit back jump. This fee is not a part of any instruction. +6. _Moving stack elements between continuations_: **1 gas unit** per element, however moving the first 32 elements is free. + +### Quick search + +:::info + +A full machine-readable list of TVM instructions is available [here](https://github.com/ton-community/ton-docs/blob/main/docs/learn/tvm-instructions/instructions.csv). +::: + +Each section of TVM Instructions includes a built-in search component for finding opcodes specific to that section as well. + +On this page, however, the search covers all existing opcodes, providing a comprehensive search option across the entire opcode range. + +Feel free to use the search field below to find a specific instruction: + + + +### TVM Instructions Content List + +- [Overview](/learn/tvm-instructions/instructions) +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [Tuple, List and Null](/learn/tvm-instructions/instructions/tuple-list-null) +- [Constants and Literals](/learn/tvm-instructions/instructions/constant) +- [Arithmetic Operations](/learn/tvm-instructions/instructions/arithmetic) +- [Data Comparison](/learn/tvm-instructions/instructions/data-comparison) +- [Cell Manipulation](/learn/tvm-instructions/instructions/cell-manipulation) +- [Continuation and Control Flow](/learn/tvm-instructions/instructions/control-flow) +- [Exception Generation and Handling](/learn/tvm-instructions/instructions/exception-gen-and-handling) +- [Dictionary Manipulation](/learn/tvm-instructions/instructions/dictionary-manipulation) +- [Application-specific Primitives](/learn/tvm-instructions/instructions/app-specific) +- [Miscellaneous](/learn/tvm-instructions/instructions/miscellaneous) + +... + +- [Exit From TVM Instruction Full Screen Mode](/learn/tvm-instructions/tvm-overview) + +### See Also + +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [\[Archived One Page\] TVM Instructions](/learn/archive/tvm-instructions) From de7d5466705edb792e519efe4fc16356f3bb2971 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:56 +0800 Subject: [PATCH 206/219] New translations app-specific.mdx (Chinese Simplified) --- .../instructions/app-specific.mdx | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/app-specific.mdx diff --git a/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/app-specific.mdx b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/app-specific.mdx new file mode 100644 index 0000000000..e198c690b9 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/app-specific.mdx @@ -0,0 +1,160 @@ +--- +hide_table_of_contents: true +wrapperClassName: bootstrap-wrapper +--- + +import { SearchField } from '@site/src/components/SearchField'; +import { appSpecificOpcodes as opcodes } from '@site/src/data/opcodes'; + +# Application-Specific Primitives + +### TVM Instructions Content List + +- [Overview](/learn/tvm-instructions/instructions) +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [Tuple, List and Null](/learn/tvm-instructions/instructions/tuple-list-null) +- [Constants and Literals](/learn/tvm-instructions/instructions/constant) +- [Arithmetic Operations](/learn/tvm-instructions/instructions/arithmetic) +- [Data Comparison](/learn/tvm-instructions/instructions/data-comparison) +- [Cell Manipulation](/learn/tvm-instructions/instructions/cell-manipulation) +- [Continuation and Control Flow](/learn/tvm-instructions/instructions/control-flow) +- [Exception Generation and Handling](/learn/tvm-instructions/instructions/exception-gen-and-handling) +- [Dictionary Manipulation](/learn/tvm-instructions/instructions/dictionary-manipulation) +- [Application-specific Primitives](/learn/tvm-instructions/instructions/app-specific) +- [Miscellaneous](/learn/tvm-instructions/instructions/miscellaneous) + +... + +- [Exit From TVM Instruction Full Screen Mode](/learn/tvm-instructions/tvm-overview) + + + +## Application-Specific Primitives + +### Gas-related primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`F800`** | `ACCEPT` | _`-`_ | 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. | `26` | +| **`F801`** | `SETGASLIMIT` | _`g - `_ | 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`](#instr-setgaslimit) with an argument `g >= 2^63-1` is equivalent to [`ACCEPT`](#instr-accept). | `26` | +| **`F80F`** | `COMMIT` | _`-`_ | 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. | `26` | + +### Pseudo-random number generator primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------- | +| **`F810`** | `RANDU256` | _`- x`_ | 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`. | `26+\|c7\|+\|c1_1\|` | +| **`F811`** | `RAND` | _`y - z`_ | 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`](#instr-randu256) [`256 MULRSHIFT`](#instr-mulrshift-var). | `26+\|c7\|+\|c1_1\|` | +| **`F814`** | `SETRAND` | _`x - `_ | Sets the random seed to unsigned 256-bit _Integer_ `x`. | `26+\|c7\|+\|c1_1\|` | +| **`F815`** | `ADDRAND`
`RANDOMIZE` | _`x - `_ | 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`. | `26` | + +### Configuration primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------- | +| **`F82i`** | `[i] GETPARAM` | _` - x`_ | Returns the `i`-th parameter from the _Tuple_ provided at `c7` for `0 <= i <= 15`. Equivalent to [`c7 PUSHCTR`](#instr-pushctr) [`FIRST`](#instr-first) [`[i] INDEX`](#instr-index).
If one of these internal operations fails, throws an appropriate type checking or range checking exception. | `26` | +| **`F823`** | `NOW` | _` - x`_ | 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`](#instr-getparam). | `26` | +| **`F824`** | `BLOCKLT` | _` - x`_ | Returns the starting logical time of the current block.
Equivalent to [`4 GETPARAM`](#instr-getparam). | `26` | +| **`F825`** | `LTIME` | _` - x`_ | Returns the logical time of the current transaction.
Equivalent to [`5 GETPARAM`](#instr-getparam). | `26` | +| **`F826`** | `RANDSEED` | _` - x`_ | Returns the current random seed as an unsigned 256-bit _Integer_.
Equivalent to [`6 GETPARAM`](#instr-getparam). | `26` | +| **`F827`** | `BALANCE` | _` - t`_ | 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`](#instr-getparam).
Note that `RAW` primitives such as [`SENDRAWMSG`](#instr-sendrawmsg) do not update this field. | `26` | +| **`F828`** | `MYADDR` | _` - s`_ | 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`](#instr-parsemsgaddr) or [`REWRITESTDADDR`](#instr-rewritestdaddr).
Equivalent to [`8 GETPARAM`](#instr-getparam). | `26` | +| **`F829`** | `CONFIGROOT` | _` - D`_ | Returns the _Maybe Cell_ `D` with the current global configuration dictionary. Equivalent to `9 GETPARAM `. | `26` | +| **`F830`** | `CONFIGDICT` | _` - D 32`_ | Returns the global configuration dictionary along with its key length (32).
Equivalent to [`CONFIGROOT`](#instr-configroot) [`32 PUSHINT`](#instr-pushint-4). | `26` | +| **`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`](#instr-configdict) [`DICTIGETREF`](#instr-dictigetref). | | +| **`F833`** | `CONFIGOPTPARAM` | _`i - c^?`_ | Returns the value of the global configuration parameter with integer index `i` as a _Maybe Cell_ `c^?`.
Equivalent to [`CONFIGDICT`](#instr-configdict) [`DICTIGETOPTREF`](#instr-dictigetoptref). | | + +### Global variable primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`F840`** | `GETGLOBVAR` | _`k - x`_ | Returns the `k`-th global variable for `0 <= k < 255`.
Equivalent to [`c7 PUSHCTR`](#instr-pushctr) [`SWAP`](#instr-swap) [`INDEXVARQ`](#instr-indexvarq). | `26` | +| **`F85_k`** | `[k] GETGLOB` | _` - x`_ | Returns the `k`-th global variable for `1 <= k <= 31`.
Equivalent to [`c7 PUSHCTR`](#instr-pushctr) [`[k] INDEXQ`](#instr-indexq). | `26` | +| **`F860`** | `SETGLOBVAR` | _`x k - `_ | Assigns `x` to the `k`-th global variable for `0 <= k < 255`.
Equivalent to [`c7 PUSHCTR`](#instr-pushctr) [`ROTREV`](#instr-rotrev) [`SETINDEXVARQ`](#instr-setindexvarq) [`c7 POPCTR`](#instr-popctr). | `26+\|c7’\|` | +| **`F87_k`** | `[k] SETGLOB` | _`x - `_ | Assigns `x` to the `k`-th global variable for `1 <= k <= 31`.
Equivalent to [`c7 PUSHCTR`](#instr-pushctr) [`SWAP`](#instr-swap) [`k SETINDEXQ`](#instr-setindexq) [`c7 POPCTR`](#instr-popctr). | `26+\|c7’\|` | + +### Hashing and cryptography primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------- | +| **`F900`** | `HASHCU` | _`c - x`_ | 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. | `26` | +| **`F901`** | `HASHSU` | _`s - x`_ | 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`](#instr-hashcu). | `526` | +| **`F902`** | `SHA256U` | _`s - x`_ | 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`. | `26` | +| **`F910`** | `CHKSIGNU` | _`h s k - ?`_ | 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`](#instr-chksignu) is equivalent to [`ROT`](#instr-rot) [`NEWC`](#instr-newc) [`256 STU`](#instr-stu) [`ENDC`](#instr-endc) [`ROTREV`](#instr-rotrev) [`CHKSIGNS`](#instr-chksigns), i.e., to [`CHKSIGNS`](#instr-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`](#instr-chksigns). | `26` | +| **`F911`** | `CHKSIGNS` | _`d s k - ?`_ | Checks whether `s` is a valid Ed25519-signature of the data portion of _Slice_ `d` using public key `k`, similarly to [`CHKSIGNU`](#instr-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. | `26` | + +### Miscellaneous primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`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`. | | +| **`F941`** | `CDATASIZE` | _`c n - x y z`_ | A non-quiet version of [`CDATASIZEQ`](#instr-cdatasizeq) that throws a cell overflow exception (8) on failure. | | +| **`F942`** | `SDATASIZEQ` | _`s n - x y z -1 or 0`_ | Similar to [`CDATASIZEQ`](#instr-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`. | | +| **`F943`** | `SDATASIZE` | _`s n - x y z`_ | A non-quiet version of [`SDATASIZEQ`](#instr-sdatasizeq) that throws a cell overflow exception (8) on failure. | | + +### Currency manipulation primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`FA00`** | `LDGRAMS`
`LDVARUINT16` | _`s - x s'`_ | 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`](#instr-ldu) [`SWAP`](#instr-swap) [`3 LSHIFT#`](#instr-lshift) [`LDUX`](#instr-ldux). | `26` | +| **`FA01`** | `LDVARINT16` | _`s - x s'`_ | Similar to [`LDVARUINT16`](#instr-ldgrams), but loads a _signed_ _Integer_ `x`.
Approximately equivalent to [`4 LDU`](#instr-ldu) [`SWAP`](#instr-swap) [`3 LSHIFT#`](#instr-lshift) [`LDIX`](#instr-ldix). | `26` | +| **`FA02`** | `STGRAMS`
`STVARUINT16` | _`b x - b'`_ | 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. | `26` | +| **`FA03`** | `STVARINT16` | _`b x - b'`_ | Similar to [`STVARUINT16`](#instr-stgrams), but serializes a _signed_ _Integer_ `x` in the range `-2^119...2^119-1`. | `26` | + +### Message and address manipulation primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`FA40`** | `LDMSGADDR` | _`s - s' s''`_ | 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. | `26` | +| **`FA41`** | `LDMSGADDRQ` | _`s - s' s'' -1 or s 0`_ | A quiet version of [`LDMSGADDR`](#instr-ldmsgaddr): on success, pushes an extra `-1`; on failure, pushes the original `s` and a zero. | `26` | +| **`FA42`** | `PARSEMSGADDR` | _`s - t`_ | 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. | `26` | +| **`FA43`** | `PARSEMSGADDRQ` | _`s - t -1 or 0`_ | A quiet version of [`PARSEMSGADDR`](#instr-parsemsgaddr): returns a zero on error instead of throwing an exception. | `26` | +| **`FA44`** | `REWRITESTDADDR` | _`s - x y`_ | 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. | `26` | +| **`FA45`** | `REWRITESTDADDRQ` | _`s - x y -1 or 0`_ | A quiet version of primitive [`REWRITESTDADDR`](#instr-rewritestdaddr). | `26` | +| **`FA46`** | `REWRITEVARADDR` | _`s - x s'`_ | A variant of [`REWRITESTDADDR`](#instr-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`). | `26` | +| **`FA47`** | `REWRITEVARADDRQ` | _`s - x s' -1 or 0`_ | A quiet version of primitive [`REWRITEVARADDR`](#instr-rewritevaraddr). | `26` | + +### Outbound message and output action primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`FB00`** | `SENDRAWMSG` | _`c x - `_ | 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`. | `526` | +| **`FB02`** | `RAWRESERVE` | _`x y - `_ | 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`. | `526` | +| **`FB03`** | `RAWRESERVEX` | _`x D y - `_ | Similar to [`RAWRESERVE`](#instr-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. | `526` | +| **`FB04`** | `SETCODE` | _`c - `_ | 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. | `526` | +| **`FB06`** | `SETLIBCODE` | _`c x - `_ | 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. | `526` | +| **`FB07`** | `CHANGELIB` | _`h x - `_ | Creates an output action similarly to [`SETLIBCODE`](#instr-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. | `526` | + +### TVM Instructions Content List + +- [Overview](/learn/tvm-instructions/instructions) +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [Tuple, List and Null](/learn/tvm-instructions/instructions/tuple-list-null) +- [Constants and Literals](/learn/tvm-instructions/instructions/constant) +- [Arithmetic Operations](/learn/tvm-instructions/instructions/arithmetic) +- [Data Comparison](/learn/tvm-instructions/instructions/data-comparison) +- [Cell Manipulation](/learn/tvm-instructions/instructions/cell-manipulation) +- [Continuation and Control Flow](/learn/tvm-instructions/instructions/control-flow) +- [Exception Generation and Handling](/learn/tvm-instructions/instructions/exception-gen-and-handling) +- [Dictionary Manipulation](/learn/tvm-instructions/instructions/dictionary-manipulation) +- [Application-specific Primitives](/learn/tvm-instructions/instructions/app-specific) +- [Miscellaneous](/learn/tvm-instructions/instructions/miscellaneous) + +... + +- [Exit From TVM Instruction Full Screen Mode](/learn/tvm-instructions/tvm-overview) + +## See Also + +- [TVM Overview](/learn/tvm-instructions/tvm-overview) From 0d6bc90c39d8406e507c601d385826463e4415df Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:57 +0800 Subject: [PATCH 207/219] New translations arithmetic.mdx (Chinese Simplified) --- .../instructions/arithmetic.mdx | 163 ++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/arithmetic.mdx diff --git a/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/arithmetic.mdx b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/arithmetic.mdx new file mode 100644 index 0000000000..4c283e9ecd --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/arithmetic.mdx @@ -0,0 +1,163 @@ +--- +hide_table_of_contents: true +wrapperClassName: bootstrap-wrapper +--- + +import { SearchField } from '@site/src/components/SearchField'; +import { arithmeticOpcodes as opcodes } from '@site/src/data/opcodes'; + +# Arithmetic primitives + +### TVM Instructions Content List + +- [Overview](/learn/tvm-instructions/instructions) +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [Tuple, List and Null](/learn/tvm-instructions/instructions/tuple-list-null) +- [Constants and Literals](/learn/tvm-instructions/instructions/constant) +- [Arithmetic Operations](/learn/tvm-instructions/instructions/arithmetic) +- [Data Comparison](/learn/tvm-instructions/instructions/data-comparison) +- [Cell Manipulation](/learn/tvm-instructions/instructions/cell-manipulation) +- [Continuation and Control Flow](/learn/tvm-instructions/instructions/control-flow) +- [Exception Generation and Handling](/learn/tvm-instructions/instructions/exception-gen-and-handling) +- [Dictionary Manipulation](/learn/tvm-instructions/instructions/dictionary-manipulation) +- [Application-specific Primitives](/learn/tvm-instructions/instructions/app-specific) +- [Miscellaneous](/learn/tvm-instructions/instructions/miscellaneous) + + + +## Arithmetic primitives + +### Addition, subtraction, multiplication + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------------------------------------ | :-------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`A0`** | `ADD` | _`x y - x+y`_ | | `18` | +| **`A1`** | `SUB` | _`x y - x-y`_ | | `18` | +| **`A2`** | `SUBR` | _`x y - y-x`_ | Equivalent to [`SWAP`](#instr-swap) [`SUB`](#instr-sub). | `18` | +| **`A3`** | `NEGATE` | _`x - -x`_ | Equivalent to [`-1 MULCONST`](#instr-mulconst) or to [`ZERO SUBR`](#instr-subr).
Notice that it triggers an integer overflow exception if `x=-2^256`. | `18` | +| **`A4`** | `INC` | _`x - x+1`_ | Equivalent to [`1 ADDCONST`](#instr-addconst). | `18` | +| **`A5`** | `DEC` | _`x - x-1`_ | Equivalent to [`-1 ADDCONST`](#instr-addconst). | `18` | +| **`A6cc`** | `[cc] ADDCONST`
`[cc] ADDINT`
`[-cc] SUBCONST`
`[-cc] SUBINT` | _`x - x+cc`_ | `-128 <= cc <= 127`. | `26` | +| **`A7cc`** | `[cc] MULCONST`
`[cc] MULINT` | _`x - x*cc`_ | `-128 <= cc <= 127`. | `26` | +| **`A8`** | `MUL` | _`x y - x*y`_ | | `18` | + +### Division + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :---------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`A9mscdf`** | | | 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`](#instr-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. | `26` | +| **`A904`** | `DIV` | _`x y - q`_ | `q=floor(x/y)`, `r=x-y*q` | `26` | +| **`A905`** | `DIVR` | _`x y - q’`_ | `q’=round(x/y)`, `r’=x-y*q’` | `26` | +| **`A906`** | `DIVC` | _`x y - q''`_ | `q’’=ceil(x/y)`, `r’’=x-y*q’’` | `26` | +| **`A908`** | `MOD` | _`x y - r`_ | | `26` | +| **`A90C`** | `DIVMOD` | _`x y - q r`_ | | `26` | +| **`A90D`** | `DIVMODR` | _`x y - q' r'`_ | | `26` | +| **`A90E`** | `DIVMODC` | _`x y - q'' r''`_ | | `26` | +| **`A925`** | `RSHIFTR` | _`x y - round(x/2^y)`_ | | `26` | +| **`A926`** | `RSHIFTC` | _`x y - ceil(x/2^y)`_ | | `34` | +| **`A935tt`** | `[tt+1] RSHIFTR#` | _`x y - round(x/2^(tt+1))`_ | | `34` | +| **`A936tt`** | `[tt+1] RSHIFTC#` | _`x y - ceil(x/2^(tt+1))`_ | | `34` | +| **`A938tt`** | `[tt+1] MODPOW2#` | _`x - x mod 2^(tt+1)`_ | | `34` | +| **`A98`** | `MULDIV` | _`x y z - q`_ | `q=floor(x*y/z)` | `26` | +| **`A985`** | `MULDIVR` | _`x y z - q'`_ | `q'=round(x*y/z)` | `26` | +| **`A98C`** | `MULDIVMOD` | _`x y z - q r`_ | `q=floor(x*y/z)`, `r=x*y-z*q` | `26` | +| **`A9A4`** | `MULRSHIFT` | _`x y z - floor(x*y/2^z)`_ | `0 <= z <= 256` | `26` | +| **`A9A5`** | `MULRSHIFTR` | _`x y z - round(x*y/2^z)`_ | `0 <= z <= 256` | `26` | +| **`A9A6`** | `MULRSHIFTC` | _`x y z - ceil(x*y/2^z)`_ | `0 <= z <= 256` | `34` | +| **`A9B4tt`** | `[tt+1] MULRSHIFT#` | _`x y - floor(x*y/2^(tt+1))`_ | | `34` | +| **`A9B5tt`** | `[tt+1] MULRSHIFTR#` | _`x y - round(x*y/2^(tt+1))`_ | | `34` | +| **`A9B6tt`** | `[tt+1] MULRSHIFTC#` | _`x y - ceil(x*y/2^(tt+1))`_ | | `26` | +| **`A9C4`** | `LSHIFTDIV` | _`x y z - floor(2^z*x/y)`_ | `0 <= z <= 256` | `26` | +| **`A9C5`** | `LSHIFTDIVR` | _`x y z - round(2^z*x/y)`_ | `0 <= z <= 256` | `26` | +| **`A9C6`** | `LSHIFTDIVC` | _`x y z - ceil(2^z*x/y)`_ | `0 <= z <= 256` | `34` | +| **`A9D4tt`** | `[tt+1] LSHIFT#DIV` | _`x y - floor(2^(tt+1)*x/y)`_ | | `34` | +| **`A9D5tt`** | `[tt+1] LSHIFT#DIVR` | _`x y - round(2^(tt+1)*x/y)`_ | | `34` | +| **`A9D6tt`** | `[tt+1] LSHIFT#DIVC` | _`x y - ceil(2^(tt+1)*x/y)`_ | | `26` | + +### Shifts, logical operations + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`AAcc`** | `[cc+1] LSHIFT#` | _`x - x*2^(cc+1)`_ | `0 <= cc <= 255` | `26` | +| **`ABcc`** | `[cc+1] RSHIFT#` | _`x - floor(x/2^(cc+1))`_ | `0 <= cc <= 255` | `18` | +| **`AC`** | `LSHIFT` | _`x y - x*2^y`_ | `0 <= y <= 1023` | `18` | +| **`AD`** | `RSHIFT` | _`x y - floor(x/2^y)`_ | `0 <= y <= 1023` | `18` | +| **`AE`** | `POW2` | _`y - 2^y`_ | `0 <= y <= 1023`
Equivalent to [`ONE`](#instr-one) [`SWAP`](#instr-swap) [`LSHIFT`](#instr-lshift-var). | `18` | +| **`B0`** | `AND` | _`x y - x&y`_ | Bitwise and of two signed integers `x` and `y`, sign-extended to infinity. | `18` | +| **`B1`** | `OR` | _`x y - x\|y`_ | Bitwise or of two integers. | `18` | +| **`B2`** | `XOR` | _`x y - x xor y`_ | Bitwise xor of two integers. | `18` | +| **`B3`** | `NOT` | _`x - ~x`_ | Bitwise not of an integer. | `26` | +| **`B4cc`** | `[cc+1] FITS` | _`x - x`_ | 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). | `26/76` | +| **`B400`** | `CHKBOOL` | _`x - x`_ | Checks whether `x` is a “boolean value'' (i.e., either 0 or -1). | `26/76` | +| **`B5cc`** | `[cc+1] UFITS` | _`x - x`_ | Checks whether `x` is a `cc+1`-bit unsigned integer for `0 <= cc <= 255` (i.e., whether `0 <= x < 2^(cc+1)`). | `26/76` | +| **`B500`** | `CHKBIT` | _`x - x`_ | Checks whether `x` is a binary digit (i.e., zero or one). | `26/76` | +| **`B600`** | `FITSX` | _`x c - x`_ | Checks whether `x` is a `c`-bit signed integer for `0 <= c <= 1023`. | `26/76` | +| **`B601`** | `UFITSX` | _`x c - x`_ | Checks whether `x` is a `c`-bit unsigned integer for `0 <= c <= 1023`. | `26/76` | +| **`B602`** | `BITSIZE` | _`x - c`_ | Computes smallest `c >= 0` such that `x` fits into a `c`-bit signed integer (`-2^(c-1) <= c < 2^(c-1)`). | `26` | +| **`B603`** | `UBITSIZE` | _`x - c`_ | Computes smallest `c >= 0` such that `x` fits into a `c`-bit unsigned integer (`0 <= x < 2^c`), or throws a range check exception. | `26` | +| **`B608`** | `MIN` | _`x y - x or y`_ | Computes the minimum of two integers `x` and `y`. | `26` | +| **`B609`** | `MAX` | _`x y - x or y`_ | Computes the maximum of two integers `x` and `y`. | `26` | +| **`B60A`** | `MINMAX`
`INTSORT2` | _`x y - x y or y x`_ | Sorts two integers. Quiet version of this operation returns two `NaN`s if any of the arguments are `NaN`s. | `26` | +| **`B60B`** | `ABS` | _`x - \|x\|`_ | Computes the absolute value of an integer `x`. | `26` | + +### Quiet arithmetic primitives + +Quiet operations return `NaN` instead of throwing exceptions if one of their arguments is a `NaN` or in the case of an integer overflow. +Quiet operations have a prefix `Q` as shown below. Another way to make an operation quiet is to add `QUIET` before it (i.e. one can write [`QUIET ADD`](#instr-add) instead of [`QADD`](#instr-qadd)). +Quiet versions of integer comparison primitives are also available ([`QUIET SGN`](#instr-sgn), [`QUIET LESS`](#instr-less) etc). + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :----------------------------------------------------------------------------------------------- | :----------- | +| **`B7A0`** | `QADD` | _`x y - x+y`_ | | `26` | +| **`B7A1`** | `QSUB` | _`x y - x-y`_ | | `26` | +| **`B7A2`** | `QSUBR` | _`x y - y-x`_ | | `26` | +| **`B7A3`** | `QNEGATE` | _`x - -x`_ | | `26` | +| **`B7A4`** | `QINC` | _`x - x+1`_ | | `26` | +| **`B7A5`** | `QDEC` | _`x - x-1`_ | | `26` | +| **`B7A8`** | `QMUL` | _`x y - x*y`_ | | `26` | +| **`B7A904`** | `QDIV` | _`x y - q`_ | Division returns `NaN` if `y=0`. | `34` | +| **`B7A905`** | `QDIVR` | _`x y - q’`_ | | `34` | +| **`B7A906`** | `QDIVC` | _`x y - q''`_ | | `34` | +| **`B7A908`** | `QMOD` | _`x y - r`_ | | `34` | +| **`B7A90C`** | `QDIVMOD` | _`x y - q r`_ | | `34` | +| **`B7A90D`** | `QDIVMODR` | _`x y - q' r'`_ | | `34` | +| **`B7A90E`** | `QDIVMODC` | _`x y - q'' r''`_ | | `34` | +| **`B7A985`** | `QMULDIVR` | _`x y z - q'`_ | | `34` | +| **`B7A98C`** | `QMULDIVMOD` | _`x y z - q r`_ | | `34` | +| **`B7AC`** | `QLSHIFT` | _`x y - x*2^y`_ | | `26` | +| **`B7AD`** | `QRSHIFT` | _`x y - floor(x/2^y)`_ | | `26` | +| **`B7AE`** | `QPOW2` | _`y - 2^y`_ | | `26` | +| **`B7B0`** | `QAND` | _`x y - x&y`_ | | `26` | +| **`B7B1`** | `QOR` | _`x y - x\|y`_ | | `26` | +| **`B7B2`** | `QXOR` | _`x y - x xor y`_ | | `26` | +| **`B7B3`** | `QNOT` | _`x - ~x`_ | | `26` | +| **`B7B4cc`** | `[cc+1] QFITS` | _`x - x`_ | Replaces `x` with a `NaN` if x is not a `cc+1`-bit signed integer, leaves it intact otherwise. | `34` | +| **`B7B5cc`** | `[cc+1] QUFITS` | _`x - x`_ | Replaces `x` with a `NaN` if x is not a `cc+1`-bit unsigned integer, leaves it intact otherwise. | `34` | +| **`B7B600`** | `QFITSX` | _`x c - x`_ | Replaces `x` with a `NaN` if x is not a c-bit signed integer, leaves it intact otherwise. | `34` | +| **`B7B601`** | `QUFITSX` | _`x c - x`_ | Replaces `x` with a `NaN` if x is not a c-bit unsigned integer, leaves it intact otherwise. | `34` | + +### TVM Instructions Content List + +- [Overview](/learn/tvm-instructions/instructions) +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [Tuple, List and Null](/learn/tvm-instructions/instructions/tuple-list-null) +- [Constants and Literals](/learn/tvm-instructions/instructions/constant) +- [Arithmetic Operations](/learn/tvm-instructions/instructions/arithmetic) +- [Data Comparison](/learn/tvm-instructions/instructions/data-comparison) +- [Cell Manipulation](/learn/tvm-instructions/instructions/cell-manipulation) +- [Continuation and Control Flow](/learn/tvm-instructions/instructions/control-flow) +- [Exception Generation and Handling](/learn/tvm-instructions/instructions/exception-gen-and-handling) +- [Dictionary Manipulation](/learn/tvm-instructions/instructions/dictionary-manipulation) +- [Application-specific Primitives](/learn/tvm-instructions/instructions/app-specific) +- [Miscellaneous](/learn/tvm-instructions/instructions/miscellaneous) From b212bcd10a83cccb31aedfb9c5685404d5405352 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:58 +0800 Subject: [PATCH 208/219] New translations cell-manipulation.mdx (Chinese Simplified) --- .../instructions/cell-manipulation.mdx | 215 ++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/cell-manipulation.mdx diff --git a/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/cell-manipulation.mdx b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/cell-manipulation.mdx new file mode 100644 index 0000000000..b53d945b76 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/cell-manipulation.mdx @@ -0,0 +1,215 @@ +--- +hide_table_of_contents: true +wrapperClassName: bootstrap-wrapper +--- + +import { SearchField } from '@site/src/components/SearchField'; +import { cellManipulationOpcodes as opcodes } from '@site/src/data/opcodes'; + +# Cell Primitives + +### TVM Instructions Content List + +- [Overview](/learn/tvm-instructions/instructions) +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [Tuple, List and Null](/learn/tvm-instructions/instructions/tuple-list-null) +- [Constants and Literals](/learn/tvm-instructions/instructions/constant) +- [Arithmetic Operations](/learn/tvm-instructions/instructions/arithmetic) +- [Data Comparison](/learn/tvm-instructions/instructions/data-comparison) +- [Cell Manipulation](/learn/tvm-instructions/instructions/cell-manipulation) +- [Continuation and Control Flow](/learn/tvm-instructions/instructions/control-flow) +- [Exception Generation and Handling](/learn/tvm-instructions/instructions/exception-gen-and-handling) +- [Dictionary Manipulation](/learn/tvm-instructions/instructions/dictionary-manipulation) +- [Application-specific Primitives](/learn/tvm-instructions/instructions/app-specific) +- [Miscellaneous](/learn/tvm-instructions/instructions/miscellaneous) + + + +## Cell Primitives + +### Cell serialization primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`C8`** | `NEWC` | _`- b`_ | Creates a new empty _Builder_. | `18` | +| **`C9`** | `ENDC` | _`b - c`_ | Converts a _Builder_ into an ordinary _Cell_. | `518` | +| **`CAcc`** | `[cc+1] STI` | _`x b - b'`_ | 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. | `26` | +| **`CBcc`** | `[cc+1] STU` | _`x b - b'`_ | Stores an unsigned `cc+1`-bit integer `x` into _Builder_ `b`. In all other respects it is similar to [`STI`](#instr-sti). | `26` | +| **`CC`** | `STREF` | _`c b - b'`_ | Stores a reference to _Cell_ `c` into _Builder_ `b`. | `18` | +| **`CD`** | `STBREFR`
`ENDCST` | _`b b'' - b`_ | Equivalent to [`ENDC`](#instr-endc) [`SWAP`](#instr-swap) [`STREF`](#instr-stref). | `518` | +| **`CE`** | `STSLICE` | _`s b - b'`_ | Stores _Slice_ `s` into _Builder_ `b`. | `18` | +| **`CF00`** | `STIX` | _`x b l - b'`_ | Stores a signed `l`-bit integer `x` into `b` for `0 <= l <= 257`. | `26` | +| **`CF01`** | `STUX` | _`x b l - b'`_ | Stores an unsigned `l`-bit integer `x` into `b` for `0 <= l <= 256`. | `26` | +| **`CF02`** | `STIXR` | _`b x l - b'`_ | Similar to [`STIX`](#instr-stix), but with arguments in a different order. | `26` | +| **`CF03`** | `STUXR` | _`b x l - b'`_ | Similar to [`STUX`](#instr-stux), but with arguments in a different order. | `26` | +| **`CF04`** | `STIXQ` | _`x b l - x b f or b' 0`_ | A quiet version of [`STIX`](#instr-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. | `26` | +| **`CF05`** | `STUXQ` | _`x b l - x b f or b' 0`_ | A quiet version of [`STUX`](#instr-stux). | `26` | +| **`CF06`** | `STIXRQ` | _`b x l - b x f or b' 0`_ | A quiet version of [`STIXR`](#instr-stixr). | `26` | +| **`CF07`** | `STUXRQ` | _`b x l - b x f or b' 0`_ | A quiet version of [`STUXR`](#instr-stuxr). | `26` | +| **`CF08cc`** | `[cc+1] STI_l` | _`x b - b'`_ | A longer version of [`[cc+1] STI`](#instr-sti). | `34` | +| **`CF09cc`** | `[cc+1] STU_l` | _`x b - b'`_ | A longer version of [`[cc+1] STU`](#instr-stu). | `34` | +| **`CF0Acc`** | `[cc+1] STIR` | _`b x - b'`_ | Equivalent to [`SWAP`](#instr-swap) [`[cc+1] STI`](#instr-sti). | `34` | +| **`CF0Bcc`** | `[cc+1] STUR` | _`b x - b'`_ | Equivalent to [`SWAP`](#instr-swap) [`[cc+1] STU`](#instr-stu). | `34` | +| **`CF0Ccc`** | `[cc+1] STIQ` | _`x b - x b f or b' 0`_ | A quiet version of [`STI`](#instr-sti). | `34` | +| **`CF0Dcc`** | `[cc+1] STUQ` | _`x b - x b f or b' 0`_ | A quiet version of [`STU`](#instr-stu). | `34` | +| **`CF0Ecc`** | `[cc+1] STIRQ` | _`b x - b x f or b' 0`_ | A quiet version of [`STIR`](#instr-stir). | `34` | +| **`CF0Fcc`** | `[cc+1] STURQ` | _`b x - b x f or b' 0`_ | A quiet version of [`STUR`](#instr-stur). | `34` | +| **`CF10`** | `STREF_l` | _`c b - b'`_ | A longer version of [`STREF`](#instr-stref). | `26` | +| **`CF11`** | `STBREF` | _`b' b - b''`_ | Equivalent to [`SWAP`](#instr-swap) [`STBREFR`](#instr-stbrefr). | `526` | +| **`CF12`** | `STSLICE_l` | _`s b - b'`_ | A longer version of [`STSLICE`](#instr-stslice). | `26` | +| **`CF13`** | `STB` | _`b' b - b''`_ | Appends all data from _Builder_ `b'` to _Builder_ `b`. | `26` | +| **`CF14`** | `STREFR` | _`b c - b'`_ | Equivalent to [`SWAP`](#instr-swap) [`STREF`](#instr-stref). | `26` | +| **`CF15`** | `STBREFR_l` | _`b b' - b''`_ | A longer encoding of [`STBREFR`](#instr-stbrefr). | `526` | +| **`CF16`** | `STSLICER` | _`b s - b'`_ | Equivalent to [`SWAP`](#instr-swap) [`STSLICE`](#instr-stslice). | `26` | +| **`CF17`** | `STBR`
`BCONCAT` | _`b b' - b''`_ | Concatenates two builders.
Equivalent to [`SWAP`](#instr-swap) [`STB`](#instr-stb). | `26` | +| **`CF18`** | `STREFQ` | _`c b - c b -1 or b' 0`_ | Quiet version of [`STREF`](#instr-stref). | `26` | +| **`CF19`** | `STBREFQ` | _`b' b - b' b -1 or b'' 0`_ | Quiet version of [`STBREF`](#instr-stbref). | `526` | +| **`CF1A`** | `STSLICEQ` | _`s b - s b -1 or b' 0`_ | Quiet version of [`STSLICE`](#instr-stslice). | `26` | +| **`CF1B`** | `STBQ` | _`b' b - b' b -1 or b'' 0`_ | Quiet version of [`STB`](#instr-stb). | `26` | +| **`CF1C`** | `STREFRQ` | _`b c - b c -1 or b' 0`_ | Quiet version of [`STREFR`](#instr-strefr). | `26` | +| **`CF1D`** | `STBREFRQ` | _`b b' - b b' -1 or b'' 0`_ | Quiet version of [`STBREFR`](#instr-stbrefr). | `526` | +| **`CF1E`** | `STSLICERQ` | _`b s - b s -1 or b'' 0`_ | Quiet version of [`STSLICER`](#instr-stslicer). | `26` | +| **`CF1F`** | `STBRQ`
`BCONCATQ` | _`b b' - b b' -1 or b'' 0`_ | Quiet version of [`STBR`](#instr-stbr). | `26` | +| **`CF20`** | `[ref] STREFCONST` | _`b - b’`_ | Equivalent to [`PUSHREF`](#instr-pushref) [`STREFR`](#instr-strefr). | `26` | +| **`CF21`** | `[ref] [ref] STREF2CONST` | _`b - b’`_ | Equivalent to [`STREFCONST`](#instr-strefconst) [`STREFCONST`](#instr-strefconst). | `26` | +| **`CF23`** | | _`b x - c`_ | 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`](#instr-endc). Otherwise some validity checks on the data and references of `b` are performed before creating the exotic cell. | `526` | +| **`CF28`** | `STILE4` | _`x b - b'`_ | Stores a little-endian signed 32-bit integer. | `26` | +| **`CF29`** | `STULE4` | _`x b - b'`_ | Stores a little-endian unsigned 32-bit integer. | `26` | +| **`CF2A`** | `STILE8` | _`x b - b'`_ | Stores a little-endian signed 64-bit integer. | `26` | +| **`CF2B`** | `STULE8` | _`x b - b'`_ | Stores a little-endian unsigned 64-bit integer. | `26` | +| **`CF30`** | `BDEPTH` | _`b - x`_ | 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`. | `26` | +| **`CF31`** | `BBITS` | _`b - x`_ | Returns the number of data bits already stored in _Builder_ `b`. | `26` | +| **`CF32`** | `BREFS` | _`b - y`_ | Returns the number of cell references already stored in `b`. | `26` | +| **`CF33`** | `BBITREFS` | _`b - x y`_ | Returns the numbers of both data bits and cell references in `b`. | `26` | +| **`CF35`** | `BREMBITS` | _`b - x'`_ | Returns the number of data bits that can still be stored in `b`. | `26` | +| **`CF36`** | `BREMREFS` | _`b - y'`_ | Returns the number of references that can still be stored in `b`. | `26` | +| **`CF37`** | `BREMBITREFS` | _`b - x' y'`_ | Returns the numbers of both data bits and references that can still be stored in `b`. | `26` | +| **`CF38cc`** | `[cc+1] BCHKBITS#` | _`b -`_ | Checks whether `cc+1` bits can be stored into `b`, where `0 <= cc <= 255`. | `34/84` | +| **`CF39`** | `BCHKBITS` | _`b x - `_ | 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. | `26/76` | +| **`CF3A`** | `BCHKREFS` | _`b y - `_ | Checks whether `y` references can be stored into `b`, `0 <= y <= 7`. | `26/76` | +| **`CF3B`** | `BCHKBITREFS` | _`b x y - `_ | Checks whether `x` bits and `y` references can be stored into `b`, `0 <= x <= 1023`, `0 <= y <= 7`. | `26/76` | +| **`CF3Ccc`** | `[cc+1] BCHKBITSQ#` | _`b - ?`_ | Checks whether `cc+1` bits can be stored into `b`, where `0 <= cc <= 255`. | `34` | +| **`CF3D`** | `BCHKBITSQ` | _`b x - ?`_ | Checks whether `x` bits can be stored into `b`, `0 <= x <= 1023`. | `26` | +| **`CF3E`** | `BCHKREFSQ` | _`b y - ?`_ | Checks whether `y` references can be stored into `b`, `0 <= y <= 7`. | `26` | +| **`CF3F`** | `BCHKBITREFSQ` | _`b x y - ?`_ | Checks whether `x` bits and `y` references can be stored into `b`, `0 <= x <= 1023`, `0 <= y <= 7`. | `26` | +| **`CF40`** | `STZEROES` | _`b n - b'`_ | Stores `n` binary zeroes into _Builder_ `b`. | `26` | +| **`CF41`** | `STONES` | _`b n - b'`_ | Stores `n` binary ones into _Builder_ `b`. | `26` | +| **`CF42`** | `STSAME` | _`b n x - b'`_ | Stores `n` binary `x`es (`0 <= x <= 1`) into _Builder_ `b`. | `26` | +| **`CFC0_xysss`** | `[slice] STSLICECONST` | _`b - b'`_ | 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`](#instr-stsliceconst) with [`PUSHSLICE`](#instr-pushslice) [`STSLICER`](#instr-stslicer) if the slice is too big. | `24` | +| **`CF81`** | `STZERO` | _`b - b'`_ | Stores one binary zero. | `24` | +| **`CF83`** | `STONE` | _`b - b'`_ | Stores one binary one. | `24` | + +### Cell Deserialization Primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :--------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------- | +| **`D0`** | `CTOS` | _`c - s`_ | 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. | `118/43` | +| **`D1`** | `ENDS` | _`s - `_ | Removes a _Slice_ `s` from the stack, and throws an exception if it is not empty. | `18/68` | +| **`D2cc`** | `[cc+1] LDI` | _`s - x s'`_ | Loads (i.e., parses) a signed `cc+1`-bit integer `x` from _Slice_ `s`, and returns the remainder of `s` as `s'`. | `26` | +| **`D3cc`** | `[cc+1] LDU` | _`s - x s'`_ | Loads an unsigned `cc+1`-bit integer `x` from _Slice_ `s`. | `26` | +| **`D4`** | `LDREF` | _`s - c s'`_ | Loads a cell reference `c` from `s`. | `18` | +| **`D5`** | `LDREFRTOS` | _`s - s' s''`_ | Equivalent to [`LDREF`](#instr-ldref) [`SWAP`](#instr-swap) [`CTOS`](#instr-ctos). | `118/43` | +| **`D6cc`** | `[cc+1] LDSLICE` | _`s - s'' s'`_ | Cuts the next `cc+1` bits of `s` into a separate _Slice_ `s''`. | `26` | +| **`D700`** | `LDIX` | _`s l - x s'`_ | Loads a signed `l`-bit (`0 <= l <= 257`) integer `x` from _Slice_ `s`, and returns the remainder of `s` as `s'`. | `26` | +| **`D701`** | `LDUX` | _`s l - x s'`_ | Loads an unsigned `l`-bit integer `x` from (the first `l` bits of) `s`, with `0 <= l <= 256`. | `26` | +| **`D702`** | `PLDIX` | _`s l - x`_ | Preloads a signed `l`-bit integer from _Slice_ `s`, for `0 <= l <= 257`. | `26` | +| **`D703`** | `PLDUX` | _`s l - x`_ | Preloads an unsigned `l`-bit integer from `s`, for `0 <= l <= 256`. | `26` | +| **`D704`** | `LDIXQ` | _`s l - x s' -1 or s 0`_ | Quiet version of [`LDIX`](#instr-ldix): loads a signed `l`-bit integer from `s` similarly to [`LDIX`](#instr-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. | `26` | +| **`D705`** | `LDUXQ` | _`s l - x s' -1 or s 0`_ | Quiet version of [`LDUX`](#instr-ldux). | `26` | +| **`D706`** | `PLDIXQ` | _`s l - x -1 or 0`_ | Quiet version of [`PLDIX`](#instr-pldix). | `26` | +| **`D707`** | `PLDUXQ` | _`s l - x -1 or 0`_ | Quiet version of [`PLDUX`](#instr-pldux). | `26` | +| **`D708cc`** | `[cc+1] LDI_l` | _`s - x s'`_ | A longer encoding for [`LDI`](#instr-ldi). | `34` | +| **`D709cc`** | `[cc+1] LDU_l` | _`s - x s'`_ | A longer encoding for [`LDU`](#instr-ldu). | `34` | +| **`D70Acc`** | `[cc+1] PLDI` | _`s - x`_ | Preloads a signed `cc+1`-bit integer from _Slice_ `s`. | `34` | +| **`D70Bcc`** | `[cc+1] PLDU` | _`s - x`_ | Preloads an unsigned `cc+1`-bit integer from `s`. | `34` | +| **`D70Ccc`** | `[cc+1] LDIQ` | _`s - x s' -1 or s 0`_ | A quiet version of [`LDI`](#instr-ldi). | `34` | +| **`D70Dcc`** | `[cc+1] LDUQ` | _`s - x s' -1 or s 0`_ | A quiet version of [`LDU`](#instr-ldu). | `34` | +| **`D70Ecc`** | `[cc+1] PLDIQ` | _`s - x -1 or 0`_ | A quiet version of [`PLDI`](#instr-pldi). | `34` | +| **`D70Fcc`** | `[cc+1] PLDUQ` | _`s - x -1 or 0`_ | A quiet version of [`PLDU`](#instr-pldu). | `34` | +| **`D714_c`** | `[32(c+1)] PLDUZ` | _`s - s x`_ | 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`](#instr-ifbitjmp) and similar instructions. | `26` | +| **`D718`** | `LDSLICEX` | _`s l - s'' s'`_ | Loads the first `0 <= l <= 1023` bits from _Slice_ `s` into a separate _Slice_ `s''`, returning the remainder of `s` as `s'`. | `26` | +| **`D719`** | `PLDSLICEX` | _`s l - s''`_ | Returns the first `0 <= l <= 1023` bits of `s` as `s''`. | `26` | +| **`D71A`** | `LDSLICEXQ` | _`s l - s'' s' -1 or s 0`_ | A quiet version of [`LDSLICEX`](#instr-ldslicex). | `26` | +| **`D71B`** | `PLDSLICEXQ` | _`s l - s' -1 or 0`_ | A quiet version of [`LDSLICEXQ`](#instr-ldslicexq). | `26` | +| **`D71Ccc`** | `[cc+1] LDSLICE_l` | _`s - s'' s'`_ | A longer encoding for [`LDSLICE`](#instr-ldslice). | `34` | +| **`D71Dcc`** | `[cc+1] PLDSLICE` | _`s - s''`_ | Returns the first `0 < cc+1 <= 256` bits of `s` as `s''`. | `34` | +| **`D71Ecc`** | `[cc+1] LDSLICEQ` | _`s - s'' s' -1 or s 0`_ | A quiet version of [`LDSLICE`](#instr-ldslice). | `34` | +| **`D71Fcc`** | `[cc+1] PLDSLICEQ` | _`s - s'' -1 or 0`_ | A quiet version of [`PLDSLICE`](#instr-pldslice). | `34` | +| **`D720`** | `SDCUTFIRST` | _`s l - s'`_ | Returns the first `0 <= l <= 1023` bits of `s`. It is equivalent to [`PLDSLICEX`](#instr-pldslicex). | `26` | +| **`D721`** | `SDSKIPFIRST` | _`s l - s'`_ | Returns all but the first `0 <= l <= 1023` bits of `s`. It is equivalent to [`LDSLICEX`](#instr-ldslicex) [`NIP`](#instr-nip). | `26` | +| **`D722`** | `SDCUTLAST` | _`s l - s'`_ | Returns the last `0 <= l <= 1023` bits of `s`. | `26` | +| **`D723`** | `SDSKIPLAST` | _`s l - s'`_ | Returns all but the last `0 <= l <= 1023` bits of `s`. | `26` | +| **`D724`** | `SDSUBSTR` | _`s l l' - s'`_ | Returns `0 <= l' <= 1023` bits of `s` starting from offset `0 <= l <= 1023`, thus extracting a bit substring out of the data of `s`. | `26` | +| **`D726`** | `SDBEGINSX` | _`s s' - s''`_ | 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`](#instr-sdpfxrev) can be considered a quiet version of [`SDBEGINSX`](#instr-sdbeginsx). | `26` | +| **`D727`** | `SDBEGINSXQ` | _`s s' - s'' -1 or s 0`_ | A quiet version of [`SDBEGINSX`](#instr-sdbeginsx). | `26` | +| **`D72A_xsss`** | `[slice] SDBEGINS` | _`s - s''`_ | 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. | `31` | +| **`D72E_xsss`** | `[slice] SDBEGINSQ` | _`s - s'' -1 or s 0`_ | A quiet version of [`SDBEGINS`](#instr-sdbegins). | `31` | +| **`D730`** | `SCUTFIRST` | _`s l r - s'`_ | Returns the first `0 <= l <= 1023` bits and first `0 <= r <= 4` references of `s`. | `26` | +| **`D731`** | `SSKIPFIRST` | _`s l r - s'`_ | Returns all but the first `l` bits of `s` and `r` references of `s`. | `26` | +| **`D732`** | `SCUTLAST` | _`s l r - s'`_ | Returns the last `0 <= l <= 1023` data bits and last `0 <= r <= 4` references of `s`. | `26` | +| **`D733`** | `SSKIPLAST` | _`s l r - s'`_ | Returns all but the last `l` bits of `s` and `r` references of `s`. | `26` | +| **`D734`** | `SUBSLICE` | _`s l r l' r' - s'`_ | 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. | `26` | +| **`D736`** | `SPLIT` | _`s l r - s' s''`_ | 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''`. | `26` | +| **`D737`** | `SPLITQ` | _`s l r - s' s'' -1 or s 0`_ | A quiet version of [`SPLIT`](#instr-split). | `26` | +| **`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`. | | +| **`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. | | +| **`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. | | +| **`D741`** | `SCHKBITS` | _`s l - `_ | 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. | `26/76` | +| **`D742`** | `SCHKREFS` | _`s r - `_ | Checks whether there are at least `r` references in _Slice_ `s`. | `26/76` | +| **`D743`** | `SCHKBITREFS` | _`s l r - `_ | Checks whether there are at least `l` data bits and `r` references in _Slice_ `s`. | `26/76` | +| **`D745`** | `SCHKBITSQ` | _`s l - ?`_ | Checks whether there are at least `l` data bits in _Slice_ `s`. | `26` | +| **`D746`** | `SCHKREFSQ` | _`s r - ?`_ | Checks whether there are at least `r` references in _Slice_ `s`. | `26` | +| **`D747`** | `SCHKBITREFSQ` | _`s l r - ?`_ | Checks whether there are at least `l` data bits and `r` references in _Slice_ `s`. | `26` | +| **`D748`** | `PLDREFVAR` | _`s n - c`_ | Returns the `n`-th cell reference of _Slice_ `s` for `0 <= n <= 3`. | `26` | +| **`D749`** | `SBITS` | _`s - l`_ | Returns the number of data bits in _Slice_ `s`. | `26` | +| **`D74A`** | `SREFS` | _`s - r`_ | Returns the number of references in _Slice_ `s`. | `26` | +| **`D74B`** | `SBITREFS` | _`s - l r`_ | Returns both the number of data bits and the number of references in `s`. | `26` | +| **`D74E_n`** | `[n] PLDREFIDX` | _`s - c`_ | Returns the `n`-th cell reference of _Slice_ `s`, where `0 <= n <= 3`. | `26` | +| **`D74C`** | `PLDREF` | _`s - c`_ | Preloads the first cell reference of a _Slice_. | `26` | +| **`D750`** | `LDILE4` | _`s - x s'`_ | Loads a little-endian signed 32-bit integer. | `26` | +| **`D751`** | `LDULE4` | _`s - x s'`_ | Loads a little-endian unsigned 32-bit integer. | `26` | +| **`D752`** | `LDILE8` | _`s - x s'`_ | Loads a little-endian signed 64-bit integer. | `26` | +| **`D753`** | `LDULE8` | _`s - x s'`_ | Loads a little-endian unsigned 64-bit integer. | `26` | +| **`D754`** | `PLDILE4` | _`s - x`_ | Preloads a little-endian signed 32-bit integer. | `26` | +| **`D755`** | `PLDULE4` | _`s - x`_ | Preloads a little-endian unsigned 32-bit integer. | `26` | +| **`D756`** | `PLDILE8` | _`s - x`_ | Preloads a little-endian signed 64-bit integer. | `26` | +| **`D757`** | `PLDULE8` | _`s - x`_ | Preloads a little-endian unsigned 64-bit integer. | `26` | +| **`D758`** | `LDILE4Q` | _`s - x s' -1 or s 0`_ | Quietly loads a little-endian signed 32-bit integer. | `26` | +| **`D759`** | `LDULE4Q` | _`s - x s' -1 or s 0`_ | Quietly loads a little-endian unsigned 32-bit integer. | `26` | +| **`D75A`** | `LDILE8Q` | _`s - x s' -1 or s 0`_ | Quietly loads a little-endian signed 64-bit integer. | `26` | +| **`D75B`** | `LDULE8Q` | _`s - x s' -1 or s 0`_ | Quietly loads a little-endian unsigned 64-bit integer. | `26` | +| **`D75C`** | `PLDILE4Q` | _`s - x -1 or 0`_ | Quietly preloads a little-endian signed 32-bit integer. | `26` | +| **`D75D`** | `PLDULE4Q` | _`s - x -1 or 0`_ | Quietly preloads a little-endian unsigned 32-bit integer. | `26` | +| **`D75E`** | `PLDILE8Q` | _`s - x -1 or 0`_ | Quietly preloads a little-endian signed 64-bit integer. | `26` | +| **`D75F`** | `PLDULE8Q` | _`s - x -1 or 0`_ | Quietly preloads a little-endian unsigned 64-bit integer. | `26` | +| **`D760`** | `LDZEROES` | _`s - n s'`_ | Returns the count `n` of leading zero bits in `s`, and removes these bits from `s`. | `26` | +| **`D761`** | `LDONES` | _`s - n s'`_ | Returns the count `n` of leading one bits in `s`, and removes these bits from `s`. | `26` | +| **`D762`** | `LDSAME` | _`s x - n s'`_ | Returns the count `n` of leading bits equal to `0 <= x <= 1` in `s`, and removes these bits from `s`. | `26` | +| **`D764`** | `SDEPTH` | _`s - x`_ | 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`. | `26` | +| **`D765`** | `CDEPTH` | _`c - x`_ | 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. | `26` | + +### TVM Instructions Content List + +- [Overview](/learn/tvm-instructions/instructions) +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [Tuple, List and Null](/learn/tvm-instructions/instructions/tuple-list-null) +- [Constants and Literals](/learn/tvm-instructions/instructions/constant) +- [Arithmetic Operations](/learn/tvm-instructions/instructions/arithmetic) +- [Data Comparison](/learn/tvm-instructions/instructions/data-comparison) +- [Cell Manipulation](/learn/tvm-instructions/instructions/cell-manipulation) +- [Continuation and Control Flow](/learn/tvm-instructions/instructions/control-flow) +- [Exception Generation and Handling](/learn/tvm-instructions/instructions/exception-gen-and-handling) +- [Dictionary Manipulation](/learn/tvm-instructions/instructions/dictionary-manipulation) +- [Application-specific Primitives](/learn/tvm-instructions/instructions/app-specific) +- [Miscellaneous](/learn/tvm-instructions/instructions/miscellaneous) From ddbc5192150df5770ce68308fa4992d8553f5e6e Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:00:59 +0800 Subject: [PATCH 209/219] New translations constant.mdx (Chinese Simplified) --- .../instructions/constant.mdx | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/constant.mdx diff --git a/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/constant.mdx b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/constant.mdx new file mode 100644 index 0000000000..bea2b33d00 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/constant.mdx @@ -0,0 +1,88 @@ +--- +hide_table_of_contents: true +wrapperClassName: bootstrap-wrapper +--- + +import { SearchField } from '@site/src/components/SearchField'; +import { constantOpcodes as opcodes } from '@site/src/data/opcodes'; + +# Constant or Literal Primitives + +### TVM Instructions Content List + +- [Overview](/learn/tvm-instructions/instructions) +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [Tuple, List and Null](/learn/tvm-instructions/instructions/tuple-list-null) +- [Constants and Literals](/learn/tvm-instructions/instructions/constant) +- [Arithmetic Operations](/learn/tvm-instructions/instructions/arithmetic) +- [Data Comparison](/learn/tvm-instructions/instructions/data-comparison) +- [Cell Manipulation](/learn/tvm-instructions/instructions/cell-manipulation) +- [Continuation and Control Flow](/learn/tvm-instructions/instructions/control-flow) +- [Exception Generation and Handling](/learn/tvm-instructions/instructions/exception-gen-and-handling) +- [Dictionary Manipulation](/learn/tvm-instructions/instructions/dictionary-manipulation) +- [Application-specific Primitives](/learn/tvm-instructions/instructions/app-specific) +- [Miscellaneous](/learn/tvm-instructions/instructions/miscellaneous) + + + +## Constant or Literal Primitives + +### Integer and Boolean Constants + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`7i`** | `[x] PUSHINT`
`[x] INT` | _`- x`_ | Pushes integer `x` into the stack. `-5 <= x <= 10`.
Here `i` equals four lower-order bits of `x` (`i=x mod 16`). | `18` | +| **`70`** | `ZERO`
`FALSE` | _`- 0`_ | | `18` | +| **`71`** | `ONE` | _`- 1`_ | | `18` | +| **`72`** | `TWO` | _`- 2`_ | | `18` | +| **`7A`** | `TEN` | _`- 10`_ | | `18` | +| **`7F`** | `TRUE` | _`- -1`_ | | `18` | +| **`80xx`** | `[xx] PUSHINT`
`[xx] INT` | _`- xx`_ | Pushes integer `xx`. `-128 <= xx <= 127`. | `26` | +| **`81xxxx`** | `[xxxx] PUSHINT`
`[xxxx] INT` | _`- xxxx`_ | Pushes integer `xxxx`. `-2^15 <= xx < 2^15`. | `34` | +| **`82lxxx`** | `[xxx] PUSHINT`
`[xxx] INT` | _`- xxx`_ | 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. | `23` | +| **`83xx`** | `[xx+1] PUSHPOW2` | _`- 2^(xx+1)`_ | (Quietly) pushes `2^(xx+1)` for `0 <= xx <= 255`.
`2^256` is a `NaN`. | `26` | +| **`83FF`** | `PUSHNAN` | _`- NaN`_ | Pushes a `NaN`. | `26` | +| **`84xx`** | `[xx+1] PUSHPOW2DEC` | _`- 2^(xx+1)-1`_ | Pushes `2^(xx+1)-1` for `0 <= xx <= 255`. | `26` | +| **`85xx`** | `[xx+1] PUSHNEGPOW2` | _`- -2^(xx+1)`_ | Pushes `-2^(xx+1)` for `0 <= xx <= 255`. | `26` | + +### Constant slices, continuations, cells, and references + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------------------------------- | :-------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`88`** | `[ref] PUSHREF` | _`- c`_ | 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). | `18` | +| **`89`** | `[ref] PUSHREFSLICE` | _`- s`_ | Similar to [`PUSHREF`](#instr-pushref), but converts the cell into a _Slice_. | `118/43` | +| **`8A`** | `[ref] PUSHREFCONT` | _`- cont`_ | Similar to [`PUSHREFSLICE`](#instr-pushrefslice), but makes a simple ordinary _Continuation_ out of the cell. | `118/43` | +| **`8Bxsss`** | `[slice] PUSHSLICE`
`[slice] SLICE` | _`- s`_ | 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. | `22` | +| **`8Crxxssss`** | `[slice] PUSHSLICE`
`[slice] SLICE` | _`- s`_ | 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. | `25` | +| **`8Drxxsssss`** | `[slice] PUSHSLICE`
`[slice] SLICE` | _`- s`_ | 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. | `28` | +| | `x{} PUSHSLICE`
`x{ABCD1234} PUSHSLICE`
`b{01101} PUSHSLICE` | _`- s`_ | Examples of [`PUSHSLICE`](#instr-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`](#instr-pushslice) with [`PUSHREFSLICE`](#instr-pushrefslice) in certain situations (e.g. if there’s not enough space in the current continuation). | | +| | ` PUSHREF`
` PUSHREFSLICE` | _`- c/s`_ | Examples of [`PUSHREF`](#instr-pushref) and [`PUSHREFSLICE`](#instr-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). | | +| **`8F_rxxcccc`** | `[builder] PUSHCONT`
`[builder] CONT` | _`- c`_ | 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`. | `26` | +| **`9xccc`** | `[builder] PUSHCONT`
`[builder] CONT` | _`- c`_ | Pushes a continuation made from `builder`.
_Details:_ Pushes an `x`-byte continuation for `0 <= x <= 15`. | `18` | +| | `<{ code }> PUSHCONT`
`<{ code }> CONT`
`CONT:<{ code }>` | _`- c`_ | Pushes a continuation with code `code`.
Note that the assembler can replace [`PUSHCONT`](#instr-pushcont) with [`PUSHREFCONT`](#instr-pushrefcont) in certain situations (e.g. if there’s not enough space in the current continuation). | | + +### TVM Instructions Content List + +- [Overview](/learn/tvm-instructions/instructions) +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [Tuple, List and Null](/learn/tvm-instructions/instructions/tuple-list-null) +- [Constants and Literals](/learn/tvm-instructions/instructions/constant) +- [Arithmetic Operations](/learn/tvm-instructions/instructions/arithmetic) +- [Data Comparison](/learn/tvm-instructions/instructions/data-comparison) +- [Cell Manipulation](/learn/tvm-instructions/instructions/cell-manipulation) +- [Continuation and Control Flow](/learn/tvm-instructions/instructions/control-flow) +- [Exception Generation and Handling](/learn/tvm-instructions/instructions/exception-gen-and-handling) +- [Dictionary Manipulation](/learn/tvm-instructions/instructions/dictionary-manipulation) +- [Application-specific Primitives](/learn/tvm-instructions/instructions/app-specific) +- [Miscellaneous](/learn/tvm-instructions/instructions/miscellaneous) From b84d4d30549242bb312a33d21afcf259c3e41d28 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:01:00 +0800 Subject: [PATCH 210/219] New translations control-flow.mdx (Chinese Simplified) --- .../instructions/control-flow.mdx | 205 ++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/control-flow.mdx diff --git a/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/control-flow.mdx b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/control-flow.mdx new file mode 100644 index 0000000000..9d54b05f16 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/control-flow.mdx @@ -0,0 +1,205 @@ +--- +hide_table_of_contents: true +wrapperClassName: bootstrap-wrapper +--- + +import { SearchField } from '@site/src/components/SearchField'; +import { continuationOpcodes as opcodes } from '@site/src/data/opcodes'; + +# Continuation and control flow primitives + +### TVM Instructions Content List + +- [Overview](/learn/tvm-instructions/instructions) +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [Tuple, List and Null](/learn/tvm-instructions/instructions/tuple-list-null) +- [Constants and Literals](/learn/tvm-instructions/instructions/constant) +- [Arithmetic Operations](/learn/tvm-instructions/instructions/arithmetic) +- [Data Comparison](/learn/tvm-instructions/instructions/data-comparison) +- [Cell Manipulation](/learn/tvm-instructions/instructions/cell-manipulation) +- [Continuation and Control Flow](/learn/tvm-instructions/instructions/control-flow) +- [Exception Generation and Handling](/learn/tvm-instructions/instructions/exception-gen-and-handling) +- [Dictionary Manipulation](/learn/tvm-instructions/instructions/dictionary-manipulation) +- [Application-specific Primitives](/learn/tvm-instructions/instructions/app-specific) +- [Miscellaneous](/learn/tvm-instructions/instructions/miscellaneous) + + + +## Continuation and control flow primitives + +### Unconditional control flow primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`D8`** | `EXECUTE`
`CALLX` | _`c - `_ | _Calls_, or _executes_, continuation `c`. | `18` | +| **`D9`** | `JMPX` | _`c - `_ | _Jumps_, or transfers control, to continuation `c`.
The remainder of the previous current continuation `cc` is discarded. | `18` | +| **`DApr`** | `[p] [r] CALLXARGS` | _`c - `_ | _Calls_ continuation `c` with `p` parameters and expecting `r` return values
`0 <= p <= 15`, `0 <= r <= 15` | `26` | +| **`DB0p`** | `[p] -1 CALLXARGS` | _`c - `_ | _Calls_ continuation `c` with `0 <= p <= 15` parameters, expecting an arbitrary number of return values. | `26` | +| **`DB1p`** | `[p] JMPXARGS` | _`c - `_ | _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). | `26` | +| **`DB2r`** | `[r] RETARGS` | | _Returns_ to `c0`, with `0 <= r <= 15` return values taken from the current stack. | `26` | +| **`DB30`** | `RET`
`RETTRUE` | | _Returns_ to the continuation at `c0`. The remainder of the current continuation `cc` is discarded.
Approximately equivalent to [`c0 PUSHCTR`](#instr-pushctr) [`JMPX`](#instr-jmpx). | `26` | +| **`DB31`** | `RETALT`
`RETFALSE` | | _Returns_ to the continuation at `c1`.
Approximately equivalent to [`c1 PUSHCTR`](#instr-pushctr) [`JMPX`](#instr-jmpx). | `26` | +| **`DB32`** | `BRANCH`
`RETBOOL` | _`f - `_ | Performs [`RETTRUE`](#instr-ret) if integer `f!=0`, or [`RETFALSE`](#instr-retalt) if `f=0`. | `26` | +| **`DB34`** | `CALLCC` | _`c - `_ | _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`). | `26` | +| **`DB35`** | `JMPXDATA` | _`c - `_ | Similar to [`CALLCC`](#instr-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`. | `26` | +| **`DB36pr`** | `[p] [r] CALLCCARGS` | _`c - `_ | Similar to [`CALLXARGS`](#instr-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`. | `34` | +| **`DB38`** | `CALLXVARARGS` | _`c p r - `_ | Similar to [`CALLXARGS`](#instr-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`. | `26` | +| **`DB39`** | `RETVARARGS` | _`p r - `_ | Similar to [`RETARGS`](#instr-retargs). | `26` | +| **`DB3A`** | `JMPXVARARGS` | _`c p r - `_ | Similar to [`JMPXARGS`](#instr-jmpxargs). | `26` | +| **`DB3B`** | `CALLCCVARARGS` | _`c p r - `_ | Similar to [`CALLCCARGS`](#instr-callccargs). | `26` | +| **`DB3C`** | `[ref] CALLREF` | | Equivalent to [`PUSHREFCONT`](#instr-pushrefcont) [`CALLX`](#instr-execute). | `126/51` | +| **`DB3D`** | `[ref] JMPREF` | | Equivalent to [`PUSHREFCONT`](#instr-pushrefcont) [`JMPX`](#instr-jmpx). | `126/51` | +| **`DB3E`** | `[ref] JMPREFDATA` | | Equivalent to [`PUSHREFCONT`](#instr-pushrefcont) [`JMPXDATA`](#instr-jmpxdata). | `126/51` | +| **`DB3F`** | `RETDATA` | | Equivalent to [`c0 PUSHCTR`](#instr-pushctr) [`JMPXDATA`](#instr-jmpxdata). In this way, the remainder of the current continuation is converted into a _Slice_ and returned to the caller. | `26` | + +### Conditional control flow primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`DC`** | `IFRET`
`IFNOT:` | _`f - `_ | Performs a [`RET`](#instr-ret), but only if integer `f` is non-zero. If `f` is a `NaN`, throws an integer overflow exception. | `18` | +| **`DD`** | `IFNOTRET`
`IF:` | _`f - `_ | Performs a [`RET`](#instr-ret), but only if integer `f` is zero. | `18` | +| **`DE`** | `IF` | _`f c - `_ | Performs [`EXECUTE`](#instr-execute) for `c` (i.e., _executes_ `c`), but only if integer `f` is non-zero. Otherwise simply discards both values. | `18` | +| **`DE`** | `IF:<{ code }>`
`<{ code }>IF` | _`f -`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`IF`](#instr-if). | | +| **`DF`** | `IFNOT` | _`f c - `_ | Executes continuation `c`, but only if integer `f` is zero. Otherwise simply discards both values. | `18` | +| **`DF`** | `IFNOT:<{ code }>`
`<{ code }>IFNOT` | _`f -`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`IFNOT`](#instr-ifnot). | | +| **`E0`** | `IFJMP` | _`f c - `_ | Jumps to `c` (similarly to [`JMPX`](#instr-jmpx)), but only if `f` is non-zero. | `18` | +| **`E0`** | `IFJMP:<{ code }>` | _`f -`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`IFJMP`](#instr-ifjmp). | | +| **`E1`** | `IFNOTJMP` | _`f c - `_ | Jumps to `c` (similarly to [`JMPX`](#instr-jmpx)), but only if `f` is zero. | `18` | +| **`E1`** | `IFNOTJMP:<{ code }>` | _`f -`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`IFNOTJMP`](#instr-ifnotjmp). | | +| **`E2`** | `IFELSE` | _`f c c' - `_ | If integer `f` is non-zero, executes `c`, otherwise executes `c'`. Equivalent to [`CONDSELCHK`](#instr-condselchk) [`EXECUTE`](#instr-execute). | `18` | +| **`E2`** | `IF:<{ code1 }>ELSE<{ code2 }>` | _`f -`_ | Equivalent to [`<{ code1 }> CONT`](#instr-pushcont) [`<{ code2 }> CONT`](#instr-pushcont) [`IFELSE`](#instr-ifelse). | | +| **`E300`** | `[ref] IFREF` | _`f - `_ | Equivalent to [`PUSHREFCONT`](#instr-pushrefcont) [`IF`](#instr-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. | `26/126/51` | +| **`E301`** | `[ref] IFNOTREF` | _`f - `_ | Equivalent to [`PUSHREFCONT`](#instr-pushrefcont) [`IFNOT`](#instr-ifnot). | `26/126/51` | +| **`E302`** | `[ref] IFJMPREF` | _`f - `_ | Equivalent to [`PUSHREFCONT`](#instr-pushrefcont) [`IFJMP`](#instr-ifjmp). | `26/126/51` | +| **`E303`** | `[ref] IFNOTJMPREF` | _`f - `_ | Equivalent to [`PUSHREFCONT`](#instr-pushrefcont) [`IFNOTJMP`](#instr-ifnotjmp). | `26/126/51` | +| **`E304`** | `CONDSEL` | _`f x y - x or y`_ | 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`](#instr-rot) [`ISZERO`](#instr-iszero) [`INC`](#instr-inc) [`ROLLX`](#instr-rollx) [`NIP`](#instr-nip). | `26` | +| **`E305`** | `CONDSELCHK` | _`f x y - x or y`_ | Same as [`CONDSEL`](#instr-condsel), but first checks whether `x` and `y` have the same type. | `26` | +| **`E308`** | `IFRETALT` | _`f -`_ | Performs [`RETALT`](#instr-retalt) if integer `f!=0`. | `26` | +| **`E309`** | `IFNOTRETALT` | _`f -`_ | Performs [`RETALT`](#instr-retalt) if integer `f=0`. | `26` | +| **`E30D`** | `[ref] IFREFELSE` | _`f c -`_ | Equivalent to [`PUSHREFCONT`](#instr-pushrefcont) [`SWAP`](#instr-swap) [`IFELSE`](#instr-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. | `26/126/51` | +| **`E30E`** | `[ref] IFELSEREF` | _`f c -`_ | Equivalent to [`PUSHREFCONT`](#instr-pushrefcont) [`IFELSE`](#instr-ifelse). | `26/126/51` | +| **`E30F`** | `[ref] [ref] IFREFELSEREF` | _`f -`_ | Equivalent to [`PUSHREFCONT`](#instr-pushrefcont) [`PUSHREFCONT`](#instr-pushrefcont) [`IFELSE`](#instr-ifelse). | `126/51` | +| **`E39_n`** | `[n] IFBITJMP` | _`x c - x`_ | Checks whether bit `0 <= n <= 31` is set in integer `x`, and if so, performs [`JMPX`](#instr-jmpx) to continuation `c`. Value `x` is left in the stack. | `26` | +| **`E3B_n`** | `[n] IFNBITJMP` | _`x c - x`_ | Jumps to `c` if bit `0 <= n <= 31` is not set in integer `x`. | `26` | +| **`E3D_n`** | `[ref] [n] IFBITJMPREF` | _`x - x`_ | Performs a [`JMPREF`](#instr-jmpref) if bit `0 <= n <= 31` is set in integer `x`. | `126/51` | +| **`E3F_n`** | `[ref] [n] IFNBITJMPREF` | _`x - x`_ | Performs a [`JMPREF`](#instr-jmpref) if bit `0 <= n <= 31` is not set in integer `x`. | `126/51` | + +### Control flow primitives: loops + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :------------------ | :----------------------------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------- | +| **`E4`** | `REPEAT` | _`n c - `_ | 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`](#instr-ret) inside the code of `c` works as a `continue`, not as a `break`. One should use either alternative (experimental) loops or alternative [`RETALT`](#instr-retalt) (along with a [`SETEXITALT`](#instr-setexitalt) before the loop) to `break` out of a loop. | `18` | +| **`E4`** | `REPEAT:<{ code }>`
`<{ code }>REPEAT` | _`n -`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`REPEAT`](#instr-repeat). | | +| **`E5`** | `REPEATEND`
`REPEAT:` | _`n - `_ | Similar to [`REPEAT`](#instr-repeat), but it is applied to the current continuation `cc`. | `18` | +| **`E6`** | `UNTIL` | _`c - `_ | 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. | `18` | +| **`E6`** | `UNTIL:<{ code }>`
`<{ code }>UNTIL` | _`-`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`UNTIL`](#instr-until). | | +| **`E7`** | `UNTILEND`
`UNTIL:` | _`-`_ | Similar to [`UNTIL`](#instr-until), but executes the current continuation `cc` in a loop. When the loop exit condition is satisfied, performs a [`RET`](#instr-ret). | `18` | +| **`E8`** | `WHILE` | _`c' c - `_ | 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. | `18` | +| **`E8`** | `WHILE:<{ cond }>DO<{ code }>` | _`-`_ | Equivalent to [`<{ cond }> CONT`](#instr-pushcont) [`<{ code }> CONT`](#instr-pushcont) [`WHILE`](#instr-while). | | +| **`E9`** | `WHILEEND` | _`c' - `_ | Similar to [`WHILE`](#instr-while), but uses the current continuation `cc` as the loop body. | `18` | +| **`EA`** | `AGAIN` | _`c - `_ | Similar to [`REPEAT`](#instr-repeat), but executes `c` infinitely many times. A [`RET`](#instr-ret) only begins a new iteration of the infinite loop, which can be exited only by an exception, or a [`RETALT`](#instr-retalt) (or an explicit [`JMPX`](#instr-jmpx)). | `18` | +| **`EA`** | `AGAIN:<{ code }>`
`<{ code }>AGAIN` | _`-`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`AGAIN`](#instr-again). | | +| **`EB`** | `AGAINEND`
`AGAIN:` | _`-`_ | Similar to [`AGAIN`](#instr-again), but performed with respect to the current continuation `cc`. | `18` | +| **`E314`** | `REPEATBRK` | _`n c -`_ | Similar to [`REPEAT`](#instr-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`](#instr-retalt) could be used to break out of the loop body. | `26` | +| **`E314`** | `REPEATBRK:<{ code }>`
`<{ code }>REPEATBRK` | _`n -`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`REPEATBRK`](#instr-repeatbrk). | | +| **`E315`** | `REPEATENDBRK` | _`n -`_ | Similar to [`REPEATEND`](#instr-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`](#instr-samealtsave) [`REPEATEND`](#instr-repeatend). | `26` | +| **`E316`** | `UNTILBRK` | _`c -`_ | Similar to [`UNTIL`](#instr-until), but also modifies `c1` in the same way as [`REPEATBRK`](#instr-repeatbrk). | `26` | +| **`E316`** | `UNTILBRK:<{ code }>` | _`-`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`UNTILBRK`](#instr-untilbrk). | | +| **`E317`** | `UNTILENDBRK`
`UNTILBRK:` | _`-`_ | Equivalent to [`SAMEALTSAVE`](#instr-samealtsave) [`UNTILEND`](#instr-untilend). | `26` | +| **`E318`** | `WHILEBRK` | _`c' c -`_ | Similar to [`WHILE`](#instr-while), but also modifies `c1` in the same way as [`REPEATBRK`](#instr-repeatbrk). | `26` | +| **`E318`** | `WHILEBRK:<{ cond }>DO<{ code }>` | _`-`_ | Equivalent to [`<{ cond }> CONT`](#instr-pushcont) [`<{ code }> CONT`](#instr-pushcont) [`WHILEBRK`](#instr-whilebrk). | | +| **`E319`** | `WHILEENDBRK` | _`c -`_ | Equivalent to [`SAMEALTSAVE`](#instr-samealtsave) [`WHILEEND`](#instr-whileend). | `26` | +| **`E31A`** | `AGAINBRK` | _`c -`_ | Similar to [`AGAIN`](#instr-again), but also modifies `c1` in the same way as [`REPEATBRK`](#instr-repeatbrk). | `26` | +| **`E31A`** | `AGAINBRK:<{ code }>` | _`-`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`AGAINBRK`](#instr-againbrk). | | +| **`E31B`** | `AGAINENDBRK`
`AGAINBRK:` | _`-`_ | Equivalent to [`SAMEALTSAVE`](#instr-samealtsave) [`AGAINEND`](#instr-againend). | `26` | + +### Manipulating the stack of continuations + +Here `s"` is the [fee for moving stack elements between continuations](#11-gas-prices). It is equal to the size of the resulting stack minus 32 (or 0 if the stack is smaller than 32). + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :--------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`ECrn`** | `[r] [n] SETCONTARGS` | _`x_1 x_2...x_r c - c'`_ | Similar to [`[r] -1 SETCONTARGS`](#instr-setcontargs-n), 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. | `26+s”` | +| **`EC0n`** | `[n] SETNUMARGS` | _`c - c'`_ | 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. | `26` | +| **`ECrF`** | `[r] -1 SETCONTARGS` | _`x_1 x_2...x_r c - c'`_ | 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. | `26+s”` | +| **`ED0p`** | `[p] RETURNARGS` | _`-`_ | Leaves only the top `0 <= p <= 15` values in the current stack (somewhat similarly to [`ONLYTOPX`](#instr-onlytopx)), with all the unused bottom values not discarded, but saved into continuation `c0` in the same way as [`SETCONTARGS`](#instr-setcontargs-n) does. | `26+s”` | +| **`ED10`** | `RETURNVARARGS` | _`p -`_ | Similar to [`RETURNARGS`](#instr-returnargs), but with Integer `0 <= p <= 255` taken from the stack. | `26+s”` | +| **`ED11`** | `SETCONTVARARGS` | _`x_1 x_2...x_r c r n - c'`_ | Similar to [`SETCONTARGS`](#instr-setcontargs-n), but with `0 <= r <= 255` and `-1 <= n <= 255` taken from the stack. | `26+s”` | +| **`ED12`** | `SETNUMVARARGS` | _`c n - c'`_ | `-1 <= n <= 255`
If `n=-1`, this operation does nothing (`c'=c`).
Otherwise its action is similar to [`[n] SETNUMARGS`](#instr-setnumargs), but with `n` taken from the stack. | `26` | + +### Creating simple continuations and closures + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`ED1E`** | `BLESS` | _`s - c`_ | Transforms a _Slice_ `s` into a simple ordinary continuation `c`, with `c.code=s` and an empty stack and savelist. | `26` | +| **`ED1F`** | `BLESSVARARGS` | _`x_1...x_r s r n - c`_ | Equivalent to [`ROT`](#instr-rot) [`BLESS`](#instr-bless) [`ROTREV`](#instr-rotrev) [`SETCONTVARARGS`](#instr-setcontvarargs). | `26+s”` | +| **`EErn`** | `[r] [n] BLESSARGS` | _`x_1...x_r s - c`_ | `0 <= r <= 15`, `-1 <= n <= 14`
Equivalent to [`BLESS`](#instr-bless) [`[r] [n] SETCONTARGS`](#instr-setcontargs-n).
The value of `n` is represented inside the instruction by the 4-bit integer `n mod 16`. | `26` | +| **`EE0n`** | `[n] BLESSNUMARGS` | _`s - c`_ | Also transforms a _Slice_ `s` into a _Continuation_ `c`, but sets `c.nargs` to `0 <= n <= 14`. | `26` | + +### Operations with continuation savelists and control registers + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :----------------------------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------- | +| **`ED4i`** | `c[i] PUSHCTR`
`c[i] PUSH` | _`- x`_ | 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. | `26` | +| **`ED44`** | `c4 PUSHCTR`
`c4 PUSH` | _`- x`_ | Pushes the “global data root'' cell reference, thus enabling access to persistent smart-contract data. | `26` | +| **`ED5i`** | `c[i] POPCTR`
`c[i] POP` | _`x - `_ | 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. | `26` | +| **`ED54`** | `c4 POPCTR`
`c4 POP` | _`x -`_ | Sets the “global data root'' cell reference, thus allowing modification of persistent smart-contract data. | `26` | +| **`ED6i`** | `c[i] SETCONT`
`c[i] SETCONTCTR` | _`x c - c'`_ | 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`](#instr-setcontctr), [`POPCTR`](#instr-popctr), and [`PUSHCTR`](#instr-pushctr). | `26` | +| **`ED7i`** | `c[i] SETRETCTR` | _`x - `_ | Equivalent to [`c0 PUSHCTR`](#instr-pushctr) [`c[i] SETCONTCTR`](#instr-setcontctr) [`c0 POPCTR`](#instr-popctr). | `26` | +| **`ED8i`** | `c[i] SETALTCTR` | _`x - `_ | Equivalent to [`c1 PUSHCTR`](#instr-pushctr) [`c[i] SETCONTCTR`](#instr-setcontctr) [`c1 POPCTR`](#instr-popctr). | `26` | +| **`ED9i`** | `c[i] POPSAVE`
`c[i] POPCTRSAVE` | _`x -`_ | Similar to [`c[i] POPCTR`](#instr-popctr), but also saves the old value of `c[i]` into continuation `c0`.
Equivalent (up to exceptions) to [`c[i] SAVECTR`](#instr-save) [`c[i] POPCTR`](#instr-popctr). | `26` | +| **`EDAi`** | `c[i] SAVE`
`c[i] SAVECTR` | | 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`](#instr-pushctr) [`c[i] SETRETCTR`](#instr-setretctr). | `26` | +| **`EDBi`** | `c[i] SAVEALT`
`c[i] SAVEALTCTR` | | Similar to [`c[i] SAVE`](#instr-save), but saves the current value of `c[i]` into the savelist of `c1`, not `c0`. | `26` | +| **`EDCi`** | `c[i] SAVEBOTH`
`c[i] SAVEBOTHCTR` | | Equivalent to [`c[i] SAVE`](#instr-save) [`c[i] SAVEALT`](#instr-savealt). | `26` | +| **`EDE0`** | `PUSHCTRX` | _`i - x`_ | Similar to [`c[i] PUSHCTR`](#instr-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`. | `26` | +| **`EDE1`** | `POPCTRX` | _`x i - `_ | Similar to [`c[i] POPCTR`](#instr-popctr), but with `0 <= i <= 255` from the stack. | `26` | +| **`EDE2`** | `SETCONTCTRX` | _`x c i - c'`_ | Similar to [`c[i] SETCONTCTR`](#instr-setcontctr), but with `0 <= i <= 255` from the stack. | `26` | +| **`EDF0`** | `COMPOS`
`BOOLAND` | _`c c' - c''`_ | 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`](#instr-swap) [`c0 SETCONT`](#instr-setcontctr). | `26` | +| **`EDF1`** | `COMPOSALT`
`BOOLOR` | _`c c' - c''`_ | 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`](#instr-swap) [`c1 SETCONT`](#instr-setcontctr). | `26` | +| **`EDF2`** | `COMPOSBOTH` | _`c c' - c''`_ | 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`''. | `26` | +| **`EDF3`** | `ATEXIT` | _`c - `_ | Sets `c0` to `compose0(c, c0)`. In other words, `c` will be executed before exiting current subroutine. | `26` | +| **`EDF3`** | `ATEXIT:<{ code }>`
`<{ code }>ATEXIT` | _`-`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`ATEXIT`](#instr-atexit). | | +| **`EDF4`** | `ATEXITALT` | _`c - `_ | Sets `c1` to `compose1(c, c1)`. In other words, `c` will be executed before exiting current subroutine by its alternative return path. | `26` | +| **`EDF4`** | `ATEXITALT:<{ code }>`
`<{ code }>ATEXITALT` | _`-`_ | Equivalent to [`<{ code }> CONT`](#instr-pushcont) [`ATEXITALT`](#instr-atexitalt). | | +| **`EDF5`** | `SETEXITALT` | _`c - `_ | Sets `c1` to `compose1(compose0(c, c0), c1)`,
In this way, a subsequent [`RETALT`](#instr-retalt) will first execute `c`, then transfer control to the original `c0`. This can be used, for instance, to exit from nested loops. | `26` | +| **`EDF6`** | `THENRET` | _`c - c'`_ | Computes `compose0(c, c0)`. | `26` | +| **`EDF7`** | `THENRETALT` | _`c - c'`_ | Computes `compose0(c, c1)` | `26` | +| **`EDF8`** | `INVERT` | _`-`_ | Interchanges `c0` and `c1`. | `26` | +| **`EDF9`** | `BOOLEVAL` | _`c - ?`_ | 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. | `26` | +| **`EDFA`** | `SAMEALT` | _`-`_ | Sets `c1` to `c0`. Equivalent to [`c0 PUSHCTR`](#instr-pushctr) [`c1 POPCTR`](#instr-popctr). | `26` | +| **`EDFB`** | `SAMEALTSAVE` | _`-`_ | Sets `c1` to `c0`, but first saves the old value of `c1` into the savelist of `c0`.
Equivalent to [`c1 SAVE`](#instr-save) [`SAMEALT`](#instr-samealt). | `26` | + +### Dictionary subroutine calls and jumps + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`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`](#instr-pushint-4) [`c3 PUSHCTR`](#instr-pushctr) [`EXECUTE`](#instr-execute). | | +| **`F12_n`** | `[n] CALL`
`[n] CALLDICT` | _`- n`_ | For `0 <= n < 2^14`, an encoding of [`[n] CALL`](#instr-calldict) for larger values of `n`. | | +| **`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`](#instr-pushint-4) [`c3 PUSHCTR`](#instr-pushctr) [`JMPX`](#instr-jmpx). | | +| **`F1A_n`** | `[n] PREPARE`
`[n] PREPAREDICT` | _` - n c`_ | Equivalent to [`n PUSHINT`](#instr-pushint-4) [`c3 PUSHCTR`](#instr-pushctr), for `0 <= n < 2^14`.
In this way, [`[n] CALL`](#instr-calldict) is approximately equivalent to [`[n] PREPARE`](#instr-preparedict) [`EXECUTE`](#instr-execute), and [`[n] JMP`](#instr-jmpdict) is approximately equivalent to [`[n] PREPARE`](#instr-preparedict) [`JMPX`](#instr-jmpx).
One might use, for instance, [`CALLXARGS`](#instr-callxargs) or [`CALLCC`](#instr-callcc) instead of [`EXECUTE`](#instr-execute) here. | | + +### TVM Instructions Content List + +- [Overview](/learn/tvm-instructions/instructions) +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [Tuple, List and Null](/learn/tvm-instructions/instructions/tuple-list-null) +- [Constants and Literals](/learn/tvm-instructions/instructions/constant) +- [Arithmetic Operations](/learn/tvm-instructions/instructions/arithmetic) +- [Data Comparison](/learn/tvm-instructions/instructions/data-comparison) +- [Cell Manipulation](/learn/tvm-instructions/instructions/cell-manipulation) +- [Continuation and Control Flow](/learn/tvm-instructions/instructions/control-flow) +- [Exception Generation and Handling](/learn/tvm-instructions/instructions/exception-gen-and-handling) +- [Dictionary Manipulation](/learn/tvm-instructions/instructions/dictionary-manipulation) +- [Application-specific Primitives](/learn/tvm-instructions/instructions/app-specific) +- [Miscellaneous](/learn/tvm-instructions/instructions/miscellaneous) From 59f574cbb355300dd0e96e59e5628a3daa1a0833 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:01:01 +0800 Subject: [PATCH 211/219] New translations data-comparison.mdx (Chinese Simplified) --- .../instructions/data-comparison.mdx | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/data-comparison.mdx diff --git a/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/data-comparison.mdx b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/data-comparison.mdx new file mode 100644 index 0000000000..e127ab4818 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/data-comparison.mdx @@ -0,0 +1,103 @@ +--- +hide_table_of_contents: true +wrapperClassName: bootstrap-wrapper +--- + +import { SearchField } from '@site/src/components/SearchField'; +import { comparisonOpcodes as opcodes } from '@site/src/data/opcodes'; + +# Comparison Primitives + +### TVM Instructions Content List + +- [Overview](/learn/tvm-instructions/instructions) +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [Tuple, List and Null](/learn/tvm-instructions/instructions/tuple-list-null) +- [Constants and Literals](/learn/tvm-instructions/instructions/constant) +- [Arithmetic Operations](/learn/tvm-instructions/instructions/arithmetic) +- [Data Comparison](/learn/tvm-instructions/instructions/data-comparison) +- [Cell Manipulation](/learn/tvm-instructions/instructions/cell-manipulation) +- [Continuation and Control Flow](/learn/tvm-instructions/instructions/control-flow) +- [Exception Generation and Handling](/learn/tvm-instructions/instructions/exception-gen-and-handling) +- [Dictionary Manipulation](/learn/tvm-instructions/instructions/dictionary-manipulation) +- [Application-specific Primitives](/learn/tvm-instructions/instructions/app-specific) +- [Miscellaneous](/learn/tvm-instructions/instructions/miscellaneous) + + + +## Comparison Primitives + +### Integer comparison + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`B8`** | `SGN` | _`x - sgn(x)`_ | Computes the sign of an integer `x`:
`-1` if `x<0`, `0` if `x=0`, `1` if `x>0`. | `18` | +| **`B9`** | `LESS` | _`x y - xy`_ | | `18` | +| **`BD`** | `NEQ` | _`x y - x!=y`_ | Equivalent to [`EQUAL`](#instr-equal) [`NOT`](#instr-not). | `18` | +| **`BE`** | `GEQ` | _`x y - x>=y`_ | Equivalent to [`LESS`](#instr-less) [`NOT`](#instr-not). | `18` | +| **`BF`** | `CMP` | _`x y - sgn(x-y)`_ | Computes the sign of `x-y`:
`-1` if `xy`.
No integer overflow can occur here unless `x` or `y` is a `NaN`. | `18` | +| **`C0yy`** | `[yy] EQINT` | _`x - x=yy`_ | Returns `-1` if `x=yy`, `0` otherwise.
`-2^7 <= yy < 2^7`. | `26` | +| **`C000`** | `ISZERO` | _`x - x=0`_ | Checks whether an integer is zero. Corresponds to Forth's `0=`. | `26` | +| **`C1yy`** | `[yy] LESSINT`
`[yy-1] LEQINT` | _`x - x`-2^7 <= yy < 2^7`. | `26` | +| **`C100`** | `ISNEG` | _`x - x<0`_ | Checks whether an integer is negative. Corresponds to Forth's `0<`. | `26` | +| **`C101`** | `ISNPOS` | _`x - x<=0`_ | Checks whether an integer is non-positive. | `26` | +| **`C2yy`** | `[yy] GTINT`
`[yy+1] GEQINT` | _`x - x>yy`_ | Returns `-1` if `x>yy`, `0` otherwise.
`-2^7 <= yy < 2^7`. | `26` | +| **`C200`** | `ISPOS` | _`x - x>0`_ | Checks whether an integer is positive. Corresponds to Forth's `0>`. | `26` | +| **`C2FF`** | `ISNNEG` | _`x - x >=0`_ | Checks whether an integer is non-negative. | `26` | +| **`C3yy`** | `[yy] NEQINT` | _`x - x!=yy`_ | Returns `-1` if `x!=yy`, `0` otherwise.
`-2^7 <= yy < 2^7`. | `26` | +| **`C4`** | `ISNAN` | _`x - x=NaN`_ | Checks whether `x` is a `NaN`. | `18` | +| **`C5`** | `CHKNAN` | _`x - x`_ | Throws an arithmetic overflow exception if `x` is a `NaN`. | `18/68` | + +### Other comparison + +Most of these "other comparison" primitives actually compare the data portions of _Slices_ as bitstrings (ignoring references if not stated otherwise). + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :----------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`C700`** | `SEMPTY` | _`s - ?`_ | Checks whether a _Slice_ `s` is empty (i.e., contains no bits of data and no cell references). | `26` | +| **`C701`** | `SDEMPTY` | _`s - ?`_ | Checks whether _Slice_ `s` has no bits of data. | `26` | +| **`C702`** | `SREMPTY` | _`s - ?`_ | Checks whether _Slice_ `s` has no references. | `26` | +| **`C703`** | `SDFIRST` | _`s - ?`_ | Checks whether the first bit of _Slice_ `s` is a one. | `26` | +| **`C704`** | `SDLEXCMP` | _`s s' - x`_ | Compares the data of `s` lexicographically with the data of `s'`, returning `-1`, 0, or 1 depending on the result. | `26` | +| **`C705`** | `SDEQ` | _`s s' - ?`_ | Checks whether the data parts of `s` and `s'` coincide, equivalent to [`SDLEXCMP`](#instr-sdlexcmp) [`ISZERO`](#instr-iszero). | `26` | +| **`C708`** | `SDPFX` | _`s s' - ?`_ | Checks whether `s` is a prefix of `s'`. | `26` | +| **`C709`** | `SDPFXREV` | _`s s' - ?`_ | Checks whether `s'` is a prefix of `s`, equivalent to [`SWAP`](#instr-swap) [`SDPFX`](#instr-sdpfx). | `26` | +| **`C70A`** | `SDPPFX` | _`s s' - ?`_ | Checks whether `s` is a proper prefix of `s'` (i.e., a prefix distinct from `s'`). | `26` | +| **`C70B`** | `SDPPFXREV` | _`s s' - ?`_ | Checks whether `s'` is a proper prefix of `s`. | `26` | +| **`C70C`** | `SDSFX` | _`s s' - ?`_ | Checks whether `s` is a suffix of `s'`. | `26` | +| **`C70D`** | `SDSFXREV` | _`s s' - ?`_ | Checks whether `s'` is a suffix of `s`. | `26` | +| **`C70E`** | `SDPSFX` | _`s s' - ?`_ | Checks whether `s` is a proper suffix of `s'`. | `26` | +| **`C70F`** | `SDPSFXREV` | _`s s' - ?`_ | Checks whether `s'` is a proper suffix of `s`. | `26` | +| **`C710`** | `SDCNTLEAD0` | _`s - n`_ | Returns the number of leading zeroes in `s`. | `26` | +| **`C711`** | `SDCNTLEAD1` | _`s - n`_ | Returns the number of leading ones in `s`. | `26` | +| **`C712`** | `SDCNTTRAIL0` | _`s - n`_ | Returns the number of trailing zeroes in `s`. | `26` | +| **`C713`** | `SDCNTTRAIL1` | _`s - n`_ | Returns the number of trailing ones in `s`. | `26` | + +### TVM Instructions Content List + +- [Overview](/learn/tvm-instructions/instructions) +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [Tuple, List and Null](/learn/tvm-instructions/instructions/tuple-list-null) +- [Constants and Literals](/learn/tvm-instructions/instructions/constant) +- [Arithmetic Operations](/learn/tvm-instructions/instructions/arithmetic) +- [Data Comparison](/learn/tvm-instructions/instructions/data-comparison) +- [Cell Manipulation](/learn/tvm-instructions/instructions/cell-manipulation) +- [Continuation and Control Flow](/learn/tvm-instructions/instructions/control-flow) +- [Exception Generation and Handling](/learn/tvm-instructions/instructions/exception-gen-and-handling) +- [Dictionary Manipulation](/learn/tvm-instructions/instructions/dictionary-manipulation) +- [Application-specific Primitives](/learn/tvm-instructions/instructions/app-specific) +- [Miscellaneous](/learn/tvm-instructions/instructions/miscellaneous) From 9c5550bcdb2489d4dfa693506ce6074bdda6a184 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:01:02 +0800 Subject: [PATCH 212/219] New translations dictionary-manipulation.mdx (Chinese Simplified) --- .../instructions/dictionary-manipulation.mdx | 266 ++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/dictionary-manipulation.mdx diff --git a/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/dictionary-manipulation.mdx b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/dictionary-manipulation.mdx new file mode 100644 index 0000000000..85da4f84cc --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/dictionary-manipulation.mdx @@ -0,0 +1,266 @@ +--- +hide_table_of_contents: true +wrapperClassName: bootstrap-wrapper +--- + +import { SearchField } from '@site/src/components/SearchField'; +import { dictionaryManipulationOpcodes as opcodes } from '@site/src/data/opcodes'; + +# Dictionary Manipulation Primitives + +The gas consumption of most dictionary operations is not fixed, it depends on the contents of the given dictionary. + +### TVM Instructions Content List + +- [Overview](/learn/tvm-instructions/instructions) +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [Tuple, List and Null](/learn/tvm-instructions/instructions/tuple-list-null) +- [Constants and Literals](/learn/tvm-instructions/instructions/constant) +- [Arithmetic Operations](/learn/tvm-instructions/instructions/arithmetic) +- [Data Comparison](/learn/tvm-instructions/instructions/data-comparison) +- [Cell Manipulation](/learn/tvm-instructions/instructions/cell-manipulation) +- [Continuation and Control Flow](/learn/tvm-instructions/instructions/control-flow) +- [Exception Generation and Handling](/learn/tvm-instructions/instructions/exception-gen-and-handling) +- [Dictionary Manipulation](/learn/tvm-instructions/instructions/dictionary-manipulation) +- [Application-specific Primitives](/learn/tvm-instructions/instructions/app-specific) +- [Miscellaneous](/learn/tvm-instructions/instructions/miscellaneous) + + + +## Dictionary Manipulation Primitives + +### Dictionary creation + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`6D`** | `NEWDICT` | _` - D`_ | Returns a new empty dictionary.
It is an alternative mnemonics for [`PUSHNULL`](#instr-null). | `18` | +| **`6E`** | `DICTEMPTY` | _`D - ?`_ | Checks whether dictionary `D` is empty, and returns `-1` or `0` accordingly.
It is an alternative mnemonics for [`ISNULL`](#instr-isnull). | `18` | + +### Dictionary serialization and deserialization + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`CE`** | `STDICTS`
\`\` | _`s b - b'`_ | Stores a _Slice_-represented dictionary `s` into _Builder_ `b`.
It is actually a synonym for [`STSLICE`](#instr-stslice). | `18` | +| **`F400`** | `STDICT`
`STOPTREF` | _`D b - b'`_ | Stores dictionary `D` into _Builder_ `b`, returing the resulting _Builder_ `b'`.
In other words, if `D` is a cell, performs [`STONE`](#instr-stone) and [`STREF`](#instr-stref); if `D` is _Null_, performs [`NIP`](#instr-nip) and [`STZERO`](#instr-stzero); otherwise throws a type checking exception. | `26` | +| **`F401`** | `SKIPDICT`
`SKIPOPTREF` | _`s - s'`_ | Equivalent to [`LDDICT`](#instr-lddict) [`NIP`](#instr-nip). | `26` | +| **`F402`** | `LDDICTS` | _`s - s' s''`_ | 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. | `26` | +| **`F403`** | `PLDDICTS` | _`s - s'`_ | Preloads a (_Slice_-represented) dictionary `s'` from _Slice_ `s`.
Approximately equivalent to [`LDDICTS`](#instr-lddicts) [`DROP`](#instr-drop). | `26` | +| **`F404`** | `LDDICT`
`LDOPTREF` | _`s - D s'`_ | 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. | `26` | +| **`F405`** | `PLDDICT`
`PLDOPTREF` | _`s - D`_ | Preloads a dictionary `D` from _Slice_ `s`.
Approximately equivalent to [`LDDICT`](#instr-lddict) [`DROP`](#instr-drop). | `26` | +| **`F406`** | `LDDICTQ` | _`s - D s' -1 or s 0`_ | A quiet version of [`LDDICT`](#instr-lddict). | `26` | +| **`F407`** | `PLDDICTQ` | _`s - D -1 or 0`_ | A quiet version of [`PLDDICT`](#instr-plddict). | `26` | + +### Get dictionary operations + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`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`. | | +| **`F40B`** | `DICTGETREF` | _`k D n - c -1 or 0`_ | Similar to [`DICTGET`](#instr-dictget), but with a [`LDREF`](#instr-ldref) [`ENDS`](#instr-ends) applied to `x` on success.
This operation is useful for dictionaries of type `HashmapE(n,^Y)`. | | +| **`F40C`** | `DICTIGET` | _`i D n - x -1 or 0`_ | Similar to [`DICTGET`](#instr-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. | | +| **`F40D`** | `DICTIGETREF` | _`i D n - c -1 or 0`_ | Combines [`DICTIGET`](#instr-dictiget) with [`DICTGETREF`](#instr-dictgetref): it uses signed `n`-bit _Integer_ `i` as a key and returns a _Cell_ instead of a _Slice_ on success. | | +| **`F40E`** | `DICTUGET` | _`i D n - x -1 or 0`_ | Similar to [`DICTIGET`](#instr-dictiget), but with _unsigned_ (big-endian) `n`-bit _Integer_ `i` used as a key. | | +| **`F40F`** | `DICTUGETREF` | _`i D n - c -1 or 0`_ | Similar to [`DICTIGETREF`](#instr-dictigetref), but with an unsigned `n`-bit _Integer_ key `i`. | | + +### Set/Replace/Add dictionary operations + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :----------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`F412`** | `DICTSET` | _`x k D n - D'`_ | Sets the value associated with `n`-bit key `k` (represented by a _Slice_ as in [`DICTGET`](#instr-dictget)) in dictionary `D` (also represented by a _Slice_) to value `x` (again a _Slice_), and returns the resulting dictionary as `D'`. | | +| **`F413`** | `DICTSETREF` | _`c k D n - D'`_ | Similar to [`DICTSET`](#instr-dictset), but with the value set to a reference to _Cell_ `c`. | | +| **`F414`** | `DICTISET` | _`x i D n - D'`_ | Similar to [`DICTSET`](#instr-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. | | +| **`F415`** | `DICTISETREF` | _`c i D n - D'`_ | Similar to [`DICTSETREF`](#instr-dictsetref), but with the key a signed `n`-bit integer as in [`DICTISET`](#instr-dictiset). | | +| **`F416`** | `DICTUSET` | _`x i D n - D'`_ | Similar to [`DICTISET`](#instr-dictiset), but with `i` an _unsigned_ `n`-bit integer. | | +| **`F417`** | `DICTUSETREF` | _`c i D n - D'`_ | Similar to [`DICTISETREF`](#instr-dictisetref), but with `i` unsigned. | | +| **`F41A`** | `DICTSETGET` | _`x k D n - D' y -1 or D' 0`_ | Combines [`DICTSET`](#instr-dictset) with [`DICTGET`](#instr-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. | | +| **`F41B`** | `DICTSETGETREF` | _`c k D n - D' c' -1 or D' 0`_ | Combines [`DICTSETREF`](#instr-dictsetref) with [`DICTGETREF`](#instr-dictgetref) similarly to [`DICTSETGET`](#instr-dictsetget). | | +| **`F41C`** | `DICTISETGET` | _`x i D n - D' y -1 or D' 0`_ | [`DICTISETGET`](#instr-dictisetget), but with `i` a signed `n`-bit integer. | | +| **`F41D`** | `DICTISETGETREF` | _`c i D n - D' c' -1 or D' 0`_ | [`DICTISETGETREF`](#instr-dictisetgetref), but with `i` a signed `n`-bit integer. | | +| **`F41E`** | `DICTUSETGET` | _`x i D n - D' y -1 or D' 0`_ | [`DICTISETGET`](#instr-dictisetget), but with `i` an unsigned `n`-bit integer. | | +| **`F41F`** | `DICTUSETGETREF` | _`c i D n - D' c' -1 or D' 0`_ | [`DICTISETGETREF`](#instr-dictisetgetref), but with `i` an unsigned `n`-bit integer. | | +| **`F422`** | `DICTREPLACE` | _`x k D n - D' -1 or D 0`_ | A _Replace_ operation, which is similar to [`DICTSET`](#instr-dictset), but sets the value of key `k` in dictionary `D` to `x` only if the key `k` was already present in `D`. | | +| **`F423`** | `DICTREPLACEREF` | _`c k D n - D' -1 or D 0`_ | A _Replace_ counterpart of [`DICTSETREF`](#instr-dictsetref). | | +| **`F424`** | `DICTIREPLACE` | _`x i D n - D' -1 or D 0`_ | [`DICTREPLACE`](#instr-dictreplace), but with `i` a signed `n`-bit integer. | | +| **`F425`** | `DICTIREPLACEREF` | _`c i D n - D' -1 or D 0`_ | [`DICTREPLACEREF`](#instr-dictreplaceref), but with `i` a signed `n`-bit integer. | | +| **`F426`** | `DICTUREPLACE` | _`x i D n - D' -1 or D 0`_ | [`DICTREPLACE`](#instr-dictreplace), but with `i` an unsigned `n`-bit integer. | | +| **`F427`** | `DICTUREPLACEREF` | _`c i D n - D' -1 or D 0`_ | [`DICTREPLACEREF`](#instr-dictreplaceref), but with `i` an unsigned `n`-bit integer. | | +| **`F42A`** | `DICTREPLACEGET` | _`x k D n - D' y -1 or D 0`_ | A _Replace_ counterpart of [`DICTSETGET`](#instr-dictsetget): on success, also returns the old value associated with the key in question. | | +| **`F42B`** | `DICTREPLACEGETREF` | _`c k D n - D' c' -1 or D 0`_ | A _Replace_ counterpart of [`DICTSETGETREF`](#instr-dictsetgetref). | | +| **`F42C`** | `DICTIREPLACEGET` | _`x i D n - D' y -1 or D 0`_ | [`DICTREPLACEGET`](#instr-dictreplaceget), but with `i` a signed `n`-bit integer. | | +| **`F42D`** | `DICTIREPLACEGETREF` | _`c i D n - D' c' -1 or D 0`_ | [`DICTREPLACEGETREF`](#instr-dictreplacegetref), but with `i` a signed `n`-bit integer. | | +| **`F42E`** | `DICTUREPLACEGET` | _`x i D n - D' y -1 or D 0`_ | [`DICTREPLACEGET`](#instr-dictreplaceget), but with `i` an unsigned `n`-bit integer. | | +| **`F42F`** | `DICTUREPLACEGETREF` | _`c i D n - D' c' -1 or D 0`_ | [`DICTREPLACEGETREF`](#instr-dictreplacegetref), but with `i` an unsigned `n`-bit integer. | | +| **`F432`** | `DICTADD` | _`x k D n - D' -1 or D 0`_ | An _Add_ counterpart of [`DICTSET`](#instr-dictset): sets the value associated with key `k` in dictionary `D` to `x`, but only if it is not already present in `D`. | | +| **`F433`** | `DICTADDREF` | _`c k D n - D' -1 or D 0`_ | An _Add_ counterpart of [`DICTSETREF`](#instr-dictsetref). | | +| **`F434`** | `DICTIADD` | _`x i D n - D' -1 or D 0`_ | [`DICTADD`](#instr-dictadd), but with `i` a signed `n`-bit integer. | | +| **`F435`** | `DICTIADDREF` | _`c i D n - D' -1 or D 0`_ | [`DICTADDREF`](#instr-dictaddref), but with `i` a signed `n`-bit integer. | | +| **`F436`** | `DICTUADD` | _`x i D n - D' -1 or D 0`_ | [`DICTADD`](#instr-dictadd), but with `i` an unsigned `n`-bit integer. | | +| **`F437`** | `DICTUADDREF` | _`c i D n - D' -1 or D 0`_ | [`DICTADDREF`](#instr-dictaddref), but with `i` an unsigned `n`-bit integer. | | +| **`F43A`** | `DICTADDGET` | _`x k D n - D' -1 or D y 0`_ | An _Add_ counterpart of [`DICTSETGET`](#instr-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. | | +| **`F43B`** | `DICTADDGETREF` | _`c k D n - D' -1 or D c' 0`_ | An _Add_ counterpart of [`DICTSETGETREF`](#instr-dictsetgetref). | | +| **`F43C`** | `DICTIADDGET` | _`x i D n - D' -1 or D y 0`_ | [`DICTADDGET`](#instr-dictaddget), but with `i` a signed `n`-bit integer. | | +| **`F43D`** | `DICTIADDGETREF` | _`c i D n - D' -1 or D c' 0`_ | [`DICTADDGETREF`](#instr-dictaddgetref), but with `i` a signed `n`-bit integer. | | +| **`F43E`** | `DICTUADDGET` | _`x i D n - D' -1 or D y 0`_ | [`DICTADDGET`](#instr-dictaddget), but with `i` an unsigned `n`-bit integer. | | +| **`F43F`** | `DICTUADDGETREF` | _`c i D n - D' -1 or D c' 0`_ | [`DICTADDGETREF`](#instr-dictaddgetref), but with `i` an unsigned `n`-bit integer. | | + +### Builder-accepting variants of Set dictionary operations + +The following primitives accept the new value as a _Builder_ `b` instead of a _Slice_ `x`. +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +|:-|:-|:-|:-|:-| +| **`F441`** | `DICTSETB` | _`b k D n - D'`_ | | | +| **`F442`** | `DICTISETB` | _`b i D n - D'`_ | | | +| **`F443`** | `DICTUSETB` | _`b i D n - D'`_ | | | +| **`F445`** | `DICTSETGETB` | _`b k D n - D' y -1 or D' 0`_ | | | +| **`F446`** | `DICTISETGETB` | _`b i D n - D' y -1 or D' 0`_ | | | +| **`F447`** | `DICTUSETGETB` | _`b i D n - D' y -1 or D' 0`_ | | | +| **`F449`** | `DICTREPLACEB` | _`b k D n - D' -1 or D 0`_ | | | +| **`F44A`** | `DICTIREPLACEB` | _`b i D n - D' -1 or D 0`_ | | | +| **`F44B`** | `DICTUREPLACEB` | _`b i D n - D' -1 or D 0`_ | | | +| **`F44D`** | `DICTREPLACEGETB` | _`b k D n - D' y -1 or D 0`_ | | | +| **`F44E`** | `DICTIREPLACEGETB` | _`b i D n - D' y -1 or D 0`_ | | | +| **`F44F`** | `DICTUREPLACEGETB` | _`b i D n - D' y -1 or D 0`_ | | | +| **`F451`** | `DICTADDB` | _`b k D n - D' -1 or D 0`_ | | | +| **`F452`** | `DICTIADDB` | _`b i D n - D' -1 or D 0`_ | | | +| **`F453`** | `DICTUADDB` | _`b i D n - D' -1 or D 0`_ | | | +| **`F455`** | `DICTADDGETB` | _`b k D n - D' -1 or D y 0`_ | | | +| **`F456`** | `DICTIADDGETB` | _`b i D n - D' -1 or D y 0`_ | | | +| **`F457`** | `DICTUADDGETB` | _`b i D n - D' -1 or D y 0`_ | | | + +### Delete dictionary operations + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------- | +| **`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`. | | +| **`F45A`** | `DICTIDEL` | _`i D n - D' ?`_ | A version of [`DICTDEL`](#instr-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''). | | +| **`F45B`** | `DICTUDEL` | _`i D n - D' ?`_ | Similar to [`DICTIDEL`](#instr-dictidel), but with `i` an unsigned `n`-bit integer. | | +| **`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`. | | +| **`F463`** | `DICTDELGETREF` | _`k D n - D' c -1 or D 0`_ | Similar to [`DICTDELGET`](#instr-dictdelget), but with [`LDREF`](#instr-ldref) [`ENDS`](#instr-ends) applied to `x` on success, so that the value returned `c` is a _Cell_. | | +| **`F464`** | `DICTIDELGET` | _`i D n - D' x -1 or D 0`_ | [`DICTDELGET`](#instr-dictdelget), but with `i` a signed `n`-bit integer. | | +| **`F465`** | `DICTIDELGETREF` | _`i D n - D' c -1 or D 0`_ | [`DICTDELGETREF`](#instr-dictdelgetref), but with `i` a signed `n`-bit integer. | | +| **`F466`** | `DICTUDELGET` | _`i D n - D' x -1 or D 0`_ | [`DICTDELGET`](#instr-dictdelget), but with `i` an unsigned `n`-bit integer. | | +| **`F467`** | `DICTUDELGETREF` | _`i D n - D' c -1 or D 0`_ | [`DICTDELGETREF`](#instr-dictdelgetref), but with `i` an unsigned `n`-bit integer. | | + +### "Maybe reference" dictionary operations + +The following operations assume that a dictionary is used to store values `c?` of type _Maybe Cell_. The representation is as follows: if `c?` is a _Cell_ , it is stored as a value with no data bits and exactly one reference to this _Cell_. If `c?` is _Null_, then the corresponding key must be absent from the dictionary. +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +|:-|:-|:-|:-|:-| +| **`F469`** | `DICTGETOPTREF` | _`k D n - c^?`_ | A variant of [`DICTGETREF`](#instr-dictgetref) that returns _Null_ instead of the value `c^?` if the key `k` is absent from dictionary `D`. | | +| **`F46A`** | `DICTIGETOPTREF` | _`i D n - c^?`_ | [`DICTGETOPTREF`](#instr-dictgetoptref), but with `i` a signed `n`-bit integer. If the key `i` is out of range, also returns _Null_. | | +| **`F46B`** | `DICTUGETOPTREF` | _`i D n - c^?`_ | [`DICTGETOPTREF`](#instr-dictgetoptref), but with `i` an unsigned `n`-bit integer. If the key `i` is out of range, also returns _Null_. | | +| **`F46D`** | `DICTSETGETOPTREF` | _`c^? k D n - D' ~c^?`_ | A variant of both [`DICTGETOPTREF`](#instr-dictgetoptref) and [`DICTSETGETREF`](#instr-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). | | +| **`F46E`** | `DICTISETGETOPTREF` | _`c^? i D n - D' ~c^?`_ | Similar to primitive [`DICTSETGETOPTREF`](#instr-dictsetgetoptref), but using signed `n`-bit _Integer_ `i` as a key. If `i` does not fit into `n` bits, throws a range checking exception. | | +| **`F46F`** | `DICTUSETGETOPTREF` | _`c^? i D n - D' ~c^?`_ | Similar to primitive [`DICTSETGETOPTREF`](#instr-dictsetgetoptref), but using unsigned `n`-bit _Integer_ `i` as a key. | | + +### Prefix code dictionary operations + +These are some basic operations for constructing prefix code dictionaries. +These primitives are completely similar to their non-prefix code counterparts ([`DICTSET`](#instr-dictset) etc), with the obvious difference that even a _Set_ may fail in a prefix code dictionary, so a success flag must be returned by [`PFXDICTSET`](#instr-pfxdictset) as well. +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +|:-|:-|:-|:-|:-| +| **`F470`** | `PFXDICTSET` | _`x k D n - D' -1 or D 0`_ | | | +| **`F471`** | `PFXDICTREPLACE` | _`x k D n - D' -1 or D 0`_ | | | +| **`F472`** | `PFXDICTADD` | _`x k D n - D' -1 or D 0`_ | | | +| **`F473`** | `PFXDICTDEL` | _`k D n - D' -1 or D 0`_ | | | + +### Variants of GetNext and GetPrev operations + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`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_). | | +| **`F475`** | `DICTGETNEXTEQ` | _`k D n - x' k' -1 or 0`_ | Similar to [`DICTGETNEXT`](#instr-dictgetnext), but computes the minimal key `k'` that is lexicographically greater than or equal to `k`. | | +| **`F476`** | `DICTGETPREV` | _`k D n - x' k' -1 or 0`_ | Similar to [`DICTGETNEXT`](#instr-dictgetnext), but computes the maximal key `k'` lexicographically smaller than `k`. | | +| **`F477`** | `DICTGETPREVEQ` | _`k D n - x' k' -1 or 0`_ | Similar to [`DICTGETPREV`](#instr-dictgetprev), but computes the maximal key `k'` lexicographically smaller than or equal to `k`. | | +| **`F478`** | `DICTIGETNEXT` | _`i D n - x' i' -1 or 0`_ | Similar to [`DICTGETNEXT`](#instr-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). | | +| **`F479`** | `DICTIGETNEXTEQ` | _`i D n - x' i' -1 or 0`_ | Similar to [`DICTGETNEXTEQ`](#instr-dictgetnexteq), but interprets keys as signed `n`-bit integers. | | +| **`F47A`** | `DICTIGETPREV` | _`i D n - x' i' -1 or 0`_ | Similar to [`DICTGETPREV`](#instr-dictgetprev), but interprets keys as signed `n`-bit integers. | | +| **`F47B`** | `DICTIGETPREVEQ` | _`i D n - x' i' -1 or 0`_ | Similar to [`DICTGETPREVEQ`](#instr-dictgetpreveq), but interprets keys as signed `n`-bit integers. | | +| **`F47C`** | `DICTUGETNEXT` | _`i D n - x' i' -1 or 0`_ | Similar to [`DICTGETNEXT`](#instr-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). | | +| **`F47D`** | `DICTUGETNEXTEQ` | _`i D n - x' i' -1 or 0`_ | Similar to [`DICTGETNEXTEQ`](#instr-dictgetnexteq), but interprets keys as unsigned `n`-bit integers. | | +| **`F47E`** | `DICTUGETPREV` | _`i D n - x' i' -1 or 0`_ | Similar to [`DICTGETPREV`](#instr-dictgetprev), but interprets keys as unsigned `n`-bit integers. | | +| **`F47F`** | `DICTUGETPREVEQ` | _`i D n - x' i' -1 or 0`_ | Similar to [`DICTGETPREVEQ`](#instr-dictgetpreveq), but interprets keys a unsigned `n`-bit integers. | | + +### GetMin, GetMax, RemoveMin, RemoveMax operations + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------- | +| **`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`. | | +| **`F483`** | `DICTMINREF` | _`D n - c k -1 or 0`_ | Similar to [`DICTMIN`](#instr-dictmin), but returns the only reference in the value as a _Cell_ `c`. | | +| **`F484`** | `DICTIMIN` | _`D n - x i -1 or 0`_ | Similar to [`DICTMIN`](#instr-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`](#instr-dictmin) and [`DICTUMIN`](#instr-dictumin). | | +| **`F485`** | `DICTIMINREF` | _`D n - c i -1 or 0`_ | Similar to [`DICTIMIN`](#instr-dictimin), but returns the only reference in the value. | | +| **`F486`** | `DICTUMIN` | _`D n - x i -1 or 0`_ | Similar to [`DICTMIN`](#instr-dictmin), but returns the key as an unsigned `n`-bit _Integer_ `i`. | | +| **`F487`** | `DICTUMINREF` | _`D n - c i -1 or 0`_ | Similar to [`DICTUMIN`](#instr-dictumin), but returns the only reference in the value. | | +| **`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`. | | +| **`F48B`** | `DICTMAXREF` | _`D n - c k -1 or 0`_ | Similar to [`DICTMAX`](#instr-dictmax), but returns the only reference in the value. | | +| **`F48C`** | `DICTIMAX` | _`D n - x i -1 or 0`_ | Similar to [`DICTMAX`](#instr-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`](#instr-dictmax) and [`DICTUMAX`](#instr-dictumax). | | +| **`F48D`** | `DICTIMAXREF` | _`D n - c i -1 or 0`_ | Similar to [`DICTIMAX`](#instr-dictimax), but returns the only reference in the value. | | +| **`F48E`** | `DICTUMAX` | _`D n - x i -1 or 0`_ | Similar to [`DICTMAX`](#instr-dictmax), but returns the key as an unsigned `n`-bit _Integer_ `i`. | | +| **`F48F`** | `DICTUMAXREF` | _`D n - c i -1 or 0`_ | Similar to [`DICTUMAX`](#instr-dictumax), but returns the only reference in the value. | | +| **`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'`. | | +| **`F493`** | `DICTREMMINREF` | _`D n - D' c k -1 or D 0`_ | Similar to [`DICTREMMIN`](#instr-dictremmin), but returns the only reference in the value as a _Cell_ `c`. | | +| **`F494`** | `DICTIREMMIN` | _`D n - D' x i -1 or D 0`_ | Similar to [`DICTREMMIN`](#instr-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`](#instr-dictremmin) and [`DICTUREMMIN`](#instr-dicturemmin). | | +| **`F495`** | `DICTIREMMINREF` | _`D n - D' c i -1 or D 0`_ | Similar to [`DICTIREMMIN`](#instr-dictiremmin), but returns the only reference in the value. | | +| **`F496`** | `DICTUREMMIN` | _`D n - D' x i -1 or D 0`_ | Similar to [`DICTREMMIN`](#instr-dictremmin), but returns the key as an unsigned `n`-bit _Integer_ `i`. | | +| **`F497`** | `DICTUREMMINREF` | _`D n - D' c i -1 or D 0`_ | Similar to [`DICTUREMMIN`](#instr-dicturemmin), but returns the only reference in the value. | | +| **`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'`. | | +| **`F49B`** | `DICTREMMAXREF` | _`D n - D' c k -1 or D 0`_ | Similar to [`DICTREMMAX`](#instr-dictremmax), but returns the only reference in the value as a _Cell_ `c`. | | +| **`F49C`** | `DICTIREMMAX` | _`D n - D' x i -1 or D 0`_ | Similar to [`DICTREMMAX`](#instr-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`](#instr-dictremmax) and [`DICTUREMMAX`](#instr-dicturemmax). | | +| **`F49D`** | `DICTIREMMAXREF` | _`D n - D' c i -1 or D 0`_ | Similar to [`DICTIREMMAX`](#instr-dictiremmax), but returns the only reference in the value. | | +| **`F49E`** | `DICTUREMMAX` | _`D n - D' x i -1 or D 0`_ | Similar to [`DICTREMMAX`](#instr-dictremmax), but returns the key as an unsigned `n`-bit _Integer_ `i`. | | +| **`F49F`** | `DICTUREMMAXREF` | _`D n - D' c i -1 or D 0`_ | Similar to [`DICTUREMMAX`](#instr-dicturemmax), but returns the only reference in the value. | | + +### Special Get dictionary and prefix code dictionary operations and constant dictionaries + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :----------------------------------------------------------- | :----------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`F4A0`** | `DICTIGETJMP` | _`i D n - `_ | Similar to [`DICTIGET`](#instr-dictiget), but with `x` [`BLESS`](#instr-bless)ed into a continuation with a subsequent [`JMPX`](#instr-jmpx) to it on success. On failure, does nothing. This is useful for implementing `switch`/`case` constructions. | | +| **`F4A1`** | `DICTUGETJMP` | _`i D n - `_ | Similar to [`DICTIGETJMP`](#instr-dictigetjmp), but performs [`DICTUGET`](#instr-dictuget) instead of [`DICTIGET`](#instr-dictiget). | | +| **`F4A2`** | `DICTIGETEXEC` | _`i D n - `_ | Similar to [`DICTIGETJMP`](#instr-dictigetjmp), but with [`EXECUTE`](#instr-execute) instead of [`JMPX`](#instr-jmpx). | | +| **`F4A3`** | `DICTUGETEXEC` | _`i D n - `_ | Similar to [`DICTUGETJMP`](#instr-dictugetjmp), but with [`EXECUTE`](#instr-execute) instead of [`JMPX`](#instr-jmpx). | | +| **`F4A6_n`** | `[ref] [n] DICTPUSHCONST` | _` - D n`_ | 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`](#instr-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`](#instr-newdict) primitive instead. | `34` | +| **`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. | | +| **`F4A9`** | `PFXDICTGET` | _`s D n - s' x s''`_ | Similar to [`PFXDICTGET`](#instr-pfxdictget), but throws a cell deserialization failure exception on failure. | | +| **`F4AA`** | `PFXDICTGETJMP` | _`s D n - s' s'' or s`_ | Similar to [`PFXDICTGETQ`](#instr-pfxdictgetq), but on success [`BLESS`](#instr-bless)es the value `x` into a _Continuation_ and transfers control to it as if by a [`JMPX`](#instr-jmpx). On failure, returns `s` unchanged and continues execution. | | +| **`F4AB`** | `PFXDICTGETEXEC` | _`s D n - s' s''`_ | Similar to [`PFXDICTGETJMP`](#instr-pfxdictgetjmp), but `EXEC`utes the continuation found instead of jumping to it. On failure, throws a cell deserialization exception. | | +| **`F4AE_n`** | `[ref] [n] PFXDICTCONSTGETJMP`
`[ref] [n] PFXDICTSWITCH` | _`s - s' s'' or s`_ | Combines [`[n] DICTPUSHCONST`](#instr-dictpushconst) for `0 <= n <= 1023` with [`PFXDICTGETJMP`](#instr-pfxdictgetjmp). | | +| **`F4BC`** | `DICTIGETJMPZ` | _`i D n - i or nothing`_ | A variant of [`DICTIGETJMP`](#instr-dictigetjmp) that returns index `i` on failure. | | +| **`F4BD`** | `DICTUGETJMPZ` | _`i D n - i or nothing`_ | A variant of [`DICTUGETJMP`](#instr-dictugetjmp) that returns index `i` on failure. | | +| **`F4BE`** | `DICTIGETEXECZ` | _`i D n - i or nothing`_ | A variant of [`DICTIGETEXEC`](#instr-dictigetexec) that returns index `i` on failure. | | +| **`F4BF`** | `DICTUGETEXECZ` | _`i D n - i or nothing`_ | A variant of [`DICTUGETEXEC`](#instr-dictugetexec) that returns index `i` on failure. | | + +### SubDict dictionary operations + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`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'`. | | +| **`F4B2`** | `SUBDICTIGET` | _`x l D n - D'`_ | Variant of [`SUBDICTGET`](#instr-subdictget) with the prefix represented by a signed big-endian `l`-bit _Integer_ `x`, where necessarily `l <= 257`. | | +| **`F4B3`** | `SUBDICTUGET` | _`x l D n - D'`_ | Variant of [`SUBDICTGET`](#instr-subdictget) with the prefix represented by an unsigned big-endian `l`-bit _Integer_ `x`, where necessarily `l <= 256`. | | +| **`F4B5`** | `SUBDICTRPGET` | _`k l D n - D'`_ | Similar to [`SUBDICTGET`](#instr-subdictget), but removes the common prefix `k` from all keys of the new dictionary `D'`, which becomes of type `HashmapE(n-l,X)`. | | +| **`F4B6`** | `SUBDICTIRPGET` | _`x l D n - D'`_ | Variant of [`SUBDICTRPGET`](#instr-subdictrpget) with the prefix represented by a signed big-endian `l`-bit _Integer_ `x`, where necessarily `l <= 257`. | | +| **`F4B7`** | `SUBDICTURPGET` | _`x l D n - D'`_ | Variant of [`SUBDICTRPGET`](#instr-subdictrpget) with the prefix represented by an unsigned big-endian `l`-bit _Integer_ `x`, where necessarily `l <= 256`. | | + +### TVM Instructions Content List + +- [Overview](/learn/tvm-instructions/instructions) +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [Tuple, List and Null](/learn/tvm-instructions/instructions/tuple-list-null) +- [Constants and Literals](/learn/tvm-instructions/instructions/constant) +- [Arithmetic Operations](/learn/tvm-instructions/instructions/arithmetic) +- [Data Comparison](/learn/tvm-instructions/instructions/data-comparison) +- [Cell Manipulation](/learn/tvm-instructions/instructions/cell-manipulation) +- [Continuation and Control Flow](/learn/tvm-instructions/instructions/control-flow) +- [Exception Generation and Handling](/learn/tvm-instructions/instructions/exception-gen-and-handling) +- [Dictionary Manipulation](/learn/tvm-instructions/instructions/dictionary-manipulation) +- [Application-specific Primitives](/learn/tvm-instructions/instructions/app-specific) +- [Miscellaneous](/learn/tvm-instructions/instructions/miscellaneous) From d6b0e7fbcc8fce524dc38fdf3909d19be97ce257 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:01:03 +0800 Subject: [PATCH 213/219] New translations exception-gen-and-handling.mdx (Chinese Simplified) --- .../exception-gen-and-handling.mdx | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/exception-gen-and-handling.mdx diff --git a/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/exception-gen-and-handling.mdx b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/exception-gen-and-handling.mdx new file mode 100644 index 0000000000..381217745b --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/exception-gen-and-handling.mdx @@ -0,0 +1,75 @@ +--- +hide_table_of_contents: true +wrapperClassName: bootstrap-wrapper +--- + +import { SearchField } from '@site/src/components/SearchField'; +import { exceptionOpcodes as opcodes } from '@site/src/data/opcodes'; + +# Exception Generating and Handling + +### TVM Instructions Content List + +- [Overview](/learn/tvm-instructions/instructions) +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [Tuple, List and Null](/learn/tvm-instructions/instructions/tuple-list-null) +- [Constants and Literals](/learn/tvm-instructions/instructions/constant) +- [Arithmetic Operations](/learn/tvm-instructions/instructions/arithmetic) +- [Data Comparison](/learn/tvm-instructions/instructions/data-comparison) +- [Cell Manipulation](/learn/tvm-instructions/instructions/cell-manipulation) +- [Continuation and Control Flow](/learn/tvm-instructions/instructions/control-flow) +- [Exception Generation and Handling](/learn/tvm-instructions/instructions/exception-gen-and-handling) +- [Dictionary Manipulation](/learn/tvm-instructions/instructions/dictionary-manipulation) +- [Application-specific Primitives](/learn/tvm-instructions/instructions/app-specific) +- [Miscellaneous](/learn/tvm-instructions/instructions/miscellaneous) + + + +## Exception Generating and Handling + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------- | +| **`F22_n`** | `[n] THROW` | _` - 0 n`_ | 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. | `76` | +| **`F26_n`** | `[n] THROWIF` | _`f - `_ | Throws exception `0 <= n <= 63` with parameter zero only if integer `f!=0`. | `26/76` | +| **`F2A_n`** | `[n] THROWIFNOT` | _`f - `_ | Throws exception `0 <= n <= 63` with parameter zero only if integer `f=0`. | `26/76` | +| **`F2C4_n`** | `[n] THROW` | _`- 0 nn`_ | For `0 <= n < 2^11`, an encoding of [`[n] THROW`](#instr-throw-short) for larger values of `n`. | `84` | +| **`F2CC_n`** | `[n] THROWARG` | _`x - x nn`_ | Throws exception `0 <= n < 2^11` with parameter `x`, by copying `x` and `n` into the stack of `c2` and transferring control to `c2`. | `84` | +| **`F2D4_n`** | `[n] THROWIF` | _`f - `_ | For `0 <= n < 2^11`, an encoding of [`[n] THROWIF`](#instr-throwif-short) for larger values of `n`. | `34/84` | +| **`F2DC_n`** | `[n] THROWARGIF` | _`x f - `_ | Throws exception `0 <= nn < 2^11` with parameter `x` only if integer `f!=0`. | `34/84` | +| **`F2E4_n`** | `[n] THROWIFNOT` | _`f - `_ | For `0 <= n < 2^11`, an encoding of [`[n] THROWIFNOT`](#instr-throwifnot-short) for larger values of `n`. | `34/84` | +| **`F2EC_n`** | `[n] THROWARGIFNOT` | _`x f - `_ | Throws exception `0 <= n < 2^11` with parameter `x` only if integer `f=0`. | `34/84` | +| **`F2F0`** | `THROWANY` | _`n - 0 n`_ | Throws exception `0 <= n < 2^16` with parameter zero.
Approximately equivalent to [`ZERO`](#instr-zero) [`SWAP`](#instr-swap) [`THROWARGANY`](#instr-throwargany). | `76` | +| **`F2F1`** | `THROWARGANY` | _`x n - x n`_ | Throws exception `0 <= n < 2^16` with parameter `x`, transferring control to the continuation in `c2`.
Approximately equivalent to [`c2 PUSHCTR`](#instr-pushctr) [`2 JMPXARGS`](#instr-jmpxargs). | `76` | +| **`F2F2`** | `THROWANYIF` | _`n f - `_ | Throws exception `0 <= n < 2^16` with parameter zero only if `f!=0`. | `26/76` | +| **`F2F3`** | `THROWARGANYIF` | _`x n f - `_ | Throws exception `0 <= n<2^16` with parameter `x` only if `f!=0`. | `26/76` | +| **`F2F4`** | `THROWANYIFNOT` | _`n f - `_ | Throws exception `0 <= n<2^16` with parameter zero only if `f=0`. | `26/76` | +| **`F2F5`** | `THROWARGANYIFNOT` | _`x n f - `_ | Throws exception `0 <= n<2^16` with parameter `x` only if `f=0`. | `26/76` | +| **`F2FF`** | `TRY` | _`c c' - `_ | 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`](#instr-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`](#instr-throwany) if it cannot handle it by itself. | `26` | +| **`F2FF`** | `TRY:<{ code1 }>CATCH<{ code2 }>` | _`-`_ | Equivalent to [`<{ code1 }> CONT`](#instr-pushcont) [`<{ code2 }> CONT`](#instr-pushcont) [`TRY`](#instr-try). | | +| **`F3pr`** | `[p] [r] TRYARGS` | _`c c' - `_ | Similar to [`TRY`](#instr-try), but with [`[p] [r] CALLXARGS`](#instr-callxargs) internally used instead of [`EXECUTE`](#instr-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. | `26` | + +### TVM Instructions Content List + +- [Overview](/learn/tvm-instructions/instructions) +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [Tuple, List and Null](/learn/tvm-instructions/instructions/tuple-list-null) +- [Constants and Literals](/learn/tvm-instructions/instructions/constant) +- [Arithmetic Operations](/learn/tvm-instructions/instructions/arithmetic) +- [Data Comparison](/learn/tvm-instructions/instructions/data-comparison) +- [Cell Manipulation](/learn/tvm-instructions/instructions/cell-manipulation) +- [Continuation and Control Flow](/learn/tvm-instructions/instructions/control-flow) +- [Exception Generation and Handling](/learn/tvm-instructions/instructions/exception-gen-and-handling) +- [Dictionary Manipulation](/learn/tvm-instructions/instructions/dictionary-manipulation) +- [Application-specific Primitives](/learn/tvm-instructions/instructions/app-specific) +- [Miscellaneous](/learn/tvm-instructions/instructions/miscellaneous) From d9564324562fa6cf925821ac4a34051b591d68be Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:01:04 +0800 Subject: [PATCH 214/219] New translations miscellaneous.mdx (Chinese Simplified) --- .../instructions/miscellaneous.mdx | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/miscellaneous.mdx diff --git a/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/miscellaneous.mdx b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/miscellaneous.mdx new file mode 100644 index 0000000000..d189f5a771 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/miscellaneous.mdx @@ -0,0 +1,77 @@ +--- +hide_table_of_contents: true +wrapperClassName: bootstrap-wrapper +--- + +import { SearchField } from '@site/src/components/SearchField'; +import { miscellaneousOpcodes as opcodes } from '@site/src/data/opcodes'; + +# Debug and Codepage Primitives + +Opcodes beginning with `FE` are reserved for the debug primitives. These primitives have known fixed operation length and behave as (multibyte) [`NOP`](#instr-nop) operations. + +However, when invoked in a TVM instance with debug mode enabled, these primitives can produce a specific output into the text debug log of the TVM instance, never affecting the TVM state. + +[`DEBUG`](#instr-debug) and [`DEBUGSTR`](#instr-debugstr) are the two debug primitives, they cover all opcodes that start with `FE`. +Other primitives listed here have opcodes from the same set. When debug is enabled, they have their specified effects. When debug is disabled, they behave as [`NOP`](#instr-nop). + +### TVM Instructions Content List + +- [Overview](/learn/tvm-instructions/instructions) +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [Tuple, List and Null](/learn/tvm-instructions/instructions/tuple-list-null) +- [Constants and Literals](/learn/tvm-instructions/instructions/constant) +- [Arithmetic Operations](/learn/tvm-instructions/instructions/arithmetic) +- [Data Comparison](/learn/tvm-instructions/instructions/data-comparison) +- [Cell Manipulation](/learn/tvm-instructions/instructions/cell-manipulation) +- [Continuation and Control Flow](/learn/tvm-instructions/instructions/control-flow) +- [Exception Generation and Handling](/learn/tvm-instructions/instructions/exception-gen-and-handling) +- [Dictionary Manipulation](/learn/tvm-instructions/instructions/dictionary-manipulation) +- [Application-specific Primitives](/learn/tvm-instructions/instructions/app-specific) +- [Miscellaneous](/learn/tvm-instructions/instructions/miscellaneous) + + + +### Debug Primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :----------------------------------------------- | :-------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`FEnn`** | `{nn} DEBUG` | _`-`_ | `0 <= nn < 240` | `26` | +| **`FEFnssss`** | `{string} DEBUGSTR`
`{string} {x} DEBUGSTRI` | _`-`_ | `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`](#instr-debugstr): `ssss` is the given string.
[`DEBUGSTRI`](#instr-debugstr): `ssss` is one-byte integer `0 <= x <= 255` followed by the given string. | `26` | +| **`FE00`** | `DUMPSTK` | _`-`_ | Dumps the stack (at most the top 255 values) and shows the total stack depth. | `26` | +| **`FE2i`** | `s[i] DUMP` | _`-`_ | Dumps `s[i]`. | `26` | + +### Codepage primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`FFnn`** | `[nn] SETCP` | _`-`_ | Selects TVM codepage `0 <= nn < 240`. If the codepage is not supported, throws an invalid opcode exception. | `26` | +| **`FF00`** | `SETCP0` | _`-`_ | Selects TVM (test) codepage zero as described in this document. | `26` | +| **`FFFz`** | `[z-16] SETCP` | _`-`_ | 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. | `26` | +| **`FFF0`** | `SETCPX` | _`c - `_ | Selects codepage `c` with `-2^15 <= c < 2^15` passed in the top of the stack. | `26` | + +### TVM Instructions Content List + +- [Overview](/learn/tvm-instructions/instructions) +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [Tuple, List and Null](/learn/tvm-instructions/instructions/tuple-list-null) +- [Constants and Literals](/learn/tvm-instructions/instructions/constant) +- [Arithmetic Operations](/learn/tvm-instructions/instructions/arithmetic) +- [Data Comparison](/learn/tvm-instructions/instructions/data-comparison) +- [Cell Manipulation](/learn/tvm-instructions/instructions/cell-manipulation) +- [Continuation and Control Flow](/learn/tvm-instructions/instructions/control-flow) +- [Exception Generation and Handling](/learn/tvm-instructions/instructions/exception-gen-and-handling) +- [Dictionary Manipulation](/learn/tvm-instructions/instructions/dictionary-manipulation) +- [Application-specific Primitives](/learn/tvm-instructions/instructions/app-specific) +- [Miscellaneous](/learn/tvm-instructions/instructions/miscellaneous) From 59f818849553c0e7faa2dc04481ea7dcc6939171 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:01:05 +0800 Subject: [PATCH 215/219] New translations stack-manipulation.mdx (Chinese Simplified) --- .../instructions/stack-manipulation.mdx | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/stack-manipulation.mdx diff --git a/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/stack-manipulation.mdx b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/stack-manipulation.mdx new file mode 100644 index 0000000000..e87d669589 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/stack-manipulation.mdx @@ -0,0 +1,117 @@ +--- +hide_table_of_contents: true +wrapperClassName: bootstrap-wrapper +--- + +import { SearchField } from '@site/src/components/SearchField'; +import { stackManipulationOpcodes as opcodes } from '@site/src/data/opcodes'; + +# Stack Manipulation + +Here `0 <= i,j,k <= 15` if not stated otherwise. + +### TVM Instructions Content List + +- [Overview](/learn/tvm-instructions/instructions) +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [Tuple, List and Null](/learn/tvm-instructions/instructions/tuple-list-null) +- [Constants and Literals](/learn/tvm-instructions/instructions/constant) +- [Arithmetic Operations](/learn/tvm-instructions/instructions/arithmetic) +- [Data Comparison](/learn/tvm-instructions/instructions/data-comparison) +- [Cell Manipulation](/learn/tvm-instructions/instructions/cell-manipulation) +- [Continuation and Control Flow](/learn/tvm-instructions/instructions/control-flow) +- [Exception Generation and Handling](/learn/tvm-instructions/instructions/exception-gen-and-handling) +- [Dictionary Manipulation](/learn/tvm-instructions/instructions/dictionary-manipulation) +- [Application-specific Primitives](/learn/tvm-instructions/instructions/app-specific) +- [Miscellaneous](/learn/tvm-instructions/instructions/miscellaneous) + + + +### Basic stack manipulation primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :-------------------------- | :-------------------------------------------------------------------------------------------------------------- | :----------- | +| **`00`** | `NOP` | _`-`_ | Does nothing. | `18` | +| **`01`** | `SWAP` | _`x y - y x`_ | Same as [`s1 XCHG0`](#instr-xchg-0i). | `18` | +| **`0i`** | `s[i] XCHG0` | | Interchanges `s0` with `s[i]`, `1 <= i <= 15`. | `18` | +| **`10ij`** | `s[i] s[j] XCHG` | | Interchanges `s[i]` with `s[j]`, `1 <= i < j <= 15`. | `26` | +| **`11ii`** | `s0 [ii] s() XCHG` | | Interchanges `s0` with `s[ii]`, `0 <= ii <= 255`. | `26` | +| **`1i`** | `s1 s[i] XCHG` | | Interchanges `s1` with `s[i]`, `2 <= i <= 15`. | `18` | +| **`2i`** | `s[i] PUSH` | | Pushes a copy of the old `s[i]` into the stack. | `18` | +| **`20`** | `DUP` | _`x - x x`_ | Same as [`s0 PUSH`](#instr-push). | `18` | +| **`21`** | `OVER` | _`x y - x y x`_ | Same as [`s1 PUSH`](#instr-push). | `18` | +| **`3i`** | `s[i] POP` | | Pops the old `s0` value into the old `s[i]`. Equivalent to [`s[i] XCHG0`](#instr-xchg-0i) [`DROP`](#instr-drop) | `18` | +| **`30`** | `DROP` | _`x -`_ | Same as [`s0 POP`](#instr-pop), discards the top-of-stack value. | `18` | +| **`31`** | `NIP` | _`x y - y`_ | Same as [`s1 POP`](#instr-pop). | `18` | + +### Complex stack manipulation + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :---------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`4ijk`** | `s[i] s[j] s[k] XCHG3` | | Equivalent to [`s2 s[i] XCHG`](#instr-xchg-ij) [`s1 s[j] XCHG`](#instr-xchg-ij) [`s[k] XCHG0`](#instr-xchg-0i). | `26` | +| **`50ij`** | `s[i] s[j] XCHG2` | | Equivalent to [`s1 s[i] XCHG`](#instr-xchg-ij) [`s[j] XCHG0`](#instr-xchg-0i). | `26` | +| **`51ij`** | `s[i] s[j] XCPU` | | Equivalent to [`s[i] XCHG0`](#instr-xchg-0i) [`s[j] PUSH`](#instr-push). | `26` | +| **`52ij`** | `s[i] s[j-1] PUXC` | | Equivalent to [`s[i] PUSH`](#instr-push) [`SWAP`](#instr-swap) [`s[j] XCHG0`](#instr-xchg-0i). | `26` | +| **`53ij`** | `s[i] s[j] PUSH2` | | Equivalent to [`s[i] PUSH`](#instr-push) [`s[j+1] PUSH`](#instr-push). | `26` | +| **`540ijk`** | `s[i] s[j] s[k] XCHG3_l` | | Long form of [`XCHG3`](#instr-xchg3). | `34` | +| 541ijk | `s[i] s[j] s[k] XC2PU` | | Equivalent to [`s[i] s[j] XCHG2`](#instr-xchg2) [`s[k] PUSH`](#instr-push). | `34` | +| **`542ijk`** | `s[i] s[j] s[k-1] XCPUXC` | | Equivalent to [`s1 s[i] XCHG`](#instr-xchg-ij) [`s[j] s[k-1] PUXC`](#instr-puxc). | `34` | +| **`543ijk`** | `s[i] s[j] s[k] XCPU2` | | Equivalent to [`s[i] XCHG0`](#instr-xchg-0i) [`s[j] s[k] PUSH2`](#instr-push2). | `34` | +| **`544ijk`** | `s[i] s[j-1] s[k-1] PUXC2` | | Equivalent to [`s[i] PUSH`](#instr-push) [`s2 XCHG0`](#instr-xchg-0i) [`s[j] s[k] XCHG2`](#instr-xchg2). | `34` | +| **`545ijk`** | `s[i] s[j-1] s[k-1] PUXCPU` | | Equivalent to [`s[i] s[j-1] PUXC`](#instr-puxc) [`s[k] PUSH`](#instr-push). | `34` | +| **`546ijk`** | `s[i] s[j-1] s[k-2] PU2XC` | | Equivalent to [`s[i] PUSH`](#instr-push) [`SWAP`](#instr-swap) [`s[j] s[k-1] PUXC`](#instr-puxc). | `34` | +| **`547ijk`** | `s[i] s[j] s[k] PUSH3` | | Equivalent to [`s[i] PUSH`](#instr-push) [`s[j+1] s[k+1] PUSH2`](#instr-push2). | `34` | +| **`55ij`** | `[i+1] [j+1] BLKSWAP` | | 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`](#instr-reverse) [`[j+1] 0 REVERSE`](#instr-reverse) [`[i+j+2] 0 REVERSE`](#instr-reverse). | `26` | +| **`5513`** | `ROT2`
`2ROT` | _`a b c d e f - c d e f a b`_ | Rotates the three topmost pairs of stack entries. | `26` | +| **`550i`** | `[i+1] ROLL` | | Rotates the top `i+1` stack entries.
Equivalent to [`1 [i+1] BLKSWAP`](#instr-blkswap). | `26` | +| **`55i0`** | `[i+1] -ROLL`
`[i+1] ROLLREV` | | Rotates the top `i+1` stack entries in the other direction.
Equivalent to [`[i+1] 1 BLKSWAP`](#instr-blkswap). | `26` | +| **`56ii`** | `[ii] s() PUSH` | | Pushes a copy of the old `s[ii]` into the stack.
`0 <= ii <= 255` | `26` | +| **`57ii`** | `[ii] s() POP` | | Pops the old `s0` value into the old `s[ii]`.
`0 <= ii <= 255` | `26` | +| **`58`** | `ROT` | _`a b c - b c a`_ | Equivalent to [`1 2 BLKSWAP`](#instr-blkswap) or to [`s2 s1 XCHG2`](#instr-xchg2). | `18` | +| **`59`** | `ROTREV`
`-ROT` | _`a b c - c a b`_ | Equivalent to [`2 1 BLKSWAP`](#instr-blkswap) or to [`s2 s2 XCHG2`](#instr-xchg2). | `18` | +| **`5A`** | `SWAP2`
`2SWAP` | _`a b c d - c d a b`_ | Equivalent to [`2 2 BLKSWAP`](#instr-blkswap) or to [`s3 s2 XCHG2`](#instr-xchg2). | `18` | +| **`5B`** | `DROP2`
`2DROP` | _`a b - `_ | Equivalent to [`DROP`](#instr-drop) [`DROP`](#instr-drop). | `18` | +| **`5C`** | `DUP2`
`2DUP` | _`a b - a b a b`_ | Equivalent to [`s1 s0 PUSH2`](#instr-push2). | `18` | +| **`5D`** | `OVER2`
`2OVER` | _`a b c d - a b c d a b`_ | Equivalent to [`s3 s2 PUSH2`](#instr-push2). | `18` | +| **`5Eij`** | `[i+2] [j] REVERSE` | | Reverses the order of `s[j+i+1] … s[j]`. | `26` | +| **`5F0i`** | `[i] BLKDROP` | | Equivalent to [`DROP`](#instr-drop) performed `i` times. | `26` | +| **`5Fij`** | `[i] [j] BLKPUSH` | | Equivalent to `PUSH s(j)` performed `i` times.
`1 <= i <= 15`, `0 <= j <= 15`. | `26` | +| **`60`** | `PICK`
`PUSHX` | | Pops integer `i` from the stack, then performs [`s[i] PUSH`](#instr-push). | `18` | +| **`61`** | `ROLLX` | | Pops integer `i` from the stack, then performs [`1 [i] BLKSWAP`](#instr-blkswap). | `18` | +| **`62`** | `-ROLLX`
`ROLLREVX` | | Pops integer `i` from the stack, then performs [`[i] 1 BLKSWAP`](#instr-blkswap). | `18` | +| **`63`** | `BLKSWX` | | Pops integers `i`,`j` from the stack, then performs [`[i] [j] BLKSWAP`](#instr-blkswap). | `18` | +| **`64`** | `REVX` | | Pops integers `i`,`j` from the stack, then performs [`[i] [j] REVERSE`](#instr-reverse). | `18` | +| **`65`** | `DROPX` | | Pops integer `i` from the stack, then performs [`[i] BLKDROP`](#instr-blkdrop). | `18` | +| **`66`** | `TUCK` | _`a b - b a b`_ | Equivalent to [`SWAP`](#instr-swap) [`OVER`](#instr-over) or to [`s1 s1 XCPU`](#instr-xcpu). | `18` | +| **`67`** | `XCHGX` | | Pops integer `i` from the stack, then performs [`s[i] XCHG`](#instr-xchg-ij). | `18` | +| **`68`** | `DEPTH` | _`- depth`_ | Pushes the current depth of the stack. | `18` | +| **`69`** | `CHKDEPTH` | _`i -`_ | Pops integer `i` from the stack, then checks whether there are at least `i` elements, generating a stack underflow exception otherwise. | `18/58` | +| **`6A`** | `ONLYTOPX` | | Pops integer `i` from the stack, then removes all but the top `i` elements. | `18` | +| **`6B`** | `ONLYX` | | Pops integer `i` from the stack, then leaves only the bottom `i` elements. Approximately equivalent to [`DEPTH`](#instr-depth) [`SWAP`](#instr-swap) [`SUB`](#instr-sub) [`DROPX`](#instr-dropx). | `18` | +| **`6Cij`** | `[i] [j] BLKDROP2` | | Drops `i` stack elements under the top `j` elements.
`1 <= i <= 15`, `0 <= j <= 15`
Equivalent to [`[i+j] 0 REVERSE`](#instr-reverse) [`[i] BLKDROP`](#instr-blkdrop) [`[j] 0 REVERSE`](#instr-reverse). | `26` | + +### TVM Instructions Content List + +- [Overview](/learn/tvm-instructions/instructions) +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [Tuple, List and Null](/learn/tvm-instructions/instructions/tuple-list-null) +- [Constants and Literals](/learn/tvm-instructions/instructions/constant) +- [Arithmetic Operations](/learn/tvm-instructions/instructions/arithmetic) +- [Data Comparison](/learn/tvm-instructions/instructions/data-comparison) +- [Cell Manipulation](/learn/tvm-instructions/instructions/cell-manipulation) +- [Continuation and Control Flow](/learn/tvm-instructions/instructions/control-flow) +- [Exception Generation and Handling](/learn/tvm-instructions/instructions/exception-gen-and-handling) +- [Dictionary Manipulation](/learn/tvm-instructions/instructions/dictionary-manipulation) +- [Application-specific Primitives](/learn/tvm-instructions/instructions/app-specific) +- [Miscellaneous](/learn/tvm-instructions/instructions/miscellaneous) From 31fdc388f58a4d11f9bd56c2fcd6f7b6a60339ce Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:01:06 +0800 Subject: [PATCH 216/219] New translations tuple-list-null.mdx (Chinese Simplified) --- .../instructions/tuple-list-null.mdx | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/tuple-list-null.mdx diff --git a/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/tuple-list-null.mdx b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/tuple-list-null.mdx new file mode 100644 index 0000000000..ec3d86fa5c --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-pages/learn/tvm-instructions/instructions/tuple-list-null.mdx @@ -0,0 +1,115 @@ +--- +hide_table_of_contents: true +wrapperClassName: bootstrap-wrapper +--- + +import { SearchField } from '@site/src/components/SearchField'; +import { tupleOpcodes as opcodes } from '@site/src/data/opcodes'; + +# Tuple, List, and Null primitives + +### TVM Instructions Content List + +- [Overview](/learn/tvm-instructions/instructions) +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [Tuple, List and Null](/learn/tvm-instructions/instructions/tuple-list-null) +- [Constants and Literals](/learn/tvm-instructions/instructions/constant) +- [Arithmetic Operations](/learn/tvm-instructions/instructions/arithmetic) +- [Data Comparison](/learn/tvm-instructions/instructions/data-comparison) +- [Cell Manipulation](/learn/tvm-instructions/instructions/cell-manipulation) +- [Continuation and Control Flow](/learn/tvm-instructions/instructions/control-flow) +- [Exception Generation and Handling](/learn/tvm-instructions/instructions/exception-gen-and-handling) +- [Dictionary Manipulation](/learn/tvm-instructions/instructions/dictionary-manipulation) +- [Application-specific Primitives](/learn/tvm-instructions/instructions/app-specific) +- [Miscellaneous](/learn/tvm-instructions/instructions/miscellaneous) + + + +### Tuple, List, and Null primitives + +| xxxxxxx
Opcode | xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Fift syntax | xxxxxxxxxxxxxxxxx
Stack | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Description | xxxx
Gas | +| :----------------- | :------------------------------------------- | :----------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | +| **`6D`** | `NULL`
`PUSHNULL` | _` - null`_ | Pushes the only value of type _Null_. | `18` | +| **`6E`** | `ISNULL` | _`x - ?`_ | Checks whether `x` is a _Null_, and returns `-1` or `0` accordingly. | `18` | +| **`6F0n`** | `[n] TUPLE` | _`x_1 ... x_n - t`_ | Creates a new _Tuple_ `t=(x_1, … ,x_n)` containing `n` values `x_1`,..., `x_n`.
`0 <= n <= 15` | `26+n` | +| **`6F00`** | `NIL` | _`- t`_ | Pushes the only _Tuple_ `t=()` of length zero. | `26` | +| **`6F01`** | `SINGLE` | _`x - t`_ | Creates a singleton `t:=(x)`, i.e., a _Tuple_ of length one. | `27` | +| **`6F02`** | `PAIR`
`CONS` | _`x y - t`_ | Creates pair `t:=(x,y)`. | `28` | +| **`6F03`** | `TRIPLE` | _`x y z - t`_ | Creates triple `t:=(x,y,z)`. | `29` | +| **`6F1k`** | `[k] INDEX` | _`t - x`_ | Returns the `k`-th element of a _Tuple_ `t`.
`0 <= k <= 15`. | `26` | +| **`6F10`** | `FIRST`
`CAR` | _`t - x`_ | Returns the first element of a _Tuple_. | `26` | +| **`6F11`** | `SECOND`
`CDR` | _`t - y`_ | Returns the second element of a _Tuple_. | `26` | +| **`6F12`** | `THIRD` | _`t - z`_ | Returns the third element of a _Tuple_. | `26` | +| **`6F2n`** | `[n] UNTUPLE` | _`t - x_1 ... x_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. | `26+n` | +| **`6F21`** | `UNSINGLE` | _`t - x`_ | Unpacks a singleton `t=(x)`. | `27` | +| **`6F22`** | `UNPAIR`
`UNCONS` | _`t - x y`_ | Unpacks a pair `t=(x,y)`. | `28` | +| **`6F23`** | `UNTRIPLE` | _`t - x y z`_ | Unpacks a triple `t=(x,y,z)`. | `29` | +| **`6F3k`** | `[k] UNPACKFIRST` | _`t - x_1 ... x_k`_ | Unpacks first `0 <= k <= 15` elements of a _Tuple_ `t`.
If `\|t\|`0 <= k <= 15`
If `k >= \|t\|`, throws a range check exception. | `26+\|t\|` | +| **`6F50`** | `SETFIRST` | _`t x - t'`_ | Sets the first component of _Tuple_ `t` to `x` and returns the resulting _Tuple_ `t'`. | `26+\|t\|` | +| **`6F51`** | `SETSECOND` | _`t x - t'`_ | Sets the second component of _Tuple_ `t` to `x` and returns the resulting _Tuple_ `t'`. | `26+\|t\|` | +| **`6F52`** | `SETTHIRD` | _`t x - t'`_ | Sets the third component of _Tuple_ `t` to `x` and returns the resulting _Tuple_ `t'`. | `26+\|t\|` | +| **`6F6k`** | `[k] INDEXQ` | _`t - x`_ | 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`. | `26` | +| **`6F60`** | `FIRSTQ`
`CARQ` | _`t - x`_ | Returns the first element of a _Tuple_. | `26` | +| **`6F61`** | `SECONDQ`
`CDRQ` | _`t - y`_ | Returns the second element of a _Tuple_. | `26` | +| **`6F62`** | `THIRDQ` | _`t - z`_ | Returns the third element of a _Tuple_. | `26` | +| **`6F7k`** | `[k] SETINDEXQ` | _`t x - 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). | `26+\|t’\|` | +| **`6F70`** | `SETFIRSTQ` | _`t x - t'`_ | Sets the first component of _Tuple_ `t` to `x` and returns the resulting _Tuple_ `t'`. | `26+\|t’\|` | +| **`6F71`** | `SETSECONDQ` | _`t x - t'`_ | Sets the second component of _Tuple_ `t` to `x` and returns the resulting _Tuple_ `t'`. | `26+\|t’\|` | +| **`6F72`** | `SETTHIRDQ` | _`t x - t'`_ | Sets the third component of _Tuple_ `t` to `x` and returns the resulting _Tuple_ `t'`. | `26+\|t’\|` | +| **`6F80`** | `TUPLEVAR` | _`x_1 ... x_n n - t`_ | Creates a new _Tuple_ `t` of length `n` similarly to [`TUPLE`](#instr-tuple), but with `0 <= n <= 255` taken from the stack. | `26+n` | +| **`6F81`** | `INDEXVAR` | _`t k - x`_ | Similar to [`k INDEX`](#instr-index), but with `0 <= k <= 254` taken from the stack. | `26` | +| **`6F82`** | `UNTUPLEVAR` | _`t n - x_1 ... x_n`_ | Similar to [`n UNTUPLE`](#instr-untuple), but with `0 <= n <= 255` taken from the stack. | `26+n` | +| **`6F83`** | `UNPACKFIRSTVAR` | _`t n - x_1 ... x_n`_ | Similar to [`n UNPACKFIRST`](#instr-unpackfirst), but with `0 <= n <= 255` taken from the stack. | `26+n` | +| **`6F84`** | `EXPLODEVAR` | _`t n - x_1 ... x_m m`_ | Similar to [`n EXPLODE`](#instr-explode), but with `0 <= n <= 255` taken from the stack. | `26+m` | +| **`6F85`** | `SETINDEXVAR` | _`t x k - t'`_ | Similar to [`k SETINDEX`](#instr-setindex), but with `0 <= k <= 254` taken from the stack. | `26+\|t’\|` | +| **`6F86`** | `INDEXVARQ` | _`t k - x`_ | Similar to [`n INDEXQ`](#instr-indexq), but with `0 <= k <= 254` taken from the stack. | `26` | +| **`6F87`** | `SETINDEXVARQ` | _`t x k - t'`_ | Similar to [`k SETINDEXQ`](#instr-setindexq), but with `0 <= k <= 254` taken from the stack. | `26+\|t’\|` | +| **`6F88`** | `TLEN` | _`t - n`_ | Returns the length of a _Tuple_. | `26` | +| **`6F89`** | `QTLEN` | _`t - n or -1`_ | Similar to [`TLEN`](#instr-tlen), but returns `-1` if `t` is not a _Tuple_. | `26` | +| **`6F8A`** | `ISTUPLE` | _`t - ?`_ | Returns `-1` or `0` depending on whether `t` is a _Tuple_. | `26` | +| **`6F8B`** | `LAST` | _`t - x`_ | Returns the last element of a non-empty _Tuple_ `t`. | `26` | +| **`6F8C`** | `TPUSH`
`COMMA` | _`t x - 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. | `26+\|t’\|` | +| **`6F8D`** | `TPOP` | _`t - t' x`_ | 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`. | `26+\|t’\|` | +| **`6FA0`** | `NULLSWAPIF` | _`x - x or null x`_ | Pushes a _Null_ under the topmost _Integer_ `x`, but only if `x!=0`. | `26` | +| **`6FA1`** | `NULLSWAPIFNOT` | _`x - x or null x`_ | 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`](#instr-plduxq). | `26` | +| **`6FA2`** | `NULLROTRIF` | _`x y - x y or null x y`_ | Pushes a _Null_ under the second stack entry from the top, but only if the topmost _Integer_ `y` is non-zero. | `26` | +| **`6FA3`** | `NULLROTRIFNOT` | _`x y - x y or null x y`_ | 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`](#instr-lduxq). | `26` | +| **`6FA4`** | `NULLSWAPIF2` | _`x - x or null null x`_ | Pushes two nulls under the topmost _Integer_ `x`, but only if `x!=0`.
Equivalent to [`NULLSWAPIF`](#instr-nullswapif) [`NULLSWAPIF`](#instr-nullswapif). | `26` | +| **`6FA5`** | `NULLSWAPIFNOT2` | _`x - x or null null x`_ | Pushes two nulls under the topmost _Integer_ `x`, but only if `x=0`.
Equivalent to [`NULLSWAPIFNOT`](#instr-nullswapifnot) [`NULLSWAPIFNOT`](#instr-nullswapifnot). | `26` | +| **`6FA6`** | `NULLROTRIF2` | _`x y - x y or null null x y`_ | Pushes two nulls under the second stack entry from the top, but only if the topmost _Integer_ `y` is non-zero.
Equivalent to [`NULLROTRIF`](#instr-nullrotrif) [`NULLROTRIF`](#instr-nullrotrif). | `26` | +| **`6FA7`** | `NULLROTRIFNOT2` | _`x y - x y or null null x y`_ | Pushes two nulls under the second stack entry from the top, but only if the topmost _Integer_ `y` is zero.
Equivalent to [`NULLROTRIFNOT`](#instr-nullrotrifnot) [`NULLROTRIFNOT`](#instr-nullrotrifnot). | `26` | +| **`6FBij`** | `[i] [j] INDEX2` | _`t - x`_ | Recovers `x=(t_{i+1})_{j+1}` for `0 <= i,j <= 3`.
Equivalent to [`[i] INDEX`](#instr-index) [`[j] INDEX`](#instr-index). | `26` | +| **`6FB4`** | `CADR` | _`t - x`_ | Recovers `x=(t_2)_1`. | `26` | +| **`6FB5`** | `CDDR` | _`t - x`_ | Recovers `x=(t_2)_2`. | `26` | +| **`6FE_ijk`** | `[i] [j] [k] INDEX3` | _`t - x`_ | Recovers `x=t_{i+1}_{j+1}_{k+1}`.
`0 <= i,j,k <= 3`
Equivalent to [`[i] [j] INDEX2`](#instr-index2) [`[k] INDEX`](#instr-index). | `26` | +| **`6FD4`** | `CADDR` | _`t - x`_ | Recovers `x=t_2_2_1`. | `26` | +| **`6FD5`** | `CDDDR` | _`t - x`_ | Recovers `x=t_2_2_2`. | `26` | + +### TVM Instructions Content List + +- [Overview](/learn/tvm-instructions/instructions) +- [Stack Manipulation](/learn/tvm-instructions/instructions/stack-manipulation) +- [Tuple, List and Null](/learn/tvm-instructions/instructions/tuple-list-null) +- [Constants and Literals](/learn/tvm-instructions/instructions/constant) +- [Arithmetic Operations](/learn/tvm-instructions/instructions/arithmetic) +- [Data Comparison](/learn/tvm-instructions/instructions/data-comparison) +- [Cell Manipulation](/learn/tvm-instructions/instructions/cell-manipulation) +- [Continuation and Control Flow](/learn/tvm-instructions/instructions/control-flow) +- [Exception Generation and Handling](/learn/tvm-instructions/instructions/exception-gen-and-handling) +- [Dictionary Manipulation](/learn/tvm-instructions/instructions/dictionary-manipulation) +- [Application-specific Primitives](/learn/tvm-instructions/instructions/app-specific) +- [Miscellaneous](/learn/tvm-instructions/instructions/miscellaneous) From 727e7873b417d7949ec9b65fda1204315b20114d Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:01:07 +0800 Subject: [PATCH 217/219] New translations custom-overlays.md (Chinese Simplified) --- .../network-maintenance/custom-overlays.md | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/custom-overlays.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/custom-overlays.md b/i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/custom-overlays.md new file mode 100644 index 0000000000..d37f4efe2f --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/participate/network-maintenance/custom-overlays.md @@ -0,0 +1,93 @@ +# Custom overlays + +TON `v2024.04` update introduces the ability to use custom overlays.\ +Currently they can be used only for broadcasting external messages. The main idea is to create private overlay with +sender nodes and validators. Only sender nodes can create broadcasts with external messages which will be (hopefully) +received by block collator and get into the block. + +## Default custom overlays + +Mytonctrl uses default custom overlays available at https://ton-blockchain.github.io/fallback_custom_overlays.json. To +stop participation in default custom overlays run commands + +```bash +MyTonCtrl> set useDefaultCustomOverlays false +MyTonCtrl> delete_custom_overlay default +``` + +## Create custom overlay + +### Collect adnl addresses + +To add validators to a custom overlay you can use either their `fullnode adnl id` available with `validator-console -c getconfig` +or `validator adnl id` which can be found in mytonctrl's status. +To add liteservers to a custom overlay you must use their `fullnode adnl id`. + +### Create a config file + +Create a config file in format: + +```json +{ + "adnl_address_hex_1": { + "msg_sender": true, + "msg_sender_priority": 1 + }, + "adnl_address_hex_2": { + "msg_sender": false + }, + ... +} +``` + +`msg_sender_priority` determines the order of external message inclusion to blocks: first processed messages from higher priority source. Messages from public overlay and local LS have priority 0. + +**Note, all nodes listed in config should participate in overlay (in other words they need to add overlay with exactly this config), otherwise connectivity will be poor and broadcasts will fail** + +There is special word `@validators` to create a dynamic custom overlay that mytonctrl will generate automatically +each round adding all current validators. + +### Add custom overlay + +Use mytonctrl command to add custom overlay: + +```bash +MyTonCtrl> add_custom_overlay +``` + +Note, that name and config file **must** be the same on all overlay members. Check that overlay has been created using +mytonctrl `list_custom_overlays` command. + +### Debug + +You can set node verbosity level equals to 4 and grep logs with "CustomOverlay" word. + +## Delete custom overlay + +To remove custom overlay from a node, use mytonctrl command `delete_custom_overlay `. +If the overlay is dynamic (i.e. there is `@validators` word in config) it will be deleted within one minute, otherwise it will be removed instantly. +To make sure that node has deleted custom overlay check `list_custom_overlays` mytonctrl and `showcustomoverlays` validator-console commands. + +## Low level + +List of validator-console commands to work with custom overlays: + +- `addcustomoverlay ` - add custom overlay to local node. Note, that this config must be in a format other than config for mytonctrl: + ```json + { + "name": "OverlayName", + "nodes": [ + { + "adnl_id": "adnl_address_b64_1", + "msg_sender": true, + "msg_sender_priority": 1 + }, + { + "adnl_id": "adnl_address_b64_2", + "msg_sender": false + }, ... + ] + } + ``` +- `delcustomoverlay ` - delete custom overlay from node. +- `showcustomoverlays` - show list of custom overlays node knows about. From 12e95ad801b19071218c15e0e9ad1da6257286bc Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:01:07 +0800 Subject: [PATCH 218/219] New translations address-verification.mdx (Chinese Simplified) --- .../asset-processing/address-verification.mdx | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/address-verification.mdx diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/address-verification.mdx b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/address-verification.mdx new file mode 100644 index 0000000000..608483649d --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/develop/dapps/asset-processing/address-verification.mdx @@ -0,0 +1,60 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Wallet Address Validation + +### How to Check the Validity of a TON Wallet Address? + + + + + +```js + const TonWeb = require("tonweb") + TonWeb.utils.Address.isValid('...') +``` + + + + +```python +package main + +import ( + "fmt" + "github.com/xssnick/tonutils-go/address" +) + +if _, err := address.ParseAddr("EQCD39VS5j...HUn4bpAOg8xqB2N"); err != nil { + return errors.New("invalid address") +} +``` + + + + +```javascript +try { + Address.of("..."); + } catch (e) { + // not valid address +} +``` + + + + +```javascript + try { + AddrStd("...") + } catch(e: IllegalArgumentException) { + // not valid address + } +``` + + + + +:::tip +Full Address description on the [Smart Contract Addresses](/learn/overviews/addresses) page. +::: From b3477462c8bf06ad83ec1b3bcd3b28648a884013 Mon Sep 17 00:00:00 2001 From: TonSquare <147710825+TonSquare@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:01:08 +0800 Subject: [PATCH 219/219] New translations academy-overview.md (Chinese Simplified) --- .../current/learn/academy/academy-overview.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 i18n/zh/docusaurus-plugin-content-docs/current/learn/academy/academy-overview.md diff --git a/i18n/zh/docusaurus-plugin-content-docs/current/learn/academy/academy-overview.md b/i18n/zh/docusaurus-plugin-content-docs/current/learn/academy/academy-overview.md new file mode 100644 index 0000000000..c1be943f1f --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs/current/learn/academy/academy-overview.md @@ -0,0 +1,13 @@ +# Educational Resources + +## TON Blockchain Course + +:::danger +Page under the development. +::: + +## See Also + +- [Speedrun TON](https://tonspeedrun.com/) +- [TON Hello World](https://tonhelloworld.com/01-wallet/) +- [[YouTube] TON Dev Study EN ](https://www.youtube.com/@TONDevStudy)[[RU]](https://www.youtube.com/results?search_query=tondevstudy)