最近在做ton上的代付Gas项目,使用highload v3钱包帮用户交易代付Gas。
记录一个遇到的问题。
当用户钱包未部署时,需要进行部署。 构造的msg大约是
const signedCell = Cell.fromBoc(Buffer.from(sig, 'base64'))[0];
const message = loadMessage(signedCell.asSlice())
body = message.body;
stateInit = message.init;
msg = internal({
to: Address.parse(address),
value: BigInt(toNano(amount)),
bounce: bounce,
body: body,
init: stateInit
})
交易报错, 在浏览器上看到以下信息
Compute Phase
Success:
true
Exit code:
37
Vm steps:
151
Gas used:
5562
看到exit code为37,搜索时搜到了vm code, Action Phase = 37 表示账户余额不足。
感觉不太对,是compute 时exit的,应该不是 Action Phase = 37 。
一番挣扎后,猜想是否是合约highload里的错误码,OK,方向正确,被我撞到了。
const int error::invalid_message_to_send = 37;
// () recv_external(slice msg_body) impure 里
throw_if(error::invalid_message_to_send, maybe_state_init); ;; throw if state-init included (state-init not supported)
external msg里不让带init,一带就会报37。
啊.. 看到这里是找到问题了,但是我的功能要怎么实现呢?
于是再去翻了下TON文档, 文档里提到
Highload v3 will always store the query ID (replay protection) once all the checks pass, however a message may not be sent due to some conditions, including but not limited to:
containing state init (such messages, if required, may be sent using the special op code to set the action cell after an internal message from Highload wallet to itself)
not enough balance
invalid message structure (that includes external out messages - only internal messages may be sent straight from the external message)
文档里提供了思路 => set the action cell after an internal message
external-in 的msg 先是调自己,然后再从internal调出去,internal里没有限制不让包含state init。
msg由internal变成action。就完全可以了,嘿嘿
const action = {
type: 'sendMsg',
mode: SendMode.PAY_GAS_SEPARATELY,
outMsg: internal({
to: Address.parse(address),
value: BigInt(toNano(amount)),
body: body,
init: stateInit ?? stateInit,
bounce: true
})
}
const msg = new HighloadWalletV3(Address.parse(from)).packActions([action], toNano(amount), HighloadQueryId.fromQueryId(BigInt(queryId)));
事后分析感觉还挺简单的,中间一直怀疑37 code是不是哪里没钱… 耽误了蛮久
