【TON】【官网基础系列二】TON 测试工作流程编写

【TON】【官网基础系列二】TON 合约测试

下载依赖

yarn add @ton/sandbox jest ts-jest @types/jest @ton/ton @ton/test-util  --dev

TON 开发了 sanbox 库仿真 TON 智能合约,本章节针对 sandboxjest 展开 TON 合约测试。

代码实现

main.fc

contracts/main.fc FunC 实现合约代码为基础教程 3.3 章节的内容,stdlib.fc 下载地址如下,放在 contracts/imports 目录下。

#include "imports/stdlib.fc";

() recv_internal(int msg_value, cell in_msg, slice in_msg_body) impure {
    slice cs = in_msg.begin_parse();
    int flags = cs~load_uint(4); 
    slice sender_address = cs~load_msg_addr();

    set_data(begin_cell().store_slice(sender_address).end_cell());
}

slice get_the_latest_sender() method_id {
    slice ds = get_data().begin_parse();
    return ds~load_msg_addr();
}

接下来我们来看看这段代码干了啥:

其中 msg_valuein_msg_body 都可以从 in_msg 派生,但为了便于使用,我们将它们作为参数接收到 receive_internal() 函数中。

  • msg_value :参数表明该报文收到了多少TON币(或gram)。
  • in_msg :收到的一条完整信息,包含发送者等所有信息。我们可以看到它的类型是 Cell。这意味着什么呢?信息正文作为一个cell 存储在 TVM 上,因此有一个完整的Cell专门用于存储我们的信息及其所有数据。
  • in_msg_body :表示收到的信息中实际可读的部分。是一个 slice 类型,是 Cell 的一部分,指出了如果我们要读取这个片段参数,应该从Cell的哪个部分开始读取的 “地址”。

什么是 Cell (TODO)

传入函数后的修饰参数可选的有impureinline/inline_refmethod_id 三个:

  • impure 用于表示修改合约状态,处理外部消息和执行状态更新操作;函数未指定 impure 且函数调用的结果未被使用,FunC 编译器可能会删除该函数调用。
  • inline/inline_ref 用于定义内联函数,常用于提高性能并避免函数调用开销。
  • method_id 用于定义公共函数,允许通过外部消息调用合约中的特定函数。

jest.config.js

jest 配置文件:

module.exports = {
    preset: 'ts-jest',
    testEnvironment: 'node',
};

什么是 jest(TODO)

package.json

添加jest 相关指令:

{
  ...
  "scripts": {
    "compile": "ts-node ./scripts/compile.ts",
    "test": "yarn compile && yarn jest"
  },
  ...

MainContract.ts

wrapper/MainContract.ts 该文件将实现并导出我们合约的包装器,确保从项目根目录运行此命令序列。

import { Address, beginCell, Cell, Contract, contractAddress, ContractProvider, Sender, SendMode } from "@ton/core";

export class MainContract implements Contract {
  constructor(
    readonly address: Address,
    readonly init?: { code: Cell; data: Cell }
  ) {}

  static createFromConfig(config: any, code: Cell, workchain = 0) {
    const data = beginCell().endCell();
    const init = { code, data };
    const address = contractAddress(workchain, init);

    return new MainContract(address, init);
  }

  async sendInternalMessage(
    provider: ContractProvider,
    sender: Sender,
    value: bigint
  ) {
    await provider.internal(sender, {
      value,
      sendMode: SendMode.PAY_GAS_SEPARATELY,
      body: beginCell().endCell(),
    });
  }

  async getData(provider: ContractProvider) {
    const { stack } = await provider.get("get_the_latest_sender", []);
    return {
      recent_sender: stack.readAddress(),
    };
  }
}

为什么需要这个 wrapper(TODO)

main.spec.ts

tests/main.spec.ts 代码如下:

import { Cell, toNano } from "@ton/core";
import { hex } from "../build/main.compiled.json";
import { Blockchain } from "@ton/sandbox";
import { MainContract } from "../wrappers/MainContract";
import "@ton/test-utils";

describe("main.fc contract tests", () => {
  it("should get the proper most recent sender address", async () => {
    const blockchain = await Blockchain.create();
    const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];

    const myContract = blockchain.openContract(
      await MainContract.createFromConfig({}, codeCell)
    );

    const senderWallet = await blockchain.treasury("sender");

    const sentMessageResult = await myContract.sendInternalMessage(
      senderWallet.getSender(),
      toNano("0.05")
    );

    expect(sentMessageResult.transactions).toHaveTransaction({
      from: senderWallet.address,
      to: myContract.address,
      success: true,
    });

    const data = await myContract.getData();

    expect(data.recent_sender.toString()).toBe(senderWallet.address.toString());
  });
});

这个文件又是干嘛的

小结

参考资料(TODO)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值