Skip to content
5 changes: 4 additions & 1 deletion contracts/contracts/examples/jetton/onramp_mock.tolk
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import "@stdlib/common.tolk"
import "../../lib/jetton/jetton_client.tolk"
import "../../lib/jetton/messages.tolk"
import "../../lib/jetton/utils.tolk"
import "../../lib/jetton/jetton-utils"
import "../../lib/utils.tolk"
Comment thread
krebernisak marked this conversation as resolved.

// OnrampMock contract in Tolk
Expand All @@ -12,6 +12,9 @@ const FEE = 5
const INCORRECT_SENDER_ERROR = 100
const FORWARD_PAYLOAD_REQUIRED_ERROR = 101

// Jetton wallet utilities for Tolk
const JETTON_TOPIC : int = 0x351 // for easier indexing

Comment on lines +17 to +19
struct OnrampMock {
JettonClient: JettonClient
}
Expand Down
9 changes: 9 additions & 0 deletions contracts/contracts/lib/jetton/errors.tolk
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

const ERROR_INVALID_OP = 72
const ERROR_WRONG_OP = 0xffff
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused

const ERROR_NOT_OWNER = 73
Comment thread
krebernisak marked this conversation as resolved.
const ERROR_NOT_VALID_WALLET = 74
const ERROR_WRONG_WORKCHAIN = 333
const ERROR_BALANCE_ERROR = 47
const ERROR_NOT_ENOUGH_GAS = 48
const ERROR_INVALID_MESSAGE = 49
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused

73 changes: 73 additions & 0 deletions contracts/contracts/lib/jetton/fees-management.tolk
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// SPDX-License-Identifier: MIT
// Imported from https://github.com/ton-blockchain/tolk-bench/blob/0f416ca611fbfa25e736973d01e5fb70af485468/contracts_Tolk/03_notcoin/messages.tolk
import "@stdlib/gas-payments"
import "errors"

// we're working in basechain, but theoretically, a jetton might even work in masterchain
const MY_WORKCHAIN = BASECHAIN

fun getPrecompiledGasConsumption(): int?
asm "GETPRECOMPILEDGAS"

// Storage costs
// these constants are used to estimate storage fee (how much we should pay for storing a wallet contract)

const STORAGE_SIZE_MaxWallet_bits = 1033
const STORAGE_SIZE_MaxWallet_cells = 3
const STORAGE_SIZE_InitStateWallet_bits = 931
const STORAGE_SIZE_InitStateWallet_cells = 3

const MESSAGE_SIZE_BurnNotification_bits = 754 // body = 32+64+124+(3+8+256)+(3+8+256)
const MESSAGE_SIZE_BurnNotification_cells = 1 // body always in ref

const MIN_STORAGE_DURATION = 5 * 365 * 24 * 3600 // 5 years


// Gas costs
// these constants are used to estimate gas fee (how much we should remain on balance for a swap to succeed);
// they must be absolutely equal to consumed gas; if not, tests fail;
// actual consumed gas (desired value of these constants) are printed to console after tests run

const GAS_CONSUMPTION_JettonTransfer = 6153
const GAS_CONSUMPTION_JettonReceive = 7253
const GAS_CONSUMPTION_BurnRequest = 4368
const GAS_CONSUMPTION_BurnNotification = 3855


fun calculateJettonWalletMinStorageFee() {
return calculateStorageFee(MY_WORKCHAIN, MIN_STORAGE_DURATION, STORAGE_SIZE_MaxWallet_bits, STORAGE_SIZE_MaxWallet_cells);
}

fun forwardInitStateOverhead() {
return calculateForwardFeeWithoutLumpPrice(MY_WORKCHAIN, STORAGE_SIZE_InitStateWallet_bits, STORAGE_SIZE_InitStateWallet_cells);
}

