Skip to content

benches: add jetton tests #3249

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions dev-docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ The code generator lives in the [`src/generator`](../src/generator) sub-folder w
The implementation that we have right now is being refactored to produce FunC ASTs and then pretty-print those ASTs as strings instead of producing source FunC code in one step. Here is the relevant pull request: <https://github.com/tact-lang/tact/pull/559>.

One can find the end-to-end codegen test spec files in the [`src/test/e2e-emulated`](../src/test/e2e-emulated) folder. The test contracts are located in the subfolders of the [`src/test/e2e-emulated`](../src/test/e2e-emulated) folder. Many of those spec files test various language features in relative isolation.
An important spec file that tests argument passing semantics for functions and assignment semantics for variables is here: [`src/test/e2e-emulated/semantics.spec.ts`](../src/test/e2e-emulated/functions/semantics.spec.ts).
An important spec file that tests argument passing semantics for functions and assignment semantics for variables is here: [`src/test/e2e-emulated/functions/semantics.spec.ts`](../src/test/e2e-emulated/functions/semantics.spec.ts).

Contracts with `inline` in the name of the file set `experimental.inline` config option to `true`.
Contracts with `external` in the name of the file set the `external` config option to `true`.
Expand All @@ -380,13 +380,14 @@ Benchmarks are located inside `src/benchmarks/`, one directory per benchmark:

#### File & folder roles

| Path / file | Purpose |
| ------------------------ | -------------------------------------------------------- |
| `tact/` | Tact project that is being benchmarked |
| `func/` | Equivalent FunC project that we compare against |
| `<benchmark>.spec.ts` | Jest test spec that prepares and runs the benchmark |
| `results_gas.json` | Aggregated gas‑consumption results, updated by the CLI |
| `results_code_size.json` | Contract byte‑code size history, also updated by the CLI |
| Path / file | Purpose |
| --------------- | -------------------------------------------------------- |
| `tact/` | Tact project that is being benchmarked |
| `func/` | Equivalent FunC project that we compare against |
| `test.spec.ts` | Jest test spec for contract functionality testing |
| `bench.spec.ts` | Jest test spec for performance benchmarking |
| `gas.json` | Aggregated gas‑consumption results, updated by the CLI |
| `size.json` | Contract byte‑code size history, also updated by the CLI |

