You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetton合约跨合约调用transfer报错73:非所有者问题求助

问题分析与解决方案

为什么会报err73?

你当前的代码逻辑完全搞反了:合约A试图调用用户的Jetton钱包执行transfer操作,但用户Jetton钱包的owner是用户本人,只有owner有权限发起transfer,合约A不是该钱包的所有者,所以触发了"not owner"错误(err73)。这种写法根本不可行。

正确的Jetton存提逻辑

Deposit(存入)流程

Deposit不能由合约主动发起,必须由用户自己操作:

  • 用户调用自己的Jetton钱包,执行transfer指令,将Jetton转到合约A对应的Jetton钱包地址
  • 合约A的Jetton钱包收到Jetton后,会触发合约A的on_jetton_receive回调函数,合约A在这个回调里记录用户的存款余额

Withdraw(提取)流程

  • 用户调用合约A的withdraw方法,合约验证用户身份后,调用合约自己的Jetton钱包执行transfer,将Jetton转到用户的Jetton钱包地址

修正后的核心代码示例

1. 合约A实现on_jetton_receive回调(处理Deposit)

当合约的Jetton钱包收到Jetton时,Jetton钱包会给合约A发送这条消息,合约需要处理并更新用户余额:

() recv_internal(int msg_value, slice in_msg_body) impure {
    slice op = in_msg_body.load_uint(32);
    if (op == op::on_jetton_receive) {
        // 解析Jetton转账信息
        int jetton_amount = in_msg_body.load_coins();
        slice sender_address = in_msg_body.load_slice();
        slice forward_payload = in_msg_body.load_slice();
        
        // 解析forward_payload里的发起存款的用户地址
        slice user_address = forward_payload.load_slice();
        
        // 更新用户的存款余额(合约需维护用户余额映射)
        (map(int, int) user_balances) = load_data();
        user_balances[user_address] = user_balances.get(user_address, 0) + jetton_amount;
        save_data(user_balances);
    }
    // 其他内部消息处理...
}

2. 合约A实现withdraw方法(处理提取)

合约调用自己的Jetton钱包发起转账,此时合约是自己Jetton钱包的owner,有权限执行transfer:

() withdraw(int jetton_amount) impure {
    slice user_address = get_sender_address();
    (map(int, int) user_balances) = load_data();
    
    // 验证用户余额足够
    if (user_balances.get(user_address, 0) < jetton_amount) {
        throw(400, "insufficient balance");
    }
    
    // 扣除用户余额
    user_balances[user_address] -= jetton_amount;
    save_data(user_balances);
    
    // 构造transfer消息给合约的Jetton钱包
    cell transfer_msg = begin_cell()
        .store_uint(op::transfer, 32)
        .store_coins(jetton_amount)
        .store_slice(user_address)  // 接收Jetton的用户地址
        .store_slice(my_address())  // 回调地址(可选)
        .store_coins(0)  // 附加的TON数量
        .store_maybe_ref(null())  // 附加消息
        .end_cell();
    
    // 获取合约的Jetton钱包地址
    slice jetton_master_address = ...; // 替换为USDT Jetton Master地址
    cell jetton_wallet_state_init = calculate_jetton_wallet_state_init(my_address(), jetton_master_address, jetton_wallet_code);
    slice jetton_wallet_address = calculate_jetton_wallet_address(jetton_wallet_state_init);
    
    // 发送消息给合约的Jetton钱包
    cell msg = begin_cell()
        .store_uint(0x10, 6)
        .store_slice(jetton_wallet_address)
        .store_coins(0.01 ton)  // 足够支付手续费的TON
        .store_uint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1)
        .store_ref(transfer_msg)
        .end_cell();
    
    send_raw_message(msg, SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_BOUNCE_ON_ACTION_FAIL);
}

3. 用户端Deposit操作示例

用户需要调用自己的Jetton钱包,发送transfer到合约的Jetton钱包,同时在附加消息里带上自己的地址:

// 用户钱包端代码
() deposit_to_contract(int jetton_amount) impure {
    slice contract_jetton_wallet_address = ...; // 合约A的Jetton钱包地址
    cell forward_payload = begin_cell()
        .store_slice(my_address())  // 告诉合约是谁存的钱
        .end_cell();
    
    cell transfer_msg = begin_cell()
        .store_uint(op::transfer, 32)
        .store_coins(jetton_amount)
        .store_slice(contract_jetton_wallet_address)
        .store_slice(my_address())
        .store_coins(0.01 ton)
        .store_maybe_ref(forward_payload)
        .end_cell();
    
    // 发送给自己的Jetton钱包
    cell msg = begin_cell()
        .store_uint(0x10, 6)
        .store_slice(my_jetton_wallet_address)
        .store_coins(0.01 ton)
        .store_uint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1)
        .store_ref(transfer_msg)
        .end_cell();
    
    send_raw_message(msg, SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_BOUNCE_ON_ACTION_FAIL);
}

关键注意点

  • 合约只能操作自己的Jetton钱包,因为合约是该钱包的owner
  • Deposit必须由用户主动发起,合约无法代用户操作其Jetton钱包
  • 处理on_jetton_receive时,要验证消息来源确实是合约的Jetton钱包,防止伪造存款记录

内容的提问来源于stack exchange,提问作者Newbie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 01:40:18