fun checkAmountIsEnoughToTransfer(msgValue: int, forwardTonAmount: int, fwdFee: int) {
var fwdCount = forwardTonAmount != 0 ? 2 : 1; // second sending (forward) will be cheaper that first

var jettonWalletGasConsumption = getPrecompiledGasConsumption();
var sendTransferGasConsumption = (jettonWalletGasConsumption == null) ? GAS_CONSUMPTION_JettonTransfer : jettonWalletGasConsumption;
var receiveTransferGasConsumption = (jettonWalletGasConsumption == null) ? GAS_CONSUMPTION_JettonReceive : jettonWalletGasConsumption;

assert (msgValue >
forwardTonAmount +
// 3 messages: wal1->wal2, wal2->owner, wal2->response
// but last one is optional (it is ok if it fails)
fwdCount * fwdFee +
forwardInitStateOverhead() + // additional fwd fees related to initstate in iternal_transfer
calculateGasFee(MY_WORKCHAIN, sendTransferGasConsumption) +
calculateGasFee(MY_WORKCHAIN, receiveTransferGasConsumption) +
calculateJettonWalletMinStorageFee()
) throw ERROR_NOT_ENOUGH_GAS;
}

fun checkAmountIsEnoughToBurn(msgValue: int) {
var jettonWalletGasConsumption = getPrecompiledGasConsumption();
var sendBurnGasConsumption = (jettonWalletGasConsumption == null) ? GAS_CONSUMPTION_BurnRequest : jettonWalletGasConsumption;

assert (msgValue >
calculateForwardFee(MY_WORKCHAIN, MESSAGE_SIZE_BurnNotification_bits, MESSAGE_SIZE_BurnNotification_cells) +
calculateGasFee(MY_WORKCHAIN, sendBurnGasConsumption) +
calculateGasFee(MY_WORKCHAIN, GAS_CONSUMPTION_BurnNotification)
) throw ERROR_NOT_ENOUGH_GAS;
}
24 changes: 24 additions & 0 deletions contracts/contracts/lib/jetton/jetton-utils.tolk
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import "fees-management"
import "storage"

fun calcDeployedJettonWallet(ownerAddress: address, minterAddress: address, jettonWalletCode: cell): AutoDeployAddress {
Comment thread
krebernisak marked this conversation as resolved.
Outdated
val emptyWalletStorage: WalletStorage = {
status: 0,
jettonBalance: 0,
ownerAddress,
minterAddress,
};

return {
workchain: MY_WORKCHAIN,
stateInit: {
code: jettonWalletCode,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if there's a way to use the hash of the code instead to save on storage and compute fees. IIUC the hash is computed from leaves to root, so the hash of the code should always result in the same.

data: emptyWalletStorage.toCell()
}
}
}

fun calcAddressOfJettonWallet(ownerAddress: address, minterAddress: address, jettonWalletCode: cell) {
val jwDeployed = calcDeployedJettonWallet(ownerAddress, minterAddress, jettonWalletCode);
return jwDeployed.calculateAddress()
}
37 changes: 12 additions & 25 deletions contracts/contracts/lib/jetton/messages.tolk
Original file line number Diff line number Diff line change
Expand Up @@ -4,116 +4,103 @@ import "storage"

type ForwardPayloadRemainder = RemainingBitsAndRefs

// nolint:opcode
struct (0x0f8a7ea5) AskToTransfer {
queryId: uint64
jettonAmount: coins
transferRecipient: address
sendExcessesTo: address
sendExcessesTo: address?
customPayload: cell?
forwardTonAmount: coins
forwardPayload: ForwardPayloadRemainder
}

// nolint:opcode
struct (0x7362d09c) TransferNotificationForRecipient {
queryId: uint64
jettonAmount: coins
transferInitiator: address
transferInitiator: address?
forwardPayload: ForwardPayloadRemainder
}

// nolint:opcode
struct (0x178d4519) InternalTransferStep {
queryId: uint64
jettonAmount: coins
transferInitiator: address
sendExcessesTo: address
transferInitiator: address? // is null when minting (not initiated by another wallet)
sendExcessesTo: address?
forwardTonAmount: coins
forwardPayload: ForwardPayloadRemainder
}

// nolint:opcode
struct (0xd53276db) ReturnExcessesBack {
queryId: uint64
}

// nolint:opcode
struct (0x595f07bc) AskToBurn {
queryId: uint64
jettonAmount: coins
sendExcessesTo: address
sendExcessesTo: address?
customPayload: cell?
}

// nolint:opcode
struct (0x7bdd97de) BurnNotificationForMinter {
queryId: uint64
jettonAmount: coins
burnInitiator: address
sendExcessesTo: address
sendExcessesTo: address?
}

// nolint:opcode
struct (0x2c76b973) RequestWalletAddress {
queryId: uint64
ownerAddress: address
includeOwnerAddress: bool
}

// nolint:opcode
struct (0xd1735400) ResponseWalletAddress {
queryId: uint64
jettonWalletAddress: address
jettonWalletAddress: address?
ownerAddress: Cell<address>?
}

// nolint:opcode
struct (0x00000015) MintNewJettons {
queryId: uint64
mintRecipient: address
tonAmount: coins
internalTransferMsg: Cell<InternalTransferStep>
}

// nolint:opcode
struct (0x6501f354) ChangeMinterAdmin {
queryId: uint64
newAdminAddress: address
}

// nolint:opcode
struct (0xfb88e119) ClaimMinterAdmin {
queryId: uint64
}

// nolint:opcode
struct (0x7431f221) DropMinterAdmin {
queryId: uint64
}

// nolint:opcode
struct (0x2508d66a) UpgradeMinterCode {
queryId: uint64
newData: cell
newCode: cell
}

// nolint:opcode
struct (0xcb862902) ChangeMinterMetadataUri {
queryId: uint64
newMetadataUri: SnakeString
newMetadataUri: RemainingBitsAndRefs // by standard, it's "inlined snake string" (but not in a ref, that's why not `string`)
}

struct (0xd372158c) TopUpTons {
}

// nolint:opcode
struct (0xd372158c) TopUpTons {}

// "forward payload" is TL/B `(Either Cell ^Cell)`;
// we want to test, that if ^Cell, no other data exists in a slice
fun ForwardPayloadRemainder.checkIsCorrectTLBEither(self) {
var mutableCopy = self;
if (mutableCopy.loadMaybeRef() != null) {
// throw "cell underflow" if there is data besides a ref
mutableCopy.assertEnd();
mutableCopy.assertEnd()
}
}
36 changes: 10 additions & 26 deletions contracts/contracts/lib/jetton/storage.tolk
Original file line number Diff line number Diff line change
@@ -1,24 +1,5 @@
// SPDX-License-Identifier: MIT
// Imported from https://github.com/ton-blockchain/tolk-bench/blob/0f416ca611fbfa25e736973d01e5fb70af485468/contracts_Tolk/03_notcoin/storage.tolk
// SnakeString describes a (potentially long) string inside a cell;
// short strings are stored as-is, like "my-picture.png";
// long strings are nested refs, like "xxxx".ref("yyyy".ref("zzzz"))
type SnakeString = slice

fun SnakeString.unpackFromSlice(mutate s: slice) {
// obviously, SnakeString can be only the last: it's just "the remainder";
// for correctness, it's better to validate it has no more refs:
// assert (s.remainingRefsCount() <= 1) throw 5;
// but since here we're matching the original FunC implementation, leave no checks
val snakeRemainder = s;
s = createEmptySlice(); // no more left to read
return snakeRemainder;
}

fun SnakeString.packToBuilder(self, mutate b: builder) {
b.storeSlice(self);
}

struct WalletStorage {
status: uint4
jettonBalance: coins
Expand All @@ -28,24 +9,27 @@ struct WalletStorage {

struct MinterStorage {
totalSupply: coins
adminAddress: address
nextAdminAddress: address
adminAddress: address?
nextAdminAddress: address?
jettonWalletCode: cell
metadataUri: Cell<SnakeString>
metadataUri: string
}



fun MinterStorage.load() {
return MinterStorage.fromCell(contract.getData());
return MinterStorage.fromCell(contract.getData())
}

fun MinterStorage.save(self) {
contract.setData(self.toCell());
contract.setData(self.toCell())
}


fun WalletStorage.load() {
return WalletStorage.fromCell(contract.getData());
return WalletStorage.fromCell(contract.getData())
}

fun WalletStorage.save(self) {
contract.setData(self.toCell());
contract.setData(self.toCell())
}
49 changes: 0 additions & 49 deletions contracts/contracts/lib/jetton/utils.tolk

This file was deleted.

Loading
Loading