1import smartpy as sp
2
3
4@sp.module
5def main():
6 class Created(sp.Contract):
7 def __init__(self):
8 self.private.px = 10
9 self.private.py = 0
10 self.data.a = sp.int(0)
11 self.data.b = sp.nat(0)
12
13 @sp.entrypoint
14 def myEntryPoint(self, params):
15 self.data.a += params.x + self.private.px
16 self.data.b += params.y + self.private.py
17
18 class CreatedWithViews(sp.Contract):
19 def __init__(self):
20 self.data.a = sp.int(1)
21 self.data.b = sp.nat(2)
22
23 @sp.entrypoint
24 def myEntryPoint(self, params):
25 self.data.a += params.x
26 self.data.b += params.y
27
28 @sp.onchain_view()
29 def myView(self, a):
30 return self.data.a * a
31
32 @sp.offchain_view()
33 def myOffchainView(self, b):
34 return self.data.b * b
35
36 class CreatedWithParamInit(sp.Contract):
37 def __init__(self, ab):
38 self.private.px = 10
39 self.private.py = ab.a + ab.b
40 self.data.a = ab.a
41 self.data.b = ab.b
42
43 @sp.entrypoint
44 def myEntryPoint(self, params):
45 self.data.a += params.x + self.private.px
46 self.data.b += params.y + self.private.py
47
48 class CreatedWithParentClass(Created):
49 def __init__(self):
50 Created.__init__(self)
51 self.private.px1 = 100
52 self.private.py1 = 1
53 self.data.a1 = sp.int(10)
54 self.data.b1 = sp.nat(0)
55
56 @sp.entrypoint
57 def myEntryPoint2(self, params):
58 self.data.a1 += params.x + self.private.px1
59 self.data.b1 += params.y + self.private.py1
60
61 def opopop(x):
62 return sp.create_contract_operation(
63 Created,
64 None,
65 sp.mutez(0),
66 sp.record(a=x, b=15),
67 private_=sp.record(px=20, py=13),
68 )
69
70 class Creator(sp.Contract):
71 def __init__(self, baker):
72 self.private.baker = baker
73 self.data.x = None
74 self.data.l = opopop
75
76 @sp.entrypoint
77 def create1(self):
78 self.data.x = sp.Some(
79 sp.create_contract(
80 Created,
81 None,
82 sp.mutez(123),
83 sp.record(a=12, b=15),
84 private_=sp.record(px=20, py=13),
85 )
86 )
87
88 @sp.entrypoint
89 def create2(self):
90 _ = sp.create_contract(
91 Created,
92 None,
93 sp.tez(2),
94 sp.record(a=12, b=15),
95 private_=sp.record(px=20, py=13),
96 )
97 _ = sp.create_contract(
98 Created,
99 None,
100 sp.tez(2),
101 sp.record(a=12, b=16),
102 private_=sp.record(px=20, py=13),
103 )
104
105 @sp.entrypoint
106 def create3(self):
107 self.data.x = sp.Some(
108 sp.create_contract(
109 Created,
110 self.private.baker,
111 sp.tez(0),
112 sp.record(a=12, b=15),
113 private_=sp.record(px=20, py=13),
114 )
115 )
116
117 @sp.entrypoint
118 def create4(self, l):
119 for x in l:
120 _ = sp.create_contract(
121 Created,
122 None,
123 sp.mutez(0),
124 sp.record(a=x, b=15),
125 private_=sp.record(px=20, py=13),
126 )
127
128 @sp.entrypoint
129 def create5(self):
130 _ = sp.create_contract(
131 CreatedWithViews, self.private.baker, sp.mutez(10), sp.record(a=1, b=2)
132 )
133
134 @sp.entrypoint
135 def create6(self):
136 _ = sp.create_contract_operation(
137 CreatedWithViews, None, sp.mutez(0), sp.record(a=1, b=2)
138 )
139
140 @sp.entrypoint
141 def create_op(self):
142 record(operation, address).match = self.data.l(42)
143 sp.operations.push(operation)
144 self.data.x = sp.Some(address)
145
146 @sp.entrypoint
147 def create7(self):
148 _ = sp.create_contract(
149 CreatedWithParamInit,
150 None,
151 sp.mutez(0),
152 sp.record(a=1, b=15),
153 private_=sp.record(px=20, py=13),
154 )
155
156 @sp.entrypoint
157 def create8(self):
158 _ = sp.create_contract(
159 CreatedWithParentClass,
160 None,
161 sp.mutez(0),
162 sp.record(a=1, b=15, a1=10, b1=0),
163 private_=sp.record(px=20, py=13, px1=30, py1=130),
164 )
165
166
167@sp.add_test()
168def test():
169 scenario = sp.test_scenario("Create", main)
170 scenario.h1("Create Contract")
171 baker = sp.test_account("My baker")
172 scenario.h2("make the factory contract")
173 c = main.Creator(sp.Some(baker.public_key_hash)) # id = 0
174 c.set_initial_balance(sp.tez(10))
175 scenario += c
176 scenario.h2("make some dynamic contracts")
177 c.create1() # id = 1
178 c.create2() # id = 2, 3
179 c.create3() # id = 4
180 c.create4([1, 2]) # id = 5, 6
181 c.create6() # id = 7
182 c.create5() # id = 8
183
184 scenario.h2(
185 "make another static contract - should be skipped in dyn contract offset calc"
186 )
187 c2 = main.CreatedWithViews() # id = 9
188 c2.set_initial_balance(sp.tez(1))
189 scenario += c2
190
191 scenario.h2("references the contract that was dynamically created N times ago")
192 # ie c.create4([***1***, 2]) <-- the '1' one
193 dyn0 = scenario.dynamic_contract(main.Created, offset=-4)
194
195 scenario.h2("can call entrypoints via the handle")
196 dyn0.myEntryPoint(sp.record(x=1, y=16))
197 scenario.verify(dyn0.data.a == 22) # 1 + 1 + 20
198 scenario.verify(dyn0.data.b == 44) # 15 + 16 + 13
199 scenario.verify(dyn0.private.px == 20)
200 scenario.verify(dyn0.private.py == 13)
201 scenario.verify(dyn0.baker == None)
202 scenario.verify(dyn0.balance == sp.tez(0))
203
204 scenario.h2("call entrypoint again but change balance")
205 dyn0.myEntryPoint(sp.record(x=1, y=15), _amount=sp.tez(2))
206 scenario.verify(dyn0.data.a == 43) # 22 + 1 + 20
207 scenario.verify(dyn0.data.b == 72) # 44 + 15 + 13
208 scenario.verify(dyn0.private.px == 20)
209 scenario.verify(dyn0.private.py == 13)
210 scenario.verify(dyn0.baker == None)
211 scenario.verify(dyn0.balance == sp.tez(2))
212
213 scenario.h2("defaults to the last one created if offset is not given, ie create5()")
214 dyn1 = scenario.dynamic_contract(main.CreatedWithViews)
215 dyn1.myEntryPoint(sp.record(x=10, y=10))
216 scenario.verify(dyn1.data.a == 11) # 1 + 10
217 scenario.verify(dyn1.data.b == 12) # 2 + 10
218 scenario.h2("this one was created with a balance and baker")
219 scenario.verify(
220 dyn1.baker == sp.Some(sp.key_hash("tz1cWFxVfWdEXMk1HwXqXahXK6RQQw1rvZRP"))
221 )
222 scenario.verify(dyn1.balance == sp.mutez(10))
223 scenario.h2("this one did not have any private data")
224 scenario.verify(dyn1.private == sp.unit)
225
226 scenario.h2("can also call views")
227 scenario.verify(dyn1.myView(3) == 33) # 3 * 11
228 scenario.verify(dyn1.myOffchainView(4) == 48) # 4 * 12
229
230 scenario.h2("the static CreatedWithViews contract is a separate instance")
231 scenario.verify(c2.data.a == 1)
232 scenario.verify(c2.data.b == 2)
233 scenario.verify(c2.myView(3) == 3)
234 scenario.verify(c2.baker == None)
235 scenario.verify(c2.balance == sp.tez(1))
236
237 scenario.h2("can inject the CREATE_CONTRACT operation")
238 c.create_op()
239 scenario.show(c.data.x)
240 scenario.verify(
241 c.data.x
242 == sp.Some(sp.address("KT1Tezoo1ozzSmartPyzzSTATiCzzzw8CmuY")) # 10th one
243 )
244 scenario.h2("can refer to that contract too")
245 dyn2 = scenario.dynamic_contract(main.Created)
246 scenario.verify(dyn2.data.a == 42)
247 scenario.verify(dyn2.data.b == 15)
248 scenario.verify(dyn2.private.px == 20)
249 scenario.verify(dyn2.private.py == 13)
250 scenario.verify(dyn2.baker == None)
251 scenario.verify(dyn2.balance == sp.tez(0))
252
253 scenario.h2("references are type checked at runtime")
254 try:
255 _ = scenario.dynamic_contract(main.CreatedWithViews)
256 except sp.RuntimeException as e:
257 print(e)
258 else:
259 assert False
260
261 scenario.h2("can dynamically create contract with parameterised init")
262 c.create7()
263 dyn3 = scenario.dynamic_contract(main.CreatedWithParamInit)
264 scenario.verify(dyn3.data.a == 1)
265 scenario.verify(dyn3.data.b == 15)
266 scenario.verify(dyn3.private.px == 20)
267 scenario.verify(dyn3.private.py == 13)
268 scenario.verify(dyn3.baker == None)
269 scenario.verify(dyn3.balance == sp.tez(0))
270
271 scenario.h2("can dynamically create contract with parent class")
272 c.create8()
273 dyn4 = scenario.dynamic_contract(main.CreatedWithParentClass)
274 scenario.verify(dyn4.data.a == 1)
275 scenario.verify(dyn4.data.a1 == 10)
276 scenario.verify(dyn4.data.b == 15)
277 scenario.verify(dyn4.data.b1 == 0)
278 scenario.verify(dyn4.private.px == 20)
279 scenario.verify(dyn4.private.px1 == 30)
280 scenario.verify(dyn4.private.py == 13)
281 scenario.verify(dyn4.private.py1 == 130)
282 scenario.verify(dyn4.baker == None)
283 scenario.verify(dyn4.balance == sp.tez(0))
284
285
286# a module that errors correctly - private data cannot be non-const at compile time
287@sp.module
288def errors():
289 class A(sp.Contract):
290 def __init__(self):
291 self.private = None
292 self.data.a = sp.int(0)
293 self.data.b = sp.nat(0)
294
295 @sp.entrypoint
296 def myEntryPoint(self, params):
297 if self.private.is_some():
298 self.data.a += params.x
299 self.data.b += params.y
300
301 class CreateBad(sp.Contract):
302 @sp.entrypoint
303 def createA(self):
304 _ = sp.create_contract(
305 A, None, sp.mutez(123), sp.record(a=12, b=15), private_=sp.Some(sp.now)
306 )
307
308
309@sp.add_test()
310def testErrors():
311 try:
312 _ = sp.test_scenario("CreateBad", errors)
313 except sp.TypeError_ as e:
314 print(e)
315 else:
316 assert False
317
318
319# a module set that errors incorrectly
320@sp.module
321def parentChild():
322 class Parent(sp.Contract):
323 @sp.private()
324 def private_(self):
325 return False
326
327 @sp.entrypoint()
328 def ov(self):
329 _ = self.private_()
330
331 class C(Parent):
332 def __init__(self):
333 Parent.__init__(self)
334
335
336@sp.module
337def factory():
338 class Factory(sp.Contract):
339 def __init__(self):
340 self.data.created = None
341
342 @sp.entrypoint
343 def create(self):
344 self.data.created = sp.Some(
345 sp.create_contract(parentChild.C, None, sp.mutez(0), ())
346 )
347
348
349@sp.add_test()
350def testHierarchy():
351 sc = sp.test_scenario("Factory", [parentChild, factory])
352 f = factory.Factory()
353 sc += f