Meet Pedro, our artisan taco chef, who has decided to open a Taco shop on the Tezos blockchain, using a smart contract. He sells two different kinds of tacos: el Clásico and the Especial del Chef.
To help Pedro open his dream taco shop, we will implement a smart contract that will manage supply, pricing & sales of his tacos to the consumers.
Pedro's tacos are a rare delicacy, so their price goes up as the stock for the day begins to deplete.
Each taco kind, has its own
max_price that it sells for, and a
finite supply for the current sales lifecycle.
For the sake of simplicity, we will not implement the replenishing of the supply after it has run out.
|Especial del Chef|
The current purchase price is calculated with the following formula:
In this tutorial, we will use LIGO's dockerized version, for the sake of simplicity. You can find the installation instructions here.
The best way to install the dockerized LIGO is as a global executable through the installation script, as shown in the screenshot below:
Implementing our First
From now on we will get a bit more technical. If you run into something we have not covered yet - please try checking out the LIGO cheat sheet for some extra tips & tricks.
To begin implementing our smart contract, we need a main function,
that is the first function being executed. We will call it
it will specify our contract's storage (
int) and input parameter
int). Of course this is not the final storage/parameter of our
contract, but it is something to get us started and test our LIGO
installation as well.
Let us break down the contract above to make sure we understand each bit of the LIGO syntax:
function main- definition of the main function, which takes the parameter of the contract and the storage
(const parameter : int; const contractStorage : int)- parameters passed to the function: the first is called
parameterbecause it denotes the parameter of a specific invocation of the contract, the second is the storage
(list (operation) * int)- return type of our function, in our case a tuple with a list of operations, and an
int(new value for the storage after a succesful run of the contract)
((nil : list (operation)), contractStorage + parameter)- essentially a return statement
(nil : list (operation))- a
nilvalue annotated as a list of operations, because that is required by our return type specified above
contractStorage + parameter- a new storage value for our contract, sum of previous storage and a transaction parameter
To test that we have installed LIGO correctly, and that
taco-shop.ligo is a valid contract, we will dry-run it.
Dry-running is a simulated execution of the smart contract, based on a mock storage value and a parameter.
Our contract has a storage of
int and accepts a parameter that is
dry-run command requires a few parameters:
- contract (file path)
- entrypoint (name of the main function in the contract)
- parameter (parameter to execute our contract with)
- storage (starting storage before our contract's code is executed)
It outputs what is returned from our main function: in our case a tuple containing an empty list (of operations to apply) and the new storage value, which, in our case, is the sum of the previous storage and the parameter we have used for the invocation.
3 + 4 = 7 yay! Our CLI & contract work as expected, we can move onto fulfilling Pedro's on-chain dream.
We know that Pedro's Taco Shop serves two kinds of tacos, so we will
need to manage stock individually, per kind. Let us define a type,
that will keep the
max_price per kind in a record with two
fields. Additionally, we will want to combine our
into a map, consisting of the entire offer of Pedro's shop.
Taco shop's storage
Next step is to update the
main function to include
taco_shop_storage in its storage. In the meanwhile, let us set the
unit as well to clear things up.
When dry-running a contract, it is crucial to provide a correct
initial storage value. In our case the storage is type-checked as
Pedro's daily offer,
our storage's value will be defined as follows:
The storage value is a map with two bindings (entries) distinguished by their keys
Dry run command with a multi-line storage value
If everything went as expected, the
dry-run command will return an
empty list of operations and the contract's current storage, which is
the map of the products we have defined based on the daily offer of
Pedro's taco shop.
Now that we have our stock well defined in form of storage, we can
move on to the actual sales. The
main function will take a key
taco_shop_storage map and will be renamed
more readability. This will allow us to calculate pricing, and if the
sale is successful, we will be able to reduce our stock because we
have sold a taco!
Let is start by customizing our contract a bit, we will:
varinstead of a
const, because we will want to modify it
current_stock when a Taco is Sold#
In order to decrease the stock in our contract's storage for a specific taco kind, a few things needs to happen:
- retrieve the
taco_kindfrom our storage, based on the
- subtract the
- we can find the absolute value of the subtraction above by
abs(otherwise we would be left with an
- update the storage, and return it.
In order to make Pedro's taco shop profitable, he needs to stop giving
away tacos for free. When a contract is invoked via a transaction, an
amount of tezzies to be sent can be specified as well. This amount is
accessible within LIGO as
To make sure we get paid, we will:
- calculate a
current_purchase_pricebased on the equation specified earlier
- check if the sent amount matches the
- if not, then our contract will fail (
- otherwise, stock for the given
taco_kindwill be decreased and the payment accepted
- if not, then our contract will fail (
In order to test the amount sent, we will use the
Purchasing a Taco with 1tez
Attempting to Purchase a Taco with 0.7tez
That's it - Pedro can now sell tacos on-chain, thanks to Tezos & LIGO.
If you would like to accept tips in your contract, simply change the following line, depending on your preference.