TON 智能合约的 3 种授权类型

发布: (2025年12月5日 GMT+8 04:48)
4 min read
原文: Dev.to

Source: Dev.to

Introduction

在智能合约中,我们经常需要将特定操作限制在特定参与者上。
一个常见的例子是钱包合约:我们必须对消息发送者进行授权,以确保只有合法用户才能发送资金。

下面介绍了 TON 智能合约中三种常见的消息授权方式。这些机制默认用于外部消息,除非另有说明。

Signature‑based Authorization (External Messages)

合约使用存储在其数据中的公钥来验证消息的签名。这种方法是确定性的,并且能够工作,因为合约存储是公开的,私钥无法保存在合约内部。

// wallet3-code.fc (excerpt)
() recv_external(slice in_msg) impure {
  var signature = in_msg~load_bits(512);
  ;; some code
  var (stored_seqno, stored_subwallet, public_key) =
      (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256));
  ;; 
  throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key));
  accept_message();
  ;; other code
}

Source: wallet‑v3 contract on GitHub

Why it works

  1. 确定性 – 加密验证是确定性的,不像随机数生成那样不确定。
  2. 公开存储 – 公钥可以安全地存放在合约的状态中。

基于签名的授权是外部消息中最常用的方法。

Address Comparison (Internal Messages)

对于内部消息,合约仅仅将发送者的地址与存储在状态中的地址进行比较。

// nft-collection-editable.fc (excerpt)
() recv_internal(cell in_msg_full, slice in_msg_body) impure {
    if (in_msg_body.slice_empty?()) {
        ;; ignore empty messages
        return ();
    }
    slice cs = in_msg_full.begin_parse();
    int flags = cs~load_uint(4);

    if (flags & 1) {
        ;; ignore all bounced messages
        return ();
    }

    slice sender_address = cs~load_msg_addr();

    int op = in_msg_body~load_uint(32);
    int query_id = in_msg_body~load_uint(64);

    var (owner_address, next_item_index, content, nft_item_code, royalty_params) = load_data();

    ;; some code

    throw_unless(401, equal_slices(sender_address, owner_address));

    ;; some code
}

Source: NFT Collection contract on GitHub

合约存储所有者地址,如果发送者地址不匹配则抛出错误。这种做法可以扩展为存储多个授权地址,以对应不同的角色。

Authorization for Multiple Senders (Jetton Wallet Example)

当合约需要授权任意数量的发送者——例如在 Jetton 合约中——我们需要一种更灵活的方案。Jetton Wallet 会验证发送者钱包是否属于同一个 Jetton Master,或者是否是由发送者地址推导出的确定性钱包地址。

// jetton-wallet.fc (excerpt)
() receive_jettons(slice in_msg_body, slice sender_address, int my_ton_balance, int msg_value)
    impure inline_ref {
    (int status, int balance, slice owner_address, slice jetton_master_address) = load_data();
    int query_id = in_msg_body~load_query_id();
    int jetton_amount = in_msg_body~load_coins();
    slice from_address = in_msg_body~load_msg_addr();
    slice response_address = in_msg_body~load_msg_addr();

    throw_unless(error::not_valid_wallet,
        equal_slices_bits(jetton_master_address, sender_address)
        |
        equal_slices_bits(
            calculate_user_jetton_wallet_address(
                from_address,
                jetton_master_address,
                my_code()
            ),
            sender_address
        )
    );

    ;; other code
}

Source: Jetton Wallet contract on GitHub

Key points

  • calculate_user_jetton_wallet_address 根据发送者地址、Jetton Master 地址以及合约代码(my_code())推导出预期的钱包地址。
  • my_code() 通过 c7 寄存器返回当前合约代码(参见TVM 寄存器文档)。
  • 合约还可以在其存储中保存另一个合约的代码,以计算外部合约地址。
  • 当需要初始化参数时,必须仔细验证;如果两个合约共享已知参数(例如管理员地址),最好从自己的存储中读取,而不是从消息中获取。

Conclusion

选择合适的授权策略是设计 TON 智能合约架构的关键环节。对外部消息使用签名验证,对内部简单检查使用地址比较,而在需要授权大量可能发送者的场景(如 Jetton Wallet)则使用确定性地址计算。

祝编码愉快! 🚀

Back to Blog

相关文章

阅读更多 »

智能合约入门

介绍 学习区块链概念可能具有挑战性,但理解智能合约并不一定如此。本指南介绍了智能合约的基础…

加密支付网关详解

加密支付网关定义 加密支付网关是一种服务或内部组件,用于将链下业务事件——如订单或发票——连接……