< Back to articles

Interacting with the Tezos Blockchain on iOS, Part 2: Tezos Contracts

This tutorial is a follow-up to the part 1 that explained how to work with TezosSwift. If you did not read it and are already familiar with it or just want to skip to the code generation, I think you should be able to follow this tutorial as well, just download the project from part one.

What’s this about? 🤔

Firstly, I want to make clear the purpose of the code generator (“codegen”). A smart contract is a powerful tool. Tezos allows for smart contracts, and its domain-specific smart contracting language, Michelson, is designed to more easily allow for formal verification and therefore more secure smart contracts than on other blockchains like Ethereum or EOSand a big selling point of Tezos that tried to learn from the mistakes of Ethereum and make them more secure. This generator aims to make it as simple as possible for Swift developers to interact with smart contracts. Let me show you how. 🤓

Contract Specification

Before generating code, we need to have a specification of the contract. We have decided to leverage the more-or-less user-friendly RPC response. So to cut it short these are the steps you need to carry out:

  1. Head to Xcode, and in the TezosContracts folder create a new empty file called eg contract_abi.json

  2. Paste this piece of JSON there: {"parameter":  , "storage": }

  3. Copy the address of the contract you want to generate code for.

  4. Do a GET request to obtain its code with: curl https://rpcalpha.tzbeta.net/chains/main/blocks/head/context/contracts/KT1DwASQY1uTEkzWUShbeQJfKpBdb2ugsE5k | tr -d '\n'

  5. Copy the data under the key “script: code: parameter: args” and “script: code: storage: args” and paste them to the keys in the JSON created in step 2 accordingly. (eg for parameter, it's the part {"prim":"set","args":[{"prim":"nat"}] for the contract address I have provided above)
    So if you followed everything correctly, the final specification should look like this:

     {"parameter":  {"prim":"set","args":[{"prim":"nat"}]}, "storage": {"prim":"set","args":[{"prim":"nat"}

Great, so now we have everything ready in order to generate some smart contract code!

Generating Smart Contract Code

Now if you followed this tutorial’s part 1 you should head to your Podfile, otherwise download the project from here. Now replace TezosSwift with TezosGen (but you can leave TezosSwift there, too), save it, and run pod install command. We are so close, just run this command and we are good to go:

Pods/TezosGen/TezosGen HelloContract TezosContracts/contract_abi.json -x TezosContracts.xcodeproj -o TezosContracts/GeneraredContracts

When the command asks you which target to use, it's quite simple, use the only one, that is option number one 😎
And voilá, if you got the message "Code generation: ✅", you have created your first Swift code for a Tezos smart contract 🚀

You should now see a group Generated Contracts and two files in it - SharedContract.swift and HelloContract.swift. The first one helps with invocating individual methods that are defined in the contract files (in our case it is HelloContract) and is the same for all generated contracts.

And to the fun part - actually using our generated code to call our contract!

Calling our Contract

We will again use our code from part 1. In the previous part, we have successfully sent some tez to another account and we will see that calling Tezos contracts works in a similar way. So head to our TezosViewController and we will call our contract from the viewDidLoad() function. Note that you will have to delete or comment the previous transaction function since it will conflict with the new one. Normally you can send our contract transaction after the first one is completed or send them as batched operations. And get ready, this is it:

tezosClient.helloContract(at: "KT1DwASQY1uTEkzWUShbeQJfKpBdb2ugsE5k").call(param1: [1, 2, 3]).send(from: wallet, amount: Tez.zero, completion: { result in  
           switch result {  
           case .failure(let error):  
               print("Call failed with error: \(error)")  
           case .success(let operationHash):  
               print("Call succeeded with \(operationHash)")  
           }  
       })

Let’s first run the app to see if it works. After running it, wait a little while and then check the Xcode console - if you see “Call succeeded with some_operation_hash”, it did indeed work! Now let me just quickly explain the structure of the call.

First, we access our contract code that is bound to our tezosClient and we specify the address of the contract. Then we run call(param1: [1, 2, 3]) that prepares our input for the final bit that actually makes the request. Note that you can also send zero tez to the contract. What’s great is that also automatically handles how much you should send as a fee!

Contract’s Storage

If you are following closely, you might ask yourself: “what about storage?” Well, wonder no more! Our code generator handles decoding the storage for you. So to get the contract’s storage, you can use this sample:

    tezosClient.helloContract(at: "KT1DwASQY1uTEkzWUShbeQJfKpBdb2ugsE5k").status { result in  
           switch result {  
           case .failure(let error):  
               print("Call failed with error: \(error)")  
           case .success(let status):  
               print("The contract's storage is: \(status.storage)")  
           }  
       }

As you can see, storage is inside of the contract’s status and the type is determined by the generator, too. Pretty cool, eh? 👏 From the contract’s status you can also obtain other data like current balance, its manager, if it’s spendable and more (use the power of Xcode suggestions or look through the contract's and `TezosClient's' code 💪).

Tezos Types

Now, our contract is written originally in Michelson and its parameter and storage type is a (set nat), so the conversion to Swift’s types was kind of straightforward to Array (note that Michelson’s set is an ordered list of unique values - to make sure that is the case, the generator mutes the data before sending it accordingly). But if you know something about Michelson, you might ask what about the pair type (Tuple might seem like an obvious choice, but Tuple does not, and cannot, conform to Decodable). To make our code more readable, and refrain from excessive nesting, we have decided to index the individual parameters of the Michelson’s pair one by one.

To make this more clear, let’s see an example:

parameter (pair (pair (pair string (list int)) (set nat)) bytes)

This Michelson parameter is then converted, so it is called like this:

func call(param1: String, param2: [Int], param3: [UInt], param4: Data)

All technicalities aside, the types are parsed from left to right.

For more details on Tezos types, go to developer documentation.

Now it’s your Turn! 🎊

Let’s see what you can do with the smart contracts! If you have any questions, I am happy to help - here, on [Twitter](https://twitter.com/marekfort](@marekfort) or on Github (either create an issue or better yet help us with a pull request!). You can download the complete project here.

Marek Fořt
Marek Fořt
iOS DeveloperMarek has a passion for opensource and developer tooling, he studies at Faculty of Information Technology at CTU and likes to ride a bike around the city.

Are you interested in working together? Let’s discuss it in person!

Get in touch >