Tact allows you to add common boilerplate behaviors to your contract by using traits.
The Stoppable trait allows the contract to allow an owner role to stop the contract.
Consider for example a protocol where users can deposit funds, like a staking service or a compounding vault. If somebody discovers a security issue, we may want to stop the contract from accepting funds from new users.
Note that this trait doesn't allow to resume the contract after it has been stopped.
This trait implicitly adds the Ownable trait. Note that the Ownable trait doesn't allow the owner to transfer ownership to a different owner. To allow changing ownership, also add the OwnableTransferable trait.
Define state variables named owner: Address and stopped: Bool and call self.requireNotStopped() on actions that should be stopped.
import "@stdlib/deploy";
import "@stdlib/ownable";
/////////////////////////////////////////////////////////////////////////////
// this trait adds basic analytics to any contract to track how popular it is
trait Trackable with Ownable { // your new trait may rely on other traits
// Storage
owner: Address;
numMessagesReceived: Int; // your new trait may add state variables but should not specify their size
// Receivers
receive("reset stats") { // your new trait may handle specific messages
self.requireOwner();
self.numMessagesReceived = 0;
self.reply("reset done".asComment());
}
// Getters
get fun stats(): Int { // your new trait may add getters
return self.numMessagesReceived;
}
// Methods
fun receivedNewMessage() { // your new trait may define new contract methods
if (self.filterMessage()) {
self.numMessagesReceived = self.numMessagesReceived + 1;
}
}
virtual fun filterMessage(): Bool { // virtual functions can be overridden by users of this trait
// the default filtering behavior is to ignore messages sent by the owner
if (sender() == self.owner) {
return false;
}
return true;
}
}
/////////////////////////////////////////////////////////////////////////////
// this Counter contract is going to use our new trait to add analytics to it
contract Counter with Deployable, Trackable {
owner: Address; // The Trackable trait requires this exact state variable
numMessagesReceived: Int as uint64; // The Trackable trait requires this exact state variable
val: Int as uint32;
init() {
self.owner = sender(); // we can initialize owner to any value we want, the deployer in this case
self.numMessagesReceived = 0;
self.val = 0;
}
receive("increment") {
self.receivedNewMessage(); // here we are using our trait
self.val = self.val + 1;
}
get fun value(): Int {
return self.val;
}
// the trait allows us to override the default filtering behavior
override fun filterMessage(): Bool {
// our contract's custom filtering behavior is to remove all filters and count all messages
return true;
}
// receive("reset stats") is added automatically to allow owner to reset the stats
// get fun stats(): Int is added automatically to query the stats
// get fun owner(): Address is added automatically to query who the owner is
}