createMessage, which is followed by send:
Union types in contracts interaction
When handling a message, some values can be represented in multiple valid forms. Union types allow expressing these alternatives explicitly, so the same message-handling logic can accept and correctly process any of them.Message value
The message value consists of a Toncoin amount:value field is defined as a union:
Message destination
Message destinations are defined using the same union-based approach.Deployment and StateInit
Consider a contract that deploys another contract. For example, a jetton minter deploying a jetton wallet. The wallet code and its initial data are known:
StateInit.
Shard-based deployment
ThecreateMessage interface supports deploying contracts into a specific shard. For example, in sharded jettons, a jetton wallet must be deployed into the same shard as the owner’s wallet.
This is expressed as follows:
- A jetton wallet is deployed close to the owner’s wallet;
- This closeness is defined by
shard_prefix.
shard_prefix is 8:
| Title | Address hash | Comment |
|---|---|---|
closeTo owner address | 01010101...xxx | owner’s wallet |
shardPrefix | 01010101 | first 8 bits of closeTo |
stateInitHash | yyyyyyyy...yyy | derived from code and data |
result jetton wallet address | 01010101...yyy | jetton wallet in the same shard as owner |
StateInit together with code and data and is required for correct contract initialization in the blockchain. The compiler embeds it automatically. But semantically, on its own shard prefix is not meaningful. For this reason, shard prefix and closeTo are treated as a single entity.
Message body
A message is a cell. Its body can either be embedded into the same cell or placed into a separate cell and referenced. When creating a message, thebody should be provided. The compiler determines how the body is stored.
Inline or referenced body
- If
bodyis small, it is embedded directly into the message cell. - If
bodyis large or has unpredictable size, it is stored as a ref.
createMessage() call has its own TBody, allowing the compiler to estimate the body size:
- if the maximum size is less than 500 bits and 2 refs, the body is embedded;
- if the size is 500 bits or more, or requires more than 2 refs, the body is stored as a ref;
- if the body contains
builderorslice, its size is considered unpredictable, and it is stored as a ref.
body is already a cell, it is stored as a ref:
body: obj.toCell(). Pass body: obj, and the compiler will choose the optimal and correct encoding.
Non-struct body
body is not limited to structs. For example:
createMessage<(int32, uint64)>(...) and encoded accordingly.
Empty body
If nobody is needed, it can be omitted entirely:
body type is void.
A struct is declared as CreateMessageOptions<TBody = void>. By convention, fields of type void may be omitted in object literals.
Sending modes
A message created withcreateMessage() is typically sent using msg.send(mode).
ContractState and StateInit
StateInit contains more fields than code and data. For this reason, the code and data pair is defined as ContractState:
stateInit: ContractState | cell is named as stateInit, emphasizing that a full StateInit can be automatically initialized from ContractState.
createExternalLogMessage
createExternalLogMessage follows the same general model as createMessage. External outgoing messages do not support bounce behavior, attached Toncoin, or related options, so the set of available fields is different. External messages are used only for emitting logs intended for indexers.
Example:
dest and body are available for external outgoing messages:
body fits into the same cell or must be stored as a reference. UnsafeBodyNoRef is also supported.
Example of emitting an external log:
ExtOutLogBucket represents a custom external address for emitting logs to the outer world. It includes a numeric topic that defines the message body format.
In the example above, a deposit event is emitted using topic: 123. Such logs can be indexed by destination address without parsing the message body.