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
相关产品推荐
相关产品推荐

