Subcurrency
The following contract implements the simplest form of a cryptocurrency. The contract allows the creation of new coins (i.e. minting) as well as sending coins from one address to another.
storage {
total_supply: int,
balances: (b256 => int),
}
// Sends an amount of newly created coins to an address
predicate Mint(receiver: b256, amount: int) {
let receiver_balance = mut storage::balances[receiver];
let total_supply = mut storage::total_supply;
constraint total_supply' == total_supply + amount;
constraint receiver_balance' == receiver_balance + amount;
}
// Sends an amount of existing coins from address `from` to address `receiver`
predicate Send(from: b256, receiver: b256, amount: int) {
let from_balance = mut storage::balances[from];
let receiver_balance = mut storage::balances[receiver];
constraint amount < from_balance;
constraint from_balance' == from_balance - amount;
constraint receiver_balance' == receiver_balance + amount;
}
This contract introduces some new concepts. Let's walk through it line by line.
The contract starts with a storage
declaration that contains two storage variables:
total_supply
is of typeint
and represents the total supply of coins available at any given point in time.balances
is a map fromb256
toint
and stores balances of addresses as integers.b256
is a primitive type that represents a 256-bit hash value and is used here to represent an address.
The contract also declares two predicates: Mint
and Send
.
The Mint
predicate is fairly simple:
- It has two parameters
receiver: b256
andamount: int
. The goal of this predicate to mintamount
coins and send them toreceiver
. - It initializes a local variable called
receiver_balance
using the storage access expressionmut storage::balances[receiver]
. This syntax returns the value inbalances
thatreceiver
maps to and makes it "mutable". The predicate also initializes another local variable calledtotal_supply
tomut storage::total_supply
. - It enforces two constraints:
- The first constraint requires the total supply to be incremented by
amount
. - The second constraint requires the balance of
receiver
to be incremented byamount
.
- The first constraint requires the total supply to be incremented by
The Send
predicate has the following structure:
- It has three parameters
from: b256
,receiver: b256
, andamount: int
. The goal of this predicate to sendamount
coins from addressfrom
to addressreceiver
. - It initializes a local variable called
from_balance
to the balance offrom
and another variable calledreceiver_balance
to the balance ofreceiver
. It also makes both balances "mutable". - It enforces three constraints
- The first constraint requires
from_balance
to be larger thanamount
. That is, the addressfrom
must currently actually have enough coins to send toreceiver
. - The second constraint effectively decrements the balance of
from
byamount
, by requiring the next state offrom_balance
to befrom_balance - amount
. - The third constraint effectively increments the balance of
receiver
byamount
, by requiring the next state ofreceiver_balance
to bereceiver_balance + amount
.
- The first constraint requires
Note to make things simpler and easier to understand, this contract has no authentication anywhere in its code. That is, anyone can mint new coins and initiate a transfer from one arbitrary address to another. This, of course, is not the desired behavior for most cryptocurrencies. That being said, examples of authentication can be found in this Github repository.