使用测试网 您已经听说过测试网,它是一个独立的网络,用于测试开发人员可能遇到的所有问题。 该机器人会询问您的地址,因此您需要有一个可以测试 TON 币的钱包。你可能熟悉 Tonhub 和 Tonkeeper 等钱包。Tonkeeper 和 Tonhub 都嵌入了测试网功能。你可以同时使用 Tonhub 或 Tonkeeper,但为了方便举例说明,我将使用 TonWhales 团队制作的 Tonhub 钱包。它同时适用于 iOS 和 Android. 请点击其中一个链接,将其下载到您的手机上。 安装好 Tonhub 后,点击底部菜单最后一个标签上的应用程序版本号约 7 次,该标签上有一个名为 "更多 "的齿轮图标。然后,点击网络选择器,将网络切换到测试网(Testnet)。现在,返回 "主页 "选项并复制您的 TON 币钱包地址--使用此地址在以下设备上接收测试网代币 testnet giver Telegram bot. + W( {- V& O, s& m: l
部署脚本现在,让我们在我们的 scripts 资料夹 - deploy.ts. 大家都记得,要部署合约,我们需要: 计算合约的未来地址 创建合约的初始状态(代码和初始数据) 编写一条携带初始状态的信息 向区块链上的合约未来地址发送所编写的信息 * |& m b' j% d; H9 r# `' u
下面是我们的部署脚本在实际发送部署信息之前的样子: - import {hex} from "../build/main.compiled.json";9 Y. Y# f& S: c
- import {Cell, contractAddress, StateInit} from "ton";
4 R" Q0 r1 d) S, `) u: E - 5 A3 s: d% C. N$ Q% P
- async function deployScript() {
1 g3 s+ x" b( p2 y; d- n
* c* V- |" w$ `5 m7 I( ^% Y5 N- const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];$ S: b( r0 y4 q) Q6 Z
- const dataCell = new Cell();
: ]/ W1 ~, P+ B; N6 E& } - 2 n% f. O1 k3 f) x
- const stateInit: StateInit = {
7 F# O* l' U4 {- { - code: codeCell,
1 N0 C6 H: k& z( G3 D9 ^. A' Y - data: dataCell,: i3 L! }" }- X% ^
- };+ i8 ], A# [7 h2 i* R+ Q
- 7 N( P+ t% E$ P3 ]
- const stateInitBuilder = beginCell();
) v/ |9 L0 \( P j# Q: S1 y) ] - storeStateInit(stateInit)(stateInitBuilder);& d; z. v6 }& }) X# N' b0 |
- const stateInitCell = stateInitBuilder.endCell();
7 O4 f0 j6 }" s* C, o - w+ w- h9 J' @! R; J
- const address = contractAddress(0, {+ s' j5 h3 e; @) g
- code: codeCell,
N8 r, j* _& P! C - data: dataCell, d$ O, L9 k8 G: X, W- h
- });
v' i5 u- N( V7 Q+ X, g* Z' C - S1 j& T6 o" C p' D
- }- J- [3 X$ P0 w
6 w7 J! y8 V+ Q: y; q1 ~- deployScript();
复制代码我们已经熟悉了所有类型和实体,只有一个除外 - StateInit 类型和 storeStateInit 功能. storeStateInit 是由 @ton/core 库,它可以帮助我们创建一个包含 StateInit 的Cell。 让我来详细介绍一下这些线路的情况: - const stateInit: StateInit = {
* Q. ^5 Z' \7 a- K+ B% ` - code: codeCell,
* J5 [/ ?) M2 s& V: }+ { - data: dataCell,
/ n1 ]$ A# } b1 D; i4 {7 M - };
4 V; O2 s1 D/ E3 l5 }& L! S - $ B- r/ H6 {4 w3 R3 _
- const stateInitBuilder = beginCell();- ?- J$ H3 C7 V& C9 m" j
- storeStateInit(stateInit)(stateInitBuilder);. x# R" C2 C4 l* w- A9 D: a1 B
- const stateInitCell = stateInitBuilder.endCell();
复制代码这里使用的一个重要概念是构建器(Builder)。要成功创建 FunC 合约并使用 TypeScript 对其进行操作,您需要了解这一点。正如你在代码中看到的,我们使用 beginCell 函数返回一个 Builder(你可以悬停在 stateInitBuilder 变量上查看它的类型)。 我们将 stateInit 传递到 storeStateInit - 这样就有了一个函数,可以将提供的 StateInit 写入构建器。我们立即执行该函数,并传递上一步创建的构建器。 此时,我们已经有了一个已写入所需 InitState 数据的构建器。我们只需运行其 .endCell() 函数,就能将构建器最终转化为一个包含 StateInit 的单元格。 我们可以通过手动构建这样一个Cell来达到同样的效果。代码如下: - const stateInitCell = beginCell()& {& i5 ?# y/ F: A4 d" ~, Z4 t6 H
- .storeBit(false) // split_depth - Parameter for the highload contracts, defines behaviour of splitting into multiple instances in different shards. Currently StateInit used without it.' L; R4 \, k7 C" o
- .storeBit(false) // special - Used for invoking smart contracts in every new block of the blockchain. Available only in the masterchain. Regular user's contracts used without it.
- f/ e4 p1 L+ o- N( V& m8 g5 G - .storeMaybeRef(codeCell) // code - Contract's serialized code.% `% G Q1 ?+ S, Y5 t2 z) ]5 j
- .storeMaybeRef(dataCell) // data - Contract initial data.
* \& O6 j/ \' z& O - .storeUint(0, 1) // library - Currently StateInit is used without libs
复制代码 不过,这需要我们深入了解 InitState 的实体。您可以阅读 here in documentation.. _) t% \/ |3 M8 s: i5 ]8 M
: I7 j2 f7 w6 U& v6 ~
使用 StateInit 创建信息为了发送信息,我们需要本课开始时安装的 Tonhub 钱包。Tonhub 有一个很酷的应用程序接口(API),可以实现与钱包的深度链接,这意味着我们可以编写一个链接,其中包含发送消息所需的所有参数,然后在 Tonhub 中打开这个链接,签署发送消息的实际交易。这需要安装一个 qs 库,它将帮助我们编写带有复杂参数的 url。 运行以下命令进行安装: - yarn add qs @types/qs --dev1 O. `* m1 u9 b4 j
复制代码因为我们的 Tonhub 钱包在手机上,而链接在桌面上,所以我们只需生成该链接的二维码,然后在终端中显示,用 Tonhub 扫描并发送部署信息。为此,我们将使用 qrcode-terminal 库。 运行以下命令进行安装: - yarn add qrcode-terminal @types/qrcode-terminal --dev9 h: p* {& J* ]# Q! x
复制代码有了这两个库的帮助,我们的部署脚本现在看起来就像这样: ( G# [/ @6 ^' F# |% C n. L' |& v" E
- import { hex } from "../build/main.compiled.json";
8 j: f+ H. a' r1 W3 e% V4 _, y- o - import { beginCell, Cell, contractAddress, StateInit, storeStateInit, toNano } from "ton";5 S0 Q1 v- M# x, a8 G
- import qs from "qs";( f; e, z5 S1 L1 {+ n- C
- import qrcode from "qrcode-terminal";5 J9 r, `* P0 ]4 G
- $ C' b) g# b3 i+ s
8 _( a- M$ Q3 u$ A5 E7 B- async function deployScript() {
& B2 s9 o5 ^. e - const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];
0 |9 S9 G2 F8 v* _0 m; Y _ - const dataCell = new Cell();
! w n- F- l" `" u5 e1 V - / o; |0 [5 @. v: s: x8 ]/ a n
- const stateInit: StateInit = {- @! L- I+ Y! C3 x
- code: codeCell,- y8 Y) k3 ~: b8 Q) E
- data: dataCell,
1 w5 Y, Y2 R) H8 ^# R0 ] - }; L' ]. ]: x: a; q# P! X3 D; V: ~
- ; k) _% m( Y# m
- const stateInitBuilder = beginCell();
! m, R( w) N, e; f% x" ] - storeStateInit(stateInit)(stateInitBuilder);; h# z/ i! z0 A: I
- const stateInitCell = stateInitBuilder.endCell();0 w- t. p9 H7 m. S' u# ~
' O6 e) N3 B# q/ D# a- const address = contractAddress(0, {
, }$ i. E W7 b( {1 e9 W - code: codeCell,
3 l9 ] J2 H; C/ C# ?1 ~. S# X - data: dataCell,6 X; M5 T1 {) W( |6 E8 z
- });
8 d; }- H' ~' A$ E. m# w - + A6 y8 _0 {4 d7 u& }
- let link =" ?" |" x n# v0 t
- `https://tonhub.com/transfer/` +% a- X B6 B3 R+ m& a
- address.toString({
$ k' B( b% T" n, x - testOnly: true,
. I. e2 @' b, O# G" {; ]# o% K - }) + "?" +4 \$ m0 Z8 q7 u9 z9 ?' S7 t
- qs.stringify({
, I7 h7 W& K+ o( g* c+ C9 n' T - text: "Deploy contract",: M p) P( l4 k# Y3 {
- amount: toNano(1).toString(10),' B1 F2 H/ v: t& g8 S
- init: stateInitCell.toBoc({ idx: false }).toString("base64"),
4 C: }7 o8 c) R/ V6 R - });+ r6 ~0 z- L1 n; b6 `4 z4 O' Z! A% @
. Y- u0 n" k" Z% U. C- qrcode.generate(link, { small: true }, (code) => {% G* d0 `* @/ A3 A) h3 s8 S% V
- console.log(code);
& T4 ?( B e# O8 N( B) j" o# \ - });9 a8 T* e$ s0 @% F
- }
4 `* a( }% o: R. \
& a& T |* [( ?- V: m' B( ?5 K" p- deployScript();
复制代码正如我们在之前的课程中所做的那样,让我们添加信息丰富的控制台日志,因为你可能想在所有项目中重复使用这些脚本。 现在,我们的代码将如下所示: - import { hex } from "../build/main.compiled.json";. a. h1 e0 r2 \9 j3 x7 S8 r
- import {, o! |8 l: n+ J6 e, d
- beginCell,: b8 E, a3 h9 N: A$ O/ e9 Q* L
- Cell,
: t& l _4 t& O& a3 b9 Z, _, g0 l - contractAddress,
8 v" K( W' P: p7 X R - StateInit,
' P A* x. p3 L - storeStateInit,
$ \7 R8 p- V9 {' i* M; O - toNano,
5 E7 B" ^, \) i7 [# w3 O - } from "ton";
9 c. M- x/ Z. \. s! [- O - import qs from "qs";6 Q& C( n8 R' o
- import qrcode from "qrcode-terminal";0 q6 R" ]1 b# u. \: g
6 r* o+ V5 b6 e( | [3 t
+ |$ D9 D/ Q) ^- async function deployScript() {4 Q" w" S% }5 Z. h9 [/ w# }- h
- console.log(
* z" J3 |4 W6 a& J( r - "================================================================="3 }6 V6 {. c7 \- i3 V
- );2 m( _# o0 p# @: V, E# D
- console.log("Deploy script is running, let's deploy our main.fc contract...");
0 Y2 G5 H5 S, s. R% P
# x( @" c( c5 {' u! `) m' i+ b- const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];
: B. Y4 [# ?' E7 W) V - const dataCell = new Cell();
1 v, @2 Y2 t x8 G8 D5 r' p; v - + z. a" w9 |' A* b; ]7 c
- const stateInit: StateInit = {, a9 ~5 S! @3 ]* v
- code: codeCell,, h1 L l" U; f
- data: dataCell,
# t- L1 W# }1 K6 X4 U - };
\0 @) e# |7 x - + G# ]% T9 {, a! J9 _
- const stateInitBuilder = beginCell();
8 o3 q+ p" a w; {" _5 c5 @- B. U8 i - storeStateInit(stateInit)(stateInitBuilder);
4 E3 ]4 z6 D3 Y+ n6 |2 ? - const stateInitCell = stateInitBuilder.endCell();
$ i5 `' X" }8 _7 p
; I, t7 g: W" M& o7 w- const address = contractAddress(0, {1 |% M7 ^5 Z' A4 j
- code: codeCell,) s& O) p1 _3 E1 Z+ Y/ F
- data: dataCell,8 e0 J+ N& z, u( P: z2 e* v$ ?8 x
- });' V" d. D3 m# r0 I; m
- w$ m+ V( p- K
- console.log(
0 G& |0 m+ C9 F4 C - `The address of the contract is following: ${address.toString()}`$ \5 y; o" S- s/ p1 |4 ?4 C3 J5 b! r
- );
' T% e. X& `) R' ~. G6 S2 \) \4 A- d0 V - console.log(`Please scan the QR code below to deploy the contract:`);9 q5 N" x" ` X* ~7 e6 X2 X
- 7 N: N7 c8 g; t
- let link =7 O, m Y/ B9 n- u
- `https://tonhub.com/transfer/` +
; l7 u; D% P3 L - address.toString({
( F! T5 c' s b; ^ - testOnly: true,
+ u$ q% U D3 n" V( B - }) +
9 r! D E: h6 ]% D6 [2 F( e- H - "?" +
- M% D* T! R6 b% C7 E - qs.stringify({: s; e' h4 l+ P/ p
- text: "Deploy contract",4 X4 V+ a4 P) L" Z' `( b |* o1 m
- amount: toNano(1).toString(10),+ J, c. G5 X+ n7 E
- init: stateInitCell.toBoc({ idx: false }).toString("base64"),' [) W3 |" a% e) L1 f8 r
- });0 S8 X* Y( ^3 v/ ~
& v& s" e" e$ \6 g0 G0 A- qrcode.generate(link, { small: true }, (code) => {9 C2 j0 ]: r0 N) a! a# i
- console.log(code);
S2 k5 O" o* E! N* C - });
2 I; {+ \) a9 Y9 Z- _: Q - }
: p( t* h5 V1 Y: [ - ! A8 U; I% A( @- [! h% r8 P {
- deployScript();
复制代码 同样,我们将立即在 package.json 文件中创建另一个脚本运行快捷方式。让我们把这个脚本叫做 deploy:testnet,这样以后我们就可以在部署主网时使用 deploy 这个名字了:
! J5 }- h1 ^# {' g8 e' i- {
! |6 S. a" C" S% \+ a - ... our previous package.json keys
' p N7 H, d J4 q# G ]- C - "scripts": {
. a" t" | v- {( y - ... previous scripts keys
6 X; M. P$ l; a$ a3 M; R - "deploy:testnet": "yarn compile && ts-node ./scripts/deploy.ts"$ J. C+ |1 ^+ o u& M+ p! w
- }
v' c" ], u1 @; F* @# P* U4 K - }
复制代码让我们运行部署脚本 (yarn deploy:testnet) 并使用通汇手机应用程序扫描二维码。确保您在 Tonhub 的这个钱包里有您的测试网 TON 代币。 在浏览器中打开测试网版本,并在搜索栏中输入智能合约的地址。这将打开智能合约页面。如果您已经按照我说的一步步完成了所有操作,您将会看到一笔 1 Ton的单笔交易,该交易已经将我们的合约部署到了区块链上。 恭喜您 您的第一份 TON 智能合约已部署完毕! 部署后的链上测试是的,你已经成功了,但还有一件重要的事情需要你认真对待,那就是部署后的链上测试。一旦我们的合约部署完成,我们要确保功能的行为符合我们的预期。为此,我们将编写一个脚本来实际测试我们的功能。 不过,在运行该测试之前,我们需要确保我们的合约已经部署。为此,我们需要访问 TON 区块链的当前状态。有一个很棒的工具可以让我们连接到几乎所有可能类型的 TON 区块链 API - TON Access 由 Orbs team 提供. 首先,创建一个文件 onchaintest.ts 在我们的 scripts 文件夹。 在 package.json 文件中添加脚本运行快捷方式: - {; a( u$ W) M. K. W; O- d
- ... our previous package.json keys
% ~3 Y; x4 X+ t& G+ |. r) U7 D2 V - "scripts": {
4 b7 {: v# L% n* x, `5 { - ... previous scripts keys+ _# ]" B- h' |5 P& a
- "onchaintest": "ts-node ./scripts/onchaintest.ts"
# O4 A/ l/ }* H: z2 _1 [) D - }
" T$ q V, S3 ~$ z: v6 e7 k( X - }
复制代码要检查合约是否已部署,我们需要它的地址。我们希望我们的设置使用方便,只需最少的手工操作,因此我们不会对部署前看到的地址进行手动编码。我们将独立计算地址,因为我们知道地址可能会根据合约代码及其初始状态数据发生变化。 当我们要获取合约的地址时,我们的初始代码就是这样的: - import { Cell, contractAddress } from "ton";
% o( t! |. X* t3 } - import { hex } from "../build/main.compiled.json";
6 f9 ^8 F! a# d6 [ - & y: G. U5 w2 j1 h
- async function onchainTestScript() {( L3 u( h& g% ?0 [1 y7 R
- const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];
* Z! z: p- C) C0 A - const dataCell = new Cell();8 Y8 g5 S+ E& H! U2 l5 ]- _
0 T9 v& d. X. h- E2 a% U) l6 d- const address = contractAddress(0, {, R. Z& m6 l5 _8 C
- code: codeCell,, W$ V4 t5 a& B
- data: dataCell,, y! q! A2 A) t+ V& r
- });
( t7 ?; Q" Y! g4 M; r; O - }
2 V* e2 v0 w' f8 z
! a- o# ~1 w, D, O" `( t/ a- onchainTestScript();
复制代码 首先安装 @orbs-network/ton-access 库:
. t6 L1 b' N; M' j) D4 v% q! V- t- yarn add @orbs-network/ton-access --save8 Y5 s) o8 o5 {+ Y" s, V
复制代码现在,我们更新代码,加入通过地址检查网络上合约状态的逻辑:
& u4 w) J8 W& r+ c" E- import { Cell, contractAddress } from "ton";2 h3 c" ~4 O2 v/ C* e9 W t
- import { hex } from "../build/main.compiled.json";( p5 `9 K2 f) ?, e! T" u5 b3 a) U
- import { getHttpV4Endpoint } from "@orbs-network/ton-access";% h* @/ u/ e- O
- / ~7 F: l2 X* e- O1 {: ?
- async function onchainTestScript() {. }- {. z' ]7 H! [5 J
- const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];( W" G5 E$ I6 \; }8 L7 M8 V
- const dataCell = new Cell();
0 I& U2 Y' z. v: Z - 7 B( N8 o' m; T6 G2 V6 H, c
- const address = contractAddress({
! W" R/ {3 |; \ - workchain: 0,
$ p2 g$ T) s( d - initialCode: codeCell,( a. f# e- ^/ \( G3 u H
- initialData: dataCell,
) \& Q Z! e( u3 u7 q - });) G/ k# W4 a* N% Q1 S$ I u) o
- ; ^ O0 c5 E& N7 }6 ?3 k- W7 J! s2 g* P
- const endpoint = await getHttpV4Endpoint({2 v' |, y0 e# ]4 s4 f! K8 Z
- network: "testnet",
# x4 E1 i* [4 v* [( u8 ~ - });5 k. y2 s7 X1 k- N+ v1 m
- const client4 = new TonClient4({ endpoint });2 C) g& }" K# r* r6 w# g7 b
" P4 X$ k5 [$ X8 M/ L, U- const latestBlock = await client4.getLastBlock();
: I; \0 c, ~$ n- a - let status = await client4.getAccount(latestBlock.last.seqno, address);8 W$ L. f' g! M: l" }' Z& u; O
' ^# h% e0 r% X- if (status.account.state.type !== "active") {4 B/ H7 J7 W; s3 B' w
- console.log("Contract is not active");
/ x" n$ l3 m6 d! ~0 _ - return;
1 D9 t$ r2 G6 }7 p* H) E - }3 m/ b. P' L+ Z6 _7 X1 N
- 7 e7 V1 b" @& A8 f3 t2 ~
- }5 h5 m, Z9 M8 ?( F( y* D& C+ J- D
' P: Z" s2 m! `( ~0 ~- onchainTestScript()
复制代码我们再深入研究一下这段代码。这里有什么? 我们使用 getHttpV4Endpoint 函数连接到 Orbs 团队托管的 API 端点,并获取 v4 协议的客户端对象。您可以了解有关 v4 协议的更多信息 here
S- n4 v x8 k) v: O) o2 ]
到目前为止,我们必须将网络配置参数设置为 testnet。
因此,在合约尚未部署的情况下,我们可以停止脚本。 现在让我们开始实际测试。我们将创建一个简单交易的链接,向合约地址发送 1 Ton。与部署过程中一样,我们将显示该链接的二维码,您可以用 Tonhub 钱包手机应用扫描。 - let link =, ~1 ]2 K) ?" H! A" I! V
- `https://tonhub.com/transfer/` +
2 V0 j) l% B3 q3 K - address.toString({- P! C1 A7 {- |9 w# i" _
- testOnly: true,
7 f. y* f5 v& U - }) +9 q7 c( m0 J+ w/ s- J
- "?" +
% ]4 K- _4 X: R - qs.stringify({+ I+ v9 d# c, X v6 @
- text: "Simple test transaction",1 {; I/ A l& a; s
- amount: toNano(1).toString(10),! }. p1 C7 } M3 D/ d8 A( H
- });
3 f3 P( f- J# S1 i8 ` - 0 w% { w& q# x
- qrcode.generate(link, { small: true }, (code) => {
; ]( {2 d% u5 ^9 G( i - console.log(code);
- t2 a8 b) `5 I# Z' ?1 | - });
复制代码我们还必须导入 toNano 从 @ton/core 库. 它可以将Ton币转换为成nanos。一个TON币有 1 000 000 000 nanos 中。在TON币中,TON币的数量总是nanos为单位。为了便于使用,大多数情况下我们会将nanos单位作为一个字符串来使用。 另外,请注意,我们使用 toString 方法,提供指定地址来自测试网的 config 参数。 在显示二维码后,我们的脚本将立即开始调用合约上的 getter 方法,以接收最新的发件人地址。我们将只在最新发件人地址发生变化时在控制台记录日志,以避免在控制台记录相同的结果。 让我们先来看看获取最新发件人地址的逻辑代码,我们将分析其中发生的情况,然后将其集成到我们的脚本中: - let recent_sender_archive: Address;) }+ D3 n; c y3 D1 [1 f
* k- b# r( t2 X4 e* s) |0 H- setInterval(async () => {
/ z! l' M) n3 R* }0 O& K! p% M - const latestBlock = await client4.getLastBlock();+ x4 J, D' R- ?7 d% U
- const { exitCode, result } = await client4.runMethod(
* G" j- ?5 F( D5 t X! Z5 i+ y/ c - latestBlock.last.seqno,3 M% S% \. } a: s, }$ _: c
- address,: v( b Z0 H) X/ `6 A
- "get_the_latest_sender"
8 U; B( x* o; _ g& b0 j - );
, r" M; ]0 F- v& ~6 ]( q' a ~: [ - ; h% P9 q" I6 H6 m
- if (exitCode !== 0) {
6 J8 b* j& _1 G% W0 _+ \ - console.log("Running getter method failed");8 i. F6 m% k/ Q% y5 G! I
- return;) H4 K V+ N2 [4 M' t
- }
1 r* H( e P/ y% V% ^0 y. S - if (result[0].type !== "slice") {
: \( I! m t- O) f7 s - console.log("Unknown result type");3 _ y- ~1 L2 W4 d, {
- return;! n, i7 z; O! `
- }' x: X: O6 I$ m5 N% K2 P9 l6 Y3 q3 r
- / w' q4 c0 A+ m- H" T0 ] @9 O
- let most_recent_sender = result[0].cell.beginParse().loadAddress();
7 w% V- x8 x4 x- f; C( b - $ p1 |) o9 X1 h/ Q* z5 c
- if (1 O4 p6 ^8 S' z9 @# m2 I6 _* K: w8 J
- most_recent_sender &&5 @% P7 s, w$ [+ V
- most_recent_sender.toString() !== recent_sender_archive?.toString()
+ [& j3 R C7 m; x9 O" m% g3 f# O - ) {
/ X2 s9 e! q! J$ ] ?! ` - console.log(5 A/ }- t# P4 J$ [2 Y& s5 u) d1 X
- "New recent sender found: " +1 Q; D2 _$ p1 T. k8 n3 ~. E
- most_recent_sender.toString({ testOnly: true }) y" c7 h( K) Q |
- );- Q) c9 K9 {1 R2 O
- recent_sender_archive = most_recent_sender;' H/ D* w- n( X) }7 P
- }7 R9 D7 t: O) U0 x, p
- }, 2000);
复制代码我们在这段代码中究竟要做什么: 我们将设置该函数每 2 秒重复一次 每次执行检查时,我们需要用 getLastBlock 获取最新区块的序列号 我们使用 runMethod 调用 getter 方法,我们感兴趣的参数是 exitCode 和 result。 如果调用不成功或接收到的数据不属于片段类型(如您所见,TON 中的地址总是一个片段),我们将确保脚本停止运行 我们获取 getter 方法的结果,即它的第一个项目 ( result 是一个数组,因为在其他情况下,getter 方法返回的结果可能不止一个) 我们采取 cell 参数的 result, 打开它进行解析,然后从中读取地址 我们会将地址的字符串版本与之前可能收到的发件人的字符串版本进行比较。如果两者不相等,我们会在控制台记录新的 most recent sender's address.
2 x* w9 k3 ~: \
( j. e+ o2 N. ~- s我们看看最终的代码是怎样的:7 O0 N# A7 |: s# Y# V1 ~
! B2 H0 }0 u: U& Y- import { Address, Cell, contractAddress, toNano } from "ton-core";
, D: o3 {$ }( u E9 d5 _ - import { hex } from "../build/main.compiled.json";
9 Q6 u2 n' ^" ?& U9 U. b# p - import { getHttpV4Endpoint } from "@orbs-network/ton-access";
2 m* G* j8 U* c+ Z - import { TonClient4 } from "ton";: l( }% J5 t3 e0 ?9 D
- import qs from "qs";1 Q9 g" g' O- {( F
- import qrcode from "qrcode-terminal";/ u9 E# E% q! O6 m0 _- E- Y
- ' v- Q6 M# x. f
- async function onchainTestScript() {
+ ?- `( y* \4 f6 @ - const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];
, a6 w- J/ q" V3 P8 Z - const dataCell = new Cell();; ]) h0 Y' s# }+ J4 ~ [
- + p! |$ X- W* _" W s) Q/ c+ {
- const address = contractAddress(0, {
0 t5 ~! m b. `/ |9 {- | - code: codeCell,4 W- @) z9 D8 a# c, \$ N8 b! r: J4 G
- data: dataCell,) d0 G: o4 }3 }- W5 V. M
- });5 h' I h. h% y8 ?
- 1 \, U$ V2 f# G
- const endpoint = await getHttpV4Endpoint({
! o- S' ~9 J- g) H; @, v5 J0 m - network: "testnet",9 [. V( e8 K$ D0 j, m1 k( \
- });8 e6 J9 s) {" o d4 O- m
- const client4 = new TonClient4({ endpoint });
4 Q% h2 {9 N+ l; V - " P- q* _8 E# n9 s h
- const latestBlock = await client4.getLastBlock();
8 [6 D. e. [% A1 C* J" k3 ` - let status = await client4.getAccount(latestBlock.last.seqno, address);- k b/ |: v: q. U8 X9 p
- ! S" H9 V: W6 X6 p! j1 ^& R# P9 n
- if (status.account.state.type !== "active") {- K7 {2 q. U% ]7 e: C: Z
- console.log("Contract is not active");
; H- l) s& [, x4 k - return;
$ O% e) l# M+ S; E. e: r6 { - }
6 L- W+ ]3 t _* x: v - & |7 e+ F% @/ r6 n& R
- let link =
1 d: p; X6 Q' V2 w7 |& q# I - `https://test.tonhub.com/transfer/` +
$ \9 G& I) s9 t' k" q - address.toString({
: |9 x) Z) T. t - testOnly: true,
t" Z9 _' x% S8 y - }) +7 @5 c8 `' f6 v0 s" F
- "?" +4 {# m& X+ N: M; U$ Z! u/ ?
- qs.stringify({2 | ~1 ? k a4 u/ j- G n
- text: "Simple test transaction", T6 \' a: g* }0 _" M0 O- p
- amount: toNano(1).toString(10),
, J5 Q, h; w+ A - });8 }$ X8 P, @1 P: A' z
! l+ C ~' i1 \ D- qrcode.generate(link, { small: true }, (code) => {/ [* R7 l! @2 p. i* C
- console.log(code);! I- z+ V, a: D3 h; a3 M- Y% g; Q
- });
* I9 _" V' ~ m: X; t! | j
! |3 b' j" e, [4 f- let recent_sender_archive: Address;
6 L; X* u5 K% N9 s - 2 |3 ~* @# b% m( Y* D
- setInterval(async () => {
7 N: H8 w* Z0 r2 p4 m - const latestBlock = await client4.getLastBlock();0 p1 E9 m6 E, U' p6 E5 v7 O9 t
- const { exitCode, result } = await client4.runMethod(1 R' w& @& J& J4 O5 D, a, K3 i; s( B
- latestBlock.last.seqno, _9 [; c- N* A6 p
- address,0 d" t) w% x" M$ e7 Z$ X1 z
- "get_the_latest_sender"
" C& @6 H( ?! V! `1 ?4 r6 Y( d - );
R- n$ `$ Y4 z& P; U8 g& W, g
$ {$ ?! T3 P7 ]5 i( G j" |- if (exitCode !== 0) {. i7 H3 Z, g# I) Y" }+ J
- console.log("Running getter method failed");
. \0 S d) k- q! h. l2 n" F - return;
3 p& P2 f, O" i' v - }5 R1 W5 j3 m' W# K5 K1 j
- if (result[0].type !== "slice") {+ k. S/ j' B4 u
- console.log("Unknown result type");
4 W+ M0 o$ v' [2 D" U( q5 M9 } - return;5 H- ?0 Z$ P6 i! s8 o
- }
% g0 ]# Z- Y3 N# h# M% U4 S - $ J- Q8 N. Y. O' ^+ d4 Z8 Q
- let most_recent_sender = result[0].cell.beginParse().loadAddress();
J# O! D! r W; g) j/ {
7 S% L5 z7 t9 J! n$ u' W* u: ]- if (
: K0 w& {3 H) c$ D" ]) y1 T# X - most_recent_sender &&
- F3 b4 [# C0 s - most_recent_sender.toString() !== recent_sender_archive?.toString()) ?+ D, a3 q2 H) m4 F9 Q' G4 T" N
- ) {
; c. F' `, D' A) J' [2 e5 ? - console.log(
' I0 Q; h; ], Z: v4 H& h- q) n6 V - "New recent sender found: " +9 Y9 _$ [$ L7 d: p
- most_recent_sender.toString({ testOnly: true })" H J) Y) L' I/ q9 |/ z
- );
" s/ S8 f0 q% ~ - recent_sender_archive = most_recent_sender;- ^4 f* s5 [' F
- }3 J' x& z8 J& Z. }
- }, 2000);
" T0 ?5 V1 C( [$ U# T8 _ - }' ]/ t) p* E% ]7 U% y
- 5 M5 M; r: O& ?8 P+ O$ s
- onchainTestScript();
复制代码 我们继续运行它!" t6 G4 J5 Y/ E1 k* W; q
- yarn onchaintest0 b! u# r( I6 b8 r
复制代码如果您的操作正确无误,您将在终端看到一个二维码和最新的发件人地址。它必须等于您的 Tonhub 钱包地址,也就是您用来部署合约的地址。 , A% ~8 D1 R! K3 P# p& B/ X
. F5 w3 m* E4 j
在部署合约时,我们将代码和初始状态连同 1 TON 一起发送,因此在部署合约后,代码立即被执行,并将发送者保存到 c4 存储中。( J- z! }, o- _7 a: H
为确保我们的脚本按预期运行,您可以在 Tonhub 上创建另一个钱包,在其中装入 测试网 TON 币,然后再次将其发送到我们的合约中。几秒钟后,你就会在控制台中看到更新,因为最近的发送者地址发生了变化。 " p) r% F" U7 M. H; A2 k
3 Q V; p& O0 V0 x! |2 X部署到主网让我们调整一下脚本,以便将合约部署到主网。我们必须更新所有包含测试网/主网差异的地方的代码。 现在我们更新 deploy.ts 文件的代码: - import { hex } from "../build/main.compiled.json";4 o4 L; m# b: ]' a( K
- import {
- \5 H8 a/ M. L/ O+ R5 {$ S# h - beginCell,
* w. X& ?! c$ r0 X& X, i - Cell,* C9 H2 E$ W4 ]! T8 w3 F% v
- contractAddress," T4 O# r3 y; L. }' q5 g9 q. }) `6 ~
- StateInit,+ `% l. R& |; r4 V6 i" a
- storeStateInit,
8 I) b h0 b! K6 K" X5 L1 i: D - toNano,7 e/ I8 S: Z9 @& b
- } from "ton";
4 Q1 H$ c& T/ K W# E( R - import qs from "qs";
: ~$ Y: v& k- e) Z3 g - import qrcode from "qrcode-terminal";
8 _, r2 z& F u, \ - # P) F3 Y/ N* g* Y: I
, m @8 q" h/ Y- async function deployScript() {
5 p9 K$ Z; n3 _ - console.log(
H: ]0 p5 p5 o - "================================================================="
. P3 G W( A! ]$ c0 H - );1 `$ [/ ~! @6 ~+ A
- console.log("Deploy script is running, let's deploy our main.fc contract...");7 }% Y' C; z: i& p
- d- R! F7 W+ c# }$ R- const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];# ~( O( O1 q* N+ D2 r
- const dataCell = new Cell();# ?2 q! N# \7 l# i; |( G
- # R- d* h! `# Q0 C$ n
- const stateInit: StateInit = {4 u3 S& F% Y2 S) y$ w( j
- code: codeCell,- m& n8 p @" o" b& I+ E1 r. ^1 I6 A
- data: dataCell,
) Z' ^) |$ @1 L, c - };, z3 y. X( b. I: N- p% l5 p
- 8 c# ]& ]8 n/ {" @5 I2 R
- const stateInitBuilder = beginCell();
) j3 S k8 m e - storeStateInit(stateInit)(stateInitBuilder);: E* m: T4 _# z; V0 j* R( j& \
- const stateInitCell = stateInitBuilder.endCell();, [+ {3 G% n# A+ K
- # N L9 L0 n+ o! I
- const address = contractAddress(0, {
+ Q) k) n! ]7 ]0 ?+ W - code: codeCell,
J9 O! E L0 v) N* b; m, r - data: dataCell,- {) \% K3 D0 n' D6 k. }
- });. I+ N _, N1 }7 U* F
- - C/ j" y% Z9 _( W, `6 }: \( i
- console.log(
' R; r9 u0 Y' S1 i; ^ - `The address of the contract is following: ${address.toString()}`$ C' i. B& B3 A7 K
- );
7 C) p- N' X/ L" p$ L - console.log(`Please scan the QR code below to deploy the contract:`);5 x. U- b8 ^: i' p% C5 Y: @
X* j8 ^/ g8 U0 U! S8 N+ W% h- let link =
* A* W0 T8 r& G2 L( V% h) A - `https://tonhub.com/transfer/` +) P) Z a! y: M k: j9 C. d" F
- address.toString({
3 ~5 T, [) i8 x- B' X. n - testOnly: false,
4 v0 Q4 l0 G0 p# ?1 @ - }) +
) W7 a. a8 P# P! v - "?" +
% [+ `! N8 f/ _ - qs.stringify({( r% j7 _& z" D$ y- y% h6 ^5 Z
- text: "Deploy contract",$ i v# J- r, l1 t
- amount: toNano(1).toString(10),4 p2 X' O- k5 j4 y- j
- init: stateInitCell.toBoc({ idx: false }).toString("base64"),: l8 M4 Q5 m! d9 M
- });, G1 w% |, ?. ~+ T
- 0 }8 j+ y/ E0 A6 p
- qrcode.generate(link, { small: true }, (code) => {
5 m" S) X! g% P n9 A5 E1 N - console.log(code);
2 N; D$ l2 D! ~6 O0 ^; @ - });% f. y2 ?3 k/ I9 ]; \5 \6 @9 y
- }& c- o. G o5 I) G7 o5 S
: I% p7 x4 Y+ v5 y) y1 a- deployScript();
复制代码我们只需修改一个地方的代码: 现在,让我们用同样的方法更新 onchaintest.ts 脚本: - import { Cell, contractAddress, Address, TonClient4, toNano } from "@ton/ton";, d9 Y8 @* R4 R% P! s4 t6 L
- import { hex } from "../build/main.compiled.json";
) ^) W" t# d A- c5 w - import { getHttpV4Endpoint } from "@orbs-network/ton-access";9 n" D9 t8 D; ~% _$ `
- import qs from "qs";
+ ?5 [! |5 Y H0 \; V, ?* J - import qrcode from "qrcode-terminal";# Z @5 V$ H( e8 d1 l
. ^4 O1 }$ R6 k# o. j
9 T# J$ a7 B, v$ f+ i- async function onchainTestScript() {/ C0 Q* c' U. p5 X3 E* e' w
- const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];
: `& c" j; G5 Z$ G; }- P0 y0 g - const dataCell = new Cell();
: C9 }2 f( Q9 R8 ~9 E5 v - 1 C! @" Y' F* F2 D. i M/ }* [
Y6 R; `" @! N* O/ y4 x2 _- const address = contractAddress(0, {* E. W# `, h% I; x( \
- code: codeCell,% }; T \( ^9 t7 G h. l4 B% t
- data: dataCell,
1 }" m+ H' @/ d3 l/ T - });9 u* x6 ^) g% M' i6 ^: E9 ^
- , _$ H2 y x8 J8 r; H9 T' |
- ( U2 K4 C1 m, l6 ` @( f
- . g% n" }# c7 A! |( N& C
- 7 h# F! A( `( L4 `( [
- const endpoint = await getHttpV4Endpoint({
5 t& v* B9 M$ e7 C# `; R* \4 I - network: "mainnet",# A* z' v3 z7 @. ^% c( w- F
- });% A2 N0 H+ O9 m. G0 J" C4 f9 d
- const client4 = new TonClient4({ endpoint });
1 N, `; H' W/ l# `/ s& @4 j - 7 p9 c B) `- T7 h9 H/ a
# `9 g7 ?2 `! F8 |6 l- const latestBlock = await client4.getLastBlock();5 M; K5 t* _9 b9 a
- let status = await client4.getAccount(latestBlock.last.seqno, address);$ m a; x/ U4 T$ `* Y- D" E7 n5 \
+ Y, w1 X; U; A# l' T# J; O
1 U$ f p2 l& L2 O8 ^- if (status.account.state.type !== "active") {" |# G8 P& @* _ r
- console.log("Contract is not active");
: d2 W! T. l% K5 L$ C$ D - return;: T6 `) N! G1 } M& G$ P# g
- }6 ~, `! b( {9 G! t3 @8 Z9 F
- 3 @$ l+ W. s( @3 n7 S
5 f- r& K3 C3 ~9 R. m% G; k- let link =) a: I) c$ O* k, Q
- `https://tonhub.com/transfer/` +
" D, A# u7 e* a6 ~0 g; Y$ p - address.toString({ testOnly: false }) +
( e9 B+ ^3 ]; ~2 d+ ~ - "?" +, b: h0 C. z" N
- qs.stringify({. C- [. b$ w7 M+ v, N$ N0 `
- text: "Simple test transaction",
' D' m- x$ U9 |, q9 s - amount: toNano(1).toString(10),$ d# C- h/ a5 R: j
- });
$ i, `' v. H. V: u - / I) U& C- g5 L: l4 W
- 7 c0 `# g5 y% ^6 ?
- qrcode.generate(link, { small: true }, (code) => {; ` a* k# D. u* f/ x- d
- console.log(code);( B4 y) ^# H0 u& c$ f5 D) [$ B2 S
- });4 n9 V* f: U) O: U2 }" E
9 R* V. \& {: a/ ?) C1 O! ?" s
% c- M3 T! T7 y5 b3 ?! s- let recent_sender_archive: Address;
& Z! u+ e$ _3 V' I( E) V
. I& h& o# ~! Q( p, K1 G) ^- 0 K" {# N( n& P$ j6 \- X; m( U
- setInterval(async () => {# N$ }$ A7 N I, Q$ ~6 e
- const latestBlock = await client4.getLastBlock();
7 o8 v9 Y! G {% J# h4 h# F - const { exitCode, result } = await client4.runMethod(
/ P& }' a/ Q, q1 }. ?3 p" Q6 z - latestBlock.last.seqno,
$ H. x0 m2 F5 j - address,
# v0 F2 u, C& M0 Y' d - "get_the_latest_sender"9 l) m$ [) f4 b# d; m8 ]: r. y) Z
- );- x; P0 c+ J+ h, o5 q1 x2 B& v
- , d6 G9 ?- q' I' Q- ^0 z& D9 p) H2 X
* i# F5 L% q, {8 e/ m- if (exitCode !== 0) {
8 w, o( W/ v+ [ z: h) K - console.log("Running getter method failed");
4 M% ~3 i! r7 L! Y& c - return;- M6 Z6 J0 D6 E b+ F" A. w
- }: r/ M+ p; ^: f! k) s
- if (result[0].type !== "slice") {8 n9 O8 M Z: Y# @5 r! ?( E
- console.log("Unknown result type");, W( X4 u7 w: J/ Q
- return; {* N1 Y0 @4 Q' e
- }
6 i4 q- j% M% T. l5 z; u - ' T1 y( P. S8 `2 B
- 8 t5 @ `+ y* ]8 G' ~
- let most_recent_sender = result[0].cell.beginParse().loadAddress();" |4 q* \. I9 R0 Z- \
- & k6 L+ z$ W+ ~2 W
- 8 r& p7 Y/ a3 D& p: F7 l
- if (. X' f0 P' D+ Q# @, L) X0 N& K" D
- most_recent_sender &&
% w' z7 ^/ d7 i5 Q' f/ I7 r - most_recent_sender.toString() !== recent_sender_archive?.toString()! d b& L& f9 s8 [
- ) {
% X4 x& }; x$ b8 U( I$ X5 F4 L - console.log(
0 f' M1 f5 F- p - "New recent sender found: " +
7 s# Y( s+ i0 n - most_recent_sender.toString({! N1 o, {: c- R! D
- testOnly: false,
) [' t- a" y' L, O$ Z0 [ - })2 Z7 O8 c2 O3 F4 |
- );
8 O& O0 \0 f5 x6 O3 x6 B" | - recent_sender_archive = most_recent_sender;
/ ^: n; u: \$ p4 G" W7 Z3 _ - }; ~5 Z8 U2 s/ V5 z; w
- }, 2000);
! O- x7 ]4 o# b E9 Z - }
) O9 x0 L$ R1 E* Z - 1 l5 ?( ? Z; Z5 v& |
. {* V& z2 {) J0 G& I7 r0 z- onchainTestScript()
复制代码同样,我们还需要修改 onchaintest.ts 脚本中的两处内容: 现在,我们可以在测试网和主网上部署和运行链上测试! 务必牢记,进行主网部署,您需要将Tonhub钱包切换回主网. 这适用于 iOS 以及 Android.
你成功了 说真的,我为你感到骄傲!我们已经做了大量的工作,但好在你现在已经了解了开发和部署合约的整个过程。 在下一章中,我们将深入探讨更复杂的 FunC 逻辑,并为其编写本地测试。 0 }: F7 z. S' O o! v" u
7 w3 N/ K- u# G1 N: v# q& a- w
|