Skip to content

Commit 59187fb

Browse files
First draft of files for updated TacoShop tutorial
1 parent cfb9f3e commit 59187fb

8 files changed

+919
-0
lines changed

ligo-tacoshop/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.tz
2+
.ligo

ligo-tacoshop/README.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# LIGO Taco Shop tutorial
2+
3+
These are the completed contract files for the LIGO Taco Shop tutorial here:
4+
5+
https://ligolang.org/docs/tutorials/taco-shop/selling-tacos

ligo-tacoshop/taco_shop_1.jsligo

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import Test = Test.Next;
2+
import Tezos = Tezos.Next;
3+
4+
namespace TacoShop {
5+
export type taco_supply = { current_stock: nat, max_price: tez };
6+
export type taco_data = map<nat, taco_supply>;
7+
export type admin_address = address;
8+
export type storage = {
9+
admin_address: admin_address,
10+
taco_data: taco_data,
11+
};
12+
13+
export const default_taco_data: taco_data = Map.literal([
14+
[1n, { current_stock: 50n, max_price: 50tez }],
15+
[2n, { current_stock: 20n, max_price: 75tez }]
16+
]);
17+
18+
// Internal function to get the price of a taco
19+
const get_taco_price_internal = (taco_kind_index: nat, taco_data: taco_data): tez => {
20+
const taco_kind: taco_supply =
21+
match (Map.find_opt(taco_kind_index, taco_data)) {
22+
when(Some(kind)): kind;
23+
when(None()): failwith("Unknown kind of taco")
24+
};
25+
return taco_kind.max_price / taco_kind.current_stock;
26+
}
27+
28+
@view
29+
const get_taco_price = (taco_kind_index: nat, storage: storage): tez =>
30+
get_taco_price_internal(taco_kind_index, storage.taco_data);
31+
32+
// Buy a taco
33+
@entry
34+
const buy_taco = (taco_kind_index: nat, storage: storage): [
35+
list<operation>,
36+
storage
37+
] => {
38+
39+
const { admin_address, taco_data } = storage;
40+
41+
// Retrieve the kind of taco from the contracts storage or fail
42+
const taco_kind: taco_supply =
43+
match (Map.find_opt(taco_kind_index, taco_data)) {
44+
when(Some(kind)): kind;
45+
when(None()): failwith("Unknown kind of taco");
46+
};
47+
48+
// Get the current price of this type of taco
49+
const current_purchase_price = get_taco_price_internal(taco_kind_index, taco_data);
50+
51+
// Verify that the caller sent the correct amount of tez
52+
if ((Tezos.get_amount()) != current_purchase_price) {
53+
return failwith("Sorry, the taco you are trying to purchase has a different price");
54+
}
55+
56+
// Verify that there is at least one of this type of taco
57+
if (taco_kind.current_stock == 0n) {
58+
return failwith("Sorry, we are out of this type of taco");
59+
}
60+
61+
// Update the storage with the new quantity of tacos
62+
const updated_taco_data: taco_data = Map.update(
63+
taco_kind_index,
64+
(Some (({...taco_kind, current_stock: abs (taco_kind.current_stock - 1n) }))),
65+
taco_data);
66+
67+
const updated_storage: storage = {
68+
admin_address: admin_address,
69+
taco_data: updated_taco_data,
70+
};
71+
72+
return [[], updated_storage];
73+
}
74+
75+
@entry
76+
const payout = (_u: unit, storage: storage): [
77+
list<operation>,
78+
storage
79+
] => {
80+
81+
// Entrypoint logic goes here
82+
83+
return [[], storage];
84+
}
85+
};

