Cowchain Farm: a Dapp built with Soroban and Flutter
Tags: soroban, stellar, smartcontract, flutter
Published: 2023-08-27 14:34:10

In this article, I will show you the steps in creating **Cowchain Farm**, a smart contract app built using Soroban on Stellar blockchain and Flutter as the front-end framework.

You can find the source code for the Dapp on the GitHub link below:

- [Cowchain Farm Soroban](https://github.com/hasToDev/cowchain-farm-soroban)
- [Cowchain Farm Flutter](https://github.com/hasToDev/cowchain-farm-app)

You watch the Web Demo & Code Walkthrough video on the Youtube link below:

- [Web Demo](https://youtu.be/e1sfjSI8wGo)
- [Code Walkthrough](https://youtu.be/8JdVxJfvWFc)

You can also try the live application on [Cowchain Farm](https://cowchain.hasto.dev/).

Ensure that you already have a [Stellar FUTURENET account](https://laboratory.stellar.org/#account-creator?network=futurenet) and have the [Freighter](https://www.freighter.app/) extension installed and enabled in your browser.

Also, make sure the **Experimental Mode** is enabled in the Freighter extension settings.

---

## Install Rust and Soroban CLI

First, we have to install Rust. You can follow the steps to install Rust in the following article:

- [Install Rust - Rust Programming Language](https://www.rust-lang.org/tools/install)
- [Walkthrough: Installing Rust on Windows](https://www.alpharithms.com/installing-rust-on-windows-403718/)
- [How To Install Rust on Ubuntu 20.04](https://www.digitalocean.com/community/tutorials/install-rust-on-ubuntu-linux)

Next we install Soroban CLI:

```shell
cargo install --locked --version 20.0.0-rc1 soroban-cli
```

_OPTIONAL_
If you want to write the smart contract using Rust nightly channel, you can install it using:
```shell
rustup toolchain install nightly
```
Then use the nightly channel by default with:
```shell
rustup default nightly
```

---

## Setup New Rust Project

Create a new rust project, then open it with your favorite IDE:
```shell
cargo new cowchain-farm-soroban --lib
```

Before we start writing a smart contract, there are 2 things we must do first on the new rust project:

- Create a new file named `rust-toolchain.toml`, which contains:
```toml
[toolchain]
channel = "stable" # this value depends on your Rust channel 
targets = ["wasm32-unknown-unknown"]
components = ["rustc", "cargo", "rustfmt", "clippy", "rust-src"]
```

- Setup your `Cargo.toml` file with:
```toml
[package]
name = "cowchain_farm"
version = "0.1.0"
edition = "2021"
authors = <Your Info Here>

[lib]
crate-type = ["cdylib"]

[profile.release]
opt-level = "z"
overflow-checks = true
debug = 0
strip = "symbols"
debug-assertions = false
panic = "abort"
codegen-units = 1
lto = true

[dependencies]
soroban-sdk = "20.0.0-rc1"

[dev_dependencies]
soroban-sdk = { version = "20.0.0-rc1", features = ["testutils"] }
```
And now we are ready to start writing our smart contracts using Soroban.

---

## Contract Enums

There are 3 enums that we will use in our smart contract:
- _Status_
- _DataKey_
- _CowBreed_

These enums will be used for administrative purposes, result status of function invocation, and identification of the types of cow in the contract.

For that, let's create `enums.rs` file in the `src` directory and use the code contained in [this GitHub repository file](https://github.com/hasToDev/cowchain-farm-soroban/blob/master/src/enums.rs) as its contents.

---

## Contract Custom Types

In our smart contract, instead of using the rust Option type, we will use [Soroban custom types](https://soroban.stellar.org/docs/fundamentals-and-concepts/custom-types) as the result of a function invocation.

All function invocations, whether successful or unsuccessful, return a custom types.

Custom types are also used for cow data and cow feeding statistics.

There are 7 custom types that we will use in our smart contract:
- _CowData_
- _CowStatus_
- _BuyCowResult_
- _SellCowResult_
- _CowFeedingStats_
- _GetAllCowResult_
- _CowAppraisalResult_

Let's create `types.rs` file in the `src` directory and use the code contained in [this GitHub repository file](https://github.com/hasToDev/cowchain-farm-soroban/blob/master/src/types.rs) as its contents.

---

## Contract Constants

There are several types of constants that we use in this contract, including:

- _Amount of ledgers for a certain period_
- _Cow prices_
- _Reward or fine's multiplier_
- _Feeding time limits_

### <u>Constant of Ledger's amount for a certain period</u>

We can use the ledger as a time reference because ledgers have an average closing time of between 5 and 6 seconds.

Therefore, we use the ledger sequence to measure time in this smart contract.

For example, assuming the closing time of the ledger is 5 seconds, then within 24 hours, there will be an accumulation of 17280 ledgers.

Thus, we can use 17280 as the time limit, which is equal to 24 hours.

Following are the time constants we use in this smart contract:
```rust
pub const LEDGER_AMOUNT_IN_24_HOURS: u32 = 17280;
pub const LEDGER_AMOUNT_IN_3_DAYS: u32 = 51840;
pub const LEDGER_AMOUNT_IN_1_WEEK: u32 = 120960;
pub const LEDGER_AMOUNT_IN_1_MONTH: u32 = 483840;
```

### <u>Constant of Cow Prices</u>

This is the price constant for each Cow breed in the XLM unit.
The constants used are:
```rust
pub const JERSEY_PRICE: i128 = 1000;
pub const LIMOUSIN_PRICE: i128 = 1000;
pub const HALLIKAR_PRICE: i128 = 1000;
pub const HEREFORD_PRICE: i128 = 5000;
pub const HOLSTEIN_PRICE: i128 = 15000;
pub const SIMMENTAL_PRICE: i128 = 15000;
```

### <u>Constant for Reward or Fine's Multiplier</u>

In this smart contract, we will get rewards or fines every time we feed the cows.

The criteria for these rewards or fines are:

1. On-time feeding, a reward of 0.5% of the cow's purchase price
2. Late feeding, a reward of 0.25% of the cow's purchase price
3. Forget feeding, a fine of 1% of the cow's purchase price

However, since **Soroban doesn't enable floating-point arithmetic**, we must convert the reward or fine from the floating-point number to a fixed-point number format.

Our smallest number is 0.25. Therefore, we have to multiply all numbers by 100 so there are no more floating-point numbers.

Thus, we will get the rewards or fines constants as follows:
```rust
pub const ON_TIME_REWARD: i128 = 50; // 0.5%
pub const LATE_REWARD: i128 = 25; // 0.25%
pub const FORGET_FINE: i128 = 100; // 1%
pub const PRECISION_100_PERCENT: i128 = 10_000; // 100%
```

### <u>Constant of Feeding Time Limits</u>

In the paragraph above, we have mentioned several criteria, such as On-time, Late, and Forget feeding.

Here, we will discuss the time limit for each of these criteria. The time limit measurement starts from when the cow was first purchased or since the last time the cow was fed.

> Full stomach, ledger 0 - 4320
> On-time feeding, ledger 4320- 8640 
> Late feeding, ledger 8640  - 12960 
> Forget feeding, ledger 12960 - 12960 

_**note: 4320 ledger is equal to 6 hours**_

Based on the time limit range above, the constants used are:
```rust
pub const WELL_FED: u32 = 4320;
pub const ON_TIME_FEED: u32 = 8640;
pub const LATE_FEED: u32 = 12960;
```

Create `constants.rs` file in the `src` directory and use all the constants codes above as its contents.

You also can use the code contained in [this GitHub repository file](https://github.com/hasToDev/cowchain-farm-soroban/blob/master/src/constants.rs) as the content for `constants.rs` file.

---

## Contract Trait (Interface)

In writing Soroban smart contracts, you don't have to use traits, this is optional.

However, using traits will make it easier for us to read what functions are in our smart contract.

The functions that we will implement are the following:

1. _init_
2. _upgrade_
3. _bump_instance_
4. _health_check_
5. _buy_cow_
6. _sell_cow_
7. _feed_the_cow_
8. _get_all_cow_
9. _cow_appraisal_

Let's create `interface.rs` file in the `src` directory and use the code contained in [this GitHub repository file](https://github.com/hasToDev/cowchain-farm-soroban/blob/master/src/interface.rs) as its contents.

---

## Contract Implementation

In the `lib.rs` file, let's use the following code as the initial code before implementing the functions in traits.
```rust
#![no_std]

use soroban_sdk::{contract, contractimpl, token, Address, BytesN, Env, String, Symbol, Vec};

use crate::constants::*;
use crate::enums::*;
use crate::interface::*;
use crate::types::*;

mod constants;
mod enums;
mod interface;
mod types;

#[contract]
pub struct CowContract;

#[contractimpl]
impl CowContractTrait for CowContract {
  // implement CowContractTrait items in interface.rs file here
}
```

You can find the complete implementation of the smart contract function in the [GitHub repository](https://github.com/hasToDev/cowchain-farm-soroban/blob/master/src/lib.rs) `lib.rs` file.

I will explain in depth what happens in each smart contract function.

---

## _init_

```rust
fn init(env: Env, admin: Address, native_token: Address, message: String) -> Status;
```

This is the first function that we should call after deploying a contract.

The purpose of this function is to store Admin and Native Token addresses to be used in other functions and extend the contract's storage instance lifetime to the next 50 weeks.

This function requires a password to ensure the identity of the contract initiator, and this is so that no one other than us can initiate it.

The use of this password can be replaced by using a [deployer contract](https://soroban.stellar.org/docs/basic-tutorials/deployer). But for this time, I chose to use a password.

<u>Let's see what happened here:</u>

**Checking initialization password**
The function will check the message/password given by the invoker.

If the message does not match the one in the contract, the function will return the `TryAgain` status enum.

```rust
let internal_password = String::from_slice(&env, "password");
if message.ne(&internal_password) {
    return Status::TryAgain;
}
```

**Checking contract initialization**
Checks whether the contract is initialized.

If the contract is initialized, then the function returns the `AlreadyInitialized` status enum.

```rust
let is_admin_exist = env.storage().instance().has(&DataKey::Admin);
if is_admin_exist {
    return Status::AlreadyInitialized;
}
```

**Checking admin authorization**
Check whether the admin address provided has authorization.
It will throw an error if the admin address has no authorization.

```rust
admin.require_auth();
```

**Saving the admin address, native token address, and initialization ledger sequence to instance storage**
```rust
env.storage().instance().set(&DataKey::Admin, &admin);
env.storage()
    .instance()
    .set(&DataKey::NativeToken, &native_token);
env.storage()
    .instance()
    .set(&DataKey::InitializedLedger, &env.ledger().sequence());
```

**Bump instance storage lifetime to 50 weeks and return Ok**
```rust
env.storage().instance().bump(LEDGER_AMOUNT_IN_50_WEEKS);
Status::Ok
```

---

## _upgrade_

```rust
fn upgrade(env: Env, new_wasm_hash: BytesN<32>) -> Status;
```

So, each time we deploy a contract, we will get a different contract address.

In some cases, this has the potential to cause problems. Because every time we deploy a contract for a new version, we have to migrate user data from the old contract address to the new one.

This is where the upgrade function comes in handy.
We can deploy the latest contract version using the old contract address.

<u>Let's see what happened here:</u>

**Checking contract initialization**
Check the contract initialization by checking the Admin address key in the instance storage.

Returns the `NotInitialized` status enum if the Admin address key does not exist.

```rust
let is_admin_exist = env.storage().instance().has(&DataKey::Admin);
if !is_admin_exist {
    return Status::NotInitialized;
}
```

**Load the Admin address and check for its authorization**
```rust
let admin: Address = env.storage().instance().get(&DataKey::Admin).unwrap();
admin.require_auth();
```

**Updating the current contract using the supplied WASH Hash and return Upgraded**
```rust
env.deployer().update_current_contract_wasm(new_wasm_hash);
Status::Upgraded
```

---

## _bump_instance_

```rust
fn bump_instance(env: Env, ledger_amount: u32) -> Status;
```

Bump instance helps extend the storage instance lifetime on our contract.

<u>Let's see what happened here:</u>

**Checking contract initialization**
Check the contract initialization by checking the Admin address key in the instance storage.

Returns the `NotInitialized` status enum if the Admin address key does not exist.

```rust
let is_admin_exist = env.storage().instance().has(&DataKey::Admin);
if !is_admin_exist {
    return Status::NotInitialized;
}
```

**Load the Admin address and check for its authorization**
```rust
let admin: Address = env.storage().instance().get(&DataKey::Admin).unwrap();
admin.require_auth();
```

**Bump the instance storage using the supplied Ledger amount and return Bumped**
```rust
env.storage().instance().bump(ledger_amount);
Status::Bumped
```

---

## _health_check_

```rust
fn health_check(env: Env) -> CowStatus;
```

As the name implies, this function determines if our contract is still alive or has expired.

This function will return CowStatus custom types if the smart contract is alive.

```rust
CowStatus {
    status: Status::Ok,
    ledger: env.ledger().sequence(),
}
```

---

## _buy_cow_

```rust
fn buy_cow(
    env: Env,
    user: Address,
    cow_name: Symbol,
    cow_id: String,
    cow_breed: CowBreed,
) -> BuyCowResult;
```

The buy cow function requires authorization from the user account.

This is because the user will transfer XLM tokens from the user account to the contract account to purchase the cow.

Before making any transaction, this function will do all necessary checks.

Starting from checking whether the contract has been initiated,
Or does the user have enough balance to make a purchase?

If all checks are passed, the contract will start carrying out activities such as token transfer, storing user data in persistent storage, and storing cow data in temporary storage.

Finally, the function will return the BuyCowResult struct to the function invoker.

<u>Let's see what happened here:</u>

**Check for user authorization**
```rust
user.require_auth();
```

**Checking contract initialization**
Check the contract initialization by checking the Native token address key in the instance storage.

```rust
let is_native_token_exist = env.storage().instance().has(&DataKey::NativeToken);
if !is_native_token_exist {
    return BuyCowResult::default(env, Status::NotInitialized);
}
```

**Create native token Client**
```rust
let native_token: Address = env.storage().instance().get(&DataKey::NativeToken).unwrap();
let native_token_client = token::Client::new(&env, &native_token);
```

**Check user balance**
Check whether the user has enough balance to purchase the cow.

```rust
let user_native_token_balance: i128 = native_token_client.balance(&user);
let minimum_user_balance: i128 = 15_000_000;
let cow_price_in_stroops: i128 = get_cow_base_price_in_stroops(&cow_breed);
let user_balance_after_tx: i128 =
    user_native_token_balance - minimum_user_balance - cow_price_in_stroops.clone();
if user_balance_after_tx <= 0 {
    return BuyCowResult::default(env, Status::InsufficientFund);
}
```

**Transfer funds from the user to the supplier**
```rust
native_token_client.transfer(
    &user,
    &env.current_contract_address(),
    &cow_price_in_stroops,
);
```

**Creating a new cow data**
```rust
let new_cow_data = CowData {
    id: cow_id.clone(),
    name: cow_name,
    breed: cow_breed,
    born_ledger: env.ledger().sequence(),
    last_fed_ledger: env.ledger().sequence(),
    feeding_stats: CowFeedingStats::default(),
};
```

**Check ownership data**
Check whether the user already has cow ownership data.

The new cow data will be added to the existing ownership list if the owner already has a cow.

```rust
let mut cow_ownership_list: Vec<String> = Vec::new(&env);
let is_owner_exist = env.storage().persistent().has(&user);
if is_owner_exist {
    let ownership_data: Vec<String> = env.storage().persistent().get(&user).unwrap();
    cow_ownership_list.append(&ownership_data);
}
cow_ownership_list.push_back(cow_id.clone());
```

**Saving the ownership to persistent storage and the cow data to temporary storage**
```rust
env.storage().persistent().set(&user, &cow_ownership_list);
env.storage()
    .persistent()
    .bump(&user, LEDGER_AMOUNT_IN_1_WEEK);

env.storage().temporary().set(&cow_id, &new_cow_data);
env.storage()
    .temporary()
    .bump(&cow_id, LEDGER_AMOUNT_IN_24_HOURS);
```

**Return BuyCowResult custom types**
```rust
BuyCowResult {
    status: Status::Ok,
    cow_data: new_cow_data,
    ownership: cow_ownership_list,
}
```

---

## _sell_cow_

```rust
fn sell_cow(env: Env, user: Address, cow_id: String) -> SellCowResult;
```

Like the previous function, the sell cow function requires user account authorization.

This function also performs all necessary checks.

Especially checking whether the cow to be sold is still alive? and if the cow is old enough to be sold.

After all checks have been passed, this function will delete the cow ID from the list of user ownership in persistent storage. And also delete the cow data in the temporary storage.

Finally, the function will return a SellCowResult struct to the invoker.

<u>Let's see what happened here:</u>

**Check for user authorization**
```rust
user.require_auth();
```

**Checking contract initialization**
Check the contract initialization by checking the Native token address key in the instance storage.

```rust
let is_native_token_exist = env.storage().instance().has(&DataKey::NativeToken);
if !is_native_token_exist {
    return SellCowResult::default(env, Status::NotInitialized);
}
```

**Checking if cow still alive**
```rust
let is_cow_alive = env.storage().temporary().has(&cow_id);
if !is_cow_alive {
    return SellCowResult::default(env, Status::NotFound);
}
```

**Checking if user data still exist**
```rust
let is_ownership_exist = env.storage().persistent().has(&user);
if !is_ownership_exist {
    return SellCowResult::default(env, Status::MissingOwnership);
}
```

**Checking for Cow's age**
The cow to be sold must be at least 3 days old.

```rust
let cow_data: CowData = env.storage().temporary().get(&cow_id).unwrap();
let current_ledger: u32 = env.ledger().sequence();
let cow_age: u32 = current_ledger - cow_data.born_ledger;
if cow_age < LEDGER_AMOUNT_IN_3_DAYS {
    return SellCowResult::default(env, Status::Underage);
}
```

**Get cow selling price**
```rust
let cow_base_price: i128 = get_cow_base_price_in_stroops(&cow_data.breed);
let cow_selling_price = get_cow_appraisal_price(&cow_data, cow_base_price);
```

**Create native token Client**
```rust
let native_token: Address = env.storage().instance().get(&DataKey::NativeToken).unwrap();
let native_token_client = token::Client::new(&env, &native_token);
```

**Check current contract balance**
```rust
let contract_native_token_balance: i128 = native_token_client.balance(&env.current_contract_address());
if contract_native_token_balance < cow_selling_price {
    return SellCowResult::default(env, Status::InsufficientFund);
}
```

**Transfer funds from the supplier to the user**
```rust
native_token_client.transfer(&env.current_contract_address(), &user, &cow_selling_price);
```

**Update ownership to delete data on cows that have been sold**
```rust
let mut cow_ownership_list: Vec<String> = env.storage().persistent().get(&user).unwrap();
let index = cow_ownership_list.first_index_of(&cow_id).unwrap();
cow_ownership_list.remove_unchecked(index);
```

**Saving the updated ownership to persistent storage and remove the cow data from temporary storage**
```rust
env.storage().persistent().set(&user, &cow_ownership_list);
env.storage()
    .persistent()
    .bump(&user, LEDGER_AMOUNT_IN_1_WEEK);

env.storage().temporary().remove(&cow_id);
```

**Return SellCowResult custom types**
```rust
SellCowResult{
    status: Status::Ok,
    ownership: cow_ownership_list,
}
```

---

## _feed_the_cow_

```rust
fn feed_the_cow(env: Env, user: Address, cow_id: String) -> CowStatus;
```

Feed the Cow is a function to update statistical data on cow’s feeding activity and also to extend the life of cow data in temporary storage.

This statistical data will then be used to estimate the cow's selling price.

<u>Let's see what happened here:</u>

**Checking if cow still alive**
```rust
let is_cow_alive = env.storage().temporary().has(&cow_id);
if !is_cow_alive {
    return CowStatus::new(env, Status::NotFound);
}
```

**Checking if user data still exist**
```rust
let is_ownership_exist = env.storage().persistent().has(&user);
if !is_ownership_exist {
    return CowStatus::new(env, Status::MissingOwnership);
}
```

**Get current cow data from temporary storage**
```rust
let mut cow_data: CowData = env.storage().temporary().get(&cow_id).unwrap();
```

**Check feeding distance**
Check whether the distance between the last feeding time and the current one is larger than 4320 ledgers or 6 hours.

```rust
let current_ledger: u32 = env.ledger().sequence();
let last_fed_ledger: u32 = cow_data.last_fed_ledger;
let feed_distance: u32 = current_ledger - last_fed_ledger;
if feed_distance <= WELL_FED {
    return CowStatus::new(env, Status::FullStomach);
}
```

**Calculate feeding performance**
Find out whether the feeding time is on time, late, or forgotten.

```rust
let mut on_time = cow_data.feeding_stats.on_time;
let mut late = cow_data.feeding_stats.late;
let mut forget = cow_data.feeding_stats.forget;

if feed_distance > WELL_FED && feed_distance <= ON_TIME_FEED {
    on_time = on_time + 1;
}
if feed_distance > ON_TIME_FEED && feed_distance <= LATE_FEED {
    late = late + 1;
}
if feed_distance > LATE_FEED {
    forget = forget + 1;
}
```

**Update cow data with the new stats**
```rust
cow_data.last_fed_ledger = env.ledger().sequence();
cow_data.feeding_stats = CowFeedingStats {
    on_time,
    late,
    forget,
};
```

**Save the updated cow data to temporary storage and bump user persistent storage**
```rust
env.storage().temporary().set(&cow_id, &cow_data);
env.storage()
    .temporary()
    .bump(&cow_id, LEDGER_AMOUNT_IN_24_HOURS);

env.storage()
    .persistent()
    .bump(&user, LEDGER_AMOUNT_IN_1_WEEK);
```

**Return CowStatus custom types**
```rust
CowStatus {
    status: Status::Ok,
    ledger: cow_data.last_fed_ledger,
}
```

---

## _get_all_cow_

```rust
fn get_all_cow(env: Env, user: Address) -> GetAllCowResult;
```

Get all cow is a function to get all cow data owned by a user. This function is usually called when the user login to the website.

<u>Let's see what happened here:</u>

**Check for user authorization**
```rust
user.require_auth();
```

**Checking if user data still exist**
```rust
let is_ownership_exist = env.storage().persistent().has(&user);
if !is_ownership_exist {
    return GetAllCowResult {
        status: Status::Fail,
        data: Vec::new(&env),
    };
}
```

**Get user ownership data**
```rust
let ownership_data: Vec<String> = env.storage().persistent().get(&user).unwrap();
```

**Iterate the ownership data to get all user cow data**
```rust
let mut cow_data_list: Vec<CowData> = Vec::new(&env);
for cow_id in ownership_data {
    let is_cow_alive = env.storage().temporary().has(&cow_id);
    if !is_cow_alive {
        continue;
    }
    let cow_data: CowData = env.storage().temporary().get(&cow_id).unwrap();
    cow_data_list.push_back(cow_data);
}
```

**Return GetAllCowResult custom types**
```rust
GetAllCowResult {
    status: Status::Ok,
    data: cow_data_list,
}
```

---

## _cow_appraisal_

```rust
fn cow_appraisal(env: Env, cow_id: String) -> CowAppraisalResult;
```

When we do sales activities, before calling the sell cow function, the function that is called first is cow appraisal.

This is because we need the user's approval regarding the cow's selling price.

This function will estimate the price based on the feeding performance of the cows.

A cow that is constantly fed on time will increase its price.

While cows that are late or even forget to be fed, the price is more difficult to increase and even tends to decrease.

<u>Let's see what happened here:</u>

**Checking if cow still alive**
```rust
let is_cow_alive = env.storage().temporary().has(&cow_id);
if !is_cow_alive {
    return CowAppraisalResult::default(Status::NotFound);
}
```

**Get cow appraisal price**
```rust
let cow_data: CowData = env.storage().temporary().get(&cow_id).unwrap();
let cow_base_price: i128 = get_cow_base_price_in_stroops(&cow_data.breed);
let cow_price_appraisal = get_cow_appraisal_price(&cow_data, cow_base_price);
```

**Return CowAppraisalResult custom types**
```rust
CowAppraisalResult {
    status: Status::Ok,
    price: cow_price_appraisal,
}
```

---

## Contract Internal Function

There are two functions that are part of this smart contract but not exposed to the public.

The two functions are:
1. _get_cow_base_price_in_stroops_
2. _get_cow_appraisal_price_

### _get_cow_base_price_in_stroops_
```rust
fn get_cow_base_price_in_stroops(breed: &CowBreed) -> i128 {
  let cow_price_in_native_token = match breed {
      CowBreed::Jersey => JERSEY_PRICE,
      CowBreed::Limousin => LIMOUSIN_PRICE,
      CowBreed::Hallikar => HALLIKAR_PRICE,
      CowBreed::Hereford => HEREFORD_PRICE,
      CowBreed::Holstein => HOLSTEIN_PRICE,
      CowBreed::Simmental => SIMMENTAL_PRICE,
  };
  cow_price_in_native_token * 10_000_000
}
```

This function retrieves cow price data from a constant based on the cow's breed.

The price retrieved from the constant is in XLM units.
This function will then convert the price into units of stroops before returning the results to the invoker.

This conversion is necessary because tokens in Soroban use the smallest unit, namely stroops.

### _get_cow_appraisal_price_
```rust
fn get_cow_appraisal_price(cow_data: &CowData, cow_base_price: i128) -> i128 {
  let on_time_rewards: i128 = (cow_data.feeding_stats.on_time as i128) * ON_TIME_REWARD;
  let late_rewards: i128 = (cow_data.feeding_stats.late as i128) * LATE_REWARD;
  let forget_fines: i128 = (cow_data.feeding_stats.forget as i128) * FORGET_FINE;
  let mut rewards_fines_multiplier: i128 = on_time_rewards + late_rewards - forget_fines;

  if rewards_fines_multiplier < -PRECISION_100_PERCENT {
      rewards_fines_multiplier = -PRECISION_100_PERCENT;
  }

  let rewards_or_fines: i128 =
      (cow_base_price * rewards_fines_multiplier) / PRECISION_100_PERCENT;

  cow_base_price + rewards_or_fines
}
```

This function calculates the cow appraisal price using a fixed point number.

First, the function will calculate the multiplier factor for rewards or fines based on cow feeding stats.

After that, the function will ensure that the multiplier cannot be negative.

Finally, the multiplier factor will be multiplied by the base price of the cow, then the unit will be changed to stroops and added to the cow's base price so that the cow's appraisal price is obtained.

---

## Build and Deploy Smart Contract to Stellar FUTURENET

Build contract:
```shell
cargo build --target wasm32-unknown-unknown --release
```

Deploy contract to FUTURENET:
```shell
soroban contract deploy \
--wasm target/wasm32-unknown-unknown/release/cowchain-farm-soroban.wasm \
--rpc-url https://rpc-futurenet.stellar.org:443 \
--network-passphrase 'Test SDF Future Network ; October 2022'
```

After the deployment is complete, you will receive a Contract Address. Save that address to be used in calling the contract functions.

The form of Contract Address will be similar to `CB7UCV29SYKUFRZNEIMKVW5XKSJCGTMBCSJFN5OJ2SSXBTPRXO42XGT8`.

---

## Calling Smart Contracts using Soroban CLI

Example for calling `health_check`, a function <u>without</u> argument:
```shell
soroban contract invoke \
--id CB7UCV29SYKUFRZNEIMKVW5XKSJCGTMBCSJFN5OJ2SSXBTPRXO42XGT8 \
--rpc-url https://rpc-futurenet.stellar.org:443 \
--network-passphrase 'Test SDF Future Network ; October 2022' \
--fee 12345678 \
-- \
health_check
```

Example for calling `bump_instance`, a function <u>with</u> argument:
```shell
soroban contract invoke \
--id CB7UCV29SYKUFRZNEIMKVW5XKSJCGTMBCSJFN5OJ2SSXBTPRXO42XGT8 \
--rpc-url https://rpc-futurenet.stellar.org:443 \
--network-passphrase 'Test SDF Future Network ; October 2022' \
--fee 12345678 \
-- \
bump_instance \
--ledger_amount 1234
```

For more details, you can read the `README.md` file in the [Cowchain Farm Soroban GitHub repository](https://github.com/hasToDev/cowchain-farm-soroban/blob/master/README.md) to discover the prerequisites for calling smart contract functions, and how to call them using Soroban CLI.

---

## What's Next? 🚀

Now that we've unraveled the intricacies of this extensive Soroban smart contract code, you might be wondering, 'What's the next step?' 

Well, armed with this newfound knowledge, you're poised to take your blockchain journey to the next level.

Keep coding, and remember: each line of code you write is a step toward transforming ideas into reality.

Happy coding and Happy building with Soroban!