Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
326 changes: 291 additions & 35 deletions Cargo.lock

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions contracts/guess-the-number/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "guess-the-number"
description = "Admin sets up the pot, anyone can guess to win it"
edition.workspace = true
license.workspace = true
repository.workspace = true
publish = false
version.workspace = true

[package.metadata.stellar]
# Set contract metadata for authors, homepage, and version based on the Cargo.toml package values
cargo_inherit = true

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

[dependencies]
soroban-sdk = { git = "https://github.com/AhaLabs/rs-soroban-sdk", rev = "5a99659f1483c926ff87ea45f8823b8c00dc4cbd" }
admin-sep = { git = "https://github.com/AhaLabs/admin-sep", rev = "bf195f4d67cc96587974212f998680ccf9a61cd7" }

[dev-dependencies]
soroban-sdk = { git = "https://github.com/AhaLabs/rs-soroban-sdk", rev = "5a99659f1483c926ff87ea45f8823b8c00dc4cbd", features = ["testutils"] }
36 changes: 36 additions & 0 deletions contracts/guess-the-number/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#![no_std]
use admin_sep::{Administratable, Upgradable};
use soroban_sdk::{contract, contractimpl, symbol_short, Address, Env, Symbol};

#[contract]
pub struct GuessTheNumber;

#[contractimpl]
impl Administratable for GuessTheNumber {}

#[contractimpl]
impl Upgradable for GuessTheNumber {}

const THE_NUMBER: Symbol = symbol_short!("n");

#[contractimpl]
impl GuessTheNumber {
pub fn __constructor(env: &Env, admin: &Address) {
Self::set_admin(env, admin);
}

/// Update the number. Only callable by admin.
pub fn reset(env: &Env) {
Self::require_admin(env);
let new_number: u64 = env.prng().gen_range(1..=10);
env.storage().instance().set(&THE_NUMBER, &new_number);
}

/// Guess a number between 1 and 10
pub fn guess(env: &Env, guesser: Address, a_number: u64) -> bool {
Copy link
Member Author

@chadoh chadoh Sep 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tutorial should explain why you need to pass guesser as an argument, instead of just requiring that the invoker—whoever they may be—signed the transaction. soroban-sdk is ~ToO fLeXiBLe~ to allow you to just check who the invoker is. This is unexpected! Make sure people understand it.

guesser.require_auth();
a_number == env.storage().instance().get::<_, u64>(&THE_NUMBER).unwrap()
}
}

mod test;
70 changes: 70 additions & 0 deletions contracts/guess-the-number/src/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#![cfg(test)]
extern crate std;

use super::*;
use soroban_sdk::{
testutils::{Address as _, MockAuth, MockAuthInvoke},
Address, Env, IntoVal, Val, Vec,
};

#[test]
fn only_admin_can_reset() {
let env = &Env::default();

let admin = Address::generate(env);
let user = Address::generate(env);

let client = generate_client(env, &admin);

set_caller(&client, "reset", &user, ());
assert!(client.try_reset().is_err());

set_caller(&client, "reset", &admin, ());
assert!(client.try_reset().is_ok());
}

#[test]
fn one_guess_in_ten_returns_true() {
let env = &Env::default();

let admin = Address::generate(env);
let user = Address::generate(env);

let client = generate_client(env, &admin);

// set the number
set_caller(&client, "reset", &admin, ());
client.reset();

let trues: std::vec::Vec<_> = (1u64..=10)
.filter(|number| {
set_caller(&client, "guess", &user, (&user, number));
client.guess(&user, number)
})
.collect();
assert_eq!(trues.len(), 1);
}

fn generate_client<'a>(env: &Env, admin: &Address) -> GuessTheNumberClient<'a> {
let contract_id = env.register(GuessTheNumber, (admin,));
GuessTheNumberClient::new(env, &contract_id)
}

fn set_caller<T>(client: &GuessTheNumberClient, method: &str, caller: &Address, args: T)
where
T: IntoVal<Env, Vec<Val>>,
{
// clear previous auth mocks
client.env.set_auths(&[]);

// mock auth as passed-in address
client.env.mock_auths(&[MockAuth {
address: caller,
invoke: &MockAuthInvoke {
contract: &client.address,
fn_name: method,
args: args.into_val(&client.env),
sub_invokes: &[],
},
}]);
}
22 changes: 0 additions & 22 deletions contracts/hello_world/Cargo.toml

This file was deleted.

14 changes: 0 additions & 14 deletions contracts/hello_world/src/lib.rs

This file was deleted.

21 changes: 0 additions & 21 deletions contracts/hello_world/src/test.rs

This file was deleted.

25 changes: 10 additions & 15 deletions environments.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ name = "me" # Required. Keys for this account will be saved to `./.stellar/ident
default = true # Optional. Whether to use this account as the `--source` for commands that need one.

[development.contracts]
fungible_token_interface_example = { client = true, constructor_args = "--owner me --initial_supply 1000000000000000000000000" }
nft_enumerable_example = { client = true, constructor_args = "--owner me" }

# Rather than in one list, TOML allows specifying contracts in their own "sections"
[development.contracts.guess_the_number]
# Generate a contract client (NPM package) for this contract. This means:
# - compile (build) the contract source to Wasm
# - deploy the contract to the `network` specified above
Expand All @@ -20,27 +25,17 @@ default = true # Optional. Whether to use this account as the `--source` for com
# You can only use `client = true` when:
# - the contract source must be part of the local Cargo workspace (in the
# PROJECT_ROOT/contracts folder)
# - The specified name here ("stellar_hello_world_contract") must match the
# - The specified name here ("guess_the_number") must match the
# underscored-version of the `name` in the contract's Cargo.toml.
# - The environment is `development` or `test`
stellar_hello_world_contract = { client = true }

# Rather than in one list, TOML allows specifying contracts in their own "sections"
[development.contracts.fungible_token_interface_example]
client = true

# If your contract has a `__constructor`, specify your arguments to it here.
# These are the same arguments you could pass after the `--` in a call to
# `stellar contract deploy`
# Only available in `development` and `test` environments
constructor_args = """
--owner me --initial_supply "1000000000000000000000000"
"""

[development.contracts.nft_enumerable_example]
client = true
constructor_args = """
--owner me
--admin me
"""

# Calls to the contract to make after it's deployed and initialized with
Expand All @@ -59,9 +54,9 @@ constructor_args = """
# `STELLAR_ACCOUNT=other-account`.
#
# Only supported in `development` and `test` environments.
# after_deploy = """
# mint --amount 2000000 --to me"
# """
after_deploy = """
reset
"""

# Coming Soon: Specify live contracts to bind & import in this project using the given name.
# During initialization, these contracts will also be "spooned" into the development network,
Expand Down
73 changes: 63 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading