大家还记得吧,在我们开始 FunC 编程之旅的时候,我们很快就注意到有一个很棒的库/工具叫做Blueprint Blueprint 由 TonTech 团队维护,并得到 TON 基金会的正式支持。我们从零开始创建了自定义脚本,以确保您完全了解 Blueprint 生成的项目中到底发生了什么。 我们甚至建议您使用以下命令启动每个新项目: - npm create ton@latest" w }/ C# H. N
复制代码该命令将使用 Blueprint 为你生成一个新项目,其中包含我们在第 3 章中介绍的所有阶段的代码,也涵盖了合约开发生命周期。 在本章中,我们不会稍微优化项目的设置,因为它的工作方式与 Blueprint 生成的项目完全相同。也就是说,我们将在编译和部署方面稍微更新我们的项目,使其使用从 Blueprint 导入的组件。 : F9 I4 | g2 i1 y
将编译过程委托给Blueprint 首先 - 我们需要更新我们的 package.json 文件. 从现在起,我们只在这里留下一个脚本 test. 我们这样做是因为,如前所述,编译和部署将由从 Blueprint 导入的组件处理。 脚本的一部分 package.json 文件现在看起来会是这样: - "scripts": {: Q9 l. F2 Z2 E. S I7 H
- "test": "jest",
! W* f' f* [! O4 H5 X0 E2 P - }
复制代码但是,如果我们运行 yarn test 命令,它只会在合约上运行测试,而不会预先编译。让我们来解决这个问题。 正如我之前提到的,我们将使用 Blueprint 的编译套件,所以让我们安装这个神奇的库吧: - yarn add @ton/blueprint --dev
5 b% ^4 [1 F& J$ A# n: X
复制代码在我们的 tests/main.spec.ts 文件中,我们必须做一些重要的更新。这就是之前的样子(在实际测试之前,我们在此仅引用开始部分): : ?+ F; D4 ^8 r* ]
- import { Cell, toNano } from "ton-core";; ~! x2 l' s3 P7 a9 E0 i7 ~. w
- import { hex } from "../build/main.compiled.json";+ G% S' i/ G y
- import { Blockchain, SandboxContract, TreasuryContract } from "@ton/sandbox";* c4 ~/ b0 S! Q" {; i4 ?1 z
- import { MainContract } from "../wrappers/MainContract";" ~8 z+ y0 C8 N# k
- import "@ton/test-utils";5 D4 j/ l% d5 T3 X" C, J6 p; v+ Q% F
- ( M: S/ {" D$ K. A* g
- describe("main.fc contract tests", () => {
; u& R# k1 h: e$ Z - let blockchain: Blockchain;4 W0 l2 y+ |) f- O* L7 M
- let myContract: SandboxContract<MainContract>;* r! i- l3 Z; {& Y
- let initWallet: SandboxContract<TreasuryContract>;
4 Q7 E2 c) d% @& P - let ownerWallet: SandboxContract<TreasuryContract>;
" V& m/ R0 ^: N- W" F" ^7 K# L - 9 S+ b* T0 B; @! @0 k5 j Y
- beforeEach(async () => {( ]2 h2 a n' C
- blockchain = await Blockchain.create();+ K% A! `3 q8 U& W; g2 k, Z! [6 y
- initWallet = await blockchain.treasury("initWallet");
" ]" n8 G; \' S4 ~# Y9 X7 S6 A - ownerWallet = await blockchain.treasury("ownerWallet");5 B+ w5 l# b9 p" J( A
( P- P1 k# s# Z! a. J: B# W- const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];. ]. W6 x7 l K, ~+ c+ c
8 X" p i9 S% e; ~+ z- {+ g- myContract = blockchain.openContract(
# w& Z* N# z% ~8 _# R - await MainContract.createFromConfig(% \* X. Z" v3 ]2 t/ u
- {
4 n X4 c: u7 l+ P% g - number: 0,
* L% a, d" ~: q' }3 X - address: initWallet.address,
4 j( d9 P' E! z: N2 a/ E- b - owner_address: ownerWallet.address,& V4 o6 m% g" w
- },
3 S, b2 P9 \3 J( O& @% X1 w - codeCell
0 E% i9 n9 W t6 a" {6 k - )2 G5 @" f4 N+ V* |, m' G7 k% s' p! F4 k
- );! H# m1 H4 H5 a" L3 B% x
- });4 W* J& {; T1 x1 e3 B" A! K
-
! }1 h$ m# h6 G6 R) B3 y -
, _& \; A, N# q9 x: C1 x - // ... the rest of testing code k" L( U, q) u. ]
- }
复制代码我们将对其进行 3 次更新: 导入来自 @ton/blueprint 库的 compile 函数 执行 jest 的 beforeAll 钩子来编译我们的代码,并为其他钩子和测试提供代码Cell 更新 beforeEach 钩子,以使用 beforeAll 钩子提供的代码Cell。
4 h( l, g, g7 Q' \# i8 m7 P) S
在进行这些更改后,我们的代码会发生这样的变化: - // ...other library imports' N- z8 N- C9 Q5 [4 G7 S6 K6 |
- import { compile } from "@ton/blueprint";
: p8 C' W5 j2 l B0 S, Z0 T' } - # U& L+ e/ F+ r5 U9 A+ O3 `
- describe("main.fc contract tests", () => {
7 m3 p9 H' o; R' w5 J2 D& G - let blockchain: Blockchain;
2 y/ f7 }! H9 K8 f, Y - let myContract: SandboxContract<MainContract>;
! v: H" H- ^9 G( p2 J) f D - let initWallet: SandboxContract<TreasuryContract>;) J2 {8 z5 d% w& a W' V
- let ownerWallet: SandboxContract<TreasuryContract>;
+ q& w3 H6 y3 \2 { - let codeCell: Cell;/ ^6 _9 k; r- R5 _
" d. W A p1 r- g' K% d0 u2 l- beforeAll(async () => {4 f7 g$ w) }8 C+ ~$ B- i
- codeCell = await compile("MainContract");0 r" ]- s/ d( c7 K# M7 \
- });
8 f/ Y$ z$ V# k - U# s3 y, I. C% g% e# j
- beforeEach(async () => {
: G7 d* l. Y8 D* v4 j - blockchain = await Blockchain.create();6 X7 f k; H" y% y
- initWallet = await blockchain.treasury("initWallet");5 R0 E i, S9 o0 e" k8 m1 L4 N( f2 |
- ownerWallet = await blockchain.treasury("ownerWallet");
7 v, C. `4 J5 P* f# J/ f/ ~
9 O& Z. y) K, }' s' O/ U- myContract = blockchain.openContract(. _% j2 T4 O/ e, p* p, U
- await MainContract.createFromConfig(" a& ?, y2 P$ V3 ^' ~( A
- {# ^) H) X. |1 q+ g/ R8 D6 \; T
- number: 0,
* r ^) N) Q2 `2 @# F- F+ s - address: initWallet.address,
5 V, z4 A! ^9 @" H - owner_address: ownerWallet.address,- ?: Y9 P B1 M8 p# w/ d
- },
8 P6 S0 C7 }9 K3 \0 a4 X - codeCell
6 y! q. `1 o. t" I }$ B9 u2 u - )$ C) S* W Z2 k# E, h. h
- );
/ j# [, ~( X: l* [- M - });' P9 w3 X* i% K7 X1 v
- 0 G5 `; P6 f+ u+ {
- // ... the rest of testing code : V! _" Y, e% ^
- }
复制代码我们进行这些更改是为了让您习惯运行 npm create ton@latest 时生成的代码。我们正在将编译过程委托给Blueprint 库。
不过,我们还需要向Blueprint 提供一样东西 - compiler config. 为此,只需在 wrappers 文件夹,并将其命名为 MainContract.compile.ts。 下面是该文件的内容: - import { CompilerConfig } from "@ton/blueprint";4 o9 a6 v' n7 ?$ W
% C- J* C$ H; k2 `9 v; n- export const compile: CompilerConfig = {
* X- A9 O4 z3 X8 i9 [6 A* ]- F - targets: ["contracts/main.fc"],8 p8 X4 P% e9 V- x8 K: I
- };
复制代码此时,您只需删除编译脚本文件 (scripts/compile.ts) 并在项目根目录下运行 yarn test 。您将看到我们的测试正在正常执行!恭喜您! 将部署流程委托给 Blueprint接下来,我们需要使用 Blueprint 的超级功能来更新部署脚本。快速免责声明--我们将使用包装器 MainContract 及其方法 .createFromConfig。这样,我们就能在部署前轻松设置合约的初始状态。 Blueprint 为部署带来了惊人的功能。它以交互方式要求我们选择网络(testnet/mainnet)以及要部署合约的钱包类型。如果你经常开发和部署,这个功能就会非常方便。 在更新部署脚本之前,我们需要做的一件事是在包装器上创建一个名为 sendDeploy 的新方法。正如你所记得的,我们需要使用包装器与我们的合约进行交互。这就是我们方法的代码: - async sendDeploy(provider: ContractProvider, via: Sender, value: bigint) {% P9 S/ T" [( L6 B
- await provider.internal(via, {
- w5 E; C* G7 L) _! y; q* c8 w - value,
% A; L+ s2 I# q: ]2 J% i% H( A - sendMode: SendMode.PAY_GAS_SEPARATELY,
7 X% h$ n8 ~# U+ y: e2 t - body: beginCell().endCell(),
- z& [! p( X* e" F7 { - });/ V) s M1 h6 `7 S6 E
- }
复制代码正如你所记得的,部署一个合约就像向其预定地址发送初始数据和代码一样简单。在第 3 章中,我们手动完成了这一工作,因此你可以深入理解其工作原理。现在,为了优化起见,我们将其划分到包装器和Blueprint 中。 我们将用新代码完全替换我们的代码,使其符合Blueprint 的功能。 下面是我们的 scripts/deploy.ts 现在的样子: - import { address, toNano } from "ton-core";
3 R5 Z" f- U. _! m. L3 O - import { MainContract } from "../wrappers/MainContract";, Q2 h# h" H$ m& i$ s
- import { compile, NetworkProvider } from "@ton/blueprint";
/ G; K' V: y6 C7 K- {
+ x3 t) d; t+ g$ _+ N+ d- export async function run(provider: NetworkProvider) {* ]* N+ b7 R9 X- U4 ]
- const myContract = MainContract.createFromConfig(9 j$ @' H6 u8 u1 m9 ^0 T
- {" P0 K; N& g, X% C S8 z4 B8 N
- number: 0,
x, U5 @7 |) k2 t- t - address: address("kQDU69xgU6Mj-iNDHYsWWuNx7yRPQC_bNZNCpq5yVc7LiE7D"),
4 Q# B9 K: f4 r - owner_address: address(6 Y% j! d7 k% s9 C1 t+ {( i
- "kQDU69xgU6Mj-iNDHYsWWuNx7yRPQC_bNZNCpq5yVc7LiE7D"
8 l7 q3 C2 D; K: F, }$ k - )," N: H5 l; D" n k9 b( N$ Z
- },
4 `& P' x1 V7 l - await compile("MainContract")0 U5 N8 Z& G& M' o4 A$ } G2 l
- );
& D2 K0 s# x* D/ M/ b
9 L9 i6 l; s# O- u' m- const openedContract = provider.open(myContract);
6 h" A! Y$ K3 b# h; G) ~
y8 R8 u# t+ a: j8 ]9 [1 k) G- openedContract.sendDeploy(provider.sender(), toNano("0.05"));
" o, T: Z k4 I1 @/ v! Z5 G - ; U: ]% s. J0 j; d8 `8 ]
- await provider.waitForDeploy(myContract.address);& b9 b# w7 |+ {( f _, t. B' ]# v% i
- }
复制代码正如你所看到的,这一切都简单多了,而且我们可以从Blueprint 中省去许多以前必须处理的事情。 其中最酷的一点是,现在我们可以使用包装器中的 .createFromConfig 方法来启动我们将要部署的合约,并通过传递给它的 config 对象获得初始状态数据。 还有两件事值得解释,它们与配置对象的内容有关。 核心部署功能由 NetworkProvider 类型的提供程序处理,当Blueprint 触发我们的部署脚本时,该类型由Blueprint 传递到 run 函数中。 如果您希望它们与Blueprint 一起工作,那您的所有脚本都必须导出 run 函数. 让我们先回到包装器的 .sendDeploy 方法。看看 body 参数。你可以看到,我们发送的消息正文是一个空Cell。根据我们的合约逻辑,如果信息正文没有设置操作码,即使合约已成功部署,消息也会被退回。稍后您将看到这种行为。 借助BluePrint部署合约让我们进入项目根目录,运行一条命令: - yarn blueprint run
0 V1 b+ K1 g, Y: J; q
复制代码运行此命令后,您会在其中看到这样的内容:
+ X) z+ i, t+ x- user$ yarn blueprint run
5 ?- U$ ]$ {( z a& J* B - , \( X5 ]) s& P& K7 U/ W K! S( L
- yarn run v1.22.11
# ^' x+ k+ t9 [( w* {
) w8 |" D) P6 E, z( I& I% p- ? Choose file to use (Use arrow keys)) ]* T8 W+ X( i8 I7 z1 F
- ❯ deploy 5 f1 n$ X2 S' t9 U; o3 T6 ?' O
- onchaintest
复制代码你会看到 Blueprint 通过命令行提示我们选择要运行的脚本。此时,我们仍有 onchaintest.ts 脚本,所以它也会被提供给我们执行,因为 Blueprint 正在捕捉来自 scripts 文件夹所有可用的脚本。我们暂时不会创建任何链上测试,因为在下一章中,我们将创建真正的网络接口来与我们的合约进行交互。你现在可以暂时删除 scripts/onchaintest.ts 文件。
选择脚本 deploy. 系统会提示您现在选择网络: - yarn blueprint run: `! y1 J' w3 F. A
" A8 `& C" ^" u6 F& q1 D- yarn run v1.22.116 `4 b# F; o! ~! \: d
( w- f1 A) S" E1 ]- p- ? Choose file to use (Use arrow keys)
. ~" V" \; q: E- k/ \# p7 e* {( J - ? Choose file to use deploy, Y# n' _# i1 D2 Y6 h
- ? Which network do you want to use? (Use arrow keys)
' s9 I# N& V6 K3 ~( Q( }8 u - ❯ mainnet & D1 Y& _9 s% k9 P* t
- testnet
复制代码选择网络 testnet.
" w( B# k$ z( ^2 T$ ]9 ?- yarn blueprint run8 I, d# }3 ]) J2 C8 ?, A$ {
- & T5 j" V% ]5 f! s6 j7 z
- yarn run v1.22.11$ D6 ]2 z) }, h- Q8 O4 b, R* s
- $ H' |" x$ ~$ I! @/ v1 K
- ? Choose file to use (Use arrow keys)( _# t$ U/ e. ~- O3 H4 Y) k
- ? Choose file to use deploy
: P7 v/ V& l" A% p - ? Which network do you want to use? / x e, w4 G; z- T8 E+ N% B
- ? Which network do you want to use? testnet
% [# C; I: @8 g: J' } - ? Which wallet are you using? (Use arrow keys)
* r9 B0 v: v+ x% C - ❯ TON Connect compatible mobile wallet (example: Tonkeeper) ; |5 [" L& t. W
- Create a ton:// deep link $ D6 O8 a7 U( A: ]) m, F6 |' Y
- Tonhub wallet
7 j5 a1 p6 ?2 s# m! O0 v" r - Mnemonic
复制代码现在,系统会提示您选择使用哪个钱包来部署合约。在第 3 章中,我们只使用了一个钱包(TON Whales的Tonhub),而且是以最简单的方式--创建一个深度链接。 Blueprint 可以让您使用任何类型的钱包,并保持最佳会话状态。也就是说,它的工作方式是授权你的本地项目从你的钱包请求交易,然后批准它们。 在本教程中,我们将再次使用 Tonhub 链接测试网和主网。 打开 Tonhub 钱包应用程序。 系统会要求您扫描二维码,授权应用程序与钱包进行交互(请求交易)。
0 H/ o! S: d$ Y/ V; ]$ X! ?- yarn blueprint run0 M7 W" H3 c/ l& m
- yarn run v1.22.11# S( O7 U" o8 ^, W: U' ^. |
- ? Choose file to use (Use arrow keys)
8 k$ d' r5 Q. V& m8 l - ? Choose file to use deploy
* A# ^, d4 U9 J7 l0 v - ? Which network do you want to use? (Use arrow keys)
0 z \4 d9 Q0 }' K: E/ u2 u- y+ t - ? Which network do you want to use? mainnet* O% p; l$ H: o' u" ]% d
- ? Which wallet are you using?
0 E2 f( L" a* K - ? Which wallet are you using? Tonhub wallet$ M, s8 I' Y2 Z6 R5 u' t8 h' Y
- {' T8 K% \7 e5 Q8 ~- 9 e+ }2 [5 U' w# ~$ s/ F2 n
- ton://connect/GzhvQ-zwLhYNxzlEPEf-hP63kgR_0_O8vXsM8mSqQ-0?endpoint=connect.tonhubapi.com( p0 R1 u: ^; ?
1 A6 M4 L1 i Z+ G- Connected to wallet at address: EQC7zjln0_fghMQg0A-ZhYFar3DU1bDW9A4Vi5Go5uu-tAHe
复制代码扫描二维码并在应用程序中授权后,您将被要求在 Tonhub 应用程序中签署交易。完成后,您将在命令行中看到以下输出: - yarn blueprint run1 E; ]: V9 V9 e% z& J5 {! L
- yarn run v1.22.11
+ j3 K% E, a4 A8 W$ s, x* M# r" e - ? Choose file to use (Use arrow keys)
) l' I3 v$ v' |! }6 B; Z - ? Choose file to use deploy
- G* ]+ @7 p) J/ r+ w - ? Which network do you want to use? " d/ |, n0 T0 U8 `
- ? Which network do you want to use? testnet
; y; p9 {- g; r" m - ? Which wallet are you using?
6 [; h% R) ^2 K$ a# C8 {! Z - ? Which wallet are you using? Tonhub wallet
8 e* |! L) f, E' h# p' r - Connected to wallet at address: EQDU69xgU6Mj-iNDHYsWWuNx7yRPQC_bNZNCpq5yVc7LiPVJ
6 H" T5 @' F, o$ v( Y4 t1 q - Contract deployed at address EQCS7PUYXVFI-4uvP1_vZsMVqLDmzwuimhEPtsyQKIcdeNPu, n7 }/ {& {+ E. {* @
- You can view it at https://testnet.tonscan.org/address/EQCS7PUYXVFI-4uvP1_vZsMVqLDmzwuimhEPtsyQKIcdeNPu
; ?+ [: v: j0 H! v# Z/ q - ✨ Done in 19.84s.
复制代码
7 j5 Z( e; w( D* m( }
4 d2 [8 Z; y, }5 c6 g% X* e6 B& y9 l6 Y1 k' h
7 l, c4 J i; A* U% L
4 R5 v- C! p- v1 n, a5 [
@7 K( [2 H# d/ i; t |