在 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 消息返回给发送者。 # F) b# w$ m4 `$ ]% T- E
如果不将多余的GAS退还给发送者,这些资金就会在您的合约中长期累积。从原则上讲,这并不可怕,但这只是一种次优做法。您可以添加一个功能来清除多余的GAS,但像 TON Jetton 这样的常用合约仍会以 op::excesses 消息返回发送方。 send_raw_message(msg, SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE) 传递其余GAS。 如果消息流是线性的:每个处理程序只发送一条消息,则此功能非常有用。
; L" F) `. G9 @1 K
TON 有一个有用的机制:
5 W& u/ l; G- }send_mode_carry_all_remaining_message_value = 64。在 send_raw_message() 中使用这种模式时,剩余的GAS将与消息一起(或返回)转发给新的收件人。如果消息流是线性的:每个消息处理程序只发送一条消息,那么这种模式就很方便。但在某些情况下,不建议使用这种机制: 不建议携带所有剩余GAS时storage_fee 从合约余额中扣除,而不是从输入的天然气中扣除。因此,如果合约中没有其他非线性处理程序,那么随着时间的推移、 storage_fee 会耗尽所有余额,因为所有进来的东西都要出去。 发出事件消耗的是合约余额,而不是GAS。 . U1 e4 C: k, s+ M! M
如果您的合约发出事件,即向外部地址发送信息。该操作的费用将从合约余额中扣除,而不是从 msg_value 中扣除。
* U: Q4 q% ?& l% o7 ]8 ?- var msg = begin_cell().store_uint(0x18, 6).store_slice(destination)
; k4 f/ ?' |1 {9 P4 A' g - .store_coins(10000000) ;; This will be deducted from contract balance
- q5 A# K& _/ t! |1 H - .store_uint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1).store_ref(msg_body);& `! `1 h6 q9 l
- send_raw_message(msg.end_cell(), 0);
4 W6 a: o# K# O1 f# a - , `8 N' | D( O! L }
- 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;; F, O3 S0 n; x- M* Z9 L. X
- int storage_fee = const::min_tons_for_storage - min(ton_balance_before_msg, const::min_tons_for_storage);
2 v% H/ ?% V0 D o - msg_value -= storage_fee + const::gas_consumption;, z* }" a4 B: u5 u& m1 p
- + `" j i& H3 H0 N& \, M& e
- if(forward_ton_amount) {, Z1 H4 [9 _# u
- msg_value -= (forward_ton_amount + fwd_fee);
$ }0 f, v3 y7 r9 e& \& G' K - ...
8 ]- K0 {. |" L8 [1 A* Q - }. v9 A2 `0 R, e, t
: E% m' R/ i" f: Q- if (msg_value > 0) { ;; there is still something to return5 ]/ W8 A4 |1 p, M |; z
- var msg = begin_cell()8 e- _4 X, `) ~$ Y L n+ A0 J# i
- .store_uint(0x10, 6)3 J- c* D: h; k# U
- .store_slice(response_address)' `! |3 Z5 B3 W% I- m; x$ Y
- .store_coins(msg_value)5 N0 _% W7 |/ A6 t! W% m% C. J4 M
- ...
复制代码请记住,如果合约余额用完,交易将被部分执行,这是不允许的。 摘要TON 需要开发人员有意识地管理GAS:计算支出并检查是否提供了足够的GAS。 如果使用 "无限制数据结构",GAS成本会随着时间推移而增加。 TON 建议将超出部分退还给发送方--这也是开发者的责任。 如果GAS耗尽,交易将被部分执行。这可能是一个关键问题。
* p ^# [ o6 P* e
. R2 D- C+ n/ C* ?! Z+ j, B2 Q7 X5 [! n' ]- y; b7 N- L
|