Skip to content

Commit dfebaac

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

File tree

8 files changed

+907
-0
lines changed

8 files changed

+907
-0
lines changed

ligo-tacoshop/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.tz
2+
.ligo

ligo-tacoshop/README.md

Lines changed: 5 additions & 0 deletions
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

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

ligo-tacoshop/taco_shop_1.mligo

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

ligo-tacoshop/taco_shop_2.jsligo

Lines changed: 162 additions & 0 deletions
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)