跳转到内容

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 the 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 npx tact --help command 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 whole 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:

Terminal window
# recommended
yarn upgrade @tact-lang/compiler

Sometimes, bug fixes require us to change the 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. There, X.Y.Z is the target version you want to install:

Terminal window
# X.Y.Z must be replaced by the actual version number
yarn add --exact @tact-lang/compiler@X.Y.Z

Build artifacts

A number of build artifacts can be produced per compilation of each contract. Some of the artifacts can be omitted using configuration settings.

The location of the artifacts depends on the compilation method:

  • For projects using tact.config.json, use the output field in the config file
  • For single contract compilation, you can specify the output directory using the -o or --output flag:
    Terminal window
    tact contract.tact --output ./custom-output
    If not specified, the files will be generated in the same directory as the input file.

In Blueprint-based projects, output 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 that it was prepared for and then the byte size of the contract compiled to BoC.

The following sub-headings describe the respective sections of the .md report.

Structures

Written as '# Types' prior to Tact 1.6

The first section introduces the present structures, i.e. some of the composite types, Structs and Messages that are 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 are described with their respective signatures and TL-B schemas, which include the message opcodes.

For example:

Total structures: 10
### StateInit
TL-B: `_ code:^cell data:^cell = StateInit`
Signature: `StateInit{code:^cell,data:^cell}`
### Deploy
TL-B: `deploy#946a98b6 queryId:uint64 = Deploy`
Signature: `Deploy{queryId:uint64}`
...etc.

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:

### GeneratedOpcode
TL-B: `generated_opcode#6dfea180 = GeneratedOpcode`
Signature: `GeneratedOpcode{}`
### ManuallySpecifiedOpcode
TL-B: `manually_specified_opcode#12345678 = ManuallySpecifiedOpcode`
Signature: `ManuallySpecifiedOpcode{}`

where 6dfea180 and 12345678 specified after the # in the constructor definitions are opcodes written in hexadecimal notation, representing 3232-bit unsigned integers. Thus, the automatically generated 6dfea180 opcode of the GeneratedOpcode Message represents the decimal value of 18454040321845404032, and the manually provided 12345678 opcode of the ManuallySpecifiedOpcode Message represents the decimal value of 305419896305419896, and not 1234567812345678 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
## lshift
Argument: x
## gas

There, 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()

There, the exit codes in the range from 00 to 255255 are those reserved by TON Blockchain or Tact compiler, while the exit code of 4293342933 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:

Trait inheritance diagram

There, 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 in order to compute the initial state of other contracts.

If the contract has no dependencies, only its name is displayed:

Contract dependency diagram without dependencies

However, if the contract, say FirstOne, somewhere in its code computes the initial state of another contract, say SecondOne, such a relationship is shown in the diagram:

Contract dependency diagram with a single dependency

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:

Contract dependency diagram of the JettonMinter

Compiled contract, .boc

The end result of compiling each contract is to get 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’d only need to send the message containing the initial code and data, usually referred to as initial state or StateInit, to the computed address and provide enough Toncoin for the deployment.

But 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’s 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

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 given in a machine-readable format, although 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 with all needed wrappers is produced. The entities added there all use the @ton/core library.

All the Structs and Messages 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 storeStructureName() and loadStructureName() functions to help compose and parse cells out of those 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 };
}

Finally, the ABI declarations are defined and a contract wrapper is given. The latter is designed specifically for you to be able to easily deploy and interact with the deployed contract.

export class Playground implements Contract {
// ...
static async fromInit() {
const init = await Playground_init(); // init code and data
const address = contractAddress(0, init);
return new Playground(address, init);
}
// ...
}