Advanced EOS 002 - Singletons

EOS Singletons

Persistence
In this example, we’re going to look at using singletons as a method to persist our contract state. Using a table for this would be a waste of resources given that we would only ever occupy one row, and assuming we don’t want to maintain a history of our contract state.

Singletons should be used to store contract state, or as a multi-index table alternative when only one row is required.

Defining the Singleton

Our contracts configuration is a perfect example of data you might want to persist in a singleton. To start we will define our object struct and assign it to a type definition.

struct Config {
	bool		closed = false;
	uint32_t	chars = 144;
};

typedef singleton<N(settings), Config> settings_table;

Defining the singleton takes a name and object-type argument, much the same as a multi-index table. We will also create an instance of our singleton called config which we will initialize in our constructor and use to access data later.

settings_table config;

Initializing the Singleton

We’re going to initialize our singleton in our contracts constructor so it’s ready to use in all our contracts member functions.

explicit singletons(action_name self) : contract(self), config(_self, _self) {}

Just like initializing a multi-index table, the singleton takes a contract-code and scope. We’ve set both the code and scope to _self here.

Getting Singleton Values

Let’s start interacting with our singleton by getting it’s stored values. We’re going to simply get the chars and print it to the console.

auto state = config.get();
print(state.chars);

And that’s it! Our config.get() retrieves the struct object we defined earlier and assigns it to our variable state. Now we can access the properties of our struct using dot notation.

Setting Singleton Values

In this example we will update the chars property using the member function singleton.set(STRUCT,PAYER). Let’s look at a few methods of building our struct to update our singleton.

1. Inline Updating

The simplest method is to build our struct inline, passing it directly to our set function like so.

config.set(Config{true, char_count}, _self);

Here we are statically setting closed to true while setting our chars to the variable passed as input to our action. This method is ideal for a singleton with only a few properties or when values can be statically set. But what about if we want to update a value while maintaining the previous state on our other properties?

2. Get & Update

For this we are going to use a fetch then update method. We will first get our current state using the singleton.get() function.

auto state = config.get();

a. Inline with previous state
Now we can build our struct again using values from our previous state and passing in our new variables. However, this method becomes cumbersome quickly if your struct has a large amount of properties.

config.set(Config{state.closed, char_count}, _self);

b. Updating previous state
Alternatively, you can simply update the property on the state object we fetched earlier, then pass the updated object to the set function like so.

state.chars = char_count;
config.set(state, _self);

Full contract code can be found on GitHub.