-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #17 from garikbesson/rust-version
Added rust version of contract, reorganized repo structure
- Loading branch information
Showing
22 changed files
with
303 additions
and
13,092 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,14 @@ | ||
node_modules | ||
build | ||
neardev | ||
yarn.lock | ||
.parcel-cache/ | ||
dist | ||
**/videos | ||
**/screenshots | ||
.idea | ||
# Rust | ||
**/target | ||
**/Cargo.lock | ||
|
||
# TypeScript | ||
**/package-lock.json | ||
**/node_modules/ | ||
**/build/ | ||
**/yarn.lock | ||
**/.tsimp | ||
|
||
# Frontend | ||
**/dist/ | ||
**/.parcel-cache |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
[package] | ||
name = "contract-rs" | ||
description = "cargo-near-new-project-description" | ||
version = "0.1.0" | ||
edition = "2021" | ||
# TODO: Fill out the repository field to help NEAR ecosystem tools to discover your project. | ||
# NEP-0330 is automatically implemented for all contracts built with https://github.com/near/cargo-near. | ||
# Link to the repository will be available via `contract_source_metadata` view-function. | ||
#repository = "https://github.com/xxx/xxx" | ||
|
||
[lib] | ||
crate-type = ["cdylib", "rlib"] | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
[dependencies] | ||
near-sdk = { version = "5.0.0", features = ["legacy"] } | ||
|
||
[dev-dependencies] | ||
near-sdk = { version = "5.0.0", features = ["unit-testing"] } | ||
near-workspaces = { version = "0.10.0", features = ["unstable"] } | ||
tokio = { version = "1.12.0", features = ["full"] } | ||
serde_json = "1" | ||
|
||
[profile.release] | ||
codegen-units = 1 | ||
# Tell `rustc` to optimize for small code size. | ||
opt-level = "z" | ||
lto = true | ||
debug = false | ||
panic = "abort" | ||
# Opt into extra safety checks on arithmetic operations https://stackoverflow.com/a/64136471/249801 | ||
overflow-checks = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
# Coin Flip 🪙 | ||
[![](https://img.shields.io/badge/⋈%20Examples-Basics-green)](https://docs.near.org/tutorials/welcome) | ||
[![](https://img.shields.io/badge/Gitpod-Ready-orange)](https://gitpod.io/#/https://github.com/near-examples/coin-flip-js) | ||
[![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fnear-examples%2Fcoin-flip-js%2Fbadge%3Fref%3Dmain&style=flat&label=Tests&ref=main)](https://actions-badge.atrox.dev/near-examples/coin-flip-js/goto?ref=main) | ||
|
||
Coin Flip is a game were the player tries to guess the outcome of a coin flip. It is one of the simplest contracts implementing random numbers. | ||
|
||
|
||
# What This Example Shows | ||
|
||
1. How to store and retrieve information in the NEAR network. | ||
2. How to integrate a smart contract in a web frontend. | ||
3. How to generate random numbers in a contract. | ||
|
||
<br /> | ||
|
||
# Quickstart | ||
|
||
1. Make sure you have installed [rust](https://rust.org/). | ||
2. Install the [`NEAR CLI`](https://github.com/near/near-cli#setup) | ||
|
||
<br /> | ||
|
||
## 1. Build, Test and Deploy | ||
To build the contract you can execute the `./build.sh` script, which will in turn run: | ||
|
||
```bash | ||
rustup target add wasm32-unknown-unknown | ||
cargo build --target wasm32-unknown-unknown --release | ||
``` | ||
|
||
Then, deploy your contract using following commands: | ||
|
||
```bash | ||
export CONTRACT_ID=test.near | ||
near deploy "'$CONTRACT_ID'" ./target/wasm32-unknown-unknown/release/contract.wasm | ||
``` | ||
|
||
<br /> | ||
|
||
## 2. Retrieve the user points | ||
|
||
`points_of` is a `view` method. | ||
|
||
`View` methods can be called for **free** by anyone, even people **without a NEAR account**! | ||
|
||
```bash | ||
# Use near-cli to get the greeting | ||
near view $CONTRACT_ID points_of '{"player": "'$CONTRACT_ID'"}' | ||
``` | ||
|
||
<br /> | ||
|
||
## 3. Flip a coin | ||
`flip_coin` changes the contract's state, for which it is a `call` method. | ||
|
||
`Call` methods can only be invoked using a NEAR account, since the account needs to pay GAS for the transaction. In this case, we are asking the account we created in step 1 to sign the transaction. | ||
|
||
```bash | ||
near call $CONTRACT_ID flip_coin '{"player_guess": "tails"}' --accountId $CONTRACT_ID | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
[toolchain] | ||
channel = "stable" | ||
components = ["rustfmt"] | ||
targets = ["wasm32-unknown-unknown"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// Find all our documentation at https://docs.near.org | ||
use near_sdk::borsh::{BorshSerialize, BorshDeserialize}; | ||
use near_sdk::env::{self, log_str}; | ||
use near_sdk::{near_bindgen, AccountId, BorshStorageKey}; | ||
use near_sdk::collections::UnorderedMap; | ||
|
||
#[derive(BorshSerialize, BorshStorageKey)] | ||
#[borsh(crate = "near_sdk::borsh")] | ||
enum StorageKey { | ||
Points, | ||
} | ||
|
||
pub(crate) fn simulate_coin_flip() -> String { | ||
// Here we get a first byte of a random seed | ||
let random_seed = *env::random_seed().get(0).unwrap() as i8; | ||
|
||
// If a first byte is EVEN we choose heads, otherwise tails | ||
if let 0 = random_seed % 2 { | ||
return "heads".to_string() | ||
} else { | ||
return "tails".to_string() | ||
}; | ||
} | ||
|
||
// Define the contract structure | ||
#[near_bindgen] | ||
#[derive(BorshDeserialize, BorshSerialize)] | ||
#[borsh(crate = "near_sdk::borsh")] | ||
pub struct Contract { | ||
points: UnorderedMap<AccountId, u8>, | ||
} | ||
|
||
// Define the default, which automatically initializes the contract | ||
impl Default for Contract { | ||
fn default() -> Self { | ||
Self { | ||
points: UnorderedMap::new(StorageKey::Points) | ||
} | ||
} | ||
} | ||
|
||
// Implement the contract structure | ||
#[near_bindgen] | ||
impl Contract { | ||
/* | ||
Flip a coin. Pass in the side (heads or tails) and a random number will be chosen | ||
indicating whether the flip was heads or tails. If you got it right, you get a point. | ||
*/ | ||
pub fn flip_coin(&mut self, player_guess: String) -> String { | ||
// Check who called the method | ||
let player: AccountId = env::predecessor_account_id(); | ||
log_str(&format!("{player} chose {player_guess}")); | ||
|
||
// Simulate a Coin Flip | ||
let outcome = simulate_coin_flip(); | ||
|
||
// Get the current player points | ||
let mut player_points = self.points.get(&player).unwrap_or(0); | ||
|
||
// Check if their guess was right and modify the points accordingly | ||
if outcome.eq(&player_guess) { | ||
player_points = player_points + 1; | ||
} else { | ||
player_points = player_points.saturating_sub(1); | ||
}; | ||
|
||
log_str(&format!("player_points: {player_points}")); | ||
|
||
// Store the new points | ||
self.points.insert(&player, &player_points); | ||
|
||
return outcome; | ||
} | ||
|
||
// View how many points a specific player has | ||
pub fn points_of(&self, player: AccountId) -> u8 { | ||
let points = self.points.get(&player).unwrap_or(0); | ||
log_str(&format!("Points for {player}: {points}")); | ||
|
||
return points; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
use near_workspaces::{types::NearToken, Account, Contract}; | ||
use serde_json::json; | ||
|
||
#[tokio::test] | ||
async fn main() -> Result<(), Box<dyn std::error::Error>> { | ||
let worker = near_workspaces::sandbox().await?; | ||
let contract_wasm = near_workspaces::compile_project("./").await?; | ||
let contract = worker.dev_deploy(&contract_wasm).await?; | ||
|
||
// create accounts | ||
let account = worker.dev_create_account().await?; | ||
let alice = account | ||
.create_subaccount("alice") | ||
.initial_balance(NearToken::from_near(30)) | ||
.transact() | ||
.await? | ||
.into_result()?; | ||
|
||
// begin tests | ||
test_user_has_no_points(&alice, &contract).await?; | ||
test_points_are_correctly_computed(&alice, &contract).await?; | ||
Ok(()) | ||
} | ||
|
||
async fn test_user_has_no_points( | ||
user: &Account, | ||
contract: &Contract, | ||
) -> Result<(), Box<dyn std::error::Error>> { | ||
let points: u8 = user | ||
.call(contract.id(), "points_of") | ||
.args_json(json!({ "player": user.id()})) | ||
.transact() | ||
.await? | ||
.json()?; | ||
|
||
assert_eq!(points, 0); | ||
println!(" Passed ✅ test_user_has_no_points"); | ||
Ok(()) | ||
} | ||
|
||
async fn test_points_are_correctly_computed( | ||
user: &Account, | ||
contract: &Contract, | ||
) -> Result<(), Box<dyn std::error::Error>> { | ||
let mut tails_counter = 0; | ||
let mut heads_counter = 0; | ||
let mut expected_points = 0; | ||
|
||
let mut i = 0; | ||
while i < 10 { | ||
let outcome: String = user.call(contract.id(), "flip_coin") | ||
.args_json(json!({"player_guess": "tails"})) | ||
.transact() | ||
.await? | ||
.json()?; | ||
|
||
if outcome.eq("tails") { | ||
tails_counter = tails_counter + 1; | ||
expected_points = expected_points + 1; | ||
} else { | ||
heads_counter = heads_counter + 1; | ||
if expected_points > 0 { | ||
expected_points = expected_points - 1; | ||
} | ||
} | ||
i = i + 1; | ||
} | ||
|
||
assert!(heads_counter >= 2); | ||
assert!(tails_counter >= 2); | ||
|
||
let points: u8 = user | ||
.call(contract.id(), "points_of") | ||
.args_json(json!({ "player": user.id()})) | ||
.transact() | ||
.await? | ||
.json()?; | ||
|
||
assert_eq!(points, expected_points); | ||
println!(" Passed ✅ test_points_are_correctly_computed"); | ||
Ok(()) | ||
} |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
Oops, something went wrong.