Starting with SmartPy Part 2: Complex Constructions

Crafting Smart Contracts with Advanced Techniques

Teckhua Chiang
The Cryptonomic Aperiodical
3 min readAug 16, 2019

--

With the solid foundation of SmartPy fundamentals that we learned in Part 1, we can start creating advanced smart contracts that can store high-level data structures, perform run-time verification, and execute internal function calls.

Introduction To Maps

Maps are data structures that store key-value pairs. As a result, they enable smart contracts to create valuable associations between related information. In the NameRegistry contract below, we define initial storage to contain a map called addressToName constructed by sp.map().

The register() entry point allows a user to map their address, sp.sender, to a given name, params.name, with the expression self.data.<map>[<key>] = <value>. Before the assignment statement, we set the type of params.name to be a string. The expression sp.setType(<var>, <type>) is used to reduce ambiguity and enable SmartPy to inference variable types when compiling.

Putting It All Together

Ready for a real challenge? Let’s try creating an EventPlanner smart contract that utilizes more advanced storage fields, entry points, and internal functions. Our EventPlanner will allow its owner to keep track of multiple events and change information about each event.

Our first step is initializing storage with two fields, owner and nameToEvent. owner is the Tezos address of the EventPlanner's owner, who is the only one capable of interacting with their instance of the smart contract. nameToEvent is a map that associates a string event name with an sp.record data structure containing date and numGuests as fields. The owner is initialized to initialOwner, while nameToEvent is set as an empty map with sp.map().

We continue by defining three entry points that will give EventPlanner its functionality. changeDate() and changeNumGuests() enable the owner to modify the date and number of guests for a given event. The params of these two entry points contains an event name, name, and the new information to be added, either newDate as a string or newNumGuests as an int. changeOwner() is a simple transference entry point that checks if the sender is the owner, and then sets the owner to the given newOwner address.

Both changeDate() and changeNumGuests() use the sp.verify() method to check that sp.sender, the address of the sender, matches self.data.owner, the address of the contract’s owner. If the requirement isn’t fulfilled, the entry point will fail and all executed operations will be reversed. Then, they call self.checkEvent(params.name), which is an helper function that will create a new entry in nameToDate if the event doesn’t already exist.

Subsequently, they use an assignment statement of the form self.data.<map>[<key>].<field> = params.<newValue> to modify the values stored in the map called nameToEvent. In this example, key is the name of the mapped event, field is the record field to be modified, and newValue is the parameter field that contains the value to be changed.

Finally, we define our helper function checkEvent(). Since this isn’t an entry point, we can define all of its parameters explicitly instead of referencing them as fields of params. Employing the sp.if() statement to define the contract’s control flow, this function checks if the event name already exists within nameToEvent. Otherwise, it maps name to a new sp.record, created with the sp.record() constructor, that has date and numGuests as fields.

With this last piece, our EventPlanner smart contract is complete!

Feel free to experiment with the EventPlanner by adding more information to each event record or creating new entry points to interact with the contract. Continue to Part 3 to learn more about contract simulation.

--

--