|
本帖最后由 riyad 于 2025-3-7 00:19 编辑 # d$ j8 t4 R0 w; |
0 D) r- B8 t6 R% J1 G& v本课的目的是建立一个能够编译智能合约示例的本地项目。我们现在还不打算编写任何 FunC。我们只是在准备搭建我们的本地环境。这部分内容以后可以在所有项目中重复使用。 4 S* ?. b9 O+ c$ `
首先,请确保您拥有三样东西: 满足上述要求后,我们就可以开始了! 2 M* t) Q% C4 n2 P" p+ e( n; O
建立项目。首先为我们的项目创建一个文件夹 - mkdir my_first_contract && cd my_first_contract. H7 }2 n$ Z5 p
复制代码让我们启动 package.json 文件. 我要用 yarn, 但如果你觉得使用 npm 更好,你可以自由选择。 - yarn init
& l% P9 i. h* e o1 L
复制代码系统会提示你输入一些参数,但你只需在每个提示符下点击 Enter 按钮即可。完成后,项目目录下的 package.json 文件将包含以下默认内容: - {
; |0 f6 K0 ?" g. q; S* e$ K& i0 Z
% s( |. ?; I8 n( O- "name": "my_first_contract",
+ W; `: ` |2 Y/ ]$ \" v" k% D
4 c9 a( ]' E- L& m5 I' A4 A* |- "version": "1.0.0",- t- S. w+ Y) C1 f, e- n
. a4 o( \; R7 _: D6 l% D- "main": "index.js", //we don't really need this line, feel free to remove it
2 y( h6 c* F! S# o, _8 o, K" ~' s! F
, E9 e" }' X' c* a, z- "license": "MIT"
) y" C' q( r5 i! {' j1 F
8 K* R1 {+ r: ^- }
复制代码现在,让我们安装以下 4 个库: 这些都是与 TypeScript 相关的库。在本课程中,我们不会深入探讨 TypeScript 本身的工作原理。 - yarn add typescript ts-node @types/node @swc/core --dev
# J! n& |; z9 s) X1 K( M
复制代码我们创建一个 tsconfig.json 文件,并在其中添加以下配置: - {
- W9 O6 W# Z3 ]4 V( N2 \3 B
0 J- |9 z. t) v. A. ~- "compilerOptions": {
$ Q# a$ ~ d0 N- u7 R
6 d3 e' h0 F; e% {- "target": "es2020",( X7 D* C' Z; J; Z. s5 k
: [9 A+ ] p; c! w" Y( ^- "module": "commonjs",
" S% y7 l2 n& j) Q0 W
0 \1 g6 M3 q! w# L/ G) {6 S; W- "esModuleInterop": true,+ E% x3 I6 I+ g# p
7 J" @2 J1 K$ }) I. M# ~# v5 J- "forceConsistentCasingInFileNames": true,
9 y. w# i' u. G; L& y7 n, v - - l! y/ [1 T, F% ]$ B
- "strict": true,3 `. t+ y! Q, @" d4 O) l
- 7 o# V: g$ g, R9 {
- "skipLibCheck": true,
+ j3 B- y" I, L4 d! w3 k _) K7 O - " p! t7 v4 y' S% ~1 [
- "resolveJsonModule": true
9 s' [5 x/ R. W! K8 J - ! b( H+ e( @ b6 w: N- e& `, J
- },1 z2 X3 l* \/ R4 z* l
- ) f8 R) C# D+ t5 G8 m! `
- "ts-node": { c9 H* x5 y- S* i2 X8 s
7 o* ]$ Y& e2 ` {- "transpileOnly": true," @) L( A5 C; e* d% Y$ W" r8 `5 Q1 j
- " x) c, G6 |& q+ g) S7 |
- "transpiler": "ts-node/transpilers/swc"
3 c+ L* Z7 h" M/ Y. M7 U: L - ! K* v" F3 u5 v) @) [, ~ {
- }
- r( L5 y9 m% t' O- H8 o9 B% \ - 2 {! L$ Q, D% P! j; P2 Z
- }
复制代码实际上,我们还需要三个与 TON 有关的 lib,它们是: ton-core - 实现 TON 区块链底层基元的核心库。 ton-crypto - 用于为 TON 区块链构建应用程序的加密基元。 @ton-community/func-js - TON FunC 编译器
7 N) A4 E, _* n8 d6 Q+ W
4 B! ]6 z& j/ H9 o [. r- yarn add @ton/core ton-crypto @ton-community/func-js --dev
% a" y, J. B) }
复制代码 FunC 代码示例现在,我们将创建一个包含最小 FunC 代码示例的文件,然后编写一个可以编译它的脚本。 我们创建一个 contracts 文件夹,并在其中创建一个 FunC 文件(main.fc): - mkdir contracts && cd contracts && touch main.fc! m6 j" v, B' l. N# U2 y x& C8 f
复制代码打开 main.fc 文件进行编辑,并插入以下示例 FunC 代码: - () recv_internal(int msg_value, cell in_msg, slice in_msg_body) impure {
) N* ]+ T8 d7 _ e$ `
- h. }& x9 C5 x- }
复制代码 您已经知道 TON 智能合约可以接收两种信息,如果您不清楚这些信息是什么,请参阅第 1 章。这些简单的代码就足够我们编写一个编译器脚本了。我们开始吧!
! v0 E4 r* O) q6 c: h9 k' b% n6 ^ ~+ f z% Z2 ]
编写编译脚本在项目根目录下创建 scripts 文件夹,并在 scripts 文件夹下新建文件 compile.ts: - mkdir scripts && cd scripts && touch compile.ts
: A; Z9 k- f4 m. O
复制代码我们已经有了一个 compile.ts 文件,但还是让它在我们完成代码后方便运行吧。让我们在 package.json 文件中创建一个脚本快捷方式。只需添加以下脚本键即可: - {8 W, D( m4 ] Y$ k! K" [7 i. M
- //...your previous package.json contents
6 n3 i2 c, D8 v2 [( [4 ^( n8 A - "scripts": {
& m3 ^% a. X/ v' C - "compile": "ts-node ./scripts/compile.ts"9 o8 F! {! L# n$ F' |/ f; j
- }
5 W* N+ d& N! h, }8 B - }
复制代码现在在编辑器中打开 scripts/compile.ts 文件,开始编写编译脚本。我们将一步一步地添加代码,并附带说明,因此在本节结束时,你将拥有编译脚本的完整代码。 首先,我们要导入: " h2 w6 I/ ]: r. M. U6 a7 C% @, X& B
- import * as fs from "fs";
: ]5 g8 ^2 p7 j _( V - import process from "process";/ C9 n, X3 t, B" c6 }
- import { Cell } from "@ton/core";
& P n4 r% m' F6 t - import { compileFunc } from "@ton-community/func-js";
复制代码我们将有一些异步代码,因此让我们创建一个异步函数,在内部运行这些代码:
, P* u( {. x; a( A# i- import * as fs from "fs";, P' O& M( V! i8 W) W' O2 l
- import process from "process";
9 E' [& f" R# U5 T% x" P2 X0 }$ O - import { Cell } from "@ton/core";
0 {8 |: y# l9 M: r1 }% n - import { compileFunc } from "@ton-community/func-js";7 R- f) n, ? R: B$ I
. A* {$ E5 Z" }2 ] w3 f9 B- async function compileScript() {
) R3 @' f9 s; d - & I' o! `7 I8 T
- }
2 c% r9 u- d7 O9 f0 p& d - : o: c2 S( Q! Z' h1 l* H
- compileScript();
复制代码现在,我们将样本合约的文件名传入 compileFunc 函数,如果结果状态为错误,则退出脚本: 4 e" v" B9 G N" G8 X: K3 `) ?2 w7 D
- import * as fs from "fs";% d, _; T- V; ~
- import process from "process";) s. p6 r6 H: m! `3 v8 A) D
- import { Cell } from "@ton/core";
! Z. L- Z1 k8 ?6 i' y6 ^8 r/ l% P - import { compileFunc } from "@ton-community/func-js";
2 [* b0 X* g% t2 ^& B- J - / o* m* f7 W1 A+ i
- async function compileScript() {+ m) D! a7 L6 A/ X" X0 l* N
2 d$ @: X4 i9 Z1 `- const compileResult = await compileFunc({& f* {8 L. `. c
- targets: ["./contracts/main.fc"],
$ j% j! o* M ^: L3 K - sources: (x) => fs.readFileSync(x).toString("utf8"),
9 Z8 H7 A0 H, I - });
+ H- ` g2 K( x/ I G1 E$ v
! U3 P! E, T- u& q' j% Q; C. L- if (compileResult.status === "error") {* s# e7 g2 s p; } j
- process.exit(1);( y. D; |1 p" L- k" o" |
- }
3 ]$ c, _) r1 v9 Y1 x) u+ X5 r - }
8 {/ U3 h; t6 G2 } r3 f - 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")+ W9 p+ m( K; j5 e, O1 g0 ~
复制代码现在,我们将其放入脚本,并使用 fs.writeFileSync 将结果保存到 JSON 文件中: - import * as fs from "fs";
$ H( U) V6 h6 d, ~ - import process from "process";
' F2 ]5 B# L( m( S2 H% x& \* T4 { - import { Cell } from "@ton/core";
" k% _0 j' |. \6 W6 [0 z9 U, H. S - import { compileFunc } from "@ton-community/func-js";; @0 \. j9 G0 p6 y
$ R; _7 a- c' p5 L2 J, ^, G- async function compileScript() {1 i' S8 Y/ `% t" ~. }; F# i9 }
- : p! J) x+ {0 F& O6 G
- const compileResult = await compileFunc({
& A; M2 N: F8 r) r1 o - targets: ["./contracts/main.fc"],
: n& j$ h" ~- f# ? - sources: (x) => fs.readFileSync(x).toString("utf8"),* _/ F% K4 X+ |3 U) f* z+ z8 i
- });
3 N3 ?! N3 Z9 B - / j# V9 d5 u- u7 u( t6 [
- if (compileResult.status === "error") {
4 Q1 Z. e. r7 g- q) _+ @+ n" ]; T - process.exit(1); {& A4 q1 A& E0 C( L/ j6 m/ `
- }9 Z; ]" c$ W5 i6 f0 g1 j4 q1 z3 m
- 4 G5 W6 e+ K0 P
- const hexArtifact = `build/main.compiled.json`;. T( Z/ E& W4 I& Q& R! @
- 4 T ~. t# K( r) h# @
- fs.writeFileSync(
/ S. v X4 x0 j% {4 q2 b/ O: g - hexArtifact,6 d* l* j/ L) o
- JSON.stringify({, F) ~# g/ ]( w8 F: p% L+ c
- hex: Cell.fromBoc(Buffer.from(compileResult.codeBoc, "base64"))[0]: h4 v. k3 i$ J- ]0 \
- .toBoc()2 {1 `' F% X' e( k
- .toString("hex"),
- q$ @* A, A% P4 T - })* p! L( D: a2 @
- );* Q, b. M, I) r3 I/ I
- }/ I& s- b6 _4 o3 M
- compileScript();
复制代码这段代码已经可以运行了,但让我们做一些最后的编辑,使我们的脚本在运行时能提供更多信息。我们将为每一步添加漂亮的控制台日志。
# x5 R1 z& n! l; Y! A U( Z以下是最终代码; w- c) `$ U3 ?5 K5 X
- import * as fs from "fs";+ d2 P5 C( k# S
- import process from "process";- x$ u7 w6 y& [
- import { Cell } from "@ton/core";
' O. P( e& i5 C1 s+ N - import { compileFunc } from "@ton-community/func-js";3 q& c. ]+ w" `7 M) [ m
% `; B/ M/ i! L6 w- async function compileScript() {
4 n, L3 g' o! R; w" k - console.log(" e% g% E# ~ M& R: T) @
- "================================================================="
( O6 s1 {8 ?0 H: C6 C0 d - );
9 n* W' V# P- j6 ^ - console.log(8 r( m' R* U3 w
- "Compile script is running, let's find some FunC code to compile...". O2 D6 ^; x1 j
- );% x2 E+ U# P: a5 S1 Z7 X
# v, p/ P! O2 J- j( F. F- const compileResult = await compileFunc({# Z8 G/ i8 x( \+ o: `4 j
- targets: ["./contracts/main.fc"]," C' d0 g* }" d& K! X) e3 z+ T
- sources: (x) => fs.readFileSync(x).toString("utf8"),
, {/ k6 [8 \6 x* m* F - });; Q8 ]$ w. N$ I% \) {3 U
. m/ G+ F& f3 J) n- if (compileResult.status === "error") {
! G; f' J. H- |+ h6 ~1 N - console.log(" - OH NO! Compilation Errors! The compiler output was:");
+ ^( P# a; u1 Q8 D. ~+ } - console.log(`\n${compileResult.message}`);
/ y1 Z% \4 V0 f& Z7 t' E/ h' p - process.exit(1);; B& R; D3 s. j0 T
- }
9 @1 P7 _" b! C' }* ?/ z - 7 K+ [% B n% q& J
- console.log(" - Compilation successful!");
1 h8 f& e. K! n7 j - $ Z* N+ e& f9 q! M/ D _3 ~8 Z
- const hexArtifact = `build/main.compiled.json`;0 v6 W* x, _& {) c+ g' G
- & S% F0 \; e8 Z6 ?
- fs.writeFileSync(3 N0 n$ ?2 D" x4 Y- Z! U$ w1 z7 Z
- hexArtifact,
$ L) C% F5 y" n6 X6 r6 x p - JSON.stringify({. n U \/ {) m6 w. [9 B
- hex: Cell.fromBoc(Buffer.from(compileResult.codeBoc, "base64"))[0]. Y' B) ]8 V) }$ m0 F0 N" z
- .toBoc()
4 r4 e% J& h+ Z* |" M4 v8 }8 y - .toString("hex"),% |, Y- u2 G: {# A8 d: \2 c" @$ L
- })
- t* C# v4 O0 y/ C' o) Y - );
" e5 ?# U! k! f/ @' o5 b5 q8 o - ( }1 }# \' {/ |7 E6 T& O+ T
- console.log(" - Compiled code saved to " + hexArtifact);
$ q A1 r+ E6 C& T- s+ R9 Z8 e y - }, C& i2 r k; E* v; U2 T* ^# H
- compileScript();
复制代码这是我们的编译脚本,每次修改代码时都要运行它。稍后,一旦我们有了一些可测试的 FunC 逻辑和部署脚本,我们就可以在任何测试或部署之前自动运行该脚本。 我们的 "卫星装配 "实验室设备越来越齐全! 不过,让我们再运行一次 yarn 编译,看看编译的结果如何。为此,让我们打开 main.compiled.fc 文件。 我们在那里看到了什么: - {"hex":"b5ee9c72410102010012000114ff00f4a413f4bcf2c80b010006d35f03fbffbf07"}
! k# Q; ?, l( c8 p0 o/ s" m X/ h
复制代码听起来很疯狂,但这就是我们的合约。在下一课中,我们将编写第一个 FunC 逻辑,并将其与我们刚刚编写的脚本进行编译。 到时见!
* K2 |! O3 ~* }
. x$ v/ I8 h# `: q$ V9 l! ^0 D3 j# W; h7 V
|
|