Soroban Contracts 101 : Custom Types
Tags: soroban, sorobanathonequinox, stellar, smartcontract
Published: 2023-03-17 11:35:52

Hi there! Welcome to my third post of my series called "Soroban Contracts 101", where I'll be explaining the basics of Soroban contracts, such as data storage, authentication, custom types, and more. All the code that we're gonna explain throughout this series will mostly come from [soroban-contracts-101](https://github.com/yuzurush/soroban-contracts-101) github repository.

In this third post of the series, I'll be covering soroban contract custom types. Custom types in smart contracts allow you to define your own complex types to use in your contract logic. Custom types allow you to raise the abstraction level of your contract code and can make it more readable, maintainable and robust.

##The Contract Code
```rust
#![no_std]
use soroban_sdk::{contractimpl, contracttype, symbol, Env, Symbol};

#[contracttype]
#[derive(Clone, Default, Debug, Eq, PartialEq)]
pub struct State {
    pub count: u32,
    pub last_incr: u32,
}

const STATE: Symbol = symbol!("STATE");

pub struct IncrementContract;

#[contractimpl]
impl IncrementContract {
    /// Increment increments an internal counter, and returns the value.
    pub fn increment(env: Env, incr: u32) -> u32 {
        // Get the current count.
        let mut state = Self::get_state(env.clone());
        // Increment the count.
        state.count += incr;
        state.last_incr = incr;
        // Save the count.
        env.storage().set(&STATE, &state);
        // Return the count to the caller.
        state.count
    }
    /// Return the current state.
    pub fn get_state(env: Env) -> State {
        env.storage()
            .get(&STATE)
            .unwrap_or_else(|| Ok(State::default())) // If no value set, assume 0.
            .unwrap() // Panic if the value of COUNTER is not a State.
    }
}

mod test;
```
This code is evolved version of the Storing Data Contract from the second post, it now :

- Stores a State struct in storage instead of a single count value
- `State` struct has `count` and `last_incr` fields
- The increment function takes an increment amount as an argument and increments by that amount
- It also stores the last increment amount in the State
- It returns the new total count

So this contract now stores more data (the last increment amount as well as the total count), and allows specifying the increment amount on each call. Other than that, it works similarly - storing data persistently in blockchain storage and returning updated values on each call.

##The Test Code

```rust
#[test]
fn test() {
    let env = Env::default();
    let contract_id = env.register_contract(None, IncrementContract);
    let client = IncrementContractClient::new(&env, &contract_id);

    assert_eq!(client.increment(), 1);
    assert_eq!(client.increment(), 2);
    assert_eq!(client.increment(), 3);
}
```
This code is a unit test for our contract. It does the following:

- Creates a default Env for testing
- Registers the IncrementContract
- Creates a client to call its methods
- Calls increment with increments of 1 and 10 and checks the results
- Calls get_state and checks that it returns the expected State (count 11, last_incr 10)

So this tests the key functionality of our contract - incrementing by specified amounts and retrieving the current State.

##Running Contract Tests
To ensure that the contract functions as intended, you can run the contract tests using the following command:
```
cargo test 
```
If the tests are successful, you should see an output similar to:
```
running 1 test
test test::test ... ok
```

##Building The Contract
To build the contract, use the following command:
```
cargo build --target wasm32-unknown-unknown --release
```
This should output a .wasm file in the ../target directory:
```
../target/wasm32-unknown-unknown/release/soroban_custom_types_contract.wasm
```

##Invoking The Contract
To invoke the `increment` function of the contract, use the following command with Soroban-CLI:
```
soroban contract invoke \
    --wasm ../target/wasm32-unknown-unknown/release/soroban_custom_types_contract.wasm \
    --id 1 \
    -- \
    increment \
    --incr 4
```
You should see the following output:
```
4
```

## Conclusion
In this third post of the series, we explained implementation custom types in soroban contracts. We hope this article has given you an idea of how Soroban custom types works. For more information about custom types, check here https://soroban.stellar.org/docs/learn/custom-types. Stay tuned for more post in this "Soroban Contracts 101" Series where we will dive deeper into Soroban Contracts and their functionalities.