TON 入门指南
从零开始在 TON Blockchain 上设置您的第一个应用程序,并了解其速度、可靠性和异步思维的基本概念。
如果您是编程完全新手,这个指南是您的最佳选择。
这个学习路径包含 5 个模块,大约需要 45 分钟。
🛳 您将学到什么
在本教程中,您将学习如何使用 JavaScript 轻松进行区块链交易。您可能可以在没有这个教程的情况下学会,但这种方法更为方便和用户友好。
您将使用 Tonkeeper 制作自己的 TON 钱包
您将使用 Testnet 水龙头为您的钱包充值以进行测试
您将了解 TON 智能合约的基本概念(地址、cell)
您将学会如何使用 TypeScript SDK 和 API 提供程序与 TON 进行交互
您将使用 NFT Miner 控制台应用程序编译您的第一笔交易
您将挖掘 NFT 火箭成就!!!
作为 TON 上的第一批矿工,您将通过工作证明的智能合约,最终为您的 TON 钱包挖掘一个秘密奖励。快来看看:
我们今天的目标是挖掘一个 NFT!这个成就将与您永远同在。
最终,您甚至可以在主网上挖掘这个 NFT 成就。(只需 0.05 TON!)
视频教程
查看这个由 TON 开发者社区成员创建的精彩视频教程!借助这个有用的指南,您可以轻松完成教程:
在 TON Blockchain 上挖矿
今天,我们将教我们有志向的建设者如何在 TON Blockchain 上挖矿。这一经验将使你们所有人都能够理解挖矿的重要性,以及为什么比特币挖矿帮助改变了这个行业。
尽管定义了最初的挖矿流程的 PoW Giver 智能合约框架在启动时完成,帮助奠定了 TON 的工作证明(PoW)代币分发机制的基础,但最后一个 TON 在 2022 年 6 月被挖出,达到了 TON 的工作证明(PoW)代币分发机制的顶点。也就是说,随着我们最近过渡到权益证明(PoS),在 TON 上进行质押的时代就将开始。
现在,让我们专注于成为 TVM 开发者 的第一步,学习如何在 TON 上挖掘一个 NFT!下面是我们要创建的示例。
如果我们集中注意力,大约半小时内就能创建成为一个矿工。
🦄 开始
要开始,所有开发者都将使用以下组件:
- 钱包:您需要一个非托管钱包,以在测试网模式下存储 NFT。
- 代码库:我们将使用专为您设计的现成模板。
- 开发环境:开发者需要确定他们是想使用本地还是云环境进行挖矿。
下载并创建钱包
首先,您需要一个非托管钱包,用于接收和存储您的 TON;在这个指南中,我们使用 Tonkeeper。您需要在钱包内启用 Testnet 模式,以便能够接收 Testnet Toncoins。稍后将使用这些代币发送最终的铸币交易到智能合约。
使用非托管钱包,用户拥有并独自持有其私钥。
要下载并创建 TON 钱包,请按照以下简单步骤操作:
简单!现在我们开始开发。
项目设置
为了使您的工作更轻松,跳过例行的低级工作,我们将使用一个样板。
注意,您需要登录到 GitHub 进行后续工作。
请使用 ton-onboarding-challenge 模板创建您的项目,点击“Use this template”按钮并选择“Create a new repository”选项,如下图所示:
完成此步骤后,您将获得一个高性能的存储库,可用作您挖矿的核心。恭喜! ✨
开发环境
下一步是选择最适合您需求、经验水平和整体技能的开发环境。如您所见,可以通过使用云端或本地环境来完成此过程。在云端开发通常被认为更简单、更容易入门。以下是两种方法的步骤。
确保您已在 GitHub 个人资料中打开了从前一步模板生成的代码库。
本地和云端开发环境
对于不熟悉 JavaScript 的用户,使用 JavaScript IDE 可能会有挑战,特别是如果您的计算机和工具系统未以此为目的进行配置。
但是,如果您熟悉 NodeJS 和 Git,并且知道如何使用
npm
,使用本地环境可能更适合您。
Cloud Codespaces
如果选择云开发环境,首先选择 Code 选项卡,然后在 GitHub 代码库内点击 Create codespace on master 按钮,如下图所示:
完成此步骤后,GitHub 将创建一个特殊的云工作空间,允许您访问 VSCode 在线 IDE(Visual Code 在线集成开发环境)。
一旦授予访问权限(codespace 通常在约 30 秒内启动),您将拥有一切开始所需的内容,无需安装 Git、Node.js 或其他开发工具。
本地开发环境
要设置本地开发环境,您需要访问这三个基本工具:
- Git:Git 是每个开发人员需要使用的基本工具,可在 此处 下载。
- NodeJS:Node.js 是通常用于 TON 上应用程序开发的 JavaScript 和 TypeScript 运行环境,可在 此处 下载。
- JavaScript IDE:JavaScript IDE 通常用于本地开发环境中的开发。例如 Visual Studio Code(VSCode)。
要开始,您需要克隆 GitHub 代码库模板,并在集成开发环境(IDE)中打开正确的代码库。
运行脚本
在本指南中,您需要运行 TypeScript 脚本。所有命令,如运行脚本或安装模块,都通过位于 IDE 的终端工作区的命令行执行。该工作区通常位于 IDE 底部。
例如,在 Cloud Codespaces 中,您应该打开终端工作区:
在此窗口中输入命令,然后按 Enter 执行:
终端也可以作为一个独立的应用程序使用。请根据您的 IDE 和操作系统选择适当的版本。
太好了!完成这些步骤后,您就可以深入了解 TON 区块链的奥秘了。👀
🎯 连接到 TON
连接到 TON 区块链需要什么?
- 智能合约地址 作为目的地。我们的目标是从 工作证明智能合约 中挖掘一个 NFT,因此我们需要一个地址以获取当前挖矿复杂性。
- API 提供商 用于向 TON 区块链发出请求。TON 有多个API 类型 用于不同目的。我们将使用 toncenter.com API 的测试网版本。
- JavaScript SDK:需要一个 JavaScript SDK(SDK 是软件开发工具包)来解析正在使用的智能合约地址并准备创建 API 请求。为了更好地理解 TON 地址以及为什么需要解析它来执行此过程,请查看这个资源。我们将使用 ton.js 执行此过程。
在下一部分中,我们将描述用户如何使用 TONCenter API 和 ton.js 发送初始请求以从 PoW 智能合约接收数据。
智能合约地址
为了让矿工正常工作,我们需要添加两种不同的智能合约地址类型。它们包括:
- 钱包地址: 必须提供钱包地址,因为这对于矿工接收他们的挖矿奖励是必要的(在这种情况下,我们必须使用Tonkeeper测试网模式)。
- 集合地址: 集合地址是必需的,作为智能合约以正确挖掘NFT(要执行此过程,请复制Getgems网站下的TON onboarding challenge集合名称)。
接下来,我们将在您的挖矿程序中打开 index.ts
文件,并创建一个由初始常量组成的主要函数,如下所示:
import {Address} from "ton"
async function main () {
const wallet = Address.parse('YOUR_WALLET_ADDRESS');
const collection = Address.parse('COLLECTION_ADDRESS');
}
main()
使用异步 main() 函数
在创建 TON NFT Miner 的过程中,将向公共API执行几个请求,以将响应中继到正确的代码字符串,以换取所需的指令。通过利用 Async/await 函数,可以大大提高代码的简洁性。
地址解析
在 TON 上,智能合约地址采用不同形式,使用多种标志位类型。在这个特定的背景中,我们将使用“用户友好地址形式”。话虽如此,如果您想了解更多有关不同智能合约地址类型的信息,请随时查看我们文档中的这个附加 资源。
为了让矿工正常工作,我们需要添加两种不同的智能合约地址类型。它们包括:
ton.js
SDK 中的 Address.parse()
命令允许开发者创建一个地址对象,简化地址从一种形式到另一种形式的转换过程。
连接到 API 提供商
在这一步中,我们将使用脚本中的特定命令通过 TONCenter(托管在 toncenter.com 上)API 提供商连接到 TON。
了解其工作原理的最简单方法是使用 @orbs-network/ton-access。
请记住,如果您正在使用 import
添加新模块,可能需要通过在 终端 中执行 npm i @orbs-network/ton-access
命令来安装它们。
我们在 index.ts
脚本中使用 TonClient 和 @orbs-network/ton-access
中的 getHttpEndpoint 添加 client
和 endpoint
:
import {Address, TonClient} from "ton"
import {getHttpEndpoint} from "@orbs-network/ton-access";
// ... previous code
// get the decentralized RPC endpoint in Testnet
const endpoint = await getHttpEndpoint({
network: "testnet",
});
// initialize ton library
const client = new TonClient({ endpoint });
建议使用 RPC 节点提供程序,或者为此运行自己的 ton-http-api 。详细信息请阅读 TonCenter API 页面。
从 TON 区块链接收挖矿数据
最后,该过程的下一步是从 TON 区块链检索特定的挖矿数据。
通过查阅 README 文件来完成ton-onboarding-challenge,可以通过运行 get_mining_data
方法获取最新的 TON 挖矿数据。一旦启动,结果将如下所示:
我们应该收到一个具有以下字段的数组:
(
int pow_complexity,
int last_success,
int seed,
int target_delta,
int min_cpl,
int max_cpl
)
在 TON 上运行智能合约的 Get 方法
通过使用 ton.js
,可以运行 callGetMethod(SMART_CONTRACT_ADDRESS, METHOD)
函数。
运行以下代码将导致如下控制台输出:
// ... previous code
const miningData = await client.callGetMethod(collection, 'get_mining_data')
console.log(miningData)
此外,要运行脚本,需要在终端中输入以下命令(在此处):
npm run start
为避免意外问题,请确保您已完成所有先前的步骤,包括输入合约地址。
好了!只要上述过程执行正确,就能成功连接到 API,并在控制台中显示所需数据。正确的控制台输出应该如下:
{
gas_used: 2374,
stack: [
[
'num',
'0x2880000000000000000000000000000000000000000000000000000000000'
],
[ 'num', '0x63984815' ],
[ 'num', '0x357401cf9b4f2386950faefd6b616264' ],
[ 'num', '0x1e' ],
[ 'num', '0xab' ],
[ 'num', '0xfc' ]
]
}
上述显示了执行过程中使用的Gas量,以及十六进制格式的数字(num)值之和。目前,这些数据并不太关键,因为有必要将十六进制输出数据转换为更易于使用的形式。
我们需要将十六进制输出转换为一些 有用的 东西。
- 要更好地理解 TON 虚拟机(TVM)的运作方式以及 TON 如何处理交易,请查看TVM 概述。
- 其次,如果您对了解 TON 上的交易和Gas费用的工作方式更感兴趣,请深入研究我们文档中的此部分。
- 最后,为了更好地了解执行 TVM 指令所需的确切的Gas值,请查看我们文档中的此部分。 :::
现在,让我们回到教程!
以用户友好的格式显示数字化挖矿的数据
在上面的部分中,我们讨论了用于接收挖矿数据所需的Gas量和十六进制格式的数字(num)值,我们注意到代码字符串中的十六进制数必须转换为更易于理解和使用的格式。
当检查给定的输出时,很明显,十六进制数的大小可能相当大。由于这里列出的特定[JavaScript限制](https://stackoverflow.com/a/307200),不可能创建一个变量并使用它们。
我们需要一种方法来进行翻译,这就是 bn.js
(JavaScript 中的大数字实现)库发挥作用的地方。Bn.js 是开发人员用来处理大于最大 JavaScript 整数值的大数字的库。让我们使用这个例子来更好地了解此过程所需的 挖矿数据:
import {BN} from 'bn.js'
// ... previous code
const parseStackNum = (sn: any) => new BN(sn[1].substring(2), 'hex');
const complexity = parseStackNum(miningData.stack[0]);
const last_success = parseStackNum(miningData.stack[1]);
const seed = parseStackNum(miningData.stack[2]);
const target_delta = parseStackNum(miningData.stack[3]);
const min_cpl = parseStackNum(miningData.stack[4]);
const max_cpl = parseStackNum(miningData.stack[5]);
console.log('complexity', complexity);
console.log('last_success', last_success.toString());
console.log('seed', seed);
console.log('target_delta', target_delta.toString());
console.log('min_cpl', min_cpl.toString());
console.log('max_cpl', max_cpl.toString());
如上所示,miningData 的不同组件使用了一个基于堆栈的数组,其中包含了不同参数的十六进制数字(将在下面的部分介绍)。为了获得想要的值,我们添加了 parseStackNum
函数,以从十六进制数创建一个 BN(Big Number)对象。
完成此过程后,有必要在控制台中打印值。在这种情况下,一些值被打印为 BN 对象,这将使该过程更加用户友好。尝试通过运行以下命令再次运行脚本:
npm run start
以下是一个示例输出:
complexity <BN: 2880000000000000000000000000000000000000000000000000000000000>
last_success 1670924309
seed <BN: 357401cf9b4f2386950faefd6b616264>
target_delta 30
min_cpl 171
max_cpl 252
让我们介绍一下Mining Data命令,该命令用于在将挖掘数据编程到TON Blockchain时转换不同的数据参数。其中包括:
complexity
对于矿工来说是最重要的数字。这代表值的工作量证明复杂性。如果最终哈希小于复杂性,那么你就成功了。last_success
是一个 Unix 时间戳 的日期和时间表示,用于跟踪 TON 上最后一次挖矿交易。每当last_success
指标更改时,需要再次运行挖矿程序,因为在此过程中seed
也会更改。seed
表示由智能合约生成的唯一值,用于计算所需的哈希值。要更好地理解这个过程以及seed如何更改以及为什么更改的原因,请使用ctx_seed关键字(Ctrl+F,带关键字“ctx_seed”)查看项目文件夹。target_delta
、min_cpl
和max_cpl
在我们的教程中不会被使用。但是你可以在项目集合的源文件中阅读更多关于它们如何在智能合约中用于计算工作量证明复杂性的信息。
现在,我们理解了上述不同的参数,我们将在下一章中使用这些值(complexity
、last_success
、seed
)在我们的 NFT 挖矿中。
🛠 准备一个 NFT Miner
嘿,你做得很好!
在连接到TON并从区块链检索创建NFT Miner所需的必要挖矿数据后,让我们专注于实现我们目标的下一步。
在本章中,您将 准备一个挖矿消息 并 计算消息的哈希。之后,您将 寻找一个小于(<
)智能合约给出的复杂度 的哈希。
这就是Miner所做的!简单,不是吗?
准备挖矿消息
首先,我们必须通过确保正确的参数来准备一个挖矿消息,以确保此过程的有效性和数据完整性。
幸运的是,README文件允许我们检索给出正确指导来实现此目标。正如您所看到的,上面的README文件包括一个表格,其中包含某些字段和cell类型(标题为“Layout of Proof of Work Cell”),来帮助实现我们期望的结果。
cell是TON上的数据存储结构,用于多种目的,包括提高网络可扩展性和智能合约交易速度。我们不会在这里详细讨论,但如果您对cell的复杂性及其工作方式感兴趣,可以考虑深入了解我们文档的这一部分。 :::
幸运的是,本教程中使用的所有数据结构已经用TypeScript编写。请使用 NftGiver.data.ts 中的 MineMessageParams
对象来构建一个 Queries 交易:
import {unixNow} from "./src/lib/utils";
import {MineMessageParams, Queries} from "./src/giver/NftGiver.data";
// ... previous code
const mineParams : MineMessageParams = {
expire: unixNow() + 300, // 5 min is enough to make a transaction
mintTo: wallet, // your wallet
data1: new BN(0), // temp variable to increment in the miner
seed // unique seed from get_mining_data
};
let msg = Queries.mine(mineParams); // transaction builder
可能您会有疑问:表格中的 op 和 data2 在哪里?
- 在表中,data1的数值必须等于data2的数值。为了省略填写data2值,交易构建器执行了一个低级别过程(参见Queries.mine()源代码)。
- 由于
op
分类始终是常量,它已经在交易构建器 Queries 和 OperationCodes 中实现。您可以通过查看mine()
方法的源代码来找到op代码。
虽然查看源代码(./src/giver/NftGiver.data
)可能很有趣,但不是必须的。
创建 TON NFT Miners
现在我们已经完成了为我们的TON Miner准备消息的过程,让我们开始实际创建Miner的初始过程。首先,让我们考虑这行代码:
let msg = Queries.mine(mineParams);
上面我们编译了一个 msg
值。挖矿的想法是找到一个哈希 msg.hash()
,它将小于最后接收到的 get_mining_data() 中的 complexity
。我们可以根据需要多次递增 data1
。
Miner可能将无限期地运行,只要 msg.hash()
大于 complexity
(消息哈希大于工作证明挖矿复杂度)。
这是一个与TypeScript中的BigNumbers相关的代码运行示例:
let msg = Queries.mine(mineParams);
while (new BN(msg.hash(), 'be').gt(complexity)) {
mineParams.expire = unixNow() + 300
mineParams.data1.iaddn(1)
msg = Queries.mine(mineParams)
}
console.log('Yoo-hoo, you found something!')
关于Big Number(BN)函数的一些重要的要考虑的点:
- 我们从
msg.hash()
创建一个 大端 BN对象,属性为'be'
。 gt()
: 表示 大于变量 用于比较 BigNumbers。iaddn(1)
: 表示递增值。
虽然完成上述步骤后Miner将正常工作,但它的视觉上看起来不够好(尝试 npm run start
)。因此,我们必须解决这个问题。让我们开始吧。
改善 TON Miner 的外观 ✨
我们现在想让Miner看起来性感!我们该怎么做?
跟着我,我的朋友,跟着我。
为了实现我们的目标,我们将添加以下命令:
let msg = Queries.mine(mineParams);
let progress = 0;
while (new BN(msg.hash(), 'be').gt(complexity)) {
progress += 1
console.clear()
console.log(`Mining started: please, wait for 30-60 seconds to mine your NFT!`)
console.log(' ')
console.log(`⛏ Mined ${progress} hashes! Last: `, new BN(msg.hash(), 'be').toString())
mineParams.expire = unixNow() + 300
mineParams.data1.iaddn(1)
msg = Queries.mine(mineParams)
}
console.log(' ')
console.log('💎 Mission completed: msg_hash less than pow_complexity found!');
console.log(' ')
console.log('msg_hash: ', new BN(msg.hash(), 'be').toString())
console.log('pow_complexity: ', complexity.toString())
console.log('msg_hash < pow_complexity: ', new BN(msg.hash(), 'be').lt(complexity))
来看看!让我们执行命令:
npm run start
很酷,不是吗? 😏
正确执行这些命令后,我们将拥有一个外观更易读的NFT Miner。在下一部分,我们将专注于将钱包连接到Miner,以创建一个可以接受和发送TON区块链交易的支付通道。
🎨 准备一个交易
接下来,我们将概述编译消息并使用您的Tonkeeper钱包将其发送到区块链的步骤。 接下来的步骤将指导您完成在TON上挖掘NFT的过程。
创建支付链接
为了确保NFT挖掘过程正确进行,并且用户可以正确存储他们的NFT,我们必须创建一个能够同时与TON区块链和Tonkeeper钱包互动的支付链接。
为了实现这个目标,我们将使用ton://transfer/<address>
格式来启动创建支付URL(支付链接),该格式可以使用不同的参数进行自定义。这将非常有效,因为我们想创建一个消息,并使用我们的Tonkeeper钱包将其发送到智能合约:
import {toNano} from "ton"
// ... previous code
console.log(' ');
console.log("💣 WARNING! As soon as you find the hash, you should quickly send the transaction.");
console.log("If someone else sends a transaction before you, the seed changes, and you'll have to find the hash again!");
console.log(' ');
// flags work only in user-friendly address form
const collectionAddr = collection.toFriendly({
urlSafe: true,
bounceable: true,
})
// we must convert TON to nanoTON
const amountToSend = toNano('0.05').toString()
// BOC means Bag Of Cells here
const preparedBodyCell = msg.toBoc().toString('base64url')
// final method to build a payment URL
const tonDeepLink = (address: string, amount: string, body: string) => {
return `ton://transfer/${address}?amount=${amount}&bin=${body}`;
};
const link = tonDeepLink(collectionAddr, amountToSend, preparedBodyCell);
console.log('🚀 Link to receive an NFT:')
console.log(link);
让我们运行上述脚本来启动新创建的支付链接。现在是时候让运行在您PC上的支付链接与智能手机兼容了。让我们开始吧。
创建智能手机兼容的支付链接
作为解决方案,世界上最聪明的头脑专门为终端使用创建了一个二维码生成器。使用这个工具,您可以简单地从您的Tonkeeper钱包扫描支付链接来发送交易。
npm install qrcode-terminal
最后,我们需要将支付链接(link
)编码为二维码,并在控制台中打印出来。这可以通过以下步骤完成:
const qrcode = require('qrcode-terminal');
qrcode.generate(link, {small: true}, function (qrcode : any) {
console.log('🚀 Link to mine your NFT (use Tonkeeper in testnet mode):')
console.log(qrcode);
console.log('* If QR is still too big, please run script from the terminal. (or make the font smaller)')
});
你能感受到空中的体验吗?那就是您在成为TVM开发人员的路上。
现在我们已经成功地编码了二维码生成器和支付链接,我们需要获得一些Testnet Toncoins来发送我们的第一笔交易。这是通过使用TON区块链上的TON代币水龙头(一个分享Testnet Toncoins的Telegram Bot)来完成的,这样我们就拥有了一些可以发送到我们创建的Tonkeeper钱包的代币。
⛏ 使用钱包挖掘 NFT
在TON上挖掘NFT有两种主要方式:
简单的:测试网 NFT 挖掘
以下是启动您的第一个测试网交易以挖掘NFT所需的步骤:
- 在您的Tonkeeper钱包中激活测试网模式
- 将我们的测试网钱包地址从Tonkeeper输入到
index.ts
中的wallet
变量 - 将Testnet上的NFT集合地址输入到
index.ts
中的collection
变量
通过代币水龙头充值钱包余额
为了进行下一步,我们需要获取一些TON测试网代币。这可以通过使用测试网水龙头来实现。
挖掘测试网 NFT Rocket
为了在测试网上成功挖掘NFT Rocket,必须遵循以下步骤:
- 打开 您的手机上的Tonkeeper钱包(应该持有了一些新接收的TON测试网代币)。
- 选择 钱包中的扫描模式来扫描二维码。
- 运行 您的Miner以获取正确的哈希(这个过程需要30至60秒)。
- 扫描 Miner生成的二维码。
因为可能有其他开发者正在尝试进行相同的过程来挖掘他们自己的NFT,您可能需要尝试几次才能成功(因为另一个用户可能在您之前挖掘到下一个可用的NFT)。
在开始这个过程后不久,您将成功挖掘到您在TON上的第一个NFT(它应该出现在您的Tonkeeper钱包中)。
欢迎上岸,真正的TVM开发者!你做到了。🛳
真正的:主网 NFT 挖掘
嘿!对于那些希望在TON主网上挖掘NFT的人来说,应该遵循以下指示:
- 您已在Tonkeeper中激活了主网模式(应该至少持有0.1 TON)。
- 将我们的主网钱包地址从Tonkeeper输入到
index.ts
中的wallet
变量 - 将主网上的NFT集合地址输入到
index.ts
中的collection
变量 - 更改端点为主网,启用orbs-access,在getHttpEndpoint中排除测试网参数:
// get the decentralized RPC endpoint in Mainnet
const endpoint = await getHttpEndpoint();
挖掘主网 NFT Rocket
就像我们在测试网NFT火箭挖掘过程中概述的那样,为了在主网上成功挖掘NFT火箭,必须遵循以下步骤:
- 打开 您的手机上的Tonkeeper钱包(记住,应该持有一些TON代币)。
- 选择 钱包中的扫描模式来扫描二维码。
- 运行 您的Miner以获取正确的哈希(这个过程需要30至60秒)。
- 扫描 Miner生成的二维码。
因为可能有其他开发者正在尝试进行相同的过程来挖掘他们自己的NFT,您可能需要尝试几次才能成功(因为另一个用户可能在您之前挖掘到下一个可用的NFT)。
一段时间后,您将挖掘到您的NFT,成为TON区块链上的TVM开发者。完成了,请查看您在Tonkeeper中的NFT。
欢迎上岸,TVM开发者!你做到了。🛳
🧙 接下来
首先,休息一下!你完成了一项大任务!你现在是一名TVM开发者了。但这只是漫长道路的开始。
参阅
在完成TON入门挑战并成功挖掘NFT后,考虑看看这些详细介绍TON生态系统不同部分的材料:
- 什么是区块链?什么是智能合约?什么是gas?
- TON Hello World:逐步指导编写您的第一个智能合约
- 开发智能合约:简介
- [YouTube] Ton Dev 研究 - FunC和Blueprint
- 如何使用钱包智能合约
- FunC之旅:第1部分
- 饺子销售机器人
- 铸造您的第一个Jetton
- 逐步铸造NFT集合
- 如何运行TON网站
你是这里的第一批探险者之一。如果你发现任何错误或感到困惑,请将反馈发送至@SwiftAdviser。我会尽快修复!:)