From 672828d983bd1af750d34e908541a544681e4de4 Mon Sep 17 00:00:00 2001 From: Dmitry Popov Date: Wed, 30 Aug 2023 10:37:21 +0300 Subject: [PATCH 1/9] Fix typo Value of second cell is FF, not AA. That was cell `7[FE]` padded to FF. --- docs/develop/data-formats/cell-boc.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/develop/data-formats/cell-boc.mdx b/docs/develop/data-formats/cell-boc.mdx index 1544623d3e..a085ca62a7 100644 --- a/docs/develop/data-formats/cell-boc.mdx +++ b/docs/develop/data-formats/cell-boc.mdx @@ -112,7 +112,7 @@ Now let's add the refs indexes: And put it all together: ```json 0201 C0 0201 -0101 AA 02 +0101 FF 02 0006 0AAAAA ``` From f684b8e63118a57723b4dbf1a3ccd52aae9f7e50 Mon Sep 17 00:00:00 2001 From: Purple Guy <89442150+purpleguy99@users.noreply.github.com> Date: Wed, 6 Sep 2023 18:38:00 +0300 Subject: [PATCH 2/9] Update developers.md --- docs/develop/dapps/ton-connect/developers.md | 22 ++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/develop/dapps/ton-connect/developers.md b/docs/develop/dapps/ton-connect/developers.md index a02de08dac..2150740dd8 100644 --- a/docs/develop/dapps/ton-connect/developers.md +++ b/docs/develop/dapps/ton-connect/developers.md @@ -107,6 +107,28 @@ Use it to connect your app to TON wallets via TonConnect protocol. * [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 Unity + +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) + ## 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. From d10bcd663a2c282c22eaacc1b03500af0004801c Mon Sep 17 00:00:00 2001 From: Spite Date: Fri, 15 Sep 2023 17:49:25 +0400 Subject: [PATCH 3/9] Add wrappers cookbok and initial examples --- docs/develop/dapps/cookbook.md | 492 +++++++++++++++++++++++++++++++++ sidebars.js | 2 +- 2 files changed, 493 insertions(+), 1 deletion(-) create mode 100644 docs/develop/dapps/cookbook.md diff --git a/docs/develop/dapps/cookbook.md b/docs/develop/dapps/cookbook.md new file mode 100644 index 0000000000..f22983b80f --- /dev/null +++ b/docs/develop/dapps/cookbook.md @@ -0,0 +1,492 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Wrappers 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. + +### How to convert (user friendly <-> raw), assemble, and extract addresses from strings? + +On TON, depending on the service, addresses can be seen in two formats: `user-friendly` and `raw`. + +```bash +User-friendly: EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF +Raw: 0:ca6e321c7cce9ecedf0a8ca2492ec8592494aa5fb5ce0387dff96ef6af982a3e +``` + +User-friendly addresses are encoded in base64, while raw addresses are encoded in hex. In the raw format, the workchain in which the address is located is written separately before the ":" character, and the case of the characters does not matter. + +To obtain an address from a string, 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 tonsdk.utils._address import Address + +address1 = Address('EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF') +address2 = Address('0:ca6e321c7cce9ecedf0a8ca2492ec8592494aa5fb5ce0387dff96ef6af982a3e') + +# to_string() arguments: is_user_friendly, is_url_safe, is_bounceable, is_test_only + +print(address1.to_string(is_user_friendly=True, is_bounceable=True, is_url_safe=True)) # EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF +print(address1.to_string(is_user_friendly=False)) # 0:ca6e321c7cce9ecedf0a8ca2492ec8592494aa5fb5ce0387dff96ef6af982a3e + +print(address2.to_string(is_user_friendly=True, is_bounceable=True, is_url_safe=True)) # EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF +print(address2.to_string(is_user_friendly=False)) # 0:ca6e321c7cce9ecedf0a8ca2492ec8592494aa5fb5ce0387dff96ef6af982a3e +``` + + + + +### How to obtain different types of addresses and determine the address type? + +Addresses come in three formats: **bounceable**, **non-bounceable**, and **testnet**. This can be easily understood by looking at the first letter of the address, because it is the first byte (8 bits) that contains flags according to [TEP-2](https://github.com/ton-blockchain/TEPs/blob/master/text/0002-address.md#smart-contract-addresses): + +Letter | Binary form | Bounceable | Testnet +:---: | :---: | :---: | :---: +E | 00010001 | yes | no | +U | 01010001 | no | no | +k | 10010001 | yes | yes | +0 | 11010001 | no | yes | + +Also, in some libraries, you may notice a field called "url safe." The thing is, the base64 format is not url safe, which means there can be issues when transmitting this 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 tonsdk.utils._address import Address + +address = Address('EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF') + +# to_string() arguments: is_user_friendly, is_url_safe, is_bounceable, is_test_only + +print(address.to_string(is_user_friendly=True, is_bounceable=True, is_url_safe=True, is_test_only=False)) # EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF +print(address.to_string(is_user_friendly=True, is_bounceable=True, is_url_safe=False, is_test_only=False)) # EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff+W72r5gqPrHF +print(address.to_string(is_user_friendly=True, is_bounceable=False, is_url_safe=True, is_test_only=False)) # UQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPuwA +print(address.to_string(is_user_friendly=True, is_bounceable=True, is_url_safe=True, is_test_only=True)) # kQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPgpP +print(address.to_string(is_user_friendly=True, is_bounceable=False, is_url_safe=True, is_test_only=True)) # 0QDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPleK +``` + + + + +### 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. It's important to note that each token can have its own `decimals`, which defaults to `9`. So, in the example below, we multiply the quantity by 10^9. If decimals were different, you would **need to multiply by a different value**. + + + + +```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 your wallet address that owns jetton wallet'); + + 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) + .storeCoins(toNano(5)) // jetton amount, amount * 10^9 + .storeAddress(destinationAddress) + .storeAddress(destinationAddress) // response destination + .storeBit(0) // no custom payload + .storeCoins(toNano('0.02')) + .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...")); +``` + + + + +To indicate that we want to include a comment, we specify 32 zero bits and then write our comment. We also specify the `response destination`, which means that a response regarding the successful transfer will be sent to this address. If we don't want a response, we can specify 2 zero bits instead of an address. + +### 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('EQBbQljOpEM4Z6Hvv8Dbothp9xp2yM-TFYVr01bSqDQskHbx'), + Address.parse('EQBbQljOpEM4Z6Hvv8Dbothp9xp2yM-TFYVr01bSqDQskHbx') + ]; + const nftsMeta = [ + '0/meta.json', + '0/meta.json', + '0/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 + }); + const internalMessageCell = beginCell() + .store(storeMessageRelaxed(internalMessage)) + .endCell(); +} + +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) + .storeUint(0, 64) + .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...")); +``` + + + + + +### 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) + .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...")); +``` + + + + +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. \ No newline at end of file diff --git a/sidebars.js b/sidebars.js index 41a3c6b9a8..c1cae83f7b 100644 --- a/sidebars.js +++ b/sidebars.js @@ -239,7 +239,7 @@ const sidebars = { label: 'DApps Development', items: [ 'develop/dapps/README', - + 'develop/dapps/cookbook', { type: 'category', label: 'Telegram Web Apps', From 6e7c69a2050c692f8d8493454232bfb69e9e4779 Mon Sep 17 00:00:00 2001 From: Spite Date: Sat, 16 Sep 2023 21:51:49 +0400 Subject: [PATCH 4/9] Add some improvements --- docs/develop/dapps/cookbook.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/docs/develop/dapps/cookbook.md b/docs/develop/dapps/cookbook.md index f22983b80f..616bf5c6c6 100644 --- a/docs/develop/dapps/cookbook.md +++ b/docs/develop/dapps/cookbook.md @@ -155,6 +155,8 @@ U | 01010001 | no | no | k | 10010001 | yes | yes | 0 | 11010001 | no | yes | +It's important to note that in base64 encoding, each character represents **6 bits** of information. As you can observe, in all cases, the last 2 bits remain unchanged, so in this case, we can focus on the first letter. If they changed, it would affect the next character in the address. + Also, in some libraries, you may notice a field called "url safe." The thing is, the base64 format is not url safe, which means there can be issues when transmitting this 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: @@ -258,12 +260,12 @@ async function main() { const messageBody = beginCell() .storeUint(0x0f8a7ea5, 32) // opcode for jetton transfer - .storeUint(0, 64) + .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')) + .storeCoins(toNano('0.02')) // forward amount .storeBit(1) // we store forwardPayload as a reference .storeRef(forwardPayload) .endCell(); @@ -306,13 +308,13 @@ async function main() { }); const ownersAddress = [ Address.parse('EQBbQljOpEM4Z6Hvv8Dbothp9xp2yM-TFYVr01bSqDQskHbx'), - Address.parse('EQBbQljOpEM4Z6Hvv8Dbothp9xp2yM-TFYVr01bSqDQskHbx'), - Address.parse('EQBbQljOpEM4Z6Hvv8Dbothp9xp2yM-TFYVr01bSqDQskHbx') + Address.parse('EQAUTbQiM522Y_XJ_T98QPhPhTmb4nV--VSPiha8kC6kRfPO'), + Address.parse('EQDWTH7VxFyk_34J1CM6wwEcjVeqRQceNwzPwGr30SsK43yo') ]; const nftsMeta = [ '0/meta.json', - '0/meta.json', - '0/meta.json' + '1/meta.json', + '2/meta.json' ]; const getMethodResult = await client.runMethod(collectionAddress, 'get_collection_data'); @@ -403,8 +405,8 @@ async function main() { const newOwnerAddress = Address.parse('put new owner wallet address'); const messageBody = beginCell() - .storeUint(3, 32) - .storeUint(0, 64) + .storeUint(3, 32) // opcode for changing owner + .storeUint(0, 64) // query id .storeAddress(newOwnerAddress) .endCell(); @@ -466,7 +468,7 @@ async function main() { const messageBody = beginCell() .storeUint(4, 32) // opcode for changing content - .storeUint(0, 64) + .storeUint(0, 64) // query id .storeRef(contentCell) .storeRef(royaltyCell) .endCell(); From d58a803d6609fc02667e14a1af07fcc9f7e98cdd Mon Sep 17 00:00:00 2001 From: Gleb Vorontsov Date: Mon, 18 Sep 2023 12:31:02 +0200 Subject: [PATCH 5/9] Update links --- docs/develop/dapps/telegram-apps/app-examples.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/develop/dapps/telegram-apps/app-examples.mdx b/docs/develop/dapps/telegram-apps/app-examples.mdx index 88e50c732f..6e111cedb7 100644 --- a/docs/develop/dapps/telegram-apps/app-examples.mdx +++ b/docs/develop/dapps/telegram-apps/app-examples.mdx @@ -8,7 +8,7 @@ Check out the examples below to see how to create your own Telegram Web App.


- logo of telegram web apps + logo of telegram web apps

@@ -20,7 +20,7 @@ This is a basic and straightforward Telegram Web App(TWA) implemented using plai - Deployment URL: [twa-dev.github.io/simple-telegram-web-app](https://twa-dev.github.io/simple-telegram-web-app/) - + @@ -40,7 +40,7 @@ To run this example, you'll need a modern web browser with JavaScript enabled. 1. Clone this repository to your local machine: ```bash -git clone https://github.com/twa-dev/simple-telegram-web-app.git +git clone https://github.com/twa-dev/vanilla-js-boilerplate.git ``` 2. Navigate to the project directory: @@ -64,7 +64,7 @@ Open index.html in your preferred code editor or IDE. 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 Web App example. -You can find example project here [https://github.com/twa-dev/my-react-telegram-web-app](https://github.com/twa-dev/my-react-telegram-web-app) or you can go through following instructions. +You can find example project here [https://github.com/twa-dev/vite-boilerplate](https://github.com/twa-dev/vite-boilerplate) or you can go through following instructions. ### Prerequisites From 86a35bc9971810714cad953bf9b3f335a966ed4d Mon Sep 17 00:00:00 2001 From: yungwine Date: Mon, 18 Sep 2023 20:39:14 +0800 Subject: [PATCH 6/9] add pytoniq and pytoniq-core to "SDKs" section --- docs/develop/dapps/apis/sdk.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/develop/dapps/apis/sdk.mdx b/docs/develop/dapps/apis/sdk.mdx index 6fc5af6465..25289ccbb9 100644 --- a/docs/develop/dapps/apis/sdk.mdx +++ b/docs/develop/dapps/apis/sdk.mdx @@ -24,6 +24,7 @@ The data provider is a [Liteserver](/participate/run-nodes/liteserver), which ca |[tonutils-go](https://github.com/xssnick/tonutils-go)|GO|Native ADNL |Golang library for interacting with TON blockchain| |[tongo](https://github.com/tonkeeper/tongo)|GO|Native ADNL |Go implementation of libraries for TON blockchain| |[tonutils](https://github.com/thekiba/tonutils) | TypeScript | Native ADNL | TypeScript-based interface for building and interacting with applications in TON Ecosystem. | +|[pytoniq](https://github.com/yungwine/pytoniq) | Python | Native ADNL | Python SDK with native LiteClient and other ADNL-based protocols implementations. | |[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.| |[justdmitry/TonLib.NET](https://github.com/justdmitry/TonLib.NET) | C# | Tonlib bin | .NET SDK for The Open Network| | [tonlib-rs](https://github.com/ston-fi/tonlib-rs) | Rust | Tonlib bin | Rust SDK for The Open Network| @@ -36,6 +37,7 @@ The data provider is a [Liteserver](/participate/run-nodes/liteserver), which ca | Library | Language | Description | |---------|----------|--------------| +|[pytoniq-core](https://github.com/yungwine/pytoniq-core) | Python | Python powerful transport-free SDK | |[ayrat555/ton](https://github.com/ayrat555/ton) | Elixir | TON SDK for Elixir| From fd497868987fe268e1ddcc39bb901fafb1e3be21 Mon Sep 17 00:00:00 2001 From: Rami Chasygov <3c9wjqgw5@mozmail.com> Date: Mon, 18 Sep 2023 17:03:00 +0300 Subject: [PATCH 7/9] Update ide-plugins.md The current version is a bit redundant, as it can be installed either through the nvim-treesitter CLI or configuration. https://github.com/ton-society/grants-and-bounties/issues/88#issue-1488757051 --- docs/develop/smart-contracts/environment/ide-plugins.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/develop/smart-contracts/environment/ide-plugins.md b/docs/develop/smart-contracts/environment/ide-plugins.md index 4ba3c69271..ff4222c3ca 100644 --- a/docs/develop/smart-contracts/environment/ide-plugins.md +++ b/docs/develop/smart-contracts/environment/ide-plugins.md @@ -22,6 +22,6 @@ There are several ways to install a plugin: - [GitHub repository](https://github.com/savva425/func_plugin_sublimetext3) -## neovim plugin +## Neovim -- [GitHub repository](https://github.com/cryshado/neovim-ton-dev) \ No newline at end of file +To enable syntax highlighting in Neovim, simply follow the installation instructions provided in the [nvim-treesitter quickstart guide](https://github.com/nvim-treesitter/nvim-treesitter#quickstart). From d54da055d55be68f8aa20a5a5b2cb5bcc5b83b61 Mon Sep 17 00:00:00 2001 From: Gleb Vorontsov Date: Mon, 18 Sep 2023 17:48:06 +0200 Subject: [PATCH 8/9] Links update --- docs/develop/dapps/telegram-apps/app-examples.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/develop/dapps/telegram-apps/app-examples.mdx b/docs/develop/dapps/telegram-apps/app-examples.mdx index 6e111cedb7..f41ea40235 100644 --- a/docs/develop/dapps/telegram-apps/app-examples.mdx +++ b/docs/develop/dapps/telegram-apps/app-examples.mdx @@ -17,7 +17,7 @@ This is a basic and straightforward Telegram Web App(TWA) implemented using plai - App is available via direct link: [t.me/simple_telegram_web_app_bot/app](https://t.me/simple_telegram_web_app_bot/app) - Or you can launch app with a bot menu button: [t.me/simple_telegram_web_app_bot](https://t.me/simple_telegram_web_app_bot) -- Deployment URL: [twa-dev.github.io/simple-telegram-web-app](https://twa-dev.github.io/simple-telegram-web-app/) +- Deployment URL: [twa-dev.github.io/vanilla-js-boilerplate](https://twa-dev.github.io/vanilla-js-boilerplate/) @@ -46,7 +46,7 @@ git clone https://github.com/twa-dev/vanilla-js-boilerplate.git 2. Navigate to the project directory: ```bash -cd simple-telegram-web-app +cd vanilla-js-boilerplate ``` Open index.html in your preferred code editor or IDE. @@ -55,7 +55,7 @@ Open index.html in your preferred code editor or IDE. 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/simple-telegram-web-app/`. You can copy this URL and use it with [BotFather](https://tg.me/BotFather) bot to create your very own TWA. +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 TWA Example From fa663cc62feaf313615319a13afb1569144c7f12 Mon Sep 17 00:00:00 2001 From: Evgeniy Trifonov <59412639+expectfun@users.noreply.github.com> Date: Wed, 20 Sep 2023 00:09:08 +0300 Subject: [PATCH 9/9] Edited simple-zk-on-ton.md The suggested version has been proofread and edited --- .../dapps/tutorials/simple-zk-on-ton.md | 210 +++++++++--------- 1 file changed, 104 insertions(+), 106 deletions(-) diff --git a/docs/develop/dapps/tutorials/simple-zk-on-ton.md b/docs/develop/dapps/tutorials/simple-zk-on-ton.md index 61feaf8400..2eb62a858d 100644 --- a/docs/develop/dapps/tutorials/simple-zk-on-ton.md +++ b/docs/develop/dapps/tutorials/simple-zk-on-ton.md @@ -1,56 +1,52 @@ -# Simple Zero-Knowledge project on TON - -Welcome to the simple Zero-Knowledge project on TON tutorial. In this tutorial, we will learn about Zero-Knowledge proofs and how to use them in TON. +# Building a simple ZK project on TON ## 👋 Introduction -**Zero-knowledge** proofs are a fundamental cryptographic primitive that allows one party (the prover) to prove to another (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, and trustless-bridges. +**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 June 2023 update -Before June 2023 it wasn't possible to verify proofs on TON. Due to complex computation behind the pairing algorithm, we needed TVM Opcodes for these operation which were added in [June 2023 update](https://docs.ton.org/learn/tvm-instructions/tvm-upgrade#bls12-381)(Only available on testnet for now). +:::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. ::: -## 🦄 What you will learn -1. You will learn about ZK and specifically zk-SNARK(Zero-knowledge Succinct Non-Interactive ARgument of Knowledge) -2. You will learn to do a trusted setup ceremony(Powers of Tau) -3. You will write and compile a simple ZK circuit(Circom language) -4. You will generate and deploy and test a FunC contract to verify a sample ZK proof - +## 🦄 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 -## 🟥🟦 Prove that you can see colors! -Before we dig into the details of ZK, let's start with a simple problem. Suppose you want to prove to a color-blind person that you can see colors. We can have an interactive solution for this problem. -Assume the color-blind person (the verifier) finds two identical pieces of paper one is 🟥 and one is 🟦. +## 🟥🟦 Explaining ZK-proofs with a color-focused example -The verifier shows one of the pieces of paper to you (the prover) and asks you to remember this color. Then the verifier will bring the paper behind himself and either change the paper or keep it the same and ask you to tell him if the color has changed or not. If you can tell the difference, then you can see colors(or you were just lucky, 50% chance of saying right answer). +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 🟦. -Now if the verifier do this 10 times, and you can tell the difference every time, then the verifier will be convinced ~99.90234% (1 - (1/2)^10) that you can see colors. -And if the verifier do this 30 times, then the verifier will be 99.99999990686774% (1 - (1/2)^30) convinced that you can see colors. +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). -But this is an interactive solution, and we can't have a DApp that ask user to send 30 transactions to prove some claim! So we need a non-interactive solution. And this is where Zk-SNARKs and STARKs come in. +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. -We will only cover Zk-SNARK in this tutorial, but you can read more about STARKs [here](https://starkware.co/stark/) and the comparison between Zk-SNARK and STARK [here](https://blog.pantherprotocol.io/zk-snarks-vs-zk-starks-differences-in-zero-knowledge-technologies/). +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. -### 🎯 Zk-SNARK: Zero-knowledge Succinct Non-Interactive ARgument of Knowledge +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 is a non-interactive proof system where the prover can prove to the verifier that a statement is true by just submitting one proof. And the verifier can verify the proof in a very short time. +### 🎯 Zk-SNARK: Zero-Knowledge Succinct Non-Interactive Argument of Knowledge -Zk-SNARK consists of three phases: -* Conducting trusted setup by [MPC](https://en.wikipedia.org/wiki/Secure_multi-party_computation) protocol to generate proving and verification keys (Powers of TAU) -* generating proof by prover using prover key, public input, and secret input (witness) -* and verifying the proof +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! -## ⚙ Setup development environment -Let's start by creating an empty [blueprint](https://github.com/ton-org/blueprint) project +## ⚙ Development environment setup -1. Create new project using blueprint and then enter a name for your contract (e.g. ZkSimple) and then choose 1st option (simple contract) +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. Now we need to clone the [snarkjs repo](https://github.com/kroist/snarkjs) that is adjusted to support FunC contracts + +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 @@ -58,14 +54,13 @@ npm ci cd ../simple-zk ``` -3. Install required libraries for ZkSNARK +3. Then we’ll install the required libraries needed for ZkSNARKs ```bash npm add --save-dev snarkjs ffjavascript npm i -g circom ``` - -4. Add this section to package.json(Some of the opcodes that we will use are not available in the mainnet release yet) +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", @@ -73,22 +68,20 @@ npm i -g circom } ``` -5. Also we need to change the version of `@ton-community/sandbox` to be able to use [latest TVM updates](https://t.me/thetontech/56) +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 in our project: -* `simple-zk` folder: contains our blueprint template, and it's where we will write our circuit and contracts and tests(always stay in this folder) +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 -Firstly let's create a file in `simple-zk/circuits` folder called `test.circom`, and add this code to it: +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; @@ -103,19 +96,19 @@ template Multiplier() { component main = Multiplier(); ``` -This is a simple multiplier circuit. Using this circuit we can prove that we know two numbers that when multiplied together, the result is a specific number(c). Without revealing the numbers(a and b) themselves. +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. -You can read more about circom language [here](https://docs.circom.io/). +To read more about the circom language consider having a look at [this website](https://docs.circom.io/). -Then let's make a folder for our build files and move there: +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 ``` -### 💪 Trusted setup (Powers of TAU) -It's time to perform a trusted setup. For this, we will use [Powers of Tau](https://a16zcrypto.com/posts/article/on-chain-trusted-setup-ceremony/) method -(it will probably take a few minutes to finish): +### 💪 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 @@ -131,38 +124,36 @@ echo 'Verify the final ptau' node ../../../snarkjs/build/cli.cjs powersoftau verify pot14_final.ptau ``` -This will create `pot14_final.ptau` file in the `build/circuits` folder which we can use for any circuits that we will write any future. - +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 you write a more complex circuit with more constraints you'll have to generate your PTAU setup with bigger parameter. +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 ``` -### 📜 Compile circuit +### 📜 Circuit compilation -Now let's compile the circuit(be sure to run this command from `build/circuits` folder) +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 `build/circuits/circuit.sym`, `build/circuits/circuit.r1cs` and `build/circuits/circuit.wasm` files. +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 -These are the curves that are currently supported by snarkjs. On Ethereum, the [altbn-128](https://eips.ethereum.org/EIPS/eip-197) curve is only supported, but on TON only bls12-381 curve is supported. +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: +Let's check the constraint size of our circuit by entering the following command: ```bash node ../../../snarkjs/build/cli.cjs r1cs info circuit.r1cs ``` -As a result, we should get: +Therefore, the correct result should be: ```bash [INFO] snarkJS: Curve: bls12-381 [INFO] snarkJS: # of Wires: 4 @@ -173,38 +164,38 @@ As a result, we should get: [INFO] snarkJS: # of Outputs: 1 ``` -Now we can generate the reference zkey +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 ``` -Let's add a contribution to the 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 ``` -Let's export the final zkey +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 in `build/circuits/circuit_final.zkey` file. We can verify it: +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 ``` -It's time to generate the verification key +Finally, it's time to generate the verification key: ```bash node ../../../snarkjs/build/cli.cjs zkey export verificationkey circuit_final.zkey verification_key.json ``` -You can remove the unnecessary files: +Then we’ll remove the unnecessary files: ```bash rm circuit_0000.zkey circuit_0001.zkey ``` -`build/circuits` folder should look like this: +After conducting the above processes, the `build/circuits` folder should be displayed as follows: ``` build └── circuits @@ -216,18 +207,18 @@ build └── verification_key.json ``` -### ✅ Export Verifier Contract -Final step in this section is to generate the FunC verifier contract which we will use in our project. + +### ✅ 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.fc` file will be generated in `contracts` folder. - - -## 🚢 Deploying Verifier Contract +## 🚢 Verifier contract deployment​ -Take a look at `contracts/verifier.fc` file. It contains the magic of ZK-SNARKs. Let's review it line by line. +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; @@ -239,7 +230,7 @@ const slice vk_alpha_1 = "a3fa7b5f78f70fbd1874ffc2104f55e658211db8a938445b4a07bd const slice vk_beta_2 = "b17e1924160eff0f027c872bc13ad3b60b2f5076585c8bce3e5ea86e3e46e9507f40c4600401bf5e88c7d6cceb05e8800712029d2eff22cbf071a5eadf166f266df75ad032648e8e421550f9e9b6c497b890a1609a349fbef9e61802fa7d9af5"s; ``` -These are the constants that verifier contract needs to use in proof verifying. These parameters can be found in `build/circuits/verification_key.json` file. +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"; @@ -250,9 +241,10 @@ slice bls_g1_multiexp( ) 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"; ``` -These lines are the new [TVM Opcodes](https://docs.ton.org/learn/tvm-instructions/tvm-upgrade-2023-07#bls12-381)(BLS12-381) that make the pairing check feasible on the TON blockchain. +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). -The `load_data` and `save_data` functions which is here just used to load and save the result of proof check(only for test purposes). ```func () load_data() impure { @@ -272,9 +264,7 @@ The `load_data` and `save_data` functions which is here just used to load and sa } ``` - -Then there are some simple util functions that is used to load the proof data sent to the contract. - +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 { ... @@ -289,7 +279,7 @@ Then there are some simple util functions that is used to load the proof data se } ``` -And the last part is the `groth16Verify` function which check the proof sent to the contract. +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, @@ -326,7 +316,7 @@ And the last part is the `groth16Verify` function which check the proof sent to } ``` -Now we need to edit the two files in `wrappers` folder. First is `ZkSimple.compile.ts` file(if you set another name in the step 1, this name is different). We need to put the `verifier.fc` file in the list of contracts to compile. +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'; @@ -337,14 +327,15 @@ export const compile: CompilerConfig = { }; ``` -And the other file is `ZkSimple.ts`. We need to first add the opcode of `verify` to the `Opcodes` enum: +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, }; ``` -And then we need to add the `sendVerify` function to the `ZkSimple` class. This function will be used to send the proof to the contract and test it. The function is like this: +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, @@ -384,7 +375,7 @@ async sendVerify( } ``` -We also need to add `cellFromInputList` function to the `ZkSimple` class. This function will be used to create a cell from the public inputs which will be sent to the contract. +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(); @@ -398,7 +389,7 @@ We also need to add `cellFromInputList` function to the `ZkSimple` class. This f } ``` -And the last function to add to the `ZkSimple` class is `getRes` function. This function will be used to get the result of the proof check. +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', []); @@ -406,14 +397,14 @@ And the last function to add to the `ZkSimple` class is `getRes` function. This } ``` -Now we can run the tests to deploy the contract. It should pass the deployment test(run this command in the root of `simple-zk` folder) +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 will be like this: + +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; @@ -440,23 +431,22 @@ describe('ZkSimple', () => { }); ``` -Firstly, we need to import some packages that we will use in 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, you will get a typescript error, because we don't have declaration file for module 'snarkjs' & ffjavascript. We can fix this by editing the -`tsconfig.json` file in the root of `simple-zk` folder. We need to change the _**strict**_ option to **_false_**. - -We will also need to import `circuit.wasm` and `circuit_final.zkey` files. We will use them to generate the proof to send to the contract. +* 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"); ``` -Lets fill the `should verify` test. We will need to generate the proof first. +Let's fill the `should verify` test. We'll need to generate the proof first. ```ts it('should verify', async () => { // proof generation @@ -478,7 +468,8 @@ it('should verify', async () => { }); ``` -We need to define `g1Compressed`, `g2Compressed`, and `toHexString` functions. They will be used to convert the proof to the format that the contract expects. +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); @@ -513,7 +504,7 @@ function toHexString(byteArray) { } ``` -Now we can send the proof to the contract. We will use the `sendVerify` function for this. The `sendVerify` function expects 5 parameters: `pi_a`, `pi_b`, `pi_c`, `pubInputs` and `value`. +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 @@ -543,12 +534,12 @@ it('should verify', async () => { }); ``` -Are you ready to verify your first proof on TON blockchain? let's run the test and see the result: +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 ``` -Result should be like this: +The result should be as follows: ```bash PASS tests/ZkSimple.spec.ts ZkSimple @@ -562,24 +553,31 @@ Time: 4.335 s, estimated 5 s Ran all test suites. ``` -You can check the repo that contains the code of this tutorial [here](https://github.com/SaberDoTcodeR/zk-ton-doc). +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 about ZK and specifically ZkSnark. -* Then you write your first Circom circuit and compiled it. -* You also performed MPC and a Powers of TAU ceremony Which you used to generate verification keys for your circuit. -* Then you used Snarkjs library to export a FunC verifier of your circuit. -* You used blueprint to deploy and write tests for your verifier. +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: -This was just a simple ZK use case and there are many more complex use-cases that you can be implemented using ZK. -* private voting system🗳 -* private lottery system🎰 -* private auction system🤝 -* private transactions💸(TON or JETTON) +* 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)