Quickstart Guide
Now that you’ve installed Pint, it’s time to write your first Pint contract. The first contract we will implement is a counter.
Creating a Project Directory
You'll start by making a directory to store your Pint code. Open a terminal and enter the following
commands to make a projects
directory:
mkdir ~/projects
cd ~/projects
Now, we will use the pint
tool to create a new project in the projects
directory:
pint new counter
cd counter
The pint new
commands creates a new Pint project with the provided name. In the newly created
directory counter
, you should find a pint.toml
file as well as a src/
directory with a single
file in it called contract.pnt
:
projects
└── counter
├── pint.toml
└── src
└── contract.pnt
The auto-generated pint.toml
file describes how to build the project. The src/contract.pnt
file is
the root file of a contract project, i.e., this is where the compilation starts when we build the
project.
Open the pint.toml
and inspect its content:
[package]
name = "counter"
kind = "contract"
[dependencies]
# Library dependencies go here.
[contract-dependencies]
# Contract dependencies go here.
The one thing to note is that the kind
field in this pint.toml
is set to contract
. This is the
default project kind. Alternatively, kind
can be a library.
Writing a Pint Program
Next, open the src/contract.pnt
file and erase its content. Then, paste the following:
storage {
counter: int,
}
predicate Increment {
state counter: int = mut storage::counter;
constraint (counter == nil && counter' == 1) || counter' == counter + 1;
}
This is a contract with a single predicate and a single storage variable. The storage variable
counter
, of type int
(i.e. integer), is declared in a storage
block. The only predicate in
this contract is called Increment
and contains two statements:
- A
state
declaration which reads the variablecounter
. - A
constraint
statement which enforces some logic on the state of the contract.
The above constraint is essentially saying: "if counter
hasn't been set yet (i.e. is nil
), set
the new value of counter
to 1
(counter' == 1
). Otherwise, increment counter
by 1
".
Don't worry if this looks a bit overwhelming! We will later dive into each feature in this contract separately.
Building the Project
To build the project above, simply run the following command in the counter
directory:
pint build
You should get something like this:
Compiling counter [contract] (/path/to/counter)
Finished build [debug] in 4.157208ms
contract counter C276E4E5EAF789F82B671E53F0B202AE357C511F2B711E4921310946ACE63B7C
└── counter::Increment 3D7ABBA5C62DF177EFD61CA9D74ED055B29267404112583FA2E4C0D98B828149
Note the two 256-bit numbers in the output. These represent the content hash (i.e. the sha256
of
the bytecode) of the counter
contract (as a whole) and the Increment
predicate, respectively.
These "addresses" will be used when referring to the contract or the predicate (in a proposed
solution for example).
In the counter
directory, you will also notice a new directory called out
. Navigate to
out/debug
and inspect the two json
files that you see.
counter.json
represents the compiled bytecode in JSON format, which is the most important artifact produced by the compiler. This file is used when validating a solution. That is, when a solution is submitted, this file contains the bytecode that "runs" and decides whether all the relevant constraints are valid.counter-abi.json
is the Application Binary Interface (ABI) of the contract. It basically describes how to interact with the contract from an external application or another contract. For example, while crafting a solution, the ABI can be used to figure out where the various storage variables are stored (i.e. their keys) and their types. This information is crucial to form correct solutions.
Note: Appendix C contains the ABI spec.
Now that we have built and inspected the artifacts of our project, we can proceed to build an application that interacts with this contract. We won't cover this topic here, but you can check out this Getting Started with Essential Application book, which covers this exact same "counter" example and how to build a Rust application that interacts with it using the Essential VM.