Soroban Quest - Custom Types
Tags: soroban, stellar, tutorial
Published: 2022-12-01 06:00:00

After successfully completing the complicated Cross Contract quest on [last post](https://dev.to/altug/soroban-quest-cross-contract-kfg), we are ready for the Custom Types quest. Beware though, this quest is quite long and challenging, just what we like!

If you haven’t checked out the discussion on custom Soroban types, please take a look at the Using `get` to Retrieve Data section on the [Auth Store](https://dev.to/altug/soroban-quest-auth-store-1aea) tutorial.

## Understanding `README.md`

Let's once again check out the `README.md` file for this quest.

![README](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u17awbpz1m6vc1wiwti5.png)

## Setting Up Our Quest Account

Use the usual commands, `sq login` if required, and `sq play 5` to start your adventure.

## Examining the Contract

Let's start with checking out the `lib.rs` file.

```rust
use types::*;

pub struct TypesContract;

#[contractimpl]
impl TypesContract {
    pub fn c_rect(_env: Env, _rect: Rectangle) {}
    pub fn c_animal(_env: Env, _animal: Animal) {}
    pub fn c_user(_env: Env, _user: User) {}
    pub fn c_rgb(_env: Env, _rgb: RGB) {}
    pub fn c_color(_env: Env, _val: Color) {}
    pub fn c_part(_env: Env, _participant: Participant) {}
    pub fn c_card(_env: Env, _card: RoyalCard) {}
}
```

It has some empty functions! The arguments of the functions are different types, which are seemingly imported from the `types.rs` file, as there is the `use types::*;` import up top.

We won't add any functionality to this file, so let's check out the `types.rs` file.

## Filling Out `types.rs`

The `types.rs` file already has two examples: a `struct` and an `enum`.

```rust
#[contracttype]
pub struct Cylinder {
    pub radius: u32,
    pub height: u32,
}


#[contracttype]
pub enum Car {
    Chevrolet,
    Ford,
    Mercedes,
    Porsche,
    Honda,
    Toyota
}
```

> #### Using Custom Types
>
> The `struct`s are used for creating objects that have multiple different values. These values can consist of basic types like `u32` and `Bytes`, another `struct`s, `enum`s, `array`s, and so on. The `struct`s allow us to carry all properties of an object together on a single variable. Well, it is useful to pass a `Car` struct to a `insert_to_garage` function instead of `tires`, `engine`, `color`, `fuel_type`, `license_plate`, `total_kilometers`...
>
> The `enum`s are used for creating numeric types with names for a limited list. It is mostly useful for programmers, as trying to remember the ID of a student in a classroom might not be helpful, but you would know who `Bob` is. The compiler is not interested in those names, and will happily use their numeric values in your code.

We have a lot of empty `struct`s and `enum`s, waiting for us to implement them. Before starting out, please check out the [Custom Types](https://soroban.stellar.org/docs/learn/custom-types) section from the [Soroban Docs](https://soroban.stellar.org/docs) to learn more about the type system of Soroban.

### Implementing `Rectangle`

Let's start with implementing the `Rectangle` `struct`.

```rust
#[contracttype]
pub struct Rectangle {
    // TODO: create your fields here for your `Rectangle` type
}
```

From `README.md`, we can see that 

> The `Rectangle` type must be a `struct`, with two fields: `width` and `height` which both must be a `u32` value.

So, we can implement `Rectangle` as follows:

```rust
pub struct Rectangle {
    pub width: u32,
    pub height: u32,
}
```

### Implementing `Animal`

Next, let's check out the `Animal` `enum`. From `README.md`, we can see that

> The `Animal` type must be an `enum`, with at least two variations: `Cat` and `Dog`.

So, we can implement `Animal` as:

```rust
pub enum Animal {
    Dog,
    Cat,
    Bear,
    Wolf,
    Gorilla /* and many more */
}
```

### Implementing `User`

Let's move on to the `User` struct. Again, from `README.md`, we see that

> The `User` type must be a `struct` with `name`, `age`, and `pet` fields, corresponding to `Bytes`, `u32`, and `Animal` values, respectively.

Let's implement the `User` type. It consists of another `struct`, but that's nothing hard to implement.

```rust
pub struct User {
    pub name: Bytes,
    pub age: u32,
    pub pet: Animal,
}
```

### Implementing `RGB`

We are almost halfway done! Let's now implement the `RGB` `struct`. 

> The `RGB` type must be a tuple `struct` type made with a tuple of 3 `u32` values.

Since it should be defined using *unnamed* `u32` types, we can define it as a `tuple` using the following definition.

```rust
#[contracttype]
pub struct RGB(pub u32, pub u32, pub u32);
```

### Implementing `Color`

More than halfway done! Now, we must implement the `Color` `enum`, using the following definiton.

> The `Color` type will combine the `RGB` custom type nested within a tuple `enum` type. Construct your `RGB` struct type as described bove, Then, your `Color` enum type must be defined as a variant with a name of "RGB" and an instance of your `RGB` type.

An `enum`s type definitions can be of any type, including custom `struct`s.

```rust
pub enum Color {
    RGB(RGB)
}
```

### Implementing `Participant`

Almost done! We now need to implement the `Participant` `enum` type. The definition is

> The `Participant` type must be an `enum` with single-value tuple variants as follows:
>
> - An "Account" variant with an `AccountId` type
> - A "Contract" variant with a `BytesN<32>` type

So, `Participant` should be composed from two different Soroban types: `AccountId`, and `BytesN<32>`. Since we know how to implement custom types on `enum`s, let's go ahead and define the `Participant` `enum`.

```rust
pub enum Participant {
    Contract(BytesN<32>),
    Account(AccountId),
}
```

### Implementing `RoyalCard`

Only one left! We now just need to implement the `RoyalCard` `enum`.

> The `RoyalCard` type must be an `enum` containing three `u32` integer variations as follows:
>
> - A "Jack" variant, with a value of 11
> - A "Queen" variant, with a value of 12
> - A "King" variant, with a value of 13

It is quite simple to define an `enum` with given values, so we can implement it as follows.

```rust
#[repr(u32)]
pub enum RoyalCard {
    Jack = 11,
    Queen = 12,
    King = 13
}
```

The `#[repr(u32)]` attribute tells the compiler to pack each `enum` element into an 32 bit memory slot, which is the size of an `u32` variable.

We have accomplished quite much! But the quest isn't over, since we still have to invoke the "empty" functions successfully using `soroban` CLI. To learn more about how we can invoke those functions, let's check out the `test.rs` file.

## Examining the Test

The test has only one function, `test_types`, which tests the types of all of the custom `struct`s and `enum`s we have created by constructing the corresponding type, and calling the corresponding function. Let's check if our implementations of the custom types are correct using

```bash
cargo test -p soroban-custom-types-contract
```

![Custom Types Test](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ej74b7td8gyugx3pb5js.png)

Success!

## Solving the Quest

Now, we can try to solve the quest by creating the arguments to invoke each function on the implementation of our Soroban contract. 

### Deploying the Contract

Before we start invoking, let's deploy our contract using

```bash
cargo build --target wasm32-unknown-unknown --release
soroban deploy --wasm target/wasm32-unknown-unknown/release/soroban_custom_types_contract.wasm
```

![Contract Deployment](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ypg8t515gxpuras2sh3.png)

### Invoking `c_rect`

First, we have to call the `c_rect` function, which has the argument `_rect` of type `Rectangle`. If we check out the `README.md` file, we see that we can

> Invoke the `c_rect` function to create a `Rectangle` using something like:
>
> ```bash
> soroban invoke \
>     --id <contract-id> \
>     --fn c_rect \
>     --arg '{"object":{"map":[{"key":{"symbol":"width"},"val":{"u32":<a-u32-integer>}},{"key":{"symbol":"height"},"val":{"u32":<a-u32-integer>}}]}}'
> ```

We can see that we should change the values for the sections marked with `<a-u32-integer>` to an actual `u32` integer, and `<contract-id>` to our contract ID. You can choose any `height` or `width` for your `rectangle`. After you are happy with your choices, invoke the contract.

```bash
soroban invoke \
    --id <contract-id> \
    --fn c_rect \
    --arg '{"object":{"map":[{"key":{"symbol":"width"},"val":{"u32":<a-u32-integer>}},{"key":{"symbol":"height"},"val":{"u32":<a-u32-integer>}}]}}'
```

![Invoke c_rect](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vbsmh8575go4g2tjogyj.png)

### Invoking `c_animal`

After successfully invoking the `c_rect` function, we now will move on to the `c_animal` function. From `README.md`, we can

> Invoke the `c_animal` function to create an `Animal` using something like:
> 
> ```bash
> soroban invoke \
>     --id <contract-id> \
>     --fn c_animal \
>     --arg '{"object":{"vec":[{"symbol":"<a-relevant-animal-symbol>"}]}}'
> ```

Again, we should change the `<contract-id>` to our contract ID, and `<a-relevant-animal-symbol>` to an animal `enum` we have created. Since the `Animal` struct is a custom type we have created, we should create the corresponding object as a JSON.

![Invoke c_animal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w0j3jyr3skj9crfqjcfm.png)

### Invoking `c_user`

Now, it's `c_user` function's turn. Again, checking out `README.md`, we see that we could

> Invoke the `c_user` function to create a `User` using something like:
> 
> ```bash
> soroban invoke \
>     --id <contract-id> \
>     --fn c_user \
>     --arg '{"object":{"map":[{"key":{"symbol":"name"},"val":{"object":{"bytes":"<a-hex-encoded-string>"}}},{"key":{"symbol":"age"},"val":{"u32":<a-u32-integer>}},{"key":> {"symbol":"pet"},"val":<an-animal-object>}]}}'
> ```

That's quite the prompt! We should find the hexadecimal encoded `string` of a name, change the `<a-u32-integer>` to an age, and `<an-animal-object>` to the JSON representation of an animal object.

Remember that we could use the same `Dog` object that we used for the `c_animal` invocation, but feel free to use any animal you'd like.

![Invoke c_user](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z5d7bbmerumov9v4ncdo.png)

### Invoking `c_rgb`

That one was the hardest! Now, we can invoke the `c_rgb` function. Again, if we check out `README.md`, we will see that we can

> Invoke the `c_rgb` function to create a `RGB` value using something like:
> 
> ```bash
> soroban invoke \
>     --id <contract-id> \
>     --fn c_rgb \
>     --arg '{"object":{"vec":[{"u32":<a-u32-integer>},{"u32":<a-u32-integer>},{"u32":<a-u32-integer>}]}}'
> ```

We again have to change the `<a-u32-integer>` values to any integer values that we'd like.

![Invoke c_rgb](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xbnbeo52gq6v4qf88wkn.png)

### Invoking `c_color`

Keep going! Now, we can try invoking the `c_color` function. The `README.md` file states that we should

> Invoke the `c_color` function to create a `Color` using something like:
> 
> ```bash
> soroban invoke \
>     --id <contract-id> \
>     --fn c_color \
>     --arg '{"object":{"vec":[{"symbol":"RGB"},<a-rgb-object>]}}'
> ```

where `<a-rgb-object>` should be a JSON object representation of the `RGB` struct. Try to construct the object without looking below!

![Invoke c_color](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4udei8tozyh1jeek87c0.png)

### Invoking `c_part`

You are close! Now it's time to call the `c_part` function. Let's check out the `README.md` file once more.

> Invoke the `c_part` function to create an account `Participant` using something like:
> 
> ```bash
> soroban invoke \
>     --id <contract-id> \
>     --fn c_part \
>     --arg '{"object":{"vec":[{"symbol":"Account"},{"object":{"accountId":{"publicKeyTypeEd25519":"<hex-encoded-account-id>"}}}]}}'
> ```
> 
> Also invoke the `c_part` function to create a contract `Participant` using something like:
> 
> ```bash
> soroban invoke \
>     --id <contract-id> \
>     --fn c_part \
>     --arg '{"object":{"vec":[{"symbol":"Contract"},{"object":{"bytes":"<contract-id>"}}]}}'
> ```

Do you remember how to obtain your hexadecimal encoded public key? (Hint: using Python!). Try to find out what to put into `<hex-encoded-account-id>`. The `<contract-id>` parameter is pretty simple, as we have been using that one for all function invocations that we have done.

![Invoke c_part](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cspqfsyj676wr027m3en.png)

### Invoking `c_card`

Last one! You should be pretty tired at this point, but bear with me. Luckily, the last invocation is quite simple, as we only have to

> Invoke the `c_card` function using something like:
> 
> ```bash
> soroban invoke \
>     --id <contract-id> \
>     --fn c_foo \
>     --arg '{"u32":<a-u32-integer>}'
> ```

Remember that the `u32` argument should be one of 11, 12, or 13, as we only defined those values. And `c_foo` should be `c_card`, if that wasn't clear.

![Invoke c_card](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jn4ngf8ydlms13vg15fc.png)

### `verify`ing Our Implementation

That was a lot of invocations! Finally, we must invoke the `verify` function of the verification contract `40d12b03a08f5dde4e0068aa752fa65eddf905e82a18f522efe350e0cd268b8a` to make the verification contract verify the correctness of our deployed contract. Can you guess which argument we should supply? If you guessed the contract ID of the contract we have deployed, you are right! We can use

```bash
soroban invoke \
--id 40d12b03a08f5dde4e0068aa752fa65eddf905e82a18f522efe350e0cd268b8a \
--fn verify \
--arg [your-contract-id]
```

We should get a reply indicating that all of our custom implementations have succeeded, and none of them have failed.

![Invoke Verify](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f9czm7hvz5pcaww2p7y5.png)

Nicely done! You have definitely earned your reward! Use `sq check 5` to claim your reward 🎉!