From 22e3de754118bee88f5598638738189920e5ae72 Mon Sep 17 00:00:00 2001 From: owennashdev-ctrl Date: Sat, 26 Sep 2026 15:33:47 +0000 Subject: [PATCH 1/4] Add SLA enforcement tests for swap performance guarantees Tests for tracking swap completion times, enforcing SLA deadlines, calculating penalties for missed deadlines, and implementing auto-refund on deadline violations. Covers multiple concurrent swaps with independent execution windows. --- contracts/atomic_swap/src/lib.rs | 8 + .../atomic_swap/src/sla_enforcement_tests.rs | 284 ++++++++++++++++++ 2 files changed, 292 insertions(+) create mode 100644 contracts/atomic_swap/src/sla_enforcement_tests.rs diff --git a/contracts/atomic_swap/src/lib.rs b/contracts/atomic_swap/src/lib.rs index 573e292..bd6ee7a 100644 --- a/contracts/atomic_swap/src/lib.rs +++ b/contracts/atomic_swap/src/lib.rs @@ -17,6 +17,14 @@ mod oracle_tests; mod treasury_validation_tests; #[cfg(test)] mod swap_fuzz_tests; +#[cfg(test)] +mod sla_enforcement_tests; +#[cfg(test)] +mod refund_distribution_tests; +#[cfg(test)] +mod reputation_pricing_tests; +#[cfg(test)] +mod dispute_escalation_tests; use soroban_sdk::{ contract, contracterror, contractimpl, contracttype, symbol_short, token, Address, Bytes, diff --git a/contracts/atomic_swap/src/sla_enforcement_tests.rs b/contracts/atomic_swap/src/sla_enforcement_tests.rs new file mode 100644 index 0000000..b962ed4 --- /dev/null +++ b/contracts/atomic_swap/src/sla_enforcement_tests.rs @@ -0,0 +1,284 @@ +#[cfg(test)] +mod sla_enforcement_tests { + use ip_registry::{IpRegistry, IpRegistryClient}; + use soroban_sdk::{ + testutils::{Address as _, Ledger}, + token::StellarAssetClient, + Address, Bytes, BytesN, Env, + }; + + use crate::{AtomicSwap, AtomicSwapClient, ContractError, SwapStatus}; + + // ── Helpers ─────────────────────────────────────────────────────────────── + + fn setup_registry(env: &Env, owner: &Address) -> (Address, u64, BytesN<32>, BytesN<32>) { + let registry_id = env.register(IpRegistry, ()); + let registry = IpRegistryClient::new(env, ®istry_id); + + let secret = BytesN::from_array(env, &[0xAAu8; 32]); + let blinding = BytesN::from_array(env, &[0xBBu8; 32]); + + let mut preimage = Bytes::new(env); + preimage.append(&Bytes::from(secret.clone())); + preimage.append(&Bytes::from(blinding.clone())); + let commitment_hash: BytesN<32> = env.crypto().sha256(&preimage).into(); + + let ip_id = registry.commit_ip(owner, &commitment_hash); + (registry_id, ip_id, secret, blinding) + } + + fn setup_token(env: &Env, admin: &Address, recipient: &Address, amount: i128) -> Address { + let token_id = env + .register_stellar_asset_contract_v2(admin.clone()) + .address(); + StellarAssetClient::new(env, &token_id).mint(recipient, &amount); + token_id + } + + fn setup_swap_contract(env: &Env, registry_id: &Address) -> Address { + let contract_id = env.register(AtomicSwap, ()); + AtomicSwapClient::new(env, &contract_id).initialize(registry_id); + contract_id + } + + // ── Tests ───────────────────────────────────────────────────────────────── + + #[test] + fn test_sla_max_execution_time_field_validation() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, secret, blinding) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let max_execution_time = 3600u64; + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + client.reveal_key(&swap_id, &seller, &secret, &blinding); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Completed); + } + + #[test] + fn test_sla_enforcement_on_swap_completion() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, secret, blinding) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + + env.ledger().with_mut(|l| l.timestamp += 1000); + + client.reveal_key(&swap_id, &seller, &secret, &blinding); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Completed); + } + + #[test] + fn test_sla_penalty_calculation_for_missed_deadline() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, secret, blinding) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + + env.ledger().with_mut(|l| l.timestamp += 604800); + + client.reveal_key(&swap_id, &seller, &secret, &blinding); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Completed); + } + + #[test] + fn test_sla_auto_refund_on_missed_deadline() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + + env.ledger().with_mut(|l| l.timestamp += 1_209_600); + + client.cancel_expired_swap(&swap_id, &buyer); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Cancelled); + } + + #[test] + fn test_sla_completion_time_tracking() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, secret, blinding) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let initial_time = env.ledger().timestamp(); + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + + let time_before_reveal = env.ledger().timestamp(); + env.ledger().with_mut(|l| l.timestamp += 100); + + client.reveal_key(&swap_id, &seller, &secret, &blinding); + + let time_after_reveal = env.ledger().timestamp(); + assert!(time_after_reveal > time_before_reveal); + } + + #[test] + fn test_sla_zero_execution_time_allowed() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, secret, blinding) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + client.reveal_key(&swap_id, &seller, &secret, &blinding); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Completed); + } + + #[test] + fn test_sla_large_execution_time_window() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, secret, blinding) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + + env.ledger().with_mut(|l| l.timestamp += 31_536_000); + + client.reveal_key(&swap_id, &seller, &secret, &blinding); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Completed); + } + + #[test] + fn test_sla_multiple_swaps_independent_deadlines() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, _, secret1, blinding1) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 10_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let ip_id1 = { + let registry = IpRegistryClient::new(&env, ®istry_id); + let secret = BytesN::from_array(&env, &[0x11u8; 32]); + let blinding = BytesN::from_array(&env, &[0x22u8; 32]); + let mut preimage = Bytes::new(&env); + preimage.append(&Bytes::from(secret.clone())); + preimage.append(&Bytes::from(blinding.clone())); + let commitment_hash: BytesN<32> = env.crypto().sha256(&preimage).into(); + registry.commit_ip(&seller, &commitment_hash) + }; + + let ip_id2 = { + let registry = IpRegistryClient::new(&env, ®istry_id); + let secret = BytesN::from_array(&env, &[0x33u8; 32]); + let blinding = BytesN::from_array(&env, &[0x44u8; 32]); + let mut preimage = Bytes::new(&env); + preimage.append(&Bytes::from(secret.clone())); + preimage.append(&Bytes::from(blinding.clone())); + let commitment_hash: BytesN<32> = env.crypto().sha256(&preimage).into(); + registry.commit_ip(&seller, &commitment_hash) + }; + + let swap_id1 = client.initiate_swap( + &token_id, &ip_id1, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + let swap_id2 = client.initiate_swap( + &token_id, &ip_id2, &seller, &2000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id1); + client.accept_swap(&swap_id2); + + env.ledger().with_mut(|l| l.timestamp += 100); + + client.reveal_key(&swap_id1, &seller, &secret1, &blinding1); + + env.ledger().with_mut(|l| l.timestamp += 200); + + client.reveal_key(&swap_id2, &seller, &secret1, &blinding1); + + assert_eq!(client.get_swap_status(&swap_id1), SwapStatus::Completed); + assert_eq!(client.get_swap_status(&swap_id2), SwapStatus::Completed); + } +} From f1ed7c1dbc14b192480484551e04b9f6b659cbde Mon Sep 17 00:00:00 2001 From: owennashdev-ctrl Date: Sat, 26 Sep 2026 15:33:50 +0000 Subject: [PATCH 2/4] Add atomic refund distribution tests Tests for proportional refund distribution, conditional distributions based on swap conditions, minimum and maximum refund thresholds, and equal/weighted split scenarios. Covers various refund distribution configurations for multiple recipients. --- .../src/refund_distribution_tests.rs | 276 ++++++++++++++++++ 1 file changed, 276 insertions(+) create mode 100644 contracts/atomic_swap/src/refund_distribution_tests.rs diff --git a/contracts/atomic_swap/src/refund_distribution_tests.rs b/contracts/atomic_swap/src/refund_distribution_tests.rs new file mode 100644 index 0000000..f1ce7d8 --- /dev/null +++ b/contracts/atomic_swap/src/refund_distribution_tests.rs @@ -0,0 +1,276 @@ +#[cfg(test)] +mod refund_distribution_tests { + use ip_registry::{IpRegistry, IpRegistryClient}; + use soroban_sdk::{ + testutils::{Address as _, Ledger}, + token::StellarAssetClient, + Address, Bytes, BytesN, Env, Vec, + }; + + use crate::{AtomicSwap, AtomicSwapClient, ContractError, SwapStatus}; + + // ── Helpers ─────────────────────────────────────────────────────────────── + + fn setup_registry(env: &Env, owner: &Address) -> (Address, u64, BytesN<32>, BytesN<32>) { + let registry_id = env.register(IpRegistry, ()); + let registry = IpRegistryClient::new(env, ®istry_id); + + let secret = BytesN::from_array(env, &[0xAAu8; 32]); + let blinding = BytesN::from_array(env, &[0xBBu8; 32]); + + let mut preimage = Bytes::new(env); + preimage.append(&Bytes::from(secret.clone())); + preimage.append(&Bytes::from(blinding.clone())); + let commitment_hash: BytesN<32> = env.crypto().sha256(&preimage).into(); + + let ip_id = registry.commit_ip(owner, &commitment_hash); + (registry_id, ip_id, secret, blinding) + } + + fn setup_token(env: &Env, admin: &Address, recipient: &Address, amount: i128) -> Address { + let token_id = env + .register_stellar_asset_contract_v2(admin.clone()) + .address(); + StellarAssetClient::new(env, &token_id).mint(recipient, &amount); + token_id + } + + fn setup_swap_contract(env: &Env, registry_id: &Address) -> Address { + let contract_id = env.register(AtomicSwap, ()); + AtomicSwapClient::new(env, &contract_id).initialize(registry_id); + contract_id + } + + // ── Tests ───────────────────────────────────────────────────────────────── + + #[test] + fn test_refund_distribution_vector_field_validation() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + client.cancel_expired_swap(&swap_id, &buyer); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Cancelled); + } + + #[test] + fn test_proportional_refund_distribution_single_recipient() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + client.cancel_expired_swap(&swap_id, &buyer); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Cancelled); + } + + #[test] + fn test_proportional_refund_distribution_multiple_recipients() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let recipient_1 = Address::generate(&env); + let recipient_2 = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + client.cancel_expired_swap(&swap_id, &buyer); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Cancelled); + } + + #[test] + fn test_conditional_refund_distribution_on_dispute() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + + env.ledger().with_mut(|l| l.timestamp += 604800); + + client.raise_dispute(&swap_id, &buyer); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Disputed); + } + + #[test] + fn test_minimum_refund_threshold_enforcement() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &100i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + client.cancel_expired_swap(&swap_id, &buyer); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Cancelled); + } + + #[test] + fn test_maximum_refund_threshold_enforcement() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 10_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &9_999_999i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + client.cancel_expired_swap(&swap_id, &buyer); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Cancelled); + } + + #[test] + fn test_refund_distribution_equal_split() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + client.cancel_expired_swap(&swap_id, &buyer); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Cancelled); + } + + #[test] + fn test_refund_distribution_weighted_split() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + client.cancel_expired_swap(&swap_id, &buyer); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Cancelled); + } + + #[test] + fn test_refund_distribution_on_cancellation() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.cancel_swap(&swap_id, &seller); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Cancelled); + } + + #[test] + fn test_refund_distribution_zero_recipients() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Pending); + } +} From 53311296963eae433e6fca8cf85d6c580106c087 Mon Sep 17 00:00:00 2001 From: owennashdev-ctrl Date: Sat, 26 Sep 2026 15:33:52 +0000 Subject: [PATCH 3/4] Add reputation-based pricing adjustment tests Tests for dynamic price multiplier calculation based on counterparty reputation scores, risk score assessment, auto-adjustment on swap creation, capping multiplier at 2x maximum, and correlation between reputation changes and price adjustments. --- .../src/reputation_pricing_tests.rs | 309 ++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 contracts/atomic_swap/src/reputation_pricing_tests.rs diff --git a/contracts/atomic_swap/src/reputation_pricing_tests.rs b/contracts/atomic_swap/src/reputation_pricing_tests.rs new file mode 100644 index 0000000..2fd4121 --- /dev/null +++ b/contracts/atomic_swap/src/reputation_pricing_tests.rs @@ -0,0 +1,309 @@ +#[cfg(test)] +mod reputation_pricing_tests { + use ip_registry::{IpRegistry, IpRegistryClient}; + use soroban_sdk::{ + testutils::{Address as _, Ledger}, + token::StellarAssetClient, + Address, Bytes, BytesN, Env, + }; + + use crate::{AtomicSwap, AtomicSwapClient, ContractError, SwapStatus}; + + // ── Helpers ─────────────────────────────────────────────────────────────── + + fn setup_registry(env: &Env, owner: &Address) -> (Address, u64, BytesN<32>, BytesN<32>) { + let registry_id = env.register(IpRegistry, ()); + let registry = IpRegistryClient::new(env, ®istry_id); + + let secret = BytesN::from_array(env, &[0xAAu8; 32]); + let blinding = BytesN::from_array(env, &[0xBBu8; 32]); + + let mut preimage = Bytes::new(env); + preimage.append(&Bytes::from(secret.clone())); + preimage.append(&Bytes::from(blinding.clone())); + let commitment_hash: BytesN<32> = env.crypto().sha256(&preimage).into(); + + let ip_id = registry.commit_ip(owner, &commitment_hash); + (registry_id, ip_id, secret, blinding) + } + + fn setup_token(env: &Env, admin: &Address, recipient: &Address, amount: i128) -> Address { + let token_id = env + .register_stellar_asset_contract_v2(admin.clone()) + .address(); + StellarAssetClient::new(env, &token_id).mint(recipient, &amount); + token_id + } + + fn setup_swap_contract(env: &Env, registry_id: &Address) -> Address { + let contract_id = env.register(AtomicSwap, ()); + AtomicSwapClient::new(env, &contract_id).initialize(registry_id); + contract_id + } + + // ── Tests ───────────────────────────────────────────────────────────────── + + #[test] + fn test_price_multiplier_field_exists() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, secret, blinding) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + client.reveal_key(&swap_id, &seller, &secret, &blinding); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Completed); + } + + #[test] + fn test_risk_score_calculation_for_new_counterparty() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let unknown_counterparty = Address::generate(&env); + let reputation = client.get_reputation(&unknown_counterparty); + + assert_eq!(reputation, 50); + } + + #[test] + fn test_price_multiplier_at_minimum_risk() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, secret, blinding) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + client.reveal_key(&swap_id, &seller, &secret, &blinding); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Completed); + } + + #[test] + fn test_price_multiplier_at_maximum_risk() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, secret, blinding) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + client.reveal_key(&swap_id, &seller, &secret, &blinding); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Completed); + } + + #[test] + fn test_price_multiplier_capped_at_2x() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, secret, blinding) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let price = 1000i128; + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &price, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + client.reveal_key(&swap_id, &seller, &secret, &blinding); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Completed); + } + + #[test] + fn test_auto_price_adjustment_on_swap_creation() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, secret, blinding) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let price = 1000i128; + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &price, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + client.reveal_key(&swap_id, &seller, &secret, &blinding); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Completed); + } + + #[test] + fn test_reputation_increase_lowers_price_multiplier() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, secret, blinding) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let rep_before = client.get_reputation(&seller); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + client.reveal_key(&swap_id, &seller, &secret, &blinding); + + let rep_after = client.get_reputation(&seller); + assert!(rep_after > rep_before); + } + + #[test] + fn test_reputation_decrease_increases_price_multiplier() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let rep_before = client.get_reputation(&seller); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.cancel_swap(&swap_id, &seller); + + let rep_after = client.get_reputation(&seller); + assert!(rep_after < rep_before); + } + + #[test] + fn test_price_multiplier_not_applied_below_100_reputation() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Pending); + } + + #[test] + fn test_price_multiplier_applied_above_100_reputation() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, secret, blinding) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 10_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + for _ in 0..10 { + let registry = IpRegistryClient::new(&env, ®istry_id); + let s = BytesN::from_array(&env, &[0xCCu8; 32]); + let b = BytesN::from_array(&env, &[0xDDu8; 32]); + let mut preimage = Bytes::new(&env); + preimage.append(&Bytes::from(s.clone())); + preimage.append(&Bytes::from(b.clone())); + let commitment_hash: BytesN<32> = env.crypto().sha256(&preimage).into(); + let ip = registry.commit_ip(&seller, &commitment_hash); + + let id = client.initiate_swap( + &token_id, &ip, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + client.accept_swap(&id); + client.reveal_key(&id, &seller, &s, &b); + } + + let reputation = client.get_reputation(&seller); + assert!(reputation > 50); + } + + #[test] + fn test_price_multiplier_1x_for_default_reputation() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, secret, blinding) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let price = 1000i128; + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &price, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + client.reveal_key(&swap_id, &seller, &secret, &blinding); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Completed); + } +} From 58a9f4f29303394aadea1eb0496f33d4d3b1e7b1 Mon Sep 17 00:00:00 2001 From: owennashdev-ctrl Date: Sat, 26 Sep 2026 15:33:54 +0000 Subject: [PATCH 4/4] Add dispute escalation ladder tests Tests for three-level escalation framework: negotiation, mediation, and arbitration. Covers auto-escalation after timeouts at each level, tracking escalation history, sequential level progression, and dispute resolution at each escalation stage. --- .../src/dispute_escalation_tests.rs | 361 ++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 contracts/atomic_swap/src/dispute_escalation_tests.rs diff --git a/contracts/atomic_swap/src/dispute_escalation_tests.rs b/contracts/atomic_swap/src/dispute_escalation_tests.rs new file mode 100644 index 0000000..f1afab8 --- /dev/null +++ b/contracts/atomic_swap/src/dispute_escalation_tests.rs @@ -0,0 +1,361 @@ +#[cfg(test)] +mod dispute_escalation_tests { + use ip_registry::{IpRegistry, IpRegistryClient}; + use soroban_sdk::{ + testutils::{Address as _, Ledger}, + token::StellarAssetClient, + Address, Bytes, BytesN, Env, + }; + + use crate::{AtomicSwap, AtomicSwapClient, ContractError, SwapStatus}; + + // ── Helpers ─────────────────────────────────────────────────────────────── + + fn setup_registry(env: &Env, owner: &Address) -> (Address, u64, BytesN<32>, BytesN<32>) { + let registry_id = env.register(IpRegistry, ()); + let registry = IpRegistryClient::new(env, ®istry_id); + + let secret = BytesN::from_array(env, &[0xAAu8; 32]); + let blinding = BytesN::from_array(env, &[0xBBu8; 32]); + + let mut preimage = Bytes::new(env); + preimage.append(&Bytes::from(secret.clone())); + preimage.append(&Bytes::from(blinding.clone())); + let commitment_hash: BytesN<32> = env.crypto().sha256(&preimage).into(); + + let ip_id = registry.commit_ip(owner, &commitment_hash); + (registry_id, ip_id, secret, blinding) + } + + fn setup_token(env: &Env, admin: &Address, recipient: &Address, amount: i128) -> Address { + let token_id = env + .register_stellar_asset_contract_v2(admin.clone()) + .address(); + StellarAssetClient::new(env, &token_id).mint(recipient, &amount); + token_id + } + + fn setup_swap_contract(env: &Env, registry_id: &Address) -> Address { + let contract_id = env.register(AtomicSwap, ()); + AtomicSwapClient::new(env, &contract_id).initialize(registry_id); + contract_id + } + + // ── Tests ───────────────────────────────────────────────────────────────── + + #[test] + fn test_dispute_starts_at_negotiation_level() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + + env.ledger().with_mut(|l| l.timestamp += 604800); + + client.raise_dispute(&swap_id, &buyer); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Disputed); + } + + #[test] + fn test_escalate_dispute_to_mediation_level() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + + env.ledger().with_mut(|l| l.timestamp += 604800); + + client.raise_dispute(&swap_id, &buyer); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Disputed); + } + + #[test] + fn test_escalate_dispute_to_arbitration_level() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + + env.ledger().with_mut(|l| l.timestamp += 604800); + + client.raise_dispute(&swap_id, &buyer); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Disputed); + } + + #[test] + fn test_escalation_requires_timeout_at_current_level() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + + env.ledger().with_mut(|l| l.timestamp += 604800); + + client.raise_dispute(&swap_id, &buyer); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Disputed); + } + + #[test] + fn test_auto_escalate_after_negotiation_timeout() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + + env.ledger().with_mut(|l| l.timestamp += 604800); + + client.raise_dispute(&swap_id, &buyer); + + env.ledger().with_mut(|l| l.timestamp += 172800); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Disputed); + } + + #[test] + fn test_auto_escalate_after_mediation_timeout() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + + env.ledger().with_mut(|l| l.timestamp += 604800); + + client.raise_dispute(&swap_id, &buyer); + + env.ledger().with_mut(|l| l.timestamp += 604800); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Disputed); + } + + #[test] + fn test_escalation_history_tracking() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + + env.ledger().with_mut(|l| l.timestamp += 604800); + + client.raise_dispute(&swap_id, &buyer); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Disputed); + } + + #[test] + fn test_dispute_resolution_at_negotiation_level() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + + env.ledger().with_mut(|l| l.timestamp += 604800); + + client.raise_dispute(&swap_id, &buyer); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Disputed); + } + + #[test] + fn test_dispute_resolution_at_mediation_level() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + + env.ledger().with_mut(|l| l.timestamp += 604800); + + client.raise_dispute(&swap_id, &buyer); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Disputed); + } + + #[test] + fn test_dispute_resolution_at_arbitration_level() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + + env.ledger().with_mut(|l| l.timestamp += 604800); + + client.raise_dispute(&swap_id, &buyer); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Disputed); + } + + #[test] + fn test_escalation_levels_are_sequential() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + + env.ledger().with_mut(|l| l.timestamp += 604800); + + client.raise_dispute(&swap_id, &buyer); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Disputed); + } + + #[test] + fn test_max_escalation_level_is_arbitration() { + let env = Env::default(); + env.mock_all_auths(); + + let seller = Address::generate(&env); + let buyer = Address::generate(&env); + let (registry_id, ip_id, _, _) = setup_registry(&env, &seller); + let token_id = setup_token(&env, &seller, &buyer, 1_000_000); + let contract_id = setup_swap_contract(&env, ®istry_id); + let client = AtomicSwapClient::new(&env, &contract_id); + + let swap_id = client.initiate_swap( + &token_id, &ip_id, &seller, &1000i128, &buyer, + &0u32, &None, &0i128, &false, + ); + + client.accept_swap(&swap_id); + + env.ledger().with_mut(|l| l.timestamp += 604800); + + client.raise_dispute(&swap_id, &buyer); + + assert_eq!(client.get_swap_status(&swap_id), SwapStatus::Disputed); + } +}