CoinFlip: A Smart Contract game built with Soroban and React
Tags: soroban, react, smartcontract, stellar
Published: 2023-12-07 13:31:33

In this article, I will describe the steps to build a coin flip web app using Soroban and React.

## GitHub Repo
[](https://github.com/simusud/coin-flip)

## Prerequisites
The first step you have to do is install Rust. You can follow the steps to install Rust in the following article:

1. Node v18 - Install here: https://nodejs.org/en/download
2. Rust - How to install Rust: https://soroban.stellar.org/docs/getting-started/setup#install-rust

3. Soroban CLI - How to install Soroban CLI: https://soroban.stellar.org/docs/getting-started/setup#install-the-soroban-cli

4. Stellar Account with test tokens on TEstnet - How to create new wallet using soroban-cli & receive test tokens: https://soroban.stellar.org/docs/getting-started/setup#configure-an-identity

5. Freighter Wallet - Wallet extension for interact with the app. Link: https://www.freighter.app


## Clone, Build, and Deploy Smart contract
1. Clone the repository:
`git clone https://github.com/simusud/coin-flip.git`

2. Go to `contracts/coin_flip`

3. Build the contract:
`soroban contract build`

4. Deploy the contract to Testnet:
```
soroban contract deploy \
--wasm target/wasm32-unknown-unknown/release/coin_flip.wasm \
--source alice \
--network testnet
```

After the deployment is complete, you will receive a Contract Address. Save that address to be used in calling the contract functions.
The Contract Address will begin with C..... and looks like this `CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC`.

## Calling Smart Contract Function
1. Contract Initialization

```
pub fn initialize(env:Env, admin:Address) {
    if Self::has_administrator(env.clone()) {
        panic!("Contract already initialized.")
    }
    let key = DataKeys::Admin;
    env.storage().instance().set(&key, &admin)
}
```
This function is called to initialize the contract with admin address as param. Upon initialization it sets a admin address

After deployment, the first function we should call is initialization. Call initialize function through CLI.

```
soroban contract invoke \
--id your_contract_id \
--source alice \
--network testnet \
-- \
initialize \
--admin alice 
```
2. Set native coin address

```
pub fn set_native_coin(env:Env, address:Address) {
    let admin = Self::get_admin(env.clone());
    admin.require_auth();
    
    let key = DataKeys::NativeCoinAddress;
    env.storage().instance().set(&key, &address)
}
```
This function sets a native coin address. It can be called only by the admin.
The native coin (XLM) address in Soroban is : `CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC `

Set the XLM contact address through CLI.

```
soroban contract invoke \
 --id your_contract_id \
 --source alice \
 --network testnet \
 -- \
 set_native_coin \
 --address CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC 
```
3. Add XLM token to your contract
The contract needs to hold double the amount of the flip. So we need to transfer some XLM to the contract depending upon the maximum limit a player can bet.

```
shell
   soroban contract invoke \
    --id CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC \
    --source alice \
    --network testnet \
    -- \
    transfer \
    --from alice \
    --to your_contract_id \
    --amount 1000000000 
```

4. Call flip function
This is a public function and can be called by any user. 

It first checks the balance of the contract if it is sufficient to pay back the double of the flip amount, if not it reverts the transaction.

```
let contract_balance = Self::get_balance_of_contract(env.clone());
        if contract_balance < (amount * 2) {
            panic!("Not enough balance in contract. Plese flip for lesser amount")
        }
```
Then, it transfers the the amount of bet native coin to the contract itself from the user calling the transaction.

```
// transfer native coin to current contract from 'sender' address
        let native_coin_addr = Self::get_native_coin(env.clone());
        let flip_key = DataKeys::FlipCount;
        let flip_count: u32 = env.storage().instance().get::<DataKeys, u32>(&flip_key).unwrap_or(0);

        let native_coin_client = token::Client::new(&env, &native_coin_addr);
        native_coin_client.transfer(&sender, &env.current_contract_address(), &(amount));
```

Then, it generates the random flip choice by shuffling the list containing head and tail items.

```
let value = vec![
            &env, 
            String::from_slice(&env, "tail"),
            String::from_slice(&env, "head"),
             ];

    let random_side = value.shuffle();
    let result = random_side.first_unchecked();
```

It now checks the flip choice given by the sender and the choice randomly generated by the contact. If the sender's choice matches then contract sends double the bet amount to sender. And if sender's flip choice doesn't match the choice generated by contract it emits an event saying "You Lost".

```
if result == flip_choice {
            native_coin_client.transfer(&env.current_contract_address(), &sender, &(2 * amount));
            win_status = true;
            env.events().publish((sender.clone(), flip_choice.clone()), "You Won");
        } else {
            env.events().publish((sender, flip_choice.clone()), "You Lost");
        }
```
It also updates the flip counter to tract the number of flips made in the app.

```
// increase flip counter 
        env.storage().instance().set(&flip_key, &(flip_count + 1));
```

And there are some getter functions to get addresses of admin and native coin and native coin balance of the contract.


## Build, deploy & run the app frontend

1. Clone this repository:
`git clone https://github.com/simusud/coin-flip.git`

2. Go to `frontend/coin_flip`

3. Change the **contractAddress** value to the contract address that you deployed in `FlipOptions.tsx` file in line no. 52.

4. Run the app
   `npm run dev`

You will set the localhost where the app will run. Open the link.

5. Click on **Connect Wallet** Botton on the top right corner of the web app.Then enter the password.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q2pf0rldo1yeqberilud.png)

6. Enter amount to flip, select flip choice and click Toss button to flip the coin.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zs5o2peaojk92nxz4psl.png)
And then wallet will pop up to approve the transaction and  click **Approve**. 

Now if you win you will receive double the amount you tossed. 

## Conclusion 
Coin flip is a smart contract game on Soroban Testnet. This app is created for fun. I request all users to use it responsibly will the knowledge of risks of this in mainnet. Happy Tossing!