在 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 消息返回给发送者。
) K% l0 `3 a( O J1 P% [
如果不将多余的GAS退还给发送者,这些资金就会在您的合约中长期累积。从原则上讲,这并不可怕,但这只是一种次优做法。您可以添加一个功能来清除多余的GAS,但像 TON Jetton 这样的常用合约仍会以 op::excesses 消息返回发送方。 send_raw_message(msg, SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE) 传递其余GAS。 如果消息流是线性的:每个处理程序只发送一条消息,则此功能非常有用。
$ Z4 w3 b- J- ]: m
TON 有一个有用的机制:0 K* F2 h6 b% t2 ?, Y5 d# O& E( m+ w6 l# d
send_mode_carry_all_remaining_message_value = 64。在 send_raw_message() 中使用这种模式时,剩余的GAS将与消息一起(或返回)转发给新的收件人。如果消息流是线性的:每个消息处理程序只发送一条消息,那么这种模式就很方便。但在某些情况下,不建议使用这种机制: 不建议携带所有剩余GAS时storage_fee 从合约余额中扣除,而不是从输入的天然气中扣除。因此,如果合约中没有其他非线性处理程序,那么随着时间的推移、 storage_fee 会耗尽所有余额,因为所有进来的东西都要出去。 发出事件消耗的是合约余额,而不是GAS。
3 M6 G! ^3 H2 j% u
如果您的合约发出事件,即向外部地址发送信息。该操作的费用将从合约余额中扣除,而不是从 msg_value 中扣除。 4 y& }; p: k( F# }! e' q7 o) c C
- var msg = begin_cell().store_uint(0x18, 6).store_slice(destination)
( ?, D! R. E1 G: K/ k - .store_coins(10000000) ;; This will be deducted from contract balance* p, U- V% m: {; v$ x
- .store_uint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1).store_ref(msg_body);
; A* ~5 ]: u. U9 i' m5 ?7 q - send_raw_message(msg.end_cell(), 0);- ~7 B+ n: t. }" ~
- / H. j2 `, h8 {6 [/ S3 V
- 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;
' L; |+ I; k/ q) p - int storage_fee = const::min_tons_for_storage - min(ton_balance_before_msg, const::min_tons_for_storage);
4 j& N* H4 f* I5 p - msg_value -= storage_fee + const::gas_consumption;
" W! E. B/ ~, o3 J; t. W% Q% y2 F
( X/ @1 E/ l8 Q/ y( V- if(forward_ton_amount) {+ e4 y# v& n. d9 p, E
- msg_value -= (forward_ton_amount + fwd_fee);
8 }! C1 F8 y1 l) J H7 j: m; V - ..., a% U- i7 U# `6 x* A( s1 L0 `* ~" _
- }* _; _$ C* |! j% S, ~+ {( i: d
- + G' A ?' |0 x6 Z1 ^
- if (msg_value > 0) { ;; there is still something to return1 [! |! c9 H" ]
- var msg = begin_cell()
2 z Q5 i; U6 M2 @3 e" a - .store_uint(0x10, 6)! {" w3 a! [+ T: x5 W/ N5 A
- .store_slice(response_address)
2 I0 R- Y- F4 H4 ]3 D* i - .store_coins(msg_value)1 E: m1 q3 P& F6 V# o
- ...
复制代码请记住,如果合约余额用完,交易将被部分执行,这是不允许的。 摘要TON 需要开发人员有意识地管理GAS:计算支出并检查是否提供了足够的GAS。 如果使用 "无限制数据结构",GAS成本会随着时间推移而增加。 TON 建议将超出部分退还给发送方--这也是开发者的责任。 如果GAS耗尽,交易将被部分执行。这可能是一个关键问题。 . W( c& e5 x- k) [0 j+ h6 C
4 ]$ w: o& ~9 w: n( l
3 Y4 [( p$ [" V2 o1 m& D% |/ W( N |