- Run tests covering all execution paths. If you miss a path, it might be the most expensive.
- Extract resource consumption from
send()method return value. The sections below describe ways to compute consumption of different kinds of resources. - Use
expect(extractedValue).toBeLessThanOrEqual(hardcodedConstant)to verify that the hardcoded limit is not exceeded.
Compute fees
There are two kinds of values: gas units and toncoins. The price of contract execution is fixed in gas units. However, the price of the gas itself is determined by the blockchain configuration. One can convert to toncoins in the contract code using current blockchain config parameters:GETGASFEE TVM opcode.
Forward fees
In general, you can calculate message size at runtime usingcomputeDataSize() which uses CDATASIZE:
And then, calculate forward fee using getForwardFee() which uses GETFORWARDFEE
ComputeDataSize have the second argument - maximum number of cells to visit. If it is ok to set in in 8192 since it is the limit for message size.
Optimized forward fee calculation
If the size of the outgoing message is bounded by the size of the incoming message, we can estimate the forward fee of an outgoing message to be no larger than the forward fee of the incoming message, that was already computed by TVM. Thus, we don’t have to calculate it again. Note, that this estimation is correct only for contract system in the same workchain.Additional forward fee calculation
Forward fee is calculated using such formulaa + b cells and x + y bits, the forward fee won’t be getForwardFee(a + b, x + y), but rather basePrice + priceForCells * (a + b) + priceForBits * (x + y).
For this case, we can use getSimpleForwardFee() which uses GETFORWARDFEESIMPLE. This function does not add basePrice (called lump_price in config) into account.
So the price of sending message with a + b cells and x + y bits is getForwardFee(a, x) + getSimpleForwardFee(b, y).
For example, when deploying contracts as part of the operation:
init field adds significant message size. Calculate forward fees using actual cell and bit counts, summing the base message and the StateInit: getForwardFee(msgCells, msgBits) + getSimpleForwardFee(stateInitCells, stateInitBits).
Complex forward fee calculation
Sometimes, out message is larger than the input one. In that cases combined approach can be used.Storage fees
We cannot predict storage fees that we have to pay for sending messages because it depends on how long the target contract didn’t pay storage fee. Storage fees are different from forward and compute fees in that term, they should be handled in receiver contracts and in internal contracts. Two distinct approaches exist: Approach 1: Maintain a positive reserve Always keep a minimum balance on the all contracts in your system. Storage fees deduct from this reserve, which replenishes with each user interaction. Do not hardcode TON; instead, hardcode the maximum possible contract size in cells and bits. Note, this is supposed to be the code in internal contracts.freeze_due_limit. Otherwise, the contract likely is already frozen and a transaction chain is likely to fail anyway.
So if we reserve storage debt from incoming messages. Allow the balance to remain at zero or with small debt.
Note, this is supposed to be the code in the internal contracts.
myStorageDue() function returns the amount needed to bring the balance to zero (or zero if it is already positive).
If we expect that the rest of trace uses n unique contracts, then it won’t take more than n freeze limits to pay their storage fees. So, in the receiver contract, the check should be: