1import smartpy as sp
2
3
4@sp.module
5def main():
6 # Worker contract that our Main contract is going to call entrypoints on.
7 # Stores a single string ('message'), with entrypoints to allow the string
8 # to be overwritten or appended to.
9 class Worker(sp.Contract):
10 def __init__(self):
11 self.data.message = ""
12
13 @sp.entrypoint
14 def set_message(self, message):
15 self.data.message = message
16
17 @sp.entrypoint
18 def append_message(self, message, separator):
19 # Only use the separator if the message is currently empty ("")
20 if sp.len(self.data.message) == 0:
21 self.data.message = message
22 else:
23 self.data.message = sp.concat([self.data.message, separator, message])
24
25 # the expected args type for the set_message entrypoint
26 set_message_args_type: type = sp.string
27
28 # the expected args type for the append_message entrypoint
29 append_message_args_type: type = sp.record(message=sp.string, separator=sp.string)
30
31 # The main contract.
32 # Calls through to the Worker contract passed by address on init.
33 class Main(sp.Contract):
34 def __init__(self, worker_contract_address):
35 self.data.worker_contract_address = worker_contract_address
36
37 @sp.entrypoint
38 def store_single_message(self, message):
39 # Prepare the full entrypoint signature.
40 # Note that we have to call `.unwrap_some()` in order to open the sp.option[...],
41 # as the contract may not exist.
42 set_message_entrypoint = sp.contract(
43 set_message_args_type, self.data.worker_contract_address, "set_message"
44 ).unwrap_some()
45
46 # call the `set_message` entrypoint on the worker contract
47 sp.transfer(message, sp.tez(0), set_message_entrypoint)
48
49 @sp.entrypoint
50 def append_multiple_messages(self, params):
51 sp.cast(params, sp.record(messages=sp.list[sp.string], separator=sp.string))
52
53 # Prepare the full entrypoint signature.
54 # Note that, as above, we have to call `.unwrap_some()` in order to open the sp.option[...],
55 # as the contract may not exist.
56 append_message_entrypoint = sp.contract(
57 append_message_args_type,
58 self.data.worker_contract_address,
59 "append_message",
60 ).unwrap_some()
61
62 # call the `append_message` entrypoint on the worker contract once for
63 # each string in params.messages
64 for message in params.messages:
65 append_message_args = sp.record(
66 message=message, separator=params.separator
67 )
68 sp.transfer(append_message_args, sp.tez(0), append_message_entrypoint)
69
70
71@sp.add_test()
72def test():
73 scenario = sp.test_scenario("InterContractCallsExample", main)
74 scenario.h1("Inter-Contract Calls - example")
75
76 # originate the Worker contract and directly invoke it
77 scenario.h2("Worker")
78 worker_contract = main.Worker()
79 scenario += worker_contract
80 scenario.h3("Call the Worker contract entrypoints directly")
81 scenario.h4("set_message")
82 worker_contract.set_message("Directly set a message")
83 scenario.verify(worker_contract.data.message == "Directly set a message")
84 scenario.h4("append_message")
85 worker_contract.append_message(message="and append another", separator=", ")
86 scenario.verify(
87 worker_contract.data.message == "Directly set a message, and append another"
88 )
89
90 # originate the Main contract
91 scenario.h2("Main")
92 main_contract = main.Main(worker_contract.address)
93 scenario += main_contract
94 # the main contract calls through to the Worker contract methods
95 scenario.h3(
96 "Call the Main contract entrypoints, which in turn call through to the Worker contract entrypoints"
97 )
98 scenario.h4("store_single_message")
99 main_contract.store_single_message("Indirectly set a message")
100 scenario.verify(worker_contract.data.message == "Indirectly set a message")
101 scenario.h4("append_multiple_messages")
102 main_contract.append_multiple_messages(
103 messages=["and", "append", "some", "more"], separator=", "
104 )
105 scenario.verify(
106 worker_contract.data.message
107 == "Indirectly set a message, and, append, some, more"
108 )