Skip to content
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

tests: add compatible notcoin benchmarks #2329

Merged
merged 21 commits into from
Mar 12, 2025
Merged
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
2 changes: 1 addition & 1 deletion src/benchmarks/contracts/escrow.tact
Original file line number Diff line number Diff line change
Expand Up @@ -198,4 +198,4 @@ contract Escrow (
get fun escrowInfo(): Escrow {
return self;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// https://github.com/ton-blockchain/TEPs/blob/master/text/0089-jetton-wallet-discovery.md

import "./jetton_wallet";
import "./jetton-wallet";
import "./messages";

const ProvideAddressGasConsumption: Int = ton("0.01");
Expand Down
136 changes: 136 additions & 0 deletions src/benchmarks/contracts/jetton-minter-notcoin.tact
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import "./messages";
import "./jetton-wallet-notcoin";

const Workchain: Int = 0;

struct JettonMasterState {
totalSupply: Int as coins;
mintable: Bool;
adminAddress: Address;
jettonContent: Cell;
jettonWalletCode: Cell;
}

contract JettonMinterNotcoin(
totalSupply: Int as coins,
owner: Address,
nextOwner: Address,
jettonContent: Cell,
) {
receive(msg: JettonBurnNotification) {
let sender = parseStdAddress(sender().asSlice());
let wallet = getJettonBasechainWalletByOwner(msg.sender);

throwUnless(74, sender.workchain == Workchain && sender.address == wallet.hash!!);

self.totalSupply -= msg.amount;

if (msg.responseDestination.isNotNone()) {
message(MessageParameters {
to: msg.responseDestination,
body: JettonExcesses{ queryId: msg.queryId }.toCell(),
value: 0,
bounce: false,
mode: SendRemainingValue | SendIgnoreErrors, // ignore errors, because supply has already been updated
});
}
}

receive(msg: ProvideWalletAddress) {
let ownerWorkchain: Int = parseStdAddress(msg.ownerAddress.asSlice()).workchain;

let targetJettonWallet: BasechainAddress =
(ownerWorkchain == Workchain) ?
contractBasechainAddress(initOf JettonWalletNotcoin(0, msg.ownerAddress, myAddress()))
: emptyBasechainAddress();

message(MessageParameters {
body: makeTakeWalletAddressMsg(targetJettonWallet, msg),
to: sender(),
value: 0,
mode: SendRemainingValue | SendBounceIfActionFail,
});
}

receive(msg: Mint) {
let ctx = context();
throwUnless(73, ctx.sender == self.owner);
self.totalSupply += msg.mintMessage.amount;

forceBasechain(msg.receiver);
checkAmountIsEnoughToTransfer(ctx.value, msg.mintMessage.forwardTonAmount, ctx.readForwardFee());

deploy(DeployParameters{
value: 0,
bounce: true,
mode: SendRemainingValue,
body: msg.mintMessage.toCell(),
init: getJettonWalletInit(msg.receiver)
});
}

receive(msg: JettonUpdateContent) {
throwUnless(73, sender() == self.owner);
self.jettonContent = msg.content;
}

receive(msg: ChangeAdmin) {
throwUnless(73, sender() == self.owner);
self.nextOwner = msg.nextAdmin;
}

receive(msg: ClaimAdmin) {
throwUnless(73, sender() == self.nextOwner);
self.owner = self.nextOwner;
self.nextOwner = emptyAddress();
}

receive(msg: DropAdmin) {
throwUnless(73, sender() == self.owner);
self.owner = emptyAddress();
self.nextOwner = emptyAddress();
}

// accept tons
receive(msg: TopUp) {}

get fun get_jetton_data(): JettonMasterState {
return JettonMasterState {
totalSupply: self.totalSupply,
mintable: true,
adminAddress: self.owner,
jettonContent: self.jettonContent,
jettonWalletCode: codeOf JettonWalletNotcoin,
}
}

get fun get_wallet_address(ownerAddress: Address): Address {
return getJettonWalletByOwner(ownerAddress);
}
}

asm fun emptyAddress(): Address { b{00} PUSHSLICE }

inline fun makeTakeWalletAddressMsg(targetJettonWallet: BasechainAddress, msg: ProvideWalletAddress): Cell {
return
beginCell()
.storeUint(TakeWalletAddressOpcode, 32)
.storeUint(msg.queryId, 64)
.storeBasechainAddress(targetJettonWallet)
.storeMaybeRef(msg.includeAddress ? beginCell().storeAddress(msg.ownerAddress).endCell() : null)
.endCell();
}

inline fun getJettonWalletInit(address: Address): StateInit {
return initOf JettonWalletNotcoin(0, address, myAddress());
}

inline fun getJettonWalletByOwner(jettonWalletOwner: Address): Address {
return contractAddress(getJettonWalletInit(jettonWalletOwner));
}

inline fun getJettonBasechainWalletByOwner(jettonWalletOwner: Address): BasechainAddress {
return contractBasechainAddress(getJettonWalletInit(jettonWalletOwner));
}

inline extends fun isNotNone(self: Address): Bool { return self.asSlice().preloadUint(2) != 0 }
156 changes: 156 additions & 0 deletions src/benchmarks/contracts/jetton-wallet-notcoin.tact
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import "./messages";

// gas
const SEND_TRANSFER_GAS_CONSUMPTION: Int = 10065;
const RECEIVE_TRANSFER_GAS_CONSUMPTION: Int = 10435;
const SEND_BURN_GAS_CONSUMPTION: Int = 5891;
const RECEIVE_BURN_GAS_CONSUMPTION: Int = 6757;

// storage
const MIN_STORAGE_DURATION: Int = 5 * 365 * 24 * 3600; // 5 years

const JETTON_WALLET_BITS: Int = 1033;
const JETTON_WALLET_CELLS: Int = 3;

const JETTON_WALLET_INIT_STATE_BITS: Int = 931;
const JETTON_WALLET_INIT_STATE_CELLS: Int = 3;

const BURN_NOTIFICATION_BITS: Int = 754;
const BURN_NOTIFICATION_CELLS: Int = 1;

inline fun checkAmountIsEnoughToTransfer(msgValue: Int, forwardTonAmount: Int, fwdFee: Int) {
let fwdCount = 1 + sign(forwardTonAmount);

throwUnless(
705,
msgValue > fwdCount * fwdFee +
getSimpleForwardFee(JETTON_WALLET_INIT_STATE_CELLS, JETTON_WALLET_INIT_STATE_BITS, false) +
getComputeFee(SEND_TRANSFER_GAS_CONSUMPTION, false) +
getComputeFee(RECEIVE_TRANSFER_GAS_CONSUMPTION, false) +
getStorageFee(JETTON_WALLET_CELLS, JETTON_WALLET_BITS, MIN_STORAGE_DURATION, false)
);
}

inline fun checkAmountIsEnoughToBurn(msgValue: Int) {
throwUnless(
705,
msgValue >
getForwardFee(BURN_NOTIFICATION_CELLS, BURN_NOTIFICATION_BITS, false) +
getComputeFee(SEND_BURN_GAS_CONSUMPTION, false) +
getComputeFee(RECEIVE_BURN_GAS_CONSUMPTION, false)
);
}

contract JettonWalletNotcoin(
balance: Int as coins,
owner: Address,
master: Address,
) {
receive(msg: JettonTransfer) {
forceBasechain(msg.destination);
throwUnless(705, sender() == self.owner);

self.balance -= msg.amount;
throwUnless(706, self.balance >= 0);
throwUnless(708, msg.forwardPayload.bits() >= 1);

let ctx = context();
checkAmountIsEnoughToTransfer(ctx.value, msg.forwardTonAmount, ctx.readForwardFee());

deploy(DeployParameters{
value: 0,
mode: SendRemainingValue,
bounce: true,
body: JettonTransferInternal{
queryId: msg.queryId,
amount: msg.amount,
sender: self.owner,
responseDestination: msg.responseDestination,
forwardTonAmount: msg.forwardTonAmount,
forwardPayload: msg.forwardPayload
}.toCell(),
init: initOf JettonWalletNotcoin(0, msg.destination, self.master),
});
}

receive(msg: JettonTransferInternal) {
self.balance += msg.amount;

// This message should come only from master, or from other JettonWallet
let wallet: StateInit = initOf JettonWalletNotcoin(0, msg.sender, self.master);
if (!wallet.hasSameBasechainAddress(sender())) {
throwUnless(707, self.master == sender());
}

if (msg.forwardTonAmount > 0) {
message(MessageParameters{
to: self.owner,
value: msg.forwardTonAmount,
mode: SendPayGasSeparately,
bounce: false,
body: JettonNotification{ // 0x7362d09c -- Remind the new Owner
queryId: msg.queryId,
amount: msg.amount,
sender: msg.sender,
forwardPayload: msg.forwardPayload,
}.toCell(),
});
}

// 0xd53276db -- Cashback to the original Sender
if (msg.responseDestination != null) {
let leaveOnBalance: Int = myBalance() - context().value + myStorageDue();
nativeReserve(
max(leaveOnBalance, getStorageFee(JETTON_WALLET_CELLS, JETTON_WALLET_BITS, MIN_STORAGE_DURATION, false)),
2
);

message(MessageParameters{
to: msg.responseDestination!!,
value: 0,
mode: SendRemainingBalance | SendIgnoreErrors,
bounce: false,
body: JettonExcesses{ queryId: msg.queryId }.toCell(),
});
}
}

receive(msg: JettonBurn) {
throwUnless(705, sender() == self.owner);

self.balance -= msg.amount;
throwUnless(706, self.balance >= 0);

checkAmountIsEnoughToBurn(context().value);

message(MessageParameters{
to: self.master,
value: 0,
mode: SendRemainingValue,
bounce: true,
body: JettonBurnNotification{
queryId: msg.queryId,
amount: msg.amount,
sender: self.owner,
responseDestination: msg.responseDestination,
}.toCell(),
});
}

bounced(msg: bounced<JettonTransferInternal>) {
self.balance += msg.amount;
}

bounced(msg: bounced<JettonBurnNotification>) {
self.balance += msg.amount;
}

get fun get_wallet_data(): JettonWalletData {
return JettonWalletData{
balance: self.balance,
owner: self.owner,
master: self.master,
code: myCode()
};
}
}
18 changes: 18 additions & 0 deletions src/benchmarks/contracts/messages.tact
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,21 @@ message(3) ChangeOwner {
queryId: Int as uint64;
newOwner: Address;
}

// notcoin
message(0xd372158c) TopUp {
queryId: Int as uint64;
}

message(0x6501f354) ChangeAdmin {
queryId: Int as uint64;
nextAdmin: Address;
}

message(0xfb88e119) ClaimAdmin {
queryId: Int as uint64;
}

message(0x7431f221) DropAdmin {
queryId: Int as uint64;
}
2 changes: 1 addition & 1 deletion src/benchmarks/contracts/wallet.tact
Original file line number Diff line number Diff line change
Expand Up @@ -241,4 +241,4 @@ contract Wallet(
get fun get_extensions(): map<Int as uint256, Bool> {
return self.extensions;
}
}
}
2 changes: 1 addition & 1 deletion src/benchmarks/escrow/escrow.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
printBenchmarkTable,
generateCodeSizeResults,
getStateSizeForAccount,
} from "../util";
} from "../utils/gas";
import benchmarkResults from "./results_gas.json";
import { readFileSync } from "fs";
import { posixNormalize } from "../../utils/filePath";
Expand Down
Loading
Loading