---
breaks: false
---
# Basic concepts
## Anatomy of a DApp
A _DApp_---short for Distributed Application---consists
of several components. Primarily, an interface,
usually web-based, which is designed for user
interaction. The most important component of a dapp
is the one (or many) smart contracts, which are stored on the blockchain. This part should ideally be minimal,
as computation is replicated on every node. Most
of the computation should be done in the client
apps.
![dapp](https://gitlab.com/kinokasai/tezos-dapp-practicum/-/raw/master/dapp.png)
### Smart contracts
Michelson smart contracts on the Tezos blockchain have three distinct components: the *balance*, the *storage space*, and the *contract code* itself. The balance denotes how many tokens the users have stored on the contract. The storage space is stored on the blockchain, enabling data to be persistent in between contract calls. Finally, the contract's code also distinguishes three components:
* the parameters, which describe the
available entrypoints to the contract,
* the type of its storage,
* and the actual Michelson instructions.
A Michelson contract has to respect a calling
convention. Its input stack must be a tuple made
of the parameter with which it is called and its
storage. Its output must be a tuple made of the
list of operations that need to be emitted after the
contract is run, and its new storage value.
Each contract is moreover indexed by a specific id, which we also refer to as the contract's _address_.
#### Contract Specification
In the rest of this document, we will use the
following syntax for specifying contracts:
`[_storage_] * _entrypoint_(_value_) ->
[_new-storage_], [_operations_]`
On the left of `->`, we describe the storage previous to the contract call, and the entrypoint with its arguments. On the right side, we describe the resulting storage, and the list of returned operations.
## Exercises
The first application, [counter.ligo](https://gitlab.com/kinokasai/tezos-dapp-practicum/-/tree/master/exos/counter/src/counter.ligo), is available on a
[git repository](https://gitlab.com/kinokasai/tezos-dapp-practicum/). To check out this repository:
git clone https://gitlab.com/kinokasai/tezos-dapp-practicum/
Solutions are available on the `solution` branch. However, we recommend to really try to do the exercise and ask for help before looking at the solutions.
# Exercise 1: Basic Math
## Level 0 - Counter
The first contract that we are going to deploy is
one of the simplest: a counter. Basically, the
contract is going to track how many time it is called. Using the sintax defined above, the contract can be specified as follows:
`[0] * main(Unit) -> [1], []`
`[13] * main(Unit) -> [14], []`
`[x] * main(Unit) -> [x + 1], []`
where ``main`` names the contract's entrypoint. In other words, the counter contract increments the stored integer and returns an empty list of operations.
### PascaLigo Implementation
In these series of excercises, we will implement the smart contracts using [LIGO](https://ligolang.org/).
Here is entirety of the smart contract code [counter.ligo](https://gitlab.com/kinokasai/tezos-dapp-practicum/-/tree/master/exos/counter/src/counter.ligo):
```
type parameter is unit
type storage is nat
function main (const param: parameter; const s: storage)
: (list(operation) * storage) is
block {skip} with
((nil: list(operation)), s + 1n)
```
- `type parameter is unit`:
Here we define the parameter type of the
contract. As the counter does not depend on any
input, it is `unit`. In the functional world,
`unit` describes a unique value. It is akin to
`void` in C.
- `type storage is nat`: This describes the shape of the storage. This is
the value that will be preserved between calls,
our counter. As it must necessarily be positive -
a contract can't be called -1 times -, we use a
natural number.
- The signature of the entrypoint function `main` is given by:
```
function main (const param: parameter; const s: storage) :
(list(operation) * storage) is
```
We
can recognize the Tezos calling conventions:
`parameter * storage` tuple as input, and operations
to be emitted * new storage for output.
- ```block { skip }```
Each Pascaligo function consists of a number
of instructions in a block. However, our counter
needs no instructions, so it will only
contain the do-nothing instruction, `skip`.
- ``` with ((nil: list(operation)), s + 1n) ```
The right-hand side of the `with` clause describes the
return value of the function. As our counter contract does
not need to make further operations such as a transfer or an origination, we
return an empty operation list, `nil`. Since `nil` could be
of any type, we need to annotate it. Finally, we
also return the new storage value, which is
incremented by one (implementing the counter specification). Note that the literal is also
annotated - `1n` is a natural number, whereas `1`
is an integer.
### Web application
The interface of the counter dapp comprises of two elements, a number, representing the counter, and a button to call the contract.
```
import {Tezos} from '@taquito/taquito';
import {TezBridgeSigner} from '@taquito/tezbridge-signer';
```
These lines are needed to import [Taquito](https://tezostaquito.io/), the library we use to communicate with a node.
```
// FIXME: Put your originated address here
let contract_address = "KT1MGDoCkk2L6zCfViRa8gzhFbxC3R877bUm";
var tk = Tezos;
```
First, some simple initialization. Later on, we will originate the counter contract. At that point, we need to replace `contract_address` with the address at
which your contract was originated.
```
tk.setProvider({rpc: 'http://localhost:18731', signer: new TezBridgeSigner ()})
```
Here is the setup for Taquito. For ease development, we
query a local sandbox node.
```
function render(elt) {
document.querySelector('#info').innerHTML = elt
}
```
This function is used to render information on the
web page, such as whereas the app is waiting for a
signature, for the block to be included, etc...
```
function update_storage() {
tk.contract.at(contract_address)
.then(contract => contract.storage())
.then(storage => document.querySelector('#value').innerHTML = storage.toString())
}
```
This function queries the chain for the contract
storage, and updates the page accordingly.
```
function call_contract() {
tk.contract.at(contract_address)
.then(contract => {
render("Waiting for signature...")
return contract.methods.main(null).send();})
.then(op => {
render("Sent! Waiting for confirmation...");
return op.confirmation();})
.then(block => {
render("confirmed!")
return update_storage(); })
.catch(err => {
console.log(err)
render(err.message)
})
return null;
}
```
The real meat of the application. This function
will get the script of the contract at a specific
address, then will call a method on that contract.
Note that the method called - `main` - is
dependent on the name of our ligo function.
```
// Load the storage on page load
update_storage();
document.querySelector('#button').addEventListener('click', call_contract);
```
The final line links the button click event with a
contract call.
#### Specification
A click of the button should increment the the
displayed by one when the operation is included in
the block.
### Testing time!
First, we launch a tezos babylonnet sandbox. This
can be beachieved using `teztool`.
If you don't have `teztool`, you can get it using
docker:
docker pull registry.gitlab.com/nomadic-labs/teztool:latest
and then register it with the following alias:
alias teztool='docker run -it -v $PWD:/mnt/pwd -e MODE=dind \
-e DIND_PWD=$PWD \
-v /var/run/docker.sock:/var/run/docker.sock \
registry.gitlab.com/nomadic-labs/teztool:latest'
Now we can launch the sandbox:
teztool babylonnet sandbox --baker bootstrap5 \
--time-between-blocks 7 start 18731
We also need to install a Tezos client, which will allow us to communicate with the node. We use snap for this:
wget https://gitlab.com/abate/tezos-snapcraft/-/raw\/master/snaps/tezos_5.1.0_multi.snap?inline=false -o tezos_5.1.0_multi.snap
sudo snap install tezos_5.1.0_multi.snap --dangerous
export PATH=/snap/bin/:$PATH
Now we can talk with our node through the `tezos.client` command.
You may have to configure the client to use use the correct RPC port to communicate with the node launched by teztool:
tezos.client -A 127.0.0.1 -P 18731 config update
and then to register the bootstrap aliases:
```
tezos.client import secret key bootstrap1 unencrypted:edsk3gUfUPyBSfrS9CCgmCiQsTCHGkviBDusMxDJstFtojtc1zcpsh
tezos.client import secret key bootstrap2 unencrypted:edsk39qAm1fiMjgmPkw1EgQYkMzkJezLNewd7PLNHTkr6w9XA2zdfo
tezos.client import secret key bootstrap3 unencrypted:edsk4ArLQgBTLWG5FJmnGnT689VKoqhXwmDPBuGx3z4cvwU9MmrPZZ
tezos.client import secret key bootstrap4 unencrypted:edsk2uqQB9AY4FvioK2YMdfmyMrer5R8mGFyuaLLFfSRo8EoyNdht3
tezos.client import secret key bootstrap5 unencrypted:edsk4QLrcijEffxV31gGdN2HU7UpyJjA8drFoNcmnB28n89YjPNRFm
```
You can verify the connection between the client and the node by running:
tezos.client get balance for bootstrap1
If the client is acting funky, you may try to use the one if the teztool docker image.
alias tezos.client='teztool babylonnet sandbox client'
If you don't have LIGO, you can get it by running
```
# next (pre-release)
curl https://gitlab.com/ligolang/ligo/raw/dev/scripts/installer.sh \
| bash -s "next"
```
Then, we originate the contract. This can be
achieved by running `make originate` at the root
of the folder.
In the receipt, you should see the address of the
newly originated contract.
```
...
Originated contracts:
KT1M4guDEWUNXeJWKs45KVTodPkGfZno3E3T <------------------ HERE
Storage size: 57 bytes
Paid storage size diff: 57 bytes
Consumed gas: 11480
Balance updates:
tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx ... -ꜩ0.057
tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx ... -ꜩ0.257
New contract KT1M4guDEWUNXeJWKs45KVTodPkGfZno3E3T originated. <--- THERE
The operation has only been included 0 blocks ago.
We recommend to wait more.
Use command
tezos-client wait for ontcd6sAKcQd9GsgEegbWUVtdvAoxckZqWmgh21YdnaVFeuZWDS to be included --confirmations 30 --branch BKsixKod4BmB4C9zUJ3DBNJXRuwpT3jEVzbvKvKXNvTovqDiJF3
and/or an external block explorer.
Contract memorized as counter.
```
Using this address, you should now be able to
replace the value of the `contract_address`
variable in `index.js`.
The web application can be launched with yarn:
`yarn watch` should launch the application at
`localhost:1234`. Test that it works!
If you've never used Tezbridge, you may need to
configure it to add a new account.
Here are the secret keys for each of the bootstrap
accounts.
`edsk3gUfUPyBSfrS9CCgmCiQsTCHGkviBDusMxDJstFtojtc1zcpsh`
`edsk39qAm1fiMjgmPkw1EgQYkMzkJezLNewd7PLNHTkr6w9XA2zdfo`
`edsk4ArLQgBTLWG5FJmnGnT689VKoqhXwmDPBuGx3z4cvwU9MmrPZZ`
`edsk2uqQB9AY4FvioK2YMdfmyMrer5R8mGFyuaLLFfSRo8EoyNdht3`
`edsk4QLrcijEffxV31gGdN2HU7UpyJjA8drFoNcmnB28n89YjPNRFm`
## Level 1 - Sum
Let's now build a contract a bit richer. Instead
of merely incrementing, transform the contract to
increment its storage by its given parameter.
### Ligo
The contract should now take a natural number as
parameter.
#### Specification
`[0n] * main(123n) -> [123n], []`
`[0n] * main(x) -> [x], [] if x is nat`
`[0n] * main(-1) -> type error`
#### Hints
* Change the type of the parameter
* Do not just add `1n` in `main`
### Web
The page now gets a new element: an input. The
value contained in this input should be
transmitted to the contract when the button is
clicked.
Useful tidbits:
* `document.querySelector('#input').value`
allows to get the value of an input
* `methods.main(_parameter_).send()` to call a
contract with a given parameter
## Level 2 - Calc
Let's enrich our contract with the following
feature. The user should now be able to either
Increment or Decrement its storage according to its
parameter value.
## Ligo
In order to implement this behaviour, the contract should now offer two entrypoints: ``add`` and ``sub``.
In Ligo, we can be express this using _sum types_. For instance, the following code snippet uses a sum type to denote how to set a boolean value.
```
type parameter is
| SetTrue of unit
| SetFalse of unit
type storage is bool
function main(const p: parameter, const s: storage) ... is
block {
case p of
| SetTrue(x) -> s := true
| SetFalse(x) -> s := false
} with ((nil : list(operation)), s)
```
### Specification
We extend the specification to consider the two different entrypoints:
`[0] * add(123n) -> [123], []`
`[0] * sub(123n) -> [-123], []`
`[0] * add(x) -> [x], [] if x is nat`
`[0] * sub(x) -> [-x], [] if x is nat`
## Web
The change from a unique entrypoint to two is
reflected in the presence of two buttons, labeled
`Add` and `Sub`.
Useful tidbits:
* In order to call a specific entrypoint in Taquito, one can use the following syntax:
`contract.methods._entrypoint_(_value_).send()`.
* Remember that you can always check the names of the entrypoints in the Michelson .tz generated by the LIGO compiler.
### Specification
When the `Add` button is clicked, the value should
change according to the specification of the
contract. Same for the `Sub`.
# Registration
## Level 0 - Infinite registration
Now for a different kind of contract. The goal
here is to make a registration list for an event.
The user should be able to register and unregister
for an event.
### Ligo
The contract should have two entrypoints:
`Register` and `Unregister`. The contract should
not handle the search for an registered user. If we are interested in implement this bahviour, it should be one at the web application layer.
Useful tidbits:
* `sender` returns the address which called this
contract
* `set_add(_value_, _set_)` returns _set_ in
which _value_ was added.
* `set_remove(_value_, _set_)` returns _set_
from which _value_ was removed.
#### Specification
`sender:'tz1x...q' * [{}] * register() ->
[{'tz1x....q'}], []`
`sender:'tz1x...q' * [{'tz1x...q'}] * register()
-> [{'tz1x....q'}], []`
`sender:'tz1f...f' * [{'tz1x...q'}] * register()
-> [{'tz1x....q'; 'tz1f...f}], []`
`sender:'tz1x...q' * [{'tz1x...q'}] *
unregister()-> [{}], []`
`sender:'tz1x...q' * [{'tz1f...f'}] * unregister()
-> [{'tz1f...f}], []`
### Web
The interface should be composed of two buttons,
Register and Unregister, as well as a list of
registered users.
## Level 1 - Bounded Registration
Right now, our registration application can handle
an arbitrary large number of participants, that's
not very realistic. Most events have a limited
capacity. Thus, let's add a participants limit.
## Ligo
The storage is now made of two components: the set
of addresses registered, and the maximum limit of
registration. This kind of structure can be
represented using *sum types*. These are also
called _records_ or _dictionaries_.
In PascaLIGO, they are created using `record`. For example, the following record type stores Tezos networks, and the year they have been launched.
```
type storage is record
network: string;
launched_in: nat;
end
const mainnet_info: storage := record
network: "mainnet";
launched_in: 2018n;
end
```
Useful tidbits:
* `failwith(_msg_)` makes the call fail with _msg_
### Specification
`sender: 'a' * [{registered: {}; max: 1n}] *
register() -> [{registered: {'a'}; max: 1n}], []`
`sender: 'b' * [{registered: {'a'}; max: 1n}] * register() -> failure('No places left.')`
## Web
The page should now display the number of places
available.
# Tokens
## Level 0 - Transfer proxy
The goal of this contract is to send the funds to
the contract specified in the parameter
that were transferred to the contract.
### Ligo
The contract should have a unique entrypoint, and
not store anything.
Useful tidbits:
* `amount` returns the amount transferred in the call
* `transaction(_param_, _amount_, _contract_)`
returns a transfer operation
* `get_contract(_address_)` returns a contract
from an address
* `balance` returns the amount of tokens stored on
the contract
* Operations to be executed are returned as output
of the contract
#### Specification
`sender: 'a' * amount: 123 * main('b') -> ([], ['a'
-> 'b' [123mutez | Unit]])`
### Web
The interface should present the balance of two
accounts, as well as a button to transfer some
tokens from one account to the other.
Useful tidbits:
* Due to a small bug in taquito, in order to
transfer mutez, one should use the following
code:
```const params = methods.donate(null).toTransferParams();
return Tezos.contract.transfer({...params, amount, mutez:true})
```
## Level 1 - Donation
The aim of this contract is to do some
fundraising. Anyone can donate tokens to a
contract, from which one address can withdraw all
the funds.
### Ligo
The contract should have two entrypoints:
_Donate_, and _TakeAllTheMoney_. Anybody can call
_Donate_, as long as they transfer some amount of
tokens. Donors should be stored associated with
the total amount they donated.
Only when the _donatee_, whose address is stored in the
contract, calls that the contract transfers all
the funds.
Useful tidbits:
* `map(address, nat)` expresses a type linking an
address to a natural number.
* `m[0]` returns an option corresponding to
`Some(x)` if the binding is in the map `m`, or
`None` else.
* Option types can be pattern matched with `case
_ of` like any sum type.
#### Specification
`sender: 'a' * amount: 0 * [{donators: {}, ...}] *
donate() -> failure("No tokens transferred")`
`sender: 'a' * amount: 123 * [{donators: {}, ...x}] *
donate() -> [{donators: { 'a': 123 }, ...x}], []`
`sender: 'a' * amount: 123 * [{donators: {'a' : 123}, ...x}] *
donate() -> [{donators: { 'a': 246 }, ...x}], []`
`self.balance: 123 * sender: 'b' * [{donatee: 'b', ...x}] *
takeAllTheMoney() -> [{donatee:
'b', ...x}], [self -> 'b' [123 | Unit]]`
### Web
The page should be comprised of three things:
* Text indicating how much has been donated and
how much is available
* An input to specify how much one wants to donate
in mutez
* Two buttons: One for donating, the other for
taking all the tokens
## Level 2 - Time-bound donation
Some donations can only happen in a certain time
period. Thus, we'll introduce a time restriction,
after which donations should not able to happen.
### Ligo
The storage should be extended with a timestamp
representing the end of the donation period.
Useful tidbits:
* `timestamp` is the name of the type
representing timestamps.
* `now` returns the current time according to
the baker.
#### Specification
`now: '2020-03-03' * [{timeout: '2020-01-01',
...}] * donate() -> failure("Time out.")`
### Web
The page should now display a message about the
timeout. If timeout is attained, print "Donations
ended on ${timeout}", else print "Donations end on
${timeout}"
Useful tidbits:
* `new Date()` returns the date as of now.
* `new Date(timestamp)` creates a new date
object based on the timestamp.
## Level 3 - Crowdfunding
Let's extend this contract to get a full-blown
crowdfunding contract. A crowdfunding campaign
works in the following way: Anyone can donate any
non-zero number of tokens to support a project. At
the end of the time allotted, two things may
happen. If the objective is attained, the project
manager gets all the tokens donated. If it is not,
all donors can claim back what they gave.
### Ligo
The contract should now have three entrypoints:
* Pledge: Backers call that entrypoint to participate to the funding of the project
* ClaimBack : Backers call that entrypoint if
the funding period is over and the goal was
not met to get back their money.
* Fund: The project creator calls that
entrypoint if the funding period is over and
the goal was met
The storage also needs to be extended. The
contract must know about the goal as well as which
project is being funded.
### Web
The main page is comprised of both persistent
and modular information.
The persistent information is the same as the
preceding level: information about the goal and
how much tokens were given, the timeout
message, and which project is currently being funded.
Actions available however depend on the
application state. If the funding is ongoing then
should only appear an input and the button
_Pledge_. If the goal has been attained, only the
_Fund_ button is available. If the funding is over
and the goal has not been met, only the button
_Claim Back_ is available.
## Level 4 - Crowdfunding dashboard
Let's now do a page in which anybody can start
their own funding.
### Ligo
Nothing needs to change in the contract. The
application will also originate the contract
instead of merely using it.
### Web
Our application needs to be extended with a new
page. This page should take the shape of a form,
in which the user can fill the desired information
(hash of the project description, timeout,
goal...). When everything is filled and sent, the
application should originate the contract and
propose the user to go to their project page (what
was developed on level 3