createMessage. In practice, it’s immediately followed by send:
- Supports extra currencies
- Supports workchains
- Supports sharding (formerly
splitDepth) - Integrated with auto-serialization of the
body - Automatically detects, body ref or not
- Also used for deployment (passing code+data of a contract)
Deployment (e.g. creating a jetton wallet contract from a minter)
is handled here as well — via
stateInit with automatic address computation.The concept is based on union types
There are many ways in which contracts interact.- sometimes, you “send to an address”
- … but sometimes, you have workchain + hash
- sometimes, you compose
StateInitfrom code+data - … but sometimes,
StateInitis a ready cell - sometimes, you send a message to basechain
- … but sometimes, you use a
MY_WORKCHAINconstant - sometimes, you just attach tons (msg value)
- … but sometimes, you also need extra currencies
Extra currencies: union
In most cases, the “message value” is just tonAmount:value is a union:
Destination: union
The same idea of union types spreads onto destination of a message.Deployment, StateInit, and workchains
Consider the following example. A contract “jetton minter” deploys a “jetton wallet”. The wallet’s code and initial data are known:In TON, “the address of a contract” == “hash of its initial state”.
Sharding: deploying “close to” another contract
ThecreateMessage interface also supports initializing contracts in specific shards. For example, in sharded jettons, a jetton wallet must be deployed to the same shard as the owner’s wallet.
In other words, the intention is:
- a jetton wallet must be close to the owner’s wallet
- this closeness is determined by a shard depth (syn. fixed prefix length, syn. split depth)
shard depth = 8:
| Title | Addr hash (256 bits) | Comment |
|---|---|---|
| closeTo (owner addr) | 01010101...xxx | owner’s wallet |
| shardPrefix | 01010101 | first 8 bits of closeTo |
| stateInitHash | yyyyyyyy...yyy | calculated by code+data |
| result (JW addr) | 01010101...yyy | jetton wallet in same shard as owner |
Technically, shard depth must be a part of
StateInit (besides code+data) — for correct initialization inside the blockchain. The compiler automatically embeds it.But semantically, shard depth alone makes no sense. That’s why shard depth + closeTo is a single entity.Body ref or not: compile-time calculation
In TON Blockchain, according to the specification, a message is a cell (flags, dest address, stateInit, etc.), and its body can be either inlined into the same cell or can be placed into its own cell (and be a ref). Fortunately, a developer shouldn’t keep this in mind. Just passbody, and the compiler does all calculations:
- if
bodyis small, it’s embedded directly into a message cell (cheaper, because creating cells is expensive) - if
bodyis large or unpredictable, it is wrapped into a ref
createMessage() call has its own TBody, and the compiler estimates its size:
- maximum size is less than 500 bits and 2 refs — small, “no ref”
- size is potentially >= 500 bits or >= 2 refs — large, “ref”
- contains
builderorsliceinside — unpredictable, “ref”
body is already a cell, it will be left as a ref, without any surprise:
body: obj.toCell(), pass just body: obj, let the compiler take care of everything.
Body is not restricted to structures
An interesting fact — this also works:createMessage<(int32, uint64)>(...) and encoded correctly.
This simply illustrates the flexibility of the type system.
Body can be empty
If nobody is needed, it can be omitted entirely:
A curious question: “what’s the type of
body here”? The answer is: void.A struct is declared like this: .
Hence, omitting body leads to void, and by convention, void fields may be omitted in object literals.SendMode
Typically,createMessage() is followed by msg.send(mode).
Read about send modes.
Low-level terminology: StateInit != code+data
This section is intended for experienced users; it discusses terminology.
block.tlb):
it also contains fixed_prefix_length (automatically set by the compiler if toShard), ticktock info, a library cell; moreover, code and data are nullable.
Therefore, the structure code + data is named ContractState, NOT StateInit:
stateInit: ContractState | cell is named stateInit, emphasizing that StateInit can be initialized automatically from ContractState (or can be a well-formed rich cell).
Q: Why not send, but createMessage?
Typically, yes — a message is sent immediately after being composed. However, certain scenarios require separating composition from sending:- not just send, but send and estimate fees,
- or estimate fees without sending,
- or get a message hash,
- or save a message cell to storage for later sending,
- or even push it to an action phase.
send(...) function, hard to identify what meaning is intended by the exact call.
Q: Why not provide a dedicated deploy function?
In other words: whystateInit is a destination? Why not make a deploy() function that accepts code+data, and drop stateInit from a regular createMessage?
The answer lies in terminology. Yes, attaching stateInit is often referred to as deployment, but it’s an inaccurate term. TON Blockchain doesn’t have a dedicated deployment mechanism. A message is sent to some void — and if this void doesn’t exist, but a way to initialize it (code+data) is provided — it’s initialized immediately and accepts the message.
To emphasize deployment intent, give it a name:
Universal createExternalLogMessage
The philosophy mirrors that ofcreateMessage. But external outs don’t have bounce, attached tons, etc. So, the options for creating are different.
Currently, external messages are used only for emitting logs (for viewing them in indexers). But theoretically, they can be extended to send messages to off-chain.
Example:
dest and body, actually:
body it fits into the same cell or needs to be a ref. UnsafeBodyNoRef is also applicable.
Emitting external logs, example 1:
ExtOutLogBucket is a variant of a custom external address for emitting logs to the outer world.
It includes some topic (arbitrary number), that determines the format of the message body.
In the example above, a deposit event is emitted (reserving topic deposit = 123), and the resulting logs will be indexed by destination address without requiring body parsing.