English 简体中文 繁體中文 한국 사람 日本語 Deutsch русский بالعربية TÜRKÇE português คนไทย french

简体中文 繁體中文 English 日本語 Deutsch 한국 사람 بالعربية TÜRKÇE คนไทย Français русский

回答

收藏

8.3 气体管理

开源社区 开源社区 5856 人阅读 | 0 人回复 | 2025-03-16

  • 在以太坊虚拟机中,如果用户提供的GAS太少,一切都将还原。
  • 如果用户提供的GAS足够多,则会自动计算实际成本并从余额中扣除。

    ' {) \$ m! {+ D
在 Solidity 中,合约开发人员并不太关心GAS问题。如果用户提供的GAS太少,一切都会恢复原状,就像什么都没发生过一样(但GAS不会退还)。如果用户提供了足够的GAS,实际成本将自动计算并从余额中扣除。
在 TON,情况有所不同:
  • 如果GAS不足,交易将部分执行。
  • 如果GAS过多,则必须退还多余的GAS。这是开发人员的责任。
  • 由于异步特性,TON 无法自己完成所有工作。

    ! ], s3 U+ c6 ~5 U; N) }
如果 "合约组 "交换信息,则必须在每个信息中进行控制和计算。TON 无法自动计算GAS。完整执行交易及其所有后果可能需要很长时间,到最后,用户钱包中可能没有足够的TON币。这里使用的是携带价值原则。
计算GAS并检查 msg_value
我们可以使用消息流程图来估算每种情况下每个处理程序的成本,并插入 msg_value 的充分性检查。
  • 找到信息流的 "入"。
  • 估算每个处理程序的成本。
  • 在 "入口 "检查 msg_value 是否足够。
  • 如果到处都有余量(比如 1 TON),就无法满足要求。GAS在 "结果 "中分配

    / e2 E& T( a5 b# [: ]0 r* Z
假设你的合约发送了三条信息,那么每条信息只能发送 0.33 TON。这意味着他们的 "需求 "应该更少。仔细计算整个合约的GAS需求量非常重要。
如果在开发过程中,您的代码开始发送更多信息,情况就会变得更加复杂。需要重新检查和更新GAS要求。
小心地退回多余GAS
  • 如果不将多余的GAS退还给发送者,这些资金就会在您的合约中长期累积。
  • 这并不可怕,但并不理想。您可以添加一个功能,用于归还多余GAS。
  • 像 TON Jetton 这样的知名合约,会将 op::excesses 消息返回给发送者。
    0 ?, X  o& r* a' A' }
如果不将多余的GAS退还给发送者,这些资金就会在您的合约中长期累积。从原则上讲,这并不可怕,但这只是一种次优做法。您可以添加一个功能来清除多余的GAS,但像 TON Jetton 这样的常用合约仍会以 op::excesses 消息返回发送方。
  • send_raw_message(msg, SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE) 传递其余GAS。
  • 如果消息流是线性的:每个处理程序只发送一条消息,则此功能非常有用。

    ( ~( q/ `5 K2 F
TON 有一个有用的机制:
9 F. c4 U$ U' y( @. h, _send_mode_carry_all_remaining_message_value = 64。在 send_raw_message() 中使用这种模式时,剩余的GAS将与消息一起(或返回)转发给新的收件人。如果消息流是线性的:每个消息处理程序只发送一条消息,那么这种模式就很方便。但在某些情况下,不建议使用这种机制:
不建议携带所有剩余GAS时
  • storage_fee  从合约余额中扣除,而不是从输入的GAS中扣除。

    / G; v' Y6 @# c) A
storage_fee 从合约余额中扣除,而不是从输入的天然气中扣除。因此,如果合约中没有其他非线性处理程序,那么随着时间的推移、 storage_fee 会耗尽所有余额,因为所有进来的东西都要出去。
  • 发出事件消耗的是合约余额,而不是GAS。

    . b4 G3 z. \  _7 f# H' N) s/ T, F
如果您的合约发出事件,即向外部地址发送信息。该操作的费用将从合约余额中扣除,而不是从 msg_value 中扣除。
  • 在信息中附加值或使用 SEND_MODE_PAY_FEES_SEPARETELY 会消耗合约余额。
    9 k4 C) ?; s/ w6 |
# Y7 |! Q# x; X* ]  s
  1. var msg = begin_cell().store_uint(0x18, 6).store_slice(destination)
    " [' z3 g$ {# [8 v* x- Y$ k
  2.       .store_coins(10000000)                ;; This will be deducted from contract balance, y, Y# C7 k0 U, G
  3.       .store_uint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1).store_ref(msg_body);& h9 s; c+ W5 G4 n$ \5 ~
  4.     send_raw_message(msg.end_cell(), 0);3 @6 x4 t0 M2 Y

  5. ; d3 A7 x2 u& q) Q
  6.     emit_log_cell_ref(query_id, ...         ;; This also spends contract balance
复制代码
如果您的合约在发送信息时附加价值或使用 SEND_MODE_PAY_FEES_SEPARETELY = 1。这些操作会从合约余额中扣除,这意味着未使用的返回是 "亏本工作"。
计算GAS成本的常用方法
下面是 TON Wallet 的代码片段。这是保持合约余额稳定的常用方法。
  1. int ton_balance_before_msg = my_ton_balance - msg_value;& S  W1 i% I) N! m
  2. int storage_fee = const::min_tons_for_storage - min(ton_balance_before_msg, const::min_tons_for_storage);. i6 ?/ A0 @& \+ d* e
  3. msg_value -= storage_fee + const::gas_consumption;% G( B8 v! V9 d. Z8 S0 u1 A

  4. $ _/ J  E* t; W8 {9 K
  5. if(forward_ton_amount) {
    ! `: G/ f  ^( Y8 m
  6.     msg_value -= (forward_ton_amount + fwd_fee);
    " L+ b+ r* o$ Z
  7.     ...
    9 J6 y5 Y- r# m
  8. }* b8 _, h% z- K% F; g$ Y$ I9 |. X

  9. 5 U/ z8 a2 p# I1 ?! f" n
  10. if (msg_value > 0) {    ;; there is still something to return, Q. b0 }" k, G4 U1 C" J9 F
  11.     var msg = begin_cell()
    # a8 K7 z% ~+ A: {0 Q- e0 n
  12.         .store_uint(0x10, 6)& v7 u# e- i' W; K
  13.         .store_slice(response_address)% `% b* x5 h/ S3 n2 y& W
  14.         .store_coins(msg_value)
    ! A* v7 K( D7 \3 w' ]1 x& ~
  15.     ...
复制代码
请记住,如果合约余额用完,交易将被部分执行,这是不允许的。
摘要
  • TON 需要开发人员有意识地管理GAS:计算支出并检查是否提供了足够的GAS。
  • 如果使用 "无限制数据结构",GAS成本会随着时间推移而增加。
  • TON 建议将超出部分退还给发送方--这也是开发者的责任。
  • 如果GAS耗尽,交易将被部分执行。这可能是一个关键问题。

    - u4 a0 [3 V* F9 _; @4 m
7 M7 G& g% c8 ?7 ^& E0 S

0 o% M; D0 U, J
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则