|
本帖最后由 riyad 于 2025-3-7 00:19 编辑 2 u* `7 }3 ^6 n7 p- }1 ]7 B
1 ^/ c9 Y4 z" A: Z6 P- _本课的目的是建立一个能够编译智能合约示例的本地项目。我们现在还不打算编写任何 FunC。我们只是在准备搭建我们的本地环境。这部分内容以后可以在所有项目中重复使用。
/ u" g" k8 ?1 ^: \9 B f& f首先,请确保您拥有三样东西: 满足上述要求后,我们就可以开始了!
$ @" N3 h x5 @) ]1 f: V建立项目。首先为我们的项目创建一个文件夹 - mkdir my_first_contract && cd my_first_contract3 T/ |. b$ i+ |/ l; O
复制代码让我们启动 package.json 文件. 我要用 yarn, 但如果你觉得使用 npm 更好,你可以自由选择。 - yarn init! R2 F; e( l* z5 i1 J7 ]
复制代码系统会提示你输入一些参数,但你只需在每个提示符下点击 Enter 按钮即可。完成后,项目目录下的 package.json 文件将包含以下默认内容: - {
) r& {/ V, }' A, s) [- n4 Q0 i" z7 U - 6 w6 S6 J6 C* D
- "name": "my_first_contract",9 Q& x. N; @8 Y3 w+ a: b
7 v, ^7 H2 o1 x+ s- "version": "1.0.0",
; {: g' V' D1 x- f- q' a! w
2 T4 w7 y9 |$ y8 d. H- "main": "index.js", //we don't really need this line, feel free to remove it
. H" C- @/ { t; u; U' [: ^
, g4 ^$ U- s- O- "license": "MIT"
3 z k+ S3 P8 j# d% I
0 [; X0 o( t0 { x- }
复制代码现在,让我们安装以下 4 个库: 这些都是与 TypeScript 相关的库。在本课程中,我们不会深入探讨 TypeScript 本身的工作原理。 - yarn add typescript ts-node @types/node @swc/core --dev
0 a0 X. M$ E R" H9 X$ n
复制代码我们创建一个 tsconfig.json 文件,并在其中添加以下配置: - {- l! n, F6 `0 W1 c3 ]8 _
* y& L; n P, L: k0 S) t9 ]- "compilerOptions": {$ S% T; l+ w! u9 }& @
% e) D' F( g; `& C( ~* A9 K- "target": "es2020",
; B) b$ q' x( l$ |$ q. V
: t+ W6 q$ H& |, }" c- "module": "commonjs",
8 ~6 C- k4 ~4 O% d6 K; |* r* [; |
1 W1 |, c5 [* V7 q% o% Z- "esModuleInterop": true,
* K. {+ ?5 a: ~3 J; I; g* T - 9 v, P& T# b( J9 p" t
- "forceConsistentCasingInFileNames": true,
# E z7 D1 H M# {3 a
* y% p. h: ]+ f! Q- "strict": true,
" K7 t2 W* A; z7 [
1 }' p# n, G' v* @" O- "skipLibCheck": true,5 M9 k8 O; S0 O# @/ r/ s
- ; K0 r$ f* _# |4 b7 T7 K
- "resolveJsonModule": true
" P* S+ V. l8 G$ M" y7 l
7 K8 t* ? \- F2 M2 c$ u1 Q* x- },9 k! J- @% z+ Z0 |4 w; d& z9 B
9 C- r- E6 R3 J D- "ts-node": {
) }6 O9 w6 S0 @1 z. T% Q) h6 h8 R - , j" Z8 c9 ^0 X P; q6 C
- "transpileOnly": true,: T8 J1 g, X! r( ~5 {
# C. K/ H; J& X9 _0 Y/ U- "transpiler": "ts-node/transpilers/swc"
. x9 T, H$ b( X- N: \, a0 t - * q; d. V2 ]! v) O5 s& w
- }
8 K0 ]; a' s6 `: @ - $ N, u8 G9 A: |& R
- }
复制代码实际上,我们还需要三个与 TON 有关的 lib,它们是: ton-core - 实现 TON 区块链底层基元的核心库。 ton-crypto - 用于为 TON 区块链构建应用程序的加密基元。 @ton-community/func-js - TON FunC 编译器 * l- d" A b9 D1 N/ d
) W, R% _% _ a9 }' J
- yarn add @ton/core ton-crypto @ton-community/func-js --dev+ R! {2 Q1 r8 C9 B9 Q" x7 o0 ?" h
复制代码 FunC 代码示例现在,我们将创建一个包含最小 FunC 代码示例的文件,然后编写一个可以编译它的脚本。 我们创建一个 contracts 文件夹,并在其中创建一个 FunC 文件(main.fc): - mkdir contracts && cd contracts && touch main.fc1 Z2 w. L) u9 d6 N
复制代码打开 main.fc 文件进行编辑,并插入以下示例 FunC 代码: - () recv_internal(int msg_value, cell in_msg, slice in_msg_body) impure {& X" i3 T/ ^( V, [8 u. Y
/ d0 p2 \- W, l+ x- }
复制代码 您已经知道 TON 智能合约可以接收两种信息,如果您不清楚这些信息是什么,请参阅第 1 章。这些简单的代码就足够我们编写一个编译器脚本了。我们开始吧!
, a: E; E6 | `- V, Q9 s& {: H; Z" h7 {" j, ?
编写编译脚本在项目根目录下创建 scripts 文件夹,并在 scripts 文件夹下新建文件 compile.ts: - mkdir scripts && cd scripts && touch compile.ts$ U5 r) Q6 {' y
复制代码我们已经有了一个 compile.ts 文件,但还是让它在我们完成代码后方便运行吧。让我们在 package.json 文件中创建一个脚本快捷方式。只需添加以下脚本键即可: - {
7 Y0 O; P, u% b! E - //...your previous package.json contents+ Q" c+ T1 d1 u* [- g* x5 o. y
- "scripts": {( Y" z% X! L1 ^/ y
- "compile": "ts-node ./scripts/compile.ts"9 h3 d) @( x8 r* e' o, o6 d: \) m
- }
]4 O, g5 r7 S - }
复制代码现在在编辑器中打开 scripts/compile.ts 文件,开始编写编译脚本。我们将一步一步地添加代码,并附带说明,因此在本节结束时,你将拥有编译脚本的完整代码。 首先,我们要导入:
6 P( q% o+ G/ ]8 `' a- import * as fs from "fs";
! U) u, C, R3 w! r1 o - import process from "process";
2 w2 V. n- w0 K6 {. m - import { Cell } from "@ton/core";
$ Q$ g2 {1 o2 \6 w1 y ^" O - import { compileFunc } from "@ton-community/func-js";
复制代码我们将有一些异步代码,因此让我们创建一个异步函数,在内部运行这些代码: " F% ]& Q9 ]! h" q3 n* y
- import * as fs from "fs";
& k* z, v( w: Q% \ - import process from "process";
5 \0 x5 E2 e7 K; \ - import { Cell } from "@ton/core";
5 P5 U0 s+ w+ [( T. K( S - import { compileFunc } from "@ton-community/func-js";
6 L! R: {6 [ g' t7 v$ y4 z% F - 2 D! B2 p, Y8 W* Q4 p
- async function compileScript() {
( C. R( A" E @5 L$ _& G7 U
: l) j* L) w$ ?- }
- }3 y' V) G3 {* H. Y - * r& [$ G5 @& c) [; K/ l. R
- compileScript();
复制代码现在,我们将样本合约的文件名传入 compileFunc 函数,如果结果状态为错误,则退出脚本:
; H6 Z$ d* X* T# y6 T7 ~* o- import * as fs from "fs";3 I# R) O0 k9 P- c6 l9 B' a
- import process from "process";
g* r) U- N( u/ m9 `& _: ?, A - import { Cell } from "@ton/core";
# O, [) M' y) F* E8 s - import { compileFunc } from "@ton-community/func-js";( W" S$ w5 o2 R: j9 z
& M2 N( i5 @/ Y" H5 Y- {/ l- async function compileScript() {
9 t0 p5 m$ ]- y9 G, g
. ]* X7 U# Y; z- q& H5 T6 V- const compileResult = await compileFunc({- W8 s: g7 w) P% y% ?; g
- targets: ["./contracts/main.fc"],
" B( T0 [8 R% D- s6 q, v9 t - sources: (x) => fs.readFileSync(x).toString("utf8"),
9 g. m5 F2 f$ E3 c' A - });. m; V6 [0 J! p/ r# X' {6 q
- 8 l5 ]+ a: x# j" T0 [6 @
- if (compileResult.status === "error") {
: s( L' z" |, C - process.exit(1);
1 s0 s, Z t9 V9 ?5 d E) I - }
; u# [+ U; M3 b! M; V, _ - }
5 `9 s A. l( P; W9 d - compileScript();
复制代码太棒了!从技术上讲,你已经可以进入我们项目的根目录,运行 yarn compile 指挥部. 但如果这样做,就无法看到编译结果。让我们将编译结果保存到文件夹 build 在我们项目的根目录下(确保先创建了它)。 编译结果将是一个 BOC(Body of Cell)base64 字符串,但如果我们想在本地进一步使用这个编译后的合约(在编写测试时),就需要构建一个包含该 BOC 的 Cell,并将其存储起来以备日后使用。 Cell 构造函数有一个 .fromBoc 方法,该方法希望接收一个缓冲区,因此我们将向其提供一个 BOC base64 字符串缓冲区。一旦我们从 BOC 构建了一个cell,我们就要为这个cell创建一个十六进制表示法,并将其存储到一个 JSON 文件中。这就是cell构建和转换命令的样子: - Cell.fromBoc(Buffer.from(compileResult.codeBoc, "base64"))[0].toBoc() .toString("hex")- |& Z" N) P% v% e& V
复制代码现在,我们将其放入脚本,并使用 fs.writeFileSync 将结果保存到 JSON 文件中: - import * as fs from "fs";0 R+ V6 u+ @& S
- import process from "process";
( p. [8 n$ w+ y% p - import { Cell } from "@ton/core";) }+ E2 M8 k* p: g. f% G6 @
- import { compileFunc } from "@ton-community/func-js";$ P5 U' C0 ]( i# ]" ~' b* x6 C2 r
- 4 T. N( j+ F5 h8 f2 g
- async function compileScript() {9 ~+ ?# Y, g8 H: Q4 q$ h5 m6 g+ I
/ r' [ I$ A. J4 ?9 x7 i; x- \( a- const compileResult = await compileFunc({* \# ^! K: b) v5 f2 t& k7 g6 N5 _9 q
- targets: ["./contracts/main.fc"],
6 l. H& {* P8 n+ \* d q - sources: (x) => fs.readFileSync(x).toString("utf8"),' a7 n3 W, [& H4 @
- });
8 M2 n- g' H7 j
3 j2 i( S* p( k! u7 e: v4 m- if (compileResult.status === "error") {
4 M& X3 A/ [! W - process.exit(1);
0 ?; g1 L, {9 {9 x7 _1 A - }
+ u R5 [3 ^, W
8 ^* i9 R; H% r# j0 Y- const hexArtifact = `build/main.compiled.json`;
2 X, b- y3 x' C0 T M$ b- W - - [& E) ~* W2 X* s' s- U& A: k& v
- fs.writeFileSync(' y% ?3 L# d* ^$ E
- hexArtifact,
+ ^7 m" N0 s! ^, T/ i( ? - JSON.stringify({
$ G7 h; f6 O1 F5 l. q# h( N - hex: Cell.fromBoc(Buffer.from(compileResult.codeBoc, "base64"))[0]
/ d" i+ I& X( f% }& I. U - .toBoc()* ?% O; g5 ~% D6 a8 n
- .toString("hex"),
: K2 A! }- r4 \ j1 K0 r5 T$ |, w, U - })8 d' u. D# O0 b7 k, L S6 N8 l. X" t
- );
$ s0 {* d9 R: g( v - }
& O8 o1 m- a* i% | - compileScript();
复制代码这段代码已经可以运行了,但让我们做一些最后的编辑,使我们的脚本在运行时能提供更多信息。我们将为每一步添加漂亮的控制台日志。
$ T; S9 V0 Q/ a, J以下是最终代码( | D& U) P7 D0 N9 M
- import * as fs from "fs";
) `5 l4 \& ~# S% ]" b- t0 I - import process from "process";. Y- r5 W }" G: u7 z7 Y2 p
- import { Cell } from "@ton/core";( C4 R% M! b; f) H6 R3 z
- import { compileFunc } from "@ton-community/func-js";" n0 s4 ]+ e9 g! D# x3 t
! t. r, m! o# y- B, I- async function compileScript() {/ z1 h3 X) ? R
- console.log(8 U( F9 Q1 V3 c. t, S! y0 t* K
- "================================================================="6 P4 @9 S- f2 A, Z
- );
* |4 |; Q- \! B- T/ a- G' _, I - console.log(7 E7 m8 ]6 K8 f% j8 g* y( ^7 Z
- "Compile script is running, let's find some FunC code to compile..."& |$ ~! o' _( E1 L3 P& Q
- );
+ d# m- _ z) n( |9 _; p, P) A
" A3 W& ]. }; o. e, d4 m, r+ l- const compileResult = await compileFunc({8 J% u& Y* p7 r) k% g; Q% q
- targets: ["./contracts/main.fc"],
+ ` k2 a. a; v" K" q. @# n - sources: (x) => fs.readFileSync(x).toString("utf8"),
6 L. x3 v5 G/ |# O8 w/ V - });
& X2 x5 H7 I0 {/ A
4 x* b7 ?/ r, s% h% P- if (compileResult.status === "error") {
+ ], b) G7 W' H1 j" U# b9 X! ^" ` - console.log(" - OH NO! Compilation Errors! The compiler output was:");
$ s# E( \, L9 P; t& v! X& _% ] - console.log(`\n${compileResult.message}`);
2 [4 D% E4 A$ A - process.exit(1);
" `5 j0 u& M& S! [ - }
) R. s6 W' d2 m% \+ K
+ e& ]0 m6 K" z" f* l) R5 f8 O- console.log(" - Compilation successful!");
5 g2 P9 P3 r+ Y' L8 R - 4 s+ c1 S( c2 C- j1 B$ N: J2 I* ~
- const hexArtifact = `build/main.compiled.json`;
3 {; a- }3 O) W# Z7 Q/ L) r7 ^ - 1 J8 b9 l+ `2 z Y& }' {" b
- fs.writeFileSync(
! I# G' o2 A0 g7 P7 g5 E- O - hexArtifact,
3 A4 @/ M( k" l - JSON.stringify({
5 w1 W( b! ~0 A2 r+ T$ n$ C9 t& C - hex: Cell.fromBoc(Buffer.from(compileResult.codeBoc, "base64"))[0]
) _, u0 H- @1 y) Z - .toBoc()" e( u4 A0 \. v: O% A
- .toString("hex"),; t, p9 _- t" ~2 M; c- I
- })! s8 g2 x- c& v! s1 V8 C
- );# I1 f* z2 c% J# B3 ?2 J7 H
- 4 r& O% x: ]6 Q- o* y
- console.log(" - Compiled code saved to " + hexArtifact);7 v# ]" R# _' p5 R: [
- }3 T A! {& Z" d) c1 v1 E- r
- compileScript();
复制代码这是我们的编译脚本,每次修改代码时都要运行它。稍后,一旦我们有了一些可测试的 FunC 逻辑和部署脚本,我们就可以在任何测试或部署之前自动运行该脚本。 我们的 "卫星装配 "实验室设备越来越齐全! 不过,让我们再运行一次 yarn 编译,看看编译的结果如何。为此,让我们打开 main.compiled.fc 文件。 我们在那里看到了什么: - {"hex":"b5ee9c72410102010012000114ff00f4a413f4bcf2c80b010006d35f03fbffbf07"}
& ?; `+ B: ?! U k2 r
复制代码听起来很疯狂,但这就是我们的合约。在下一课中,我们将编写第一个 FunC 逻辑,并将其与我们刚刚编写的脚本进行编译。 到时见!
& a. N( E( r* @8 w# A( E5 ]* P9 v# V: S; h6 u3 h
, C- E8 [ p) w6 w; ?. Q |
|