在 Solidity 中,合约开发人员并不太关心GAS问题。如果用户提供的GAS太少,一切都会恢复原状,就像什么都没发生过一样(但GAS不会退还)。如果用户提供了足够的GAS,实际成本将自动计算并从余额中扣除。 在 TON,情况有所不同: 如果 "合约组 "交换信息,则必须在每个信息中进行控制和计算。TON 无法自动计算GAS。完整执行交易及其所有后果可能需要很长时间,到最后,用户钱包中可能没有足够的TON币。这里使用的是携带价值原则。 计算GAS并检查 msg_value我们可以使用消息流程图来估算每种情况下每个处理程序的成本,并插入 msg_value 的充分性检查。 假设你的合约发送了三条信息,那么每条信息只能发送 0.33 TON。这意味着他们的 "需求 "应该更少。仔细计算整个合约的GAS需求量非常重要。 如果在开发过程中,您的代码开始发送更多信息,情况就会变得更加复杂。需要重新检查和更新GAS要求。 小心地退回多余GAS如果不将多余的GAS退还给发送者,这些资金就会在您的合约中长期累积。 这并不可怕,但并不理想。您可以添加一个功能,用于归还多余GAS。 像 TON Jetton 这样的知名合约,会将 op::excesses 消息返回给发送者。 ! E$ ]6 U" z+ S* w! r7 b& E
如果不将多余的GAS退还给发送者,这些资金就会在您的合约中长期累积。从原则上讲,这并不可怕,但这只是一种次优做法。您可以添加一个功能来清除多余的GAS,但像 TON Jetton 这样的常用合约仍会以 op::excesses 消息返回发送方。 send_raw_message(msg, SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE) 传递其余GAS。 如果消息流是线性的:每个处理程序只发送一条消息,则此功能非常有用。
# r+ Q0 O, }1 S5 G- z
TON 有一个有用的机制:
% ~" C0 J0 N* C. u) N6 {- H1 csend_mode_carry_all_remaining_message_value = 64。在 send_raw_message() 中使用这种模式时,剩余的GAS将与消息一起(或返回)转发给新的收件人。如果消息流是线性的:每个消息处理程序只发送一条消息,那么这种模式就很方便。但在某些情况下,不建议使用这种机制: 不建议携带所有剩余GAS时storage_fee 从合约余额中扣除,而不是从输入的天然气中扣除。因此,如果合约中没有其他非线性处理程序,那么随着时间的推移、 storage_fee 会耗尽所有余额,因为所有进来的东西都要出去。 发出事件消耗的是合约余额,而不是GAS。 9 N/ p9 r2 E6 }$ a7 J6 {
如果您的合约发出事件,即向外部地址发送信息。该操作的费用将从合约余额中扣除,而不是从 msg_value 中扣除。 ( t* T8 o! z6 n: Y
- var msg = begin_cell().store_uint(0x18, 6).store_slice(destination)
1 |8 v* P* [" Z; }5 ? - .store_coins(10000000) ;; This will be deducted from contract balance6 ~0 B9 E) x9 E
- .store_uint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1).store_ref(msg_body);$ Y& _0 M, ^* {" K$ }
- send_raw_message(msg.end_cell(), 0);
; A. C+ I: D3 f8 E9 t - 8 ~* X- M$ |3 b
- emit_log_cell_ref(query_id, ... ;; This also spends contract balance
复制代码如果您的合约在发送信息时附加价值或使用 SEND_MODE_PAY_FEES_SEPARETELY = 1。这些操作会从合约余额中扣除,这意味着未使用的返回是 "亏本工作"。 计算GAS成本的常用方法下面是 TON Wallet 的代码片段。这是保持合约余额稳定的常用方法。 - int ton_balance_before_msg = my_ton_balance - msg_value;( Q8 q8 g+ M* Z, M7 c& V( A
- int storage_fee = const::min_tons_for_storage - min(ton_balance_before_msg, const::min_tons_for_storage);" F1 t; x" a G
- msg_value -= storage_fee + const::gas_consumption;
$ M: G! K6 G. _
3 `2 A, O1 h0 ~- if(forward_ton_amount) {: g- [+ ^ L. W8 I
- msg_value -= (forward_ton_amount + fwd_fee);* A& ?4 ?$ I# V
- ...
4 ]" S/ s; a( U- C - }, h$ P0 i% ~ \; y/ \% l
- 5 l; G6 s. A% P- M* M
- if (msg_value > 0) { ;; there is still something to return
+ P5 G/ B! f3 G/ m, o+ {+ W8 ^% f - var msg = begin_cell(): Y- N) |5 g3 |; a+ w9 ^
- .store_uint(0x10, 6)
3 n$ r# L" U/ l7 r - .store_slice(response_address)
3 A) x3 Z' P6 t3 ]+ W! l2 l _ - .store_coins(msg_value)3 y Q8 u1 A% w2 x4 }3 W3 x5 B& O
- ...
复制代码请记住,如果合约余额用完,交易将被部分执行,这是不允许的。 摘要TON 需要开发人员有意识地管理GAS:计算支出并检查是否提供了足够的GAS。 如果使用 "无限制数据结构",GAS成本会随着时间推移而增加。 TON 建议将超出部分退还给发送方--这也是开发者的责任。 如果GAS耗尽,交易将被部分执行。这可能是一个关键问题。
9 C$ F/ z' r1 Q! I _: s & y5 C/ L6 L" b5 H% _! R* T' n
, R: B0 x& v3 D, C9 k |