This documents discusses design choices and differences between this and other jetton implementations
There are a few ways to deal with TEP-74 defined gas constraints and storage fees:
- Precalculated in emulation gas constants
- Hardcoded raw tons amount
- Runtime calculations using latest blockchain configuration
- Original forward fee approximations
We decided to use a mixed approach:
Hardcoded ton amount with nativeReserve and SendMode SendRemainingBalance for storage phase assertions
We moved away from using myStorageDue() or getStorageFee() to work with storage fees since big enough constant to not allow contract freezing together with the ability to claim TON from both Minter and Wallet contracts are good solution that will work in all production use-cases
const minTonsForStorage: Int = ton("0.01");
nativeReserve(minTonsForStorage, ReserveExact | ReserveBounceIfActionFail);
message(MessageParameters {
to: msg.receiver,
value: 0,
mode: SendRemainingBalance,
});This is combination of two techniques:
- Using gas constants with
getComputeFee()to always get precise compute fees in tons - Using
ctx.readForwardFee()for approximation when the outgoing message size is always smaller or the same compared to the incoming
const gasForTransfer: Int = 8000;
let ctx = context();
let fwdCount = 1 + sign(msg.forwardTonAmount);
require(
ctx.value >
msg.forwardTonAmount +
fwdCount * ctx.readForwardFee() +
(2 * getComputeFee(gasForTransfer, false) + minTonsForStorage),
"Insufficient amount of TON attached",
);Terminology first: we call receiver atomic if it only sends a single message that is not expected to further continue as a chain of transactions (we don't say it couldn't; we just don't make such assertions in our logic)
Examples of atomic receivers are:
- Claim TON
- Provide Wallet address
- Provide Wallet balance
We decided to remove gas usage checks from atomic receivers since they will fail shortly after in the action phase anyway. The only difference is the custom exit code. However, someone can argue that TVM exit codes are more explicit about what happened anyway.
We have added additional receivers, not specified by TEP, but long awaited by devs
Not being able to know the balance of other address implies certain obstacles in the implementation of complex DeFi. This additional receiver provides functionality similar to TEP-89, but with wallet balance. You can request and then receive balance from any Jetton wallet with possible additional info for transaction verification
sequenceDiagram
participant D as Any Account
participant C as Jetton Wallet
D ->>+ C: ProvideWalletBalance<BR />(0x7ac8d559)
C ->>+ D: TakeWalletBalance<BR />(0xca77fdc2)
provide_wallet_balance#7ac8d559 receiver:MsgAddress include_verify_info:Bool = InternalMsgBody;
verify_info$_ owner:MsgAddress minter:MsgAddress code:^Cell = VerifyInfo;
take_wallet_balance#ca77fdc2 balance:Coins verify_info:(Maybe VerifyInfo) = InternalMsgBody;
These receivers both on Jetton Wallet and Jetton Minter allow to claim stale TON coins from contracts, leaving just enough balance for them to not freeze and function properly. Message body includes receiver field, that allows to specify funds receiver
sequenceDiagram
participant D as Owner
participant C as Jetton Wallet/Minter
participant F as msg.receiver
D ->>+ C: ClaimTON<BR />(0x0393b1ce)
C ->>+ F: TON's<BR />(empty body)
claim_ton#0393b1ce receiver:MsgAddress = InternalMsgBody;
We decided to change the behavior of a few receivers.
In other implementations, the mint TL-B looks like this:
mint#642b7d07
query_id:uint64
to_address:MsgAddressInt
ton_amount:Coins
master_msg:^JettonInternalTransfer
= JettonMinterMsg;
The ton_amount field represents the amount that we want to send as value with master_msg, which is JettonInternalTransfer. However, since it's an atomic receiver and its only functionality is to perform checks and send the next message in the chain, it makes sense for it to follow general semantics in such cases, which is
message(MessageParameters {
...
value: 0,
mode: SendRemainingValue,
});In our Jetton implementations we decided to remove ton_amount field from mint message and use the send mode 64 (SendRemainingValue ) instead. Given excesses, this approach seems preferable and easier to follow both for users and developers.