Compilation
The Tact compiler can produce various outputs, ranging from the BoC of the compiled contract to various supplementary files, such as the compilation report or the contract package.
With the proper configuration settings, you can customize the behavior of the compiler to skip the generation of some or all build artifacts, and even control the addition or exclusion of getters for supported interfaces.
Since the Tact compiler is a command-line program, some of the configuration settings can be set directly. When inside a folder with a Tact-based project, such as one created using the Blueprint or from the tact-template, refer to the command npx tact --help
for further instructions.
Compiler upgrades
Work on the compiler is active and extensive, so be sure to check out NPM whenever we push new releases. To follow updates, see the Tact Kitchen in Telegram for the short scoop, or our GitHub organization for the full picture in all its details.
To upgrade the Tact compiler to its latest version in a Blueprint-based project or a project created from the tact-template, run:
# recommendedyarn upgrade @tact-lang/compiler
npm i @tact-lang/compiler@latest
pnpm upgrade @tact-lang/compiler
bun update --latest @tact-lang/compiler
Sometimes, bug fixes require us to change existing behavior, introducing small breaking changes. To be prepared for these, make sure to read the CHANGELOG.md and act accordingly. Except for bug fixes, Tact tries its best not to introduce breaking changes in minor or patch versions according to semantic versioning.
If you want to pin down a specific version of the compiler, run the following command. Here, X.Y.Z
is the target version you want to install:
# X.Y.Z must be replaced by the actual version numberyarn add --exact @tact-lang/compiler@X.Y.Z
# X.Y.Z must be replaced by the actual version numbernpm i --save-exact @tact-lang/compiler@X.Y.Z
# X.Y.Z must be replaced by the actual version numberpnpm add --save-exact @tact-lang/compiler@X.Y.Z
# X.Y.Z must be replaced by the actual version numberbun add --exact @tact-lang/compiler@X.Y.Z
Build artifacts
A number of build artifacts can be produced per compilation of each contract. Some artifacts can be omitted using configuration settings.
The location of the artifacts depends on the compilation method:
- For projects using
tact.config.json
, use theoutput
field in the config file. - For single contract compilation, you can specify the output directory using the
-o
or--output
flag:If not specified, the files will be generated in the same directory as the input file.Terminal window tact contract.tact --output ./custom-output
In Blueprint-based projects, the output
field is not used, and all generated files are always placed in build/ProjectName/
.
Compilation report, .md
Every markdown compilation report first features the name of the contract for which it was prepared, followed by the byte size of the contract compiled into BoC.
The following subheadings describe the respective sections of the .md
report.
Structures
Written as '# Types' prior to Tact 1.6The first section introduces the present structures, i.e., some of the composite types, Structs and Messages declared or imported directly in the contract code, as well as those exposed from the Core standard library.
Along with the number of structures present, each of the Structs and Messages is described with its respective signature and TL-B schemas, which include the message opcodes.
For example:
Total structures: 10
### StateInitTL-B: `_ code:^cell data:^cell = StateInit`Signature: `StateInit{code:^cell,data:^cell}`
### DeployTL-B: `deploy#946a98b6 queryId:uint64 = Deploy`Signature: `Deploy{queryId:uint64}`
...etc.
The TL-B schema of each Message contains its opcode, which is handy for looking up the auto-generated opcodes as well as confirming the manually provided ones. For example, the following Message declarations:
message GeneratedOpcode { }message(0x12345678) ManuallySpecifiedOpcode { }
translate to their respective TL-B schemas:
### GeneratedOpcodeTL-B: `generated_opcode#6dfea180 = GeneratedOpcode`Signature: `GeneratedOpcode{}`
### ManuallySpecifiedOpcodeTL-B: `manually_specified_opcode#12345678 = ManuallySpecifiedOpcode`Signature: `ManuallySpecifiedOpcode{}`
Here, 6dfea180
and 12345678
, specified after the #
in the constructor definitions, are opcodes written in hexadecimal notation representing 32-bit unsigned integers. Thus, the automatically generated 6dfea180
opcode of the GeneratedOpcode
Message represents the decimal value 1845404032, and the manually provided 12345678
opcode of the ManuallySpecifiedOpcode
Message represents the decimal value 305419896, and not 12345678 as it might appear.
Get methods
This section specifies the number of available get methods or getter functions and outlines them with their argument names, if any.
For example:
Total get methods: 2
## lshiftArgument: x
## gas
Here, the lshift()
getter has a single argument x
, whereas the gas()
getter has no arguments.
Exit codes
This section lists all default exit codes, as well as those generated from the error messages of the require()
, along with those messages for your convenience.
For example:
* 2: Stack underflow* 3: Stack overflow...etc.* 135: Code of a contract was not found* 42933: Hey, I'm the error message of require()
Here, the exit codes in the range from to are those reserved by TON Blockchain or the Tact compiler, while the exit code of is produced by the respective call to the require()
function.
Trait inheritance diagram
This section shows a Mermaid diagram of inherited traits, including the BaseTrait
.
For example:
Here, JettonWallet
inherits the WalletExitcodes
and GasConstant
traits, and all inherit the BaseTrait
.
Contract dependency diagram
This section shows a Mermaid diagram of contract dependencies, i.e., any calls to initOf
to compute the initial state of other contracts and codeOf
to compute the code of other contracts.
If the contract has no dependencies, only its name is displayed:
However, if the contract, for example FirstOne
, somewhere in its code computes the initial state of another contract, for example SecondOne
, such a relationship is shown in the diagram:
A real-world example of this would be the JettonMinter
contract, commonly referred to as the Jetton Master. Often, JettonMinter
needs the initial state of the JettonWallet
, which is why JettonMinter
defines the following internal function:
inline fun getJettonWalletInit(address: Address): StateInit { return initOf JettonWallet(address, myAddress());}
Thus, the following dependency diagram is produced:
Compiled contract, .boc
The end result of compiling each contract is obtaining its code in the BoC format, which is placed in a binary file with the .boc
extension.
By combining the initial code with the initial data for the contract, you can locally and deterministically obtain the blockchain Address
for the given contract.
From there, you only need to send the message containing the initial code and data, usually referred to as the initial state or StateInit
, to the computed address and provide enough Toncoin for the deployment.
However, instead of doing this manually, prefer using the provided TypeScript wrappers and other deployment facilities.
Contract package, .pkg
The contract package is a way to bundle a compiled contract, its dependencies, and all related metadata into a single file. It is a JSON file with the .pkg
extension that one can use to verify the smart contract’s origin.
Additionally, the contract ABI is included in the package as a string.
Read more: OTP-006: Contract Package.
Contract ABI, .abi
A contract’s ABI is a JSON file with the .abi
extension, which describes the contract’s data structures, receivers, getters, possible exit codes (errors), and interfaces.
Essentially, it’s a compilation report provided in a machine-readable format, though without the diagrams.
Note that the contract ABI is included in the contract package as a string.
Read more: OTP-002: Contract ABI.
Bindings and wrappers
Using the compiled .boc
directly is inconvenient and error-prone, which is why Tact also provides a number of contract wrappers in different general-purpose languages.
TypeScript wrappers, .ts
To aid in deployment and further interactions with the contract, after each successful compilation, a .ts
file containing all necessary wrappers is produced. The entities added there all use the @ton/core library.
All the structs and message structs observable in the compilation report are provided as new type
definitions.
export type StateInit = { $$type: 'StateInit'; code: Cell; data: Cell;}
For each such definition, there are corresponding storeStructureName()
and loadStructureName()
functions to help compose and parse cells from these structures.
export function storeStateInit(src: StateInit) { return (builder: Builder) => { const b_0 = builder; b_0.storeRef(src.code); b_0.storeRef(src.data); };}
export function loadStateInit(slice: Slice) { const sc_0 = slice; const _code = sc_0.loadRef(); const _data = sc_0.loadRef(); return { $$type: 'StateInit' as const, code: _code, data: _data };}
All global constants are exported as well.
// Global constantexport const CUSTOM_CONSTANT_NAME = 42n;
Finally, the ABI declarations are defined and a contract wrapper is provided. The latter is designed specifically to allow you to easily deploy and interact with the deployed contract.
export class Playground implements Contract { // Constants declared in the contract or its inherited traits public static readonly storageReserve = 0n;
// Named constructor from the initial package // for deployments and further interactions static async fromInit() { const init = await Playground_init(); // init code and data const address = contractAddress(0, init); return new Playground(address, init); }
// Named constructor from the address // for interacting with deployed contracts static fromAddress(address: Address) { return new Playground(address); }
// A helper function to send messages to the contract async send( provider: ContractProvider, via: Sender, args: { value: bigint, bounce?: boolean| null | undefined }, message: null | "plays", ) { /* ... */ }
// Getters async getLshift(provider: ContractProvider, x: bigint): bigint { /* ... */ } async getGas(provider: ContractProvider): bigint { /* ... */ }}
Now, you can also access two records that mirror each other: a ContractName_errors
record, which maps exit code numbers to their error message strings, and a ContractName_errors_backward
record, which does the opposite — maps the error message strings to the exit code numbers. Organized this way, they provide convenient bidirectional access to exit codes.
Both records only feature the standard exit codes reserved by TON Blockchain and Tact compiler and those generated by require()
function. If you define some custom exit codes as global or contract constants, those will be exported separately.
export const Playground_errors = { 2: { message: `Stack underflow` }, 3: { message: `Stack overflow` }, // ... 47499: { message: `The expected exit code didn't match the actual one!` },} as const
export const Playground_errors_backward = { "Stack underflow": 2, "Stack overflow": 3, // ... "The expected exit code didn't match the actual one!": 47499,} as const
// Global constantexport const CUSTOM_CONSTANT_NAME = 42n;
export class Playground implements Contract { // Constants declared in the contract or its inherited traits public static readonly storageReserve = 0n;
// ...}
The ContractName_errors_backward
record is also accessible through a ContractName.errors
static field on each exported contract class.
export class Playground implements Contract { // ...
// Same record from error message names to their exit code numbers, // but exposed as a public static class field and accessible through // instances of this contract in your tests or scripts public static readonly errors = Playground_errors_backward;
// ...}
To help you access opcodes of all message structs that are declared or imported directly in the contract code and those exposed from the Core standard library, there’s a ContractName.opcodes
static field.
export class Playground implements Contract { // ...
// A record from message struct names to their opcodes (32-bit integer headers) public static readonly opcodes = Playground_opcodes;
// ...}