Skip to content

Constants

Constants in Tact can be slightly more advanced than in popular languages: they can be virtual and abstract. Smart contracts often need to implement multiple traits, and sometimes you need to configure some of them at compile time. Constructors in traits are prohibited due to their unpredictable behavior. Therefore, we have to use constants and fields instead to pass values to them. It is the job of the main contract to implement values and constants for all traits.

Simple constant

Let’s start with a simple constant. It is a value that is defined at compile time and cannot be changed. You can define a constant at the top level or inside a contract/trait. Let’s define a constant at the top level:

const MY_CONSTANT: Int = 42;

Similarly for traits and contracts:

trait MyTrait {
const MY_CONSTANT: Int = 42;
}
contract MyContract {
const MY_CONSTANT: Int = 42;
}

Virtual and abstract constants

Virtual constants are constants that can be defined in a trait but changed in a contract. This is useful when you need to configure some traits at compile time. Let’s define a virtual constant and an abstract one:

trait MyTrait {
virtual const MY_FEE: Int = ton("1.0");
}
trait MyAbstractTrait {
abstract const MY_DEV_FEE: Int;
}

Now you can override the defaults in the contract:

contract MyContract with
MyTrait,
MyAbstractTrait, // trailing comma is allowed
{
override const MY_FEE: Int = ton("0.5");
override const MY_DEV_FEE: Int = ton("1000");
}

This can be very useful for helping the compiler determine certain values at compile time. For example, you can enable and disable features without needing to change the code and without wasting gas:

trait Treasure {
virtual const ENABLE_TIMELOCK: Bool = true;
receive("Execute") {
if (self.ENABLE_TIMELOCK) {
//
// This branch would be removed at compile time if ENABLE_TIMELOCK is false
//
}
}
}
contract MyContract with Treasure {
override const ENABLE_TIMELOCK: Bool = false;
}