|
本帖最后由 riyad 于 2025-3-7 00:19 编辑 % @ i0 I+ m3 Q' w ?. D
" d0 \! w3 J" z+ p8 q3 Y/ p本课的目的是建立一个能够编译智能合约示例的本地项目。我们现在还不打算编写任何 FunC。我们只是在准备搭建我们的本地环境。这部分内容以后可以在所有项目中重复使用。 " ~% t" J8 C6 j) Q6 w7 B& W# I6 M) P) D& e" ]
首先,请确保您拥有三样东西: 满足上述要求后,我们就可以开始了!
. Q q$ Q/ e6 W; P建立项目。首先为我们的项目创建一个文件夹 - mkdir my_first_contract && cd my_first_contract
, k/ w' c2 m e9 X: j1 m. z6 [
复制代码让我们启动 package.json 文件. 我要用 yarn, 但如果你觉得使用 npm 更好,你可以自由选择。 - yarn init
G% g) A; O! o- W6 h; X
复制代码系统会提示你输入一些参数,但你只需在每个提示符下点击 Enter 按钮即可。完成后,项目目录下的 package.json 文件将包含以下默认内容: - {
# S& k8 N" u2 m& R
6 w+ {. y4 y8 C' d* \1 X$ _- "name": "my_first_contract",
) Q# ]! V7 e* L. x$ f; h9 s - ) r4 `- s1 ^( T1 i$ K( c
- "version": "1.0.0",
5 P7 l' Y' l5 F8 ^/ |9 d - : k E/ Z$ p4 P, J$ h# a
- "main": "index.js", //we don't really need this line, feel free to remove it. g( V5 v" U3 Z% y
3 G$ `( Q0 F" O8 }2 m- "license": "MIT"# u+ R* j" M/ O5 J4 G' r0 ], X! z
9 q- {9 x' a5 f" h3 C3 _ Y; u2 U" V- }
复制代码现在,让我们安装以下 4 个库: 这些都是与 TypeScript 相关的库。在本课程中,我们不会深入探讨 TypeScript 本身的工作原理。 - yarn add typescript ts-node @types/node @swc/core --dev. d; e; f, _+ n5 P- Z6 W- }' u; n+ Z
复制代码我们创建一个 tsconfig.json 文件,并在其中添加以下配置: - {+ U3 e; O. x/ x' ]( ?
4 _$ i! `' M% J, V1 Z2 v0 _- "compilerOptions": {
- P2 R% }( y) o6 _. d) P, \, { - " Y8 {% N$ |+ i; [9 `+ x
- "target": "es2020",$ b2 F) I% x8 e' R* ]
! o: \& ^: H( m$ A- "module": "commonjs",0 a4 H- d5 m& l5 y* t9 @) Y) v$ D. P
1 ~3 R, Y) _/ o- i& y3 e- "esModuleInterop": true,3 P e4 U% x9 R7 M: s- f8 Z3 d* V0 D
{& M" X8 p$ j9 \8 G$ v2 A- "forceConsistentCasingInFileNames": true,
! B# \, j: Y3 a- i# @$ D, l
- t9 R: [3 j: ]. S4 g) G- "strict": true,8 P: x, z; v# |4 B, l: j' n# R( p
, y1 y% j! q. x% D- "skipLibCheck": true,
6 I! |; Y& k: i9 x$ I T
" K! p9 s% U1 U; Q; u6 q- "resolveJsonModule": true( _, V3 {1 F* d$ \
# ]% F: d i% L7 C- },3 O: G$ ?0 Q$ ]
! h1 T5 }* j% X3 T- "ts-node": {. A' ~; R0 ]2 ] ?0 J% g* @& ]9 X
- 6 p8 V8 S. M& V/ O& h7 ^' ?- X5 s
- "transpileOnly": true,; j1 N) {: e; q* {' N3 P/ N4 F
' I8 H- m( z! ^6 y+ I( P+ m- "transpiler": "ts-node/transpilers/swc"
5 h/ y2 Q. C* N6 I0 W2 ]; t - . I& ]5 e% h5 ^' _! u
- }& N% a: [- f2 }3 |8 A. N( I
- ( }+ h1 R4 k. [+ S) {- F
- }
复制代码实际上,我们还需要三个与 TON 有关的 lib,它们是: ton-core - 实现 TON 区块链底层基元的核心库。 ton-crypto - 用于为 TON 区块链构建应用程序的加密基元。 @ton-community/func-js - TON FunC 编译器 * K& E) u( y U% |) l
9 j4 G( U8 j& p% E- yarn add @ton/core ton-crypto @ton-community/func-js --dev/ ]: K; D: C2 F- M* W$ l
复制代码 FunC 代码示例现在,我们将创建一个包含最小 FunC 代码示例的文件,然后编写一个可以编译它的脚本。 我们创建一个 contracts 文件夹,并在其中创建一个 FunC 文件(main.fc): - mkdir contracts && cd contracts && touch main.fc
* B% k! C2 O7 z3 S2 W$ M
复制代码打开 main.fc 文件进行编辑,并插入以下示例 FunC 代码: - () recv_internal(int msg_value, cell in_msg, slice in_msg_body) impure {8 a) T( {9 L( t" U
- : n+ c# c$ [$ A V
- }
复制代码 您已经知道 TON 智能合约可以接收两种信息,如果您不清楚这些信息是什么,请参阅第 1 章。这些简单的代码就足够我们编写一个编译器脚本了。我们开始吧!) i( F4 L; h# b# g4 n
$ N) [5 K* f4 @3 I$ J3 {; s! j
编写编译脚本在项目根目录下创建 scripts 文件夹,并在 scripts 文件夹下新建文件 compile.ts: - mkdir scripts && cd scripts && touch compile.ts
9 `7 L2 j( \0 T. {' E* ^
复制代码我们已经有了一个 compile.ts 文件,但还是让它在我们完成代码后方便运行吧。让我们在 package.json 文件中创建一个脚本快捷方式。只需添加以下脚本键即可: - {
) _# I [5 ?( T+ ~ - //...your previous package.json contents/ D) j, j: W* J3 @. n6 s8 p w
- "scripts": {
5 Y4 h" S1 M+ U* v2 A% I - "compile": "ts-node ./scripts/compile.ts"6 I* P$ |! i; a
- }- P1 H+ y1 r; J4 \/ h9 n, z8 C
- }
复制代码现在在编辑器中打开 scripts/compile.ts 文件,开始编写编译脚本。我们将一步一步地添加代码,并附带说明,因此在本节结束时,你将拥有编译脚本的完整代码。 首先,我们要导入:
: d) `2 D; j) P0 R, y/ P. H8 ]- import * as fs from "fs";5 }& Z4 p9 }+ ?! z) W
- import process from "process";
$ y1 K4 w7 l l) C - import { Cell } from "@ton/core";
* P4 |( |9 l: A - import { compileFunc } from "@ton-community/func-js";
复制代码我们将有一些异步代码,因此让我们创建一个异步函数,在内部运行这些代码: . R7 {+ s( X5 Z6 f. z) p. b/ _0 D
- import * as fs from "fs";$ L) N9 Z9 X) Z7 M" Z) H8 S
- import process from "process";- C8 T5 D3 K+ U/ F1 L( T3 o: f
- import { Cell } from "@ton/core"; w! k2 d9 O0 M# N
- import { compileFunc } from "@ton-community/func-js";
. ^7 ]6 M9 i: H" X" l8 {
; |6 G7 ~) A% z0 O% X9 P2 q0 k- async function compileScript() {
* G5 v2 V3 [8 g% p- [$ G
. y. S, b r a+ C: L- N- }& m H' N. T j$ q! h& }7 z' ^
- 1 C2 e% h# ?; j" l+ }4 N* y, b
- compileScript();
复制代码现在,我们将样本合约的文件名传入 compileFunc 函数,如果结果状态为错误,则退出脚本: 0 `- @+ V) v' H; R& h8 z
- import * as fs from "fs";
- X+ q; i' b* P% I) V" B - import process from "process";4 E4 G7 F4 y0 \$ x" }$ r; h' t9 g
- import { Cell } from "@ton/core";
0 U1 ?: Y$ S5 [0 A0 R4 x* F( s - import { compileFunc } from "@ton-community/func-js";5 M- p6 x4 r( l1 i6 T( y
- & i/ j) `+ X7 S! ]" r7 I; Y
- async function compileScript() {; h! y7 U1 ?$ K# z4 _
- * b6 u( Y) j0 ~+ J, f2 A0 h
- const compileResult = await compileFunc({
$ T' E, K1 v" g) O - targets: ["./contracts/main.fc"],
# X- ~( j) T7 [ - sources: (x) => fs.readFileSync(x).toString("utf8"),
3 W; F% H5 ^8 H6 C: \% ] - });
6 n. d/ Y4 C. c
- b @( J) L2 _5 m9 r- r- if (compileResult.status === "error") { W# a. I7 ^7 m; H8 O
- process.exit(1);
; k% Q& ]6 S' D% Z - }
* s5 J+ I$ l. Q - }
" [# t0 Y$ Y+ A - 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")
0 K1 q* W D) u; ?; y+ o* n/ S$ g
复制代码现在,我们将其放入脚本,并使用 fs.writeFileSync 将结果保存到 JSON 文件中: - import * as fs from "fs";
" m: A$ W" b) C1 c - import process from "process";4 c: z7 n" z S5 [% o& O
- import { Cell } from "@ton/core";
: [$ r+ e7 a5 x - import { compileFunc } from "@ton-community/func-js";
4 ~% W: I. h3 W" ~, f9 `2 m" |
, \4 l! V7 H( S2 p# a- async function compileScript() {
" U3 ?6 X$ D* F n" y4 y1 Z
0 r, s4 b3 Y. I& V- const compileResult = await compileFunc({ R7 l& c8 r9 y) U
- targets: ["./contracts/main.fc"],6 J. a6 x* X L" ~
- sources: (x) => fs.readFileSync(x).toString("utf8"),
( C, q7 A5 W# ]6 A, S# M2 D - });. ]- B6 U3 Z0 I, q$ b
- 5 Z6 z- b# v W1 o; x, ^
- if (compileResult.status === "error") {
3 c" d" v) o2 S6 q* s - process.exit(1);
* N4 e& X Z; } U - }
7 a, Q6 N% }4 i9 v3 j
% {: |+ C* g" Q; U4 j- const hexArtifact = `build/main.compiled.json`;
) o6 A: w( f* ?3 Y) J. I: B4 c( Q - 5 m$ O1 v1 t @' E4 g* ~2 ?
- fs.writeFileSync( X8 p' P+ Q& [' G# C
- hexArtifact,
* t4 U7 P- z: f3 p6 S4 d- m: X - JSON.stringify({
! \% i. w. `( D& [3 N# w& N - hex: Cell.fromBoc(Buffer.from(compileResult.codeBoc, "base64"))[0]
7 a$ Q: F8 A) ^/ n* R - .toBoc()
# N$ f) A1 |$ t$ W+ u: p - .toString("hex"),0 X) c6 a8 u- |6 \% z
- })
( \& o7 D5 B2 K0 q - );
8 r+ K* T: j5 i' U0 u1 O - }
0 p3 R* }+ c0 i( Y9 j* C - compileScript();
复制代码这段代码已经可以运行了,但让我们做一些最后的编辑,使我们的脚本在运行时能提供更多信息。我们将为每一步添加漂亮的控制台日志。
9 o' \. t3 S& b+ d, m以下是最终代码- f/ U8 x8 ^6 _1 E$ s% R6 j
- import * as fs from "fs";0 B8 t+ I+ E5 Z6 E2 L
- import process from "process";
$ X& D7 O! F( C; F4 p& e - import { Cell } from "@ton/core";3 v, U' T' x: A" M+ _; \" j9 {9 L
- import { compileFunc } from "@ton-community/func-js";+ L5 K+ u% A- m1 n( P: W' e
" W5 H5 f; Z' K( z! X+ }+ Y5 g9 \5 M- async function compileScript() {
* e7 K1 _* J3 d4 v7 V3 W4 J - console.log(
, c \3 ]3 o# V% M) { - "================================================================="
8 M( D5 d% D( E3 k& k# H - );
- r0 J: i) X& ^# }. `7 h% S - console.log(
" {5 [' ~4 I3 D/ b - "Compile script is running, let's find some FunC code to compile..."$ f( Q' x& z3 b5 R, N
- );8 j# V; i3 z9 Z3 J; b9 {$ C
0 n" ?! |3 F* E' V9 Y7 A4 `- const compileResult = await compileFunc({5 r3 T; w- l1 X( [
- targets: ["./contracts/main.fc"],: Q" Y$ ?. X7 L" P
- sources: (x) => fs.readFileSync(x).toString("utf8"),
. E% }: s7 K% c* ~$ U - });$ W9 H6 U7 z$ G3 C2 x" S
7 R" F3 r5 \. {! z$ D9 z4 ]5 F! X5 C- if (compileResult.status === "error") {
( O; J) ?" N- F/ X5 j) {. }) _4 @ - console.log(" - OH NO! Compilation Errors! The compiler output was:");
" {6 h; m* W2 ?* A% O - console.log(`\n${compileResult.message}`);2 ]6 {& q. \" ?" N
- process.exit(1);
* F# R4 O) H9 a# L0 Y( m* d - }
1 f* m+ g0 T4 R; k' @& W" y5 K - + [2 Q7 x; _/ G
- console.log(" - Compilation successful!");3 E) C5 O: p& O' }$ K
" g7 N6 k* }0 Y; q: r' l- \6 D5 e- const hexArtifact = `build/main.compiled.json`;; v0 ?: L' ~: g8 |. ?- y" R
/ w) `6 f2 B2 | L& U/ Q) g# z G; Q- fs.writeFileSync(% j- s/ e' ?! d: c1 d
- hexArtifact,
& B; b- L6 a1 Y& c1 W) t' p - JSON.stringify({+ K/ q1 U! U* p3 w
- hex: Cell.fromBoc(Buffer.from(compileResult.codeBoc, "base64"))[0]
" @: Z# [9 D% T; V3 B2 z9 u - .toBoc()
) K: X0 ]. x& b6 z, `* Q3 i - .toString("hex"),
5 A% w( T5 V1 O$ N, R2 f) s. s - })6 }% l9 k+ n! S: W- i U9 S! ]3 V5 E- d
- );: {# B" G3 [5 t
c* U# Y8 G; T9 y( l5 P! v- console.log(" - Compiled code saved to " + hexArtifact);, B" f3 r% f- j( R: L7 w# I( r
- }
. l' y/ B; r9 @+ B4 ?, ] - compileScript();
复制代码这是我们的编译脚本,每次修改代码时都要运行它。稍后,一旦我们有了一些可测试的 FunC 逻辑和部署脚本,我们就可以在任何测试或部署之前自动运行该脚本。 我们的 "卫星装配 "实验室设备越来越齐全! 不过,让我们再运行一次 yarn 编译,看看编译的结果如何。为此,让我们打开 main.compiled.fc 文件。 我们在那里看到了什么: - {"hex":"b5ee9c72410102010012000114ff00f4a413f4bcf2c80b010006d35f03fbffbf07"}+ q- B, n$ D* G4 R
复制代码听起来很疯狂,但这就是我们的合约。在下一课中,我们将编写第一个 FunC 逻辑,并将其与我们刚刚编写的脚本进行编译。 到时见!
( R! W/ q+ ~2 i! m& u
+ f6 q+ U& L5 I7 N1 K% h! z. g: s4 E$ X/ k4 M$ K0 Q+ s& g. @
|
|