ligo-tacoshop/taco_shop_1.mligo

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
module Test = Test.Next
2+
module Tezos = Tezos.Next
3+
4+
module TacoShop = struct
5+
6+
type taco_supply = { current_stock: nat; max_price: tez }
7+
type taco_data = (nat, taco_supply) map
8+
type admin_address = address
9+
type storage = {
10+
admin_address: admin_address;
11+
taco_data: taco_data;
12+
}
13+
14+
let default_taco_data: taco_data = Map.literal [
15+
(1n, { current_stock = 50n; max_price = 50tez });
16+
(2n, { current_stock = 20n; max_price = 75tez });
17+
]
18+
19+
(* Internal function to get the price of a taco *)
20+
let get_taco_price_internal (taco_kind_index : nat) (taco_data : taco_data) : tez =
21+
let taco_kind : taco_supply =
22+
match Map.find_opt taco_kind_index taco_data with
23+
| Some kind -> kind
24+
| None -> failwith "Unknown kind of taco"
25+
in
26+
taco_kind.max_price / taco_kind.current_stock
27+
28+
[@view]
29+
let get_taco_price (taco_kind_index : nat) (storage : storage) : tez =
30+
get_taco_price_internal taco_kind_index storage.taco_data
31+
32+
(* Buy a taco *)
33+
[@entry]
34+
let buy_taco (taco_kind_index : nat) (storage : storage) : operation list * storage =
35+
36+
let { admin_address; taco_data } = storage in
37+
38+
(* Retrieve the kind of taco from the contracts storage or fail *)
39+
let taco_kind : taco_supply =
40+
match Map.find_opt taco_kind_index taco_data with
41+
| Some kind -> kind
42+
| None -> failwith "Unknown kind of taco" in
43+
44+
(* Get the current price of this type of taco *)
45+
let current_purchase_price = get_taco_price_internal taco_kind_index taco_data in
46+
47+
(* Verify that the caller sent the correct amount of tez *)
48+
let _ = if (Tezos.get_amount () <> current_purchase_price) then
49+
failwith "Sorry, the taco you are trying to purchase has a different price" in
50+
51+
(* Verify that there is at least one of this type of taco *)
52+
let _ = if (taco_kind.current_stock = 0n) then
53+
failwith "Sorry, we are out of this type of taco" in
54+
55+
56+
(* Update the storage with the new quantity of tacos *)
57+
let updated_taco_data : taco_data = Map.update
58+
taco_kind_index
59+
(Some { taco_kind with current_stock = abs (taco_kind.current_stock - 1n) })
60+
taco_data in
61+
62+
63+
let updated_storage : storage = {
64+
admin_address = admin_address;
65+
taco_data = updated_taco_data;
66+
} in
67+
68+
[], updated_storage
69+
70+
[@entry]
71+
let payout (_u : unit) (storage : storage) : operation list * storage =
72+
73+
(* Entrypoint logic goes here *)
74+
75+
[], storage
76+
77+
end

ligo-tacoshop/taco_shop_2.jsligo

