From f4d688df7bfd4a344f8f938391243b38987692d5 Mon Sep 17 00:00:00 2001 From: Christian Rinderknecht Date: Fri, 21 Feb 2020 17:16:53 +0100 Subject: [PATCH] Fixed the tutorial. Enabled underscores in tez amounts. Fixed docs on Tez and tuples (zero-indexation). --- .../docs/language-basics/math-numbers-tez.md | 29 +- .../docs/language-basics/sets-lists-tuples.md | 14 +- .../get-started/tezos-taco-shop-payout.ligo | 61 +-- .../get-started/tezos-taco-shop-payout.md | 234 ++++++----- .../tezos-taco-shop-smart-contract.ligo | 44 +- .../tezos-taco-shop-smart-contract.md | 394 +++++++++++------- gitlab-pages/owner.pp.ligo | 0 gitlab-pages/timestamp.pp.ligo | 0 .../version-next-sidebars.json | 12 +- src/passes/1-parser/shared/Lexer.mll | 15 +- 10 files changed, 493 insertions(+), 310 deletions(-) delete mode 100644 gitlab-pages/owner.pp.ligo delete mode 100644 gitlab-pages/timestamp.pp.ligo diff --git a/gitlab-pages/docs/language-basics/math-numbers-tez.md b/gitlab-pages/docs/language-basics/math-numbers-tez.md index 41d05ed5d..7d8c2342a 100644 --- a/gitlab-pages/docs/language-basics/math-numbers-tez.md +++ b/gitlab-pages/docs/language-basics/math-numbers-tez.md @@ -3,7 +3,28 @@ id: math-numbers-tez title: Math, Numbers & Tez --- -LIGO offers three built-in numerical types: `int`, `nat` and `tez`. +LIGO offers three built-in numerical types: `int`, `nat` and +`tez`. Values of type `int` are integers; values of type `nat` are +natural numbers (integral numbers greater than or equal to zero); +values of type `tez` are units of measure of Tezos tokens. + + * Integer literals are the same found in mainstream programming + languages, for example, `10`, `-6` and `0`, but there is only one + canonical zero: `0` (so, for instance, `-0` and `00` are invalid). + + * Natural numbers are written as digits follwed by the suffix `n`, + like so: `12n`, `0n`, and the same restriction on zero as integers + applies: `0n` is the only way to specify the natural zero. + + * Tezos tokens can be specified using literals of three kinds: + * units of millionth of `tez`, using the suffix `mutez` after a + natural literal, like `10000mutez` or `0mutez`; + * units of `tez`, using the suffix `tz` or `tez`, like `3tz` or + `3tez`; + * decimal amounts of `tz` or `tez`, like `12.3tz` or `12.4tez`. + +Note that large integral values can be expressed using underscores to +separate groups of digits, like `1_000mutez` or `0.000_004tez`. ## Addition @@ -27,7 +48,7 @@ const a : int = 5 + 10 const b : int = 5n + 10 // tez + tez yields tez -const c : tez = 5mutez + 10mutez +const c : tez = 5mutez + 0.000_010tez //tez + int or tez + nat is invalid // const d : tez = 5mutez + 10n @@ -57,7 +78,7 @@ let a : int = 5 + 10 let b : int = 5n + 10 // tez + tez yields tez -let c : tez = 5mutez + 10mutez +let c : tez = 5mutez + 0.000_010tez // tez + int or tez + nat is invalid // let d : tez = 5mutez + 10n @@ -87,7 +108,7 @@ let a : int = 5 + 10; let b : int = 5n + 10; // tez + tez yields tez -let c : tez = 5mutez + 10mutez; +let c : tez = 5mutez + 0.000_010tez; // tez + int or tez + nat is invalid: // let d : tez = 5mutez + 10n; diff --git a/gitlab-pages/docs/language-basics/sets-lists-tuples.md b/gitlab-pages/docs/language-basics/sets-lists-tuples.md index a3db92b6d..6a38d6104 100644 --- a/gitlab-pages/docs/language-basics/sets-lists-tuples.md +++ b/gitlab-pages/docs/language-basics/sets-lists-tuples.md @@ -62,32 +62,28 @@ Accessing the components of a tuple in OCaml is achieved by [pattern matching](language-basics/unit-option-pattern-matching.md). LIGO currently supports tuple patterns only in the parameters of functions, not in pattern matching. However, we can access components by their -position in their tuple, which cannot be done in OCaml. +position in their tuple, which cannot be done in OCaml. *Tuple +components are zero-indexed*, that is, the first component has index +`0`. -Tuple components are one-indexed and accessed like so: - ```pascaligo group=tuple -const first_name : string = full_name.1 +const first_name : string = full_name.0 ``` -Tuple elements are zero-indexed and accessed like so: - ```cameligo group=tuple let first_name : string = full_name.0 ``` -Tuple components are one-indexed and accessed like so: - ```reasonligo group=tuple -let first_name : string = full_name[1]; +let first_name : string = full_name[0]; ``` diff --git a/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-payout.ligo b/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-payout.ligo index 5623eaf3f..9cf169397 100644 --- a/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-payout.ligo +++ b/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-payout.ligo @@ -1,37 +1,46 @@ -type taco_supply is record +type taco_supply is + record [ current_stock : nat; - max_price : tez; -end -type taco_shop_storage is map(nat, taco_supply); + max_price : tez + ] -const ownerAddress: address = "tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV"; -const donationAddress: address = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx"; +type taco_shop_storage is map (nat, taco_supply) -function buy_taco (const taco_kind_index: nat ; var taco_shop_storage : taco_shop_storage) : (list(operation) * taco_shop_storage) is - begin - // Retrieve the taco_kind from the contract's storage - const taco_kind : taco_supply = get_force(taco_kind_index, taco_shop_storage); - - const current_purchase_price : tez = taco_kind.max_price / taco_kind.current_stock; +type return is list (operation) * taco_shop_storage + +const ownerAddress : address = "tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" +const donationAddress : address = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" + +function buy_taco (const taco_kind_index : nat; var taco_shop_storage : taco_shop_storage) : return is + block { + // Retrieve the taco_kind from the contract's storage or fail + const taco_kind : taco_supply = + case taco_shop_storage[taco_kind_index] of + Some (kind) -> kind + | None -> (failwith ("Unknown kind of taco.") : taco_supply) + end; + + const current_purchase_price : tez = + taco_kind.max_price / taco_kind.current_stock; if amount =/= current_purchase_price then - // we won't sell tacos if the amount isn't correct - fail("Sorry, the taco you're trying to purchase has a different price"); - else - // Decrease the stock by 1n, because we've just sold one - taco_kind.current_stock := abs(taco_kind.current_stock - 1n); + // We won't sell tacos if the amount is not correct + failwith ("Sorry, the taco you are trying to purchase has a different price"); + else skip; + + // Decrease the stock by 1n, because we have just sold one + taco_kind.current_stock := abs (taco_kind.current_stock - 1n); // Update the storage with the refreshed taco_kind taco_shop_storage[taco_kind_index] := taco_kind; - const receiver: contract(unit) = get_contract(ownerAddress); - const donationReceiver: contract(unit) = get_contract(donationAddress); + const receiver : contract (unit) = get_contract (ownerAddress); + const donationReceiver : contract (unit) = get_contract (donationAddress); - const donationAmount: tez = amount / 10n; + const donationAmount : tez = amount / 10n; - const operations : list(operation) = list - transaction(unit, amount - donationAmount, receiver); - transaction(unit, donationAmount, donationReceiver); - end; - - end with (operations, taco_shop_storage) \ No newline at end of file + const operations : list (operation) = list [ + transaction (unit, amount - donationAmount, receiver); + transaction (unit, donationAmount, donationReceiver); + ] + } with (operations, taco_shop_storage) diff --git a/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-payout.md b/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-payout.md index ae9a1b8e3..2d02544d5 100644 --- a/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-payout.md +++ b/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-payout.md @@ -3,8 +3,14 @@ id: tezos-taco-shop-payout title: Paying out profits from the Taco Shop --- -In the [previous tutorial](tutorials/get-started/tezos-taco-shop-smart-contract.md) we've learned how to setup & interact with the LIGO CLI. Followed by implementation of a simple Taco Shop smart contract for our entepreneur Pedro. In this tutorial we'll make sure Pedro has access to tokens that people have spent at his shop when buying tacos. +In the +[previous tutorial](tutorials/get-started/tezos-taco-shop-smart-contract.md) +we have learnt how to setup & interact with the LIGO CLI. Followed an +implementation of a simple Taco Shop smart contract for our +entrepreneur Pedro. +In this tutorial we will make sure Pedro has access to tokens that +people have spent at his shop when buying tacos.
@@ -14,133 +20,173 @@ In the [previous tutorial](tutorials/get-started/tezos-taco-shop-smart-contract. -## Analyzing the current contract +## Analyzing the Current Contract ### **`taco-shop.ligo`** ```pascaligo group=a -type taco_supply is record - current_stock : nat; - max_price : tez; -end -type taco_shop_storage is map(nat, taco_supply); +type taco_supply is record [ + current_stock : nat; + max_price : tez +] -function buy_taco (const taco_kind_index: nat ; var taco_shop_storage : taco_shop_storage) : - (list(operation) * taco_shop_storage) is - begin - // Retrieve the taco_kind from the contract's storage - const taco_kind : taco_supply = get_force(taco_kind_index, taco_shop_storage); - - const current_purchase_price : tez = taco_kind.max_price / taco_kind.current_stock; +type taco_shop_storage is map (nat, taco_supply) + +type return is list (operation) * taco_shop_storage + +function buy_taco (const taco_kind_index : nat ; var taco_shop_storage : taco_shop_storage) : return is + block { + // Retrieve the taco_kind from the contract's storage or fail + const taco_kind : taco_supply = + case taco_shop_storage[taco_kind_index] of + Some (kind) -> kind + | None -> (failwith ("Unknown kind of taco.") : taco_supply) + end; + + const current_purchase_price : tez = + taco_kind.max_price / taco_kind.current_stock; if amount =/= current_purchase_price then - // we won't sell tacos if the amount isn't correct - failwith("Sorry, the taco you're trying to purchase has a different price"); - else - // Decrease the stock by 1n, because we've just sold one - taco_kind.current_stock := abs(taco_kind.current_stock - 1n); + // We won't sell tacos if the amount is not correct + failwith ("Sorry, the taco you are trying to purchase has a different price"); + else skip; + + // Decrease the stock by 1n, because we have just sold one + taco_kind.current_stock := abs (taco_kind.current_stock - 1n); // Update the storage with the refreshed taco_kind - taco_shop_storage[taco_kind_index] := taco_kind; - end with ((nil : list(operation)), taco_shop_storage) + taco_shop_storage[taco_kind_index] := taco_kind + } with ((nil : list (operation)), taco_shop_storage) ``` -### Purchase price formula -Pedro's Taco Shop contract currently enables customers to buy tacos, at a computed price based on a simple formula. +### Purchase Price Formula + +Pedro's Taco Shop contract currently enables customers to buy tacos, +at a price based on a simple formula. ```pascaligo skip -const current_purchase_price : tez = taco_kind.max_price / taco_kind.current_stock; +const current_purchase_price : tez = + taco_kind.max_price / taco_kind.current_stock ``` -### Replacing *spendable* smart contracts -However, due to the [recent protocol upgrade](http://tezos.gitlab.io/mainnet/protocols/004_Pt24m4xi.html) of the Tezos mainnet, Pedro can't access the tokens stored in his Shop's contract directly. This was previously possible via `spendable` smart contracts, which are no longer available in the new protocol. We will have to implement a solution to access tokens from the contract programatically. +### Replacing *spendable* Smart Contracts + +However, due to the +[recent protocol upgrade](http://tezos.gitlab.io/mainnet/protocols/004_Pt24m4xi.html) +of the Tezos `mainnet`, Pedro cannot access the tokens stored in his +shop's contract directly. This was previously possible via *spendable +smart contracts*, which are no longer available in the new +protocol. We will have to implement a solution to access tokens from +the contract programatically. --- -## Designing a payout scheme +## Designing a Payout Scheme -Pedro is a standalone bussines owner, and in our case, he doesn't have to split profits / earnings of the taco shop with anyone. So for the sake of simplicity, we'll payout all the earned XTZ directly to Pedro right after a succesful taco purchase. +Pedro is a standalone bussines owner, and in our case, he does not +have to split profits and earnings of the taco shop with anyone. So +for the sake of simplicity, we will payout all the earned XTZ directly +to Pedro right after a succesful purchase. -This means that after all the *purchase conditions* of our contract are met - e.g. correct amount is sent to the contract - we'll not only decrease the supply of the individual purchased *taco kind*, but we'll also transfer this amount in a *subsequent transaction* to Pedro's personal address. +This means that after all the *purchase conditions* of our contract +are met, e.g., the correct amount is sent to the contract, we will not +only decrease the supply of the individual purchased *taco kind*, but +we will also transfer this amount in a *subsequent transaction* to +Pedro's personal address. -## Forging a payout transaction +## Forging a Payout Transaction -### Defining the recipient -In order to send tokens, we will need a receiver address - which in our case will be Pedro's personal account. Additionally we'll wrap the given address as a *`contract(unit)`* - which represents either a contract with no parameters, or an implicit account. +### Defining the Recipient + +In order to send tokens, we will need a receiver address, which, in +our case, will be Pedro's personal account. Additionally we will wrap +the given address as a *`contract (unit)`*, which represents either a +contract with no parameters, or an implicit account. ```pascaligo group=ex1 const ownerAddress : address = ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address); -const receiver : contract(unit) = get_contract(ownerAddress); +const receiver : contract (unit) = get_contract (ownerAddress); ``` -> Would you like to learn more about addresses, contracts and operations in LIGO? Check out the [LIGO cheat sheet](api/cheat-sheet.md) +> Would you like to learn more about addresses, contracts and +> operations in LIGO? Check out the +> [LIGO cheat sheet](api/cheat-sheet.md) -### Adding the transaction to the list of output operations -Now we can transfer the `amount` received by `buy_taco` to Pedro's `ownerAddress`. We will do so by forging a `transaction(unit, amount, receiver)` within a list of operations returned at the end of our contract. +### Adding the Transaction to the List of Output Operations +Now we can transfer the amount received by `buy_taco` to Pedro's +`ownerAddress`. We will do so by forging a `transaction (unit, amount, +receiver)` within a list of operations returned at the end of our +contract. ```pascaligo group=ex1 -const payoutOperation : operation = transaction(unit, amount, receiver) ; -const operations : list(operation) = list - payoutOperation -end; +const payoutOperation : operation = transaction (unit, amount, receiver) ; +const operations : list (operation) = list [payoutOperation]; ``` --- -## Finalizing the contract +## Finalizing the Contract ### **`taco-shop.ligo`** ```pascaligo group=b -type taco_supply is record - current_stock : nat; - max_price : tez; -end -type taco_shop_storage is map(nat, taco_supply); +type taco_supply is record [ + current_stock : nat; + max_price : tez +] -const ownerAddress: address = ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address); +type taco_shop_storage is map (nat, taco_supply) -function buy_taco (const taco_kind_index: nat ; var taco_shop_storage : taco_shop_storage) : (list(operation) * taco_shop_storage) is - begin - // Retrieve the taco_kind from the contract's storage - const taco_kind : taco_supply = get_force(taco_kind_index, taco_shop_storage); - - const current_purchase_price : tez = taco_kind.max_price / taco_kind.current_stock; +type return is list (operation) * taco_shop_storage + +const ownerAddress : address = + ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address) + +function buy_taco (const taco_kind_index : nat ; var taco_shop_storage : taco_shop_storage) : return is + block { + // Retrieve the taco_kind from the contract's storage or fail + const taco_kind : taco_supply = + case taco_shop_storage[taco_kind_index] of + Some (kind) -> kind + | None -> (failwith ("Unknown kind of taco.") : taco_supply) + end; + + const current_purchase_price : tez = + taco_kind.max_price / taco_kind.current_stock; if amount =/= current_purchase_price then - // we won't sell tacos if the amount isn't correct - failwith("Sorry, the taco you're trying to purchase has a different price"); - else - // Decrease the stock by 1n, because we've just sold one - taco_kind.current_stock := abs(taco_kind.current_stock - 1n); + // We won't sell tacos if the amount is not correct + failwith ("Sorry, the taco you are trying to purchase has a different price"); + else skip; + + // Decrease the stock by 1n, because we have just sold one + taco_kind.current_stock := abs (taco_kind.current_stock - 1n); // Update the storage with the refreshed taco_kind taco_shop_storage[taco_kind_index] := taco_kind; - const receiver : contract(unit) = get_contract(ownerAddress); - const payoutOperation : operation = transaction(unit, amount, receiver); - const operations : list(operation) = list - payoutOperation - end; - - end with (operations, taco_shop_storage) + const receiver : contract(unit) = get_contract (ownerAddress); + const payoutOperation : operation = transaction (unit, amount, receiver); + const operations : list(operation) = list [payoutOperation] + } with ((nil : list (operation)), taco_shop_storage) ``` +### Dry-run the Contract -### Dry-run the contract - -To confirm that our contract is valid, we can dry run it. As a result we see a *new operation* in the list of returned operations to be executed subsequently. +To confirm that our contract is valid, we can dry-run it. As a result, +we see a *new operation* in the list of returned operations to be +executed subsequently. ```pascaligo skip -ligo dry-run taco-shop.ligo --syntax pascaligo --amount 1 buy_taco 1n "map - 1n -> record - current_stock = 50n; - max_price = 50000000mutez; - end; - 2n -> record - current_stock = 20n; - max_price = 75000000mutez; - end; -end" +ligo dry-run taco-shop.ligo --syntax pascaligo --amount 1 buy_taco 1n "map [ + 1n -> record [ + current_stock = 50n; + max_price = 50tez + ]; + 2n -> record [ + current_stock = 20n; + max_price = 75tez + ]; +]" ``` @@ -150,32 +196,34 @@ end"
-**Done! Our tokens are no longer locked in the contract, and instead they are sent to Pedro's personal account/wallet.** +**Done! Our tokens are no longer locked in the contract, and instead + they are sent to Pedro's personal account/wallet.** --- -## 👼 Bonus: donating part of the profits +## 👼 Bonus: Donating Part of the Profits -Because Pedro is a member of the (STA) Specialty Taco Association, he has decided to donate **10%** of the earnings to the STA. We'll just add a `donationAddress` to the contract, and compute a 10% donation sum from each taco purchase. +Because Pedro is a member of the Specialty Taco Association (STA), he +has decided to donate **10%** of the earnings to the STA. We will just +add a `donationAddress` to the contract, and compute a 10% donation +sum from each taco purchase. ```pascaligo group=bonus -const ownerAddress: address = ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address); -const donationAddress: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address); -``` +const ownerAddress : address = ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address); +const donationAddress : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address); -```pascaligo group=bonus -const receiver : contract(unit) = get_contract(ownerAddress); -const donationReceiver : contract(unit) = get_contract(donationAddress); +const receiver : contract (unit) = get_contract (ownerAddress); +const donationReceiver : contract(unit) = get_contract (donationAddress); -const donationAmount: tez = amount / 10n; +const donationAmount : tez = amount / 10n; -const operations : list(operation) = list +const operations : list (operation) = list [ // Pedro will get 90% of the amount - transaction(unit, amount - donationAmount, receiver); - transaction(unit, donationAmount, donationReceiver); -end; + transaction (unit, amount - donationAmount, receiver); + transaction (unit, donationAmount, donationReceiver) +]; ``` This will result into two operations being subsequently executed on the blockchain: - Donation transfer (10%) -- Pedro's profits (90%) \ No newline at end of file +- Pedro's profits (90%) diff --git a/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-smart-contract.ligo b/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-smart-contract.ligo index b51460bde..8871a9c87 100644 --- a/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-smart-contract.ligo +++ b/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-smart-contract.ligo @@ -1,23 +1,33 @@ -type taco_supply is record +type taco_supply is + record [ current_stock : nat; - max_price : tez; -end -type taco_shop_storage is map(nat, taco_supply); + max_price : tez + ] -function buy_taco (const taco_kind_index: nat ; var taco_shop_storage : taco_shop_storage) : (list(operation) * taco_shop_storage) is - begin - // Retrieve the taco_kind from the contract's storage - const taco_kind : taco_supply = get_force(taco_kind_index, taco_shop_storage); - - const current_purchase_price : tez = taco_kind.max_price / taco_kind.current_stock; +type taco_shop_storage is map (nat, taco_supply) + +type return is list (operation) * taco_shop_storage + +function buy_taco (const taco_kind_index : nat ; var taco_shop_storage : taco_shop_storage) : return is + block { + // Retrieve the taco_kind from the contract's storage or fail + const taco_kind : taco_supply = + case taco_shop_storage[taco_kind_index] of + Some (kind) -> kind + | None -> (failwith ("Unknown kind of taco.") : taco_supply) + end; + + const current_purchase_price : tez = + taco_kind.max_price / taco_kind.current_stock; if amount =/= current_purchase_price then - // we won't sell tacos if the amount isn't correct - fail("Sorry, the taco you're trying to purchase has a different price"); - else - // Decrease the stock by 1n, because we've just sold one - taco_kind.current_stock := abs(taco_kind.current_stock - 1n); + // We won't sell tacos if the amount is not correct + failwith ("Sorry, the taco you are trying to purchase has a different price"); + else skip; + + // Decrease the stock by 1n, because we have just sold one + taco_kind.current_stock := abs (taco_kind.current_stock - 1n); // Update the storage with the refreshed taco_kind - taco_shop_storage[taco_kind_index] := taco_kind; - end with ((nil : list(operation)), taco_shop_storage) \ No newline at end of file + taco_shop_storage[taco_kind_index] := taco_kind + } with ((nil : list (operation)), taco_shop_storage) diff --git a/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-smart-contract.md b/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-smart-contract.md index 9d2bf5ab2..8770974a2 100644 --- a/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-smart-contract.md +++ b/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-smart-contract.md @@ -1,13 +1,18 @@ --- id: tezos-taco-shop-smart-contract -title: Taco shop smart contract +title: The Taco Shop Smart Contract ---
-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, the **el clásico** and the **especial del chef**. +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'll implement a smart contract, that will manage supply, pricing & sales of his tacos to the consumers. +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.
@@ -18,91 +23,120 @@ To help Pedro open his dream taco shop, we'll implement a smart contract, that w ## Pricing -Pedro's tacos are a rare delicacy, so their **price goes up**, as the **stock for the day begins to deplete**. +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. +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 won't implement replenishing of the supply after it runs out. +> For the sake of simplicity, we will not implement the replenishing +> of the supply after it has run out. -### Daily offer +### Daily Offer |**kind** |id |**available_stock**| **max_price**| |---|---|---|---| -|el clásico | `1n` | `50n` | `50000000mutez` | -|especial del chef | `2n` | `20n` | `75000000mutez` | +|Clásico | `1n` | `50n` | `50tez` | +|Especial del Chef | `2n` | `20n` | `75tez` | -### Calculating the current purchase price +### Calculating the Current Purchase Price -Current purchase price is calculated with the following equation: +The current purchase price is calculated with the following formula: ```pascaligo skip current_purchase_price = max_price / available_stock ``` -#### El clásico +#### El Clásico |**available_stock**|**max_price**|**current_purchase_price**| |---|---|---| -| `50n` | `50000000mutez` | `1tz`| -| `20n` | `50000000mutez` | `2.5tz` | -| `5n` | `50000000mutez` | `10tz` | +| `50n` | `50tez` | `1tez`| +| `20n` | `50tez` | `2.5tez` | +| `5n` | `50tez` | `10tez` | #### Especial del chef |**available_stock**|**max_price**|**current_purchase_price**| |---|---|---| -| `20n` | `75000000mutez` | `3.75tz` | -| `10n` | `75000000mutez` | `7.5tz`| -| `5n` | `75000000mutez` | `15tz` | +| `20n` | `75tez` | `3.75tez` | +| `10n` | `75tez` | `7.5tez`| +| `5n` | `75tez` | `15tez` | --- ## Installing LIGO -In this tutorial, we'll use LIGO's dockerized version for the sake of simplicity. You can find the installation instructions [here](intro/installation.md#dockerized-installation-recommended). +In this tutorial, we will use LIGO's dockerized version, for the sake +of simplicity. You can find the installation instructions +[here](intro/installation.md#dockerized-installation-recommended). -The best way to install the dockerized LIGO is as a **global executable** through the installation script, as shown in the screenshot below: +The best way to install the dockerized LIGO is as a **global +executable** through the installation script, as shown in the +screenshot below:
Installing the next version of LIGO's CLI
-## Implementing our first entry point +## Implementing our first access function -> From now on we'll get a bit more technical. If you run into something we have not covered yet - please try checking out the [LIGO cheat sheet](api/cheat-sheet.md) for some extra tips & tricks. +> 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](api/cheat-sheet.md) for some extra tips & tricks. -To begin implementing our smart contract, we need an entry point. We'll call it `main` and it'll 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. +To begin implementing our smart contract, we need an *access +function*, that is the first function being executed. We will call it +`main` and 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. ### `taco-shop.ligo` ```pascaligo group=a -function main (const parameter: int; const contractStorage: int) : (list(operation) * int) is - block {skip} with ((nil : list(operation)), contractStorage + parameter) +function main (const parameter : int; const contractStorage : int) : +list (operation) * int is + ((nil : list (operation)), contractStorage + parameter) ``` -Let's break down the contract above to make sure we understand each bit of the LIGO syntax: +Let us break down the contract above to make sure we understand each +bit of the LIGO syntax: -- **`function main`** - definition of a function that serves as an entry point -- **`(const parameter : int; const contractStorage : int)`** - parameters passed to the function - - **`const parameter : int`** - parameter provided by a transaction that invokes our contract - - **`const contractStorage : int`** - definition of our storage (`int`) -- **`(list(operation) * int)`** - return type of our function, in our case a touple with a list of operations, and an int -- **`block {skip}`** - our function has no body, so we instruct LIGO to `skip` it -- **`with ((nil : list(operation)), contractStorage + parameter)`** - essentially a return statement - - **`(nil : list(operation))`** - a `nil` value annotated as a list of operations, because that's required by our return type specified above - - **`contractStorage + parameter`** - a new storage value for our contract, sum of previous storage and a transaction parameter -### Running LIGO for the first time +- **`function main`** - definition of the access function, which takes + a the parameter of the contract and the storage +- **`(const parameter : int; const contractStorage : int)`** - + parameters passed to the function: the first is called `parameter` + because 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 `nil` value 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've installed LIGO correctly, and that `taco-shop.ligo` is a valid contract, we'll dry-run it. +### Running LIGO for the First Time -> Dry-running is a simulated execution of the smart contract, based on a mock storage value and a parameter. +To test that we have installed LIGO correctly, and that +`taco-shop.ligo` is a valid contract, we will dry-run it. -Our contract has a storage of `int` and accepts a parameter that is also an `int`. +> 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 +also an `int`. The `dry-run` command requires a few parameters: - **contract** *(file path)* -- **entrypoint** *(name of the entrypoint function in the contract)* +- **entrypoint** *(name of the access function in the contract)* - **parameter** *(parameter to execute our contract with)* - **storage** *(starting storage before our contract's code is executed)* - -And outputs what's returned from our entrypoint - in our case a touple 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've used. +It outputs what is returned from our access 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. ```zsh # Contract: taco-shop.ligo @@ -124,66 +158,80 @@ ligo dry-run taco-shop.ligo --syntax pascaligo main 4 3 --- -## Designing Taco shop's contract storage +## Designing the Taco Shop's Contract Storage -We know that Pedro's Taco Shop serves two kinds of tacos, so we'll need to manage stock individually, per kind. Let's define a type, that will keep the `stock` & `max_price` per kind - in a record with two fields. Additionally, we'll want to combine our `taco_supply` type into a map, consisting of the entire offer of Pedro's shop. +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 `stock` & `max_price` per kind in a record with two +fields. Additionally, we will want to combine our `taco_supply` type +into a map, consisting of the entire offer of Pedro's shop. **Taco shop's storage** ```pascaligo group=b -type taco_supply is record - current_stock : nat; - max_price : tez; -end +type taco_supply is record [ + current_stock : nat; + max_price : tez +] -type taco_shop_storage is map(nat, taco_supply); +type taco_shop_storage is map (nat, taco_supply) ``` -Next step is to update the `main` entry point to include `taco_shop_storage` in its storage - while doing that let's set the `parameter` to `unit` as well to clear things up. +Next step is to update the `main` access function to include +`taco_shop_storage` in its storage. In the meanwhile, let us set the +`parameter` to `unit` as well to clear things up. **`taco-shop.ligo`** ```pascaligo group=b+ -type taco_supply is record - current_stock : nat; - max_price : tez; -end -type taco_shop_storage is map(nat, taco_supply); +type taco_supply is record [ + current_stock : nat; + max_price : tez +] -function main (const parameter: unit ; const taco_shop_storage : taco_shop_storage) : (list(operation) * taco_shop_storage) is - block {skip} with ((nil : list(operation)), taco_shop_storage) +type taco_shop_storage is map (nat, taco_supply) + +type return is list (operation) * taco_shop_storage + +function main (const parameter : unit; const taco_shop_storage : taco_shop_storage) : return is + ((nil : list (operation)), taco_shop_storage) ``` -### Populating our storage in a dry-run +### Populating our Storage in a dry-run -When dry-running a contract, it is crucial to provide a correct initial storage value - in our case the storage is type-checked as `taco_shop_storage`. Reflecting [Pedro's daily offer](tutorials/get-started/tezos-taco-shop-smart-contract.md#daily-offer), our storage's value will be defined as following: +When dry-running a contract, it is crucial to provide a correct +initial storage value. In our case the storage is type-checked as +`taco_shop_storage`. Reflecting +[Pedro's daily offer](tutorials/get-started/tezos-taco-shop-smart-contract.md#daily-offer), +our storage's value will be defined as follows: **Storage value** ```zsh -map - 1n -> record - current_stock = 50n; - max_price = 50000000mutez; - end; - 2n -> record - current_stock = 20n; - max_price = 75000000mutez; - end; -end +map [ + 1n -> record [ + current_stock = 50n; + max_price = 50tez + ]; + 2n -> record [ + current_stock = 20n; + max_price = 75tez + ] +] ``` -> Storage value is a map, with two items in it, both items are records identified by natural numbers `1n` & `2n`. +> The storage value is a map with two bindings (entries) distinguished +> by their keys `1n` and `2n`. **Dry run command with a multi-line storage value** ```zsh -ligo dry-run taco-shop.ligo --syntax pascaligo main unit "map - 1n -> record - current_stock = 50n; - max_price = 50000000mutez; - end; - 2n -> record - current_stock = 20n; - max_price = 75000000mutez; - end; -end" +ligo dry-run taco-shop.ligo --syntax pascaligo main unit "map [ + 1n -> record [ + current_stock = 50n; + max_price = 50tez + ]; + 2n -> record [ + current_stock = 20n; + max_price = 75tez + ] +]" ``` @@ -191,62 +239,85 @@ end"
-*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 products we've defined based on the daily offer of Pedro's taco shop.* +*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.* --- -## Providing an entrypoint for buying tacos +## Providing another Access Function for Buying Tacos -Now that we have our stock well defined in form of storage, we can move on to the actual sales. We'll replace the `main` entrypoint with `buy_taco`, that takes an `id` - effectively a key from our `taco_shop_storage` map. This will allow us to calculate pricing, and if the sale is successful - then we can reduce our stock - because we have sold a taco! +Now that we have our stock well defined in form of storage, we can +move on to the actual sales. We will replace the `main` access +function with another, `buy_taco`, that takes a key `id` from our +`taco_shop_storage` map. 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! -### Selling the tacos for free +### Selling the Tacos for Free -Let's start by customizing our contract a bit, we will: +Let is start by customizing our contract a bit, we will: -- rename the entrypoint from `main` to `buy_taco` +- rename the access function from `main` to `buy_taco` - rename `parameter` to `taco_kind_index` -- change `taco_shop_storage` to a `var` instead of a `const`, because we'll want to modify it +- change `taco_shop_storage` to a `var` instead of a `const`, because + we will want to modify it **`taco-shop.ligo`** ```pascaligo group=c -type taco_supply is record +type taco_supply is record [ current_stock : nat; - max_price : tez; -end -type taco_shop_storage is map(nat, taco_supply); + max_price : tez +] +type taco_shop_storage is map (nat, taco_supply) -function buy_taco (const taco_kind_index: nat ; var taco_shop_storage : taco_shop_storage) : (list(operation) * taco_shop_storage) is - block { skip } with ((nil : list(operation)), taco_shop_storage) +type return is list (operation) * taco_shop_storage + +function buy_taco (const taco_kind_index : nat; var taco_shop_storage : taco_shop_storage) : return is + ((nil : list (operation)), taco_shop_storage) ``` -#### Decreasing `current_stock` when a taco is sold +#### Decreasing `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: +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_kind` from our storage, based on the `taco_kind_index` provided -- subtract the `taco_kind.current_stock` by `1n` - - we can find the absolute (`nat`) value of the subtraction above by using `abs`, otherwise we'd be left with an `int` -- update the storage, and return it +- retrieve the `taco_kind` from our storage, based on the + `taco_kind_index` provided; +- subtract the `taco_kind.current_stock` by `1n`; +- we can find the absolute value of the subtraction above by + calling `abs` (otherwise we would be left with an `int`); +- update the storage, and return it. **`taco-shop.ligo`** ```pascaligo group=d -type taco_supply is record - current_stock : nat; - max_price : tez; -end -type taco_shop_storage is map(nat, taco_supply); +type taco_supply is record [ + current_stock : nat; + max_price : tez +] + +type taco_shop_storage is map (nat, taco_supply) + +type return is list (operation) * taco_shop_storage + +function buy_taco (const taco_kind_index : nat; var taco_shop_storage : taco_shop_storage) : return is + block { + // Retrieve the taco_kind from the contract's storage or fail + const taco_kind : taco_supply = + case taco_shop_storage[taco_kind_index] of + Some (kind) -> kind + | None -> (failwith ("Unknown kind of taco.") : taco_supply) + end; + + // Decrease the stock by 1n, because we have just sold one + taco_kind.current_stock := abs (taco_kind.current_stock - 1n); -function buy_taco (const taco_kind_index: nat ; var taco_shop_storage : taco_shop_storage) : (list(operation) * taco_shop_storage) is - begin - // Retrieve the taco_kind from the contract's storage - const taco_kind : taco_supply = get_force(taco_kind_index, taco_shop_storage); - // Decrease the stock by 1n, because we've just sold one - taco_kind.current_stock := abs(taco_kind.current_stock - 1n); // Update the storage with the refreshed taco_kind - taco_shop_storage[taco_kind_index] := taco_kind; - end with ((nil : list(operation)), taco_shop_storage) + taco_shop_storage[taco_kind_index] := taco_kind + } with ((nil : list (operation)), taco_shop_storage) ``` @@ -254,67 +325,85 @@ function buy_taco (const taco_kind_index: nat ; var taco_shop_storage : taco_sho
-### Making sure we get paid for our tacos +### Making Sure We Get Paid for Our Tacos -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 `amount`. +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 `amount`. To make sure we get paid, we will: -- calculate a `current_purchase_price` based on the [equation specified earlier](tutorials/get-started/tezos-taco-shop-smart-contract.md#calculating-the-current-purchase-price) -- check if the sent `amount` matches the `current_purchase_price` - - if not, then our contract will `fail` and stop executing - - if yes, stock for the given `taco_kind` will be decreased and the payment accepted +- calculate a `current_purchase_price` based on the + [equation specified earlier](tutorials/get-started/tezos-taco-shop-smart-contract.md#calculating-the-current-purchase-price) +- check if the sent `amount` matches the `current_purchase_price`: + - if not, then our contract will fail (`failwith`) + - otherwise, stock for the given `taco_kind` will be decreased and + the payment accepted **`taco-shop.ligo`** ```pascaligo group=e -type taco_supply is record - current_stock : nat; - max_price : tez; -end -type taco_shop_storage is map(nat, taco_supply); +type taco_supply is record [ + current_stock : nat; + max_price : tez +] -function buy_taco (const taco_kind_index: nat ; var taco_shop_storage : taco_shop_storage) : (list(operation) * taco_shop_storage) is - begin - // Retrieve the taco_kind from the contract's storage - const taco_kind : taco_supply = get_force(taco_kind_index, taco_shop_storage); - - const current_purchase_price : tez = taco_kind.max_price / taco_kind.current_stock; +type taco_shop_storage is map (nat, taco_supply) + +type return is list (operation) * taco_shop_storage + +function buy_taco (const taco_kind_index : nat ; var taco_shop_storage : taco_shop_storage) : return is + block { + // Retrieve the taco_kind from the contract's storage or fail + const taco_kind : taco_supply = + case taco_shop_storage[taco_kind_index] of + Some (kind) -> kind + | None -> (failwith ("Unknown kind of taco.") : taco_supply) + end; + + const current_purchase_price : tez = + taco_kind.max_price / taco_kind.current_stock; if amount =/= current_purchase_price then - // we won't sell tacos if the amount isn't correct - failwith("Sorry, the taco you're trying to purchase has a different price"); - else - // Decrease the stock by 1n, because we've just sold one - taco_kind.current_stock := abs(taco_kind.current_stock - 1n); + // We won't sell tacos if the amount is not correct + failwith ("Sorry, the taco you are trying to purchase has a different price"); + else skip; + + // Decrease the stock by 1n, because we have just sold one + taco_kind.current_stock := abs (taco_kind.current_stock - 1n); // Update the storage with the refreshed taco_kind - taco_shop_storage[taco_kind_index] := taco_kind; - end with ((nil : list(operation)), taco_shop_storage) + taco_shop_storage[taco_kind_index] := taco_kind + } with ((nil : list (operation)), taco_shop_storage) ``` -In order to test the `amount` sent, we'll use the `--amount` option of `dry-run`: +In order to test the `amount` sent, we will use the `--amount` option +of `dry-run`: ```zsh -ligo dry-run taco-shop.ligo --syntax pascaligo --amount 1 buy_taco 1n "map - 1n -> record - current_stock = 50n; - max_price = 50000000mutez; - end; - 2n -> record - current_stock = 20n; - max_price = 75000000mutez; - end; -end" +ligo dry-run taco-shop.ligo --syntax pascaligo --amount 1 buy_taco 1n "map [ + 1n -> record [ + current_stock = 50n; + max_price = 50tez + ]; + 2n -> record [ + current_stock = 20n; + max_price = 75tez + ] +]" ``` -**Purchasing a taco with 1.0tz** + +** Purchasing a Taco with 1tez **
Stock decreases after selling a taco, if the right amount of tezzies is provided

-**Attempting to purchase a taco with 0.7tz** +**Attempting to Purchase a Taco with 0.7tez** -
Stock does not decrease after a purchase attempt with a lower than required amount.
+
Stock does not decrease after a purchase attempt +with an insufficient payment.

@@ -322,9 +411,10 @@ end" --- -## 💰 Bonus: *Accepting tips above the taco purchase price* +## 💰 Bonus: *Accepting Tips above the Taco Purchase Price* -If you'd like to accept tips in your contract as well, simply change the following line, depending on your preference. +If you would like to accept tips in your contract, simply change the +following line, depending on your preference. **Without tips** ```pascaligo skip diff --git a/gitlab-pages/owner.pp.ligo b/gitlab-pages/owner.pp.ligo deleted file mode 100644 index e69de29bb..000000000 diff --git a/gitlab-pages/timestamp.pp.ligo b/gitlab-pages/timestamp.pp.ligo deleted file mode 100644 index e69de29bb..000000000 diff --git a/gitlab-pages/website/versioned_sidebars/version-next-sidebars.json b/gitlab-pages/website/versioned_sidebars/version-next-sidebars.json index 07fc287dd..5109f0260 100644 --- a/gitlab-pages/website/versioned_sidebars/version-next-sidebars.json +++ b/gitlab-pages/website/versioned_sidebars/version-next-sidebars.json @@ -24,9 +24,17 @@ "version-next-advanced/include", "version-next-advanced/first-contract" ], - "API": [ + "API & Reference": [ "version-next-api/cli-commands", - "version-next-api/cheat-sheet" + "version-next-api/cheat-sheet", + "version-next-reference/big-map-reference", + "version-next-reference/bytes-reference", + "version-next-reference/crypto-reference", + "version-next-reference/current-reference", + "version-next-reference/list-reference", + "version-next-reference/map-reference", + "version-next-reference/set-reference", + "version-next-reference/string-reference" ] }, "version-next-contributors-docs": { diff --git a/src/passes/1-parser/shared/Lexer.mll b/src/passes/1-parser/shared/Lexer.mll index 8b213f70b..569486ef7 100644 --- a/src/passes/1-parser/shared/Lexer.mll +++ b/src/passes/1-parser/shared/Lexer.mll @@ -499,7 +499,7 @@ module Make (Token: TOKEN) : (S with module Token = Token) = | Error Token.Non_canonical_zero -> fail region Non_canonical_zero - let mk_tz state buffer = + let mk_tez state buffer = let region, lexeme, state = sync state buffer in let lexeme = Str.string_before lexeme (String.index lexeme 't') in let lexeme = Z.mul (Z.of_int 1_000_000) (Z.of_string lexeme) in @@ -508,7 +508,7 @@ module Make (Token: TOKEN) : (S with module Token = Token) = | Error Token.Non_canonical_zero -> fail region Non_canonical_zero - let format_tz s = + let format_tez s = match String.index s '.' with index -> let len = String.length s in @@ -522,10 +522,11 @@ module Make (Token: TOKEN) : (S with module Token = Token) = if Z.equal Z.one should_be_1 then Some (Q.num mutez) else None | exception Not_found -> assert false - let mk_tz_decimal state buffer = + let mk_tez_decimal state buffer = let region, lexeme, state = sync state buffer in + let lexeme = Str.(global_replace (regexp "_") "" lexeme) in let lexeme = Str.string_before lexeme (String.index lexeme 't') in - match format_tz lexeme with + match format_tez lexeme with None -> assert false | Some tz -> match Token.mk_mutez (Z.to_string tz ^ "mutez") region with @@ -573,7 +574,7 @@ let nl = ['\n' '\r'] | "\r\n" let blank = ' ' | '\t' let digit = ['0'-'9'] let natural = digit | digit (digit | '_')* digit -let decimal = digit+ '.' digit+ +let decimal = natural '.' natural let small = ['a'-'z'] let capital = ['A'-'Z'] let letter = small | capital @@ -624,9 +625,9 @@ and scan state = parse | natural 'n' { mk_nat state lexbuf |> enqueue } | natural "mutez" { mk_mutez state lexbuf |> enqueue } | natural "tz" -| natural "tez" { mk_tz state lexbuf |> enqueue } +| natural "tez" { mk_tez state lexbuf |> enqueue } | decimal "tz" -| decimal "tez" { mk_tz_decimal state lexbuf |> enqueue } +| decimal "tez" { mk_tez_decimal state lexbuf |> enqueue } | natural { mk_int state lexbuf |> enqueue } | symbol { mk_sym state lexbuf |> enqueue } | eof { mk_eof state lexbuf |> enqueue }