大家还记得吧,在我们开始 FunC 编程之旅的时候,我们很快就注意到有一个很棒的库/工具叫做Blueprint Blueprint 由 TonTech 团队维护,并得到 TON 基金会的正式支持。我们从零开始创建了自定义脚本,以确保您完全了解 Blueprint 生成的项目中到底发生了什么。 我们甚至建议您使用以下命令启动每个新项目: - npm create ton@latest$ x4 I V. W5 ], F6 v r
复制代码该命令将使用 Blueprint 为你生成一个新项目,其中包含我们在第 3 章中介绍的所有阶段的代码,也涵盖了合约开发生命周期。 在本章中,我们不会稍微优化项目的设置,因为它的工作方式与 Blueprint 生成的项目完全相同。也就是说,我们将在编译和部署方面稍微更新我们的项目,使其使用从 Blueprint 导入的组件。
) Q* F C# X. q3 N* e+ X将编译过程委托给Blueprint 首先 - 我们需要更新我们的 package.json 文件. 从现在起,我们只在这里留下一个脚本 test. 我们这样做是因为,如前所述,编译和部署将由从 Blueprint 导入的组件处理。 脚本的一部分 package.json 文件现在看起来会是这样: - "scripts": {
5 [/ d& [# H9 V1 h( R( n - "test": "jest",
, g+ ?: ^& |3 x! U, w, K4 M9 a - }
复制代码但是,如果我们运行 yarn test 命令,它只会在合约上运行测试,而不会预先编译。让我们来解决这个问题。 正如我之前提到的,我们将使用 Blueprint 的编译套件,所以让我们安装这个神奇的库吧: - yarn add @ton/blueprint --dev
: C. n# q7 d: K' n P3 q
复制代码在我们的 tests/main.spec.ts 文件中,我们必须做一些重要的更新。这就是之前的样子(在实际测试之前,我们在此仅引用开始部分):
; ]. h0 x4 ~" D/ o- import { Cell, toNano } from "ton-core";
3 k( z9 w! e4 T Q5 N' e6 v5 y8 I - import { hex } from "../build/main.compiled.json";$ q' ^; v# }3 F! C4 r& G4 Y$ F
- import { Blockchain, SandboxContract, TreasuryContract } from "@ton/sandbox";% Y3 F8 b8 q3 A; v3 a* ?! A( r/ ^
- import { MainContract } from "../wrappers/MainContract";
. p% j- s: z/ p0 Y' k0 N1 w' V - import "@ton/test-utils";/ k+ E' u0 H. {" h/ Y
5 V3 s. F' m9 P5 l- describe("main.fc contract tests", () => {
9 I& ~: c; d$ z0 v - let blockchain: Blockchain;+ l1 q0 E4 P$ V
- let myContract: SandboxContract<MainContract>;
4 M1 [: |# y" L - let initWallet: SandboxContract<TreasuryContract>;. r" X; c& L6 E8 E
- let ownerWallet: SandboxContract<TreasuryContract>;
3 ~4 ]/ H& g# |. m
: o. {) Z& s6 d% r3 T- beforeEach(async () => {
1 y$ @/ S. |- b! N5 m; c% ? - blockchain = await Blockchain.create();
2 x4 u# b2 f1 G3 w8 v - initWallet = await blockchain.treasury("initWallet");
3 f8 P4 w' q" W6 d5 {% {3 k* d - ownerWallet = await blockchain.treasury("ownerWallet");
. A" w, {) h) u - B1 D: [, M* e8 |/ ^2 f
- const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];" k" M1 C, H H# o7 u+ f
- 1 d- u: Z, M9 U) m* S0 ^
- myContract = blockchain.openContract(
6 I1 f; l) F4 ^' o8 ]1 T - await MainContract.createFromConfig(
8 z( S7 w h/ P# ~2 q - {
: i2 q& c2 d% f - number: 0,
2 C4 Y) a$ f- a - address: initWallet.address,- A, J6 l, \3 \* |$ @ V" `( e
- owner_address: ownerWallet.address,1 i/ O0 A) U& P! Z2 I
- },& U1 m; ~. Z/ x% g: k
- codeCell
~2 m9 F1 K. V7 _9 k/ g: F - )8 |- U0 x8 m4 X3 E1 Y3 w
- );# {5 r c O0 X, M
- });0 j) b' w) u- n0 T! Y$ N. y# X) w
-
) [$ B: ~/ S+ l* U4 L9 n, q -
; h: _/ W8 p1 J0 O) P! n - // ... the rest of testing code7 n2 H1 G5 `$ b' a" [
- }
复制代码我们将对其进行 3 次更新: 导入来自 @ton/blueprint 库的 compile 函数 执行 jest 的 beforeAll 钩子来编译我们的代码,并为其他钩子和测试提供代码Cell 更新 beforeEach 钩子,以使用 beforeAll 钩子提供的代码Cell。
+ _6 q3 p9 i, ~0 d
在进行这些更改后,我们的代码会发生这样的变化: - // ...other library imports# Y8 w( n7 ]9 N) G' ^3 p! m; a P
- import { compile } from "@ton/blueprint";, ~/ `) b2 u7 C3 ]9 t. \% o
- * e7 M! W$ F0 h1 o+ G# E- L0 m+ f
- describe("main.fc contract tests", () => {
( Y% `: E: V3 f( _* a0 B0 w - let blockchain: Blockchain;
. i4 e1 {2 G+ Q: x" t5 E) u2 z7 I4 X3 t& U - let myContract: SandboxContract<MainContract>;/ T/ @; J% q& ^2 V$ I
- let initWallet: SandboxContract<TreasuryContract>;
9 [5 R- V+ @9 K4 `0 h$ G - let ownerWallet: SandboxContract<TreasuryContract>;! e9 ~/ W" P5 a' t
- let codeCell: Cell;
- w* l+ i1 n* ^1 f" f - # c2 c D! N5 |7 u6 i: @$ f
- beforeAll(async () => {2 s8 n$ F4 H; H# w2 n3 l! ?
- codeCell = await compile("MainContract");
! C+ [* f8 a0 k) T. w, H - });8 K& ^0 s: k, v/ ^6 X' G
- ! U9 N4 _# q+ }% W/ z K2 M
- beforeEach(async () => {
: }/ a" [; E) l b - blockchain = await Blockchain.create();; A" ~ {- Q( q& A. ~ O( j9 Z: t
- initWallet = await blockchain.treasury("initWallet");! F! W; X8 `% `2 A* N
- ownerWallet = await blockchain.treasury("ownerWallet");
N1 e4 b& E7 l& y1 d V0 x
1 P3 {+ }7 `' e0 n$ F- myContract = blockchain.openContract(
) Z" e+ L. P* o' P - await MainContract.createFromConfig(
7 o3 q% b N+ X - {) [( M- U4 o1 ]! s
- number: 0,* b$ L! r. o: h7 r5 t
- address: initWallet.address,* F3 _6 u# I+ u5 Y
- owner_address: ownerWallet.address,
' X4 D( A- u; ? - },6 F$ ?* H/ F) M( Z7 C- ~! v
- codeCell% M' E D" E6 W+ A$ Z
- )% X/ b) J( c' n' R, i+ Z
- ); e5 a: c1 L. X% u) }
- });
; c! h& M6 y6 I, w -
{' ?* W* @: q/ w0 t$ @: [ ` - // ... the rest of testing code
5 P. {8 l& l; ` C5 n - }
复制代码我们进行这些更改是为了让您习惯运行 npm create ton@latest 时生成的代码。我们正在将编译过程委托给Blueprint 库。
不过,我们还需要向Blueprint 提供一样东西 - compiler config. 为此,只需在 wrappers 文件夹,并将其命名为 MainContract.compile.ts。 下面是该文件的内容: - import { CompilerConfig } from "@ton/blueprint";
2 f# }$ p( O, Y4 B6 v- I9 h) r5 A- h
$ I/ e( {+ q) x( _- export const compile: CompilerConfig = {$ z& }8 W7 S7 A2 T
- targets: ["contracts/main.fc"],( G$ x0 F; x- O: H* v0 Y( e/ b
- };
复制代码此时,您只需删除编译脚本文件 (scripts/compile.ts) 并在项目根目录下运行 yarn test 。您将看到我们的测试正在正常执行!恭喜您! 将部署流程委托给 Blueprint接下来,我们需要使用 Blueprint 的超级功能来更新部署脚本。快速免责声明--我们将使用包装器 MainContract 及其方法 .createFromConfig。这样,我们就能在部署前轻松设置合约的初始状态。 Blueprint 为部署带来了惊人的功能。它以交互方式要求我们选择网络(testnet/mainnet)以及要部署合约的钱包类型。如果你经常开发和部署,这个功能就会非常方便。 在更新部署脚本之前,我们需要做的一件事是在包装器上创建一个名为 sendDeploy 的新方法。正如你所记得的,我们需要使用包装器与我们的合约进行交互。这就是我们方法的代码: - async sendDeploy(provider: ContractProvider, via: Sender, value: bigint) {! x, v) `8 V2 e" s! p- m
- await provider.internal(via, {
0 V7 S, h- B6 G9 m% c( E, E - value,0 y( d$ k# O% {- B2 C! a! g
- sendMode: SendMode.PAY_GAS_SEPARATELY,
! p4 N% p1 \, P/ Z* n+ Q - body: beginCell().endCell(),5 |$ D- F+ w _8 C: H0 i: C
- });: j |5 s: D- p4 j4 V
- }
复制代码正如你所记得的,部署一个合约就像向其预定地址发送初始数据和代码一样简单。在第 3 章中,我们手动完成了这一工作,因此你可以深入理解其工作原理。现在,为了优化起见,我们将其划分到包装器和Blueprint 中。 我们将用新代码完全替换我们的代码,使其符合Blueprint 的功能。 下面是我们的 scripts/deploy.ts 现在的样子: - import { address, toNano } from "ton-core";- j" k% e2 O/ l* m e$ D5 h
- import { MainContract } from "../wrappers/MainContract";: Z5 h, r1 S9 P$ w. ?& K5 m; R" c
- import { compile, NetworkProvider } from "@ton/blueprint";) ~9 |7 v3 R7 ~' a
L" r5 r( x+ p- export async function run(provider: NetworkProvider) {
' f! K7 |6 b3 w! l2 ^ - const myContract = MainContract.createFromConfig(
; R* I5 ]! C4 r/ H% l5 g; \% g - {6 V! \3 p# s/ f9 W+ Q% U% a0 M+ h
- number: 0,
$ }7 W+ i9 H& g8 F+ F - address: address("kQDU69xgU6Mj-iNDHYsWWuNx7yRPQC_bNZNCpq5yVc7LiE7D"),7 I/ s3 I/ i |2 V. f
- owner_address: address( S8 x' {' D+ L. i" M
- "kQDU69xgU6Mj-iNDHYsWWuNx7yRPQC_bNZNCpq5yVc7LiE7D"8 @) I1 j% T- I J9 u, X" x
- ),2 K( P' L, o7 M: N* U1 d7 S
- },
2 ^1 A7 j. i$ }# I% E# k5 \2 R - await compile("MainContract")
# p& G& l, M) q% @+ K& c" A - );7 {: X; @6 y. r3 G4 o, x
4 \5 X# O6 |; c- i {+ S- const openedContract = provider.open(myContract);
) K+ P8 S( o+ k3 W
; m% ^8 \2 z7 Q4 `8 @0 |4 g y- openedContract.sendDeploy(provider.sender(), toNano("0.05"));: r) G i2 C7 E) B% P2 _
- 3 z6 _" M& ]- J% }
- await provider.waitForDeploy(myContract.address);
% F; G" E) D3 `8 N - }
复制代码正如你所看到的,这一切都简单多了,而且我们可以从Blueprint 中省去许多以前必须处理的事情。 其中最酷的一点是,现在我们可以使用包装器中的 .createFromConfig 方法来启动我们将要部署的合约,并通过传递给它的 config 对象获得初始状态数据。 还有两件事值得解释,它们与配置对象的内容有关。 核心部署功能由 NetworkProvider 类型的提供程序处理,当Blueprint 触发我们的部署脚本时,该类型由Blueprint 传递到 run 函数中。 如果您希望它们与Blueprint 一起工作,那您的所有脚本都必须导出 run 函数. 让我们先回到包装器的 .sendDeploy 方法。看看 body 参数。你可以看到,我们发送的消息正文是一个空Cell。根据我们的合约逻辑,如果信息正文没有设置操作码,即使合约已成功部署,消息也会被退回。稍后您将看到这种行为。 借助BluePrint部署合约让我们进入项目根目录,运行一条命令: - yarn blueprint run5 R/ ?) G J- B
复制代码运行此命令后,您会在其中看到这样的内容: 2 G8 y3 l8 f* n7 [- A0 B" p! S
- user$ yarn blueprint run
6 `; [* A8 I) V& u - H) y$ P% w" \. q
- yarn run v1.22.11
1 h7 a3 N) k' h0 [ - 4 E4 x" R" Z/ S$ |; Y
- ? Choose file to use (Use arrow keys)+ U. }% ^2 n T6 v- K9 Q" q& }* I
- ❯ deploy
5 z. k, C/ J$ o" k* n% X2 @ - onchaintest
复制代码你会看到 Blueprint 通过命令行提示我们选择要运行的脚本。此时,我们仍有 onchaintest.ts 脚本,所以它也会被提供给我们执行,因为 Blueprint 正在捕捉来自 scripts 文件夹所有可用的脚本。我们暂时不会创建任何链上测试,因为在下一章中,我们将创建真正的网络接口来与我们的合约进行交互。你现在可以暂时删除 scripts/onchaintest.ts 文件。
选择脚本 deploy. 系统会提示您现在选择网络: - yarn blueprint run* [$ g2 J+ H, J+ {7 e
- * } D$ _. G" ~& J
- yarn run v1.22.11
( z2 w3 r q; X1 b, v - # [- m) T' ]$ f0 h3 @* n
- ? Choose file to use (Use arrow keys)6 I3 y, r$ e/ X" ^7 X1 ~ [
- ? Choose file to use deploy
) P: T E; ]8 U - ? Which network do you want to use? (Use arrow keys), ?8 V+ X) w$ l C# Z
- ❯ mainnet ( h" d& W! Q$ z- q+ |2 _
- testnet
复制代码选择网络 testnet. / S' x1 y" e" J
- yarn blueprint run5 ?- |0 P/ T. [; S3 g" a
0 d8 P; q1 m5 I; m- m# r. \3 ~" f- yarn run v1.22.115 L( c+ F' [$ L' f/ C1 |8 @3 t
( D& |, [; o+ B: N- ? Choose file to use (Use arrow keys)
+ K! i% g; k2 O. f/ B) j - ? Choose file to use deploy6 N. f4 X! f5 c7 n; k9 {) {" v
- ? Which network do you want to use? 5 I/ M5 N- ~/ U& z7 C& r
- ? Which network do you want to use? testnet
/ o2 z! Z7 G9 v- v4 W - ? Which wallet are you using? (Use arrow keys)
( R5 P$ I! \6 `% Z+ z) C7 \ - ❯ TON Connect compatible mobile wallet (example: Tonkeeper) - z( [/ S2 Z/ i% K3 H: `
- Create a ton:// deep link ( |+ U$ m$ T+ j7 B
- Tonhub wallet # b- v4 H/ k, ^; R
- Mnemonic
复制代码现在,系统会提示您选择使用哪个钱包来部署合约。在第 3 章中,我们只使用了一个钱包(TON Whales的Tonhub),而且是以最简单的方式--创建一个深度链接。 Blueprint 可以让您使用任何类型的钱包,并保持最佳会话状态。也就是说,它的工作方式是授权你的本地项目从你的钱包请求交易,然后批准它们。 在本教程中,我们将再次使用 Tonhub 链接测试网和主网。 打开 Tonhub 钱包应用程序。 系统会要求您扫描二维码,授权应用程序与钱包进行交互(请求交易)。 6 x; D( m+ A1 n- E
- yarn blueprint run* V3 P r0 e; U; ^ M1 C. K2 [2 Y
- yarn run v1.22.11
2 ]% S' s6 @# r1 C - ? Choose file to use (Use arrow keys)5 B/ ]% o+ `& O
- ? Choose file to use deploy
; H: [, q6 s; t& W5 t6 M - ? Which network do you want to use? (Use arrow keys)* C* R& a; K) n- h! n
- ? Which network do you want to use? mainnet
+ G5 x6 _* e$ h9 v- w( ~! ~% T - ? Which wallet are you using?
+ }, F# [ _8 {* Z+ T - ? Which wallet are you using? Tonhub wallet1 H8 |0 c7 W2 d3 {5 T9 s M: g
" v, l- X! ~* ]8 ~* w7 p- 1 [) r0 M( l* B/ I5 u* a# R/ W
- ton://connect/GzhvQ-zwLhYNxzlEPEf-hP63kgR_0_O8vXsM8mSqQ-0?endpoint=connect.tonhubapi.com, j: D- Y$ Y: x
- ' _6 J0 j& p1 w+ P
- Connected to wallet at address: EQC7zjln0_fghMQg0A-ZhYFar3DU1bDW9A4Vi5Go5uu-tAHe
复制代码扫描二维码并在应用程序中授权后,您将被要求在 Tonhub 应用程序中签署交易。完成后,您将在命令行中看到以下输出: - yarn blueprint run4 b" R' M5 D# v8 u
- yarn run v1.22.11: ~ t6 F. y8 p* |9 A/ \4 e- E
- ? Choose file to use (Use arrow keys)* t0 H% h! s! |! W, X+ D
- ? Choose file to use deploy9 X2 n) p3 l, X2 g
- ? Which network do you want to use?
' P( j- i7 W2 J' ?1 S - ? Which network do you want to use? testnet& T7 d. R6 j w3 ^* D+ F0 L
- ? Which wallet are you using? * {: e4 u2 g! ~' q
- ? Which wallet are you using? Tonhub wallet
( ^& i! [: D) y0 [& l5 Z8 [3 j2 r - Connected to wallet at address: EQDU69xgU6Mj-iNDHYsWWuNx7yRPQC_bNZNCpq5yVc7LiPVJ
$ c$ j6 }0 ~1 F" p - Contract deployed at address EQCS7PUYXVFI-4uvP1_vZsMVqLDmzwuimhEPtsyQKIcdeNPu
" k7 u. i L. T) o6 Q2 ^ - You can view it at https://testnet.tonscan.org/address/EQCS7PUYXVFI-4uvP1_vZsMVqLDmzwuimhEPtsyQKIcdeNPu
6 @ [, \3 U* r5 y4 L - ✨ Done in 19.84s.
复制代码 L( E' T, k0 C! i* u9 t5 Y
4 U- p: f4 W& `) C) v3 n2 X
# N3 b1 U. ~* g4 w0 n
; ~& J5 k: K, t, G, Z6 \' c
: a9 p! n) }4 l4 @3 @+ D- `. x$ j5 b. q; P o' ]* s" E
|