From 7363acb66e03fcc8d6421f595c46f1d7910835e2 Mon Sep 17 00:00:00 2001 From: pselle Date: Wed, 11 Nov 2026 14:04:58 -0500 Subject: [PATCH 1/3] test(guess-the-number): register XLM with register_import! Replaces the #[cfg(test)] stand-in asset with stellar-registry 0.1.4's register_import!, so the contract has one code path for XLM. Co-Authored-By: Claude --- Cargo.lock | 15 +++++---- Cargo.toml | 2 +- contracts/guess-the-number/Cargo.toml | 1 + contracts/guess-the-number/src/lib.rs | 4 +-- contracts/guess-the-number/src/test.rs | 9 ++--- contracts/guess-the-number/src/xlm.rs | 46 +++----------------------- 6 files changed, 20 insertions(+), 57 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 33d06bc..b621e40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1518,9 +1518,9 @@ dependencies = [ [[package]] name = "stellar-build" -version = "0.0.6" +version = "0.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fd5e3be902a9658fb9f642d4eef7837ab1d51e52dde9be1d9b218326499d97" +checksum = "915a3455d5ef6013227e3ba69e7fd966c0830c4620f84c06b1dc684cae3a9465" dependencies = [ "cargo_metadata", "sha2", @@ -1559,26 +1559,27 @@ dependencies = [ [[package]] name = "stellar-registry" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b98aa78076a5727fee482732938e54c87da69428c283d5203d974ed71c3e18" +checksum = "9a6ef5b2abed78ae92d9f5f6481c87f7d7c20bebdfe759c9d18d27a3b0daa92a" dependencies = [ "stellar-registry-macro", ] [[package]] name = "stellar-registry-macro" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5dc0bdbd48f99c253a65a95848215bb3099ed191194c14f8a3639824dab9f5" +checksum = "97e50129dd0615302ff31a0ad0f4d86d75a9e25a66b07ae8cd33d63b000bc314" dependencies = [ "proc-macro2", "quote", + "soroban-spec", "stellar-build", "stellar-registry-name", "stellar-strkey 0.0.18", "stellar-xdr 30.0.0", - "syn 2.0.101", + "syn 3.0.3", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 5526556..0e6de25 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ soroban-sdk = "28.0.0" stellar-access = "0.8.0" stellar-macros = "0.8.0" stellar-tokens = "0.8.0" -stellar-registry = "0.1.0" +stellar-registry = "0.1.4" stellar-xdr = "28.0.0" [profile.release] diff --git a/contracts/guess-the-number/Cargo.toml b/contracts/guess-the-number/Cargo.toml index f2677e8..a3c16dd 100644 --- a/contracts/guess-the-number/Cargo.toml +++ b/contracts/guess-the-number/Cargo.toml @@ -24,3 +24,4 @@ stellar-registry = { workspace = true } [dev-dependencies] stellar-xdr = { workspace = true, features = ["serde"] } soroban-sdk = { workspace = true, features = ["testutils"] } +stellar-registry = { workspace = true, features = ["testutils"] } diff --git a/contracts/guess-the-number/src/lib.rs b/contracts/guess-the-number/src/lib.rs index 6a8a60f..a4c81ab 100644 --- a/contracts/guess-the-number/src/lib.rs +++ b/contracts/guess-the-number/src/lib.rs @@ -29,9 +29,7 @@ impl GuessTheNumber { let admin = Self::admin(env).expect("admin not set"); // Only the admin can reset the game and authorize spending their XLM. admin.require_auth(); - // Ensure the XLM contract exists on the network. Unit tests create - // their own asset in test setup, outside contract invocations. - #[cfg(not(test))] + // Ensure the XLM contract exists on the network. xlm::register(env); // Seed the pot with 1 XLM from the admin for players to win. xlm::token_client(env).transfer( diff --git a/contracts/guess-the-number/src/test.rs b/contracts/guess-the-number/src/test.rs index 6e7cdab..43d6a7a 100644 --- a/contracts/guess-the-number/src/test.rs +++ b/contracts/guess-the-number/src/test.rs @@ -8,6 +8,7 @@ use soroban_sdk::{ testutils::{Address as _, MockAuth, MockAuthInvoke}, token::{StellarAssetClient, TokenClient}, }; +use stellar_registry::testutils::register_import; fn init_test<'a>(env: &'a Env) -> (Address, StellarAssetClient<'a>, GuessTheNumberClient<'a>) { let admin = Address::generate(env); @@ -16,9 +17,9 @@ fn init_test<'a>(env: &'a Env) -> (Address, StellarAssetClient<'a>, GuessTheNumb env.mock_all_auths(); let contract_id = env.register(GuessTheNumber, (&admin,)); let client = GuessTheNumberClient::new(env, &contract_id); - // Create the asset outside contract invocations, after the game exists. - let asset = xlm::register(env, &admin, &contract_id); - let sac = StellarAssetClient::new(env, &asset.address()); + // Put a mintable test asset at the address `import_contract!(env, xlm)` uses. + let asset = register_import!(env, xlm); + let sac = StellarAssetClient::new(env, &asset); // Mint a starting balance for the admin. On testnet, use Friendbot instead. sac.mint(&admin, &xlm::to_stroops(10_000)); // Clear setup's auth mocks so each test chooses what to authorize. @@ -188,7 +189,7 @@ fn reset_without_funds_does_not_change_round() { // Authorize reset and its nested transfer, rather than mocking every caller. fn set_caller(client: &GuessTheNumberClient, caller: &Address) { let env = &client.env; - let asset = env.as_contract(&client.address, || xlm::contract_id(env)); + let asset = xlm::token_client(env).address; env.mock_auths(&[MockAuth { address: caller, invoke: &MockAuthInvoke { diff --git a/contracts/guess-the-number/src/xlm.rs b/contracts/guess-the-number/src/xlm.rs index 18b85a6..89f6aca 100644 --- a/contracts/guess-the-number/src/xlm.rs +++ b/contracts/guess-the-number/src/xlm.rs @@ -4,17 +4,12 @@ pub const fn to_stroops(num: u64) -> i128 { (num as i128) * ONE_XLM } -#[cfg(not(test))] -// Resolve the real network's XLM contract; tests use a separate asset below. -stellar_registry::import_asset!("xlm"); - -#[cfg(test)] -pub use test_asset::*; -#[cfg(not(test))] -pub use xlm::*; +/// Client for the network's native XLM asset contract. +pub fn token_client<'a>(env: &soroban_sdk::Env) -> soroban_sdk::token::TokenClient<'a> { + stellar_registry::import_contract!(env, xlm) +} /// Ensure the native XLM asset contract exists before funding a round. -#[cfg(not(test))] pub fn register(env: &soroban_sdk::Env) { if token_client(env) .try_balance(&env.current_contract_address()) @@ -24,36 +19,3 @@ pub fn register(env: &soroban_sdk::Env) { env.deployer().with_stellar_asset([0u8; 4]).deploy(); } } - -#[cfg(test)] -mod test_asset { - const XLM_KEY: &soroban_sdk::Symbol = &soroban_sdk::symbol_short!("XLM"); - - pub fn contract_id(env: &soroban_sdk::Env) -> soroban_sdk::Address { - env.storage() - .instance() - .get::<_, soroban_sdk::Address>(XLM_KEY) - .expect("XLM contract not initialized. Please deploy the XLM contract first.") - } - - pub fn register( - env: &soroban_sdk::Env, - admin: &soroban_sdk::Address, - game: &soroban_sdk::Address, - ) -> soroban_sdk::testutils::StellarAssetContract { - // SDK test generators must run outside contract invocations. - // This is a mintable test asset, not native XLM. - let sac = env.register_stellar_asset_contract_v2(admin.clone()); - // Run the storage write in the game's context so its token client can - // find this asset when called from inside a contract method. - env.as_contract(game, || { - env.storage().instance().set(XLM_KEY, &sac.address()); - }); - sac - } - - /// Create a client for the test asset stored in the game's instance. - pub fn token_client<'a>(env: &soroban_sdk::Env) -> soroban_sdk::token::TokenClient<'a> { - soroban_sdk::token::TokenClient::new(env, &contract_id(env)) - } -} From c97da474c559e243c0fcbd1b9ca44f3719b308c3 Mon Sep 17 00:00:00 2001 From: pselle Date: Wed, 11 Nov 2026 14:17:56 -0500 Subject: [PATCH 2/3] test(guess-the-number): keep import_asset! for XLM Co-Authored-By: Claude --- contracts/guess-the-number/src/test.rs | 2 +- contracts/guess-the-number/src/xlm.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts/guess-the-number/src/test.rs b/contracts/guess-the-number/src/test.rs index 43d6a7a..6bc87b8 100644 --- a/contracts/guess-the-number/src/test.rs +++ b/contracts/guess-the-number/src/test.rs @@ -189,7 +189,7 @@ fn reset_without_funds_does_not_change_round() { // Authorize reset and its nested transfer, rather than mocking every caller. fn set_caller(client: &GuessTheNumberClient, caller: &Address) { let env = &client.env; - let asset = xlm::token_client(env).address; + let asset = xlm::contract_id(env); env.mock_auths(&[MockAuth { address: caller, invoke: &MockAuthInvoke { diff --git a/contracts/guess-the-number/src/xlm.rs b/contracts/guess-the-number/src/xlm.rs index 89f6aca..96dc96a 100644 --- a/contracts/guess-the-number/src/xlm.rs +++ b/contracts/guess-the-number/src/xlm.rs @@ -4,10 +4,10 @@ pub const fn to_stroops(num: u64) -> i128 { (num as i128) * ONE_XLM } -/// Client for the network's native XLM asset contract. -pub fn token_client<'a>(env: &soroban_sdk::Env) -> soroban_sdk::token::TokenClient<'a> { - stellar_registry::import_contract!(env, xlm) -} +// Resolve the network's XLM contract; tests put a test asset at the same address. +stellar_registry::import_asset!("xlm"); + +pub use xlm::*; /// Ensure the native XLM asset contract exists before funding a round. pub fn register(env: &soroban_sdk::Env) { From 22ef7b08f9b580602c76a62adf2ab82ca8d2e36e Mon Sep 17 00:00:00 2001 From: pselle Date: Wed, 11 Nov 2026 14:26:56 -0500 Subject: [PATCH 3/3] test(guess-the-number): fix test asset comment --- contracts/guess-the-number/src/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/guess-the-number/src/test.rs b/contracts/guess-the-number/src/test.rs index 6bc87b8..e25b3a7 100644 --- a/contracts/guess-the-number/src/test.rs +++ b/contracts/guess-the-number/src/test.rs @@ -17,7 +17,7 @@ fn init_test<'a>(env: &'a Env) -> (Address, StellarAssetClient<'a>, GuessTheNumb env.mock_all_auths(); let contract_id = env.register(GuessTheNumber, (&admin,)); let client = GuessTheNumberClient::new(env, &contract_id); - // Put a mintable test asset at the address `import_contract!(env, xlm)` uses. + // Put a mintable test asset at the address `import_asset!("xlm")` resolves to. let asset = register_import!(env, xlm); let sac = StellarAssetClient::new(env, &asset); // Mint a starting balance for the admin. On testnet, use Friendbot instead.