Skip to content

Commit d0eb831

Browse files
committed
feat: replace HelloWorld with GuessTheNumber
- The HelloWorld contract is so simple as to be obtuse. It's confusing in its pointlessness. And it doesn't show off anything interesting about blockchain programming. - GuessTheNumber represents a most-basic starting point for a contract that can show some interesting blockchain functionality. Already, it shows: - storing data - constructor - admin-guarded methods - forthcoming contracttrait composability pattern (stellar/rs-soroban-sdk#1522) - forthcoming admin SEP (https://github.com/AhaLabs/admin-sep) - This represents the probable starting point for the contract. In its current state, it has many purposeful ommissions/shortcomings. - `unwrap` in `guess` causes error if admin did not yet call `reset`; need to extract number-setting logic to separate function and call it in both `reset` and `__constructor` - no XLM is currently owned by contract or awarded to winning guess; need to require XLM transfer from admin in `reset` as well as nominal fee to call `guess` (which gets added to pot) - can inspect contract storage to see stored number; need to obfuscate with fun crypto tricks - These shortcomings would be addressed through an onboarding tutorial in the Stellar docs and maybe in the UI of the app itself, which so far has some very basic info about interacting with the contract
1 parent 69868bb commit d0eb831

File tree

11 files changed

+606
-162
lines changed

11 files changed

+606
-162
lines changed

Cargo.lock

Lines changed: 291 additions & 35 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
[package]
2+
name = "guess-the-number"
3+
description = "Admin sets up the pot, anyone can guess to win it"
4+
edition.workspace = true
5+
license.workspace = true
6+
repository.workspace = true
7+
publish = false
8+
version.workspace = true
9+
10+
[package.metadata.stellar]
11+
# Set contract metadata for authors, homepage, and version based on the Cargo.toml package values
12+
cargo_inherit = true
13+
14+
[lib]
15+
crate-type = ["cdylib"]
16+
doctest = false
17+
18+
[dependencies]
19+
soroban-sdk = { git = "https://github.com/AhaLabs/rs-soroban-sdk", rev = "5a99659f1483c926ff87ea45f8823b8c00dc4cbd" }
20+
admin-sep = { git = "https://github.com/AhaLabs/admin-sep", rev = "bf195f4d67cc96587974212f998680ccf9a61cd7" }
21+
22+
[dev-dependencies]
23+
soroban-sdk = { git = "https://github.com/AhaLabs/rs-soroban-sdk", rev = "5a99659f1483c926ff87ea45f8823b8c00dc4cbd", features = ["testutils"] }
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#![no_std]
2+
use admin_sep::{Administratable, Upgradable};
3+
use soroban_sdk::{contract, contractimpl, symbol_short, Address, Env, Symbol};
4+
5+
#[contract]
6+
pub struct GuessTheNumber;
7+
8+
#[contractimpl]
9+
impl Administratable for GuessTheNumber {}
10+
11+
#[contractimpl]
12+
impl Upgradable for GuessTheNumber {}
13+
14+
const THE_NUMBER: Symbol = symbol_short!("n");
15+
16+
#[contractimpl]
17+
impl GuessTheNumber {
18+
pub fn __constructor(env: &Env, admin: &Address) {
19+
Self::set_admin(env, admin);
20+
}
21+
22+
/// Update the number. Only callable by admin.
23+
pub fn reset(env: &Env) {
24+
Self::require_admin(env);
25+
let new_number: u64 = env.prng().gen_range(1..=10);
26+
env.storage().instance().set(&THE_NUMBER, &new_number);
27+
}
28+
29+
/// Guess a number between 1 and 10
30+
pub fn guess(env: &Env, a_number: u64) -> bool {
31+
a_number == env.storage().instance().get::<_, u64>(&THE_NUMBER).unwrap()
32+
}
33+
}
34+
35+
mod test;
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#![cfg(test)]
2+
extern crate std;
3+
4+
use super::*;
5+
use soroban_sdk::{
6+
testutils::{Address as _, MockAuth, MockAuthInvoke},
7+
Address, Env, IntoVal,
8+
};
9+
10+
#[test]
11+
fn only_admin_can_reset() {
12+
let env = &Env::default();
13+
14+
let admin = Address::generate(env);
15+
let user = Address::generate(env);
16+
17+
let client = generate_client(env, &admin);
18+
19+
set_caller(&client, "reset", &user);
20+
assert!(client.try_reset().is_err());
21+
22+
set_caller(&client, "reset", &admin);
23+
assert!(client.try_reset().is_ok());
24+
}
25+
26+
#[test]
27+
fn one_guess_in_ten_returns_true() {
28+
let env = &Env::default();
29+
30+
let admin = Address::generate(env);
31+
32+
let client = generate_client(env, &admin);
33+
34+
// set the number
35+
set_caller(&client, "reset", &admin);
36+
client.reset();
37+
38+
let trues: std::vec::Vec<_> = (1u64..=10).filter(|number| client.guess(number)).collect();
39+
assert_eq!(trues.len(), 1);
40+
}
41+
42+
fn generate_client<'a>(env: &Env, admin: &Address) -> GuessTheNumberClient<'a> {
43+
let contract_id = env.register(GuessTheNumber, (admin,));
44+
GuessTheNumberClient::new(env, &contract_id)
45+
}
46+
47+
fn set_caller(client: &GuessTheNumberClient, method: &str, caller: &Address) {
48+
// clear previous auth mocks
49+
client.env.set_auths(&[]);
50+
51+
// mock auth as passed-in address
52+
client.env.mock_auths(&[MockAuth {
53+
address: caller,
54+
invoke: &MockAuthInvoke {
55+
contract: &client.address,
56+
fn_name: method,
57+
args: ().into_val(&client.env),
58+
sub_invokes: &[],
59+
},
60+
}]);
61+
}

contracts/hello_world/Cargo.toml

Lines changed: 0 additions & 22 deletions
This file was deleted.

contracts/hello_world/src/lib.rs

Lines changed: 0 additions & 14 deletions
This file was deleted.

contracts/hello_world/src/test.rs

Lines changed: 0 additions & 21 deletions
This file was deleted.

environments.toml

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ name = "me" # Required. Keys for this account will be saved to `./.stellar/ident
99
default = true # Optional. Whether to use this account as the `--source` for commands that need one.
1010

1111
[development.contracts]
12+
fungible_token_interface_example = { client = true, constructor_args = "--owner me --initial_supply 1000000000000000000000000" }
13+
nft_enumerable_example = { client = true, constructor_args = "--owner me" }
14+
15+
# Rather than in one list, TOML allows specifying contracts in their own "sections"
16+
[development.contracts.guess_the_number]
1217
# Generate a contract client (NPM package) for this contract. This means:
1318
# - compile (build) the contract source to Wasm
1419
# - deploy the contract to the `network` specified above
@@ -20,27 +25,17 @@ default = true # Optional. Whether to use this account as the `--source` for com
2025
# You can only use `client = true` when:
2126
# - the contract source must be part of the local Cargo workspace (in the
2227
# PROJECT_ROOT/contracts folder)
23-
# - The specified name here ("stellar_hello_world_contract") must match the
28+
# - The specified name here ("guess_the_number") must match the
2429
# underscored-version of the `name` in the contract's Cargo.toml.
2530
# - The environment is `development` or `test`
26-
stellar_hello_world_contract = { client = true }
27-
28-
# Rather than in one list, TOML allows specifying contracts in their own "sections"
29-
[development.contracts.fungible_token_interface_example]
3031
client = true
3132

3233
# If your contract has a `__constructor`, specify your arguments to it here.
3334
# These are the same arguments you could pass after the `--` in a call to
3435
# `stellar contract deploy`
3536
# Only available in `development` and `test` environments
3637
constructor_args = """
37-
--owner me --initial_supply "1000000000000000000000000"
38-
"""
39-
40-
[development.contracts.nft_enumerable_example]
41-
client = true
42-
constructor_args = """
43-
--owner me
38+
--admin me
4439
"""
4540

4641
# Calls to the contract to make after it's deployed and initialized with
@@ -59,9 +54,9 @@ constructor_args = """
5954
# `STELLAR_ACCOUNT=other-account`.
6055
#
6156
# Only supported in `development` and `test` environments.
62-
# after_deploy = """
63-
# mint --amount 2000000 --to me"
64-
# """
57+
after_deploy = """
58+
reset
59+
"""
6560

6661
# Coming Soon: Specify live contracts to bind & import in this project using the given name.
6762
# During initialization, these contracts will also be "spooned" into the development network,

package-lock.json

Lines changed: 63 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)