English 简体中文 繁體中文 한국 사람 日本語 Deutsch русский بالعربية TÜRKÇE português คนไทย french

简体中文 繁體中文 English 日本語 Deutsch 한국 사람 بالعربية TÜRKÇE คนไทย Français русский

回答

收藏

4.5 使用BluePrint部署合同

开源社区 开源社区 6565 人阅读 | 0 人回复 | 2025-03-08

大家还记得吧,在我们开始 FunC 编程之旅的时候,我们很快就注意到有一个很棒的库/工具叫做Blueprint
Blueprint 由 TonTech 团队维护,并得到 TON 基金会的正式支持。我们从零开始创建了自定义脚本,以确保您完全了解 Blueprint 生成的项目中到底发生了什么。
我们甚至建议您使用以下命令启动每个新项目:
  1. npm create ton@latest
    7 H4 k& Q0 q2 M+ _0 G: Z
复制代码
该命令将使用 Blueprint 为你生成一个新项目,其中包含我们在第 3 章中介绍的所有阶段的代码,也涵盖了合约开发生命周期。
在本章中,我们不会稍微优化项目的设置,因为它的工作方式与 Blueprint 生成的项目完全相同。也就是说,我们将在编译和部署方面稍微更新我们的项目,使其使用从 Blueprint 导入的组件。

  r, W% x; Z. A% T将编译过程委托给Blueprint
首先 - 我们需要更新我们的 package.json 文件. 从现在起,我们只在这里留下一个脚本 test. 我们这样做是因为,如前所述,编译和部署将由从 Blueprint 导入的组件处理。
脚本的一部分 package.json 文件现在看起来会是这样:
  1. "scripts": {9 k( t% J! y2 P1 b0 Z: n
  2.     "test": "jest",
    ; M7 c4 f7 C) f; J' X
  3. }
复制代码
但是,如果我们运行  yarn test 命令,它只会在合约上运行测试,而不会预先编译。让我们来解决这个问题。
正如我之前提到的,我们将使用 Blueprint 的编译套件,所以让我们安装这个神奇的库吧:
  1. yarn add @ton/blueprint --dev
    4 s) m) i, `6 I5 k8 b# j
复制代码
在我们的 tests/main.spec.ts 文件中,我们必须做一些重要的更新。这就是之前的样子(在实际测试之前,我们在此仅引用开始部分):
2 g% t7 {6 x2 U/ V9 C; {) o
  1. import { Cell, toNano } from "ton-core";3 I: z' r" y4 V; \, t
  2. import { hex } from "../build/main.compiled.json";
    8 S. L" S) W3 b7 {& O& m( B
  3. import { Blockchain, SandboxContract, TreasuryContract } from "@ton/sandbox";
    3 I7 P, k2 p3 f+ `: o7 \: ]* i; r
  4. import { MainContract } from "../wrappers/MainContract";4 ~: E) Q' Y' ^1 Z- p: ]
  5. import "@ton/test-utils";
    7 M  y0 S  u+ y7 q7 r) }

  6. 2 i) S" h# l+ L1 E' J0 s$ J
  7. describe("main.fc contract tests", () => {
    8 `: p! k2 u; p) T* d
  8.   let blockchain: Blockchain;5 f6 {: X" d/ J* h1 i! j$ f
  9.   let myContract: SandboxContract<MainContract>;% B0 R$ h8 ~# G: V9 N, ~
  10.   let initWallet: SandboxContract<TreasuryContract>;) `: |( r+ G" @
  11.   let ownerWallet: SandboxContract<TreasuryContract>;, n7 X3 J$ n1 M. s

  12. ( }$ t5 o$ `& V: f+ u) W- E9 H$ q
  13.   beforeEach(async () => {
    . W1 p! m& s7 f+ d
  14.     blockchain = await Blockchain.create();
    $ R& H/ V) N6 d: G' J5 d# O
  15.     initWallet = await blockchain.treasury("initWallet");+ v* H& J8 f3 |) F) u
  16.     ownerWallet = await blockchain.treasury("ownerWallet");8 e0 H& n! J0 \
  17. 0 e3 w: C: D% X2 s! h* [
  18.     const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];  D4 m1 b" E6 P$ T3 d
  19. " s- ]7 @6 I; A, u$ p: g
  20.     myContract = blockchain.openContract(+ b, f5 o9 Y) F, b& r6 d& c* K
  21.       await MainContract.createFromConfig(- h7 Q$ F. y% A+ l' M
  22.         {
    # t+ r$ H+ B! d+ s+ `5 z
  23.           number: 0,
    1 j- |8 L' `' F9 |0 S( F2 C' e
  24.           address: initWallet.address,' w6 h9 P# b! C( w
  25.           owner_address: ownerWallet.address,5 L4 S" ?1 `" t" n. Q: m2 s8 f
  26.         },2 e) y- B) E4 k* p' L$ v3 f% w' _
  27.         codeCell1 W! j# p! z/ T
  28.       )' S8 n0 E( m) z# ]  B+ B4 t, ~' z
  29.     );
    " K: H! }( X0 G- T" K7 U& ?0 f+ }$ h
  30.   });" J; H& c# d  u6 l6 A  u
  31.     @' I+ F- H% ?) Z
  32.   ; Q! S/ a, D3 u
  33.   // ... the rest of testing code+ z% T, `. N, u' X5 ^) G
  34. }
