在 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 消息返回给发送者。 0 D( x0 W) k3 V' V1 G* G8 w
如果不将多余的GAS退还给发送者,这些资金就会在您的合约中长期累积。从原则上讲,这并不可怕,但这只是一种次优做法。您可以添加一个功能来清除多余的GAS,但像 TON Jetton 这样的常用合约仍会以 op::excesses 消息返回发送方。 send_raw_message(msg, SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE) 传递其余GAS。 如果消息流是线性的:每个处理程序只发送一条消息,则此功能非常有用。
$ Q* b1 Z; p3 B
TON 有一个有用的机制:
7 u& K2 T% Z7 M/ Z3 N4 M8 p1 Qsend_mode_carry_all_remaining_message_value = 64。在 send_raw_message() 中使用这种模式时,剩余的GAS将与消息一起(或返回)转发给新的收件人。如果消息流是线性的:每个消息处理程序只发送一条消息,那么这种模式就很方便。但在某些情况下,不建议使用这种机制: 不建议携带所有剩余GAS时storage_fee 从合约余额中扣除,而不是从输入的天然气中扣除。因此,如果合约中没有其他非线性处理程序,那么随着时间的推移、 storage_fee 会耗尽所有余额,因为所有进来的东西都要出去。 发出事件消耗的是合约余额,而不是GAS。
1 {1 }- W* h3 F: f/ J+ H' k; z2 Y
如果您的合约发出事件,即向外部地址发送信息。该操作的费用将从合约余额中扣除,而不是从 msg_value 中扣除。 / O/ D# z" m( Y' u7 f# V4 Q
- var msg = begin_cell().store_uint(0x18, 6).store_slice(destination)7 O5 m/ l1 [; h3 X: x/ D! w% B1 N
- .store_coins(10000000) ;; This will be deducted from contract balance
4 T! K8 g2 T, ?- q4 Z {3 {; j4 j - .store_uint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1).store_ref(msg_body);
* v( B5 d0 W/ h, o" W - send_raw_message(msg.end_cell(), 0);
6 u: T0 ^# Y8 ^) }
/ R* @; e' W; u- 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;1 @; @2 _9 M. l
- int storage_fee = const::min_tons_for_storage - min(ton_balance_before_msg, const::min_tons_for_storage);
, Y9 e5 E* y+ h1 [, X3 q0 | - msg_value -= storage_fee + const::gas_consumption;
' I8 n5 n, p( }' N! u2 I& {
4 J5 u7 A# d7 z+ J5 h- if(forward_ton_amount) {. X9 g* o, v4 }* _5 Z- O
- msg_value -= (forward_ton_amount + fwd_fee);3 D; Y$ X- Y6 ^4 W/ ^
- ...
3 n% Y6 N; _5 t" X a& v& b - }
! q) i# G) |, C0 V) i
) R z- p s, s" r+ I% ]! y) o) S- if (msg_value > 0) { ;; there is still something to return. y; E6 K3 o6 u% W
- var msg = begin_cell()
x+ Q+ N2 d; ]6 u8 ]1 {! } - .store_uint(0x10, 6)
5 x5 W7 ^2 y4 ` - .store_slice(response_address)
" D# T& `3 ~* z/ S, p - .store_coins(msg_value); N6 y; x% C* }- c( I
- ...
复制代码请记住,如果合约余额用完,交易将被部分执行,这是不允许的。 摘要TON 需要开发人员有意识地管理GAS:计算支出并检查是否提供了足够的GAS。 如果使用 "无限制数据结构",GAS成本会随着时间推移而增加。 TON 建议将超出部分退还给发送方--这也是开发者的责任。 如果GAS耗尽,交易将被部分执行。这可能是一个关键问题。 $ n2 p3 \, u2 o* g6 C |
1 d2 p; A. V6 I8 D, b! P
2 t% N) p) {; S7 P& [! l: t |