Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
17 changes: 17 additions & 0 deletions src/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/// Token name constant. Changing this is a BREAKING CHANGE for indexers.
pub const TOKEN_NAME: &str = "Stellar Wrap Registry";

/// Token symbol constant. Changing this is a BREAKING CHANGE for indexers.
pub const TOKEN_SYMBOL: &str = "WRAP";

/// Token decimals constant. Soulbound tokens are non-divisible.
pub const TOKEN_DECIMALS: u32 = 0;

/// Contract description for metadata.
pub const CONTRACT_DESCRIPTION: &str = "Soulbound token registry for Stellar Wrap";

/// Contract version. Bump this on every WASM upgrade.
pub const VERSION: u32 = 1;

/// Hash preview bytes length constant.
pub const HASH_PREVIEW_BYTES: usize = 8;
14 changes: 8 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@

use crate::constants::{TOKEN_NAME, TOKEN_SYMBOL, TOKEN_DECIMALS, CONTRACT_DESCRIPTION, VERSION};

use soroban_sdk::{

soroban_sdk::contractmeta!(
key = "Description",
val = "Soulbound token registry for Stellar Wrap"
val = CONTRACT_DESCRIPTION
);
soroban_sdk::contractmeta!(key = "Version", val = "0.1.0");
soroban_sdk::contractmeta!(key = "Name", val = "Stellar Wrap Registry");
soroban_sdk::contractmeta!(key = "Name", val = TOKEN_NAME);
soroban_sdk::contractmeta!(key = "Author", val = "Stellar Wrap Team");

/// Errors returned by the StellarWrap contract.
Expand Down Expand Up @@ -758,15 +760,15 @@ impl StellarWrapContract {
/// # Returns
/// `"Stellar Wrap Registry"`
pub fn name(e: Env) -> String {
String::from_str(&e, "Stellar Wrap Registry")
String::from_str(&e, TOKEN_NAME)
}

/// Return the ticker symbol for this token registry.
///
/// # Returns
/// `"WRAP"`
pub fn symbol(e: Env) -> String {
String::from_str(&e, "WRAP")
String::from_str(&e, TOKEN_SYMBOL)
}

/// Return the number of decimals. Soulbound tokens are non-divisible, so this is always `0`.
Expand All @@ -782,10 +784,10 @@ impl StellarWrapContract {
/// Return contract-level metadata useful for explorers and indexers.
pub fn contract_info(e: Env) -> ContractInfo {
ContractInfo {
name: String::from_str(&e, "Stellar Wrap Registry"),
name: String::from_str(&e, TOKEN_NAME),
version: String::from_str(&e, "0.1.0"),
repo: String::from_str(&e, "https://github.com/zintarh/stellar-wrap-contract"),
description: String::from_str(&e, "Soulbound token registry for Stellar Wrap"),
description: String::from_str(&e, CONTRACT_DESCRIPTION),
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/storage_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,5 @@ pub enum DataKey {
pub const SCHEMA_VERSION: u32 = 1;
/// Target schema version after v1 → v2 migration (`image_uri` field).
pub const SCHEMA_VERSION_V2: u32 = 2;
/// Target schema version after v2 → v3 migration (wrap record updates).
pub const SCHEMA_VERSION_V3: u32 = 3;
46 changes: 37 additions & 9 deletions src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
extern crate std;

use super::*;
use crate::constants::{TOKEN_NAME, TOKEN_SYMBOL, TOKEN_DECIMALS, CONTRACT_DESCRIPTION, VERSION};
use ed25519_dalek::{Signer, SigningKey};
use soroban_sdk::{
symbol_short,
Expand Down Expand Up @@ -405,12 +406,39 @@ fn test_token_metadata() {
let contract_id = env.register_contract(None, StellarWrapContract);
let client = StellarWrapContractClient::new(&env, &contract_id);

assert_eq!(client.decimals(), 0);
assert_eq!(
client.name(),
String::from_str(&env, "Stellar Wrap Registry")
);
assert_eq!(client.symbol(), String::from_str(&env, "WRAP"));
assert_eq!(client.decimals(), TOKEN_DECIMALS);
assert_eq!(client.name(), String::from_str(&env, TOKEN_NAME));
assert_eq!(client.symbol(), String::from_str(&env, TOKEN_SYMBOL));
}

/// Regression test: Verify `name()`, `symbol()`, and `decimals()` return stable
/// values across contract upgrades. These functions are pure view functions that
/// must not change after a WASM upgrade to maintain indexer compatibility.
///
/// NOTE: These functions return constant values hardcoded in the contract.
/// Changing TOKEN_NAME, TOKEN_SYMBOL, or TOKEN_DECIMALS is a BREAKING CHANGE
/// for indexers and downstream consumers that cache these values.
#[test]
fn test_token_metadata_preserved_after_upgrade() {
let env = Env::default();

let contract_v1_id = env.register_contract(None, StellarWrapContract);
let client_v1 = StellarWrapContractClient::new(&env, &contract_v1_id);

assert_eq!(client_v1.decimals(), TOKEN_DECIMALS, "V1 decimals");
assert_eq!(client_v1.name(), String::from_str(&env, TOKEN_NAME), "V1 name");
assert_eq!(client_v1.symbol(), String::from_str(&env, TOKEN_SYMBOL), "V1 symbol");

let contract_v2_id = env.register_contract(None, StellarWrapContract);
let client_v2 = StellarWrapContractClient::new(&env, &contract_v2_id);

assert_eq!(client_v2.decimals(), TOKEN_DECIMALS, "V2 decimals must match V1");
assert_eq!(client_v2.name(), String::from_str(&env, TOKEN_NAME), "V2 name must match V1");
assert_eq!(client_v2.symbol(), String::from_str(&env, TOKEN_SYMBOL), "V2 symbol must match V1");

assert_eq!(client_v1.decimals(), client_v2.decimals(), "decimals stable across versions");
assert_eq!(client_v1.name(), client_v2.name(), "name stable across versions");
assert_eq!(client_v1.symbol(), client_v2.symbol(), "symbol stable across versions");
}

// ─── Issue #48: version tests ───────────────────────────────────────────────
Expand All @@ -421,7 +449,7 @@ fn test_version_returns_expected_value() {
let contract_id = env.register_contract(None, StellarWrapContract);
let client = StellarWrapContractClient::new(&env, &contract_id);

assert_eq!(client.version(), 1);
assert_eq!(client.version(), VERSION);
}

// ─── Issue #56: contract_info tests ─────────────────────────────────────────
Expand All @@ -433,15 +461,15 @@ fn test_contract_info_returns_correct_fields() {
let client = StellarWrapContractClient::new(&env, &contract_id);

let info = client.contract_info();
assert_eq!(info.name, String::from_str(&env, "Stellar Wrap Registry"));
assert_eq!(info.name, String::from_str(&env, TOKEN_NAME));
assert_eq!(info.version, String::from_str(&env, "0.1.0"));
assert_eq!(
info.repo,
String::from_str(&env, "https://github.com/zintarh/stellar-wrap-contract")
);
assert_eq!(
info.description,
String::from_str(&env, "Soulbound token registry for Stellar Wrap")
String::from_str(&env, CONTRACT_DESCRIPTION)
);
}

Expand Down
Loading