跳转到内容

发送消息

TON 区块链是基于消息的—要与其他合约通信和部署新合约,您需要发送消息。

Tact 中的消息通常使用内置Struct SendParameters组成,它由以下部分组成:

字段类型说明
bounceBool如果设置为true(默认),当接收合约不存在或无法处理消息时,消息会退回给发送者。
toAddressTON 区块链中的内部接收器 Address
valueInt消息中要发送的nanoToncoins的金额。 此值通常用于支付转发费用,除非使用了可选标志SendPayGasSeparately
modeInt一个 8 位值,用于配置发送消息的方式,默认值为 00。 参见:消息模式。 见:消息mode.
bodyCell?可选消息正文作为Cell
codeCell?可选 合约的初始代码(编译后的字节码)
dataCell?可选合约的初始数据(合约的init()函数的参数)

字段 codedata 被称为初始化包,它用于新合约的部署。

发送简单回复

最简单的消息是回复收到的消息,返回消息的所有过剩值:

self.reply("Hello, World!".asComment()); // asComment converts a String to a Cell with a comment

发送消息

如果需要更高级的逻辑,可以直接使用 send() 函数和 SendParameters Struct

事实上,前面使用 .reply() 的示例可以通过调用下面的 send() 函数来实现:

send(SendParameters{
// bounce is set to true by default
to: sender(), // sending message back to the sender
value: 0, // don't add Toncoins to the message...
mode: SendRemainingValue | SendIgnoreErrors, // ...except for ones received from the sender due to SendRemainingValue
body: "Hello, World".asComment(), // asComment converts a String to a Cell with a comment
});

另一个示例是向指定的 Address发送一条消息,消息的11 TON,body为带有 String "Hello, World!"的注释:

let recipient: Address = address("...");
let value: Int = ton("1");
send(SendParameters{
// bounce is set to true by default
to: recipient,
value: value,
mode: SendIgnoreErrors, // will send the message despite any errors
body: "Hello, World!".asComment(),
});

可选标记 SendIgnoreErrors表示即使在发送消息过程中发生错误,也会继续发送下一条消息。 在发送阶段,没有错误会导致交易回滚。

发送输入的消息

要发送二进制类型的消息,您可以使用以下代码:

let recipient: Address = address("...");
let value: Int = ton("1");
send(SendParameters{
// bounce is set to true by default
to: recipient,
value: value,
mode: SendIgnoreErrors, // don't stop in case of errors
body: SomeMessage{arg1: 123, arg2: 1234}.toCell(),
});

部署合约

要部署一个合约,你需要用 initOf计算它的地址和初始状态,然后在初始化消息中发送它们:

let init: StateInit = initOf SecondContract(arg1, arg2);
let address: Address = contractAddress(init);
let value: Int = ton("1");
send(SendParameters{
// bounce is set to true by default
to: address,
value: value,
mode: SendIgnoreErrors, // don't stop in case of errors
code: init.code,
data: init.data,
body: "Hello, World!".asComment(), // not necessary, can be omitted
});

外发消息处理

TON 区块链上的每笔交易都由 多个阶段 组成。 发送消息是在计算阶段进行评估,但是在该阶段不是发送。 相反,它们将按照出现顺序排入操作阶段,在计算阶段中列出的所有操作,如外向消息或储备请求,都会在此阶段执行。

由于所有值都是在计算阶段中计算的,所有费用都是在计算结束前计算的,而且在操作阶段中出现异常时不会恢复交易,因此向外发送消息可能会因操作费转发费不足而失败,不会出现跳转。

请看下面的例子:

// This contract initially has 0 nanoToncoins on the balance
contract FailureIsNothingButAnotherStep {
// And all the funds it gets are obtained from inbound internal messages
receive() {
// 1st outbound message evaluated and queued (but not sent yet)
send(SendParameters{
to: sender(),
value: ton("0.042"), // plus forward fee due to SendPayGasSeparately
mode: SendIgnoreErrors | SendPayGasSeparately,
});
// 2nd outbound message evaluated and queued (but not sent yet, and never will be!)
send(SendParameters{
to: sender(),
value: 0,
mode: SendRemainingValue | SendIgnoreErrors,
});
}
}

在那里,第二条消息实际上不会被发送:

  • 计算阶段结束后,计算合约的剩余价值 R\mathrm{R}

  • 在出站消息处理过程中,假设入站消息中提供了足够的金额,第一条消息会在余额上留下 R(0.042+forward_fees)\mathrm{R} - (0.042 + \mathrm{forward\_fees}) nanoToncoins

  • 处理第二条消息时,合约会尝试发送 R\mathrm{R} nano Toncoins,但发送失败,因为剩余的金额已经较少。