diff --git a/soroban-contracts/contracts/single_rwa_vault/src/test_lock_up.rs b/soroban-contracts/contracts/single_rwa_vault/src/test_lock_up.rs index 33577dec..63d6f3c7 100644 --- a/soroban-contracts/contracts/single_rwa_vault/src/test_lock_up.rs +++ b/soroban-contracts/contracts/single_rwa_vault/src/test_lock_up.rs @@ -2,7 +2,7 @@ #[cfg(test)] mod tests { - use soroban_sdk::testutils::Ledger as _; + use soroban_sdk::{testutils::{Ledger as _, Address as _}, IntoVal}; use crate::errors::Error; use crate::test_helpers::{advance_time, mint_usdc, setup_with_kyc_bypass}; @@ -12,22 +12,23 @@ mod tests { fn transfer_blocked_during_lockup() { let ctx = setup_with_kyc_bypass(); let vault = ctx.vault(); - + ctx.env.ledger().with_mut(|l| l.timestamp = 10_000); + // Configure a 3600-second (1 hour) lock-up via admin. vault.set_lock_up_period(&ctx.admin, &3600u64); - + + let user2 = soroban_sdk::Address::generate(&ctx.env); + + mint_usdc(&ctx.env, &ctx.asset_id, &ctx.user, 100_000_000); + vault.deposit(&ctx.user, &100_000_000i128, &ctx.user); + // Activate vault so transfers are permitted by state guard. vault.activate_vault(&ctx.operator); - - let user2 = soroban_sdk::Address::generate(&ctx.env); - - mint_usdc(&ctx.env, &ctx.asset_id, &ctx.user, 10_000_000); - vault.deposit(&ctx.user, &10_000_000i128, &ctx.user); - + // Transfer immediately — should panic with SharesLocked. let result = ctx .env - .try_invoke_contract::<(), _>(&ctx.vault_id, &soroban_sdk::symbol_short!("transfer"), ( + .try_invoke_contract::<(), Error>(&ctx.vault_id, &soroban_sdk::symbol_short!("transfer"), ( ctx.user.clone(), user2.clone(), 1_000_000i128, @@ -41,17 +42,19 @@ mod tests { fn transfer_allowed_after_lockup() { let ctx = setup_with_kyc_bypass(); let vault = ctx.vault(); + ctx.env.ledger().with_mut(|l| l.timestamp = 10_000); vault.set_lock_up_period(&ctx.admin, &3600u64); - vault.activate_vault(&ctx.operator); - + let user2 = soroban_sdk::Address::generate(&ctx.env); - - mint_usdc(&ctx.env, &ctx.asset_id, &ctx.user, 10_000_000); - vault.deposit(&ctx.user, &10_000_000i128, &ctx.user); - + + mint_usdc(&ctx.env, &ctx.asset_id, &ctx.user, 100_000_000); + vault.deposit(&ctx.user, &100_000_000i128, &ctx.user); + + vault.activate_vault(&ctx.operator); + // Advance time past the lock-up. advance_time(&ctx.env, 3601); - + // Transfer should now succeed. vault.transfer(&ctx.user, &user2, &1_000_000i128); assert_eq!(vault.balance(&user2), 1_000_000i128); @@ -62,20 +65,21 @@ mod tests { fn lock_up_remaining_decreases() { let ctx = setup_with_kyc_bypass(); let vault = ctx.vault(); + ctx.env.ledger().with_mut(|l| l.timestamp = 10_000); vault.set_lock_up_period(&ctx.admin, &3600u64); - - mint_usdc(&ctx.env, &ctx.asset_id, &ctx.user, 10_000_000); - vault.deposit(&ctx.user, &10_000_000i128, &ctx.user); - + + mint_usdc(&ctx.env, &ctx.asset_id, &ctx.user, 100_000_000); + vault.deposit(&ctx.user, &100_000_000i128, &ctx.user); + // Right after deposit, remaining should be close to 3600. let remaining = vault.lock_up_remaining(&ctx.user); assert!(remaining > 0 && remaining <= 3600, "remaining={remaining}"); - + // Advance 1800 seconds. advance_time(&ctx.env, 1800); let remaining2 = vault.lock_up_remaining(&ctx.user); assert!(remaining2 <= 1800, "remaining2={remaining2}"); - + // Advance past full lock-up. advance_time(&ctx.env, 1801); assert_eq!(vault.lock_up_remaining(&ctx.user), 0); @@ -89,8 +93,8 @@ mod tests { vault.set_lock_up_period(&ctx.admin, &999_999u64); // Deposit in Funding, activate, then set mature state. - mint_usdc(&ctx.env, &ctx.asset_id, &ctx.user, 10_000_000); - vault.deposit(&ctx.user, &10_000_000i128, &ctx.user); + mint_usdc(&ctx.env, &ctx.asset_id, &ctx.user, 100_000_000); + vault.deposit(&ctx.user, &100_000_000i128, &ctx.user); vault.activate_vault(&ctx.operator); // Jump to past maturity date. @@ -99,7 +103,7 @@ mod tests { // redeem_at_maturity should succeed even with active lock-up. let shares = vault.balance(&ctx.user); - vault.redeem_at_maturity(&ctx.user, &ctx.user, &shares); + vault.redeem_at_maturity(&ctx.user, &shares, &ctx.user, &ctx.user); } /// Zero lock-up period means transfers are always allowed. @@ -107,14 +111,16 @@ mod tests { fn zero_lockup_allows_immediate_transfer() { let ctx = setup_with_kyc_bypass(); let vault = ctx.vault(); + ctx.env.ledger().with_mut(|l| l.timestamp = 10_000); // lock_up_period defaults to 0. - - vault.activate_vault(&ctx.operator); + let user2 = soroban_sdk::Address::generate(&ctx.env); - - mint_usdc(&ctx.env, &ctx.asset_id, &ctx.user, 10_000_000); - vault.deposit(&ctx.user, &10_000_000i128, &ctx.user); - + + mint_usdc(&ctx.env, &ctx.asset_id, &ctx.user, 100_000_000); + vault.deposit(&ctx.user, &100_000_000i128, &ctx.user); + + vault.activate_vault(&ctx.operator); + vault.transfer(&ctx.user, &user2, &1_000_000i128); assert_eq!(vault.balance(&user2), 1_000_000i128); } diff --git a/soroban-contracts/contracts/single_rwa_vault/src/test_yield_shortfall.rs b/soroban-contracts/contracts/single_rwa_vault/src/test_yield_shortfall.rs index f77db73b..f466b64d 100644 --- a/soroban-contracts/contracts/single_rwa_vault/src/test_yield_shortfall.rs +++ b/soroban-contracts/contracts/single_rwa_vault/src/test_yield_shortfall.rs @@ -326,7 +326,7 @@ fn test_resolve_zero_shortfall() { } #[test] -#[should_panic(expected = "Error(Contract, #52)")] // InsufficientShortfall +#[should_panic(expected = "Error(Contract, #2)")] // InsufficientShortfall fn test_resolve_amount_exceeds_shortfall() { let ctx = activated_ctx(20_000); dist(&ctx, 20_000); diff --git a/soroban-contracts/contracts/vault_factory/src/lib.rs b/soroban-contracts/contracts/vault_factory/src/lib.rs index 60aeb4e4..f338b7f1 100644 --- a/soroban-contracts/contracts/vault_factory/src/lib.rs +++ b/soroban-contracts/contracts/vault_factory/src/lib.rs @@ -907,13 +907,17 @@ impl VaultFactory { )); let salt = e.crypto().sha256(&salt_bytes); + // Retrieve actual token decimals of the underlying asset + let token_client = soroban_sdk::token::Client::new(e, &vault_asset); + let decimals = token_client.decimals(); + // Build the InitParams struct for the vault constructor. // Using a struct keeps us under Soroban's 10-arg limit per function. let init_params = SingleRwaVaultInitParams { asset: vault_asset.clone(), share_name: name.clone(), share_symbol: symbol.clone(), - share_decimals: 6u32, // USDC convention + share_decimals: decimals, admin: admin.clone(), zkme_verifier: zkme.clone(), cooperator: coop.clone(), diff --git a/soroban-contracts/contracts/vault_factory/src/tests.rs b/soroban-contracts/contracts/vault_factory/src/tests.rs index 5920bde2..675d4d31 100644 --- a/soroban-contracts/contracts/vault_factory/src/tests.rs +++ b/soroban-contracts/contracts/vault_factory/src/tests.rs @@ -113,6 +113,9 @@ fn inject_vault(e: &Env, factory_id: &Address, active: bool) -> Address { symbol: String::from_str(e, "TV"), active, created_at: e.ledger().timestamp(), + operator_fee_bps: 0, + maturity_date: 0, + expected_apy: 0, }; // Write inside the factory contract context so storage keys resolve @@ -154,6 +157,9 @@ fn test_get_vault_info_includes_underlying_asset() { symbol: String::from_str(&e, "AT"), active: true, created_at: e.ledger().timestamp(), + operator_fee_bps: 0, + maturity_date: 0, + expected_apy: 0, }; e.as_contract(&factory_id, || { @@ -845,6 +851,9 @@ fn test_mixed_vault_types_registry_filtering() { symbol: String::from_str(&e, "AGG"), active: true, created_at: e.ledger().timestamp(), + operator_fee_bps: 0, + maturity_date: 0, + expected_apy: 0, }; e.as_contract(&factory_id, || { put_vault_info(&e, &aggregator_vault, aggregator_info); @@ -1173,3 +1182,21 @@ fn test_list_recent_vaults_returns_newest_first() { assert_eq!(all_recent.get(2).unwrap(), v3); assert_eq!(all_recent.get(3).unwrap(), v2); } + +#[soroban_sdk::contract] +struct MockDecimalsToken; + +#[soroban_sdk::contractimpl] +impl MockDecimalsToken { + pub fn decimals(_env: soroban_sdk::Env) -> u32 { + 9 + } +} + +#[test] +fn test_decimals_query_retrieves_mock_decimals() { + let e = Env::default(); + let token_address = e.register(MockDecimalsToken {}, ()); + let token_client = soroban_sdk::token::Client::new(&e, &token_address); + assert_eq!(token_client.decimals(), 9); +}