Skip to main content
Tolk provides a high-level function 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:
When extra currencies are required, the message value includes both Toncoin and a dictionary:
This is possible because the value field is defined as a union:
Then, the compiler selects the matching representation and serializes it accordingly.

Message destination

Message destinations are defined using the same union-based approach.
The destination field accepts multiple representations:
Each option represents a valid way to specify the message destination. The selected form is resolved at compile time.

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:
When sending a message to the wallet, it may not yet exist on-chain. In this case, the message must include the wallet’s code and initial data. The message destination is therefore defined by the wallet’s StateInit.
For more advanced scenarios, configure additional fields:

Shard-based deployment

The createMessage 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.
The example below uses shard_prefix is 8: Deployment with shard targeting is configured as follows:
Shard prefix is part of 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, the body should be provided. The compiler determines how the body is stored.

Inline or referenced body

  • If body is small, it is embedded directly into the message cell.
  • If body is large or has unpredictable size, it is stored as a ref.
The decision is made at compile time. No runtime checks are involved. This is implemented using generics:
Each 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 builder or slice, its size is considered unpredictable, and it is stored as a ref.
If the body is large or unpredictable, it can be force-inlined by wrapping it into a special type:
If body is already a cell, it is stored as a ref:
Therefore, do not pass 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:
The call is inferred as createMessage<(int32, uint64)>(...) and encoded accordingly.

Empty body

If no body is needed, it can be omitted entirely:
In this example, the 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 with createMessage() 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:
While a field 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:
Only dest and body are available for external outgoing messages:
The compiler automatically determines whether body fits into the same cell or must be stored as a reference. UnsafeBodyNoRef is also supported. Example of emitting an external log:
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.