+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import Test = Test.Next;
2+
import Tezos = Tezos.Next;
3+
4+
namespace TacoShop {
5+
export type taco_supply = { current_stock: nat, max_price: tez };
6+
export type taco_data = map<nat, taco_supply>;
7+
export type admin_address = address;
8+
export type storage = {
9+
admin_address: admin_address,
10+
taco_data: taco_data,
11+
};
12+
13+
export const default_taco_data: taco_data = Map.literal ([
14+
[1n, { current_stock: 50n, max_price: 50tez }],
15+
[2n, { current_stock: 20n, max_price: 75tez }]
16+
]);
17+
18+
// Internal function to get the price of a taco
19+
const get_taco_price_internal = (taco_kind_index: nat, taco_data: taco_data): tez => {
20+
const taco_kind: taco_supply =
21+
match (Map.find_opt(taco_kind_index, taco_data)) {
22+
when(Some(kind)): kind;
23+
when(None()): failwith("Unknown kind of taco")
24+
};
25+
return taco_kind.max_price / taco_kind.current_stock;
26+
}
27+
28+
@view
29+
const get_taco_price = (taco_kind_index: nat, storage: storage): tez =>
30+
get_taco_price_internal(taco_kind_index, storage.taco_data);
31+
32+
// Buy a taco
33+
@entry
34+
const buy_taco = (taco_kind_index: nat, storage: storage): [
35+
list<operation>,
36+
storage
37+
] => {
38+
39+
const { admin_address, taco_data } = storage;
40+
41+
// Retrieve the kind of taco from the contracts storage or fail
42+
const taco_kind: taco_supply =
43+
match (Map.find_opt(taco_kind_index, taco_data)) {
44+
when(Some(kind)): kind;
45+
when(None()): failwith("Unknown kind of taco");
46+
};
47+
48+
// Get the current price of this type of taco
49+
const current_purchase_price = get_taco_price_internal(taco_kind_index, taco_data);
50+
51+
// Verify that the caller sent the correct amount of tez
52+
if ((Tezos.get_amount()) != current_purchase_price) {
53+
return failwith("Sorry, the taco you are trying to purchase has a different price");
54+
}
55+
56+
// Verify that there is at least one of this type of taco
57+
if (taco_kind.current_stock == 0n) {
58+
return failwith("Sorry, we are out of this type of taco");
59+
}
60+
61+
// Update the storage with the new quantity of tacos
62+
const updated_taco_data: taco_data = Map.update(
63+
taco_kind_index,
64+
(Some (({...taco_kind, current_stock: abs (taco_kind.current_stock - 1n) }))),
65+
taco_data);
66+
67+
const updated_storage: storage = {
68+
admin_address: admin_address,
69+
taco_data: updated_taco_data,
70+
};
71+
72+
return [[], updated_storage];
73+
}
74+
75+
@entry
76+
const payout = (_u: unit, storage: storage): [
77+
list<operation>,
78+
storage
79+
] => {
80+
81+
// Entrypoint logic goes here
82+
83+
return [[], storage];
84+
}
85+
};
86+
87+
// Convenience function to get current taco price
88+
const get_taco_price = (untyped_address: address, taco_kind_index: nat): tez => {
89+
const view_result_option: option<tez> = Tezos.View.call("get_taco_price", taco_kind_index, untyped_address);
90+
return match(view_result_option) {
91+
when(Some(cost_mutez)): cost_mutez;
92+
when(None()): Test.failwith("Couldn't get the price of the taco.")
93+
};
94+
}
95+
96+
// Convenience function for testing equality in maps
97+
const eq_in_map = (r: TacoShop.taco_supply, m: TacoShop.taco_data, k: nat) =>
98+
match(Map.find_opt(k, m)) {
99+
when(None):
100+
false
101+
when(Some(v)):
102+
v.current_stock == r.current_stock && v.max_price == r.max_price
103+
};
104+
105+
const test = (() => {
106+
107+
// Set the initial storage and deploy the contract
108+
const admin_address: address = Test.Account.address(0n);
109+
const initial_storage: TacoShop.storage = {
110+
admin_address: admin_address,
111+
taco_data: TacoShop.default_taco_data,
112+
}
113+
const contract = Test.Originate.contract(contract_of(TacoShop), initial_storage, 0tez);
114+
115+
// Get the current price of a taco
116+
const untyped_address = Test.Typed_address.to_address(contract.taddr);
117+
const current_price = get_taco_price(untyped_address, 1n);
118+
119+
// Purchase a taco
120+
const success_result =
121+
Test.Contract.transfer(
122+
Test.Typed_address.get_entrypoint("buy_taco", contract.taddr),
123+
1n,
124+
current_price
125+
);
126+
127+
// Verify that the stock was updated
128+
match(success_result) {
129+
when(Success(_s)):
130+
do {
131+
const storage = Test.Typed_address.get_storage(contract.taddr);
132+
// Check that the stock has been updated correctly
133+
Assert.assert(
134+
eq_in_map(
135+
{ current_stock: 49n, max_price: 50000000mutez },
136+
storage.taco_data,
137+
1n
138+
));
139+
// Check that the amount of the other taco type has not changed
140+
Assert.assert(eq_in_map(
141+
{ current_stock: 20n, max_price: 75000000mutez },
142+
storage.taco_data,
143+
2n
144+
)
145+
);
146+
Test.IO.log("Successfully bought a taco");
147+
}
148+
when(Fail(err)): failwith(err);
149+
};
150+
151+
// Fail to purchase a taco without sending enough tez
152+
const fail_result =
153+
Test.Contract.transfer(
154+
Test.Typed_address.get_entrypoint("buy_taco", contract.taddr),
155+
1n,
156+
1mutez
157+
);
158+
match(fail_result) {
159+
when(Success(_s)): failwith("Test was able to buy a taco for the wrong price");
160+
when(Fail(_err)): Test.IO.log("Contract successfully blocked purchase with incorrect price");
161+
};
162+
}) ();

0 commit comments

Comments
 (0)