Skip to content

Functions

Functions in Tact can be defined in different ways:

All functions, except for receiver functions, can have a trailing comma in their definitions (parameter lists) and calls (argument lists):

fun foo(
a: Int, // trailing comma in parameter lists is allowed
) {}
fun bar() {
foo(
5, // trailing comma in argument lists is allowed too!
);
}

Available since Tact 1.6.1

Naming a parameter with an underscore _ causes its value to be considered unused and discarded. This is useful when you don’t access the parameter but want to include it in the signature for possible overrides. Note that such a wildcard parameter name _ cannot be accessed.

trait WildThing {
// Using wildcards for parameter names
virtual fun assure(_: Int, _: Int): Bool {
return true;
}
}
contract YouMakeMyHeartSing with WildThing {
// And then overriding them with concrete names
override fun assure(a: Int, b: Int): Bool {
return a + b == b + a;
}
}

Global static functions

You can define global functions anywhere in your program:

fun customPow(a: Int, c: Int): Int {
let res: Int = 1;
repeat(c) {
res *= a;
}
return res;
}

Virtual and abstract functions

You can allow a contract inheriting a trait to modify an internal function marked with the virtual keyword by using override. A function can also be marked as abstract, in which case the inheriting contract must define its implementation:

trait FilterTrait with Ownable {
// Virtual functions can be overridden by users of this trait
virtual fun filterMessage(): Bool {
return sender() != self.owner;
}
abstract fun specialFilter(): Bool;
}
contract Filter with FilterTrait {
// Overriding the default behavior of the FilterTrait
override fun filterMessage(): Bool {
return true;
}
override fun specialFilter(): Bool {
return true;
}
}

Extension function

Extension functions allow you to implement extensions for any possible type.

Warning The first argument MUST be named self, and the argument’s type must be the type you are extending.

extends fun customPow(self: Int, c: Int): Int {
let res: Int = 1;
repeat(c) {
res *= self;
}
return res;
}

Mutation functions

Mutation functions perform mutation on a value by replacing it with an execution result. To perform mutation, the function must change the self value.

extends mutates fun customPow(self: Int, c: Int) {
let res: Int = 1;
repeat(c) {
res *= self;
}
self = res;
}

Native functions

Native functions are direct bindings of FunC functions:

Note Native functions can also be mutation and extension functions.

@name(store_uint)
native storeUint(s: Builder, value: Int, bits: Int): Builder;
@name(load_int)
extends mutates native loadInt(self: Slice, l: Int): Int;

Assembly functions, asm

Available since Tact 1.5

Read more about them on their dedicated page: Assembly functions.

Receiver functions

Receiver functions are special functions responsible for receiving messages in contracts and can be defined only within a contract or trait.

contract Treasure {
// This means that this contract can receive the comment "Increment", and this function would be called for such messages
receive("Increment") {
self.counter += 1;
}
}

Getter Functions

Getter functions define getters on smart contracts and can be defined only within a contract or trait. Getter functions cannot be used to read another contract’s state. If you need to obtain data, you should do so by sending a message with a request and defining a receiver function that processes the request answer.

contract Treasure {
get fun counter(): Int {
return self.counter;
}
}

Explicit resolution of method ID collisions

Available since Tact 1.6

Like other functions in TON contracts, getters have their unique associated function selectors, which are 1919-bit signed integer identifiers commonly called method IDs.

Method IDs of getters are derived from their names using the CRC16 algorithm as follows: (crc16(<function_name>) & 0xffff) | 0x10000. In addition, the Tact compiler conditionally reserves some method IDs for use in getters of supported interfaces, namely: 113617113617 for supported_interfaces, 115390115390 for lazy_deployment_completed, and 121275121275 for get_abi_ipfs.

Sometimes, getters with different names end up with the same method ID. If this happens, you can either rename some of the getters or manually specify the method ID as a compile-time expression like so:

contract ManualMethodId {
const methodId: Int = 16384 + 42;
get(self.methodId)
fun methodId1(): Int {
return self.methodId;
}
get(crc32("crc32") + 42 & 0x3ffff | 0x4000)
fun methodId2(): Int {
return crc32("crc32") + 42 & 0x3ffff | 0x4000;
}
}

Unlike getters, method IDs for internal functions and some special functions are obtained sequentially: integers in the inclusive range from 4-4 to 00 are given to certain message handlers, while internal functions are numbered with method IDs starting at 11 and going up to 21412^{14} - 1 inclusive.

Since method IDs are 1919-bit signed integers and some of them are reserved, only the inclusive ranges from 218-2^{18} to 5-5 and from 2142^{14} to 21812^{18} - 1 are free to be used by users. To avoid collisions, it is recommended to specify method IDs only in these ranges, avoiding the method IDs of Tact-specific getters mentioned above.