Skip to main content

Flipper: our first Coco program

In this example, we're going to go step by step from installing Coco compiler to writing, compiling and running a small logic. The logic, called Flipper demonstrates the use of Coco compiler and test environment Cocolab. The only tool that's needed for the whole example is a coco binary one can install as described in installation instructions.

Create module

Coco module is the basic source code that compiles to a manifest that can be deployed to MOI. It contains a coco.nut file that serves as a configuration for module compilation, containing amonng other module name, output name, output options etc.

To create a module, first create a new folder, then run an nut init command in it.

mkdir flipper
cd flipper
coco nut init Flipper

nut init command creates a file coco.nut in the current folder that specifies Flipper as the module name, flipper.yaml as the output and PISA v0.5.0 in BIN format as the output. You can manually edit this file and most of the options in coco.nut file can be overridden with coco command options.

Write Coco code

Now let's write our first program. Coco module is a collection of all files in a folder with .coco extension and with coco <module_name> (in our example, coco Flipper) in the first line of code. As our example is simple, we'll just create one file, called flipper.coco:

flipper.coco
coco Flipper

state logic:
values Map[Identifier]Bool

endpoint deploy Init():
mutate values <- Flipper.Logic.values:
values[Sender] = true

endpoint dynamic Flip():
mutate values <- Flipper.Logic.values:
values[Sender] = !values[Sender]

endpoint Mode() -> (value Bool):
observe values <- Flipper.Logic.values:
value = values[Sender]

endpoint dynamic Set(value Bool):
mutate values <- Flipper.Logic.values:
values[Sender] = value

Compiling with Coco is simple

coco compile

compile command compiles the module, it uses all .coco files in the folder, compiles and produces the manifest, in our case flipper.yaml. Manifest is the logic that can be deployed on the blockchain, but coco can also run it locally so you can test it before deployment.

Starting Cocolab

coco binary also contains a tool that can execute the manifest, called Cocolab. It's started with:

coco lab start

lab start starts a REPL (Read-Eval-Print-Loop), an interactive environment where one can run and evaluate the program. Before running anything, we need to create at least one user and declare it to be the default.sender. You can use any username (e.g. your own name), but we're using a generic default_user name here.

register default_user
set default.sender default_user

To compile the manifest to local logic, we issue a compile command:

compile Flipper from manifest(flipper.yaml)

As all of the above commands are repeated every time one starts Cocolab, there's also a shortcut, you can run Cocolab using init and have a default_user registered, set as default.sender and the logic in the current folder (one in coco.nut file) compiled.

coco lab init

Running Flipper in Cocolab

Once Flipper is compiled and we have a default_user that acts as a default.sender we can check if it works. In Cocolab, let's check if we have the user and the logic:

users

and we should get a response like

default_user [0x199a5ef028fd0bf4b632f110edb498999211f5f1045493f5287269ad3ff23a04]

The identifier (a 32-byte hex code) will be different for everyone, but we should have at least one user.

logics
[0] Flipper [0x5c6801eadefac2187d3b04543ef59caf37d3eb3644cc69ec79eb6a683eb78af6]

again, the logic's identifier will be different, but Flipper should be there.

Let's deploy the logic first: every logic has to be deployed and only then we can invoke endpoints and check the data. On blockchain, the logic can be deployed by passing the manifest, and at the time we can also perform some initialization. This is done by invoking an endpoint that has deploy qualifier. In our case, we have Init endpoint.

deploy Flipper.Init()

This command initializes the logic, as our example was written, it writes some initial value true into the values field of the logic's state for the Sender - i.e. the user (actor) that has deployed the logic. So let's check the logic's state for this user.

observe Flipper.Logic.values[default_user]

We should get a response

true

Looks like deployment has worked and that state was set to true.

Now, let's flip the value by invoking Flip endpoint.

invoke Flipper.Flip()

and let's read the value not by directly observing the value as above (with observe command in the REPL) but by invoking Mode endpoint.

invoke Flipper.Mode()

The last line of the response should read

Execution Outputs ||| value:false

So, the returned data was value: false, looke like Flipper works. Let's check if direct setting of the value using Set endpoint works:

invoke Flipper.Set(value: true)
observe Flipper.Logic.values[default_user]

The value is true, looks like Flipper works.

Now, let's register a new user and invoke the logic as this new user: their value should also work. First, register the user, wipe the current default.sender and set it to the new user, so invoke commands will be sent as this new user.

register Robert
wipe default.sender
set default.sender Robert
invoke Flipper.Set(value: false)
invoke Flipper.Mode()
invoke Flipper.Flip()
observe Flipper.Logic.values[Robert]

The above should set the value for user Robert to false, then flip it and it should be observed as true.

Congratulations, your first Coco program works, you can leave Cocolab!

exit