> **CLI support** – All commands for creating, updating, or comparing benchmarks are documented in the [Updating Benchmarks](#benchmarks) section.

Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@
"test": "jest",
"test:fast": "jest --config=./jest-fast.config.js",
"test:allure": "rimraf ./allure-results && yarn test && allure serve allure-results",
"bench": "yarn gen:contracts:benchmarks && cross-env PRINT_TABLE=true jest ./src/benchmarks",
"bench:ci": "yarn gen:contracts:benchmarks && cross-env PRINT_TABLE=false jest ./src/benchmarks",
"bench": "yarn gen:contracts:benchmarks && cross-env PRINT_TABLE=true jest --testMatch=\"**/src/benchmarks/**/bench.spec.ts\"",
"bench:test": "yarn gen:contracts:benchmarks && cross-env PRINT_TABLE=false jest --testMatch=\"**/src/benchmarks/**/test.spec.ts\"",
"bench:ci": "yarn gen:contracts:benchmarks && cross-env PRINT_TABLE=true jest --testMatch=\"**/src/benchmarks/**/bench.spec.ts\" && cross-env PRINT_TABLE=false jest --testMatch=\"**/src/benchmarks/**/test.spec.ts\"",
"bench:update": "yarn gen:contracts:benchmarks && cross-env PRINT_TABLE=true ts-node src/benchmarks/update.build.ts",
"bench:add": "ts-node src/benchmarks/prompt.build.ts && yarn gen:contracts:benchmarks && cross-env PRINT_TABLE=true ADD=true ts-node src/benchmarks/update.build.ts",
"coverage": "cross-env COVERAGE=true NODE_OPTIONS=--max_old_space_size=5120 jest --config=./jest-ci.config.js",
Expand Down
1 change: 1 addition & 0 deletions spell/cspell-list.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAU
Aksakov
Aliaksandr
alnum
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import type {
Cancel,
} from "@/benchmarks/escrow/tact/output/escrow_Escrow";

import benchmarkResults from "@/benchmarks/escrow/results_gas.json";
import benchmarkCodeSizeResults from "@/benchmarks/escrow/results_code_size.json";
import benchmarkResults from "@/benchmarks/escrow/gas.json";
import benchmarkCodeSizeResults from "@/benchmarks/escrow/size.json";

const loadFunCEscrowBoc = () => {
const bocEscrow = readFileSync(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,65 +1,39 @@
import "@ton/test-utils";
import {
Address,
Cell,
beginCell,
toNano,
contractAddress,
type Sender,
} from "@ton/core";
import { Address, Cell, beginCell, toNano, type Sender } from "@ton/core";

import { Blockchain } from "@ton/sandbox";
import type { SandboxContract, TreasuryContract } from "@ton/sandbox";
import {
generateResults,
getStateSizeForAccount,
generateCodeSizeResults,
getUsedGas,
printBenchmarkTable,
type BenchmarkResult,
type CodeSizeResult,
} from "@/benchmarks/utils/gas";
import { resolve } from "path";
import { readFileSync } from "fs";
import { posixNormalize } from "@/utils/filePath";
import {

import type {
JettonMinter,
type JettonUpdateContent,
type Mint,
type ProvideWalletAddress,
JettonUpdateContent,
Mint,
ProvideWalletAddress,
} from "@/benchmarks/jetton/tact/output/minter_JettonMinter";

import {
JettonWallet,
type JettonTransfer,
type JettonBurn,
} from "@/benchmarks/jetton/tact/output/minter_JettonWallet";

import benchmarkResults from "@/benchmarks/jetton/results_gas.json";
import benchmarkCodeSizeResults from "@/benchmarks/jetton/results_code_size.json";
import { step, parameter } from "@/test/allure/allure";
import type {
FromInitMinter,
FromInitWallet,
} from "@/benchmarks/jetton/tests/utils";

const loadFunCJettonsBoc = () => {
const bocMinter = readFileSync(
posixNormalize(
resolve(__dirname, "./func/output/jetton-minter-discoverable.boc"),
),
);

const bocWallet = readFileSync(
posixNormalize(resolve(__dirname, "./func/output/jetton-wallet.boc")),
);

return { bocMinter, bocWallet };
};

function testJetton(
function benchJetton(
benchmarkResults: BenchmarkResult,
codeSizeResults: CodeSizeResult,
fromInit: (
totalSupply: bigint,
owner: Address,
jettonContent: Cell,
) => Promise<JettonMinter>,
fromInit: FromInitMinter,
_fromInitWallet: FromInitWallet,
) {
let blockchain: Blockchain;
let deployer: SandboxContract<TreasuryContract>;
Expand Down Expand Up @@ -339,50 +313,6 @@ function testJetton(
});
}

describe("Jetton Gas Tests", () => {
const fullResults = generateResults(benchmarkResults);
const fullCodeSizeResults = generateCodeSizeResults(
benchmarkCodeSizeResults,
);

describe("func", () => {
const funcCodeSize = fullCodeSizeResults.at(0)!;
const funcResult = fullResults.at(0)!;

function fromInit(salt: bigint, admin: Address, _content: Cell) {
const jettonData = loadFunCJettonsBoc();
const minterCell = Cell.fromBoc(jettonData.bocMinter)[0]!;
const walletCell = Cell.fromBoc(jettonData.bocWallet)[0]!;

const stateInitMinter = beginCell()
.storeCoins(0)
.storeAddress(admin)
.storeRef(beginCell().storeUint(1, 1).endCell()) // as salt
.storeRef(walletCell)
.endCell();

const init = { code: minterCell, data: stateInitMinter };
const address = contractAddress(0, init);
return Promise.resolve(new JettonMinter(address, init));
}

testJetton(funcResult, funcCodeSize, fromInit);
});

describe("tact", () => {
const tactCodeSize = fullCodeSizeResults.at(-1)!;
const tactResult = fullResults.at(-1)!;
testJetton(
tactResult,
tactCodeSize,
JettonMinter.fromInit.bind(JettonMinter),
);
});
import { run } from "@/benchmarks/jetton/run";

afterAll(() => {
printBenchmarkTable(fullResults, fullCodeSizeResults, {
implementationName: "FunC",
printMode: "full",
});
});
});
run(benchJetton);
120 changes: 120 additions & 0 deletions src/benchmarks/jetton/run.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import "@ton/test-utils";
import { type Address, Cell, beginCell, contractAddress } from "@ton/core";

import {
generateResults,
generateCodeSizeResults,
printBenchmarkTable,
type BenchmarkResult,
type CodeSizeResult,
} from "@/benchmarks/utils/gas";
import { resolve } from "path";
import { readFileSync } from "fs";
import { posixNormalize } from "@/utils/filePath";
import { JettonMinter } from "@/benchmarks/jetton/tact/output/minter_JettonMinter";
import { JettonWallet } from "@/benchmarks/jetton/tact/output/minter_JettonWallet";

import benchmarkResults from "@/benchmarks/jetton/gas.json";
import benchmarkCodeSizeResults from "@/benchmarks/jetton/size.json";
import type {
FromInitMinter,
FromInitWallet,
} from "@/benchmarks/jetton/tests/utils";

const loadFunCJettonsBoc = () => {
const bocMinter = readFileSync(
posixNormalize(
resolve(__dirname, "./func/output/jetton-minter-discoverable.boc"),
),
);

const bocWallet = readFileSync(
posixNormalize(resolve(__dirname, "./func/output/jetton-wallet.boc")),
);

return { bocMinter, bocWallet };
};
export const run = (
testJetton: (
benchmarkResults: BenchmarkResult,
codeSizeResults: CodeSizeResult,
fromInitMinter: FromInitMinter,
fromInitWallet: FromInitWallet,
) => void,
) => {
describe("Jetton Gas Tests", () => {
const fullResults = generateResults(benchmarkResults);
const fullCodeSizeResults = generateCodeSizeResults(
benchmarkCodeSizeResults,
);

describe("func", () => {
const funcCodeSize = fullCodeSizeResults.at(0)!;
const funcResult = fullResults.at(0)!;

const fromInitMinter = (
salt: bigint,
admin: Address,
_content: Cell,
) => {
const jettonData = loadFunCJettonsBoc();
const minterCell = Cell.fromBoc(jettonData.bocMinter)[0]!;
const walletCell = Cell.fromBoc(jettonData.bocWallet)[0]!;

const stateInitMinter = beginCell()
.storeCoins(0)
.storeAddress(admin)
.storeRef(beginCell().storeUint(1, 1).endCell()) // as salt
.storeRef(walletCell)
.endCell();

const init = { code: minterCell, data: stateInitMinter };
const address = contractAddress(0, init);
return Promise.resolve(new JettonMinter(address, init));
};

const fromInitWallet = (
owner: Address,
jettonMinter: Address,
jettonAmount: bigint,
) => {
const __code = Cell.fromBoc(loadFunCJettonsBoc().bocWallet)[0]!;
const __data = beginCell()
.storeCoins(jettonAmount)
.storeAddress(owner)
.storeAddress(jettonMinter)
.storeRef(__code)
.endCell();

const __gen_init = { code: __code, data: __data };
const address = contractAddress(0, __gen_init);
return Promise.resolve(new JettonWallet(address, __gen_init));
};

testJetton(
funcResult,
funcCodeSize,
fromInitMinter,
fromInitWallet,
);
});

describe("tact", () => {
const tactCodeSize = fullCodeSizeResults.at(-1)!;
const tactResult = fullResults.at(-1)!;
testJetton(
tactResult,
tactCodeSize,
JettonMinter.fromInit.bind(JettonMinter),
JettonWallet.fromInit.bind(JettonWallet),
);
});

afterAll(() => {
printBenchmarkTable(fullResults, fullCodeSizeResults, {
implementationName: "FunC",
printMode: "full",
});
});
});
};
4 changes: 3 additions & 1 deletion src/benchmarks/jetton/tact/wallet.tact
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import "./messages";

asm fun forceBasechainFunc(address: Address) { REWRITESTDADDR DROP 333 THROWIF }

contract JettonWallet(
owner: Address,
master: Address,
Expand All @@ -9,7 +11,7 @@ contract JettonWallet(
const gasConsumption: Int = ton("0.015");

receive(msg: JettonTransfer) {
forceBasechain(msg.destination);
forceBasechainFunc(msg.destination);
throwUnless(705, sender() == self.owner);

self.balance -= msg.amount;
Expand Down
23 changes: 23 additions & 0 deletions src/benchmarks/jetton/test.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { BenchmarkResult, CodeSizeResult } from "@/benchmarks/utils/gas";
import { run } from "@/benchmarks/jetton/run";
import { testMinter } from "@/benchmarks/jetton/tests/minter";
import { testWallet, testBounces } from "@/benchmarks/jetton/tests/wallet";
import type {
FromInitMinter,
FromInitWallet,
} from "@/benchmarks/jetton/tests/utils";

const testJetton = (
_benchmarkResults: BenchmarkResult,
_codeSizeResults: CodeSizeResult,
fromInitMinter: FromInitMinter,
fromInitWallet: FromInitWallet,
) => {
testMinter(fromInitMinter, fromInitWallet);
testWallet(fromInitMinter, fromInitWallet);
testBounces(fromInitMinter, fromInitWallet);
};

describe("jetton", () => {
run(testJetton);
});
Loading