TON Highload V3 - msg init

最近在做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,方向正确,被我撞到了。

https://github.com/ton-blockchain/highload-wallet-contract-v3/blob/main/contracts/highload-wallet-v3.func#L169C77-L169C87

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是不是哪里没钱… 耽误了蛮久