Skip to main content
Each Tolk contract has special entrypoints – reserved functions that handle different message types. Handling an incoming message uses ordinary language constructs.

onInternalMessage

Contracts primarily handle internal messages. Users interact with contracts through their wallets, which send internal messages to the contract. The entrypoint is declared as follows:
The basic guidelines are:
  • For each incoming message, declare a struct with a unique 32-bit prefix, opcode.
  • Declare a union type that represents all supported messages.
  • Parse this union from in.body and match over structures.

Example breakdown

  • struct declares business data, including messages and storage.
  • (0x12345678) defines a message opcode, 32-bit. Unique prefixes are used to route binary data in in.body.
  • AllowedMessage is a type alias for a union type.
  • in: InMessage provides access to message properties such as in.body and in.senderAddress.
  • T.fromSlice parses binary data into T. When combined with lazy, parsing is performed on demand.
  • match routes a union type. Within each branch, the type of msg is narrowed, smart cast.
  • throw 0xFFFF is a standard reaction to an unrecognized message. Contracts typically ignore empty messages, which represent balance top-ups with an empty body. For this reason, throw is guarded by if or assert.
Bounced messages are not handled by onInternalMessage.

Define and modify contract storage

Contract storage is defined as a regular structure. Storage types commonly define load and save methods to access persistent contract data:
Then, in match cases, invoke those methods:
Storage may also be loaded once before the match statement and reused across branches.

Legacy onInternalMessage

In FunC, a handler is declared as:
Tolk continues to support this style of declaration. Code produced by a converter results in:
The modern approach uses the InMessage type. It simplifies message handling and reduces gas consumption. Migrating from the legacy code is:
  • myBalance -> contract.getOriginalBalance(), contract state, not a message property
  • msgValue -> in.valueCoins
  • msgFull -> use in.senderAddress etc., without manual parsing
  • msgBody -> in.body

onBouncedMessage

onBouncedMessage is a special entrypoint for handling bounced messages.
InMessageBounced is similar to InMessage. The difference is that in.bouncedBody has a different layout, depending on how the original message is sent.

BounceMode in createMessage

When sending a message using createMessage, the bounce behavior must be specified:
BounceMode is an enum with the following options:
  • BounceMode.NoBounce.
  • BounceMode.Only256BitsOfBodyin.bouncedBody contains 0xFFFFFFFF followed by the first 256 bits; lowest gas cost, often sufficient.
  • BounceMode.RichBounce — provides access to the entire originalBody; gasUsed, exitCode, and other failure-related properties are also available; highest gas cost.
  • BounceMode.RichBounceOnlyRootCell — similar to RichBounce, but originalBody contains only the root cell.

Handle in.bouncedBody

The structure of in.bouncedBody depends on the BounceMode. When all bounceable messages are sent using Only256BitsOfBody:
When RichBounce is used:
Mixing different modes, where some messages use a minimal body and others use a full body, complicates handling and is discouraged. The binary body of an outgoing message, such as TransferMessage, is returned either as in.bouncedBody with a 256-bit limit or as rich.originalBody, which contains the full slice. To handle this consistently:
  • define a union type that includes all message types that may be bounced;
  • handle it using lazy in onBouncedMessage.

onExternalMessage

In addition to internal messages, a contract can handle external messages originating off-chain. For example, wallet contracts process external messages and perform signature validation using a public key.
When a contract accepts an external message, it has limited gas for execution. After validating the request, the contract must call acceptExternalMessage() to increase the available gas. The commitContractDataAndActions() function can also be used. Both functions are part of the standard library and are documented inline.

Additional reserved entrypoints

Tolk defines several reserved entrypoints:
  • fun onTickTock is invoked on tick-tock transactions;
  • fun onSplitPrepare and fun onSplitInstall are reserved for split and install transactions; currently not used by the blockchain;
  • fun main is used for simple snippets and demos.
The following program is valid:
It compiles and runs, pushing value 123 onto the stack. The corresponding TVM method_id is 0.