(res.config.zero_state_id.root_hash.as_slice().data());
+```
+
+可以从[配置文件](https://ton.org/global-config.json)中获取创世块信息(zero_state)。了解其复杂性和细节并非必要,但重要的是要记住`subwallet_id`的默认值为`698983191`。
+
+每个钱包合约都会检查外部交易的subwallet_id字段,以避免将请求发送到具有不同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);
+```
+
+我们需要将以上的值添加到合约的初始数据中,所以变量需要保存如下:
+
+
+
+
+```js
+const subWallet = 698983191;
+```
+
+
+
+
+```go
+var subWallet uint64 = 698983191
+```
+
+
+
+
+
+### 编译钱包代码
+
+既然我们已经明确定义了私钥、公钥和子钱包ID,我们需要编译钱包代码。为此,我们将使用官方库中的[钱包v3代码](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/wallet3-code.fc)。
+
+为了编译钱包代码,我们需要使用[@ton-community/func-js](https://github.com/ton-community/func-js)库。使用这个库,我们可以编译FunC代码并检索包含代码的cell。要开始使用,需要安装库并将其保存(--save)到`package.json`中,如下所示:
+
+```bash
+npm i --save @ton-community/func-js
+```
+
+我们将仅使用JavaScript来编译代码,因为用于编译代码的库基于JavaScript。
+但是,一旦编译完成,只要我们拥有编译后的cell的**base64输出**,就可以在其他编程语言(如Go等)中使用这些编译后的代码。
+
+首先,我们需要创建两个文件:`wallet_v3.fc`和`stdlib.fc`。编译器和stdlib.fc库一起使用。库中创建了所有必需的基本函数,这些函数对应于`asm`指令。可以从[这里](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/stdlib.fc)下载stdlib.fc文件。在`wallet_v3.fc`文件中,需要复制上面的代码。
+
+现在,我们为我们正在创建的项目有了以下结构:
+
+```
+.
+├── src/
+│ ├── main.ts
+│ ├── wallet_v3.fc
+│ └── stdlib.fc
+├── nodemon.json
+├── package-lock.json
+├── package.json
+└── tsconfig.json
+```
+
+:::info
+如果您的IDE插件与`stdlib.fc`文件中的`() set_seed(int) impure asm "SETRAND";`冲突,这没关系。
+:::
+
+请记住,在`wallet_v3.fc`文件的开头添加以下行,以指示将在下面使用stdlib中的函数:
+
+```func
+#include "stdlib.fc";
+```
+
+现在,让我们编写代码来编译我们的智能合约并使用`npm run start:dev`来运行它:
+
+```js
+import { compileFunc } from '@ton-community/func-js';
+import fs from 'fs'; // 我们使用fs来读取文件内容
+import { Cell } from '@ton/core';
+
+const result = await compileFunc({
+ targets: ['wallet_v3.fc'], // 您的项目的目标
+ 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]; // 从base64编码的BOC中获取缓冲区,并从该缓冲区获取cell
+
+// 现在我们获得了包含编译代码的base64编码的BOC
+console.log('Code BOC: ' + result.codeBoc);
+console.log('\nHash: ' + codeCell.hash().toString('base64')); // 获取cell的哈希并将其转换为base64编码的字符串。我们将会在后面需要它
+```
+
+终端的输出结果如下:
+
+```text
+Code BOC: te6ccgEBCAEAhgABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQCW8oMI1xgg0x/TH9MfAvgju/Jj7UTQ0x/TH9P/0VEyuvKhUUS68qIE+QFUEFX5EPKj+ACTINdKltMH1AL7AOgwAaTIyx/LH8v/ye1UAATQMAIBSAYHABe7Oc7UTQ0z8x1wv/gAEbjJftRNDXCx+A==
+
+Hash: idlku00WfSC36ujyK2JVT92sMBEpCNRUXOGO4sJVBPA=
+```
+
+完成后,可以使用其他库和语言使用我们的钱包代码检索相同的cell(使用base64编码的输出):
+
+
+
+
+```go
+import (
+ "encoding/base64"
+ "github.com/xssnick/tonutils-go/tvm/cell"
+)
+
+base64BOC := "te6ccgEBCAEAhgABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQCW8oMI1xgg0x/TH9MfAvgju/Jj7UTQ0x/TH9P/0VEyuvKhUUS68qIE+QFUEFX5EPKj+ACTINdKltMH1AL7AOgwAaTIyx/LH8v/ye1UAATQMAIBSAYHABe7Oc7UTQ0z8x1wv/gAEbjJftRNDXCx+A==" // 保存我们从编译器保存的base64编码输出到变量
+codeCellBytes, _ := base64.StdEncoding.DecodeString(base64BOC) // 解码base64以获取字节数组
+codeCell, err := cell.FromBOC(codeCellBytes) // 从字节数组获取包含代码的cell
+if err != nil { // 检查是否有任何错误
+ panic(err)
+}
+
+log.Println("Hash:", base64.StdEncoding.EncodeToString(codeCell.Hash())) // 获取cell的哈希,将其编码为base64,因为它具有[]byte类型,并输出到终端
+```
+
+
+
+
+
+
+将会在终端输出以下内容:
+
+```text
+idlku00WfSC36ujyK2JVT92sMBEpCNRUXOGO4sJVBPA=
+```
+
+完成上述过程后,确认我们的cell中正在使用正确的代码,因为哈希值相匹配。
+
+### 创建部署的初始化状态
+
+在构建交易之前,了解State Init非常重要。首先让我们了解[TL-B方案](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L141-L143):
+
+选项 | 说明
+:---: | :---:
+split_depth | 此选项适用于可以拆分并位于多个[分片链](/learn/overviews/ton-blockchain#many-accountchains-shards)上的高负载智能合约。有关此工作原理的更多详细信息,请参见[tblkch.pdf](https://ton.org/tblkch.pdf)(4.1.6)。只存储`0`,因为它仅在钱包智能合约内使用。
+special | 用于TicTok。这些智能合约会在每个区块自动调用,常规智能合约不需要。关于此的信息可以在[此章节中](/develop/data-formats/transaction-layout#tick-tock)或[tblkch.pdf](https://ton.org/tblkch.pdf) 中找到。此规范中仅存储`0`,因为我们不需要此功能。
+code | `1`位表示智能合约代码的存在。
+data | `1`位表示智能合约数据的存在。
+library | 操作[主链](/learn/overviews/ton-blockchain#masterchain-blockchain-of-blockchains)上的库,可以由不同的智能合约使用。对于钱包,不会使用它,因此设置为`0`。有关此的信息可以在[tblkch.pdf](https://ton.org/tblkch.pdf)(1.8.4)中找到。
+
+接下来我们将准备“初始数据”,这将在部署后立即出现在我们合约的存储中:
+
+
+
+
+```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()
+```
+
+
+
+
+在这个阶段,智能合约`代码`和`初始数据`都存在。有了这些数据,我们可以生成我们的**钱包地址**。钱包的地址取决于State Init,其中包括代码和初始数据。
+
+
+
+
+```js
+import { Address } from '@ton/core';
+
+const stateInit = beginCell().
+ storeBit(0). // 没有split_depth
+ storeBit(0). // 没有special
+ storeBit(1). // 表示有代码
+ storeRef(codeCell).
+ storeBit(1). // 表示有数据
+ storeRef(dataCell).
+ storeBit(0). // 没有library
+ endCell();
+
+const contractAddress = new Address(0, stateInit.hash()); // 获取stateInit的哈希,以获取我们的智能合约在`ID`为0的工作链中的地址
+console.log(`Contract address: ${contractAddress.toString()}`); // 将智能合约地址输出到控制台
+```
+
+
+
+
+```go
+import (
+ "github.com/xssnick/tonutils-go/address"
+)
+
+stateInit := cell.BeginCell().
+ MustStoreBoolBit(false). // 没有split_depth
+ MustStoreBoolBit(false). // 没有special
+ MustStoreBoolBit(true). // 表示有代码
+ MustStoreRef(codeCell).
+ MustStoreBoolBit(true). // 表示有数据
+ MustStoreRef(dataCell).
+ MustStoreBoolBit(false). // 没有library
+ EndCell()
+
+contractAddress := address.NewAddress(0, 0, stateInit.Hash()) // 获取stateInit的哈希,以获取我们的智能合约在`ID`为0的工作链中的地址
+log.Println("Contract address:", contractAddress.String()) // 将智能合约地址输出到控制台
+```
+
+
+
+
+使用State Init,我们现在可以构建交易并发送到区块链。要执行此过程,需要一个最低交易余额为0.1 TON(余额可以更低,但此金额足够)。要完成这个操作,我们需要运行教程中提到的代码,获取正确的钱包地址,并向该地址发送0.1 TON。
+
+让我们从构建类似于我们在**上一节**构建的交易开始:
+
+
+
+
+```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). // 不使用反弹
+ 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). // 保存1表示body是引用
+ storeRef(internalMessageBody).
+ endCell();
+
+// 用于我们的钱包的交易
+const toSign = beginCell().
+ storeUint(subWallet, 32).
+ storeUint(Math.floor(Date.now() / 1e3) + 60, 32).
+ storeUint(0, 32). // 我们将seqno设置为0,因为在部署之后,钱包将将0存储为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). // 没有反弹
+ 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). // 保存1表示body是引用
+ MustStoreRef(internalMessageBody).
+ EndCell()
+
+// 用于我们的钱包的交易
+toSign := cell.BeginCell().
+ MustStoreUInt(subWallet, 32).
+ MustStoreUInt(uint64(time.Now().UTC().Unix()+60), 32).
+ MustStoreUInt(0, 32). // 我们将seqno设置为0,因为在部署之后,钱包将将0存储为seqno
+ MustStoreUInt(3, 8).
+ MustStoreRef(internalMessage)
+
+signature := ed25519.Sign(privateKey, toSign.EndCell().Hash())
+body := cell.BeginCell().
+ MustStoreSlice(signature, 512).
+ MustStoreBuilder(toSign).
+ EndCell()
+```
+
+
+
+
+完成后,结果是正确的State Init和消息体。
+
+### 发送外部交易
+
+主要的区别将在外部消息的存在上,因为State Init被存储用于正确的合约部署。由于合约尚无自己的代码,因此无法处理任何内部消息。因此,接下来,我们将在成功部署后发送其代码和初始数据,以便可处理我们带有“Hello, TON!”评论的消息:
+
+
+
+
+```js
+const externalMessage = beginCell().
+ storeUint(0b10, 2). // 表示它是一笔外部传入的交易
+ storeUint(0, 2). // src -> addr_none
+ storeAddress(contractAddress).
+ storeCoins(0). // 导入费用
+ storeBit(1). // 我们有State Init
+ storeBit(1). // 我们将State Init存储为引用
+ storeRef(stateInit). // 将State Init存储为引用
+ storeBit(1). // 我们将消息体存储为引用
+ storeRef(body). // 将消息体存储为引用
+ endCell();
+```
+
+
+
+
+```go
+externalMessage := cell.BeginCell().
+ MustStoreUInt(0b10, 2). // 表示它是一笔外部传入的交易
+ MustStoreUInt(0, 2). // src -> addr_none
+ MustStoreAddr(contractAddress).
+ MustStoreCoins(0). // 导入费用
+ MustStoreBoolBit(true). // 我们有State Init
+ MustStoreBoolBit(true). // 我们将State Init存储为引用
+ MustStoreRef(stateInit). // 将State Init存储为引用
+ MustStoreBoolBit(true). // 我们将消息体存储为引用
+ MustStoreRef(body). // 将消息体存储为引用
+ EndCell()
+```
+
+
+
+
+最后,我们可以将我们的交易发送到区块链上部署我们的钱包并使用它。
+
+
+
+
+```js
+import { TonClient } from '@ton/ton';
+
+const client = new TonClient({
+ endpoint: "https://toncenter.com/api/v2/jsonRPC",
+ apiKey: "put your api key" // 你可以从Telegram中的@tonapibot获得API密钥
+});
+
+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
+}
+```
+
+
+
+
+请注意,我们使用mode`3`发送了一个内部消息。如果需要重复部署相同的钱包,**智能合约将被销毁**。为此,请正确设置的mode,通过添加128(取整个智能合约的余额)+ 32(销毁智能合约),以获取剩余的TON余额并再次部署钱包。
+
+重要说明:对于每个新的交易,**seqno需要增加1**。
+
+:::info
+我们使用的合约代码是[已验证的](https://tonscan.org/tx/BL9T1i5DjX1JRLUn4z9JOgOWRKWQ80pSNevis26hGvc=),因此您可以在此处查看一个[示例](https://tonscan.org/address/EQDBjzo_iQCZh3bZSxFnK9ue4hLTOKgsCNKfC8LOUM4SlSCX#source)。
+:::
+
+## 💸 使用钱包智能合约
+
+在完成本教程的前半部分后,我们现在对钱包智能合约以及它们的开发和使用有了更深入的了解。我们学习了如何部署和销毁它们,以及如何在不依赖预配置的库函数的情况下发送消息。为了更多地应用我们上面学到的知识,在下一部分中,我们将专注于构建和发送更复杂的消息。
+
+### 同时发送多条消息
+
+正如您可能已经知道的,[一个cell可以存储最多1023位的数据和最多4个指向其他cells的引用](/develop/data-formats/cell-boc#cell)。在本教程的第一部分中,我们详细介绍了内部消息是如何以“整体”循环作为链接发送的。这意味着可以**在外部消息内存储多达4条内部消息**。这允许同时发送四笔交易。
+
+为了实现这一点,需要创建4个不同的内部消息。我们可以手动创建,也可以通过`循环(loop)`来创建。我们需要定义3个数组:TON金额数组,评论数组,消息数组。对于消息,我们需要准备另一个数组 - 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",
+ "", // 我们让第三笔交易不留评论
+ "Hello, TON! #4"
+]
+const destinationAddresses = [
+ "输入属于你的任何地址",
+ "输入属于你的任何地址",
+ "输入属于你的任何地址",
+ "输入属于你的任何地址"
+] // 所有4个地址可以相同
+
+let internalMessages:Cell[] = []; // 存储我们内部消息的数组
+```
+
+
+
+
+```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",
+ "", // 我们让第三笔交易不留评论
+ "Hello, TON! #4",
+}
+destinationAddresses := [4]string{
+ "输入属于你的任何地址",
+ "输入属于你的任何地址",
+ "输入属于你的任何地址",
+ "输入属于你的任何地址",
+} // 所有4个地址可以相同
+
+var internalMessages [len(internalMessagesAmount)]*cell.Cell // 存储我们内部消息的数组
+```
+
+
+
+
+所有消息的[发送模式](/develop/smart-contracts/messages#message-modes)都设置为`mode 3`。但是,如果需要不同的模式,则可以创建一个数组来满足不同的目的。
+
+
+
+
+```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);
+
+ /*
+ 在这个阶段,并不清楚我们是否会有一个消息体。
+ 所以只设置stateInit的一位,如果我们有评论,那意味着
+ 我们有一个消息体。在这种情况下,将位设置为1并将
+ 体作为引用存储。
+ */
+
+ if(internalMessagesComment[index] != "") {
+ internalMessage.storeBit(1) // 我们将消息体作为引用存储
+
+ let internalMessageBody = beginCell().
+ storeUint(0, 32).
+ storeStringTail(internalMessagesComment[index]).
+ endCell();
+
+ internalMessage.storeRef(internalMessageBody);
+ }
+ else
+ /*
+ 由于我们没有消息体,我们表明这个消息
+ 中有消息体,但不写入,意味着它不存在。
+ 在这种情况下,只需设置位为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)
+
+ /*
+ 在这个阶段,并不清楚我们是否会有一个消息体。
+ 所以只设置stateInit的一位,如果我们有评论,那意味着
+ 我们有一个消息体。在这种情况下,将位设置为1并将
+ 体作为引用存储。
+ */
+
+ if internalMessagesComment[i] != "" {
+ internalMessage.MustStoreBoolBit(true) // 我们将消息体作为引用存储
+
+ internalMessageBody := cell.BeginCell().
+ MustStoreUInt(0, 32).
+ MustStoreStringSnake(internalMessagesComment[i]).
+ EndCell()
+
+ internalMessage.MustStoreRef(internalMessageBody)
+ } else {
+ /*
+ 由于我们没有消息体,我们表明这个消息
+ 中有消息体,但不写入,意味着它不存在。
+ 在这种情况下,只需设置位为0。
+ */
+ internalMessage.MustStoreBoolBit(false)
+ }
+ internalMessages[i] = internalMessage.EndCell()
+}
+```
+
+
+
+
+现在让我们利用[第二章](/develop/smart-contracts/tutorials/wallet#-deploying-our-wallet)的知识,为我们的钱包构建一个可以同时发送4笔交易的交易:
+
+
+
+
+```js
+import { TonClient } from '@ton/ton';
+import { mnemonicToWalletKey } from '@ton/crypto';
+
+const walletAddress = Address.parse('输入你的钱包地址');
+const client = new TonClient({
+ endpoint: "https://toncenter.com/api/v2/jsonRPC",
+ apiKey: "输入你的api密钥" // 你可以从Telegram中的@tonapibot机器人获取api密钥
+});
+
+const mnemonic = '输入你的助记词'; // word1 word2 word3
+let getMethodResult = await client.runMethod(walletAddress, "seqno"); // 从你的钱包合约运行"seqno"GET方法
+let seqno = getMethodResult.stack.readNumber(); // 从响应中获取seqno
+
+const mnemonicArray = mnemonic.split(' '); // 从字符串获取数组
+const keyPair = await mnemonicToWalletKey(mnemonicArray); // 从助记词获取密钥对
+
+let toSign = beginCell().
+ storeUint(698983191, 32). // subwallet_id
+ storeUint(Math.floor(Date.now() / 1e3) + 60, 32). // 交易过期时间,+60 = 1分钟
+ storeUint(seqno, 32); // 存储seqno
+ // 别忘了,如果我们使用Wallet V4,我们需要添加 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("输入你的钱包地址")
+
+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("输入你的助记词", " ") // word1 word2 word3
+// 以下三行代码将使用助记词提取私钥。
+// 我们不会深入讲解密码学细节。在tonutils-go库中,这一切都已经实现,
+// 但它立即返回带有地址和现成方法的钱包对象。
+// 所以我们必须单独编写获取密钥的代码行。Goland IDE会自动导入
+// 所需的库(crypto, pbkdf2等)。
+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) // 在TON库中使用"TON default seed"作为提取密钥时的salt
+// 32是密钥长度
+privateKey := ed25519.NewKeyFromSeed(k) // 获取私钥
+
+block, err := client.CurrentMasterchainInfo(context.Background()) // 获取当前区块,我们在向LiteServer请求时会用到它
+if err != nil {
+ log.Fatalln("CurrentMasterchainInfo err:", err.Error())
+ return
+}
+
+getMethodResult, err := client.RunGetMethod(context.Background(), block, walletAddress, "seqno") // 从你的钱包合约运行"seqno"GET方法
+if err != nil {
+ log.Fatalln("RunGetMethod err:", err.Error())
+ return
+}
+seqno := getMethodResult.MustInt(0) // 从响应中获取seqno
+
+toSign := cell.BeginCell().
+ MustStoreUInt(698983191, 32). // subwallet_id | 我们之后考虑这个
+ MustStoreUInt(uint64(time.Now().UTC().Unix()+60), 32). // 交易过期时间,+60 = 1分钟
+ MustStoreUInt(seqno.Uint64(), 32) // 存储seqno
+ // 别忘了,如果我们使用Wallet V4,我们需要添加 MustStoreUInt(0, 8).
+```
+
+
+
+
+接下来,我们将在循环中添加之前构建的消息:
+
+
+
+
+```js
+for (let index = 0; index < internalMessages.length; index++) {
+ const internalMessage = internalMessages[index];
+ toSign.storeUint(3, 8) // 存储我们内部交易的mode
+ toSign.storeRef(internalMessage) // 将我们的内部消息作为引用存储
+}
+```
+
+
+
+
+```go
+for i := 0; i < len(internalMessages); i++ {
+ internalMessage := internalMessages[i]
+ toSign.MustStoreUInt(3, 8) // 存储我们内部交易的mode
+ toSign.MustStoreRef(internalMessage) // 将我们的内部消息作为引用存储
+}
+```
+
+
+
+
+完成上述过程后,让我们**签名**我们的消息,**构建一个外部消息**(如本教程前几节所述)并**将其发送**到区块链:
+
+
+
+
+```js
+import { sign } from '@ton/crypto';
+
+let signature = sign(toSign.endCell().hash(), keyPair.secretKey); // 获取我们钱包智能合约的消息的哈希并签名以获得签名
+
+let body = beginCell().
+ storeBuffer(signature). // 存储签名
+ storeBuilder(toSign). // 存储我们的消息
+ endCell();
+
+let externalMessage = beginCell().
+ storeUint(0b10, 2). // ext_in_msg_info$10
+ storeUint(0, 2). // src -> addr_none
+ storeAddress(walletAddress). // 目的地址
+ storeCoins(0). // 引入费
+ storeBit(0). // 无State Init
+ storeBit(1). // 我们将消息体作为引用存储
+ storeRef(body). // 将消息体作为引用存储
+ endCell();
+
+client.sendFile(externalMessage.toBoc());
+```
+
+
+
+
+```go
+import (
+ "github.com/xssnick/tonutils-go/tl"
+)
+
+signature := ed25519.Sign(privateKey, toSign.EndCell().Hash()) // 获取我们钱包智能合约的消息的哈希并签名以获得签名
+
+body := cell.BeginCell().
+ MustStoreSlice(signature, 512). // 存储签名
+ MustStoreBuilder(toSign). // 存储我们的消息
+ EndCell()
+
+externalMessage := cell.BeginCell().
+ MustStoreUInt(0b10, 2). // ext_in_msg_info$10
+ MustStoreUInt(0, 2). // src -> addr_none
+ MustStoreAddr(walletAddress). // 目的地址
+ MustStoreCoins(0). // 引入费
+ MustStoreBoolBit(false). // 无State Init
+ MustStoreBoolBit(true). // 我们将消息体作为引用存储
+ MustStoreRef(body). // 将消息体作为引用存储
+ 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 连接错误
+如果出现与轻服务器(lite-server)连接相关的错误(Golang),必须重复运行代码,直到能够发送交易。这是因为tonutils-go库通过代码中指定的全局配置使用了几个不同的轻服务器,但并非所有轻服务器都能接受我们的连接。
+:::
+
+完成此过程后,可以使用TON区块链浏览器来验证钱包是否已向之前指定的地址发送了四笔交易。
+
+### NFT 转移
+
+除了常规交易之外,用户经常彼此发送 NFT。不幸的是,并非所有库都包含为这种智能合约量身定制的方法。因此,我们需要创建代码,使我们能够构建发送 NFT 的交易。首先,让我们更熟悉 TON NFT [标准](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md)。
+
+特别是,我们需要详细了解用于 [NFT 转移](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md#1-transfer) 的 TL-B。
+
+- `query_id`:查询 ID 在交易处理方面没有价值。NFT 合约不验证它;它只是读取它。当服务希望为其每个交易分配特定的查询 ID 以供识别之用时,此值可能会有用。因此,我们将其设置为 0。
+
+- `response_destination`:处理所有权变更交易后会有额外的 TON。它们将发送到此地址,如果指定了的话,否则保留在 NFT 余额中。
+
+- `custom_payload`:custom_payload 需要用来执行特定任务,并且不与普通 NFT 一起使用。
+
+- `forward_amount`:如果 forward_amount 不为零,指定的 TON 数量将发送给新所有者。这样,新所有者将被通知他们收到了某物。
+
+- `forward_payload`:forward_payload 是可以与 forward_amount 一起发送给新所有者的附加数据。例如,使用 forward_payload 允许用户在转移 NFT 时[添加评论](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md#forward_payload-format),如本教程前面所示。然而,尽管 TON 的 NFT 标准中写有 forward_payload,区块链浏览器并不完全支持显示各种细节。显示 Jettons 时也存在同样的问题。
+
+现在让我们构建交易本身:
+
+
+
+
+```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");
+
+// 我们可以添加评论,但由于目前尚未得到支持,因此不会在浏览器中显示。
+const forwardPayload = beginCell().
+ storeUint(0, 32).
+ storeStringTail("Hello, TON!").
+ endCell();
+
+const transferNftBody = beginCell().
+ storeUint(0x5fcc3d14, 32). // NFT 转移的操作码
+ storeUint(0, 64). // query_id
+ storeAddress(destinationAddress). // new_owner
+ storeAddress(walletAddress). // response_destination 的超额部分
+ storeBit(0). // 我们没有 custom_payload
+ storeCoins(toNano("0.01")). // forward_amount
+ storeBit(1). // 我们以引用的形式存储 forward_payload
+ storeRef(forwardPayload). // 以引用的形式存储 forward_payload
+ endCell();
+
+const internalMessage = beginCell().
+ storeUint(0x18, 6). // 弹回
+ storeAddress(nftAddress).
+ storeCoins(toNano("0.05")).
+ storeUint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1). // 我们存储 1 表示我们有body作为引用
+ 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")
+
+// 我们可以添加评论,但因为目前不支持,所以不会显示在浏览器中。
+forwardPayload := cell.BeginCell().
+ MustStoreUInt(0, 32).
+ MustStoreStringSnake("Hello, TON!").
+ EndCell()
+
+transferNftBody := cell.BeginCell().
+ MustStoreUInt(0x5fcc3d14, 32). // NFT 转移的操作码
+ MustStoreUInt(0, 64). // query_id
+ MustStoreAddr(destinationAddress). // new_owner
+ MustStoreAddr(walletAddress). // response_destination 的超额部分
+ MustStoreBoolBit(false). // 我们没有 custom_payload
+ MustStoreBigCoins(tlb.MustFromTON("0.01").NanoTON()). // forward_amount
+ MustStoreBoolBit(true). // 我们以引用的形式存储 forward_payload
+ MustStoreRef(forwardPayload). // 以引用的形式存储 forward_payload
+ EndCell()
+
+internalMessage := cell.BeginCell().
+ MustStoreUInt(0x18, 6). // 弹回
+ MustStoreAddr(nftAddress).
+ MustStoreBigCoins(tlb.MustFromTON("0.05").NanoTON()).
+ MustStoreUInt(1, 1 + 4 + 4 + 64 + 32 + 1 + 1). // 我们存储 1 表示我们有body作为引用
+ MustStoreRef(transferNftBody).
+ EndCell()
+```
+
+
+
+
+NFT 转移操作码来自[相同的标准](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md#tl-b-schema)。
+现在让我们完成交易,按本教程前面部分的布局。完成交易所需的正确代码可在 [GitHub 库](/develop/smart-contracts/tutorials/wallet#source-code)中找到。
+
+使用 Jettons 也可以完成相同的程序。要进行此过程,请阅读有关 jettons 转移的 TL-B [标准](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md)。特别是,NFT 和 Jettons 转移之间存在一些小差异。
+
+### Wallet v3 和 Wallet v4 的 Get 方法
+
+智能合约经常使用 [GET 方法](/develop/smart-contracts/guidelines/get-methods),但它们不在区块链内部运行,而是在客户端上运行。GET 方法有许多用途,为智能合约提供对不同数据类型的访问。例如,NFT 智能合约中的 [get_nft_data() 方法](https://github.com/ton-blockchain/token-contract/blob/991bdb4925653c51b0b53ab212c53143f71f5476/nft/nft-item.fc#L142-L145) 允许用户检索特定的内容、所有者和 NFT 集合信息。
+
+下面我们将了解 V3 和 V4 钱包使用的 GET 方法的基础知识:
+
+方法 | 说明
+:---: | :---:
+int seqno() | 该方法需要用来接收当前的 seqno 并发送带有正确值的交易。在本教程的前几节中,该方法被频繁调用。
+int get_public_key() | 该方法用于检索公钥。get_public_key() 并不广泛使用,可以被不同的服务使用。例如,一些 API 服务允许检索具有相同公钥的多个钱包
+
+现在,我们转向只有 V4 钱包使用的方法:
+
+方法 | 说明
+:---: | :---:
+int get_subwallet_id() | 教程前面已经考虑过这个。此方法允许您检索 subwallet_id。
+int is_plugin_installed(int wc, int addr_hash) | 让我们知道插件是否已安装。调用此方法时,需要传递 [工作链](/learn/overviews/ton-blockchain#workchain-blockchain-with-your-own-rules) 和插件地址哈希。
+tuple get_plugin_list() | 此方法返回已安装插件的地址。
+
+让我们考虑 `get_public_key` 和 `is_plugin_installed` 方法。选择这两种方法是因为,首先我们需要从 256 位数据中获取公钥,然后我们需要学习如何向 GET 方法传递切片和不同类型的数据。这对于我们正确使用这些方法非常有用。
+
+首先,我们需要一个能够发送请求的客户端。因此,我们将使用特定的钱包地址([EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF](https://tonscan.org/address/EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF))作为例子:
+
+
+
+
+```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" // 你可以从 Telegram 中的 @tonapibot 机器人获取 api 密钥
+});
+
+const walletAddress = Address.parse("EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF"); // 以我的钱包地址为例
+```
+
+
+
+
+```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()) // 获取当前区块, 我们将需要它用于向 LiteServer 发送请求
+if err != nil {
+ log.Fatalln("CurrentMasterchainInfo err:", err.Error())
+ return
+}
+
+walletAddress := address.MustParseAddr("EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF") // 以我的钱包地址为例
+```
+
+
+
+
+现在我们需要调用钱包的 GET 方法。
+
+
+
+
+```js
+// 我总是调用 runMethodWithError 而不是 runMethod,以便能够检查被调用方法的 exit_code。
+let getResult = await client.runMethodWithError(walletAddress, "get_public_key"); // 运行 get_public_key GET 方法
+const publicKeyUInt = getResult.stack.readBigNumber(); // 读取包含 uint256 的回答
+const publicKey = publicKeyUInt.toString(16); // 从 bigint(uint256)获取十六进制字符串
+console.log(publicKey)
+```
+
+
+
+
+```go
+getResult, err := client.RunGetMethod(context.Background(), block, walletAddress, "get_public_key") // 运行 get_public_key GET 方法
+if err != nil {
+ log.Fatalln("RunGetMethod err:", err.Error())
+ return
+}
+
+// 我们有一个包含值的数组作为回应,并且在读取它时应该指定索引
+// 在 get_public_key 的情况下,我们只有一个返回值,存储在 0 索引处
+publicKeyUInt := getResult.MustInt(0) // 读取包含 uint256 的回答
+publicKey := publicKeyUInt.Text(16) // 从 bigint(uint256)获取十六进制字符串
+log.Println(publicKey)
+```
+
+
+
+
+调用成功完成后,最终结果是一个极大的 256 位数,必须转换为十六进制字符串。对于我们提供的钱包地址,结果十六进制字符串如下:`430db39b13cf3cb76bfa818b6b13417b82be2c6c389170fbe06795c71996b1f8`。
+接下来,我们使用 [TonAPI](https://tonapi.io/swagger-ui)(/v1/wallet/findByPubkey 方法),通过输入获得的十六进制字符串到系统中,立即就可以清楚,答复内数组的第一个元素将识别我的钱包。
+
+然后我们转向 `is_plugin_installed` 方法。作为例子,我们将再次使用之前使用的钱包([EQAM7M--HGyfxlErAIUODrxBA3yj5roBeYiTuy6BHgJ3Sx8k](https://tonscan.org/address/EQAM7M--HGyfxlErAIUODrxBA3yj5roBeYiTuy6BHgJ3Sx8k))和插件([EQBTKTis-SWYdupy99ozeOvnEBu8LRrQP_N9qwOTSAy3sQSZ](https://tonscan.org/address/EQBTKTis-SWYdupy99ozeOvnEBu8LRrQP_N9qwOTSAy3sQSZ)):
+
+
+
+
+```js
+const oldWalletAddress = Address.parse("EQAM7M--HGyfxlErAIUODrxBA3yj5roBeYiTuy6BHgJ3Sx8k"); // 我的旧钱包地址
+const subscriptionAddress = Address.parseFriendly("EQBTKTis-SWYdupy99ozeOvnEBu8LRrQP_N9qwOTSAy3sQSZ"); // 已经安装在钱包上的订阅插件地址
+```
+
+
+
+
+```go
+oldWalletAddress := address.MustParseAddr("EQAM7M--HGyfxlErAIUODrxBA3yj5roBeYiTuy6BHgJ3Sx8k")
+subscriptionAddress := address.MustParseAddr("EQBTKTis-SWYdupy99ozeOvnEBu8LRrQP_N9qwOTSAy3sQSZ") // 已经安装在钱包上的订阅插件地址
+```
+
+
+
+
+现在我们需要检索插件的哈希地址,以便地址可以转换成数字并发送给 GET 方法。
+
+
+
+
+```js
+const hash = BigInt(`0x${subscriptionAddress.address.hash.toString("hex")}`) ;
+
+getResult = await client.runMethodWithError(oldWalletAddress, "is_plugin_installed",
+[
+ {type: "int", value: BigInt("0")}, // 作为 int 传递 workchain
+ {type: "int", value: hash} // 作为 int 传递插件地址哈希
+]);
+console.log(getResult.stack.readNumber()); // -1
+```
+
+
+
+
+```go
+import (
+ "math/big"
+)
+
+hash := big.NewInt(0).SetBytes(subscriptionAddress.Data())
+// runGetMethod 会自动识别传递值的类型
+getResult, err = client.RunGetMethod(context.Background(), block, oldWalletAddress,
+ "is_plugin_installed",
+ 0, // 传递工作链
+ hash) // 传递插件地址
+if err != nil {
+ log.Fatalln("RunGetMethod err:", err.Error())
+ return
+}
+
+log.Println(getResult.MustInt(0)) // -1
+```
+
+
+
+
+响应必须是 `-1`,意味着结果是真的。如果需要的话,也可以发送切片和cell。创建切片或cell并将其传递替代 BigInt 就足够了,指定相应的类型。
+
+### 通过钱包部署合约
+
+在第三章中,我们部署了一个钱包。为此,我们最初发送了一些TON,然后从钱包发送了一笔交易以部署一个智能合约。然而,这个过程并不常用于外部交易,通常主要用于钱包。在开发合约时,部署过程是通过发送内部消息来初始化的。
+
+为了完成这个过程,我们将使用在[第三章](/develop/smart-contracts/tutorials/wallet#compiling-our-wallet-code)中使用的V3R2钱包智能合约。在这种情况下,我们将`subwallet_id`设置为`3`或者使用相同的私钥检索另一个地址时需要的任何其他数字(它是可变的):
+
+
+
+
+```js
+import { beginCell, Cell } from '@ton/core';
+import { mnemonicToWalletKey } from '@ton/crypto';
+
+const mnemonicArray = 'put your mnemonic'.split(" ");
+const keyPair = await mnemonicToWalletKey(mnemonicArray); // 从助记词提取私钥和公钥
+
+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). // 子钱包ID
+ storeBuffer(keyPair.publicKey). // 公钥
+ endCell();
+
+const stateInit = beginCell().
+ storeBit(0). // 没有 split_depth
+ storeBit(0). // 没有特殊
+ storeBit(1). // 我们有代码
+ storeRef(codeCell).
+ storeBit(1). // 我们有数据
+ storeRef(dataCell).
+ storeBit(0). // 没有库
+ 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", " ")
+// 下面的三行将使用助记词短语提取私钥。
+// 我们不会深入讨论加密细节。在tonutils-go库中,这些都已实现,
+// 但它直接返回的是带有地址和准备好的方法的完成的钱包对象。
+// 因此,我们必须单独编写代码行来获取密钥。Goland IDE将自动导入
+// 所需的所有库(crypto, pbkdf2等)。
+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) // 在TON库中,使用"TON default seed"作为获取密钥时的salt
+// 32 是密钥长度
+privateKey := ed25519.NewKeyFromSeed(k) // 获取私钥
+publicKey := privateKey.Public().(ed25519.PublicKey) // 从私钥获取公钥
+
+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). // 子钱包ID
+ MustStoreSlice(publicKey, 256). // 公钥
+ EndCell()
+
+stateInit := cell.BeginCell().
+ MustStoreBoolBit(false). // 没有 split_depth
+ MustStoreBoolBit(false). // 没有特殊
+ MustStoreBoolBit(true). // 我们有代码
+ MustStoreRef(codeCell).
+ MustStoreBoolBit(true). // 我们有数据
+ MustStoreRef(dataCell).
+ MustStoreBoolBit(false). // 没有库
+ EndCell()
+```
+
+
+
+
+接下来我们将从我们的合约中获取地址并构建内部消息。同时,我们将向我们的交易中添加"Deploying..."评论。
+
+
+
+
+```js
+import { Address, toNano } from '@ton/core';
+
+const contractAddress = new Address(0, stateInit.hash()); // 获取stateInit的哈希来获取我们的智能合约在工作链ID为0的地址
+console.log(`合约地址: ${contractAddress.toString()}`); // 输出合约地址到控制台
+
+const internalMessageBody = beginCell().
+ storeUint(0, 32).
+ storeStringTail('Deploying...').
+ endCell();
+
+const internalMessage = beginCell().
+ storeUint(0x10, 6). // 无弹回
+ storeAddress(contractAddress).
+ storeCoins(toNano('0.01')).
+ storeUint(0, 1 + 4 + 4 + 64 + 32).
+ storeBit(1). // 我们有State Init
+ storeBit(1). // 我们将State Init作为引用存储
+ storeRef(stateInit). // 将State Init作为引用存储
+ storeBit(1). // 我们将消息体作为引用存储
+ storeRef(internalMessageBody). // 将消息体Init作为引用存储
+ endCell();
+```
+
+
+
+
+```go
+import (
+ "github.com/xssnick/tonutils-go/address"
+ "github.com/xssnick/tonutils-go/tlb"
+ "log"
+)
+
+contractAddress := address.NewAddress(0, 0, stateInit.Hash()) // 获取stateInit的哈希来获取我们的智能合约在工作链ID为0的地址
+log.Println("合约地址:", contractAddress.String()) // 输出合约地址到控制台
+
+internalMessageBody := cell.BeginCell().
+ MustStoreUInt(0, 32).
+ MustStoreStringSnake("Deploying...").
+ EndCell()
+
+internalMessage := cell.BeginCell().
+ MustStoreUInt(0x10, 6). // 不反弹
+ MustStoreAddr(contractAddress).
+ MustStoreBigCoins(tlb.MustFromTON("0.01").NanoTON()).
+ MustStoreUInt(0, 1+4+4+64+32).
+ MustStoreBoolBit(true). // 我们有State Init
+ MustStoreBoolBit(true). // 我们将State Init作为引用存储
+ MustStoreRef(stateInit). // 将State Init作为引用存储
+ MustStoreBoolBit(true). // 我们将消息体作为引用存储
+ MustStoreRef(internalMessageBody). // 将消息体Init作为引用存储
+ EndCell()
+```
+
+
+
+
+:::info
+请注意,上述中已指定位,并且stateInit和internalMessageBody已作为引用保存。由于链接是分开存储的,我们可以写4(0b100)+ 2(0b10)+ 1(0b1)->(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)即(0b111, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1),然后保存两个引用。
+:::
+
+接下来,我们将为我们的钱包准备一条消息并发送它:
+
+
+
+
+```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' // 您可以从Telegram中的@tonapibot bot获取api key
+});
+
+const walletMnemonicArray = 'put your mnemonic'.split(' ');
+const walletKeyPair = await mnemonicToWalletKey(walletMnemonicArray); // 从助记词提取私钥和公钥
+const walletAddress = Address.parse('用来部署的你的钱包地址');
+const getMethodResult = await client.runMethod(walletAddress, 'seqno'); // 从你的钱包合约运行"seqno" GET方法
+const seqno = getMethodResult.stack.readNumber(); // 从回应中获取seqno
+
+// 我们钱包的交易
+const toSign = beginCell().
+ storeUint(698983191, 32). // 子钱包id
+ storeUint(Math.floor(Date.now() / 1e3) + 60, 32). // 交易过期时间, +60 = 1 分钟
+ storeUint(seqno, 32). // 存储seqno
+ // 不要忘记如果我们使用钱包V4,我们需要添加storeUint(0, 8).
+ storeUint(3, 8).
+ storeRef(internalMessage);
+
+const signature = sign(toSign.endCell().hash(), walletKeyPair.secretKey); // 获取我们发往钱包智能合约的消息hash并签名以获取签名
+const body = beginCell().
+ storeBuffer(signature). // 存储签名
+ storeBuilder(toSign). // 存储我们的消息
+ endCell();
+
+const external = beginCell().
+ storeUint(0b10, 2). // 表示这是一个传入的外部交易
+ storeUint(0, 2). // src -> addr_none
+ storeAddress(walletAddress).
+ storeCoins(0). // 导入费
+ storeBit(0). // 我们没有State Init
+ storeBit(1). // 我们将消息体作为引用存储
+ storeRef(body). // 将消息体作为引用存储
+ 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()) // 获取当前区块,我们在请求LiteServer时需要它
+if err != nil {
+ log.Fatalln("CurrentMasterchainInfo 错误:", 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) // 在TON库中,使用"TON default seed"作为获取密钥时的salt
+// 32 是密钥长度
+walletPrivateKey := ed25519.NewKeyFromSeed(k) // 获取私钥
+walletAddress := address.MustParseAddr("用来部署的你的钱包地址")
+
+getMethodResult, err := client.RunGetMethod(context.Background(), block, walletAddress, "seqno") // 从你的钱包合约运行"seqno" GET方法
+if err != nil {
+ log.Fatalln("RunGetMethod 错误:", err.Error())
+ return
+}
+seqno := getMethodResult.MustInt(0) // 从回应中获取seqno
+
+toSign := cell.BeginCell().
+ MustStoreUInt(698983191, 32). // 子钱包id | 我们稍后考虑这个
+ MustStoreUInt(uint64(time.Now().UTC().Unix()+60), 32). // 交易过期时间, +60 = 1 分钟
+ MustStoreUInt(seqno.Uint64(), 32). // 存储seqno
+ // 不要忘记如果我们使用钱包V4,我们需要添加MustStoreUInt(0, 8).
+ MustStoreUInt(3, 8). // 存储我们内部交易的模式
+ MustStoreRef(internalMessage) // 将我们的内部消息作为引用存储
+
+signature := ed25519.Sign(walletPrivateKey, toSign.EndCell().Hash()) // 获取我们发往钱包智能合约的消息hash并签名以获取签名
+
+body := cell.BeginCell().
+ MustStoreSlice(signature, 512). // 存储签名
+ MustStoreBuilder(toSign). // 存储我们的消息
+ EndCell()
+
+externalMessage := cell.BeginCell().
+ MustStoreUInt(0b10, 2). // ext_in_msg_info$10
+ MustStoreUInt(0, 2). // src -> addr_none
+ MustStoreAddr(walletAddress). // 目的地址
+ MustStoreCoins(0). // 导入费
+ MustStoreBoolBit(false). // 没有State Init
+ MustStoreBoolBit(true). // 我们将消息体作为引用存储
+ MustStoreRef(body). // 将消息体作为引用存储
+ 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
+}
+```
+
+
+
+
+这就结束了我们和普通钱包的工作。在这个阶段,您应该对如何与钱包智能合约互动,发送交易,以及能够使用各种库类型有一个深入的了解。
+
+## 🔥 高负载钱包
+
+在某些情况下,可能需要一次发送大量的交易。如前所述,普通钱包支持一次发送最多4笔交易,这是通过在单个cell中存储[最多4个引用](/develop/data-formats/cell-boc#cell)来支持的。高负载钱包则允许一次发送255笔交易。这个限制的存在是因为区块链的配置设置中对外部消息(动作)的最大数量设定为255。
+
+交易所可能是使用高负载钱包的最佳示例。像币安这样的大型交易所有着极大的用户基础,这意味着在短时间内会处理大量的交易提款请求。高负载钱包有助于处理这些提款请求。
+
+### 高负载钱包 FunC 代码
+
+首先,让我们查看[高负载钱包智能合约的代码结构](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); ;; 从消息体中获取签名
+ var cs = in_msg;
+ var (subwallet_id, query_id) = (cs~load_uint(32), cs~load_uint(64)); ;; 从消息体中获取其余值
+ var bound = (now() << 32); ;; 位左移操作
+ throw_if(35, query_id < bound); ;; 如果交易已过期则抛出错误
+ 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()); ;; 从存储中读取值
+ ds.end_parse(); ;; 确保 ds 中没有任何东西
+ (_, var found?) = old_queries.udict_get?(64, query_id); ;; 检查是否已经存在此类请求
+ throw_if(32, found?); ;; 如果是则抛出错误
+ throw_unless(34, subwallet_id == stored_subwallet);
+ throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key));
+ var dict = cs~load_dict(); ;; 获取包含消息的字典
+ cs.end_parse(); ;; 确保 cs 中没有任何东西
+ accept_message();
+```
+
+> 💡 有用的链接:
+>
+> [位运算](/develop/func/stdlib/#dict_get)
+>
+> [load_dict()](/develop/func/stdlib/#load_dict)
+>
+> [udict_get?()](/develop/func/stdlib/#dict_get)
+
+您会发现与普通钱包有些不同。现在让我们更详细地看看高负载钱包在TON上的工作原理(除了子钱包,因为我们之前已经讨论过了)。
+
+### 使用查询 ID 代替 Seqno
+
+如我们之前讨论的,普通钱包在每次交易后 seqno 增加 `1`。在使用钱包序列时,我们必须等待这个值更新,然后使用 GET 方法检索它并发送新的交易。
+这个过程需要很长时间,高负载钱包不是为此设计的(如上所述,它们旨在快速发送大量交易)。因此,TON上的高负载钱包使用了 `query_id`。
+
+如果相同的交易请求已经存在,合约将不会接受它,因为它已经被处理过了:
+
+```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()); ;; 从存储中读取值
+ds.end_parse(); ;; 确保 ds 中没有任何东西
+(_, var found?) = old_queries.udict_get?(64, query_id); ;; 检查是否已经存在此类请求
+throw_if(32, found?); ;; 如果是则抛出错误
+```
+
+通过这种方式,我们**被保护免受重复交易的影响**,这是普通钱包中 seqno 的作用。
+
+### 发送交易
+
+合约接受外部消息后,将开始循环,在循环中取出存储在字典中的 `slices`。这些切片存储了交易模式和交易本身。发送新交易一直进行,直到字典为空。
+
+```func
+int i = -1; ;; 我们写 -1 是因为它将是所有字典键中的最小值
+do {
+ (i, var cs, var f) = dict.idict_get_next?(16, i); ;; 获取键及其对应的最小键值,这个键值大于 i
+ if (f) { ;; 检查是否找到了任何值
+ var mode = cs~load_uint(8); ;; 加载交易模式
+ send_raw_message(cs~load_ref(), mode); ;; 加载交易本身并发送
+ }
+} until (~ f); ;; 如果找到任何值则继续
+```
+
+> 💡 有用的链接:
+>
+> [idict_get_next()](/develop/func/stdlib/#dict_get_next)
+
+请注意,如果找到一个值,`f` 永远等于 -1(真)。`~ -1` 操作(位非)将始终返回 0 的值,意味着应该继续循环。与此同时,当字典填充了交易时,需要开始计算那些**大于 -1** 的值(例如,0),并且每次交易都将值递增 1。这个结构允许以正确的顺序发送交易。
+
+### 移除过期查询
+
+通常情况下,[TON上的智能合约需要为自己的存储付费](/develop/smart-contracts/fees#storage-fee)。这意味着智能合约可以存储的数据量是有限的,以防止高网络交易费用。为了让系统更高效,超过 64 秒的交易将从存储中移除。按照以下方式进行:
+
+```func
+bound -= (64 << 32); ;; 清除记录,这些记录超过 64 秒前已过期
+old_queries~udict_set_builder(64, query_id, begin_cell()); ;; 将当前查询添加到字典中
+var queries = old_queries; ;; 将字典复制到另一个变量中
+do {
+ var (old_queries', i, _, f) = old_queries.udict_delete_get_min(64);
+ f~touch();
+ if (f) { ;; 检查是否找到了任何值
+ f = (i < bound); ;; 检查是否超过 64 秒后过期
+ }
+ if (f) {
+ old_queries = old_queries'; ;; 如果是,则在我们的字典中保存更改
+ last_cleaned = i; ;; 保存最后移除的查询
+ }
+} until (~ f);
+```
+
+> 💡 有用的链接:
+>
+> [udict_delete_get_min()](/develop/func/stdlib/#dict_delete_get_min)
+
+请注意,必须多次与 `f` 变量进行交互。由于 [TVM 是一个堆栈机器](/learn/tvm-instructions/tvm-overview#tvm-is-a-stack-machine),在每次与 `f` 变量交互时,必须弹出所有值以获得所需的变量。`f~touch()` 操作将 f 变量放在堆栈顶部,以优化代码执行。
+
+### 位运算
+
+如果您之前没有使用过位运算,那么这个部分可能会显得有些复杂。在智能合约代码中可以看到以下代码行:
+
+```func
+var bound = (now() << 32); ;; 位左移操作
+```
+结果,在右侧的数字上添加了 32 位。这意味着 **现有值向左移动 32 位**。举例来说,让我们考虑数字 3 并将其翻译成二进制形式,结果是 11。应用 `3 << 2` 操作,11 移动了 2 位。这意味着在字符串的右侧添加了两位。最后,我们得到了 1100,即 12。
+
+关于这个过程要理解的第一件事是记住 `now()` 函数返回 uint32 的结果,意味着结果值将是 32 位。通过向左移动 32 位,为另一个 uint32 打开了空间,结果是正确的 query_id。这样,**时间戳和 query_id 可以在一个变量中组合**以进行优化。
+
+接下来,让我们考虑以下代码行:
+
+```func
+bound -= (64 << 32); ;; 清除超过 64 秒之前过期的记录
+```
+
+在上面,我们执行了一个操作,将数字 64 向左移动 32 位,以**减去 64 秒**的时间戳。这样我们就可以比较过去的 query_ids,看看它们是否小于接收到的值。如果是这样,它们就超过了 64 秒:
+
+```func
+if (f) { ;; 检查是否找到了任何值
+ f = (i < bound); ;; 检查是否超过 64 秒后过期
+}
+```
+为了更好地理解,让我们使用 `1625918400` 作为时间戳的示例。它的二进制表示(左侧添加零以得到 32 位)是 01100000111010011000101111000000。执行 32 位位左移操作后,我们数字的二进制表示末尾会出现 32 个零。
+
+完成后,**可以添加任何 query_id (uint32)**。然后减去 `64 << 32` 的结果是 64 秒前有相同 query_id 的时间戳。可以通过执行以下计算来验证这一点 `((1625918400 << 32) - (64 << 32)) >> 32`。这样我们可以比较我们数字的必要部分(时间戳),同时 query_id 不会干扰。
+
+### 存储更新
+
+所有操作完成后,剩下的唯一任务就是将新的值保存在存储中:
+
+```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 方法
+
+在我们深入了解钱包部署和交易创建之前,我们必须考虑的最后一件事是高负载钱包的 GET 方法:
+
+方法 | 说明
+:---: | :---:
+int processed?(int query_id) | 通知用户特定请求是否已处理。这意味着如果请求已经处理,则返回 `-1`;如果尚未处理,则返回 `0`。此外,如果答案未知,因为请求较旧,且不再存储在合约中,此方法可能返回 `1`。
+int get_public_key() | 检索公钥。我们之前已经讨论过这个方法。
+
+让我们仔细看看 `int processed?(int query_id)` 方法,以帮助我们了解为什么我们需要使用 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);
+}
+```
+`last_cleaned` 从合约的存储和旧查询字典中检索。如果找到了查询,它应返回 true;如果没有,则表达式 `- (query_id <= last_cleaned)`。last_cleaned 包含最后一个被删除的、**时间戳最高**的请求,因为我们开始时从最小时间戳删除请求。
+
+这意味着,如果传递给方法的 query_id 小于 last_cleaned 值,就无法确定它是否曾在合约中。因此 `query_id <= last_cleaned` 返回 -1,而表达式前面的减号将答案改为 1。如果 query_id 大于 last_cleaned 方法,则表示它尚未被处理。
+
+### 部署高负载钱包
+
+为了部署高负载钱包,必须提前生成一个助记词密钥,用户将使用此密钥。可以使用在本教程之前部分中使用的相同密钥。
+
+要开始部署高负载钱包的过程,必须将[智能合约的代码](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/highload-wallet-v2-code.fc)复制到 stdlib.fc 和 wallet_v3 所在的同一目录中,并记得在代码开头添加`#include "stdlib.fc";`。接下来,我们将像在[第三节](/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'], // 你项目的目标
+ 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];
+
+// 现在我们有了编译后代码的 base64 编码 BOC 在 result.codeBoc 中
+console.log('代码 BOC: ' + result.codeBoc);
+console.log('\n哈希值: ' + codeCell.hash().toString('base64')); // 获取cell的哈希值并转换为 base64 编码字符串
+
+```
+
+
+
+
+在终端中的输出将如下所示:
+
+```text
+Code BOC: te6ccgEBCQEA5QABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQHq8oMI1xgg0x/TP/gjqh9TILnyY+1E0NMf0z/T//QE0VNggED0Dm+hMfJgUXO68qIH+QFUEIf5EPKjAvQE0fgAf44WIYAQ9HhvpSCYAtMH1DAB+wCRMuIBs+ZbgyWhyEA0gED0Q4rmMQHIyx8Tyz/L//QAye1UCAAE0DACASAGBwAXvZznaiaGmvmOuF/8AEG+X5dqJoaY+Y6Z/p/5j6AmipEEAgegc30JjJLb/JXdHxQANCCAQPSWb6VsEiCUMFMDud4gkzM2AZJsIeKz
+
+Hash: lJTRzI7fEvBWcaGpugmSEJbrUIEeGSTsZcPGKfu4CBI=
+```
+
+在上述结果的基础上,我们可以使用base64编码的输出,在其他库和语言中检索包含我们钱包代码的cell,具体操作如下:
+
+
+
+
+```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" // 将编译器输出的base64编码保存到变量中
+codeCellBytes, _ := base64.StdEncoding.DecodeString(base64BOC) // 解码base64以获取字节数组
+codeCell, err := cell.FromBOC(codeCellBytes) // 从字节数组中获取包含代码的cell
+if err != nil { // 检查是否有任何错误
+ panic(err)
+}
+
+log.Println("Hash:", base64.StdEncoding.EncodeToString(codeCell.Hash())) // 获取我们cell的哈希值,因为它的类型是[]byte,所以编码为base64并输出到终端
+```
+
+
+
+
+现在我们需要检索由其初始数据组成的cell,构建一个State Init,并计算一个高负载钱包地址。经过研究智能合约代码后,我们发现subwallet_id、last_cleaned、public_key和old_queries是顺序存储在存储中的:
+
+
+
+
+```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); // 从助记词中提取私钥和公钥
+
+const dataCell = beginCell().
+ storeUint(698983191, 32). // 子钱包ID
+ storeUint(0, 64). // 上次清理时间
+ storeBuffer(highloadKeyPair.publicKey). // 公钥
+ storeBit(0). // 表示字典为空
+ endCell();
+
+const stateInit = beginCell().
+ storeBit(0). // 无split_depth
+ storeBit(0). // 无special
+ storeBit(1). // 我们有代码
+ storeRef(codeCell).
+ storeBit(1). // 我们有数据
+ storeRef(dataCell).
+ storeBit(0). // 无库
+ endCell();
+
+const contractAddress = new Address(0, stateInit.hash()); // 获取stateInit的哈希值以获得我们智能合约在工作链ID为0的地址
+console.log(`Contract address: ${contractAddress.toString()}`); // 输出合约地址到控制台
+```
+
+
+
+
+```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", " ") // 单词1 单词2 单词3
+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) // 在TON库中,获取钥匙时使用的salt是"TON default seed"
+// 钥匙长度为32
+highloadPrivateKey := ed25519.NewKeyFromSeed(k) // 获取私钥
+highloadPublicKey := highloadPrivateKey.Public().(ed25519.PublicKey) // 从私钥获取公钥
+
+dataCell := cell.BeginCell().
+ MustStoreUInt(698983191, 32). // 子钱包ID
+ MustStoreUInt(0, 64). // 上次清理时间
+ MustStoreSlice(highloadPublicKey, 256). // 公钥
+ MustStoreBoolBit(false). // 表示字典为空
+ EndCell()
+
+stateInit := cell.BeginCell().
+ MustStoreBoolBit(false). // 无split_depth
+ MustStoreBoolBit(false). // 无special
+ MustStoreBoolBit(true). // 我们有代码
+ MustStoreRef(codeCell).
+ MustStoreBoolBit(true). // 我们有数据
+ MustStoreRef(dataCell).
+ MustStoreBoolBit(false). // 无库
+ EndCell()
+
+contractAddress := address.NewAddress(0, 0, stateInit.Hash()) // 获取stateInit的哈希值以获得我们智能合约在工作链ID为0的地址
+log.Println("Contract address:", contractAddress.String()) // 输出合约地址到控制台
+```
+
+
+
+
+以上我们详细描述的步骤与[通过钱包部署合约](/develop/smart-contracts/tutorials/wallet#contract-deployment-via-wallet)部分中的步骤一致。为了更好地分析完整功能的代码,请访问教程开始处提到的库,其中存储了所有源代码。
+
+### 发送高负载钱包交易
+
+现在,让我们编程高负载钱包同时发送多条消息。例如,让我们每条消息发送12笔交易,这样gas费用就很小。
+
+:::info 高负载余额
+要完成交易,合约的余额必须至少为0.5 TON。
+:::
+
+每条消息携带其自己的含代码的评论,目的地址将是我们部署的钱包:
+
+
+
+
+```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). // 弹回
+ storeAddress(walletAddress).
+ storeCoins(toNano('0.01')).
+ storeUint(0, 1 + 4 + 4 + 64 + 32).
+ storeBit(0). // 我们没有State Init
+ storeBit(1). // 我们将消息体存储为引用
+ storeRef(internalMessageBody). // 将消息体Init存储为引用
+ 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). // 弹回
+ MustStoreAddr(wallletAddress).
+ MustStoreBigCoins(tlb.MustFromTON("0.001").NanoTON()).
+ MustStoreUInt(0, 1+4+4+64+32).
+ MustStoreBoolBit(false). // 我们没有State Init
+ MustStoreBoolBit(true). // 我们将消息体存储为引用
+ MustStoreRef(internalMessageBody). // 将消息体Init存储为引用
+ EndCell()
+
+ messageData := cell.BeginCell().
+ MustStoreUInt(3, 8). // 交易mode
+ MustStoreRef(internalMessage).
+ EndCell()
+
+ internalMessages = append(internalMessages, messageData)
+}
+```
+
+
+
+
+完成上述过程后,结果是一系列内部消息。接下来,需要创建一个消息存储的字典来准备并签名消息体。如下所示:
+
+
+
+
+```js
+import { Dictionary } from '@ton/core';
+import { mnemonicToWalletKey, sign } from '@ton/crypto';
+import * as crypto from 'crypto';
+
+const dictionary = Dictionary.empty(); // 创建一个键为数字值为cell的空字典
+for (let i = 0; i < internalMessages.length; i++) {
+ const internalMessage = internalMessages[i]; // 从数组中获取我们的消息
+ dictionary.set(i, internalMessage); // 在字典中保存该消息
+}
+
+const queryID = crypto.randomBytes(4).readUint32BE(); // 创建一个随机的uint32数字,4字节 = 32位
+const now = Math.floor(Date.now() / 1000); // 获取当前时间戳
+const timeout = 120; // 消息失效的超时时间,120秒 = 2分钟
+const finalQueryID = (BigInt(now + timeout) << 32n) + BigInt(queryID); // 获取我们最终的query_id
+console.log(finalQueryID); // 打印query_id。使用这个query_id我们可以调用GET方法来检查我们的请求是否已被处理
+
+const toSign = beginCell().
+ storeUint(698983191, 32). // subwallet_id
+ storeUint(finalQueryID, 64).
+ // 在这里我们创建自己的方法来保存
+ // 交易mode和对交易的引用
+ storeDict(dictionary, Dictionary.Keys.Int(16), {
+ serialize: (src, buidler) => {
+ buidler.storeUint(3, 8); // 保存交易mode,mode = 3
+ buidler.storeRef(src); // 以引用形式保存交易
+ },
+ // 实际上我们不会使用这个,但这个方法
+ // 将帮助读取我们保存的字典
+ 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); // 从助记词中提取私钥和公钥
+const highloadWalletAddress = Address.parse('put your high-load wallet address');
+
+const signature = sign(toSign.endCell().hash(), highloadKeyPair.secretKey); // 获取我们向智能合约钱包发送的消息哈希并签名以获取签名
+```
+
+
+
+
+```go
+import (
+ "crypto/ed25519"
+ "crypto/hmac"
+ "crypto/sha512"
+ "golang.org/x/crypto/pbkdf2"
+ "log"
+ "math/big"
+ "math/rand"
+ "strings"
+ "time"
+)
+
+dictionary := cell.NewDict(16) // 创建一个空字典,键为数字,值为cell
+for i := 0; i < len(internalMessages); i++ {
+ internalMessage := internalMessages[i] // 从数组中获取消息
+ err := dictionary.SetIntKey(big.NewInt(int64(i)), internalMessage) // 在字典中保存消息
+ if err != nil {
+ return
+ }
+}
+
+queryID := rand.Uint32()
+timeout := 120 // 消息过期的超时时间,120秒 = 2分钟
+now := time.Now().Add(time.Duration(timeout)*time.Second).UTC().Unix() << 32 // 获取当前时间戳 + 超时时间
+finalQueryID := uint64(now) + uint64(queryID) // 获取最终的query_id
+log.Println(finalQueryID) // 打印query_id。使用此query_id我们可以调用GET方法检查请求是否已处理
+
+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) // 在TON库中,“TON default seed”被用作获取密钥时的salt
+// 32是密钥长度
+highloadPrivateKey := ed25519.NewKeyFromSeed(k) // 获取私钥
+highloadWalletAddress := address.MustParseAddr("put your high-load wallet address")
+
+signature := ed25519.Sign(highloadPrivateKey, toSign.EndCell().Hash())
+```
+
+
+
+
+:::note 重要
+请注意,在使用JavaScript和TypeScript时,我们的消息被保存在数组中而没有使用发送模式。这是因为,在使用@ton/ton库时,预期开发者将自行实现序列化和反序列化的过程。因此,会传递一个首先保存交易模式然后保存交易本身的方法。如果我们使用`Dictionary.Values.Cell()`规范作为值方法,它会将整个消息作为cell引用保存,而不是单独保存模式。
+:::
+
+接下来我们将创建一个外部消息并使用以下代码发送到区块链:
+
+
+
+
+```js
+import { TonClient } from '@ton/ton';
+
+const body = beginCell().
+ storeBuffer(signature). // 保存签名
+ storeBuilder(toSign). // 保存我们的消息
+ endCell();
+
+const externalMessage = beginCell().
+ storeUint(0b10, 2). // 表明这是一个传入的外部交易
+ storeUint(0, 2). // src -> addr_none
+ storeAddress(highloadWalletAddress).
+ storeCoins(0). // 导入费用
+ storeBit(0). // 我们没有State Init
+ storeBit(1). // 我们以引用形式存储消息体
+ storeRef(body). // 以引用形式存储消息体
+ endCell();
+
+// 我们在这里不需要键,因为我们将以每秒1个请求的速度发送
+const client = new TonClient({
+ endpoint: 'https://toncenter.com/api/v2/jsonRPC',
+ // apiKey: 'put your api key' // 你可以从Telegram中的@tonapibot bot获得一个api密钥
+});
+
+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). // 存储签名
+ MustStoreBuilder(toSign). // 存储我们的消息
+ EndCell()
+
+externalMessage := cell.BeginCell().
+ MustStoreUInt(0b10, 2). // ext_in_msg_info$10
+ MustStoreUInt(0, 2). // src -> addr_none
+ MustStoreAddr(highloadWalletAddress). // 目标地址
+ MustStoreCoins(0). // 导入费用
+ MustStoreBoolBit(false). // 无State Init
+ MustStoreBoolBit(true). // 我们以引用形式存储消息体
+ MustStoreRef(body). // 以引用形式存储消息体
+ 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
+}
+```
+
+
+
+
+完成此过程后,我们可以查看我们的钱包并验证我们的钱包上发送了12个传出交易。使用我们最初在控制台中使用的query_id,也可以调用`processed?` GET方法。如果此请求已正确处理,它会提供`-1`(真)的结果。
+
+## 🏁 结论
+
+这个教程让我们更好地理解了TON区块链上不同钱包类型的运作方式。它还让我们学会了如何创建外部和内部消息,而不使用预定义的库方法。
+
+这有助于我们独立于使用库,并以更深入的方式理解TON区块链的结构。我们还学习了如何使用高负载钱包,并分析了许多与不同数据类型和各种操作相关的细节。
+
+## 🧩 下一步
+
+阅读上述文档是一项复杂的任务,人们难以完全理解TON平台的全部内容。然而,这对于那些热衷于在TON上建设的人来说是一个很好的练习。另一个建议是开始学习如何在TON上编写智能合约,可以参考以下资源:[FunC概览](https://docs.ton.org/develop/func/overview),[最佳实践](https://docs.ton.org/develop/smart-contracts/guidelines),[智能合约示例](https://docs.ton.org/develop/smart-contracts/examples),[FunC开发手册](https://docs.ton.org/develop/func/cookbook)
+
+此外,建议读者更详细地熟悉以下文档:[ton.pdf](https://docs.ton.org/ton.pdf) 和 [tblkch.pdf](https://ton.org/tblkch.pdf) 文档。
+
+## 📬 关于作者
+
+如果您有任何问题、评论或建议,请通过 [Telegram](https://t.me/aspite) (@aSpite 或 @SpiteMoriarty) 或 [GitHub](https://github.com/aSpite) 联系本文档部分的作者。
+
+## 📖 参阅
+
+- 钱包的源代码:[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),[高负载](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/highload-wallet-v2-code.fc)
+
+- 有用的概念文件(可能包含过时信息):[ton.pdf](https://docs.ton.org/ton.pdf),[tblkch.pdf](https://ton.org/tblkch.pdf),[tvm.pdf](https://ton.org/tvm.pdf)
+
+代码的主要来源:
+
+ - [@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).
+
+官方文档:
+
+ - [内部消息](/develop/smart-contracts/guidelines/internal-messages)
+
+ - [外部消息](/develop/smart-contracts/guidelines/external-messages)
+
+ - [钱包合约类型](/participate/wallets/contracts#wallet-v4)
+
+ - [TL-B](/develop/data-formats/tl-b-language)
+
+ - [区块链网络](https://docs.ton.org/learn/overviews/ton-blockchain)
+
+外部参考:
+
+- [Ton Deep](https://github.com/xssnick/ton-deep-doc)
+
+- [Block.tlb](https://github.com/ton-blockchain/ton/blob/master/crypto/block/block.tlb)
+
+- [TON中的标准](https://github.com/ton-blockchain/TEPs)
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/smart-contracts/security/overview.mdx b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/smart-contracts/security/overview.mdx
new file mode 100644
index 0000000000..9944305ab8
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/smart-contracts/security/overview.mdx
@@ -0,0 +1,26 @@
+import Button from '@site/src/components/button'
+
+# 概览
+
+:::info
+本文需要更新。请帮助我们改进它。
+:::
+
+**本页包含了可以帮助您确保智能合约安全的建议。**
+
+如果您正在创建智能合约,那么在这里您可以看到一些示例,这些错误可能导致您丢失资金:
+
+- [TON Hack挑战赛#1](https://github.com/ton-blockchain/hack-challenge-1)
+ - [从TON Hack挑战赛中得出的结论](/develop/smart-contracts/security/ton-hack-challenge-1)
+
+## TON 课程:安全性
+
+[TON区块链课程](https://stepik.org/course/201638/)是对TON区块链开发的全面指导。
+
+第8模块完全涵盖了TON区块链上智能合约的安全性。
+
+
+
+查看TON区块链课程
+
+
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/smart-contracts/security/random-number-generation.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/smart-contracts/security/random-number-generation.md
new file mode 100644
index 0000000000..c1c5e6074b
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/smart-contracts/security/random-number-generation.md
@@ -0,0 +1,106 @@
+# 随机数生成
+
+生成随机数是许多不同项目中常见的任务。你可能已经在FunC文档中看到过`random()`函数,但请注意,除非你采用一些额外的技巧,否则其结果很容易被预测。
+
+## 如何预测随机数?
+
+计算机在生成随机信息方面非常糟糕,因为它们只是遵循用户的指令。然而,由于人们经常需要随机数,他们设计了各种方法来生成_伪随机_数。
+
+这些算法通常要求你提供一个_seed_值,该值将被用来生成一系列_伪随机_数。因此,如果你多次运行相同的程序并使用相同的_seed_,你将始终得到相同的结果。在TON中,每个区块的_seed_是不同的。
+
+- [区块随机seed的生成](/develop/smart-contracts/security/random)
+
+因此,要预测智能合约中`random()`函数的结果,你只需要知道当前区块的`seed`,如果你不是验证者,这是不可能的。
+
+## 只需使用`randomize_lt()`
+
+为了使随机数生成不可预测,你可以将当前的[逻辑时间](/develop/smart-contracts/guidelines/message-delivery-guarantees#what-is-a-logical-time)添加到seed中,这样不同的交易将具有不同的seed和结果。
+
+只需在生成随机数之前调用`randomize_lt()`,你的随机数就会变得不可预测:
+
+```func
+randomize_lt();
+int x = random(); ;; users can't predict this number
+```
+
+然而,你应该注意验证者或协作者仍然可能影响随机数的结果,因为他们决定了当前区块的seed。
+
+## 有没有办法防止验证者操纵?
+
+为了防止(或至少复杂化)验证者替换seed,你可以使用更复杂的方案。例如,你可以在生成随机数之前跳过一个区块。如果我们跳过一个区块,seed将以不太可预测的方式改变。
+
+跳过区块并不是一个复杂的任务。你可以通过简单地将消息发送到主链,然后再发送回你合约的工作链来完成。让我们来看一个简单的例子!
+
+:::caution
+不要在真实项目中使用此示例合约,请自己编写。
+:::
+
+### 任何工作链中的主合约
+
+让我们以一个简单的彩票合约为例。用户将向它发送1 TON,有50%的机会会得到2 TON回报。
+
+```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
+ );
+ }
+ }
+}
+```
+
+在你需要的任何工作链(可能是基本链)部署这个合约,就完成了!
+
+## 这种方法100%安全吗?
+
+虽然它确实有所帮助,但如果入侵者同时控制了几个验证者,仍然有可能被操纵。在这种情况下,他们可能会以某种概率[影响](/develop/smart-contracts/security/random#conclusion)依赖的_seed_。即使这种可能性极小,仍然值得考虑。
+
+随着最新的TVM升级,向`c7`寄存器中引入新值可以进一步提高随机数生成的安全性。具体来说,升级在`c7`寄存器中添加了关于最近16个主链区块的信息。
+
+由于主链区块信息的不断变化性质,它可以作为随机数生成的额外熵源。通过将这些数据纳入你的随机算法中,你可以创建出更难以被潜在对手预测的数字。
+
+有关此TVM升级的更多详细信息,请参考[TVM升级](/learn/tvm-instructions/tvm-upgrade-2023-07)。
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/smart-contracts/security/random.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/smart-contracts/security/random.md
new file mode 100644
index 0000000000..cb6007c725
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/smart-contracts/security/random.md
@@ -0,0 +1,76 @@
+# 区块随机 seed 的生成
+
+:::caution
+此信息在撰写时是最新的。它可能会在任何网络升级时发生变化。
+:::
+
+偶尔,在TON上会创建一个彩票合约。通常它使用不安全的方法来处理随机性,因此生成的值可以被用户预测,彩票可能被耗尽。
+
+但是,利用随机数生成中的弱点通常涉及使用代理合约,如果随机值正确,代理合约会转发消息。存在有关钱包合约的提案,这些合约将能够执行链上任意代码(当然由用户指定和签名),但大多数流行的钱包版本不支持这样做。那么,如果彩票检查赌徒是否通过钱包合约参与,它是否安全?
+
+或者,这个问题可以这样表述。外部消息能否被包含在随机值正好符合发送者需求的区块中?
+
+当然,发送者无法以任何方式影响随机性。但是生成区块并包含提议的外部消息的验证者可以。
+
+## 验证者如何影响seed
+
+即使在白皮书中,关于这一点的信息也不多,所以大多数开发者都感到困惑。这是关于区块随机的唯一提及,在 [TON白皮书](https://docs.ton.org/ton.pdf) 中:
+
+> 为每个分片(w, s)选择验证者任务组的算法是确定性伪随机的。**它使用验证者嵌入到每个主链区块中的伪随机数(通过使用阈值签名达成共识)来创建随机seed**,然后为每个验证者计算例如Hash(code(w). code(s).validator_id.rand_seed)。
+
+然而,唯一被保证真实且最新的是代码。所以让我们看看 [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();
+ }
+```
+
+这是生成区块随机seed的代码。它位于协作者代码中,因为它由生成区块的一方需要(并且对轻量级验证者不是必需的)。
+
+所以,我们可以看到,seed是由单个验证者或协作者与区块一起生成的。下一个问题是:
+
+## 在知道seed后是否可以决定包含外部消息?
+
+是的,可以。证据如下:如果外部消息被导入,它的执行必须成功。执行可以依赖于随机值,所以保证seed事先已知。
+
+因此,如果发送者可以与验证者合作,那么确实**存在**一种方法来攻击"不安全"(让我们称之为单区块,因为它不使用发送消息后的任何区块信息)随机。即使使用了`randomize_lt()`。验证者可以生成适合发送者的seed,或者将提议的外部消息包含在将满足所有条件的区块中。这样做的验证者仍然被认为是公平的。这就是去中心化的本质。
+
+为了让这篇文章完全覆盖随机性,这里还有一个问题。
+
+## 区块seed如何影响合约中的随机数?
+
+验证者生成的seed并不直接用于所有合约。相反,它是[与账户地址一起哈希](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;
+}
+```
+
+然后,伪随机数是使用 [TVM指令](/learn/tvm-instructions/instructions#112-pseudo-random-number-generator-primitives) 页面上描述的过程生成的:
+
+> **x\{F810} RANDU256**\
+> 生成一个新的伪随机无符号256位整数x。算法如下:如果r是随机seed的旧值,被视为一个32字节的数组(通过构造无符号256位整数的大端表示),那么计算它的sha512(r);这个哈希的前32字节被存储为随机seed的新值r',剩余的32字节作为下一个随机值x返回。
+
+我们可以通过查看 [准备c7合约](https://github.com/ton-blockchain/ton/blob/master/crypto/block/transaction.cpp#L903) 的代码(c7是存储临时数据的元组,存储合约地址、起始余额、随机seed等)和 [随机值本身的生成](https://github.com/ton-blockchain/ton/blob/master/crypto/vm/tonops.cpp#L217-L268) 来确认这一点。
+
+## 结论
+
+TON中没有随机是完全安全的,就不可预测性而言。这意味着**这里不可能存在完美的彩票**,也不可能相信任何彩票是公平的。
+
+PRNG的典型用途可能包括`randomize_lt()`,但是可以通过选择正确的区块向它发送消息来欺骗这样的合约。提出的解决方案是向其他工作链发送消息,接收回答,从而跳过区块等...但这只是推迟了威胁。事实上,任何验证者(即TON区块链的1/250)都可以在正确的时间选择发送请求给彩票合约,以便来自其他工作链的回复在他生成的区块中到达,然后他可以选择他希望的任何区块seed。一旦协作者出现在主网,危险将会增加,因为他们永远不会因为标准投诉而被罚款,因为他们不会向Elector合约注入任何资金。
+
+
+
+
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/smart-contracts/security/ton-hack-challenge-1.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/smart-contracts/security/ton-hack-challenge-1.md
new file mode 100644
index 0000000000..ff5003e37e
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/smart-contracts/security/ton-hack-challenge-1.md
@@ -0,0 +1,157 @@
+# 从 TON Hack 挑战赛中得出结论
+
+TON Hack挑战赛于10月23日举行。在TON主网上部署了几个带有人为安全漏洞的智能合约。每个合约都有3000或5000 TON的余额,允许参与者攻击它并立即获得奖励。
+
+源代码和比赛规则托管在Github [这里](https://github.com/ton-blockchain/hack-challenge-1)。
+
+## 合约
+
+### 1. 互助基金
+
+:::note 安全规则
+始终检查函数是否有[`impure`](/develop/func/functions#impure-specifier)修饰符。
+:::
+
+第一个任务非常简单。攻击者可以发现`authorize`函数没有`impure`。这个修饰符的缺失允许编译器在函数不返回任何内容或返回值未使用时跳过对该函数的调用。
+
+```func
+() authorize (sender) inline {
+ throw_unless(187, equal_slice_bits(sender, addr1) | equal_slice_bits(sender, addr2));
+}
+```
+
+### 2. 银行
+
+:::note 安全规则
+始终检查[修改/非修改](/develop/func/statements#methods-calls)方法。
+:::
+
+使用`.`而不是`~`调用了`udict_delete_get?`,所以真正的 dict 没有被触及。
+
+```func
+(_, slice old_balance_slice, int found?) = accounts.udict_delete_get?(256, sender);
+```
+
+### 3. DAO
+
+:::note 安全规则
+如果你真的需要,使用符号整数。
+:::
+
+投票权在消息中以整数形式存储。所以攻击者可以在转移投票权时发送一个负值,并获得无限投票权。
+
+```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. 彩票
+
+:::note 安全规则
+在执行[`rand()`](/develop/func/stdlib#rand)之前,始终随机化seed。
+:::
+
+seed来自交易的逻辑时间,黑客可以通过暴力破解当前区块中的逻辑时间来赢得比赛(因为lt在一个区块的边界内是连续的)。
+
+```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. 钱包
+
+:::note 安全规则
+记住区块链上存储的一切。
+:::
+
+钱包受密码保护,其哈希存储在合约数据中。然而,区块链记住一切——密码在交易历史中。
+
+### 6. 资金库
+
+:::note 安全规则
+始终检查[bounced](/develop/smart-contracts/guidelines/non-bouncable-messages)消息。
+不要忘记由[标准](/develop/func/stdlib/)函数引起的错误。
+尽可能使条件严格。
+:::
+
+资金库在数据库消息处理程序中有以下代码:
+
+```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
+}
+```
+
+如果用户发送“支票”,资金库没有弹回处理程序或代理消息到数据库。在数据库中,我们可以设置`msg_addr_none`作为奖励地址,因为`load_msg_address`允许它。我们向资金库请求支票,数据库尝试解析`msg_addr_none`使用[`parse_std_addr`](/develop/func/stdlib#parse_std_addr),并失败。消息从数据库弹回到金库,并且op不是`op_not_winner`。
+
+### 7. 更好的银行
+
+:::note 安全规则
+永远不要为了好玩而销毁账户。做[`raw_reserve`](/develop/func/stdlib#raw_reserve)而不是把钱发给自己。考虑可能的竞争条件。小心哈希映射的gas费用消耗。
+:::
+
+合约中存在竞争条件:你可以存入钱,然后尝试在并发消息中两次提取它。无法保证保留有资金的消息会被处理,所以银行在第二次提款后可能会关闭。之后,合约可以被重新部署,任何人都可以提取未领取的资金。
+
+### 8. 驱逐者
+
+:::note 安全规则
+避免在合约中执行第三方代码。
+:::
+
+```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;
+}
+```
+
+在合约中安全执行第三方代码是不可能的,因为[`out of gas`](/learn/tvm-instructions/tvm-exit-codes#standard-exit-codes)异常不能被`CATCH`处理。攻击者可以简单地[`COMMIT`](/learn/tvm-instructions/instructions#11-application-specific-primitives)合约的任何状态,并引发`out of gas`。
+
+## 结论
+
+希望这篇文章能对FunC开发者揭示一些不明显的规则。
+
+## 参考资料
+
+原文作者 Dan Volkov
+
+- [dvlkv on Github](https://github.com/dvlkv)
+- [原文链接](https://dev.to/dvlkv/drawing-conclusions-from-ton-hack-challenge-1aep)
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/smart-contracts/testing/overview.mdx b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/smart-contracts/testing/overview.mdx
new file mode 100644
index 0000000000..d65d01a29e
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/smart-contracts/testing/overview.mdx
@@ -0,0 +1,141 @@
+# 使用 Blueprint 编写测试
+
+## 概览
+
+测试工具包(通常是沙盒)已经包含在名为[Blueprint](/develop/smart-contracts/sdk/javascript)的TypeScript SDK中。您可以创建一个演示项目并通过两个步骤启动默认测试:
+
+1. 创建一个新的Blueprint项目:
+
+```bash
+npm create ton@latest MyProject
+```
+
+2. 运行测试:
+
+```bash
+cd MyProject
+npx blueprint test
+```
+
+然后,您将在终端窗口中看到相应的输出:
+
+```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.
+```
+
+## 基本用法
+
+测试智能合约可以涵盖安全性、优化Gas支出和检查极端情况。
+在Blueprint(基于[Sandbox](https://github.com/ton-org/sandbox))中编写测试是通过定义与合约的任意操作并将测试结果与预期结果进行比较来实现的,例如:
+
+```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
+});
+```
+
+### 编写复杂Assertion的测试
+
+创建测试的基本流程是:
+
+1. 使用`blockchain.openContract()`创建特定的wrapped`Contract`实体。
+2. 描述您的`Contract`应执行的操作并将执行结果保存在`res`变量中。
+3. 使用`expect()`函数和匹配器`toHaveTransaction()`验证属性。
+
+`toHaveTransaction`匹配器所期望的对象包含以下属性中的任意组合,这些属性来自`FlatTransaction`类型
+
+| 名称 | 类型 | 描述 |
+| -------------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| from | Address? | 消息发送者的合约地址 |
+| on | Address | 消息目的地的合约地址 (属性`to`的替代名称)。 |
+| value | | 消息中Toncoin的数量,以nanotons计算 |
+| body | Cell | 定义为Cell的消息体 |
+| op | number? | op是操作标识符号码(通常为TL-B的crc32)。在消息体的前32位中。 |
+| success | boolean? | 自定义沙盒标志,定义特定交易的结果状态。如果计算和行动阶段都成功,则为True。否则为False。 |
+
+您可以省略您不感兴趣的字段,并传递接受类型并返回布尔值的函数(true表示可以),以检查例如数字范围、消息操作码等。请注意,如果字段是可选的(如`from?: Address`),那么函数也需要接受可选类型。
+
+:::tip
+您可以从[Sandbox文档](https://github.com/ton-org/sandbox#test-a-transaction-with-matcher)中查看所有匹配器字段的完整列表。
+:::
+
+### 特定测试套件
+
+#### 提取发送模式
+
+要提取发送消息的发送模式,您可以使用以下代码:
+
+```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;
+
+```
+
+## 教程
+
+从TON社区最有价值的教程中了解更多关于测试的信息:
+
+- [第2课:为智能合约测试FunC](https://github.com/romanovichim/TonFunClessons_Eng/blob/main/lessons/smartcontract/2lesson/secondlesson.md)
+- [TON Hello World第4部分:逐步指导测试您的第一个智能合约](https://ton-community.github.io/tutorials/04-testing/)
+- [TON智能合约传递途径](https://dev.to/roma_i_m/ton-smart-contract-pipeline-write-simple-contract-and-compile-it-4pnh)
+- [\[YouTube\]第六课 FunC & Blueprint。Gas,费用,测试。](https://youtu.be/3XIpKZ6wNcg)
+
+## 示例
+
+查看用于TON生态系统合约的测试套件,并通过示例进行学习。
+
+- [liquid-staking-contract沙盒测试](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)
+
+## 参阅
+
+-
+- [toncli](/develop/smart-contracts/testing/toncli)
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/smart-contracts/testing/writing-test-examples.mdx b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/smart-contracts/testing/writing-test-examples.mdx
new file mode 100644
index 0000000000..6d89c233d2
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/smart-contracts/testing/writing-test-examples.mdx
@@ -0,0 +1,572 @@
+# 编写测试示例
+
+此页面展示了如何为在[Blueprint SDK](https://github.com/ton-org/blueprint)([Sandbox](https://github.com/ton-org/sandbox))中创建的FunC合约编写测试。
+测试套件为演示合约[fireworks](https://github.com/ton-community/fireworks-func)构建。Fireworks是一个通过`set_first`消息初始化运行的智能合约。
+
+通过`npm create ton@latest`创建一个新的FunC项目后,测试文件`tests/contract.spec.ts`将自动生成在项目目录中,用于测试合约:
+
+```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
+});
+```
+
+使用以下命令运行测试:
+
+```bash
+npx blueprint test
+```
+
+可以通过`blockchain.verbosity`指定附加选项和vmLogs:
+
+```typescript
+blockchain.verbosity = {
+ ...blockchain.verbosity,
+ blockchainLogs: true,
+ vmLogs: 'vm_logs_full',
+ debugLogs: true,
+ print: false,
+}
+```
+
+## 直接 cell 测试
+
+Fireworks演示了在TON区块链中发送消息的不同操作。
+
+![](/img/docs/writing-test-examples/test-examples-schemes.svg)
+
+一旦你有足够TON金额并通过`set_first`消息部署它,它将使用主要和可用的发送模式组合自动执行。
+
+Fireworks重新部署自己,结果将创建3个Fireworks实体,每个实体都有自己的ID(被保存在存储中),因此有不同的智能合约地址。
+
+为了清晰起见,我们定义不同ID的Fireworks实例(不同的`state_init`)并以下列名称命名:
+
+- 1 - Fireworks setter - 传播不同启动操作码的实体。可以扩展到四种不同的操作码。
+- 2 - Fireworks launcher-1 - 启动第一个firework的Fireworks实例,意味着消息将被发送给launcher。
+- 3 - Fireworks launcher-2 - 启动第二个firework的Fireworks实例,意味着消息将被发送给launcher。
+
+
+ 展开交易细节
+
+index - 是`launchResult`数组中交易的ID。
+
+- `0` - 对资金库(the Launcher)的外部请求,导致向fireworks发送2.5 TON的出站消息`op::set_first`
+- `1` - 在Fireworks setter合约中使用`op::set_first`调用的交易,并执行了两个出站消息到Fireworks Launcher-1和Fireworks Launcher-2
+- `2` - 在Fireworks launcher 1中使用`op::launch_first`调用的交易,并执行了四个出站消息到the Launcher。
+- `3` - 在Fireworks launcher 2中使用`op::launch_second`调用的交易,并执行了一个出站消息到the Launcher。
+- `4` - 在the Launcher中来自Fireworks launcher 1的入站消息的交易。此消息以`send mode = 0`发送。
+- `5` - 在the Launcher中来自Fireworks launcher 1的入站消息的交易。此消息以`send mode = 1`发送。
+- `6` - 在the Launcher中来自Fireworks launcher 1的入站消息的交易。此消息以`send mode = 2`发送。
+- `7` - 在the Launcher中来自Fireworks launcher 1的入站消息的交易。此消息以`send mode = 128 + 32`发送。
+- `8` - 在the Launcher中来自Fireworks launcher 2的入站消息的交易。此消息以`send mode = 64`发送。
+
+
+
+每个“firework” - 是交易ID:3和ID:4中出现的带有独特消息体的出站消息。
+
+以下是每个预期成功执行的交易的测试列表。交易[ID:0]是对资金库(the Launcher)的外部请求,导致向fireworks发送2.5 TON的出站消息`op::set_first`。如果您将Fireworks部署到区块链,launcher会是您的钱包。
+
+### 交易ID:1 成功测试
+
+[此测试](https://github.com/ton-community/fireworks-func/blob/main/tests/Fireworks.spec.ts#L75)检查是否通过发送2.5 TON的交易成功设置了fireworks。
+这是最简单的情况,主要目的是确认交易成功属性为true。
+
+要从`launhcResult.transactions`数组中过滤出特定交易,我们可以使用最方便的字段。
+通过`from`(合约发送方地址)、`to`(合约目的地地址)、`op`(操作码值) - 我们将仅检索此组合的一个交易。
+
+![](/img/docs/writing-test-examples/test-examples-schemes_id1.svg)
+
+交易[ID:1]在Fireworks Setter合约中被`op::set_first`调用,并执行了两个出站消息到Fireworks Launcher-1和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
+ })
+
+ });
+
+```
+
+### 交易ID:2 成功测试
+
+[此测试](https://github.com/ton-community/fireworks-func/blob/main/tests/Fireworks.spec.ts#L92)检查交易[ID:2]是否成功执行。
+
+![](/img/docs/writing-test-examples/test-examples-schemes_id2.svg)
+
+交易[ID:2]在Fireworks launcher 1中进行,用`op::launch_first`调用,并执行了四个出站消息到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);
+
+ });
+```
+
+在交易要影响合约状态的情况下,可以使用`destroyed`、`endStatus`字段指定。
+
+完整的账户状态相关字段列表:
+
+- `destroyed` - `true` - 如果现有合约因执行某个交易而被销毁。否则 - `false`。
+- `deploy` - 自定义沙盒标志位,表明合约在此交易期间是否部署。如果合约在此交易前未初始化,而在此交易后变为已初始化,则为`true`。否则 - `false`。
+- `oldStatus` - 交易执行前的账户状态。值:`'uninitialized'`, `'frozen'`, `'active'`, `'non-existing'`。
+- `endStatus` - 交易执行后的账户状态。值:`'uninitialized'`, `'frozen'`, `'active'`, `'non-existing'`。
+
+### 交易ID:3 成功测试
+
+[此测试](https://github.com/ton-community/fireworks-func/blob/main/tests/Fireworks.spec.ts#L113)检查交易[ID:3]是否成功执行。
+
+![](/img/docs/writing-test-examples/test-examples-schemes_id3.svg)
+
+交易[ID:3]在Fireworks launcher 1中进行,用`op::launch_first`调用,并执行了四个出站消息到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);
+
+ });
+
+
+
+
+```
+
+### 交易ID:4 成功测试
+
+[此测试](https://github.com/ton-community/fireworks-func/blob/main/tests/Fireworks.spec.ts#L133)检查交易[ID:4]是否成功执行。
+
+![](/img/docs/writing-test-examples/test-examples-schemes_id4.svg)
+
+收到来自Fireworks launcher 1的入站消息,交易[ID:4]在the Launcher(部署钱包)中进行。此消息以`send mode = 0`发送。
+
+```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
+
+ });
+ })
+```
+
+### 交易ID:5 成功测试
+
+[此测试](https://github.com/ton-community/fireworks-func/blob/main/tests/Fireworks.spec.ts#L152)检查交易[ID:5]是否成功执行。
+
+![](/img/docs/writing-test-examples/test-examples-schemes_id5.svg)
+
+收到来自Fireworks launcher 1的入站消息,交易[ID:5]在the Launcher中进行。此消息以`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
+ });
+
+ })
+
+
+```
+
+### 交易ID:6 成功测试
+
+[此测试](https://github.com/ton-community/fireworks-func/blob/main/tests/Fireworks.spec.ts#L170)检查交易[ID:6]是否成功执行。
+
+![](/img/docs/writing-test-examples/test-examples-schemes_id6.svg)
+
+收到来自Fireworks launcher 1的入站消息,交易[ID:6]在the Launcher中进行。此消息以`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
+ });
+
+ })
+```
+
+### 交易ID:7 成功测试
+
+[此测试](https://github.com/ton-community/fireworks-func/blob/main/tests/Fireworks.spec.ts#L188)检查交易[ID:7]是否成功执行。
+
+![](/img/docs/writing-test-examples/test-examples-schemes_id7.svg)
+
+收到来自Fireworks launcher 1的入站消息,交易[ID:7]在the Launcher中进行。此消息以`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
+ });
+ })
+```
+
+### 交易ID:8 成功测试
+
+[此测试](https://github.com/ton-community/fireworks-func/blob/main/tests/Fireworks.spec.ts#L188)检查交易[ID:8]是否成功执行。
+
+![](/img/docs/writing-test-examples/test-examples-schemes_id8.svg)
+
+收到来自Fireworks launcher 2的入站消息,交易[ID:8]在the Launcher中进行。此消息以`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
+
+ });
+
+ })
+
+```
+
+## 打印和阅读交易费用
+
+在测试期间,阅读有关费用的详细信息对优化合约很有用。printTransactionFees函数以一种方便的方式打印整个交易链。"
+
+```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));
+
+ });
+
+```
+
+例如,在`launchResult`的情况下,将打印以下表格:
+
+| (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 - 是`launchResult`数组中交易的ID。
+
+- `0` - 对资金库(the Launcher)的外部请求,导致发送消息`op::set_first`到Fireworks
+- `1` - 导致发送4条消息到the Launcher的Fireworks交易
+- `2` - 在Launched Fireworks - 1中从the Launcher收到消息,消息使用`op::launch_first`操作码发送。
+- `2` - 在Launched Fireworks - 2中从the Launcher收到消息,消息使用`op::launch_second`操作码发送。
+- `4` - 在the Launcher中收到来自Launched Fireworks - 1的消息的交易,消息以`send mode = 0`发送
+- `5` - 在the Launcher中收到来自Launched Fireworks - 1的消息的交易,消息以`send mode = 1`发送
+- `6` - 在the Launcher中收到来自Launched Fireworks - 1的消息的交易,消息以`send mode = 2`发送
+- `7` - 在the Launcher中收到来自Launched Fireworks - 1的消息的交易,消息以`send mode = 128 + 32`发送
+- `8` - 在the Launcher中收到来自Launched Fireworks - 2的消息的交易,消息以`send mode = 64`发送
+
+## 交易费用测试
+
+此测试验证启动fireworks的交易费用是否符合预期。可以为佣金费用的不同部分进行自定义定价。
+
+```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'));
+
+
+ });
+
+```
+
+## 极端情况测试
+
+在本节中,将提供在交易处理期间可能发生的TVM [exit codes(退出代码)](/learn/tvm-instructions/tvm-exit-codes)的测试用例。这些exit codes在区块链代码本身中。同时,必须区分在[Compute Phase( Compute Phase )](/learn/tvm-instructions/tvm-overview#compute-phase)和Action Phase(行动阶段)期间的exit code。
+
+Compute Phase期间执行合约逻辑(其代码)。在处理期间,可以创建不同的action(动作)。这些action将在下一阶段 - Action Phase处理。如果Compute Phase不成功,则Action Phase不开始。然而,如果Compute Phase成功,这并不保证Action Phase也会成功结束。
+
+### Compute Phase | exit code = 0
+
+此exit code表示交易的Compute Phase已成功完成。
+
+### Compute Phase | exit code = 1
+
+标记Compute Phase成功的另一种exit code是`1`。要获得此exit code,您需要使用[RETALT](https://github.com/ton-community/fireworks-func/blob/ef49b4da12d287a8f6c2b6f0c19d65814c1578fc/contracts/fireworks.fc#L20)。
+
+值得注意的是,这个操作码应该在主函数中调用(例如,recv_internal)。如果在另一个函数中调用,则该函数的exit将为`1`,但总体exit code将为`0`。
+
+### Compute Phase | exit code = 2
+
+TVM是[堆栈机](/learn/tvm-instructions/tvm-overview#tvm-is-a-stack-machine)。与不同值交互时,它们会出现在堆栈上。如果突然堆栈上没有元素,但某些操作码需要它们,那么将抛出此错误。
+
+这可能发生在直接使用操作码时,因为[stdlib.fc](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/stdlib.fc)(FunC的库)假设不会有这样的问题。
+
+### Compute Phase | exit code = 3
+
+任何代码在执行前都变成了`continuation`。这是一种特殊的数据类型,包含有代码的切片、堆栈、寄存器和其他执行代码所需的数据。如果需要,这种continuation可以在稍后运行,来传递开始执行堆栈的必要参数。
+
+首先,我们[构建](https://github.com/ton-community/fireworks-func/blob/ef49b4da12d287a8f6c2b6f0c19d65814c1578fc/contracts/fireworks.fc#L31-L32)这样的continuation。在这种情况下,这只是一个空的continuation,什么也不做。接下来,使用操作码`0 SETNUMARGS`,我们指示在执行开始时堆栈中不应有值。然后,使用操作码`1 -1 SETCONTARGS`,我们调用continuation,传递1个值。由于本来应该没有值,因此我们得到了StackOverflow错误。
+
+### Compute Phase | exit code = 4
+
+在TVM中,`integer`可以在-2256 \< x \< 2256 范围内。如果在计算过程中值超出此范围,则抛出exit code 4。
+
+### Compute Phase | exit code = 5
+
+如果`integer`值超出预期范围,则抛出exit code 5。例如,如果在`.store_uint()`函数中使用了负值。
+
+### Compute Phase | exit code = 6
+
+在较低层级,使用操作码而不是熟悉的函数名称,可以在[此表](/learn/archive/tvm-instructions)中以HEX形式看到。在这个例子中,我们[使用](https://github.com/ton-community/fireworks-func/blob/ef49b4da12d287a8f6c2b6f0c19d65814c1578fc/contracts/fireworks.fc#L25)`@addop`,添加了一个不存在的操作码。
+
+模拟器在尝试处理此操作码时无法识别它,并抛出 6。
+
+### Compute Phase | exit code = 7
+
+这是一个发生在接收到错误的数据类型时的很常见的错误。在[示例](https://github.com/ton-community/fireworks-func/blob/ef49b4da12d287a8f6c2b6f0c19d65814c1578fc/contracts/fireworks.fc#L79-L80)中,`tuple`包含3个元素,但在解包时尝试获取4个。
+
+还有许多其他情况会抛出此错误。其中一些:
+
+- [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
+
+TON 中的所有数据都存储在 [cells](/develop/data-formats/cell-boc#cell) 中。一个单元格可存储 1023 位数据和 4 个指向其他单元格的引用。如果尝试写入超过 1023 位的数据或超过 4 个引用,将抛出 exit code 8。
+
+### Compute Phase | exit code = 9
+
+如果尝试从切片中读取比它包含的更多数据(从cell中读取数据时,必须将其转换为切片数据类型),则抛出exit code 9。例如,如果切片中有10位,而读取了11位,或者如果没有对其他引用的链接,但尝试加载引用。
+
+### Compute Phase | exit code = 10
+
+此错误在处理[字典](/develop/func/stdlib/#dictionaries-primitives)时抛出。例如,当值属于键时[存储在另一个cell中](https://github.com/ton-community/fireworks-func/blob/ef49b4da12d287a8f6c2b6f0c19d65814c1578fc/contracts/fireworks.fc#L100-L110)作为引用。在这种情况下,您需要使用`.udict_get_ref()`函数来获取这样的值。
+
+然而,另一个cell中的链接[应该只有1个](https://github.com/ton-blockchain/ton/blob/9728bc65b75defe4f9dcaaea0f62a22f198abe96/crypto/vm/dict.cpp#L454),而不是2个,如我们的例子:
+
+```
+root_cell
+├── key
+│ ├── value
+│ └── value - second reference for one key
+└── key
+ └── value
+```
+
+这就是为什么在尝试读取数值时,我们会得到 exit code 10。
+
+**附加:** 您还可以在字典中存储键旁的值:
+
+```
+root_cell
+├── key-value
+└── key-value
+```
+
+**注意:** 实际上,字典的结构(数据如何放置在cell中)比上面的示例更复杂。因此,它们被简化了,以便理解示例。
+
+### Compute Phase | exit code = 11
+
+此错误发生在未知情况。例如,在使用[SENDMSG](/learn/tvm-instructions/tvm-upgrade-2023-07#sending-messages)操作码时,如果传递了[错误](https://github.com/ton-community/fireworks-func/blob/ef49b4da12d287a8f6c2b6f0c19d65814c1578fc/contracts/fireworks.fc#L116)(例如,空的)的消息cell,那么就会发生这种错误。
+
+此外,它还在尝试调用不存在的方法时发生。开发人员通常是在调用不存在的GET方法时面临这种情况。
+
+### Compute Phase | exit code = -14 (13)
+
+如果处理Compute Phase的TON不足,则抛出此错误。在枚举类`Excno`中,其中指示了Compute Phase中各种错误的exit code,[指示的值为13](https://github.com/ton-blockchain/ton/blob/9728bc65b75defe4f9dcaaea0f62a22f198abe96/crypto/vm/excno.hpp#L39)。
+
+然而,在处理过程中,对此值应用了[NOT操作](https://github.com/ton-blockchain/ton/blob/9728bc65b75defe4f9dcaaea0f62a22f198abe96/crypto/block/transaction.cpp#L1574),将此值更改为`-14`。这样做是为了这个exit code不能被伪造,例如使用`throw`函数,因为所有这些函数只接受exit code是正值。
+
+### Action Phase | exit code = 32
+
+Action Phase在Compute Phase之后开始,它处理在Compute Phase期间写入[寄存器c5](/learn/tvm-instructions/tvm-initialization#control-register-c5)的动作。如果此寄存器中的数据写入不正确,则抛出exit code 32。
+
+### Action Phase | exit code = 33
+
+目前,一个交易中最多可以有`255`个动作。如果超过这个值,则Action Phase将以exit code 33 结束。
+
+### Action Phase | exit code = 34
+
+Exit code是造成处理action时的大部分错误的原因:无效消息、不正确动作等。
+
+### Action Phase | exit code = 35
+
+在构建消息的 [CommonMsgInfo](/develop/smart-contracts/tutorials/wallet#commonmsginfo) 部分时,必须指定正确的源地址。它必须等于[addr_none](/develop/data-formats/msg-tlb#addr_none00) 或发送消息的账户地址。
+
+在区块链代码中,这由[check_replace_src_addr](https://github.com/ton-blockchain/ton/blob/9728bc65b75defe4f9dcaaea0f62a22f198abe96/crypto/block/transaction.cpp#L1985)处理。
+
+### Action Phase | exit code = 36
+
+如果目的地地址无效,则抛出exit code 36。一些可能的原因是不存在的工作链或不正确的地址。所有检查都可以在[check_rewrite_dest_addr](https://github.com/ton-blockchain/ton/blob/9728bc65b75defe4f9dcaaea0f62a22f198abe96/crypto/block/transaction.cpp#L2014-L2113)中看到。
+
+### Action Phase | exit code = 37
+
+此exit code类似于Compute Phase的`-14`。在这里,它意味着余额不足以发送指定金额的TON。
+
+### Action Phase | exit code = 38
+
+与exit code 37相同,但指的是余额中缺乏[ExtraCurrency](/develop/research-and-development/minter-flow#extracurrency)。
+
+### Action Phase | exit code = 40
+
+在这种情况下,有足够的TON来处理消息的某个部分(比如说5个cell),而消息中有10个cell,将抛出exit code 40。
+
+### Action Phase | exit code = 43
+
+可能发生的情况是超过了库中cell的最大数量或超过了Merkle树的最大深度。
+
+库是存储在[Masterchain](/learn/overviews/ton-blockchain#masterchain-blockchain-of-blockchains)中的cell,如果它是[公开的](https://github.com/ton-blockchain/ton/blob/9728bc65b75defe4f9dcaaea0f62a22f198abe96/crypto/block/transaction.cpp#L1844),可以被所有智能合约使用。
+
+:::info
+由于更新代码时行的顺序可能会改变,一些链接变得不相关。因此,所有链接都将使用提交[9728bc65b75defe4f9dcaaea0f62a22f198abe96](https://github.com/ton-blockchain/ton/tree/9728bc65b75defe4f9dcaaea0f62a22f198abe96)时的代码库状态。
+:::
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/business/ton-connect-comparison.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/business/ton-connect-comparison.md
new file mode 100644
index 0000000000..efa2c6ae20
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/business/ton-connect-comparison.md
@@ -0,0 +1,19 @@
+# TON Connect 2.0 与 1.0 对比
+
+TON Connect 2.0 解决了 TON Connect 1.0 中存在的许多问题。
+
+TON Connect 2.0 协议提供了最高级别的安全性,为分散式应用程序(dApps)的开发提供了一个对开发者友好的环境,并由实时UX驱动,提供了流畅的用户体验。
+
+请参阅以下两个版本之间的对比:
+
+| | TON Connect v1 | TON Connect v2 |
+| :---------------: | :----------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------: |
+| 连接到基于 web 的 dApps | ✔︎ | ✔︎ |
+| 发送交易 | ✔︎ | ✔︎ |
+| 在钱包内连接 dapps | | ✔︎ |
+| 扫描二维码 | 每次操作都需要 | 连接时只需一次 |
+| 无服务器 dApps | | ✔︎ |
+| 实时UX | | ✔︎ |
+| 切换账户 | | 即将推出 |
+| 应用和用户之间发送消息 | | 即将推出 |
+| 钱包兼容性 | Tonkeeper | Tonkeeper, OpenMask, MyTonWallet, TonHub (即将推出)和[其他](/participate/wallets/apps#basics-features) |
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/business/ton-connect-for-business.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/business/ton-connect-for-business.md
new file mode 100644
index 0000000000..62efb8b45a
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/business/ton-connect-for-business.md
@@ -0,0 +1,46 @@
+# TON Connect 企业版
+
+TON Connect 旨在通过提供吸引流量和增加用户留存的强大功能,为企业定制化服务。
+
+## 产品特性
+
+- 安全私密的认证,可控的个人数据公开
+- 在单个用户会话内的 TON 上进行任意事务签名
+- 应用程序与用户钱包之间的即时连通性
+- 钱包内直接自动可用的应用程序
+
+## 采用 TON Connect
+
+### 基本步骤
+
+为了让开发者将 TON Connect 集成到他们的应用中,使用了专门的 TON Connect SDK。这个过程相当简单,可以在需要时访问正确的文档进行操作。
+
+TON Connect 允许用户通过二维码或通用连接链接将他们的应用与众多钱包连接。应用程序还可以使用内置浏览器扩展在钱包内打开,因此了解添加到TON Connect中的附加功能至关重要。
+
+### TON Connect 的开发者集成协助
+
+1. 描述您的应用的现有用户流程
+2. 确定所需的操作(例如,事务授权,消息签名)
+3. 向我们的团队描述您的技术栈
+
+如果您想了解更多关于 TON Connect 及其各种服务和功能,欢迎通过 [developer](https://t.me/tonrostislav) 与 TON Connect 业务团队联系,讨论您想要的解决方案。
+
+### 常见的实现案例
+
+通过使用 [TON Connect SDK](https://github.com/ton-connect/sdk),详细的集成说明让开发者能够:
+
+- 将他们的应用与各种 TON 钱包类型连接
+- 通过相应钱包的地址进行后端登录
+- 发送请求事务和在钱包内签名(接受请求)
+
+为了更好地了解这个解决方案的可能性,请查看我们在 Github 上可用的示例应用:[https://github.com/ton-connect/](https://github.com/ton-connect/demo-dapp)
+
+### 目前支持的技术栈:
+
+- 所有 web 应用 —— 无服务器的和后端的
+- React-Native 移动应用
+- 即将推出:Swift、Java、Kotlin 的移动应用 SDK
+
+TON Connect 是一个开放协议,可用于使用任何编程语言或开发环境开发 dapps。
+
+对于 JavaScript (JS) 应用,TON 开发者社区创建了一个 JavaScript SDK,使开发者能够在几分钟内无缝集成 TON Connect。将来,将提供设计用于其他编程语言的 SDK。
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/business/ton-connect-for-security.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/business/ton-connect-for-security.md
new file mode 100644
index 0000000000..cc1f118320
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/business/ton-connect-for-security.md
@@ -0,0 +1,11 @@
+# TON Connect 的安全性
+
+TON Connect 确保用户对他们分享的数据有明确的控制权,这意味着在应用程序和钱包传输期间数据不会泄露。为了加强这一设计,钱包和应用采用了强大的加密身份验证系统,这些系统相互协作。
+
+## 用户数据和资金的安全性
+
+- 在 TON Connect 上,当用户数据通过bridge被传输到钱包时,是端到端加密的。这允许应用和钱包使用第三方bridge服务器,减少数据被盗和被篡改的可能性,在此过程中显著提高数据的完整性和安全性。
+- 通过 TON Connect,安全参数被设置以允许用户数据直接与他们的钱包地址进行认证。这允许用户使用多个钱包,并选择在特定应用内使用哪一个。
+- TON Connect 协议允许分享个人数据项(如联系方式和 KYC 信息等),这意味着用户明确确认了这些数据的分享。
+
+有关 TON Connect 及其以安全为重点的设计的具体细节和相关代码示例,可以通过 [TON Connect Github](https://github.com/ton-connect/) 找到。
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/frameworks/react.mdx b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/frameworks/react.mdx
new file mode 100644
index 0000000000..0c8ba0c270
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/frameworks/react.mdx
@@ -0,0 +1,332 @@
+# TON Connect for React
+
+推荐用于React应用程序的SDK是[UI React SDK](/develop/dapps/ton-connect/developers#ton-connect-react)。它是一个React组件,提供了与TON Connect交互的高级方式。
+
+## 实现
+
+### 1. 安装
+
+要开始将TON Connect集成到您的DApp中,您需要安装`@tonconnect/ui-react`包。您可以使用npm或yarn来实现这一目的:
+
+```bash npm2yarn
+npm i @tonconnect/ui-react
+```
+
+### 2. TON Connect初始化
+
+安装包之后,您应该为您的应用程序创建一个`manifest.json`文件。有关如何创建manifest.json文件的更多信息,请参阅[此处](/develop/dapps/ton-connect/manifest)。
+
+创建manifest文件后,将TonConnectUIProvider导入到您的Mini App的根目录,并传入manifest URL:
+
+```tsx
+import { TonConnectUIProvider } from '@tonconnect/ui-react';
+
+export function App() {
+ return (
+
+ { /* Your app */ }
+
+ );
+}
+
+```
+
+### 3. 连接到钱包
+
+添加`TonConnectButton`。TonConnect按钮是初始化连接的通用UI组件。连接钱包后,它会变成钱包菜单。建议将其放置在应用程序的右上角。
+
+```tsx
+export const Header = () => {
+ return (
+
+ My App with React UI
+
+
+ );
+};
+```
+
+您还可以为按钮添加className和style属性。请注意,您不能给TonConnectButton传递子组件:
+
+```js
+
+```
+
+此外,您始终可以使用`useTonConnectUI`hook和[connectWallet](https://github.com/ton-connect/sdk/tree/main/packages/ui#call-connect)方法手动初始化连接。
+
+### 4. 重定向
+
+如果您想在连接钱包后重定向用户至特定页面,您可以使用`useTonConnectUI`hook和[定制您的返回策略](https://github.com/ton-connect/sdk/tree/main/packages/ui#add-the-return-strategy)。
+
+#### Telegram小程序
+
+如果您想在连接钱包后重定向用户至[Telegram Mini App](/develop/dapps/telegram-apps/),您可以定制`TonConnectUIProvider`元素:
+
+```tsx
+ '
+ }}
+ >
+
+```
+
+[在GitHub上查看示例](https://github.com/ton-connect/demo-dapp-with-wallet/blob/master/src/App.tsx)
+
+### 5. UI自定义
+
+要[定制模态窗口的UI](https://github.com/ton-connect/sdk/tree/main/packages/ui#ui-customisation),您可以使用`useTonConnectUI`hook和`setOptions`函数。详见[Hooks](#hooks)部分中关于useTonConnectUIhook的更多信息。
+
+## Hooks
+
+如果您想在React应用程序中使用一些低级TON Connect UI SDK的特性,您可以使用`@tonconnect/ui-react`包中的hook。
+
+### useTonAddress
+
+使用它来获取用户当前的ton钱包地址。传递布尔参数isUserFriendly来选择地址的格式。如果钱包未连接,hook将返回空字符串。
+
+```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
+
+使用它来获取用户当前的ton钱包。如果钱包未连接,hook会返回null。
+
+查看所有钱包属性
+
+[Wallet接口](https://ton-connect.github.io/sdk/interfaces/_tonconnect_sdk.Wallet.html)
+[WalletInfo接口](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
+
+使用它来获取`TonConnectUI`实例和UI选项更新函数的访问权限。
+
+[了解更多关于TonConnectUI实例方法](https://github.com/ton-connect/sdk/tree/main/packages/ui#send-transaction)
+
+[了解更多关于setOptions函数](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 (
+
+
tonConnectUI.sendTransaction(myTransaction)}>
+ Send transaction
+
+
+
+ language
+ onLanguageChange(e.target.value)}>
+ en
+ ru
+
+
+
+ );
+};
+```
+
+### useIsConnectionRestored
+
+指示连接恢复过程的当前状态。
+您可以使用它检测连接恢复过程是否已完成。
+
+```tsx
+import { useIsConnectionRestored } from '@tonconnect/ui-react';
+
+export const EntrypointPage = () => {
+ const connectionRestored = useIsConnectionRestored();
+
+ if (!connectionRestored) {
+ return Please wait... ;
+ }
+
+ return ;
+};
+```
+
+## 使用
+
+让我们来看看如何在实践中使用React UI SDK。
+
+### 发送交易
+
+向特定地址发送TON币(以nanotons计):
+
+```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 (
+
+ tonConnectUI.sendTransaction(transaction)}>
+ Send transaction
+
+
+ );
+};
+```
+
+- 在这里获取更多示例:[准备消息](/develop/dapps/ton-connect/message-builders)
+
+### 通过哈希理解交易状态
+
+位于支付处理中(使用tonweb)的原理。[了解更多](/develop/dapps/asset-processing/#checking-contracts-transactions)
+
+### 后端的可选检查(tonproof)
+
+:::tip
+了解如何签名和验证消息:[签名与验证](/develop/dapps/ton-connect/sign)
+:::
+
+使用`tonConnectUI.setConnectRequestParameters`函数来传递你的连接请求参数。
+
+该函数接受一个参数:
+
+当你在等待来自后端的响应时,设置状态为 ‘loading’。如果用户此刻打开连接钱包modals,他会看到一个加载器。
+
+```ts
+const [tonConnectUI] = useTonConnectUI();
+
+tonConnectUI.setConnectRequestParameters({
+ state: 'loading'
+});
+```
+
+或
+
+设置状态为 ‘ready’ 并定义 `tonProof` 值。传递的参数将应用于连接请求(二维码和通用链接)。
+
+```ts
+const [tonConnectUI] = useTonConnectUI();
+
+tonConnectUI.setConnectRequestParameters({
+ state: 'ready',
+ value: {
+ tonProof: ''
+ }
+});
+```
+
+或
+
+如果通过 `state: 'loading'` 启用了加载器(例如,你从后端收到一个错误而不是响应),则移除加载器。连接请求将不包含任何额外参数。
+
+```ts
+const [tonConnectUI] = useTonConnectUI();
+
+tonConnectUI.setConnectRequestParameters(null);
+```
+
+如果你的tonProof有效载荷的期限是有限的(例如,你可以每10分钟刷新一次连接请求参数),你可以多次调用`tonConnectUI.setConnectRequestParameters`。
+
+```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 }
+ });
+}
+
+```
+
+当钱包连接时,你可以在`wallet`对象中找到`ton_proof`结果:
+
+```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);
+ }
+ }), []);
+```
+
+### 钱包断开
+
+断开钱包的调用:
+
+```js
+import { useTonConnectUI } from '@tonconnect/ui-react';
+
+const [tonConnectUI] = useTonConnectUI();
+
+await tonConnectUI.disconnect();
+```
+
+## API 文档
+
+[最新的API文档](https://ton-connect.github.io/sdk/modules/_tonconnect_ui_react.html)
+
+## 示例
+
+- [TON Hello World 指南](https://ton-community.github.io/tutorials/03-client/) 来创建一个简单的DApp与React UI。
+- [Demo dApp](https://github.com/ton-connect/demo-dapp-with-react-ui) - 使用`@tonconnect/ui-react`的DApp示例。
+- [ton.vote](https://github.com/orbs-network/ton-vote) - 带有TON Connect实现的React网站示例。
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/guidelines/creating-manifest.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/guidelines/creating-manifest.md
new file mode 100644
index 0000000000..06d75cc616
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/guidelines/creating-manifest.md
@@ -0,0 +1,40 @@
+# 创建 manifest.json
+
+每个应用都需要有它的 manifest 文件,用以向钱包传递元信息。Manifest 是一个名为 `tonconnect-manifest.json` 的 JSON 文件,遵循以下格式:
+
+```json
+{
+ "url": "", // required
+ "name": "", // required
+ "iconUrl": "", // required
+ "termsOfUseUrl": "", // optional
+ "privacyPolicyUrl": "" // optional
+}
+```
+
+## 示例
+
+您可以在下面找到一个 manifest 的示例:
+
+```json
+{
+ "url": "https://ton.vote",
+ "name": "TON Vote",
+ "iconUrl": "https://ton.vote/logo.png"
+}
+```
+
+## 最佳实践
+
+- 最佳实践是将 manifest 放置在您应用和库的根目录,例如 `https://myapp.com/tonconnect-manifest.json`。这样可以让钱包更好地处理您的应用,并提升与您应用相关的用户体验。
+- 确保 `manifest.json` 文件通过其 URL 可以被 GET 访问。
+
+## 字段描述
+
+| 字段 | 要求 | 描述 |
+| ------------------ | -- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `url` | 必需 | 应用 URL。将被用作 DApp 标识符。点击钱包中的图标后,将用来打开 DApp。推荐传递不带关闭斜杠的 url,例如 'https://mydapp.com' 而非 'https://mydapp.com/'。 |
+| `name` | 必需 | 应用名称。可以简单,不会被用作标识符。 |
+| `iconUrl` | 必需 | 应用图标的 URL。必须是 PNG、ICO 等格式,不支持 SVG 图标。最好传递指向 180x180px PNG 图标的 url。 |
+| `termsOfUseUrl` | 可选 | 使用条款文档的 url。普通应用为可选,但对于放在 Tonkeeper 推荐应用列表中的应用则为必填。 |
+| `privacyPolicyUrl` | 可选 | 隐私政策文档的 url。普通应用为可选,但对于放在 Tonkeeper 推荐应用列表中的应用则为必填。 |
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/guidelines/developers.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/guidelines/developers.md
new file mode 100644
index 0000000000..60c46ea119
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/guidelines/developers.md
@@ -0,0 +1,162 @@
+# TON Connect SDKs
+
+## SDK 列表
+
+:::info
+如果可能,建议您为您的 dApps 使用 [@tonconnect/ui-react](/develop/dapps/ton-connect/developers#ton-connect-ui-react) 工具包。仅当您的产品确实需要时,才切换到 SDK 的更低层级或重新实现协议版本。
+:::
+
+本页内容包括 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) - 适用于 React 应用的 TON Connect 用户界面(UI)
+
+TonConnect UI React 是一个 React UI 工具包,用于在 React 应用中通过 TonConnect 协议连接您的应用程序至 TON 钱包。
+
+- 包含 `@tonconnect/ui-react` 的 DApp 示例:[GitHub](https://github.com/ton-connect/demo-dapp-with-react-ui)
+- 部署的 `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 文档](https://ton-connect.github.io/sdk/modules/_tonconnect_ui_react.html)
+
+## TON Connect JS SDK
+
+TON Connect 存储库包含以下主要包:
+
+- [@tonconnect/ui](/develop/dapps/ton-connect/developers#ton-connect-ui) - TON Connect 用户界面(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 协议规范
+
+### TON Connect UI
+
+TonConnect UI 是 TonConnect SDK 的一个 UI 工具包。使用它可以通过 TonConnect 协议将您的应用程序连接到 TON 钱包。它允许您使用我们的 UI 元素(如“连接钱包按钮”、“选择钱包对话框”和确认modals)更轻松地将 TonConnect 集成到您的应用中。
+
+```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 文档](https://ton-connect.github.io/sdk/modules/_tonconnect_ui.html)
+
+TON Connect 用户界面(UI)是一个框架,允许开发者提高应用用户的用户体验(UX)。
+
+TON Connect 可以通过简单的 UI 元素(如“连接钱包按钮”、“选择钱包对话框”和确认模态)轻松地与应用集成。这里有三个主要示例,展示了 TON Connect 如何在应用中提升 UX:
+
+- DApp 浏览器中的应用功能示例:[GitHub](https://ton-connect.github.io/demo-dapp/)
+- 上述 DApp 的后端部分示例:[GitHub](https://github.com/ton-connect/demo-dapp-backend)
+- 使用 Go 的 Bridge 服务器:[GitHub](https://github.com/ton-connect/bridge)
+
+此工具包将简化用 TON Connect 实现到 TON 区块链为目标平台所构建的应用中。它支持标准的前端框架,以及不使用预定框架的应用。
+
+### TON Connect SDK
+
+这三个框架中最底层的一个是 TON Connect SDK,它帮助开发者将 TON Connect 集成到他们的应用程序中。它主要用于通过 TON Connect 协议将应用程序连接到 TON 钱包。
+
+- [GitHub](https://github.com/ton-connect/sdk/tree/main/packages/sdk)
+- [NPM](https://www.npmjs.com/package/@tonconnect/sdk)
+
+### TON Connect 协议模型
+
+该包含协议请求、协议响应、事件模型以及编码和解码功能。它可用于将 TON Connect 集成到用 TypeScript 编写的钱包应用中。为了将 TON Connect 集成到 DApp 中,应该使用 [@tonconnect/sdk](https://www.npmjs.com/package/@tonconnect/sdk)。
+
+- [GitHub](https://github.com/ton-connect/sdk/tree/main/packages/protocol)
+- [NPM](https://www.npmjs.com/package/@tonconnect/protocol)
+
+## TON Connect Python
+
+### pytonconnect
+
+TON Connect 2.0 的 Python SDK。相当于 `@tonconnect/sdk` 库。
+
+使用它可以通过 TonConnect 协议将您的应用程序连接到 TON 钱包。
+
+```bash
+pip3 install pytonconnect
+```
+
+- [GitHub](https://github.com/XaBbl4/pytonconnect)
+
+### ClickoTON-Foundation tonconnect
+
+用于将 TON Connect 连接到 Python 应用的库
+
+```bash
+git clone https://github.com/ClickoTON-Foundation/tonconnect.git
+pip install -e tonconnect
+```
+
+[GitHub](https://github.com/ClickoTON-Foundation/tonconnect)
+
+## TON Connect Dart
+
+TON Connect 2.0 的 Dart SDK。相当于 `@tonconnect/sdk` 库。
+
+使用它可以通过 TonConnect 协议将您的应用程序连接到 TON 钱包。
+
+```bash
+ $ dart pub add darttonconnect
+```
+
+- [GitHub](https://github.com/romanovichim/dartTonconnect)
+
+## TON Connect C\#
+
+TON Connect 2.0 的 C# SDK。相当于 `@tonconnect/sdk` 库。
+
+使用它可以通过 TonConnect 协议将您的应用程序连接到 TON 钱包。
+
+```bash
+ $ dotnet add package TonSdk.Connect
+```
+
+- [GitHub](https://github.com/continuation-team/TonSdk.NET/tree/main/TonSDK.Connect)
+
+## TON Connect Go
+
+TON Connect 2.0 的 Go SDK。
+
+使用它可以通过 TonConnect 协议将您的应用程序连接到 TON 钱包。
+
+```bash
+ go get github.com/cameo-engineering/tonconnect
+```
+
+- [GitHub](https://github.com/cameo-engineering/tonconnect)
+
+## 常见问题和关注点
+
+如果我们的开发者或社区成员在使用 TON Connect 2.0 期间遇到任何额外问题,请联系 [Tonkeeper 开发者](https://t.me/tonkeeperdev) 频道。
+
+如果您遇到任何额外的问题,或者想提出有关如何改进 TON Connect 2.0 的提议,请通过适当的 [GitHub 目录](https://github.com/ton-connect/) 直接联系我们。
+
+## TON Connect Unity
+
+TON Connect 2.0 的 Unity 资源。使用 `continuation-team/TonSdk.NET/tree/main/TonSDK.Connect`。
+
+使用它将 TonConnect 协议与您的游戏集成。
+
+- [GitHub](https://github.com/continuation-team/unity-ton-connect)
+- [Docs](https://docs.tonsdk.net/user-manual/unity-tonconnect-2.0/getting-started)
+
+## 参阅
+
+- [构建您的第一个 Web 客户端的分步指南](https://ton-community.github.io/tutorials/03-client/)
+- [[YouTube] TON 智能合约 | 10 | Telegram DApp[EN]](https://www.youtube.com/watch?v=D6t3eZPdgAU\&t=254s\&ab_channel=AlefmanVladimir%5BEN%5D)
+- [Ton Connect 入门](https://github.com/ton-connect/sdk/tree/main/packages/sdk)
+- [集成手册](/develop/dapps/ton-connect/integration)
+- [[YouTube] TON Dev 研究 TON Connect 协议 [RU]](https://www.youtube.com/playlist?list=PLyDBPwv9EPsCJ226xS5_dKmXXxWx1CKz_)
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/guidelines/how-ton-connect-works.mdx b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/guidelines/how-ton-connect-works.mdx
new file mode 100644
index 0000000000..5661e9a20e
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/guidelines/how-ton-connect-works.mdx
@@ -0,0 +1,31 @@
+import Button from '@site/src/components/button'
+import ThemedImage from '@theme/ThemedImage';
+
+# TON Connect的工作原理
+
+TON Connect是TON中**钱包**和**应用**之间的通信协议。
+
+
+
+
+
+## 概览
+
+基于TON构建的**应用**提供丰富的功能和高性能,并旨在通过智能合约保护用户资金。由于应用程序使用了区块链等去中心化技术进行构建,它们通常被称为去中心化应用程序(dApps)。
+
+**钱包**提供了批准交易的用户界面,并在个人设备上安全地持有用户的加密密钥。
+这种关注点的分离使得用户能够实现快速创新和高水平的安全性:钱包不需要自行构建封闭的生态系统,而应用程序也不需要承担持有最终用户账户的风险。
+
+TON Connect旨在提供钱包和应用之间的无缝用户体验。
+
+## 另请参见
+
+- [TON Connect 企业版](/develop/dapps/ton-connect/business)
+- [TON Connect安全](/develop/dapps/ton-connect/security)
+- [TON Connect2.0与1.0的对比](/develop/dapps/ton-connect/comparison)
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/guidelines/integration-with-javascript-sdk.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/guidelines/integration-with-javascript-sdk.md
new file mode 100644
index 0000000000..7694b2fa85
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/guidelines/integration-with-javascript-sdk.md
@@ -0,0 +1,506 @@
+# 与 JavaScript SDK 的集成手册
+
+在本教程中,我们将创建一个示例网页应用,支持 TON Connect 2.0 认证。这将允许进行签名验证,以消除在各方之间未建立协议时的身份冒用的可能性。
+
+## 文档链接
+
+1. [@tonconnect/sdk 文档](https://www.npmjs.com/package/@tonconnect/sdk)
+2. [钱包应用消息交换协议](https://github.com/ton-connect/docs/blob/main/requests-responses.md)
+3. [Tonkeeper 钱包端实现](https://github.com/tonkeeper/wallet/tree/main/src/tonconnect)
+
+## 必要条件
+
+为了使应用和钱包之间的连接流畅,网页应用必须使用可通过钱包应用访问的 manifest。完成此项的必要条件通常是静态文件的主机。例如,假如开发者想利用 GitHub 页面,或使用托管在他们电脑上的 TON Sites 部署他们的网站。这将意味着他们的网页应用站点是公开可访问的。
+
+## 获取钱包支持列表
+
+为了提高 TON 区块链的整体采用率,TON Connect 2.0 需要能够促进大量应用和钱包连接集成。近期,TON Connect 2.0 的持续开发使得连接 Tonkeeper、TonHub、MyTonWallet 和其他钱包与各种 TON 生态系统应用成为可能。我们的使命是最终允许通过 TON Connect 协议在基于 TON 构建的所有钱包类型与应用之间交换数据。目前,这是通过为TON Connect提供加载当前在TON生态系统中运行的可用钱包的广泛列表的能力来实现的。
+
+目前我们的示例网页应用能够实现以下功能:
+
+1. 加载 TON Connect SDK(旨在简化集成的库),
+2. 创建一个连接器(当前没有应用 manifest),
+3. 加载支持的钱包列表(来自 [GitHub 上的 wallets.json](https://raw.githubusercontent.com/ton-connect/wallets-list/main/wallets.json))。
+
+为了学习目的,让我们来看看以下代码描述的 HTML 页面:
+
+```html
+
+
+
+
+
+
+
+
+
+
+```
+
+如果您在浏览器中加载此页面并查看控制台,可能会得到类似以下内容:
+
+```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"
+```
+
+根据 TON Connect 2.0 规范,钱包应用信息总是使用以下格式:
+
+```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
+}
+```
+
+## 不同钱包应用的按钮显示
+
+按钮可能会根据您的网页应用设计而变化。
+当前页面产生以下结果:
+
+```html
+
+
+
+
+
+
+ // highlight-start
+
+ // highlight-end
+
+
+ // highlight-start
+
+
+ // highlight-end
+
+
+
+
+```
+
+请注意以下几点:
+
+1. 如果网页通过钱包应用显示,它会将 `embedded` 选项设置为 `true`。这意味着标记这个登录选项很重要,因为它是最常使用的。
+2. 如果一个特定的钱包只使用 JavaScript 构建(它没有 `bridgeUrl`),并且它没有设置 `injected` 属性(或 `embedded`,为了安全),那么它显然是不可访问的,按钮应该被禁用。
+
+## 无应用 manifest 的连接
+
+在没有应用 manifest 的情况下进行连接时,脚本应该如下更改:
+
+```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);
+ }
+ };
+```
+
+现在已经进行了上述操作,正在记录状态变化(以查看 TON Connect 是否工作)。展示用于连接的 QR 代码的modals超出了本手册的范围。为了测试目的,可以使用浏览器扩展或通过任何必要的手段将连接请求链接发送到用户的手机(例如,使用 Telegram)。
+注意:我们还没有创建应用 manifest。目前,如果未满足此要求,最佳做法是分析最终结果。
+
+### 使用 Tonkeeper 登录
+
+为了用 Tonkeeper 登录,创建了以下用于认证的链接(下面提供参考):
+
+```
+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
+```
+
+当解码时,`r` 参数产生以下 JSON 格式:
+
+```js
+{"manifestUrl":"null/tonconnect-manifest.json","items":[{"name":"ton_addr"}]}
+```
+
+点击手机链接后,Tonkeeper 自动打开然后关闭,忽略请求。此外,在网页应用页面的控制台出现以下错误:
+`Error: [TON_CONNECT_SDK_ERROR] Can't get null/tonconnect-manifest.json`。
+
+这意味着应用 manifest 必须可供下载。
+
+## 使用应用清单连接
+
+从现在开始,需要在某处托管用户文件(主要是tonconnect-manifest.json)。在这个例子中,我们将使用另一个Web应用程序的清单。然而,这不推荐用于生产环境,但允许用于测试目的。
+
+以下代码片段:
+
+```js
+ window.onload = async () => {
+ const connector = new TonConnectSDK.TonConnect();
+
+ const walletsList = await connector.getWallets();
+
+ const unsubscribe = connector.onStatusChange(
+ walletInfo => {
+ console.log('Connection status:', walletInfo);
+ }
+ );
+```
+
+必须被这个版本替换:
+
+```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();
+```
+
+在上方的新版本中,添加了将 `connector` 变量存储在 `window` 中,使其在浏览器控制台中可以访问。此外,添加了 `restoreConnection`,这样用户就不必在每个Web应用程序页面都登录。
+
+### 用Tonkeeper登录
+
+如果我们拒绝钱包的请求,控制台显示的结果将是`Error: [TON_CONNECT_SDK_ERROR] Wallet declined the request`。
+
+因此,如果保存了链接,用户能够接受相同的登录请求。这意味着Web应用程序应该能够将认证拒绝视为非最终状态,以确保其正确工作。
+
+之后,接受登录请求,浏览器控制台立即反映如下:
+
+```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"
+```
+
+以上结果考虑了以下内容:
+
+1. **账户**:包含地址(工作链+哈希)、网络(主网/测试网)以及用于提取公钥的walletStateInit的信息。
+2. **设备**:包含请求时的名称和钱包应用程序版本(名称应该与最初请求的相同,但这可以进行验证以确保真实性),以及平台名称和支持功能列表。
+3. **提供者**:包含http -- 这允许钱包与Web应用程序之间进行的所有请求与响应通过bridge进行服务。
+
+## 登出并请求TonProof
+
+现在我们已经登录了我们的Mini App,但是...后端如何知道它是正确的一方呢?为了验证这一点,我们必须请求钱包所有权证明。
+
+这只能通过认证来完成,所以我们必须登出。因此,我们在控制台中运行以下代码:
+
+```js
+connector.disconnect();
+```
+
+当断开连接过程完成时,将显示 `Connection status: null`。
+
+在添加TonProof之前,让我们更改代码以表明当前实现是不安全的:
+
+```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'
+});
+```
+
+控制台显示的代码行几乎与最初启动连接时显示的一样。因此,如果后端不按预期正确执行用户认证,需要一个方法来测试它是否工作正确。为了实现这一点,可以在控制台中充当TON Foundation,以便可以测试令牌余额和令牌所有权参数的合法性。自然,提供的代码不会更改连接器中的任何变量,但是用户可以根据自己的意愿使用应用程序,除非该连接器受到闭包的保护。即使是这种情况,使用调试器和编码断点也不难提取它。
+
+现在用户的认证已经得到验证,让我们继续写代码。
+
+## 使用TonProof连接
+
+根据TON Connect的SDK文档,第二个参数指的是`connect()`方法,其中包含将由钱包warp并签名的有效载荷。因此,结果是新的连接代码:
+
+```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-'}));
+ };
+```
+
+连接链接:
+
+```
+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
+```
+
+展开并简化的`r`参数:
+
+```js
+{
+ "manifestUrl":
+ "https://ratingers.pythonanywhere.com/ratelance/tonconnect-manifest.json",
+ "items": [
+ {"name": "ton_addr"},
+ {"name": "ton_proof", "payload": "doc-example-"}
+ ]
+}
+```
+
+接下来,将url地址链接发送到移动设备并使用Tonkeeper打开。
+
+完成此过程后,接收到以下特定于钱包的信息:
+
+```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-"
+ }
+ }
+ }
+}
+```
+
+让我们验证接收到的签名。为了完成这一点,签名验证使用Python,因为它可以轻松地与后端交互。要进行这个过程所需的库是`tonsdk`和`pynacl`。
+
+接下来,需要检索钱包的公钥。为了完成这一任务,不使用`tonapi.io`或类似服务,因为最终结果不能可靠地被信任。取而代之,这是通过解析`walletStateInit`完成的。
+
+确保`address`和`walletStateInit`匹配也至关重要,否则,如果他们在`stateInit`字段中提供自己的钱包,并在`address`字段中提供另一个钱包,则可以用他们的钱包密钥签名有效载荷。
+
+`StateInit`由两种引用类型组成:一个用于代码,一个用于数据。在这个上下文中,目的是检索公钥,因此加载第二个引用(数据引用)。然后跳过8字节(在所有现代钱包合约中,4字节用于`seqno`字段和4字节用于`subwallet_id`),然后加载下一个32字节(256位)——公钥。
+
+```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))
+```
+
+在实现了上述代码后,需要查阅正确的文档来检查使用钱包密钥验证和签名了哪些参数:
+
+> ```
+> message = utf8_encode("ton-proof-item-v2/") ++
+> Address ++
+> AppDomain ++
+> Timestamp ++
+> Payload
+>
+> signature = Ed25519Sign(
+> privkey,
+> sha256(0xffff ++ utf8_encode("ton-connect") ++ sha256(message))
+> )
+> ```
+
+> 其中:
+>
+> - `Address` 表示钱包地址编码为序列:
+> - `workchain`:32位有符号整数大端序;
+> - `hash`:256位无符号整数大端序;
+> - `AppDomain` 是 Length ++ EncodedDomainName
+> - `Length` 使用32位值表示utf-8编码的app域名长度(以字节为单位)
+> - `EncodedDomainName` 是 `Length` 字节的utf-8编码的域名
+> - `Timestamp` 表示签名操作的64位 unix epoch 时间
+> - `Payload` 表示可变长度的二进制字符串
+> - `utf8_encode` 生成一个没有长度前缀的纯字节字符串。
+
+接下来用Python重实现这一部分。上述部分整数的端序没有详细说明,因此需要考虑几个示例。请参阅以下Tonkeeper实现,详细了解相关示例:: [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'
+```
+
+在实现上述参数后,如果有攻击者试图冒充用户并且没有提供有效的签名,将会显示以下错误:`nacl.exceptions.BadSignatureError: Signature was forged or corrupt`。
+
+## 下一步
+
+当编写dApp时,还应该考虑:
+
+- 在成功完成连接(无论是恢复的连接还是新连接)后,应显示`Disconnect`按钮,而不是多个`Connect`按钮
+- 用户断开连接后,需要重新创建`Disconnect`按钮
+- 应检查钱包代码,因为
+ - 最新版本的钱包可能会将公钥放在不同的位置,并导致问题
+ - 当前用户可能使用非钱包类型的合约登录。幸运的是,这将在预期的位置包含公钥
+
+祝你编写dApps时好运,并且能够享受乐趣!
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/guidelines/preparing-messages.mdx b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/guidelines/preparing-messages.mdx
new file mode 100644
index 0000000000..0c3b03a126
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/guidelines/preparing-messages.mdx
@@ -0,0 +1,1177 @@
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+# 准备消息
+
+在使用TON Connect时,您应该为在各种交易中使用的Payload构造消息体。在此页面上,您可以找到与TON Connect SDK一起使用的payload的最相关示例。
+
+:::info
+期望您学习了创建TON Connect连接的基础知识。了解更多请参阅[集成手册](/develop/dapps/ton-connect/integration)。
+:::
+
+
+## TON Connect JS SDK 示例
+
+### 交易模板
+
+无论开发者正在解决的任务级别如何,通常都需要使用来自@tonconnect/sdk或@tonconnect/ui的连接器实体。
+基于@tonconnect/sdk和@tonconnect/ui创建的示例:
+
+
+
+
+
+```js
+import { useTonConnectUI } from '@tonconnect/ui-react';
+const [tonConnectUI] = useTonConnectUI();
+
+const transaction = {
+ //transaction body
+})
+
+export const Settings = () => {
+ const [tonConnectUI, setOptions] = useTonConnectUI();
+
+ return (
+
+ tonConnectUI.sendTransaction(transaction)}>
+ Send transaction
+
+
+ );
+};
+```
+
+
+
+
+```js
+import TonConnectUI from '@tonconnect/ui';
+
+const tonConnectUI = new TonConnectUI({ //连接应用
+ 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
+})
+
+```
+
+
+
+
+
+### 常规 TON 转账
+
+TON Connect SDK提供了发送消息的封装器,使准备两个钱包之间的Toncoin的常规转账作为默认交易无需载荷变得容易。
+
+使用TON Connect JS SDK执行常规TON转账如下所示:
+
+
+
+
+
+```js
+import { useTonConnectUI } from '@tonconnect/ui-react';
+const [tonConnectUI] = useTonConnectUI();
+
+const transaction = {
+ messages: [
+ {
+ address: "0:412410771DA82CBA306A55FA9E0D43C9D245E38133CB58F1457DFB8D5CD8892F", // 目的地址
+ amount: "20000000" //以nanotons计的Toncoin
+ }
+ ]
+
+}
+
+export const Settings = () => {
+ const [tonConnectUI, setOptions] = useTonConnectUI();
+
+ return (
+
+ tonConnectUI.sendTransaction(transaction)}>
+ Send transaction
+
+
+ );
+};
+```
+
+
+
+
+
+```js
+import TonConnectUI from '@tonconnect/ui';
+
+const tonConnectUI = new TonConnectUI({ //连接应用
+ manifestUrl: 'https:///tonconnect-manifest.json',
+ buttonRootId: ''
+});
+
+const transaction = {
+ messages: [
+ {
+ address: "0:412410771DA82CBA306A55FA9E0D43C9D245E38133CB58F1457DFB8D5CD8892F", // 目的地址
+ amount: "20000000" //以nanotons计的Toncoin
+ }
+ ]
+}
+
+const result = await tonConnectUI.sendTransaction(transaction)
+```
+
+
+
+
+
+
+```js
+import TonConnect from '@tonconnect/sdk';
+const connector = new TonConnect();
+
+await connector.sendTransaction({
+ messages: [
+ {
+ address: "0:412410771DA82CBA306A55FA9E0D43C9D245E38133CB58F1457DFB8D5CD8892F", // 目的地址
+ amount: "20000000" //以nanotons计的Toncoin
+ }
+ ]
+})
+
+```
+
+
+
+:::tip
+了解更多信息请参阅[TON智能合约地址](/learn/overviews/addresses)。
+:::
+
+对于特定的自定义交易,必须定义特定的载荷。
+
+
+
+
+### 添加评论的转账
+
+最简单的例子是添加一个包含评论的载荷。更多详情请查看[此页面](/develop/smart-contracts/guidelines/internal-messages#simple-message-with-comment)。
+在交易之前,需要通过[@ton/ton](https://github.com/ton-org/ton) JavaScript库准备一个`body` [cell](/develop/data-formats/cell-boc)。
+
+```js
+import { beginCell } from '@ton/ton'
+
+const body = beginCell()
+ .storeUint(0, 32) // 写入32个零位以表示后面将跟随文本评论
+ .storeStringTail("Hello, TON!") // 写下我们的文本评论
+ .endCell();
+```
+
+通过以下方式创建交易体:
+
+
+
+
+
+```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") // body中带有评论的载荷
+ }
+ ]
+}
+
+export const Settings = () => {
+ const [tonConnectUI, setOptions] = useTonConnectUI();
+
+ return (
+
+ tonConnectUI.sendTransaction(myTransaction)}>
+ Send transaction
+
+
+ );
+};
+```
+
+
+
+
+```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") // 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") // body中带有评论的载荷
+ }
+ ]
+})
+```
+
+
+
+
+### Jetton 转账
+
+根据以下方式进行的 Jetton 转账操作的 `body`([TEP-74](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#1-transfer)) 通常应如下所示:
+
+```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 转账操作码
+ .storeUint(0, 64) // query_id:uint64
+ .storeCoins(1000000) // amount:(VarUInteger 16) - 转账的 Jetton 金额(小数位 = 6 - jUSDT, 9 - 默认)
+ .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)
+ .storeUInt(0,1) // forward_payload:(Either Cell ^Cell)
+ .endCell();
+```
+
+然后,将带有此 body 的交易发送到发送者的 jettonWalletContract 执行:
+
+
+
+
+```js
+import { useTonConnectUI } from '@tonconnect/ui-react';
+
+const myTransaction = {
+ validUntil: Math.floor(Date.now() / 1000) + 360,
+ messages: [
+ {
+ address: jettonWalletContract, // 发送方 Jetton 钱包
+ amount: toNano("0.05"), // 用于手续费,超额部分将被退回
+ payload: body.toBoc().toString("base64") // 带有 Jetton 转账 body 的载荷
+ }
+ ]
+}
+
+export const Settings = () => {
+ const [tonConnectUI, setOptions] = useTonConnectUI();
+
+ return (
+
+ tonConnectUI.sendTransaction(myTransaction)}>
+ Send transaction
+
+
+ );
+};
+```
+
+
+
+
+
+
+```js
+import TonConnectUI from '@tonconnect/ui'
+
+const transaction = {
+ validUntil: Math.floor(Date.now() / 1000) + 360,
+ messages: [
+ {
+ address: jettonWalletContract, // 发送方 Jetton 钱包
+ amount: toNano("0.05"), // 用于手续费,超额部分将被退回
+ payload: body.toBoc().toString("base64") // 带有 Jetton 转账 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, // 发送方 Jetton 钱包
+ amount: toNano("0.05"), // 用于手续费,超额部分将被退回
+ payload: body.toBoc().toString("base64") // 带有 Jetton 转账 body 的载荷
+ }
+ ]
+})
+```
+
+
+
+
+- `validUntil` - 消息有效的 UNIX 时间
+- `jettonWalletAddress` - 地址,基于 JettonMaser 和 Wallet 合约定义的 JettonWallet 地址
+- `balance` - 整数,用于gas费用的 Toncoin 金额,以 nanotons 计。
+- `body` - 用于 jettonContract 的载荷
+
+
+ Jetton 钱包状态初始化和地址准备示例
+
+```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: '放置你的 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 销毁
+
+Jetton 销毁([TEP-74](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#2-burn)) 的`body`通常应该按照以下方式完成:
+
+```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 销毁操作码
+ .storeUint(0, 64) // query_id:uint64
+ .storeCoins(1000000) // amount:(VarUInteger 16) - 以小数形式的 Jetton 金额
+ .storeAddress(Wallet_SRC) // response_destination:MsgAddress - 持有者的钱包
+ .storeUint(0, 1) // custom_payload:(Maybe ^Cell) - 通常没有载荷
+ .endCell();
+```
+
+消息放入以下请求中:
+
+
+
+
+```js
+import { useTonConnectUI } from '@tonconnect/ui-react';
+
+const myTransaction = {
+ validUntil: Math.floor(Date.now() / 1000) + 360,
+ messages: [
+ {
+ address: jettonWalletContract, // 持有者的 Jetton 钱包
+ amount: toNano("0.05"), // 用于手续费,超额部分将被退回
+ payload: body.toBoc().toString("base64") // 带有 Jetton 销毁 body 的载荷
+ }
+ ]
+}
+
+export const Settings = () => {
+ const [tonConnectUI, setOptions] = useTonConnectUI();
+
+ return (
+
+ tonConnectUI.sendTransaction(myTransaction)}>
+ Send transaction
+
+
+ );
+};
+```
+
+
+
+
+
+
+```js
+import TonConnectUI from '@tonconnect/ui'
+
+const transaction = {
+ validUntil: Math.floor(Date.now() / 1000) + 360,
+ messages: [
+ {
+ address: jettonWalletContract, // 持有者的 Jetton 钱包
+ amount: toNano("0.05"), // 用于手续费,超额部分将被退回
+ payload: body.toBoc().toString("base64") // 带有 Jetton 销毁 body 的载荷
+ }
+ ]
+}
+
+const result = await tonConnectUI.sendTransaction(transaction)
+```
+
+
+
+
+
+```js
+await connector.sendTransaction({
+ validUntil: Math.floor(Date.now() / 1000) + 360,
+ messages: [
+ {
+ address: jettonWalletContract, // 持有者的 Jetton 钱包
+ amount: toNano("0.05"), // 用于手续费,超额部分将被退回
+ payload: body.toBoc().toString("base64") // 带有 Jetton 销毁 body 的载荷
+ }
+ ]
+})
+```
+
+
+
+
+- `jettonWalletAddress` - Jetton 钱包合约地址,基于 JettonMaser 和 Wallet 合约定义
+- `amount` - 整数,用于gas费用的 Toncoin 金额,以 nanotons 计。
+- `body` - 带有 `burn#595f07bc` 操作码的 Jetton 钱包载荷
+
+### NFT 转移
+
+`body` 消息通常应按照以下方式进行:
+
+```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 转移操作码 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` - 地址 - 初始 NFT 持有者地址,用于接收超额资金
+将 `NFTitem` 转移给新所有者 `NEW_OWNER_WALLET`。
+
+
+
+
+```js
+import { useTonConnectUI } from '@tonconnect/ui-react';
+
+const myTransaction = {
+ validUntil: Math.floor(Date.now() / 1000) + 360,
+ messages: [
+ {
+ address: jettonWalletContract, // 将要转移的 NFT 物品地址
+ amount: toNano("0.05"), // 用于佣金费,超额部分将被返回
+ payload: body.toBoc().toString("base64") // 带有 NFT 转移 body 的 payload
+ }
+ ]
+}
+
+export const Settings = () => {
+ const [tonConnectUI, setOptions] = useTonConnectUI();
+
+ return (
+
+ tonConnectUI.sendTransaction(myTransaction)}>
+ Send transaction
+
+
+ );
+};
+```
+
+
+
+
+
+
+```js
+import TonConnectUI from '@tonconnect/ui'
+
+const transaction = {
+ validUntil: Math.floor(Date.now() / 1000) + 360,
+ messages: [
+ {
+ address: NFTitem, // 将要转移的 NFT 物品地址
+ amount: toNano("1.08"), // 用于佣金费,超额部分将被返回
+ payload: transferNftBody.toBoc().toString("base64") // 带有 transferNftBody 消息的 payload
+ }
+ ]
+}
+
+const result = await tonConnectUI.sendTransaction(transaction)
+```
+
+
+
+
+
+```js
+await connector.sendTransaction({
+ validUntil: Math.floor(Date.now() / 1000) + 360,
+ messages: [
+ {
+ address: NFTitem, // 将要转移的 NFT 物品地址
+ amount: toNano("1.08"), // 用于佣金费,超额部分将被返回
+ payload: transferNftBody.toBoc().toString("base64") // 带有 transferNftBody 消息的 payload
+ }
+ ]
+})
+```
+
+
+
+- `NFTitem` - 地址 - 我们希望转移到新所有者 `NEW_OWNER_Wallet` 的NFT项目智能合约的地址。
+- `balance` - 整数,用于gas支付的 Toncoin 数量(单位是nanotons)。
+- `body` - 用于 NFT 合约的载荷
+
+### NFT 销售(GetGems)
+
+以下是根据合约[nft-fixprice-sale-v3r2](https://github.com/getgems-io/nft-contracts/blob/main/packages/contracts/sources/nft-fixprice-sale-v3r2.fc)准备消息和交易以在GetGems市场上进行销售的示例。
+
+要将 NFT 放置在 GetGems 销售合约上,我们应该准备特殊消息体 `transferNftBody`,它将 NFT 转移到特殊的 NFT 销售合约。
+```js
+ const transferNftBody = beginCell()
+ .storeUint(0x5fcc3d14, 32) // NFT 转移操作码
+ .storeUint(0, 64) // query_id
+ .storeAddress(destinationAddress) // new_owner - GetGems 销售合约部署者,此操作不应更改
+ .storeAddress(walletAddress) // 超额资金的响应目的地
+ .storeBit(0) // 我们没有 custom_payload
+ .storeCoins(toNano("1")) // forward_amount
+ .storeBit(0) // 我们在此cell中存储 forward_payload
+ .storeUint(0x0fe0ede, 31) // 非 32,因为之前存储的 0 位将作为销售操作码读取
+ .storeRef(stateInitCell)
+ .storeRef(saleBody)
+ .endCell();
+```
+
+因为消息需要很多步骤,整个算法庞大,可以在此处找到:
+
+ 显示创建 NFT 销售消息体的整个算法
+
+
+```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 地址
+ const marketplaceFeeAddress = Address.parse('EQCjk1hh952vWaE9bRguFkAhDAL5jj3xj9p0uPWrFBq_GEMS'); // GetGems 收费地址
+ const destinationAddress = Address.parse("EQAIFunALREOeQ99syMbO6sSzM_Fa1RsPD5TBoS0qVeKQ-AR"); // GetGems 销售合约部署者
+
+ 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 收费
+ .storeCoins(price / BigInt(100) * BigInt(5))
+ .storeAddress(royaltyAddress)
+ // 5% - 版权费,可更改
+ .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) // 以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();
+
+ // 仅示例,非必需
+ const saleContractAddress = new Address(0, stateInitCell.hash());
+
+ const saleBody = beginCell()
+ .storeUint(1, 32) // 只是接收硬币
+ .storeUint(0, 64)
+ .endCell();
+
+ const transferNftBody = beginCell()
+ .storeUint(0x5fcc3d14, 32) // NFT 转移操作码
+ .storeUint(0, 64) // query_id
+ .storeAddress(destinationAddress) // new_owner
+ .storeAddress(walletAddress) // 超额资金的响应目的地
+ .storeBit(0) // 我们没有 custom_payload
+ .storeCoins(toNano("1")) // forward_amount
+ .storeBit(0) // 我们在此cell中存储 forward_payload
+ // 非 32,因为我们存储了 0 位 | 部署者的销售操作码
+ .storeUint(0x0fe0ede, 31)
+ .storeRef(stateInitCell)
+ .storeRef(saleBody)
+ .endCell();
+```
+
+
+
+准备好的 `transferNftBody` 应发送到 NFT 物品合约,至少需要 `1.08` TON,以成功处理。多余的部分将退还给发件人钱包。
+
+
+
+
+```js
+import { useTonConnectUI } from '@tonconnect/ui-react';
+
+const myTransaction = {
+ validUntil: Math.floor(Date.now() / 1000) + 360,
+ messages: [
+ {
+ address: NFTitem, // NFT 物品合约地址,应该放置于市场上
+ amount: toNano("1.08"), // 需要的gas费金额,多余的部分将返回
+ payload: transferNftBody.toBoc().toString("base64") // 带有 transferNftBody 消息的 payload
+ }
+ ]
+}
+
+export const Settings = () => {
+ const [tonConnectUI, setOptions] = useTonConnectUI();
+
+ return (
+
+ tonConnectUI.sendTransaction(myTransaction)}>
+ Send transaction
+
+
+ );
+};
+```
+
+
+
+
+
+```js
+import TonConnectUI from '@tonconnect/ui'
+
+const transaction = {
+ validUntil: Math.floor(Date.now() / 1000) + 360,
+ messages: [
+ {
+ address: NFTitem, // NFT 物品合约地址,应该放置于市场上
+ amount: toNano("1.08"), // 需要的gas费金额,多余的部分将返回
+ payload: transferNftBody.toBoc().toString("base64") // 带有 transferNftBody 消息的 payload
+ }
+ ]
+}
+
+const result = await tonConnectUI.sendTransaction(transaction)
+```
+
+
+
+
+
+```js
+await connector.sendTransaction({
+ validUntil: Math.floor(Date.now() / 1000) + 360,
+ messages: [
+ {
+ address: NFTitem, // NFT 物品合约地址,应该放置于市场上
+ amount: toNano("1.08"), // 需要的gas费金额,多余的部分将返回
+ payload: transferNftBody.toBoc().toString("base64") // 带有 transferNftBody 消息的 payload
+ }
+ ]
+})
+```
+
+
+
+### NFT 购买 (GetGems)
+
+购买 [nft-fixprice-sale-v3r2](https://github.com/getgems-io/nft-contracts/blob/main/packages/contracts/sources/nft-fixprice-sale-v3r2.fc) 销售合约的 NFT 的过程可以通过常规转账进行,无需负荷,唯一重要的是准确的 TON 数量,按如下计算:
+`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 销售合约地址
+ amount: toNano(buyAmount), // NFT 价格 + 1 TON, 多余的会被返回
+ }
+ ]
+}
+
+export const Settings = () => {
+ const [tonConnectUI, setOptions] = useTonConnectUI();
+
+ return (
+
+ tonConnectUI.sendTransaction(myTransaction)}>
+ Send transaction
+
+
+ );
+};
+```
+
+
+
+
+```js
+import TonConnectUI from '@tonconnect/ui'
+
+const transaction = {
+ validUntil: Math.floor(Date.now() / 1000) + 360,
+ messages: [
+ {
+ address: nftSaleContract, // 当前希望购买的 NFT 销售合约地址
+ amount: toNano(buyAmount), // NFT 价格 + 1 TON, 多余的会被返回
+ }
+ ]
+}
+
+const result = await tonConnectUI.sendTransaction(transaction)
+```
+
+
+
+
+```js
+await connector.sendTransaction({
+validUntil: Math.floor(Date.now() / 1000) + 360,
+messages: [
+ {
+ address: nftSaleContract, // 当前希望购买的 NFT 销售合约地址
+ amount: toNano(buyAmount), // NFT 价格 + 1 TON, 多余的会被返回
+ }
+]
+})
+```
+
+
+
+
+## TON Connect Python SDK
+
+Python 示例使用 [PyTonConnect](https://github.com/XaBbl4/pytonconnect) 和 [pytoniq](https://github.com/yungwine/pytoniq)。
+
+```python
+ from pytoniq_core import Address
+ from pytonconnect import TonConnect
+```
+
+:::tip
+阅读示例 [源码](https://github.com/yungwine/ton-connect-examples/blob/master/main.py)。
+:::
+
+### 常规 TON 转账
+
+
+```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', # 目的地址
+ 'amount' : 1000000000, # 1 TON,数额应以nanocoins计
+ )
+ ]
+}
+```
+
+
+### 附带评论的转账
+
+首先,通过以下函数实现带有评论的消息:
+
+```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) # 评论消息的操作码
+ .store_string(comment) # 储存评论
+ .end_cell() # 结束 cell
+ .to_boc() # 转换成 boc
+ )
+ .decode() # 编码成 url 安全的 base64
+ }
+
+ return data
+```
+
+带有评论的转账的最终交易体:
+
+```python
+transaction = {
+ 'valid_until': int(time.time() + 3600),
+ 'messages': [
+ get_comment_message(
+ destination_address='0:0000000000000000000000000000000000000000000000000000000000000000',
+ amount=int(0.01 * 10**9), # 数额应以纳币指定
+ comment='hello world!'
+ )
+ ]
+}
+```
+:::tip
+了解更多关于 [TON 智能合约地址](/learn/overviews/addresses)。
+:::
+
+### Jetton 转账
+
+构建 jetton 转账交易的函数示例:
+
+```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) # jetton 转账消息的操作码
+ .store_uint(0, 64) # query_id
+ .store_coins(jettons_amount)
+ .store_address(recipient_address) # 目的地址
+ .store_address(response_address or recipient_address) # 超额资金发送到的地址
+ .store_uint(0, 1) # 自定义负载
+ .store_coins(1) # 转发金额
+ .store_uint(0, 1) # 转发负载
+ .end_cell() # 结束 cell
+ .to_boc() # 转换成 boc
+ )
+ .decode() # 编码成 url 安全的 base64
+ }
+
+ return data
+```
+
+最终的交易体:
+
+```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), # 将jetton十进制数替换为9。例如对于 jUSDT 应该是 (amount * 10**6),
+ response_address=wallet_address
+ ),
+ ]
+}
+
+```
+
+
+### Jetton 销毁
+
+构建 jetton 销毁交易的函数示例:
+
+```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) # jetton 转账消息的操作码
+ .store_uint(0, 64) # query_id
+ .store_coins(jettons_amount)
+ .store_address(response_address) # # 超额资金发送到的地址
+ .end_cell() # 结束 cell
+ .to_boc() # 转换成 boc
+ )
+ .decode() # 编码成 url 安全的 base64
+ }
+ return data
+```
+
+最终的交易体:
+
+```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), # 将jetton十进制数替换为9。例如对于 jUSDT 应该是 (amount * 10**6),
+ response_address=wallet_address
+ ),
+ ]
+}
+```
+
+
+### NFT 转账
+
+NFT 转账交易函数的示例:
+
+```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) # nft 转账消息的操作码
+ .store_uint(0, 64) # query_id
+ .store_address(recipient_address) # 新主人
+ .store_address(response_address or recipient_address) # 超额资金发送到的地址
+ .store_uint(0, 1) # custom payload
+ .store_coins(1) # forward amount
+ .store_uint(0, 1) # forward payload
+ .end_cell() # 结束 cell
+ .to_boc() # 转换成 boc
+ )
+ .decode() # 编码成 url 安全的 base64
+ }
+ return data
+
+```
+
+最终的交易体:
+
+```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 销售 (GetGems)
+
+以下是在 GetGems 市场上进行销售时准备消息和交易的示例,根据合约 [nft-fixprice-sale-v3r2](https://github.com/getgems-io/nft-contracts/blob/main/packages/contracts/sources/nft-fixprice-sale-v3r2.fc)。
+
+为了将 NFT 放置在 GetGems 销售合约上,我们应该准备特殊的消息体 `transferNftBody`,该消息体将 NFT 转移给特殊的 NFT 销售合约。
+
+
+创建 NFT 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):
+
+ # 合约代码
+ 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=')
+
+ # 费用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
+```
+
+
+
+最终交易体:
+
+```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 购买 (GetGems)
+
+使用 [nft-fixprice-sale-v3r2](https://github.com/getgems-io/nft-contracts/blob/main/packages/contracts/sources/nft-fixprice-sale-v3r2.fc) 销售合约购买 NFT 的过程可以通过不含有效负载的常规转账进行,唯一重要的是准确的 TON 数额,计算方式如下:
+`buyAmount = Nftprice TON + 1.0 TON`。
+
+```python
+transaction = {
+ 'valid_until': int(time.time() + 3600),
+ 'messages': [
+ {
+ 'address': nft_address,
+ 'amount': buyAmount,
+ ]
+}
+```
+
+## 作者
+- JavaScript 示例提供者 [@aSpite](https://t.me/aspite)
+- Python 示例提供者 [@yunwine](https://t.me/yungwine)
+
+## 参阅
+
+* [TON Connect SDKs](/develop/dapps/ton-connect/developers)
+* [TON Connect - 发送消息](/develop/dapps/ton-connect/transactions)
+* [智能合约开发 - 发送消息(低层级)](/develop/smart-contracts/messages)
\ No newline at end of file
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/guidelines/sending-messages.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/guidelines/sending-messages.md
new file mode 100644
index 0000000000..49d13ec821
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/guidelines/sending-messages.md
@@ -0,0 +1,185 @@
+# 发送消息
+
+TON Connect 2.0 不仅仅提供了在 dApp 中认证用户的强大选项:它还可以通过已连接的钱包发送外部消息!
+
+您将了解到:
+- 如何从 DApp 发送消息到区块链
+- 如何在一次交易中发送多条消息
+- 如何使用 TON Connect 部署合约
+
+## 演示页面
+
+我们将使用 JavaScript 的低级 [TON Connect SDK](https://github.com/ton-connect/sdk/tree/main/packages/sdk) 。我们将在钱包已连接的页面上的浏览器控制台上做实验。以下是示例页面:
+
+```html
+
+
+
+
+
+
+
+
+
+
+
+```
+
+随意将其复制粘贴到您的浏览器控制台并运行。
+
+## 发送多条消息
+
+### 1) 了解任务
+
+我们将在一次交易中发送两条独立的消息:一条发送到您自己的地址,携带 0.2 TON,另一条发送到其他钱包地址,携带 0.1 TON。
+
+顺便说一下,一次交易中发送的消息有限制:
+- 标准 ([v3](/participate/wallets/contracts#wallet-v3)/[v4](/participate/wallets/contracts#wallet-v4)) 钱包:4 条传出消息;
+- 高负载钱包:255 条传出消息(接近区块链限制)。
+
+### 2) 发送消息
+
+运行以下代码:
+
+```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"
+ }
+ ]
+}));
+```
+
+您会注意到这个命令没有在控制台打印任何东西,像返回无内容的函数一样,`null` 或 `undefined`。这意味着 `connector.sendTransaction` 不会立即退出。
+
+打开您的钱包应用,您会看到原因。有一个请求,显示您要发送的内容以及coin将会去向哪里。请接受它。
+
+
+### 3) 获取结果
+
+函数将退出,并且区块链的输出将被打印:
+
+```json
+{
+ boc: "te6cckEBAwEA4QAC44gBZUPZ6qi8Dtmm1cot1P175lXUARlUVwlfMM19lkERK1oCUB3RqDxAFnPpeo191X/jiimn9Bwnq3zwcU/MMjHRNN5sC5tyymBV3SJ1rjyyscAjrDDFAIV/iE+WBySEPP9wCU1NGLsfcvVgAAACSAAYHAECAGhCAFlQ9nqqLwO2abVyi3U/XvmVdQBGVRXCV8wzX2WQRErWoAmJaAAAAAAAAAAAAAAAAAAAAGZCAFlQ9nqqLwO2abVyi3U/XvmVdQBGVRXCV8wzX2WQRErWnMS0AAAAAAAAAAAAAAAAAAADkk4U"
+}
+```
+
+BOC 是 [Bag of Cells](/learn/overviews/cells),这是 TON 中存储数据的方式。现在我们可以解码它。
+
+在您选择的工具中解码这个 BOC,您将得到以下cell树:
+
+```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)
+ ...
+```
+
+返回发送交易的 BOC 的目的是跟踪它。
+
+## 发送复杂的交易
+
+### cell的序列化
+
+在我们继续之前,让我们谈谈我们打算发送的消息格式。
+
+* **payload** (string base64, 可选): 以 Base64 编码的单cell BoC。
+ * 我们将使用它来存储转账上的文本评论
+* **stateInit** (string base64, 可选): 以 Base64 编码的单cell BoC。
+ * 我们将使用它来部署智能合约
+
+构建消息后,您可以将其序列化为 BOC。
+
+```js
+TonWeb.utils.bytesToBase64(await payloadCell.toBoc())
+```
+
+### 带评论的转账
+
+您可以使用 [toncenter/tonweb](https://github.com/toncenter/tonweb) JS SDK 或您喜欢的工具将cell序列化为 BOC。
+
+转账上的文本评论被编码为操作码 0 (32 个零位) + 评论的 UTF-8 字节。以下是将其转换为cell包的示例。
+
+```js
+let a = new TonWeb.boc.Cell();
+a.bits.writeUint(0, 32);
+a.bits.writeString("TON Connect 2 教程!");
+let payload = TonWeb.utils.bytesToBase64(await a.toBoc());
+
+console.log(payload);
+// te6ccsEBAQEAHQAAADYAAAAAVE9OIENvbm5lY3QgMiB0dXRvcmlhbCFdy+mw
+```
+
+### 智能合约部署
+
+我们将部署一个非常简单的 [聊天机器人 Doge](https://github.com/LaDoger/doge.fc),在[智能合约示例](/develop/smart-contracts/#smart-contract-examples)中提到。首先,我们加载它的代码并在数据中存储一些独特的内容,以便我们接收到一个尚未被其他人部署的全新实例。然后我们将代码和数据合并到 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
+```
+
+现在,是时候发送我们的交易了!
+
+```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
+在 [准备消息](/develop/dapps/ton-connect/message-builders) 页面获取更多关于传输 NFT 和 Jettons 的示例。
+:::
+
+确认后,我们可以在 [tonscan.org](https://tonscan.org/tx/pCA8LzWlCRTBc33E2y-MYC7rhUiXkhODIobrZVVGORg=) 看到我们的交易已完成。
+
+## 如果用户拒绝了交易请求会发生什么?
+
+处理请求拒绝相当简单,但当您正在开发某个项目时,最好提前知道会发生什么。
+
+当用户在钱包应用中的弹出窗口中点击“取消”时,会抛出异常:`Error: [TON_CONNECT_SDK_ERROR] Wallet declined the request`。这个错误可以被视为最终的(不像连接取消那样) - 如果它被抛出,那么直到发送下一个请求,请求的交易绝对不会发生。
+
+## 参阅
+
+* [准备消息](/develop/dapps/ton-connect/message-builders)
\ No newline at end of file
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/guidelines/verifying-signed-in-users.mdx b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/guidelines/verifying-signed-in-users.mdx
new file mode 100644
index 0000000000..792a506688
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/guidelines/verifying-signed-in-users.mdx
@@ -0,0 +1,213 @@
+import ThemedImage from '@theme/ThemedImage';
+
+# 签名与验证
+
+## 使用案例
+
+请注意,并非所有DApps都需要 ton_proof 验证。
+这对于后端的授权是必要的,以确保用户确实拥有声明的地址,因此可以推断出用户有权访问其在后端的数据。
+
+如果您想验证用户以便从后端提供其个人信息,这将是有用的。
+
+## ton_proof 如何工作?
+
+- 向客户端发送DAppid。通常,DAppid嵌入在二维码中。
+- 检索带有 ton_proof 实体的已签名交易
+- 在后端验证 ton_proof
+
+
+
+
+
+
+## ton_proof 的结构
+
+ton_proof 在 TON Connect 中与特殊的 `TonProof` 实体一起工作,该实体在连接器内部实现。
+
+```js
+type TonProofItemReply = TonProofItemReplySuccess | TonProofItemReplyError;
+
+type TonProofItemReplySuccess = {
+ name: "ton_proof";
+ proof: {
+ timestamp: string; // 64位unix时代签名操作的时间(秒)
+ domain: {
+ lengthBytes: number; // AppDomain 长度
+ value: string; // app 域名(作为url部分,无编码)
+ };
+ signature: string; // base64编码的签名
+ payload: string; // 来自请求的有效载荷
+ }
+}
+
+```
+
+## 在服务器端检查 ton_proof
+
+1. 从用户处检索 `TonProofItemReply`。
+2. 验证接收到的域是否对应于应用程序的域。
+3. 检查 `TonProofItemReply.payload` 是否被原始服务器允许且仍然有效。
+4. 检查 `timestamp` 在当前是否真实。
+5. 根据[消息方案](/develop/dapps/ton-connect/sign#concept-explanation)组装消息。
+6. 通过API (a) 或 (b) 在后端实现的逻辑获取 `public_key`
+- 6a:
+ - 通过 [TON API](https://docs.tonconsole.com/tonapi/api-v2#:~:text=/v2/-,tonconnect,-/stateinit) 方法 `POST /v2/tonconnect/stateinit` 从 `walletStateInit` 中检索 `{public_key, address}`。
+ - 验证从 `walletStateInit` 提取的 `address` 或对应于用户声明的钱包 `address`。
+- 6b:
+ - 通过钱包合约的 [get 方法](https://github.com/ton-blockchain/wallet-contract/blob/main/func/wallet-v4-code.fc#L174) 获得钱包的 `public_key`。
+ - 如果合约未激活,或者缺少在旧钱包版本(v1-v3)中发现的 get_method,则以这种方式获取密钥将是不可能的。相反,您将需要解析前端提供的 walletStateInit。确保 TonAddressItemReply.walletStateInit.hash() 等于 TonAddressItemReply.address.hash(),表示一个BoC哈希。
+7. 验证前端的 `signature` 实际上签署了组装的消息,并且对应于地址的 `public_key`。
+
+## React 示例
+
+1. 将token提供器添加到应用的根部:
+
+```tsx
+function App() {
+ const [token, setToken] = useState(null);
+
+ return (
+
+ { /* Your app */ }
+
+ )
+}
+```
+
+2. 描述后端认证:
+
+
+示例
+
+```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])
+}
+```
+
+
+## 概念解释
+
+如果请求了 `TonProofItem`,钱包证明了账户所选密钥的所有权。签名消息绑定到:
+
+- 独特的前缀,以将消息与链上消息分开。(`ton-connect`)
+- 钱包地址
+- App域
+- 签名时间戳
+- 应用的自定义有效载荷(服务器可能在其中放置其随机数、cookie id、过期时间)
+
+```
+message = utf8_encode("ton-proof-item-v2/") ++
+ Address ++
+ AppDomain ++
+ Timestamp ++
+ Payload
+
+signature = Ed25519Sign(privkey, sha256(0xffff ++ utf8_encode("ton-connect") ++ sha256(message)))
+```
+
+其中:
+
+* `Address` 是钱包地址编码为一系列:
+ * `workchain`:32位有符号整数大端;
+ * `hash`:256位无符号整数大端;
+* `AppDomain` 是长度 ++ 编码的域名
+- `长度` 是utf-8编码的应用域名长度的32位值(以字节为单位)
+- `编码的域名` 是长度字节的utf-8编码的应用域名
+* `Timestamp` 是签名操作的64位unix时代时间
+* `Payload` 是一个可变长度的二进制字符串。
+
+注意:有效载荷是可变长度的不受信任数据。为避免使用不必要的长度前缀,我们将其放在消息的最后。
+
+公钥必须验证签名:
+
+1. 首先,尝试通过在 `Address` 部署的智能合约上的 `get_public_key` get-method 获得公钥。
+
+2. 如果智能合约尚未部署,或缺少get-method,您需要:
+
+ 1. 解析 `TonAddressItemReply.walletStateInit` 并从stateInit获取公钥。您可以将 `walletStateInit.code` 与标准钱包合约的代码进行比较,并根据找到的钱包版本解析数据。
+
+ 2. 检查 `TonAddressItemReply.publicKey` 是否等于获得的公钥。
+
+ 3. 检查 `TonAddressItemReply.walletStateInit.hash()` 是否等于 `TonAddressItemReply.address`。`.hash()` 意味着BoC哈希。
+
+
+### TON Proof 验证示例
+
+* [GO 演示应用](https://github.com/ton-connect/demo-dapp-backend/blob/master/proof.go)
+* [TS 示例](https://gist.github.com/TrueCarry/cac00bfae051f7028085aa018c2a05c6)
+* [Python 示例](https://github.com/XaBbl4/pytonconnect/blob/main/examples/check_proof.py)
+
+## 参阅
+
+* [[YouTube] 为 @tonconnect/react-ui 检查 ton_proof [RU]](https://youtu.be/wIMbkJHv0Fs?list=PLyDBPwv9EPsCJ226xS5_dKmXXxWx1CKz_&t=2971)
+* [准备消息](/develop/dapps/ton-connect/message-builders)
+* [发送消息](/develop/dapps/ton-connect/transactions)
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/overview.mdx b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/overview.mdx
new file mode 100644
index 0000000000..66b9641a5b
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/overview.mdx
@@ -0,0 +1,101 @@
+import Button from '@site/src/components/button'
+
+# 关于 TON Connect
+
+使用TON Connect,在[TON](/learn/introduction)中实现钱包之间的无缝连接。
+
+![](/img/docs/ton-connect/ton-connect-overview.png?raw=true)
+
+随意使用以下流程之一,以集成您的应用程序:
+
+
+
+React Apps
+
+
+
+
+
+HTML/JS Apps
+
+
+
+
+
+Telegram Bots
+
+
+
+## 应用程序的使用案例
+
+探索 TON 生态系统为卓越的应用程序集成提供的这些可交付成果。
+
+- **流量**。通过支持TON Connect的加密钱包,推动额外用户访问。
+- **真实**。利用TON用户的钱包作为现成账户,无需额外的身份验证步骤,从而提升用户体验。
+- **支付**。通过TON区块链使用Toncoin或封装的稳定币(jUSDC/jUSDT)快速安全地处理交易。
+- **留存**。通过应用内列表保存功能增强用户留存,使用户能够跟踪最近打开和收藏的应用。
+
+## 对于钱包开发者
+
+如果您是钱包开发者,您可以将您的钱包连接到 TON Connect,让您的用户以安全、便捷的方式与 TON 应用程序互动,请阅读如何[将 TON Connect 集成到您的钱包](/develop/dapps/ton-connect/wallet/)。
+
+## 成功案例
+
+- [GetGems — 开放网络Marketplace](https://getgems.io/)
+- [STON.fi — TON区块链AMM DEX](https://ston.fi/)
+- [Tonstarter](http://tonstarter.com/)
+
+
+ 显示完整列表
+
+- [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/)
+
+
+
+## 加入 TON 生态系统
+
+为了将您的服务与TON生态系统连接起来,您需要执行以下操作:
+
+- **TON Connect**。在您的应用程序中集成TON Connect协议。
+- **交易**。使用TON库创建指定的交易消息。通过我们的全面指南深入了解[发送消息](/develop/dapps/ton-connect/message-builders)的过程。
+- **支付**。通过公共API([tonapi](https://tonapi.io/))或您自己的索引器,例如[gobycicle](http://github.com/gobicycle/bicycle)处理支付。从我们关于[资产处理](/develop/dapps/asset-processing)的详尽指南中了解更多信息。
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/wallet.mdx b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/wallet.mdx
new file mode 100644
index 0000000000..99811809f8
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/ton-connect/wallet.mdx
@@ -0,0 +1,19 @@
+import Button from '@site/src/components/button'
+
+# 连接钱包
+
+如果您是一名钱包开发者,您可以将您的钱包连接到TON Connect,并使您的用户能够以安全便捷的方式与TON应用程序进行交互。
+
+## 集成
+
+使用以下步骤将您的钱包连接到TON Connect:
+
+1. 仔细阅读[协议规范](/develop/dapps/ton-connect/protocol/)。
+2. 使用其中一个[SDK](/develop/dapps/ton-connect/developers)实现协议。
+3. 通过拉取请求将您的钱包添加到[钱包列表](https://github.com/ton-blockchain/wallets-list)中。
+
+
+
+开始集成
+
+
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/overview.mdx b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/overview.mdx
new file mode 100644
index 0000000000..20ef53dba7
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/overview.mdx
@@ -0,0 +1,17 @@
+# 概览
+
+## 概念
+
+阅读更多关于思想的内容:
+
+- [TON上的支付](https://blog.ton.org/ton-payments)
+- [TON DNS和域名](/participate/web3/dns)
+- [TON网站、TON WWW和TON代理](https://blog.ton.org/ton-sites)
+
+## 用例
+
+- [为任何智能合约提供\*.ton用户友好域名](/participate/web3/dns)
+- [使用TON代理连接到TON网站](/participate/web3/setting-proxy)
+- [运行自己的TON代理以连接到TON网站](/participate/web3/sites-and-proxy)
+- [将您的TON钱包或TON网站链接到一个域名](/participate/web3/site-management)
+- [如何使用TON DNS智能合约创建子域名](/participate/web3/site-management#how-to-set-up-subdomains)
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-dns/dns.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-dns/dns.md
new file mode 100644
index 0000000000..6964dd0046
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-dns/dns.md
@@ -0,0 +1,45 @@
+# TON DNS和域名
+
+TON DNS是一项服务,用于将易于人类阅读的域名(如`test.ton`或`mysite.temp.ton`)转换为TON智能合约地址、TON网络上运行的服务(如TON网站)所使用的ADNL地址等。
+
+## 标准
+
+[TON DNS标准](https://github.com/ton-blockchain/TIPs/issues/81)描述了域名的格式、解析域的过程、DNS智能合约的接口以及DNS记录的格式。
+
+## SDK
+
+在JavaScript SDK [TonWeb](https://github.com/toncenter/tonweb) 和 [TonLib](https://ton.org/#/apis/?id=_2-ton-api)中实现了与TON DNS的交互。
+
+```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);
+```
+
+`lite-client` 和 `tonlib-cli` 也支持DNS查询。
+
+## 一级域名
+
+目前,只有以`.ton`结尾的域名被认为是有效的TON DNS域名。
+
+根DNS智能合约源代码 - https://github.com/ton-blockchain/dns-contract/blob/main/func/root-dns.fc。
+
+将来这可能会改变。添加新的一级域名将需要新的根智能合约和改变[网络配置#4](https://ton.org/#/smart-contracts/governance?id=config)的通用投票。
+
+## \*.ton域名
+
+\*.ton域名以NFT的形式实现。由于它们实现了NFT标准,因此与常规NFT服务(例如NFT市场)和可以显示NFT的钱包兼容。
+
+\*.ton域名源代码 - https://github.com/ton-blockchain/dns-contract。
+
+.ton域名解析器实现了NFT集合接口,而.ton域名实现了NFT项接口。
+
+\*.ton域名的首次销售通过https://dns.ton.org上的去中心化公开拍卖进行。源代码 - https://github.com/ton-blockchain/dns。
+
+## 子域名
+
+域名所有者可以通过在DNS记录`sha256("dns_next_resolver")`中设置负责解析子域名的智能合约地址来创建子域名。
+
+它可以是任何实现DNS标准的智能合约。
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-dns/subresolvers.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-dns/subresolvers.md
new file mode 100644
index 0000000000..6378cdbee4
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-dns/subresolvers.md
@@ -0,0 +1,445 @@
+# TON DNS 解析器
+
+## 介绍
+
+TON DNS 是一个强大的工具。它不仅允许将 TON 网站/存储包分配给域名,还可以设置子域名解析。
+
+## 相关链接
+
+1. [TON 智能合约地址系统](/learn/overviews/addresses)
+2. [TEP-0081 - TON DNS 标准](https://github.com/ton-blockchain/TEPs/blob/master/text/0081-dns-standard.md)
+3. [.ton DNS 集合的源代码](https://tonscan.org/address/EQC3dNlesgVD8YbAazcauIrXBPfiVhMMr5YYk2in0Mtsz0Bz#source)
+4. [.t.me DNS 集合的源代码](https://tonscan.org/address/EQCA14o1-VWhS2efqoh_9M1b_A9DtKTuoqfmkn83AbJzwnPi#source)
+5. [域名合约搜索器](https://tonscan.org/address/EQDkAbAZNb4uk-6pzTPDO2s0tXZweN-2R08T2Wy6Z3qzH_Zp#source)
+6. [简单子域名管理器代码](https://github.com/Gusarich/simple-subdomain/blob/198485bbc9f7f6632165b7ab943902d4e125d81a/contracts/subdomain-manager.fc)
+
+## 域名合约搜索器
+
+子域名具有实际用途。例如,区块链浏览器目前没有提供通过名称查找域名合约的方法。让我们探索如何创建一个合约,提供查找这类域名的机会。
+
+:::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.
+
+您可以尝试通过访问 `resolve-contract.ton.resolve-contract.ton` 来查看解析器代码。不幸的是,这将不会显示子解析器(那是不同的智能合约),您将看到域名合约本身的页面。
+:::
+
+### dnsresolve() 代码
+
+部分重复部分已省略。
+
+```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);
+ }
+}
+```
+
+### dnsresolve() 解释
+
+- 用户请求 `"stabletimer.ton.resolve-contract.ton"`。
+- 应用程序将其转换为 `"\0ton\0resolve-contract\0ton\0stabletimer\0"`(第一个零字节是可选的)。
+- 根 DNS 解析器将请求定向到 TON DNS 集合,剩余部分为 `"\0resolve-contract\0ton\0stabletimer\0"`。
+- TON DNS 集合将请求委托给特定域名,留下 `"\0ton\0stabletimer\0"`。
+- .TON DNS 域名合约将解析传递给编辑器指定的子解析器,子域名为 `"ton\0stabletimer\0"`。
+
+**这是 dnsresolve() 被调用的点。** 分步解释其工作方式:
+
+1. 它将子域名和类别作为输入。
+2. 如果开头有零字节,则跳过。
+3. 检查子域名是否以 `"ton\0"` 开头。如果是,
+ 1. 跳过前32位(子域名 = `"resolve-contract\0"`)
+ 2. 设置 `subdomain_sfx` 的值为 `subdomain`,并读取直到零字节的字节
+ 3. (子域名 = `"resolve-contract\0"`,subdomain_sfx = `""`)
+ 4. 从子域名切片的末尾裁剪零字节和 subdomain_sfx(子域名 = `"resolve-contract"`)
+ 5. 使用 slice_hash 和 get_ton_dns_nft_address_by_index 函数将域名转换为合约地址。您可以在 [[Subresolvers#Appendix 1. resolve-contract.ton 的代码|附录 1]] 中看到它们。
+4. 否则,dnsresolve() 检查子域名是否以 `"address\0"` 开头。如果是,它跳过该前缀并读取 base64 地址。
+5. 如果提供的用于解析的子域名与这些前缀都不匹配,函数通过返回 `(0, null())`(零字节前缀解析无 DNS 条目)表示失败。
+6. 然后检查子域名后缀是否为空。空后缀表示请求已完全满足。如果后缀为空:
+ 1. dnsresolve() 为域名的 "wallet" 子部分创建一个 DNS 记录,使用它检索到的 TON 域名合约地址。
+ 2. 如果请求类别 0(所有 DNS 条目),则将记录包装在字典中并返回。
+ 3. 如果请求类别为 "wallet"H,则按原样返回记录。
+ 4. 否则,指定类别没有 DNS 条目,因此函数表示解析成功但未找到任何结果。
+7. 如果后缀不为空:
+ 1. 之前获得的合约地址用作下一个解析器。函数构建指向它的下一个解析器记录。
+ 2. `"\0ton\0stabletimer\0"` 被传递给该合约:处理的位是子域名的位。
+
+总结来说,dnsresolve() 要么:
+
+- 将子域名完全解析为 DNS 记录
+- 部分解析为解析器记录,以将解析传递给另一个合约
+- 为未知子域名返回“未找到域名”的结果
+
+:::warning
+实际上,base64 地址解析不起作用:如果您尝试输入 `.address.resolve-contract.ton`,您将收到一个错误,表明域名配置错误或不存在。原因是域名不区分大小写(从真实 DNS 继承的功能),因此会转换为小写,将您带到不存在的工作链的某个地址。
+:::
+
+### 绑定解析器
+
+现在子解析器合约已部署,我们需要将域名指向它,即更改域名的 `dns_next_resolver` 记录。我们可以通过将以下 TL-B 结构的消息发送到域名合约来实现。
+
+1. `change_dns_record#4eb1f0f9 query_id:uint64 record_key#19f02441ee588fdb26ee24b2568dd035c3c9206e11ab979be62e55558a1d17ff record:^[dns_next_resolver#ba93 resolver:MsgAddressInt]`
+
+## 创建自己的子域名管理器
+
+子域名对普通用户来说可能有用 - 例如,将几个项目链接到单个域名,或链接到朋友的钱包。
+
+### 合约数据
+
+我们需要在合约数据中存储所有者的地址和 *域名*->*记录哈希*->*记录值* 字典。
+
+```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());
+}
+```
+
+### 处理记录更新
+
+```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();
+ }
+}
+```
+
+我们检查传入消息是否包含某些请求,不是弹回的,来自所有者,且请求为 `op::update_record`。
+
+然后,我们从消息中加载域名。我们不能将域名按原样存储在字典中:它们可能有不同的长度,但 TVM 非前缀字典只能包含等长的键。因此,我们计算 `string_hash(domain)` - 域名的 SHA-256;域名保证有整数个八位字节,因此这是有效的。
+
+之后,我们为指定域名更新记录,并将新数据保存到合约存储中。
+
+### 解析域名
+
+```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);
+}
+```
+
+`dnsresolve` 函数检查请求的子域名是否包含整数个八位字节,跳过子域名切片开头的可选零字节,然后将其分割为最高级别的域和其他部分(`test\0qwerty\0` 被分割为 `test` 和 `qwerty\0`)。加载与请求的域名对应的记录字典。
+
+如果存在非空子域名后缀,函数返回已解析的字节数和在 `"dns_next_resolver"H` 键下找到的下一个解析器记录。否则,函数返回已解析的字节数(即整个切片长度)和请求的记录。
+
+可以通过更优雅地处理错误来改进此函数,但这不是绝对必需的。
+
+## 附录 1. resolve-contract.ton 的代码
+
+
+
+```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 ();
+}
+```
+
+
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-proxy-sites/connect-with-ton-proxy.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-proxy-sites/connect-with-ton-proxy.md
new file mode 100644
index 0000000000..c9e1b260d1
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-proxy-sites/connect-with-ton-proxy.md
@@ -0,0 +1,71 @@
+# 通过 TON 代理连接
+
+## 公共入口 TON 代理
+
+您可以使用以下公共入口TON代理之一:
+
+- `in1.ton.org` 端口 `8080`
+- `in2.ton.org` 端口 `8080`
+- `in3.ton.org` 端口 `8080`
+
+TON代理与常规HTTP代理兼容,因此您可以直接在浏览器或操作系统设置中使用它。
+
+## Google Chrome
+
+根据您的操作系统,遵循Windows、macOS、Linux、iOS或Android的说明。
+
+## Firefox
+
+设置 -> 常规 -> 网络设置 -> 配置 -> 手动代理设置 -> HTTP代理
+
+在“HTTP代理”字段中,输入其中一个公共入口代理的地址,在“端口”字段中,输入“8080”(不带引号)。
+
+点击“确定”。
+
+## Safari
+
+根据您的操作系统,遵循Windows、macOS、Linux、iOS或Android的说明。
+
+## iOS
+
+设置 -> WiFi -> 点击当前连接的网络 -> 代理设置 -> 手动
+
+在“服务器”字段中,输入其中一个公共入口代理的地址,在“端口”字段中,输入“8080”(不带引号)。
+
+点击“保存”。
+
+## Android
+
+设置 -> WiFi -> 长按Wi-Fi网络名称 -> 修改网络 -> 高级选项 -> 手动
+
+在“服务器”字段中,输入其中一个公共入口代理的地址,在“端口”字段中,输入“8080”(不带引号)。
+
+点击“保存”。
+
+## Windows
+
+点击“开始”按钮,然后选择设置 > 网络和互联网 > 代理。
+
+在“手动代理设置”下,旁边的“使用代理服务器”选择“设置”。
+
+在“编辑代理服务器对话框”中,执行以下操作:
+
+打开“使用代理服务器”。
+
+输入其中一个公共入口代理的地址,在“端口”字段中,输入“8080”(不带引号)。
+
+点击“保存”。
+
+## MacOS
+
+设置 -> 网络 -> 高级 -> 代理 -> 网络代理(HTTP)。
+
+在“网络代理服务器”字段中,输入其中一个公共入口代理的地址,冒号后面输入“8080”(不带引号)。
+
+点击“确定”。
+
+## Ubuntu
+
+设置 -> 网络 -> 网络代理按钮 -> 手动
+
+在“HTTP代理”字段中,输入其中一个公共入口代理的地址,对于端口,输入“8080”(不带引号)。
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-proxy-sites/how-to-open-any-ton-site.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-proxy-sites/how-to-open-any-ton-site.md
new file mode 100644
index 0000000000..cc2b4cecc1
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-proxy-sites/how-to-open-any-ton-site.md
@@ -0,0 +1,47 @@
+# 如何打开任何 TON 网站?
+
+在这篇文章中,我们将看看从不同设备访问TON网站的最常用方法。
+
+每种方法都有其优缺点,我们将在这里分析。
+
+我们将从最简单的方法开始,最后介绍最高级的方法。
+
+## 😄 简单方法
+
+### 通过ton.run浏览
+
+打开TON网站最简单的方法是通过[ton.run](https://ton.run)。您无需在设备上安装或设置任何东西 - 只需打开**ton.run**,您就可以探索TON网站。
+
+对于偶尔浏览TON网站或进行一些检查,这种方法可能适合,但不适合常规使用,因为它也有缺点:
+
+- 您信任您的互联网流量给**ton.run**
+- 它可能随时离线或出故障
+- 它可能被您的互联网提供商封锁
+
+### TON Wallet 和 MyTonWallet 扩展
+
+稍微困难一点但更好的方法是使用某些浏览器扩展,它将连接您到TON代理,并允许您在没有任何中间服务(如ton.run)的情况下浏览TON网站。
+
+目前,TON代理已经在[MyTonWallet](https://mytonwallet.io/)扩展中可用,并且很快也将在[TON Wallet](https://chrome.google.com/webstore/detail/ton-wallet/nphplpgoakhhjchkkhmiggakijnkhfnd)扩展中可用。
+
+这种方法也相当简单,但您需要在浏览器中安装一个扩展才能使其工作。它适合大多数用户。
+
+### 连接到公共代理
+
+如果您不想安装任何扩展,或者您正在使用移动设备,您可以使用此方法。您需要在设备上配置一些东西以连接到代理。
+
+此方法在此处描述:
+
+- [通过TON代理连接](/participate/web3/setting-proxy/)
+
+## 🤓 高级方法
+
+### 使用Tonutils-Proxy
+
+这是访问TON网站最安全的方式。
+
+1. 从[这里](https://github.com/xssnick/Tonutils-Proxy#download-precompiled-version)下载最新版本
+
+2. 启动它并按“启动网关”
+
+3. 完成!
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-proxy-sites/how-to-run-ton-site.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-proxy-sites/how-to-run-ton-site.md
new file mode 100644
index 0000000000..4c9f3cba37
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-proxy-sites/how-to-run-ton-site.md
@@ -0,0 +1,63 @@
+# 如何运行 TON 网站
+
+## 👋 引言
+
+[TON 网站](https://blog.ton.org/ton-sites)的工作方式几乎与普通网站相同,除了它们的安装。需要执行一些额外的操作来启动它们。在这篇教程中,我将向您展示如何做到这一点。
+
+## 🖥 运行 TON 网站
+
+安装 [Tonutils 反向代理](https://github.com/tonutils/reverse-proxy) 来使用 TON 代理为您的网站服务。
+
+### 在任何 Linux 上的安装
+
+##### 下载
+
+```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
+```
+
+##### 运行
+
+用域配置运行,并按步骤操作:
+
+```
+./tonutils-reverse-proxy-linux-amd64 --domain your-domain.ton
+```
+
+使用 Tonkeeper、Tonhub 或任何其他钱包扫描你的终端中的 QR 码,执行交易。您的域将会链接到您的网站上。
+
+###### 无域运行
+
+作为替代,如果你没有 .ton 或 .t.me 域,你可以以简单模式运行,使用 .adnl 域:
+
+```
+./tonutils-reverse-proxy-linux-amd64
+```
+
+##### 使用
+
+现在任何人都可以访问您的 TON 网站了!使用 ADNL 地址或域名。
+
+如果您想更改一些设置,如代理pass url - 打开 `config.json` 文件,编辑后重启代理。默认的代理pass url是 `http://127.0.0.1:80/`
+
+代理添加了额外的头部:
+`X-Adnl-Ip` - 客户端的 IP 和 `X-Adnl-Id` - 客户端的 ADNL ID
+
+### 在任何其他操作系统上的安装
+
+使用 `./build.sh` 从源代码构建,然后如第 2 步中的 Linux 一样运行。构建需要 Go 环境。
+
+## 👀 后续步骤
+
+### 🔍 检查网站的可用性
+
+完成您选择的方法的所有步骤后,TON 代理应该已经启动。如果一切成功,您的网站将可在相应步骤收到的 ADNL 地址处访问。
+
+您可以通过使用域 `.adnl` 打开这个地址来检查网站的可用性。另请注意,为了打开网站,您必须在浏览器中运行 TON 代理,例如通过扩展 [MyTonWallet](https://mytonwallet.io/)。
+
+## 📌 参考资料
+
+- [TON 网站、TON WWW 和 TON 代理](https://blog.ton.org/ton-sites)
+- [Tonutils 反向代理](https://github.com/tonutils/reverse-proxy)
+- 作者: [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)
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-proxy-sites/running-your-own-ton-proxy.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-proxy-sites/running-your-own-ton-proxy.md
new file mode 100644
index 0000000000..b2b5dd82db
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-proxy-sites/running-your-own-ton-proxy.md
@@ -0,0 +1,204 @@
+# 运行自己的 TON 代理
+
+本文档旨在简要地介绍TON网站,即通过TON网络访问的网站。TON网站可以方便地作为进入其他TON服务的入口。特别是,从TON网站下载的HTML页面可能包含指向`ton://...` URI的链接,用户点击这些链接后,如果用户设备上安装了TON钱包,就可以执行支付操作。
+
+从技术角度看,TON网站非常类似于标准网站,但它们是通过[TON网络](/learn/networking/overview)(互联网内的一个覆盖网络)而不是互联网访问的。更具体地说,它们拥有一个[ADNL](/learn/networking/adnl)地址(而不是更常见的IPv4或IPv6地址),并通过[RLDP](/learn/networking/rldp)协议(这是建立在ADNL之上的高级RPC协议,ADNL是TON网络的主要协议)接受HTTP查询,而不是常规的TCP/IP。所有加密由ADNL处理,所以如果入口代理托管在用户设备上,就没有必要使用HTTPS(即TLS)。
+
+为了访问现有的网站和创建新的TON网站,需要特殊的网关来连接“普通”互联网和TON网络。本质上,通过在客户端机器上本地运行的HTTP->RLDP代理访问TON网站,并通过在远程Web服务器上运行的RLDP->HTTP代理来创建它们。
+
+[了解更多关于TON网站、WWW和代理的信息](https://blog.ton.org/ton-sites)
+
+## 运行入口代理
+
+为了访问现有的TON网站,你需要在你的电脑上运行一个RLDP-HTTP代理。
+
+1. 从[TON自动构建](https://github.com/ton-blockchain/ton/releases/latest)下载**rldp-http-proxy**。
+
+ 或者你可以按照这些[指示](/develop/howto/compile#rldp-http-proxy)自己编译**rldp-http-proxy**。
+
+2. [下载](/develop/howto/compile#download-global-config)TON全局配置。
+
+3. 运行**rldp-http-proxy**
+
+ ```bash
+ rldp-http-proxy/rldp-http-proxy -p 8080 -c 3333 -C global.config.json
+ ```
+
+在上面的例子中,`8080`是将在本地主机上监听传入HTTP查询的TCP端口,而`3333`是将用于所有出站和入站RLDP和ADNL活动的UDP端口(即通过TON网络连接到TON网站)。`global.config.json`是TON全局配置的文件名。
+
+如果一切正确,入口代理将不会终止,而是会继续在终端运行。现在可以用它来访问TON网站。当你不再需要它时,可以通过按`Ctrl-C`或简单地关闭终端窗口来终止它。
+
+你的入口代理将通过HTTP在`localhost`端口`8080`上可用。
+
+## 在远程计算机上运行入口代理
+
+1. 从[TON自动构建](https://github.com/ton-blockchain/ton/releases/latest)下载**rldp-http-proxy**。
+
+ 或者你可以按照这些[指示](/develop/howto/compile#rldp-http-proxy)自己编译**rldp-http-proxy**。
+
+2. [下载](/develop/howto/compile#download-global-config)TON全局配置。
+
+3. 从[TON自动构建](https://github.com/ton-blockchain/ton/releases/latest)下载**generate-random-id**。
+
+ 或者你可以按照这些[指示](/develop/howto/compile#generate-random-id)自己编译**generate-random-id**。
+
+4. 为你的入口代理生成一个持久的ANDL地址
+
+ ```bash
+ mkdir keyring
+
+ utils/generate-random-id -m adnlid
+ ```
+
+ 你会看到类似于
+
+ ```
+ 45061C1D4EC44A937D0318589E13C73D151D1CEF5D3C0E53AFBCF56A6C2FE2BD vcqmha5j3ceve35ammfrhqty46rkhi455otydstv66pk2tmf7rl25f3
+ ```
+
+ 这是你新生成的持久ADNL地址,以十六进制和用户友好形式显示。相应的私钥保存在当前目录的文件`45061...2DB`中。将密钥移动到keyring目录
+
+ ```bash
+ mv 45061C1* keyring/
+ ```
+
+5. 运行**rldp-http-proxy**
+
+ ```
+ rldp-http-proxy/rldp-http-proxy -p 8080 -a :3333 -C global.config.json -A
+ ```
+
+ 其中``是你的公共IPv4地址,``是在上一步中生成的ADNL地址。
+
+ 示例:
+
+ ```
+ rldp-http-proxy/rldp-http-proxy -p 8080 -a 777.777.777.777:3333 -C global.config.json -A vcqmha5j3ceve35ammfrhqty46rkhi455otydstv66pk2tmf7rl25f3
+ ```
+
+ 在上面的示例中,`8080`是将在本地主机上监听传入HTTP查询的TCP端口,而`3333`是将用于所有出站和入站RLDP和ADNL活动的UDP端口(即通过TON网络连接到TON网站)。`global.config.json`是TON全局配置的文件名。
+
+如果你做得都对,代理不会终止,而是会继续在终端运行。现在可以用它来访问TON网站。当你不再需要它时,可以通过按`Ctrl-C`或简单地关闭终端窗口来终止它。你可以将这个运行为一个unix服务以永久运行。
+
+你的入口代理将通过HTTP在``端口`8080`上可用。
+
+## 访问TON网站
+
+现在假设你在电脑上运行了一个RLDP-HTTP代理的实例,并且正在`localhost:8080`上监听传入的TCP连接,如[上面](#running-entry-proxy)所解释的。
+
+使用诸如`curl`或`wget`之类的程序进行简单测试以确认一切正常运行是可行的。例如,
+
+```
+curl -x 127.0.0.1:8080 http://just-for-test.ton
+```
+
+尝试使用代理`127.0.0.1:8080`下载(TON)站点`just-for-test.ton`的主页。如果代理正常运行,你将看到类似于
+
+```html
+
+
+
+TON Site
+
+
+TON Proxy Works!
+
+
+
+```
+
+你还可以通过使用假域名`.adnl`通过它们的ADNL地址访问TON网站
+
+```bash
+curl -x 127.0.0.1:8080 http://utoljjye6y4ixazesjofidlkrhyiakiwrmes3m5hthlc6ie2h72gllt.adnl/
+```
+
+目前获取的是同一个TON网页。
+
+或者,你可以在浏览器中将`localhost:8080`设置为HTTP代理。例如,如果你使用Firefox,请访问[设置] -> 通用 -> 网络设置 -> 设置 -> 配置代理访问 -> 手动代理配置,并在“HTTP代理”字段中输入“127.0.0.1”,在“端口”字段中输入“8080”。
+
+一旦你在浏览器中设置了`localhost:8080`作为HTTP代理,你就可以在浏览器的导航
+
+## 运行TON网站
+
+:::tip 教程找到了!
+嘿!不要从初学者友好的教程[如何运行TON网站?](/develop/dapps/tutorials/how-to-run-ton-site)开始
+:::
+
+大多数人只需要访问现有的TON网站,而不是创建新的。然而,如果你想创建一个,你需要在你的服务器上运行RLDP-HTTP代理,以及像Apache或Nginx这样的常规Web服务器软件。
+
+我们假设您已经知道如何建立一个普通网站,并且已经在服务器上配置了一个网站,接受 TCP 端口 `:80` 的 HTTP 连接,并在网络服务器配置中定义了所需的 TON 网络域名(例如 `example.ton`)作为网站的主域名或别名。
+
+1. 从 [TON Auto Builds](https://github.com/ton-blockchain/ton/releases/latest) 下载 **rldp-http-proxy** 。
+
+ 或者你可以按照这个[指示](/develop/howto/compile#rldp-http-proxy)自己编译**rldp-http-proxy**。
+
+2. [下载](/develop/howto/compile#download-global-config)TON全局配置。
+
+3. 从[TON自动构建](https://github.com/ton-blockchain/ton/releases/latest)下载**generate-random-id**。
+
+ 或者你可以按照这些[指示](/develop/howto/compile#generate-random-id)自己编译**generate-random-id**。
+
+4. 为你的服务器生成一个持久的ANDL地址
+
+ ```bash
+ mkdir keyring
+
+ utils/generate-random-id -m adnlid
+ ```
+
+ 你会看到类似于
+
+ ```bash
+ 45061C1D4EC44A937D0318589E13C73D151D1CEF5D3C0E53AFBCF56A6C2FE2BD vcqmha5j3ceve35ammfrhqty46rkhi455otydstv66pk2tmf7rl25f3
+ ```
+
+ 这是你新生成的持久ADNL地址,以十六进制和用户友好形式显示。相应的私钥保存在当前目录的文件`45061...2DB`中。将密钥移动到keyring目录
+
+ ```bash
+ mv 45061C1* keyring/
+ ```
+
+5. 确保你的Web服务器接受带有`.ton`和`.adnl`域名的HTTP请求。
+
+ 例如,如果你使用带有配置`server_name example.com;`的nginx,你需要将其更改为`server_name _;`或`server_name example.com example.ton vcqmha5j3ceve35ammfrhqty46rkhi455otydstv66pk2tmf7rl25f3.adnl;`。
+
+6. 以反向模式运行代理
+
+ ```bash
+ rldp-http-proxy/rldp-http-proxy -a :3333 -L '*' -C global.config.json -A -d -l
+ ```
+
+ 其中``是你的服务器公共IPv4地址,``是在上一步中生成的ADNL地址。
+
+如果你想让你的TON网站永久运行,你将不得不使用选项`-d`和`-l `。
+
+示例:
+
+```bash
+rldp-http-proxy/rldp-http-proxy -a 777.777.777.777:3333 -L '*' -C global.config.json -A vcqmha5j3ceve35ammfrhqty46rkhi455otydstv66pk2tmf7rl25f3 -d -l tonsite.log
+```
+
+如果一切正常工作,RLDP-HTTP代理将接受来自TON网络的传入HTTP查询,通过运行在UDP端口3333的IPv4地址``(特别是,如果你使用防火墙,请不要忘记允许`rldp-http-proxy`从该端口接收和发送UDP数据包)的RLDP/ADNL,它将把这些HTTP查询转发到所有主机(如果你只想转发特定主机,请将`-L '*'`更改为`-L `)的`127.0.0.1`TCP端口`80`(即你的常规Web服务器)。
+
+你可以在客户端机器上的浏览器中访问TON网站`http://.adnl`(在这个示例中是`http://vcqmha5j3ceve35ammfrhqty46rkhi455otydstv66pk2tmf7rl25f3.adnl`),如“访问TON网站”部分所解释的,并检查你的TON网站是否真的对公众开放。
+
+如果你愿意,你可以[注册](/participate/web3/site-management)一个TON DNS域名,比如'example.ton',并为这个域名创建一个指向你TON网站的持久ADNL地址的`site`记录。然后,在客户端模式下运行的RLDP-HTTP代理将会解析http://example.ton为指向你的ADNL地址,并访问你的TON网站。
+
+你还可以在一个单独的服务器上运行反向代理,并将你的Web服务器设置为远程地址。在这种情况下,请使用`-R '*'@:`替代`-L '*'`。
+
+示例:
+
+```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
+```
+
+在这种情况下,你的常规Web服务器应该在 `http://333.333.333.333:80` 上可用(这个IP不会对外暴露)。
+
+### 建议
+
+由于匿名功能将只在TON Proxy 2.0中可用,如果你不想公开你的Web服务器的IP地址,你可以通过以下两种方式实现:
+
+- 在单独的服务器上运行反向代理,并使用`-R`标志,如上所述。
+
+- 制作一个带有你网站副本的重复服务器,并在本地运行反向代理。
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-proxy-sites/site-and-domain-management.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-proxy-sites/site-and-domain-management.md
new file mode 100644
index 0000000000..f91488cdba
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-proxy-sites/site-and-domain-management.md
@@ -0,0 +1,49 @@
+# 网站和域名管理
+
+## 如何打开域名进行编辑
+
+1. 在您的电脑上打开Google Chrome浏览器。
+
+2. 从此[链接](https://chrome.google.com/webstore/detail/ton-wallet/nphplpgoakhhjchkkhmiggakijnkhfnd)安装Google Chrome的TON扩展。
+
+3. 打开扩展,点击“导入钱包”,并导入存储域名的钱包。
+
+> 恢复短语
+>
+> 您的恢复短语是您在创建钱包时写下的24个单词。
+>
+> 如果您丢失了短语,可以使用任何TON钱包进行恢复。
+> 在Tonkeeper中:设置 > 钱包保护 > 您的私钥。
+>
+> 请务必记下这24个单词,并将它们保存在安全的地方。在紧急情况下,您只能通过恢复短语来恢复对钱包的访问。
+> 请严格保密您的恢复短语。任何获得您恢复短语的人都将完全控制您的资金。
+
+4. 现在在https://dns.ton.org打开您的域名并点击“编辑”按钮。
+
+## 如何将钱包链接到域名
+
+您可以将钱包链接到域名,这样用户将能够通过输入域名作为接收地址来向该钱包发送币,而不是钱包地址。
+
+1. 按上述方法打开域名进行编辑。
+
+2. 将您的钱包地址复制到“Wallet address”字段中,然后点击“保存”。
+
+3. 在扩展中确认发送交易。
+
+## 如何将 TON 网站链接到域名
+
+1. 按上述方法打开域名进行编辑。
+
+2. 将您的TON网站的ADNL地址以HEX格式复制到“Site”字段中,然后点击“保存”。
+
+3. 在扩展中确认发送交易。
+
+## 如何设置子域名
+
+1. 在网络上创建一个智能合约,用于管理您网站或服务的子域名。您可以使用现成的[manual-dns](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/dns-manual-code.fc)或[auto-dns](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/dns-auto-code.fc)智能合约,或任何实现TON DNS接口的其他智能合约。
+
+2. 按上述方法打开域名进行编辑。
+
+3. 将子域名的智能合约地址复制到“Subdomains”字段中,然后点击“保存”。
+
+4. 在扩展中确认发送交易。
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-storage/storage-daemon.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-storage/storage-daemon.md
new file mode 100644
index 0000000000..c9c1485deb
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/v3/guidelines/web3/ton-storage/storage-daemon.md
@@ -0,0 +1,159 @@
+# 存储守护程序
+
+*存储守护程序是用于在TON网络中下载和共享文件的程序。`storage-daemon-cli`控制台程序用于管理正在运行的存储守护程序。*
+
+当前版本的存储守护程序可以在[Testnet](https://github.com/ton-blockchain/ton/tree/testnet)分支中找到。
+
+## 硬件要求
+
+- 至少1GHz和2核CPU
+- 至少2 GB RAM
+- 至少2 GB SSD(不计算种子文件占用空间)
+- 10 Mb/s网络带宽,具有静态IP
+
+## 二进制文件
+
+您可以从[TON自动构建](https://github.com/ton-blockchain/ton/releases/latest)下载适用于Linux/Windows/MacOS的`storage-daemon`和`storage-daemon-cli`二进制文件。
+
+## 从源代码编译
+
+您可以使用此[说明](/develop/howto/compile#storage-daemon)从源代码编译`storage-daemon`和`storage-daemon-cli`。
+
+## 关键概念
+
+- *文件包*或*包* - 通过TON存储分发的文件集合
+- TON存储的网络部分基于类似于种子的技术,因此术语*种子*、*文件包*和*包*将互换使用。但重要的是要注意一些区别:TON存储通过[ADNL](/learn/networking/adnl)通过[RLDP](/learn/networking/rldp)协议传输数据,每个*包*通过其自己的网络覆盖层分发,merkle结构可以存在两个版本 - 用于高效下载的大块和用于高效所有权证明的小块,以及[TON DHT](/learn/networking/ton-dht)网络用于查找节点。
+- *文件包*由*种子信息*和数据块组成。
+- 数据块以*种子头*开头 - 包含文件列表及其名称和大小的结构。文件本身紧随在数据块中。
+- 数据块被划分为块(默认为128 KB),并且在这些块的 SHA256 散列上构建了一个 *merkle 树*(由 TVM cell构成)。这允许构建和验证单个块的 *merkle 证明*,以及通过仅交换修改块的证明来高效重建 *包*。
+- *种子信息*包含*merkle根*:
+ - 块大小(数据块)
+ - 块大小列表
+ - Hash *merkle树*
+ - 描述 - 种子创建者指定的任何文本
+- *种子信息*被序列化为TVM cell。此cell的哈希称为*BagID*,它唯一标识*包*。
+- *包元数据*是一个包含*种子信息*和*种子头*的文件。\*这是`.torrent`文件的类比。
+
+## 启动存储守护程序和storage-daemon-cli
+
+### 启动存储守护程序的示例命令:
+
+`storage-daemon -v 3 -C global.config.json -I :3333 -p 5555 -D storage-db`
+
+- `-v` - 详细程度(INFO)
+- `-C` - 全局网络配置([下载全局配置](/develop/howto/compile#download-global-config))
+- `-I` - ADNL的IP地址和端口
+- `-p` - 控制台接口的TCP端口
+- `-D` - 存储守护程序数据库的目录
+
+### storage-daemon-cli管理
+
+它的启动方式如下:
+
+```
+storage-daemon-cli -I 127.0.0.1:5555 -k storage-db/cli-keys/client -p storage-db/cli-keys/server.pub
+```
+
+- `-I` - 守护程序的IP地址和端口(端口与上面的`-p`参数相同)
+- `-k` 和 `-p`- 这是客户端的私钥和服务器的公钥(类似于`validator-engine-console`)。这些密钥在守护程序第一次运行时生成,并放置在`/cli-keys/`中。
+
+### 命令列表
+
+`storage-daemon-cli`命令列表可以使用`help`命令获取。
+
+命令有位置参数和标志。带空格的参数应用引号(`'`或`"`)括起来,也可以转义空格。还可以使用其他转义,例如:
+
+```
+create filename\ with\ spaces.txt -d "Description\nSecond line of \"description\"\nBackslash: \"
+```
+
+`--`后的所有参数都是位置参数。可以用它来指定以破折号开头的文件名:
+
+```
+create -d "Description" -- -filename.txt
+```
+
+`storage-daemon-cli` 可以通过传递要执行的命令来以非交互模式运行:
+
+```
+storage-daemon-cli ... -c "add-by-meta m" -c "list --hashes"
+```
+
+## 添加文件包
+
+要下载 *文件包*,您需要知道其 `BagID` 或拥有一个元文件。以下命令可用于添加下载 *包*:
+
+```
+add-by-hash -d directory
+add-by-meta -d directory
+```
+
+*包* 将被下载到指定的目录。您可以省略它,然后它将被保存到存储守护程序目录中。
+
+:::info
+哈希以十六进制形式指定(长度 - 64个字符)。
+:::
+
+通过元文件添加 *包* 时,有关 *包* 的信息将立即可用:大小,描述,文件列表。通过哈希添加时,您将必须等待这些信息被加载。
+
+## 管理添加的包
+
+- `list` 命令输出 *包* 列表。
+- `list --hashes` 输出带有完整哈希的列表。
+
+在所有后续命令中,`` 要么是哈希(十六进制),要么是会话中 *包* 的序号(可以在 `list` 命令中看到的数字)。*包* 的序号不会在 storage-daemon-cli 重启之间保存,并且在非交互模式下不可用。
+
+### 方法
+
+- `get ` - 输出有关 *包* 的详细信息:描述,大小,下载速度,文件列表。
+- `get-peers ` - 输出对方节点列表。
+- `download-pause `、`download-resume ` - 暂停或恢复下载。
+- `upload-pause `、`upload-resume ` - 暂停或恢复上传。
+- `remove ` - 移除 *包*。`remove --remove-files` 还会删除 *包* 的所有文件。请注意,如果 *包* 保存在内部存储守护程序目录中,无论如何都会删除文件。
+
+## 部分下载,优先级
+
+:::info
+添加 *包* 时,您可以指定您想从中下载哪些文件:
+:::
+
+```
+add-by-hash -d dir --partial file1 file2 file3
+add-by-meta -d dir --partial file1 file2 file3
+```
+
+### 优先级
+
+*包文件* 中的每个文件都有一个优先级,从 0 到 255 的数字。优先级 0 表示文件不会被下载。`--partial` 标志位将指定的文件设置为优先级 1,其余文件设置为 0。
+
+可以使用以下命令更改已添加 *包* 中的优先级:
+
+- `priority-all ` - 对所有文件。
+- `priority-idx