前置工作
这一章的目的是建立一个能够编译 smartcontract 示例的本地项目。我们现在还不打算编写任何 FunC,只是在准备‘实验室’。这部分内容以后可以在所有项目中重复使用。
首先,请确保拥有以下三项工作已经完善:
- 现代版本的 Node.js(版本 16.15.0 或更高版本) 安装说明可在此处。在终端运行 node -v 以检查安装情况。
- 软件包管理器 - Node.js 安装的时候可能会自动安装。 Yarn、NPM,都可以。
- 支持 FunC 和 TypeScript 的集成开发环境 ,建议使用安装了 FunC 插件的 Visual Studio Code。如果您使用的是 IntelliJ,下面是相应的 FunC 插件链接。
创建项目
首先为我们的项目创建一个文件夹:
mkdir my_first_contract && cd my_first_contract
通过yarn/npm来创建一个package.json 文件来管理包。
yarn init
console会提示你输入一些参数,但暂时只需不停Enter 即可。完成后,项目目录下的 package.json 文件将包含以下默认内容:
{ "name": "my_first_contract", "version": "1.0.0", "main": "index.js", "license": "MIT" }
接下来再安装一些跟TS相关的libraries,便于后续的测试脚本编写。
yarn add typescript ts-node @types/node @swc/core --dev
在项目根目录下创建一个 tsconfig.json 文件,并在其中添加以下配置:
{ "compilerOptions": { "target": "es2020", "module": "commonjs", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true, "resolveJsonModule": true }, "ts-node": { "transpileOnly": true, "transpiler": "ts-node/transpilers/swc" } }
我们还需要三个与 TON 相关的库,分别是
- ton-core - 实现 TON 区块链底层基元的核心库。
- @ton-crypto - 用于构建 TON 区块链应用程序的加密基元。
- @ton-community/func-js - TON FunC 编译器。
yarn add @ton/core ton-crypto @ton-community/func-js --dev
FunC示例代码
现在,我们要创建一个包含 FunC 代码示例的文件,然后编写一个可以编译它的脚本。
创建一个 contracts 文件夹,并在其中创建一个 FunC 文件 (main.fc):
mkdir contracts && cd contracts && touch main.fc
打开 main.fc 文件进行编辑,并插入以下 FunC 代码:
() recv_internal(int msg_value, cell in_msg, slice in_msg_body) impure { }
这些简单的代码就足够我们编写一个编译器脚本了.
撰写编译脚本
在项目根目录下创建 scripts 文件夹,并在 scripts 文件夹下新建文件 compile.ts:
mkdir scripts && cd scripts && touch compile.ts
我们已经有了一个 compile.ts 文件了,然后在 package.json 文件中创建一个脚本快捷方式。
{ //...之前的那些package.JASOn内容 "scripts": { "compile": "ts-node ./scripts/compile.ts" } }
现在在编辑器中打开 scripts/compile.ts 文件,开始编写编译脚本。
首先,我们将导入以下库
- fs 用于处理文件
- process 用于控制脚本执行过程
- Cell 构造函数(合同的字节码将存储为 Cell)
- compileFunc - 实际编译函数
import * as fs from "fs"; import process from "process"; import { Cell } from "@ton/core"; import { compileFunc } from "@ton-community/func-js";
接着创建一个异步函数,在内部运行这些代码:
async function compileScript() { const compileResult = await compileFunc({ targets: ["./contracts/main.fc"], sources: (x) => fs.readFileSync(x).toString("utf8"), }); if (compileResult.status === "error") { process.exit(1); } } compileScript();
从技术上讲,现在已经可以进入项目的根目录,运行 yarn compile命令。但这样做的话,无法看到编译的结果。现在把编译结果保存到项目根目录下的 build 文件夹中(先创建了这个文件夹)。
由于编译的结果将是一个 BOC(Body of Cell,单元格的主体)base64 字符串,如果想在本地进一步处理编译后的合约(编写测试时),就需要构建一个包含该 BOC 的单元格,将其存储起来以备后用。
Cell constructor 有一个 .fromBoc 函数,该函数通过接收 Buffer创建Cell,我们将向它提供一个 BOC base64 string 的Buffer。一旦我们从 BOC 构建了一个 Cell,我们就为这个 Cell 创建一个十六进制的表示并将其存储到一个 JSON 文件中。这就是单元格构建和转换命令的样子:
Cell.fromBoc(Buffer.from(compileResult.codeBoc, "base64"))[0].toBoc() .toString("hex")
现在,将其放入脚本中,并用 fs.writeFileSync 将结果保存到 JSON 文件中,添加一些console指令让整个过程更直观:
import * as fs from "fs"; import process from "process"; import { Cell } from "@ton/core"; import { compileFunc } from "@ton-community/func-js"; async function compileScript() { console.log( "=================================================================" ); console.log( "Compile script is running, let's find some FunC code to compile..." ); const compileResult = await compileFunc({ targets: ["./contracts/main.fc"], sources: (x) => fs.readFileSync(x).toString("utf8"), }); if (compileResult.status === "error") { console.log(" - OH NO! Compilation Errors! The compiler output was:"); console.log(`\n${compileResult.message}`); process.exit(1); } console.log(" - Compilation successful!"); const hexArtifact = `build/main.compiled.json`; fs.writeFileSync( hexArtifact, JSON.stringify({ hex: Cell.fromBoc(Buffer.from(compileResult.codeBoc, "base64"))[0] .toBoc() .toString("hex"), }) ); console.log(" - Compiled code saved to " + hexArtifact); } compileScript();
这是我们的编译脚本compile.ts,每次修改代码后都要运行它。以后,一旦需要进行FunC 逻辑和部署脚本的测试,都可以自动运行这个脚本。
最后再运行一次yarn compile,编译的结果应该出现在build文件夹下,一个main.compile.json文件中:
{"hex":"b5ee9c72410102010012000114ff00f4a413f4bcf2c80b010006d35f03fbffbf07"}
这就是我们的合同。在下一章中将编写第一个 FunC 逻辑,并用刚刚写好的scripts脚本编译。