Constants
Constants in Tact could be a little bit more advanced than in popular languages: they could be virtual and abstract. Smart contracts often need to implement multiple traits and sometimes you need to configure some of them in compile time. Constructors in traits are prohibited due to their unpredicted behavior. So, we have to use constants and fields instead to pass values to them. It is the job of a 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 in compile time and cannot be changed. You can define a constant on the top level or inside a contract/trait. Let's define a constant on top level:
const MY_CONSTANT: Int = 42;
Similar 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 the constants that could be defined in a trait but changed in a contract. It is useful when you need to configure some of the traits in 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 overwrite 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 could be very useful to help a compiler to have some values in compile time, for example, you can enable and disable features without needing to change the code and not wasting gas.
trait Treasure {
virtual const ENABLE_TIMELOCK: Bool = true;
receive("Execute") {
if (self.ENABLE_TIMELOCK) {
//
// This branch would be removed in compile time if ENABLE_TIMELOCK is false
//
}
}
}
contract MyContract with Treasure {
override const ENABLE_TIMELOCK: Bool = false;
}