From c64a6c030b6ceae2a9e8462ced9eb1f5431e299f Mon Sep 17 00:00:00 2001 From: Shvetc Andrei <91282981+Shvandre@users.noreply.github.com> Date: Wed, 29 Jan 2025 15:00:33 +0300 Subject: [PATCH] refactor: optimize `readForwardFee` stdlib function (#1614) --- src/stdlib/stdlib.ts | 7 ++- src/stdlib/stdlib/std/context.tact | 18 ++++--- src/test/benchmarks/benchmarks.spec.ts | 52 ++++++++++++++----- .../contracts/benchmark_functions.tact | 13 ++++- src/test/e2e-emulated/contracts/stdlib.tact | 12 +++++ src/test/e2e-emulated/stdlib.spec.ts | 13 +++-- 6 files changed, 86 insertions(+), 29 deletions(-) diff --git a/src/stdlib/stdlib.ts b/src/stdlib/stdlib.ts index 85dedf05f..ded72e632 100644 --- a/src/stdlib/stdlib.ts +++ b/src/stdlib/stdlib.ts @@ -232,10 +232,9 @@ files["std/config.tact"] = files["std/context.tact"] = "c3RydWN0IENvbnRleHQgewogICAgYm91bmNlZDogQm9vbDsKICAgIHNlbmRlcjogQWRkcmVzczsKICAgIHZhbHVlOiBJbnQ7CiAgICByYXc6IFNsaWNlOwp9CgpAbmFt" + "ZShfX3RhY3RfY29udGV4dF9nZXQpCm5hdGl2ZSBjb250ZXh0KCk6IENvbnRleHQ7CgpAbmFtZShfX3RhY3RfY29udGV4dF9nZXRfc2VuZGVyKQpuYXRpdmUgc2VuZGVy" + - "KCk6IEFkZHJlc3M7CgpleHRlbmRzIGZ1biByZWFkRm9yd2FyZEZlZShzZWxmOiBDb250ZXh0KTogSW50IHsKICAgIGxldCBzYzogU2xpY2UgPSBzZWxmLnJhdzsKICAg" + - "IHNjLmxvYWRBZGRyZXNzKCk7IC8vIFNraXAgZGVzdGluYXRpb24KICAgIHNjLmxvYWRDb2lucygpOyAvLyBTa2lwIHZhbHVlCiAgICBzYy5za2lwQml0cygxKTsgLy8g" + - "U2tpcCBleHRyYSBjdXJyZW5jeSBjb2xsZWN0aW9uCiAgICBzYy5sb2FkQ29pbnMoKTsgLy8gU2tpcCBpaHJfZmVlCiAgICByZXR1cm4gKHNjLmxvYWRDb2lucygpICog" + - "MykgLyAyOwp9"; + "KCk6IEFkZHJlc3M7Cgphc20gZXh0ZW5kcyBmdW4gcmVhZEZvcndhcmRGZWUoc2VsZjogQ29udGV4dCk6IEludCB7CiAgICBMRE1TR0FERFIKICAgIExER1JBTVMKICAg" + + "IE9ORQogICAgU0RTS0lQRklSU1QKICAgIExER1JBTVMKICAgIExER1JBTVMKICAgIERST1AKICAgIDYgMSBCTEtEUk9QMgogICAgWkVSTwogICAgR0VUT1JJR0lOQUxG" + + "V0RGRUUKfQ=="; files["std/contract.tact"] = "c3RydWN0IFN0YXRlSW5pdCB7CiAgICBjb2RlOiBDZWxsOwogICAgZGF0YTogQ2VsbDsKfQoKQG5hbWUoX190YWN0X2NvbXB1dGVfY29udHJhY3RfYWRkcmVzcykKbmF0" + "aXZlIGNvbnRyYWN0QWRkcmVzc0V4dChjaGFpbjogSW50LCBjb2RlOiBDZWxsLCBkYXRhOiBDZWxsKTogQWRkcmVzczsKCmlubGluZSBmdW4gY29udHJhY3RBZGRyZXNz" + diff --git a/src/stdlib/stdlib/std/context.tact b/src/stdlib/stdlib/std/context.tact index 55462f0c9..938872797 100644 --- a/src/stdlib/stdlib/std/context.tact +++ b/src/stdlib/stdlib/std/context.tact @@ -11,11 +11,15 @@ native context(): Context; @name(__tact_context_get_sender) native sender(): Address; -extends fun readForwardFee(self: Context): Int { - let sc: Slice = self.raw; - sc.loadAddress(); // Skip destination - sc.loadCoins(); // Skip value - sc.skipBits(1); // Skip extra currency collection - sc.loadCoins(); // Skip ihr_fee - return (sc.loadCoins() * 3) / 2; +asm extends fun readForwardFee(self: Context): Int { + LDMSGADDR + LDGRAMS + ONE + SDSKIPFIRST + LDGRAMS + LDGRAMS + DROP + 6 1 BLKDROP2 + ZERO + GETORIGINALFWDFEE } \ No newline at end of file diff --git a/src/test/benchmarks/benchmarks.spec.ts b/src/test/benchmarks/benchmarks.spec.ts index 85b629d1f..3c340277f 100644 --- a/src/test/benchmarks/benchmarks.spec.ts +++ b/src/test/benchmarks/benchmarks.spec.ts @@ -1,13 +1,26 @@ import { + beginCell, toNano, TransactionComputeVm, TransactionDescriptionGeneric, } from "@ton/core"; -import { Blockchain, SandboxContract, TreasuryContract } from "@ton/sandbox"; +import { + Blockchain, + BlockchainTransaction, + SandboxContract, + TreasuryContract, +} from "@ton/sandbox"; import { Functions } from "./contracts/output/benchmark_functions_Functions"; import { Functions as FunctionsInline } from "./contracts/output/benchmark_functions_inline_Functions"; import "@ton/test-utils"; +function measureGas(txs: BlockchainTransaction[]) { + return ( + (txs[1]!.description as TransactionDescriptionGeneric) + .computePhase as TransactionComputeVm + ).gasUsed; +} + describe("benchmarks", () => { let blockchain: Blockchain; let treasure: SandboxContract; @@ -27,17 +40,13 @@ describe("benchmarks", () => { { $$type: "Add", value: 10n }, ); - const gasUsed = ( - ( - sendResult.transactions[1]! - .description as TransactionDescriptionGeneric - ).computePhase as TransactionComputeVm - ).gasUsed; + const gasUsed = measureGas(sendResult.transactions); + expect(gasUsed).toMatchInlineSnapshot(`2869n`); // Verify code size const codeSize = functions.init!.code.toBoc().length; - expect(codeSize).toMatchInlineSnapshot(`227`); + expect(codeSize).toMatchInlineSnapshot(`283`); }); it("benchmark functions (inline)", async () => { @@ -51,16 +60,31 @@ describe("benchmarks", () => { { $$type: "Add", value: 10n }, ); - const gasUsed = ( - ( - sendResult.transactions[1]! - .description as TransactionDescriptionGeneric - ).computePhase as TransactionComputeVm - ).gasUsed; + const gasUsed = measureGas(sendResult.transactions); expect(gasUsed).toMatchInlineSnapshot(`2738n`); // Verify code size const codeSize = functionsInline.init!.code.toBoc().length; expect(codeSize).toMatchInlineSnapshot(`220`); }); + it("benchmark readFwdFee", async () => { + const testContract = blockchain.openContract( + await Functions.fromInit(), + ); + const sendResult = await testContract.send( + treasure.getSender(), + { value: toNano(1) }, + { + $$type: "TestGetFwdFee", + any: beginCell() + .storeUint(0, 32) + .storeStringTail("This is test payload") + .asSlice(), + }, + ); + const gasUsed = measureGas(sendResult.transactions); + expect(gasUsed).toMatchInlineSnapshot(`3283n`); + const codeSize = testContract.init!.code.toBoc().length; + expect(codeSize).toMatchInlineSnapshot(`283`); + }); }); diff --git a/src/test/benchmarks/contracts/benchmark_functions.tact b/src/test/benchmarks/contracts/benchmark_functions.tact index 20ad4ee2f..fcd6b2191 100644 --- a/src/test/benchmarks/contracts/benchmark_functions.tact +++ b/src/test/benchmarks/contracts/benchmark_functions.tact @@ -6,6 +6,14 @@ message Sub { value: Int; } +message TestGetFwdFee { + any: Slice; +} + +asm fun touch(x: Int) { + NOP +} + contract Functions { value: Int; @@ -26,4 +34,7 @@ contract Functions { require(msg.value > 0, "Value must be greater than 0"); self.update(-msg.value); } -} \ No newline at end of file + receive(msg: TestGetFwdFee) { + touch(context().readForwardFee()); + } +} diff --git a/src/test/e2e-emulated/contracts/stdlib.tact b/src/test/e2e-emulated/contracts/stdlib.tact index c93ac431f..1bfc606c7 100644 --- a/src/test/e2e-emulated/contracts/stdlib.tact +++ b/src/test/e2e-emulated/contracts/stdlib.tact @@ -58,4 +58,16 @@ contract StdlibTest { get fun parseVarAddress(slice: Slice): VarAddress { return parseVarAddress(slice); } + + get fun parseOriginalFwdFee(msg: Slice): Int { + msg.skipBits(4); //Skip tags + msg.loadAddress(); // Skip source + let ctx: Context = Context{ + bounced: false, + sender: myAddress(), + value: ton("1"), + raw: msg + }; + return ctx.readForwardFee(); + } } \ No newline at end of file diff --git a/src/test/e2e-emulated/stdlib.spec.ts b/src/test/e2e-emulated/stdlib.spec.ts index 8a39ca4fc..1e2ce6295 100644 --- a/src/test/e2e-emulated/stdlib.spec.ts +++ b/src/test/e2e-emulated/stdlib.spec.ts @@ -1,4 +1,4 @@ -import { Address, beginCell, toNano } from "@ton/core"; +import { Address, beginCell, Cell, toNano } from "@ton/core"; import { Blockchain, SandboxContract, TreasuryContract } from "@ton/sandbox"; import { StdlibTest } from "./contracts/output/stdlib_StdlibTest"; import "@ton/test-utils"; @@ -55,8 +55,7 @@ describe("stdlib", () => { .endCell() .toString(), ).toBe(beginCell().storeBit(true).endCell().toString()); - - expect(await contract.getTvm_2023_07Upgrade()).toEqual(1289n); // gas consumed + expect(await contract.getTvm_2023_07Upgrade()).toEqual(1389n); // gas consumed expect(await contract.getTvm_2024_04Upgrade()).toEqual(82009144n); expect( @@ -106,5 +105,13 @@ describe("stdlib", () => { expect(addrVar.address.asCell()).toEqualCell( beginCell().storeUint(345, 123).endCell(), ); + + const RandomMessage = Cell.fromBase64( + "te6ccuEBAQEAZwDOAMloAdbATUBllK0egYWU34F08lIun9zBwyu7UZQrueKKJgnXADfmsDtWQP5D/YkXX+XlULvs4HivRaKY38ftT2hS5yAAEE1v+YAGCCNaAABhF0kRG4TPMTmAapk7bYAAGEXSDt8BwKQrvKE=", + ); + const res = await contract.getParseOriginalFwdFee( + RandomMessage.beginParse(), + ); + expect(res).toBe(400000n); }); });