In this article, I will describe the steps to build a coin flip web app using Soroban and React.
GitHub Repo
Prerequisites
The first step you have to do is install Rust. You can follow the steps to install Rust in the following article:
- Node v18 - Install here: https://nodejs.org/en/download
Rust - How to install Rust: https://soroban.stellar.org/docs/getting-started/setup#install-rust
Soroban CLI - How to install Soroban CLI: https://soroban.stellar.org/docs/getting-started/setup#install-the-soroban-cli
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
Freighter Wallet - Wallet extension for interact with the app. Link: https://www.freighter.app
Clone, Build, and Deploy Smart contract
Clone the repository:
git clone https://github.com/simusud/coin-flip.git
Go to
contracts/coin_flip
Build the contract:
soroban contract build
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
- 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
- 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
- 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
- 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
Clone this repository:
git clone https://github.com/simusud/coin-flip.git
Go to
frontend/coin_flip
Change the contractAddress value to the contract address that you deployed in
FlipOptions.tsx
file in line no. 52.Run the app
npm run dev
You will set the localhost where the app will run. Open the link.
- Click on Connect Wallet Botton on the top right corner of the web app.Then enter the password.
- Enter amount to flip, select flip choice and click Toss button to flip the coin.
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!
Top comments (0)