复制代码
我们将对其进行 3 次更新:
  • 导入来自 @ton/blueprint 库的 compile 函数
  • 执行 jest 的 beforeAll 钩子来编译我们的代码,并为其他钩子和测试提供代码Cell
  • 更新 beforeEach 钩子,以使用 beforeAll 钩子提供的代码Cell。

    ; P% D7 g6 ]# V/ J. _, c2 H+ m* x
在进行这些更改后,我们的代码会发生这样的变化:
  1. // ...other library imports8 k, v: ?" O$ X) N
  2. import { compile } from "@ton/blueprint";* L3 E0 W& W2 N( G" o% a3 W8 V
  3. 7 `. g5 y0 g5 j& O9 k# {
  4. describe("main.fc contract tests", () => {
    . ?/ K  k6 g1 i+ D# t, n
  5.   let blockchain: Blockchain;
    ( N0 Y" _: i3 Z; R) s
  6.   let myContract: SandboxContract<MainContract>;: ~+ r; H8 C! I! j
  7.   let initWallet: SandboxContract<TreasuryContract>;9 }3 d+ q* N( o4 }+ I  G
  8.   let ownerWallet: SandboxContract<TreasuryContract>;: ^, V# U8 v0 ^( R! g$ u* ~
  9.   let codeCell: Cell;, J# \) L2 |( \
  10. 4 S7 ]# S5 {6 g: o+ a8 d
  11.   beforeAll(async () => {5 \$ _1 i+ x, V; F  O% U/ [
  12.     codeCell = await compile("MainContract");
    & n0 F' a$ w7 V  n/ l
  13.   });
    , n8 k, ~% R% y" I& m

  14. 3 u3 T9 e% A& K- \7 M& p7 R
  15.   beforeEach(async () => {
    7 w. u4 O- Q5 C6 H, ]
  16.     blockchain = await Blockchain.create();
    / J# t7 V4 m" G. B( l6 K0 R
  17.     initWallet = await blockchain.treasury("initWallet");
    ! v: Z. x( V" e4 r# U! Q8 a! B
  18.     ownerWallet = await blockchain.treasury("ownerWallet");
      J" M! v. `* ]8 z; }! ]

  19. ' ?; i& u) \/ A3 @- j
  20.     myContract = blockchain.openContract(# d3 ]4 x' W) }
  21.       await MainContract.createFromConfig(
    6 [6 t/ I$ B% i4 o: ]
  22.         {4 d; C) {" J; I
  23.           number: 0,
    # F) T) G+ q4 O: X4 O9 i
  24.           address: initWallet.address,8 g: F# d  c# G. @
  25.           owner_address: ownerWallet.address,
    1 i9 K2 j6 N( b0 C; Y, k; \
  26.         },
    / d" {* b& X% X. @
  27.         codeCell6 ?( P; h( @4 @4 J; p5 h) O& h+ r
  28.       )7 Y, S. p3 k3 `" T0 m' O3 m3 j
  29.     );9 ^5 q; d+ ^9 n
  30.   });; C- Q. Y1 s# q# w* e: B" R, X
  31.   
    ; c7 y& u. x5 `: ^$ G( @/ Y& P
  32. // ... the rest of testing code % b% E$ f/ h3 n$ J% d6 q
  33. }
复制代码
我们进行这些更改是为了让您习惯运行  npm create ton@latest 时生成的代码。我们正在将编译过程委托给Blueprint 库。
不过,我们还需要向Blueprint 提供一样东西 - compiler config. 为此,只需在 wrappers 文件夹,并将其命名为 MainContract.compile.ts。
下面是该文件的内容:
  1. import { CompilerConfig } from "@ton/blueprint";- e# o( g- F; F

  2. 8 i* r3 t% D& l
  3. export const compile: CompilerConfig = {9 f! ?$ [0 {( e0 L/ N  z: ~, J0 @  ^
  4.   targets: ["contracts/main.fc"],
    - R& M" t. N* j8 l0 ^7 R
  5. };
复制代码
此时,您只需删除编译脚本文件 (scripts/compile.ts) 并在项目根目录下运行 yarn test 。您将看到我们的测试正在正常执行!恭喜您!
将部署流程委托给 Blueprint
接下来,我们需要使用 Blueprint 的超级功能来更新部署脚本。快速免责声明--我们将使用包装器 MainContract 及其方法 .createFromConfig。这样,我们就能在部署前轻松设置合约的初始状态。
Blueprint 为部署带来了惊人的功能。它以交互方式要求我们选择网络(testnet/mainnet)以及要部署合约的钱包类型。如果你经常开发和部署,这个功能就会非常方便。
在更新部署脚本之前,我们需要做的一件事是在包装器上创建一个名为 sendDeploy 的新方法。正如你所记得的,我们需要使用包装器与我们的合约进行交互。这就是我们方法的代码:
  1. async sendDeploy(provider: ContractProvider, via: Sender, value: bigint) {
    0 h. W8 C! |: e6 w
  2.     await provider.internal(via, {# }( I9 t+ `6 W' o' ~
  3.       value,( O9 s% e; K$ V; \8 E, R
  4.       sendMode: SendMode.PAY_GAS_SEPARATELY,
    & u0 H0 ]6 H5 R  Q6 }2 m6 h
  5.       body: beginCell().endCell(),
    ' e$ ~( Y3 x/ V4 T+ \
  6.     });
    ( _/ F! p$ W& J! c! O/ w
  7. }
复制代码
正如你所记得的,部署一个合约就像向其预定地址发送初始数据和代码一样简单。在第 3 章中,我们手动完成了这一工作,因此你可以深入理解其工作原理。现在,为了优化起见,我们将其划分到包装器和Blueprint 中。
我们将用新代码完全替换我们的代码,使其符合Blueprint 的功能。
下面是我们的 scripts/deploy.ts 现在的样子:
  1. import { address, toNano } from "ton-core";' [9 p) l* `5 I) ?% |
  2. import { MainContract } from "../wrappers/MainContract";! y$ ~/ L: t$ c. n0 @5 J
  3. import { compile, NetworkProvider } from "@ton/blueprint";
    $ n- O6 |  C0 W( ]3 ?( C* x
  4. 5 c4 j4 g- Y9 \+ p( k+ E& F3 b. @
  5. export async function run(provider: NetworkProvider) {
    ( V9 @; J# D" ?- `9 O! l
  6.   const myContract = MainContract.createFromConfig(' Y- Y' d! q( e
  7.     {6 A4 @6 k( [7 o" Z8 G
  8.       number: 0,
    : Q+ e6 G" ~  h' p
  9.       address: address("kQDU69xgU6Mj-iNDHYsWWuNx7yRPQC_bNZNCpq5yVc7LiE7D"),% D/ k4 h9 J9 E
  10.       owner_address: address(& Y4 e/ c8 p7 O# T2 ]$ H% [' K7 u
  11.         "kQDU69xgU6Mj-iNDHYsWWuNx7yRPQC_bNZNCpq5yVc7LiE7D"- T1 x9 f2 k3 `+ b2 D- B% |
  12.       ),$ v; O+ z. z# N
  13.     },
    , U/ `" R: _& g, x& V! i
  14.     await compile("MainContract")
    7 B' b* Z9 T; G2 k& X2 n* K4 U6 _
  15.   );
    5 N0 Z5 G: X' b  p4 u

  16. - w% B9 c- o8 o$ D2 r' ~+ q0 J% \
  17.   const openedContract = provider.open(myContract);$ Y' |# y2 O  F$ r. C
  18. + }$ T* X6 `' `$ k2 I5 u, o
  19.   openedContract.sendDeploy(provider.sender(), toNano("0.05"));( A6 {/ t- Z) M. {3 N- e! w& |

  20. : b* w( G4 v- L/ d) G/ z! \. z
  21.   await provider.waitForDeploy(myContract.address);
    % `8 b0 Y# D/ R* s. U4 f$ n! G
  22. }
复制代码
正如你所看到的,这一切都简单多了,而且我们可以从Blueprint 中省去许多以前必须处理的事情。
其中最酷的一点是,现在我们可以使用包装器中的 .createFromConfig 方法来启动我们将要部署的合约,并通过传递给它的 config 对象获得初始状态数据。
还有两件事值得解释,它们与配置对象的内容有关。
  • 大家都记得,合约的初始数据必须包含三个参数--当前计数器值(数字)、最近的发件人(地址)和合约的 "所有者 "地址--能够提取资金的人。因为我们要在链上使用这个合约,所以我在这两个字段中都设置了我的真实测试网地址。这样我以后就可以提取资金了。
  • 函数 address 会帮助我们将字符串地址解析为适当的地址类型,就像我们的配置请求一样

    & L+ h/ p& k* J# T
核心部署功能由 NetworkProvider 类型的提供程序处理,当Blueprint 触发我们的部署脚本时,该类型由Blueprint 传递到 run 函数中。
如果您希望它们与Blueprint 一起工作,那您的所有脚本都必须导出  run 函数.
让我们先回到包装器的 .sendDeploy 方法。看看 body 参数。你可以看到,我们发送的消息正文是一个空Cell。根据我们的合约逻辑,如果信息正文没有设置操作码,即使合约已成功部署,消息也会被退回。稍后您将看到这种行为。
借助BluePrint部署合约
让我们进入项目根目录,运行一条命令:
  1. yarn blueprint run' Z& _0 J0 B" g# ?* i" n
复制代码
运行此命令后,您会在其中看到这样的内容:

' K3 S) Y' f- p: i% N% p7 ^( ]
  1. user$ yarn blueprint run; R0 y, y8 w) ?$ r1 Y" i

  2. 8 {  D1 q: G4 C8 H
  3. yarn run v1.22.118 A6 N& c( k* k( w( |

  4. 3 {  Y3 ~7 |! m) `( H
  5. ? Choose file to use (Use arrow keys)
    + i5 L: Q- s9 v" m* M% I: ?  _
  6. ❯ deploy
    ; E" n( R4 e" P0 D
  7.   onchaintest
复制代码
你会看到 Blueprint 通过命令行提示我们选择要运行的脚本。此时,我们仍有 onchaintest.ts 脚本,所以它也会被提供给我们执行,因为 Blueprint 正在捕捉来自 scripts 文件夹所有可用的脚本。我们暂时不会创建任何链上测试,因为在下一章中,我们将创建真正的网络接口来与我们的合约进行交互。你现在可以暂时删除 scripts/onchaintest.ts 文件。
选择脚本 deploy.
系统会提示您现在选择网络:
  1. yarn blueprint run
    . z0 D7 v5 ^/ E5 K. a# I

  2. ! g4 }6 [7 Y: I, L
  3. yarn run v1.22.11( u  L+ K- a6 }7 O9 W

  4. # |$ @8 D, Q" v  z6 R
  5. ? Choose file to use (Use arrow keys)' _/ y# L& h7 Q: h& n4 y& I( o
  6. ? Choose file to use deploy. v# T! y, C* H0 |- A3 m8 B& c9 I
  7. ? Which network do you want to use? (Use arrow keys)1 p7 Z5 H4 p8 V/ b! _
  8. ❯ mainnet
    . g. ~' Y7 E$ I+ e" n
  9.   testnet
复制代码
选择网络 testnet.
5 C# F/ M8 S$ r  H% c7 o
  1. yarn blueprint run
      S& q$ e+ w! k6 v* A: z2 ?, w  }

  2. 8 _) A* ]! Y, D) b; K' S4 R
  3. yarn run v1.22.115 _/ S3 g  Z6 A' Q2 r: \
  4. 8 ]) M# W; a- {/ \
  5. ? Choose file to use (Use arrow keys)" Q1 u+ X3 }1 i6 u- Y1 N
  6. ? Choose file to use deploy) {+ `6 y. I4 D2 E2 a( H$ a4 C! _4 p
  7. ? Which network do you want to use? ( L7 P6 V1 h9 j7 [" p
  8. ? Which network do you want to use? testnet
    % z' C* }, |! ]- s
  9. ? Which wallet are you using? (Use arrow keys)
    ( A0 y( O1 {+ a: z
  10. ❯ TON Connect compatible mobile wallet (example: Tonkeeper)
    - {# k- j+ V/ e: Q
  11.   Create a ton:// deep link + O/ s* l" U! V9 F  }9 L6 Q
  12.   Tonhub wallet
    7 f- v3 x" r% X2 ~9 J7 q7 k8 @
  13.   Mnemonic
复制代码
现在,系统会提示您选择使用哪个钱包来部署合约。在第 3 章中,我们只使用了一个钱包(TON Whales的Tonhub),而且是以最简单的方式--创建一个深度链接。
Blueprint 可以让您使用任何类型的钱包,并保持最佳会话状态。也就是说,它的工作方式是授权你的本地项目从你的钱包请求交易,然后批准它们。
在本教程中,我们将再次使用 Tonhub 链接测试网和主网。
打开 Tonhub 钱包应用程序。
系统会要求您扫描二维码,授权应用程序与钱包进行交互(请求交易)。
: n1 r& E: t3 }. E; _: t
  1. yarn blueprint run3 u- l5 R# ~' U6 @* l& l: a% |
  2. yarn run v1.22.115 [6 F- F% T* ?8 v+ C: Q3 @
  3. ? Choose file to use (Use arrow keys)
    " D0 A$ X4 G; r8 x$ F' U( X
  4. ? Choose file to use deploy
    2 I1 j; r& p9 c/ s& r& K7 a
  5. ? Which network do you want to use? (Use arrow keys)
    $ Q. J/ z; c2 D
  6. ? Which network do you want to use? mainnet- |# D- O  [7 t
  7. ? Which wallet are you using?
      k' S+ b$ ?( v, g, g7 ^$ g/ X
  8. ? Which wallet are you using? Tonhub wallet
    + |9 F0 B$ V1 H: r( Q  Y

  9. 8 a$ x% F- B% b# t2 u) z3 C, ^

  10. " a7 ~8 f* a  k% }, B& I5 P
  11. ton://connect/GzhvQ-zwLhYNxzlEPEf-hP63kgR_0_O8vXsM8mSqQ-0?endpoint=connect.tonhubapi.com4 @# U( u2 f  V5 s8 ]& Z5 c0 m* R! p

  12. * T6 K+ G6 D( z1 Q8 _! v
  13. Connected to wallet at address: EQC7zjln0_fghMQg0A-ZhYFar3DU1bDW9A4Vi5Go5uu-tAHe
复制代码
扫描二维码并在应用程序中授权后,您将被要求在 Tonhub 应用程序中签署交易。完成后,您将在命令行中看到以下输出:
  1. yarn blueprint run
    6 E/ M' J# w: ?
  2. yarn run v1.22.11
    $ o4 W/ C6 a, }# ?0 O$ A
  3. ? Choose file to use (Use arrow keys)
    1 D* g, G7 V/ J
  4. ? Choose file to use deploy2 V! @# g1 e  `0 d6 f' N
  5. ? Which network do you want to use? % e7 S- w( H1 z$ P; d1 w
  6. ? Which network do you want to use? testnet5 c4 P' e; |) C( O
  7. ? Which wallet are you using? 5 m* t6 V* L* i# C- v: N
  8. ? Which wallet are you using? Tonhub wallet  G; c/ T5 R. n: R  m+ F
  9. Connected to wallet at address: EQDU69xgU6Mj-iNDHYsWWuNx7yRPQC_bNZNCpq5yVc7LiPVJ8 K# P  A8 a: o2 r. m# y# W
  10. Contract deployed at address EQCS7PUYXVFI-4uvP1_vZsMVqLDmzwuimhEPtsyQKIcdeNPu
    ) L7 u3 n$ \$ E' a0 J8 ]0 d- V
  11. You can view it at https://testnet.tonscan.org/address/EQCS7PUYXVFI-4uvP1_vZsMVqLDmzwuimhEPtsyQKIcdeNPu
      S1 K9 a- T* p- ~- K# o7 ^* h! ]
  12. ✨  Done in 19.84s.
复制代码
+ p& \  h" z, t: P" b* A$ s
2 ^' C/ T4 {- Z  B( W" `! c3 a
5 u- f4 {8 Q3 G& w5 q+ _
- M+ m( r. L( q6 Q, @
# b. c5 M" w0 d) m$ G

  e2 `- ?, X$ k+ @3 T8 F
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则