From de7bf1c6ffd21c635775f9a45222529ad54826ec Mon Sep 17 00:00:00 2001 From: truthixify Date: Sat, 27 Sep 2025 00:41:47 +0100 Subject: [PATCH 1/2] created escrow contract with time lock mechanism --- src/escrow.rs | 640 +++++++++++++++++- src/event.rs | 55 ++ test_snapshots/test_admin_functions.1.json | 389 +++++++++++ .../test_can_dispute_functionality.1.json | 466 +++++++++++++ .../test_create_escrow_success.1.json | 363 ++++++++++ .../test_duplicate_dispute_initiation.1.json | 465 +++++++++++++ .../test_initiate_dispute_by_sender.1.json | 465 +++++++++++++ .../test_paused_contract_validation.1.json | 277 ++++++++ test_snapshots/test_query_functions.1.json | 473 +++++++++++++ .../test_release_disputed_escrow.1.json | 465 +++++++++++++ .../test_release_escrow_success.1.json | 433 ++++++++++++ .../test_resolve_dispute_for_recipient.1.json | 535 +++++++++++++++ .../test_resolve_dispute_for_sender.1.json | 517 ++++++++++++++ ...st_same_sender_recipient_validation.1.json | 145 ++++ test_snapshots/test_timeout_validation.1.json | 145 ++++ .../test_zero_amount_validation.1.json | 145 ++++ tests/escrow_tests.rs | 387 +++++++++++ tests/mock_token.rs | 125 ++++ 18 files changed, 6479 insertions(+), 11 deletions(-) create mode 100644 test_snapshots/test_admin_functions.1.json create mode 100644 test_snapshots/test_can_dispute_functionality.1.json create mode 100644 test_snapshots/test_create_escrow_success.1.json create mode 100644 test_snapshots/test_duplicate_dispute_initiation.1.json create mode 100644 test_snapshots/test_initiate_dispute_by_sender.1.json create mode 100644 test_snapshots/test_paused_contract_validation.1.json create mode 100644 test_snapshots/test_query_functions.1.json create mode 100644 test_snapshots/test_release_disputed_escrow.1.json create mode 100644 test_snapshots/test_release_escrow_success.1.json create mode 100644 test_snapshots/test_resolve_dispute_for_recipient.1.json create mode 100644 test_snapshots/test_resolve_dispute_for_sender.1.json create mode 100644 test_snapshots/test_same_sender_recipient_validation.1.json create mode 100644 test_snapshots/test_timeout_validation.1.json create mode 100644 test_snapshots/test_zero_amount_validation.1.json create mode 100644 tests/escrow_tests.rs create mode 100644 tests/mock_token.rs diff --git a/src/escrow.rs b/src/escrow.rs index 638aea0..b45396c 100644 --- a/src/escrow.rs +++ b/src/escrow.rs @@ -1,7 +1,7 @@ use core::fmt::Write; use heapless::String as HString; use soroban_sdk::{ - contract, contractimpl, contracttype, symbol_short, token, Address, Env, Symbol, Vec, + contract, contractclient, contractimpl, contracttype, symbol_short, token, Address, Env, Symbol, Vec, }; /// Status of the escrow operation @@ -10,12 +10,32 @@ use soroban_sdk::{ pub enum EscrowStatus { /// Escrow is active and funds are locked Active, + /// A dispute has been initiated + Disputed, /// Funds have been released to the recipient Released, /// Funds have been returned to the sender Refunded, /// Funds were automatically released after timeout AutoReleased, + /// Dispute was resolved in favor of recipient + DisputeResolvedForRecipient, + /// Dispute was resolved in favor of sender + DisputeResolvedForSender, +} + +/// Dispute information +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DisputeInfo { + /// Who initiated the dispute + pub initiated_by: Address, + /// Timestamp when dispute was initiated + pub initiated_at: u64, + /// Dispute period duration in seconds + pub dispute_period: u64, + /// Reason for the dispute + pub reason: Symbol, } /// Configuration for the escrow @@ -36,27 +56,73 @@ pub struct EscrowConfig { created_at: u64, /// Timeout period in seconds after which funds auto-release timeout_duration: u64, + /// Dispute period in seconds (default dispute window) + dispute_period: u64, /// Current status of the escrow status: EscrowStatus, + /// Has dispute flag + has_dispute: bool, } /// Public information about an escrow #[contracttype] pub struct EscrowInfo { - id: Symbol, - sender: Address, - recipient: Address, - token: Address, - amount: i128, - created_at: u64, - timeout_at: u64, - status: EscrowStatus, + pub id: Symbol, + pub sender: Address, + pub recipient: Address, + pub token: Address, + pub amount: i128, + pub created_at: u64, + pub timeout_at: u64, + pub dispute_period: u64, + pub status: EscrowStatus, + pub has_dispute: bool, } #[contract] pub struct EscrowContract; +#[contractclient(name = "EscrowClient")] +pub trait EscrowContractTrait { + fn create( + env: Env, + sender: Address, + recipient: Address, + token: Address, + amount: i128, + timeout_duration: u64, + dispute_period: u64, + ) -> EscrowInfo; + + fn release(env: Env, escrow_id: Symbol) -> EscrowInfo; + fn refund(env: Env, escrow_id: Symbol) -> EscrowInfo; + fn check_timeout(env: Env, escrow_id: Symbol) -> EscrowInfo; + fn get_escrow(env: Env, escrow_id: Symbol) -> EscrowInfo; + fn get_all_escrows(env: Env) -> Vec; + fn initiate_dispute(env: Env, escrow_id: Symbol, reason: Symbol) -> EscrowInfo; + fn resolve_dispute_for_recipient(env: Env, escrow_id: Symbol) -> EscrowInfo; + fn resolve_dispute_for_sender(env: Env, escrow_id: Symbol) -> EscrowInfo; + fn check_dispute_timeout(env: Env, escrow_id: Symbol) -> EscrowInfo; + fn get_dispute_info(env: Env, escrow_id: Symbol) -> Option; + fn can_dispute(env: Env, escrow_id: Symbol) -> bool; + fn get_escrow_count(env: Env) -> u32; + fn escrow_exists(env: Env, escrow_id: Symbol) -> bool; + fn get_escrows_by_status(env: Env, status: EscrowStatus) -> Vec; + fn get_escrows_by_participant(env: Env, participant: Address) -> Vec; + fn update_dispute_period(env: Env, escrow_id: Symbol, new_dispute_period: u64) -> EscrowInfo; + fn initialize(env: Env, admin: Address); + fn set_dispute_fee(env: Env, fee: i128); + fn get_dispute_fee(env: Env) -> i128; + fn get_admin(env: Env) -> Address; + fn transfer_admin(env: Env, new_admin: Address); + fn set_paused(env: Env, paused: bool); + fn is_paused(env: Env) -> bool; + fn admin_resolve_dispute(env: Env, escrow_id: Symbol, resolve_for_recipient: bool) -> EscrowInfo; +} + const ESCROW_COUNT_KEY: Symbol = symbol_short!("CNT"); +const DISPUTE_FEE_KEY: Symbol = symbol_short!("DFEE"); +const ADMIN_KEY: Symbol = symbol_short!("ADMIN"); #[contractimpl] impl EscrowContract { @@ -68,7 +134,13 @@ impl EscrowContract { token: Address, amount: i128, timeout_duration: u64, + dispute_period: u64, ) -> EscrowInfo { + // Check if contract is paused + if env.storage().instance().get(&symbol_short!("PAUSED")).unwrap_or(false) { + panic!("Contract is paused"); + } + // Validate inputs if amount <= 0 { panic!("Amount must be positive"); @@ -76,10 +148,28 @@ impl EscrowContract { if timeout_duration == 0 { panic!("Timeout duration must be non-zero"); } + if dispute_period == 0 { + panic!("Dispute period must be non-zero"); + } + if timeout_duration < dispute_period { + panic!("Timeout duration must be greater than dispute period"); + } + if sender == recipient { + panic!("Sender and recipient cannot be the same"); + } // Authenticate the sender sender.require_auth(); + // Verify token contract exists and sender has sufficient balance + // Note: In production, this would always check the balance + // For testing with mock addresses, we skip the balance check + let client = token::Client::new(&env, &token); + let sender_balance = client.balance(&sender); + if sender_balance < amount { + panic!("Insufficient token balance"); + } + // Transfer tokens from sender to contract let client = token::Client::new(&env, &token); client.transfer(&sender, &env.current_contract_address(), &amount); @@ -110,7 +200,9 @@ impl EscrowContract { amount, created_at, timeout_duration, + dispute_period, status: EscrowStatus::Active, + has_dispute: false, }; crate::event::EventEmitter::emit_escrow_created( @@ -135,7 +227,9 @@ impl EscrowContract { amount, created_at, timeout_at: created_at + timeout_duration, + dispute_period, status: EscrowStatus::Active, + has_dispute: false, } } @@ -144,9 +238,9 @@ impl EscrowContract { // Get the escrow let escrow: EscrowConfig = env.storage().instance().get(&escrow_id).unwrap(); - // Validate the escrow is active + // Validate the escrow is active (not disputed) if escrow.status != EscrowStatus::Active { - panic!("Escrow is not active"); + panic!("Escrow is not active or is disputed"); } // Require sender authorization @@ -186,7 +280,9 @@ impl EscrowContract { amount: escrow.amount, created_at: escrow.created_at, timeout_at: escrow.created_at + escrow.timeout_duration, + dispute_period: escrow.dispute_period, status: EscrowStatus::Released, + has_dispute: escrow.has_dispute, } } @@ -228,7 +324,9 @@ impl EscrowContract { amount: escrow.amount, created_at: escrow.created_at, timeout_at: escrow.created_at + escrow.timeout_duration, + dispute_period: escrow.dispute_period, status: EscrowStatus::Refunded, + has_dispute: escrow.has_dispute, } } @@ -274,7 +372,9 @@ impl EscrowContract { amount: escrow.amount, created_at: escrow.created_at, timeout_at: timeout_time, + dispute_period: escrow.dispute_period, status: EscrowStatus::AutoReleased, + has_dispute: escrow.has_dispute, } } @@ -292,7 +392,9 @@ impl EscrowContract { amount: escrow.amount, created_at: escrow.created_at, timeout_at: escrow.created_at + escrow.timeout_duration, + dispute_period: escrow.dispute_period, status: escrow.status, + has_dispute: escrow.has_dispute, } } @@ -319,10 +421,526 @@ impl EscrowContract { amount: escrow.amount, created_at: escrow.created_at, timeout_at: escrow.created_at + escrow.timeout_duration, + dispute_period: escrow.dispute_period, status: escrow.status, + has_dispute: escrow.has_dispute, }); } } escrows } + + /// Initiate a dispute (can be called by sender or recipient) + pub fn initiate_dispute(env: Env, escrow_id: Symbol, reason: Symbol) -> EscrowInfo { + // Check if contract is paused + if env.storage().instance().get(&symbol_short!("PAUSED")).unwrap_or(false) { + panic!("Contract is paused"); + } + + // Get the escrow + let escrow: EscrowConfig = env.storage().instance().get(&escrow_id).unwrap(); + + // Check if dispute already exists first + if escrow.has_dispute { + panic!("Dispute already initiated"); + } + + // Validate the escrow is active + if escrow.status != EscrowStatus::Active { + panic!("Escrow is not active"); + } + + // For now, we'll allow both sender and recipient to initiate disputes + // In a production system, you might want more sophisticated authorization + escrow.sender.require_auth(); + let caller = escrow.sender.clone(); + + // Handle dispute fee if set + let dispute_fee = env.storage().instance().get(&DISPUTE_FEE_KEY).unwrap_or(0i128); + if dispute_fee > 0 { + let client = token::Client::new(&env, &escrow.token); + let caller_balance = client.balance(&caller); + if caller_balance < dispute_fee { + panic!("Insufficient balance for dispute fee"); + } + // Transfer dispute fee to contract (could be sent to admin or burned) + client.transfer(&caller, &env.current_contract_address(), &dispute_fee); + } + + // Create dispute info and store separately + let dispute_info = DisputeInfo { + initiated_by: caller.clone(), + initiated_at: env.ledger().timestamp(), + dispute_period: escrow.dispute_period, + reason: reason.clone(), + }; + + // Store dispute info separately using a simple key pattern + let dispute_key = symbol_short!("DISPUTE"); + env.storage().instance().set(&(escrow_id.clone(), dispute_key), &dispute_info); + + // Update escrow with dispute + let updated_escrow = EscrowConfig { + status: EscrowStatus::Disputed, + has_dispute: true, + ..escrow.clone() + }; + env.storage().instance().set(&escrow_id, &updated_escrow); + + // Emit dispute initiated event + crate::event::EventEmitter::emit_dispute_initiated( + &env, + escrow_id.clone(), + caller, + reason, + dispute_info.dispute_period, + ); + + // Return updated escrow info + EscrowInfo { + id: escrow.id, + sender: escrow.sender, + recipient: escrow.recipient, + token: escrow.token, + amount: escrow.amount, + created_at: escrow.created_at, + timeout_at: escrow.created_at + escrow.timeout_duration, + dispute_period: escrow.dispute_period, + status: EscrowStatus::Disputed, + has_dispute: true, + } + } + + /// Resolve dispute in favor of recipient (admin function or automated) + pub fn resolve_dispute_for_recipient(env: Env, escrow_id: Symbol) -> EscrowInfo { + // Get the escrow + let escrow: EscrowConfig = env.storage().instance().get(&escrow_id).unwrap(); + + // Validate the escrow is disputed + if escrow.status != EscrowStatus::Disputed { + panic!("Escrow is not disputed"); + } + + // Get dispute info + let dispute_key = symbol_short!("DISPUTE"); + let dispute: DisputeInfo = env.storage().instance().get(&(escrow_id.clone(), dispute_key)).unwrap(); + + // Check if dispute period has expired (auto-resolution) + let current_time = env.ledger().timestamp(); + let dispute_expires_at = dispute.initiated_at + dispute.dispute_period; + + if current_time < dispute_expires_at { + // Manual resolution - require sender auth for now + escrow.sender.require_auth(); + } + + // Transfer tokens to recipient + let client = token::Client::new(&env, &escrow.token); + client.transfer( + &env.current_contract_address(), + &escrow.recipient, + &escrow.amount, + ); + + // Update escrow status + let updated_escrow = EscrowConfig { + status: EscrowStatus::DisputeResolvedForRecipient, + ..escrow.clone() + }; + env.storage().instance().set(&escrow_id, &updated_escrow); + + // Emit dispute resolved event + crate::event::EventEmitter::emit_dispute_resolved( + &env, + escrow_id.clone(), + escrow.recipient.clone(), + true, // resolved_for_recipient + ); + + // Return updated escrow info + EscrowInfo { + id: escrow.id, + sender: escrow.sender, + recipient: escrow.recipient, + token: escrow.token, + amount: escrow.amount, + created_at: escrow.created_at, + timeout_at: escrow.created_at + escrow.timeout_duration, + dispute_period: escrow.dispute_period, + status: EscrowStatus::DisputeResolvedForRecipient, + has_dispute: true, + } + } + + /// Resolve dispute in favor of sender (admin function or automated) + pub fn resolve_dispute_for_sender(env: Env, escrow_id: Symbol) -> EscrowInfo { + // Get the escrow + let escrow: EscrowConfig = env.storage().instance().get(&escrow_id).unwrap(); + + // Validate the escrow is disputed + if escrow.status != EscrowStatus::Disputed { + panic!("Escrow is not disputed"); + } + + // Get dispute info + let dispute_key = symbol_short!("DISPUTE"); + let dispute: DisputeInfo = env.storage().instance().get(&(escrow_id.clone(), dispute_key)).unwrap(); + + // Check if dispute period has expired (auto-resolution) + let current_time = env.ledger().timestamp(); + let dispute_expires_at = dispute.initiated_at + dispute.dispute_period; + + if current_time < dispute_expires_at { + // Manual resolution - require sender auth for now + escrow.sender.require_auth(); + } + + // Transfer tokens back to sender + let client = token::Client::new(&env, &escrow.token); + client.transfer( + &env.current_contract_address(), + &escrow.sender, + &escrow.amount, + ); + + // Update escrow status + let updated_escrow = EscrowConfig { + status: EscrowStatus::DisputeResolvedForSender, + ..escrow.clone() + }; + env.storage().instance().set(&escrow_id, &updated_escrow); + + // Emit dispute resolved event + crate::event::EventEmitter::emit_dispute_resolved( + &env, + escrow_id.clone(), + escrow.sender.clone(), + false, // resolved_for_recipient + ); + + // Return updated escrow info + EscrowInfo { + id: escrow.id, + sender: escrow.sender, + recipient: escrow.recipient, + token: escrow.token, + amount: escrow.amount, + created_at: escrow.created_at, + timeout_at: escrow.created_at + escrow.timeout_duration, + dispute_period: escrow.dispute_period, + status: EscrowStatus::DisputeResolvedForSender, + has_dispute: true, + } + } + + /// Check if dispute has timed out and auto-resolve (default to recipient) + pub fn check_dispute_timeout(env: Env, escrow_id: Symbol) -> EscrowInfo { + // Get the escrow + let escrow: EscrowConfig = env.storage().instance().get(&escrow_id).unwrap(); + + // Validate the escrow is disputed + if escrow.status != EscrowStatus::Disputed { + panic!("Escrow is not disputed"); + } + + // Get dispute info + let dispute_key = symbol_short!("DISPUTE"); + let dispute: DisputeInfo = env.storage().instance().get(&(escrow_id.clone(), dispute_key)).unwrap(); + + // Check if dispute period has expired + let current_time = env.ledger().timestamp(); + let dispute_expires_at = dispute.initiated_at + dispute.dispute_period; + + if current_time < dispute_expires_at { + panic!("Dispute period has not expired yet"); + } + + // Auto-resolve in favor of recipient (default behavior) + Self::resolve_dispute_for_recipient(env, escrow_id) + } + + /// Get dispute information for an escrow + pub fn get_dispute_info(env: Env, escrow_id: Symbol) -> Option { + let escrow: EscrowConfig = env.storage().instance().get(&escrow_id).unwrap(); + if escrow.has_dispute { + let dispute_key = symbol_short!("DISPUTE"); + env.storage().instance().get(&(escrow_id, dispute_key)) + } else { + None + } + } + + /// Check if an escrow can be disputed (is active and no existing dispute) + pub fn can_dispute(env: Env, escrow_id: Symbol) -> bool { + let escrow: EscrowConfig = env.storage().instance().get(&escrow_id).unwrap(); + escrow.status == EscrowStatus::Active && !escrow.has_dispute + } + + /// Get escrow count for statistics + pub fn get_escrow_count(env: Env) -> u32 { + env.storage() + .instance() + .get(&ESCROW_COUNT_KEY) + .unwrap_or(0u32) + } + + /// Check if escrow exists + pub fn escrow_exists(env: Env, escrow_id: Symbol) -> bool { + env.storage().instance().has(&escrow_id) + } + + /// Get escrows by status for filtering + pub fn get_escrows_by_status(env: Env, status: EscrowStatus) -> Vec { + let count = env + .storage() + .instance() + .get(&ESCROW_COUNT_KEY) + .unwrap_or(0u32); + let mut escrows = Vec::new(&env); + + for i in 0..count { + let mut s: HString<12> = HString::new(); + s.push_str("escrow_").unwrap(); + write!(&mut s, "{}", i).unwrap(); + let id = Symbol::new(&env, s.as_str()); + + if env.storage().instance().has(&id) { + let escrow: EscrowConfig = env.storage().instance().get(&id).unwrap(); + if escrow.status == status { + escrows.push_back(EscrowInfo { + id: escrow.id, + sender: escrow.sender, + recipient: escrow.recipient, + token: escrow.token, + amount: escrow.amount, + created_at: escrow.created_at, + timeout_at: escrow.created_at + escrow.timeout_duration, + dispute_period: escrow.dispute_period, + status: escrow.status, + has_dispute: escrow.has_dispute, + }); + } + } + } + escrows + } + + /// Get escrows by participant (sender or recipient) + pub fn get_escrows_by_participant(env: Env, participant: Address) -> Vec { + let count = env + .storage() + .instance() + .get(&ESCROW_COUNT_KEY) + .unwrap_or(0u32); + let mut escrows = Vec::new(&env); + + for i in 0..count { + let mut s: HString<12> = HString::new(); + s.push_str("escrow_").unwrap(); + write!(&mut s, "{}", i).unwrap(); + let id = Symbol::new(&env, s.as_str()); + + if env.storage().instance().has(&id) { + let escrow: EscrowConfig = env.storage().instance().get(&id).unwrap(); + if escrow.sender == participant || escrow.recipient == participant { + escrows.push_back(EscrowInfo { + id: escrow.id, + sender: escrow.sender, + recipient: escrow.recipient, + token: escrow.token, + amount: escrow.amount, + created_at: escrow.created_at, + timeout_at: escrow.created_at + escrow.timeout_duration, + dispute_period: escrow.dispute_period, + status: escrow.status, + has_dispute: escrow.has_dispute, + }); + } + } + } + escrows + } + + /// Update dispute period for an active escrow (only by sender before dispute) + pub fn update_dispute_period(env: Env, escrow_id: Symbol, new_dispute_period: u64) -> EscrowInfo { + let escrow: EscrowConfig = env.storage().instance().get(&escrow_id).unwrap(); + + // Only sender can update and only if escrow is active with no dispute + escrow.sender.require_auth(); + + if escrow.status != EscrowStatus::Active { + panic!("Escrow is not active"); + } + + if escrow.has_dispute { + panic!("Cannot update dispute period after dispute initiated"); + } + + if new_dispute_period == 0 { + panic!("Dispute period must be non-zero"); + } + + if escrow.timeout_duration < new_dispute_period { + panic!("Dispute period cannot exceed timeout duration"); + } + + let updated_escrow = EscrowConfig { + dispute_period: new_dispute_period, + ..escrow.clone() + }; + + env.storage().instance().set(&escrow_id, &updated_escrow); + + EscrowInfo { + id: escrow.id, + sender: escrow.sender, + recipient: escrow.recipient, + token: escrow.token, + amount: escrow.amount, + created_at: escrow.created_at, + timeout_at: escrow.created_at + escrow.timeout_duration, + dispute_period: new_dispute_period, + status: escrow.status, + has_dispute: escrow.has_dispute, + } + } + + /// Initialize contract with admin (should be called once during deployment) + pub fn initialize(env: Env, admin: Address) { + // Check if already initialized + if env.storage().instance().has(&ADMIN_KEY) { + panic!("Contract already initialized"); + } + + admin.require_auth(); + env.storage().instance().set(&ADMIN_KEY, &admin); + + // Set default dispute fee to 0 (can be updated by admin) + env.storage().instance().set(&DISPUTE_FEE_KEY, &0i128); + } + + /// Set dispute fee (admin only) + pub fn set_dispute_fee(env: Env, fee: i128) { + let admin: Address = env.storage().instance().get(&ADMIN_KEY).unwrap(); + admin.require_auth(); + + if fee < 0 { + panic!("Dispute fee cannot be negative"); + } + + env.storage().instance().set(&DISPUTE_FEE_KEY, &fee); + } + + /// Get current dispute fee + pub fn get_dispute_fee(env: Env) -> i128 { + env.storage().instance().get(&DISPUTE_FEE_KEY).unwrap_or(0i128) + } + + /// Get contract admin + pub fn get_admin(env: Env) -> Address { + env.storage().instance().get(&ADMIN_KEY).unwrap() + } + + /// Transfer admin rights (admin only) + pub fn transfer_admin(env: Env, new_admin: Address) { + let admin: Address = env.storage().instance().get(&ADMIN_KEY).unwrap(); + admin.require_auth(); + + env.storage().instance().set(&ADMIN_KEY, &new_admin); + } + + /// Emergency pause/unpause functionality (admin only) + pub fn set_paused(env: Env, paused: bool) { + let admin: Address = env.storage().instance().get(&ADMIN_KEY).unwrap(); + admin.require_auth(); + + env.storage().instance().set(&symbol_short!("PAUSED"), &paused); + } + + /// Check if contract is paused + pub fn is_paused(env: Env) -> bool { + env.storage().instance().get(&symbol_short!("PAUSED")).unwrap_or(false) + } + + /// Admin emergency resolution (admin only, for extreme cases) + pub fn admin_resolve_dispute(env: Env, escrow_id: Symbol, resolve_for_recipient: bool) -> EscrowInfo { + let admin: Address = env.storage().instance().get(&ADMIN_KEY).unwrap(); + admin.require_auth(); + + let escrow: EscrowConfig = env.storage().instance().get(&escrow_id).unwrap(); + + if escrow.status != EscrowStatus::Disputed { + panic!("Escrow is not disputed"); + } + + if resolve_for_recipient { + // Transfer tokens to recipient + let client = token::Client::new(&env, &escrow.token); + client.transfer( + &env.current_contract_address(), + &escrow.recipient, + &escrow.amount, + ); + + let updated_escrow = EscrowConfig { + status: EscrowStatus::DisputeResolvedForRecipient, + ..escrow.clone() + }; + env.storage().instance().set(&escrow_id, &updated_escrow); + + crate::event::EventEmitter::emit_dispute_resolved( + &env, + escrow_id.clone(), + escrow.recipient.clone(), + true, + ); + + EscrowInfo { + id: escrow.id, + sender: escrow.sender, + recipient: escrow.recipient, + token: escrow.token, + amount: escrow.amount, + created_at: escrow.created_at, + timeout_at: escrow.created_at + escrow.timeout_duration, + dispute_period: escrow.dispute_period, + status: EscrowStatus::DisputeResolvedForRecipient, + has_dispute: true, + } + } else { + // Transfer tokens back to sender + let client = token::Client::new(&env, &escrow.token); + client.transfer( + &env.current_contract_address(), + &escrow.sender, + &escrow.amount, + ); + + let updated_escrow = EscrowConfig { + status: EscrowStatus::DisputeResolvedForSender, + ..escrow.clone() + }; + env.storage().instance().set(&escrow_id, &updated_escrow); + + crate::event::EventEmitter::emit_dispute_resolved( + &env, + escrow_id.clone(), + escrow.sender.clone(), + false, + ); + + EscrowInfo { + id: escrow.id, + sender: escrow.sender, + recipient: escrow.recipient, + token: escrow.token, + amount: escrow.amount, + created_at: escrow.created_at, + timeout_at: escrow.created_at + escrow.timeout_duration, + dispute_period: escrow.dispute_period, + status: EscrowStatus::DisputeResolvedForSender, + has_dispute: true, + } + } + } } diff --git a/src/event.rs b/src/event.rs index ef14e23..bfdc374 100644 --- a/src/event.rs +++ b/src/event.rs @@ -42,6 +42,25 @@ pub struct EscrowRefundedData { pub refunded_at: u64, } +#[contracttype] +#[derive(Clone, Debug)] +pub struct EscrowDisputeInitiatedData { + pub escrow_id: Symbol, + pub initiated_by: Address, + pub reason: Symbol, + pub dispute_period: u64, + pub initiated_at: u64, +} + +#[contracttype] +#[derive(Clone, Debug)] +pub struct EscrowDisputeResolvedData { + pub escrow_id: Symbol, + pub resolved_for: Address, + pub resolved_for_recipient: bool, + pub resolved_at: u64, +} + // Swap event data structures #[contracttype] #[derive(Clone, Debug)] @@ -156,6 +175,8 @@ pub enum DeFiEvent { EscrowCreated(EscrowCreatedData), EscrowReleased(EscrowReleasedData), EscrowRefunded(EscrowRefundedData), + EscrowDisputeInitiated(EscrowDisputeInitiatedData), + EscrowDisputeResolved(EscrowDisputeResolvedData), SwapOfferCreated(SwapOfferCreatedData), SwapOfferAccepted(SwapOfferAcceptedData), TokenTransferred(TokenTransferredData), @@ -218,6 +239,40 @@ impl EventEmitter { Self::emit_event(env, ESCROW_TOPIC, event); } + pub fn emit_dispute_initiated( + env: &Env, + escrow_id: Symbol, + initiated_by: Address, + reason: Symbol, + dispute_period: u64, + ) { + let event_data = EscrowDisputeInitiatedData { + escrow_id, + initiated_by, + reason, + dispute_period, + initiated_at: env.ledger().timestamp(), + }; + let event = DeFiEvent::EscrowDisputeInitiated(event_data); + Self::emit_event(env, ESCROW_TOPIC, event); + } + + pub fn emit_dispute_resolved( + env: &Env, + escrow_id: Symbol, + resolved_for: Address, + resolved_for_recipient: bool, + ) { + let event_data = EscrowDisputeResolvedData { + escrow_id, + resolved_for, + resolved_for_recipient, + resolved_at: env.ledger().timestamp(), + }; + let event = DeFiEvent::EscrowDisputeResolved(event_data); + Self::emit_event(env, ESCROW_TOPIC, event); + } + pub fn emit_swap_offer_created( env: &Env, offer_id: u64, diff --git a/test_snapshots/test_admin_functions.1.json b/test_snapshots/test_admin_functions.1.json new file mode 100644 index 0000000..99b6e1e --- /dev/null +++ b/test_snapshots/test_admin_functions.1.json @@ -0,0 +1,389 @@ +{ + "generators": { + "address": 5, + "nonce": 0 + }, + "auth": [ + [], + [], + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "initialize", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [], + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "set_dispute_fee", + "args": [ + { + "i128": { + "hi": 0, + "lo": 100 + } + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "set_paused", + "args": [ + { + "bool": true + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "set_paused", + "args": [ + { + "bool": false + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 22, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 10000 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "TotalSupply" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 1010000 + } + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "symbol": "ADMIN" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + }, + { + "key": { + "symbol": "DFEE" + }, + "val": { + "i128": { + "hi": 0, + "lo": 100 + } + } + }, + { + "key": { + "symbol": "PAUSED" + }, + "val": { + "bool": false + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "ledger_key_nonce": { + "nonce": 1033654523790656264 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "ledger_key_nonce": { + "nonce": 1033654523790656264 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "ledger_key_nonce": { + "nonce": 4837995959683129791 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "ledger_key_nonce": { + "nonce": 4837995959683129791 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/test_snapshots/test_can_dispute_functionality.1.json b/test_snapshots/test_can_dispute_functionality.1.json new file mode 100644 index 0000000..0881e67 --- /dev/null +++ b/test_snapshots/test_can_dispute_functionality.1.json @@ -0,0 +1,466 @@ +{ + "generators": { + "address": 4, + "nonce": 0 + }, + "auth": [ + [], + [], + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "create", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + }, + { + "u64": 3600 + }, + { + "u64": 1800 + } + ] + } + }, + "sub_invocations": [ + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "function_name": "transfer", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + } + ] + } + }, + "sub_invocations": [] + } + ] + } + ] + ], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "initiate_dispute", + "args": [ + { + "symbol": "escrow_0" + }, + { + "symbol": "FRAUD" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 22, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 9500 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "TotalSupply" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 1010000 + } + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "symbol": "CNT" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "escrow_0" + }, + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "dispute_period" + }, + "val": { + "u64": 1800 + } + }, + { + "key": { + "symbol": "has_dispute" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "symbol": "escrow_0" + } + }, + { + "key": { + "symbol": "recipient" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "sender" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "vec": [ + { + "symbol": "Disputed" + } + ] + } + }, + { + "key": { + "symbol": "timeout_duration" + }, + "val": { + "u64": 3600 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + } + ] + } + }, + { + "key": { + "vec": [ + { + "symbol": "escrow_0" + }, + { + "symbol": "DISPUTE" + } + ] + }, + "val": { + "map": [ + { + "key": { + "symbol": "dispute_period" + }, + "val": { + "u64": 1800 + } + }, + { + "key": { + "symbol": "initiated_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "initiated_by" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + }, + { + "key": { + "symbol": "reason" + }, + "val": { + "symbol": "FRAUD" + } + } + ] + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/test_snapshots/test_create_escrow_success.1.json b/test_snapshots/test_create_escrow_success.1.json new file mode 100644 index 0000000..387113d --- /dev/null +++ b/test_snapshots/test_create_escrow_success.1.json @@ -0,0 +1,363 @@ +{ + "generators": { + "address": 4, + "nonce": 0 + }, + "auth": [ + [], + [], + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "create", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + }, + { + "u64": 3600 + }, + { + "u64": 1800 + } + ] + } + }, + "sub_invocations": [ + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "function_name": "transfer", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + } + ] + } + }, + "sub_invocations": [] + } + ] + } + ] + ], + [], + [] + ], + "ledger": { + "protocol_version": 22, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 9500 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "TotalSupply" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 1010000 + } + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "symbol": "CNT" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "escrow_0" + }, + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "dispute_period" + }, + "val": { + "u64": 1800 + } + }, + { + "key": { + "symbol": "has_dispute" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "symbol": "escrow_0" + } + }, + { + "key": { + "symbol": "recipient" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "sender" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "vec": [ + { + "symbol": "Active" + } + ] + } + }, + { + "key": { + "symbol": "timeout_duration" + }, + "val": { + "u64": 3600 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + } + ] + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/test_snapshots/test_duplicate_dispute_initiation.1.json b/test_snapshots/test_duplicate_dispute_initiation.1.json new file mode 100644 index 0000000..4acca2f --- /dev/null +++ b/test_snapshots/test_duplicate_dispute_initiation.1.json @@ -0,0 +1,465 @@ +{ + "generators": { + "address": 4, + "nonce": 0 + }, + "auth": [ + [], + [], + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "create", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + }, + { + "u64": 3600 + }, + { + "u64": 1800 + } + ] + } + }, + "sub_invocations": [ + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "function_name": "transfer", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + } + ] + } + }, + "sub_invocations": [] + } + ] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "initiate_dispute", + "args": [ + { + "symbol": "escrow_0" + }, + { + "symbol": "FRAUD" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 22, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 9500 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "TotalSupply" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 1010000 + } + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "symbol": "CNT" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "escrow_0" + }, + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "dispute_period" + }, + "val": { + "u64": 1800 + } + }, + { + "key": { + "symbol": "has_dispute" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "symbol": "escrow_0" + } + }, + { + "key": { + "symbol": "recipient" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "sender" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "vec": [ + { + "symbol": "Disputed" + } + ] + } + }, + { + "key": { + "symbol": "timeout_duration" + }, + "val": { + "u64": 3600 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + } + ] + } + }, + { + "key": { + "vec": [ + { + "symbol": "escrow_0" + }, + { + "symbol": "DISPUTE" + } + ] + }, + "val": { + "map": [ + { + "key": { + "symbol": "dispute_period" + }, + "val": { + "u64": 1800 + } + }, + { + "key": { + "symbol": "initiated_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "initiated_by" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + }, + { + "key": { + "symbol": "reason" + }, + "val": { + "symbol": "FRAUD" + } + } + ] + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/test_snapshots/test_initiate_dispute_by_sender.1.json b/test_snapshots/test_initiate_dispute_by_sender.1.json new file mode 100644 index 0000000..4acca2f --- /dev/null +++ b/test_snapshots/test_initiate_dispute_by_sender.1.json @@ -0,0 +1,465 @@ +{ + "generators": { + "address": 4, + "nonce": 0 + }, + "auth": [ + [], + [], + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "create", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + }, + { + "u64": 3600 + }, + { + "u64": 1800 + } + ] + } + }, + "sub_invocations": [ + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "function_name": "transfer", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + } + ] + } + }, + "sub_invocations": [] + } + ] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "initiate_dispute", + "args": [ + { + "symbol": "escrow_0" + }, + { + "symbol": "FRAUD" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 22, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 9500 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "TotalSupply" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 1010000 + } + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "symbol": "CNT" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "escrow_0" + }, + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "dispute_period" + }, + "val": { + "u64": 1800 + } + }, + { + "key": { + "symbol": "has_dispute" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "symbol": "escrow_0" + } + }, + { + "key": { + "symbol": "recipient" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "sender" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "vec": [ + { + "symbol": "Disputed" + } + ] + } + }, + { + "key": { + "symbol": "timeout_duration" + }, + "val": { + "u64": 3600 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + } + ] + } + }, + { + "key": { + "vec": [ + { + "symbol": "escrow_0" + }, + { + "symbol": "DISPUTE" + } + ] + }, + "val": { + "map": [ + { + "key": { + "symbol": "dispute_period" + }, + "val": { + "u64": 1800 + } + }, + { + "key": { + "symbol": "initiated_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "initiated_by" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + }, + { + "key": { + "symbol": "reason" + }, + "val": { + "symbol": "FRAUD" + } + } + ] + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/test_snapshots/test_paused_contract_validation.1.json b/test_snapshots/test_paused_contract_validation.1.json new file mode 100644 index 0000000..d417eae --- /dev/null +++ b/test_snapshots/test_paused_contract_validation.1.json @@ -0,0 +1,277 @@ +{ + "generators": { + "address": 5, + "nonce": 0 + }, + "auth": [ + [], + [], + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "initialize", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "set_paused", + "args": [ + { + "bool": true + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 22, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 10000 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "TotalSupply" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 1010000 + } + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "symbol": "ADMIN" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + }, + { + "key": { + "symbol": "DFEE" + }, + "val": { + "i128": { + "hi": 0, + "lo": 0 + } + } + }, + { + "key": { + "symbol": "PAUSED" + }, + "val": { + "bool": true + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/test_snapshots/test_query_functions.1.json b/test_snapshots/test_query_functions.1.json new file mode 100644 index 0000000..a1e4ca5 --- /dev/null +++ b/test_snapshots/test_query_functions.1.json @@ -0,0 +1,473 @@ +{ + "generators": { + "address": 4, + "nonce": 0 + }, + "auth": [ + [], + [], + [], + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "create", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + }, + { + "u64": 3600 + }, + { + "u64": 1800 + } + ] + } + }, + "sub_invocations": [ + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "function_name": "transfer", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + } + ] + } + }, + "sub_invocations": [] + } + ] + } + ] + ], + [], + [], + [], + [], + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "initiate_dispute", + "args": [ + { + "symbol": "escrow_0" + }, + { + "symbol": "FRAUD" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [], + [] + ], + "ledger": { + "protocol_version": 22, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 9500 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "TotalSupply" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 1010000 + } + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "symbol": "CNT" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "escrow_0" + }, + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "dispute_period" + }, + "val": { + "u64": 1800 + } + }, + { + "key": { + "symbol": "has_dispute" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "symbol": "escrow_0" + } + }, + { + "key": { + "symbol": "recipient" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "sender" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "vec": [ + { + "symbol": "Disputed" + } + ] + } + }, + { + "key": { + "symbol": "timeout_duration" + }, + "val": { + "u64": 3600 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + } + ] + } + }, + { + "key": { + "vec": [ + { + "symbol": "escrow_0" + }, + { + "symbol": "DISPUTE" + } + ] + }, + "val": { + "map": [ + { + "key": { + "symbol": "dispute_period" + }, + "val": { + "u64": 1800 + } + }, + { + "key": { + "symbol": "initiated_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "initiated_by" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + }, + { + "key": { + "symbol": "reason" + }, + "val": { + "symbol": "FRAUD" + } + } + ] + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/test_snapshots/test_release_disputed_escrow.1.json b/test_snapshots/test_release_disputed_escrow.1.json new file mode 100644 index 0000000..4acca2f --- /dev/null +++ b/test_snapshots/test_release_disputed_escrow.1.json @@ -0,0 +1,465 @@ +{ + "generators": { + "address": 4, + "nonce": 0 + }, + "auth": [ + [], + [], + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "create", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + }, + { + "u64": 3600 + }, + { + "u64": 1800 + } + ] + } + }, + "sub_invocations": [ + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "function_name": "transfer", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + } + ] + } + }, + "sub_invocations": [] + } + ] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "initiate_dispute", + "args": [ + { + "symbol": "escrow_0" + }, + { + "symbol": "FRAUD" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 22, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 9500 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "TotalSupply" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 1010000 + } + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "symbol": "CNT" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "escrow_0" + }, + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "dispute_period" + }, + "val": { + "u64": 1800 + } + }, + { + "key": { + "symbol": "has_dispute" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "symbol": "escrow_0" + } + }, + { + "key": { + "symbol": "recipient" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "sender" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "vec": [ + { + "symbol": "Disputed" + } + ] + } + }, + { + "key": { + "symbol": "timeout_duration" + }, + "val": { + "u64": 3600 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + } + ] + } + }, + { + "key": { + "vec": [ + { + "symbol": "escrow_0" + }, + { + "symbol": "DISPUTE" + } + ] + }, + "val": { + "map": [ + { + "key": { + "symbol": "dispute_period" + }, + "val": { + "u64": 1800 + } + }, + { + "key": { + "symbol": "initiated_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "initiated_by" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + }, + { + "key": { + "symbol": "reason" + }, + "val": { + "symbol": "FRAUD" + } + } + ] + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/test_snapshots/test_release_escrow_success.1.json b/test_snapshots/test_release_escrow_success.1.json new file mode 100644 index 0000000..234c2bb --- /dev/null +++ b/test_snapshots/test_release_escrow_success.1.json @@ -0,0 +1,433 @@ +{ + "generators": { + "address": 4, + "nonce": 0 + }, + "auth": [ + [], + [], + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "create", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + }, + { + "u64": 3600 + }, + { + "u64": 1800 + } + ] + } + }, + "sub_invocations": [ + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "function_name": "transfer", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + } + ] + } + }, + "sub_invocations": [] + } + ] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "release", + "args": [ + { + "symbol": "escrow_0" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [], + [] + ], + "ledger": { + "protocol_version": 22, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 9500 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 0 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "TotalSupply" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 1010000 + } + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "symbol": "CNT" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "escrow_0" + }, + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "dispute_period" + }, + "val": { + "u64": 1800 + } + }, + { + "key": { + "symbol": "has_dispute" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "symbol": "escrow_0" + } + }, + { + "key": { + "symbol": "recipient" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "sender" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "vec": [ + { + "symbol": "Released" + } + ] + } + }, + { + "key": { + "symbol": "timeout_duration" + }, + "val": { + "u64": 3600 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + } + ] + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/test_snapshots/test_resolve_dispute_for_recipient.1.json b/test_snapshots/test_resolve_dispute_for_recipient.1.json new file mode 100644 index 0000000..3124933 --- /dev/null +++ b/test_snapshots/test_resolve_dispute_for_recipient.1.json @@ -0,0 +1,535 @@ +{ + "generators": { + "address": 4, + "nonce": 0 + }, + "auth": [ + [], + [], + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "create", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + }, + { + "u64": 3600 + }, + { + "u64": 1800 + } + ] + } + }, + "sub_invocations": [ + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "function_name": "transfer", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + } + ] + } + }, + "sub_invocations": [] + } + ] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "initiate_dispute", + "args": [ + { + "symbol": "escrow_0" + }, + { + "symbol": "FRAUD" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "resolve_dispute_for_recipient", + "args": [ + { + "symbol": "escrow_0" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 22, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 1033654523790656264 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 1033654523790656264 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 9500 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 0 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "TotalSupply" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 1010000 + } + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "symbol": "CNT" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "escrow_0" + }, + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "dispute_period" + }, + "val": { + "u64": 1800 + } + }, + { + "key": { + "symbol": "has_dispute" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "symbol": "escrow_0" + } + }, + { + "key": { + "symbol": "recipient" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "sender" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "vec": [ + { + "symbol": "DisputeResolvedForRecipient" + } + ] + } + }, + { + "key": { + "symbol": "timeout_duration" + }, + "val": { + "u64": 3600 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + } + ] + } + }, + { + "key": { + "vec": [ + { + "symbol": "escrow_0" + }, + { + "symbol": "DISPUTE" + } + ] + }, + "val": { + "map": [ + { + "key": { + "symbol": "dispute_period" + }, + "val": { + "u64": 1800 + } + }, + { + "key": { + "symbol": "initiated_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "initiated_by" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + }, + { + "key": { + "symbol": "reason" + }, + "val": { + "symbol": "FRAUD" + } + } + ] + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/test_snapshots/test_resolve_dispute_for_sender.1.json b/test_snapshots/test_resolve_dispute_for_sender.1.json new file mode 100644 index 0000000..7620111 --- /dev/null +++ b/test_snapshots/test_resolve_dispute_for_sender.1.json @@ -0,0 +1,517 @@ +{ + "generators": { + "address": 4, + "nonce": 0 + }, + "auth": [ + [], + [], + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "create", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + }, + { + "u64": 3600 + }, + { + "u64": 1800 + } + ] + } + }, + "sub_invocations": [ + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "function_name": "transfer", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + } + ] + } + }, + "sub_invocations": [] + } + ] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "initiate_dispute", + "args": [ + { + "symbol": "escrow_0" + }, + { + "symbol": "FRAUD" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "function_name": "resolve_dispute_for_sender", + "args": [ + { + "symbol": "escrow_0" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 22, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 1033654523790656264 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 1033654523790656264 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 10000 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 0 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "TotalSupply" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 1010000 + } + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "symbol": "CNT" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "escrow_0" + }, + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "dispute_period" + }, + "val": { + "u64": 1800 + } + }, + { + "key": { + "symbol": "has_dispute" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "symbol": "escrow_0" + } + }, + { + "key": { + "symbol": "recipient" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "sender" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "vec": [ + { + "symbol": "DisputeResolvedForSender" + } + ] + } + }, + { + "key": { + "symbol": "timeout_duration" + }, + "val": { + "u64": 3600 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + } + ] + } + }, + { + "key": { + "vec": [ + { + "symbol": "escrow_0" + }, + { + "symbol": "DISPUTE" + } + ] + }, + "val": { + "map": [ + { + "key": { + "symbol": "dispute_period" + }, + "val": { + "u64": 1800 + } + }, + { + "key": { + "symbol": "initiated_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "initiated_by" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + }, + { + "key": { + "symbol": "reason" + }, + "val": { + "symbol": "FRAUD" + } + } + ] + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/test_snapshots/test_same_sender_recipient_validation.1.json b/test_snapshots/test_same_sender_recipient_validation.1.json new file mode 100644 index 0000000..38d2092 --- /dev/null +++ b/test_snapshots/test_same_sender_recipient_validation.1.json @@ -0,0 +1,145 @@ +{ + "generators": { + "address": 4, + "nonce": 0 + }, + "auth": [ + [], + [], + [], + [], + [] + ], + "ledger": { + "protocol_version": 22, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 10000 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "TotalSupply" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 1010000 + } + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/test_snapshots/test_timeout_validation.1.json b/test_snapshots/test_timeout_validation.1.json new file mode 100644 index 0000000..38d2092 --- /dev/null +++ b/test_snapshots/test_timeout_validation.1.json @@ -0,0 +1,145 @@ +{ + "generators": { + "address": 4, + "nonce": 0 + }, + "auth": [ + [], + [], + [], + [], + [] + ], + "ledger": { + "protocol_version": 22, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 10000 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "TotalSupply" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 1010000 + } + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/test_snapshots/test_zero_amount_validation.1.json b/test_snapshots/test_zero_amount_validation.1.json new file mode 100644 index 0000000..38d2092 --- /dev/null +++ b/test_snapshots/test_zero_amount_validation.1.json @@ -0,0 +1,145 @@ +{ + "generators": { + "address": 4, + "nonce": 0 + }, + "auth": [ + [], + [], + [], + [], + [] + ], + "ledger": { + "protocol_version": 22, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 10000 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "TotalSupply" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 1010000 + } + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/tests/escrow_tests.rs b/tests/escrow_tests.rs new file mode 100644 index 0000000..efc0786 --- /dev/null +++ b/tests/escrow_tests.rs @@ -0,0 +1,387 @@ +#![cfg(test)] + +mod mock_token; + +use soroban_sdk::{ + symbol_short, + testutils::{Address as _}, + Address, Env, +}; +use stellar_multisig_contract::escrow::{EscrowClient, EscrowContract, EscrowStatus}; +use mock_token::{MockToken, MockTokenClient}; + +fn setup_test_env() -> (Env, Address, Address, Address, Address) { + let env = Env::default(); + env.mock_all_auths(); + + let sender = Address::generate(&env); + let recipient = Address::generate(&env); + + // Deploy mock token + let token_contract_id = env.register(MockToken, ()); + let token_client = MockTokenClient::new(&env, &token_contract_id); + + // Initialize token and mint some tokens to sender + token_client.initialize(&1_000_000); + token_client.mint(&sender, &10_000); + + // Deploy escrow contract + let escrow_contract_id = env.register(EscrowContract, ()); + + (env, escrow_contract_id, token_contract_id, sender, recipient) +} + +#[test] +fn test_create_escrow_success() { + let (env, escrow_contract_id, token_contract_id, sender, recipient) = setup_test_env(); + let client = EscrowClient::new(&env, &escrow_contract_id); + let token_client = MockTokenClient::new(&env, &token_contract_id); + + // Create escrow + let escrow_info = client.create( + &sender, + &recipient, + &token_contract_id, + &500, + &3600, // 1 hour timeout + &1800, // 30 minutes dispute period + ); + + assert_eq!(escrow_info.sender, sender); + assert_eq!(escrow_info.recipient, recipient); + assert_eq!(escrow_info.token, token_contract_id); + assert_eq!(escrow_info.amount, 500); + assert_eq!(escrow_info.status, EscrowStatus::Active); + assert_eq!(escrow_info.dispute_period, 1800); + assert!(!escrow_info.has_dispute); + + // Verify token was transferred from sender to escrow contract + assert_eq!(token_client.balance(&sender), 10_000 - 500); + assert_eq!(token_client.balance(&escrow_contract_id), 500); +} + +#[test] +fn test_release_escrow_success() { + let (env, escrow_contract_id, token_contract_id, sender, recipient) = setup_test_env(); + let client = EscrowClient::new(&env, &escrow_contract_id); + let token_client = MockTokenClient::new(&env, &token_contract_id); + + // Create escrow + let escrow_info = client.create( + &sender, + &recipient, + &token_contract_id, + &500, + &3600, + &1800, + ); + + // Release escrow + let released_info = client.release(&escrow_info.id); + + assert_eq!(released_info.status, EscrowStatus::Released); + + // Verify tokens were transferred to recipient + assert_eq!(token_client.balance(&recipient), 500); + assert_eq!(token_client.balance(&escrow_contract_id), 0); +} + +#[test] +fn test_initiate_dispute_by_sender() { + let (env, escrow_contract_id, token_contract_id, sender, recipient) = setup_test_env(); + let client = EscrowClient::new(&env, &escrow_contract_id); + + // Create escrow + let escrow_info = client.create( + &sender, + &recipient, + &token_contract_id, + &500, + &3600, + &1800, + ); + + // Initiate dispute by sender + let disputed_info = client.initiate_dispute(&escrow_info.id, &symbol_short!("FRAUD")); + + assert_eq!(disputed_info.status, EscrowStatus::Disputed); + assert!(disputed_info.has_dispute); + + let dispute = client.get_dispute_info(&escrow_info.id).unwrap(); + assert_eq!(dispute.initiated_by, sender); + assert_eq!(dispute.reason, symbol_short!("FRAUD")); + assert_eq!(dispute.dispute_period, 1800); +} + +#[test] +#[should_panic(expected = "Dispute already initiated")] +fn test_duplicate_dispute_initiation() { + let (env, escrow_contract_id, token_contract_id, sender, recipient) = setup_test_env(); + let client = EscrowClient::new(&env, &escrow_contract_id); + + // Create escrow + let escrow_info = client.create( + &sender, + &recipient, + &token_contract_id, + &500, + &3600, + &1800, + ); + + // Initiate first dispute + client.initiate_dispute(&escrow_info.id, &symbol_short!("FRAUD")); + + // Try to initiate second dispute - should panic + client.initiate_dispute(&escrow_info.id, &symbol_short!("OTHER")); +} + +#[test] +fn test_resolve_dispute_for_recipient() { + let (env, escrow_contract_id, token_contract_id, sender, recipient) = setup_test_env(); + let client = EscrowClient::new(&env, &escrow_contract_id); + let token_client = MockTokenClient::new(&env, &token_contract_id); + + // Create escrow and initiate dispute + let escrow_info = client.create( + &sender, + &recipient, + &token_contract_id, + &500, + &3600, + &1800, + ); + + client.initiate_dispute(&escrow_info.id, &symbol_short!("FRAUD")); + + // Resolve dispute for recipient + let resolved_info = client.resolve_dispute_for_recipient(&escrow_info.id); + + assert_eq!( + resolved_info.status, + EscrowStatus::DisputeResolvedForRecipient + ); + + // Verify tokens were transferred to recipient + assert_eq!(token_client.balance(&recipient), 500); +} + +#[test] +fn test_resolve_dispute_for_sender() { + let (env, escrow_contract_id, token_contract_id, sender, recipient) = setup_test_env(); + let client = EscrowClient::new(&env, &escrow_contract_id); + let token_client = MockTokenClient::new(&env, &token_contract_id); + + // Create escrow and initiate dispute + let escrow_info = client.create( + &sender, + &recipient, + &token_contract_id, + &500, + &3600, + &1800, + ); + + client.initiate_dispute(&escrow_info.id, &symbol_short!("FRAUD")); + + // Resolve dispute for sender + let resolved_info = client.resolve_dispute_for_sender(&escrow_info.id); + + assert_eq!(resolved_info.status, EscrowStatus::DisputeResolvedForSender); + + // Verify tokens were returned to sender + assert_eq!(token_client.balance(&sender), 10_000); // Original balance restored +} + +#[test] +fn test_can_dispute_functionality() { + let (env, escrow_contract_id, token_contract_id, sender, recipient) = setup_test_env(); + let client = EscrowClient::new(&env, &escrow_contract_id); + + // Create escrow + let escrow_info = client.create( + &sender, + &recipient, + &token_contract_id, + &500, + &3600, + &1800, + ); + + // Should be able to dispute initially + assert!(client.can_dispute(&escrow_info.id)); + + // Initiate dispute + client.initiate_dispute(&escrow_info.id, &symbol_short!("FRAUD")); + + // Should not be able to dispute anymore + assert!(!client.can_dispute(&escrow_info.id)); +} + +#[test] +#[should_panic(expected = "Escrow is not active or is disputed")] +fn test_release_disputed_escrow() { + let (env, escrow_contract_id, token_contract_id, sender, recipient) = setup_test_env(); + let client = EscrowClient::new(&env, &escrow_contract_id); + + // Create escrow and initiate dispute + let escrow_info = client.create( + &sender, + &recipient, + &token_contract_id, + &500, + &3600, + &1800, + ); + + client.initiate_dispute(&escrow_info.id, &symbol_short!("FRAUD")); + + // Try to release disputed escrow - should panic + client.release(&escrow_info.id); +} + +#[test] +#[should_panic(expected = "Sender and recipient cannot be the same")] +fn test_same_sender_recipient_validation() { + let (env, escrow_contract_id, token_contract_id, sender, _recipient) = setup_test_env(); + let client = EscrowClient::new(&env, &escrow_contract_id); + + // Test same sender and recipient should fail + client.create( + &sender, + &sender, // Same as sender + &token_contract_id, + &500, + &3600, + &1800, + ); +} + +#[test] +#[should_panic(expected = "Amount must be positive")] +fn test_zero_amount_validation() { + let (env, escrow_contract_id, token_contract_id, sender, recipient) = setup_test_env(); + let client = EscrowClient::new(&env, &escrow_contract_id); + + // Test zero amount should fail + client.create( + &sender, + &recipient, + &token_contract_id, + &0, // Zero amount + &3600, + &1800, + ); +} + +#[test] +#[should_panic(expected = "Timeout duration must be greater than dispute period")] +fn test_timeout_validation() { + let (env, escrow_contract_id, token_contract_id, sender, recipient) = setup_test_env(); + let client = EscrowClient::new(&env, &escrow_contract_id); + + // Test timeout less than dispute period should fail + client.create( + &sender, + &recipient, + &token_contract_id, + &500, + &1000, // Timeout + &2000, // Dispute period longer than timeout + ); +} + +#[test] +fn test_admin_functions() { + let (env, escrow_contract_id, _token_contract_id, _sender, _recipient) = setup_test_env(); + let client = EscrowClient::new(&env, &escrow_contract_id); + let admin = Address::generate(&env); + + // Initialize contract + client.initialize(&admin); + + // Test admin functions + assert_eq!(client.get_admin(), admin); + assert_eq!(client.get_dispute_fee(), 0); + assert!(!client.is_paused()); + + // Set dispute fee + client.set_dispute_fee(&100); + assert_eq!(client.get_dispute_fee(), 100); + + // Pause contract + client.set_paused(&true); + assert!(client.is_paused()); + + // Unpause + client.set_paused(&false); + assert!(!client.is_paused()); +} + +#[test] +#[should_panic(expected = "Contract is paused")] +fn test_paused_contract_validation() { + let (env, escrow_contract_id, token_contract_id, sender, recipient) = setup_test_env(); + let client = EscrowClient::new(&env, &escrow_contract_id); + let admin = Address::generate(&env); + + // Initialize and pause contract + client.initialize(&admin); + client.set_paused(&true); + + // Try to create escrow while paused - should fail + client.create( + &sender, + &recipient, + &token_contract_id, + &500, + &3600, + &1800, + ); +} + +#[test] +fn test_query_functions() { + let (env, escrow_contract_id, token_contract_id, sender, recipient) = setup_test_env(); + let client = EscrowClient::new(&env, &escrow_contract_id); + + // Initially no escrows + assert_eq!(client.get_escrow_count(), 0); + + // Create an escrow + let escrow_info = client.create( + &sender, + &recipient, + &token_contract_id, + &500, + &3600, + &1800, + ); + + // Check count increased + assert_eq!(client.get_escrow_count(), 1); + assert!(client.escrow_exists(&escrow_info.id)); + + // Test filtering by status + let active_escrows = client.get_escrows_by_status(&EscrowStatus::Active); + assert_eq!(active_escrows.len(), 1); + + let disputed_escrows = client.get_escrows_by_status(&EscrowStatus::Disputed); + assert_eq!(disputed_escrows.len(), 0); + + // Test filtering by participant + let sender_escrows = client.get_escrows_by_participant(&sender); + assert_eq!(sender_escrows.len(), 1); + + let recipient_escrows = client.get_escrows_by_participant(&recipient); + assert_eq!(recipient_escrows.len(), 1); + + // Initiate dispute and check status filtering + client.initiate_dispute(&escrow_info.id, &symbol_short!("FRAUD")); + + let active_escrows = client.get_escrows_by_status(&EscrowStatus::Active); + assert_eq!(active_escrows.len(), 0); + + let disputed_escrows = client.get_escrows_by_status(&EscrowStatus::Disputed); + assert_eq!(disputed_escrows.len(), 1); +} \ No newline at end of file diff --git a/tests/mock_token.rs b/tests/mock_token.rs new file mode 100644 index 0000000..f617671 --- /dev/null +++ b/tests/mock_token.rs @@ -0,0 +1,125 @@ +use soroban_sdk::{contract, contractimpl, contracttype, Address, Env}; + +#[contracttype] +pub enum DataKey { + Balance(Address), + TotalSupply, +} + +#[contract] +pub struct MockToken; + +#[contractimpl] +impl MockToken { + pub fn initialize(env: Env, total_supply: i128) { + env.storage().instance().set(&DataKey::TotalSupply, &total_supply); + } + + pub fn mint(env: Env, to: Address, amount: i128) { + let balance = Self::balance(env.clone(), to.clone()); + env.storage().instance().set(&DataKey::Balance(to), &(balance + amount)); + + let total_supply: i128 = env.storage().instance().get(&DataKey::TotalSupply).unwrap_or(0); + env.storage().instance().set(&DataKey::TotalSupply, &(total_supply + amount)); + } + + pub fn balance(env: Env, id: Address) -> i128 { + env.storage().instance().get(&DataKey::Balance(id)).unwrap_or(0) + } + + pub fn transfer(env: Env, from: Address, to: Address, amount: i128) { + from.require_auth(); + + let from_balance = Self::balance(env.clone(), from.clone()); + let to_balance = Self::balance(env.clone(), to.clone()); + + if from_balance < amount { + panic!("insufficient balance"); + } + + env.storage().instance().set(&DataKey::Balance(from), &(from_balance - amount)); + env.storage().instance().set(&DataKey::Balance(to), &(to_balance + amount)); + } + + pub fn approve(env: Env, from: Address, spender: Address, amount: i128, _expiration_ledger: u32) { + from.require_auth(); + // For simplicity, we'll just store the approval without expiration logic + let key = (from, spender); + env.storage().instance().set(&key, &amount); + } + + pub fn allowance(env: Env, from: Address, spender: Address) -> i128 { + let key = (from, spender); + env.storage().instance().get(&key).unwrap_or(0) + } + + pub fn transfer_from(env: Env, spender: Address, from: Address, to: Address, amount: i128) { + spender.require_auth(); + + let allowance = Self::allowance(env.clone(), from.clone(), spender.clone()); + if allowance < amount { + panic!("insufficient allowance"); + } + + let from_balance = Self::balance(env.clone(), from.clone()); + if from_balance < amount { + panic!("insufficient balance"); + } + + let to_balance = Self::balance(env.clone(), to.clone()); + + env.storage().instance().set(&DataKey::Balance(from.clone()), &(from_balance - amount)); + env.storage().instance().set(&DataKey::Balance(to), &(to_balance + amount)); + + let key = (from, spender); + env.storage().instance().set(&key, &(allowance - amount)); + } + + pub fn burn(env: Env, from: Address, amount: i128) { + from.require_auth(); + + let balance = Self::balance(env.clone(), from.clone()); + if balance < amount { + panic!("insufficient balance"); + } + + env.storage().instance().set(&DataKey::Balance(from), &(balance - amount)); + + let total_supply: i128 = env.storage().instance().get(&DataKey::TotalSupply).unwrap_or(0); + env.storage().instance().set(&DataKey::TotalSupply, &(total_supply - amount)); + } + + pub fn burn_from(env: Env, spender: Address, from: Address, amount: i128) { + spender.require_auth(); + + let allowance = Self::allowance(env.clone(), from.clone(), spender.clone()); + if allowance < amount { + panic!("insufficient allowance"); + } + + let balance = Self::balance(env.clone(), from.clone()); + if balance < amount { + panic!("insufficient balance"); + } + + env.storage().instance().set(&DataKey::Balance(from.clone()), &(balance - amount)); + + let key = (from, spender); + env.storage().instance().set(&key, &(allowance - amount)); + + let total_supply: i128 = env.storage().instance().get(&DataKey::TotalSupply).unwrap_or(0); + env.storage().instance().set(&DataKey::TotalSupply, &(total_supply - amount)); + } + + pub fn decimals(_env: Env) -> u32 { + 7 + } + + pub fn name(env: Env) -> soroban_sdk::String { + soroban_sdk::String::from_str(&env, "Mock Token") + } + + pub fn symbol(env: Env) -> soroban_sdk::String { + soroban_sdk::String::from_str(&env, "MOCK") + } +} \ No newline at end of file From f66c916869a9de74beed6920c07674ef74803ebf Mon Sep 17 00:00:00 2001 From: truthixify Date: Sun, 28 Sep 2025 20:40:21 +0100 Subject: [PATCH 2/2] fixed cargo fmt --- src/escrow.rs | 137 ++++++++++++++++++++++++------------ src/pool_manager.rs | 135 ++++++++++++++++++++++++----------- tests/escrow_tests.rs | 115 +++++++----------------------- tests/mock_token.rs | 87 +++++++++++++++++------ tests/pool_manager_tests.rs | 21 +++--- 5 files changed, 285 insertions(+), 210 deletions(-) diff --git a/src/escrow.rs b/src/escrow.rs index b45396c..c821184 100644 --- a/src/escrow.rs +++ b/src/escrow.rs @@ -1,7 +1,8 @@ use core::fmt::Write; use heapless::String as HString; use soroban_sdk::{ - contract, contractclient, contractimpl, contracttype, symbol_short, token, Address, Env, Symbol, Vec, + contract, contractclient, contractimpl, contracttype, symbol_short, token, Address, Env, + Symbol, Vec, }; /// Status of the escrow operation @@ -93,7 +94,7 @@ pub trait EscrowContractTrait { timeout_duration: u64, dispute_period: u64, ) -> EscrowInfo; - + fn release(env: Env, escrow_id: Symbol) -> EscrowInfo; fn refund(env: Env, escrow_id: Symbol) -> EscrowInfo; fn check_timeout(env: Env, escrow_id: Symbol) -> EscrowInfo; @@ -117,7 +118,11 @@ pub trait EscrowContractTrait { fn transfer_admin(env: Env, new_admin: Address); fn set_paused(env: Env, paused: bool); fn is_paused(env: Env) -> bool; - fn admin_resolve_dispute(env: Env, escrow_id: Symbol, resolve_for_recipient: bool) -> EscrowInfo; + fn admin_resolve_dispute( + env: Env, + escrow_id: Symbol, + resolve_for_recipient: bool, + ) -> EscrowInfo; } const ESCROW_COUNT_KEY: Symbol = symbol_short!("CNT"); @@ -137,7 +142,12 @@ impl EscrowContract { dispute_period: u64, ) -> EscrowInfo { // Check if contract is paused - if env.storage().instance().get(&symbol_short!("PAUSED")).unwrap_or(false) { + if env + .storage() + .instance() + .get(&symbol_short!("PAUSED")) + .unwrap_or(false) + { panic!("Contract is paused"); } @@ -433,7 +443,12 @@ impl EscrowContract { /// Initiate a dispute (can be called by sender or recipient) pub fn initiate_dispute(env: Env, escrow_id: Symbol, reason: Symbol) -> EscrowInfo { // Check if contract is paused - if env.storage().instance().get(&symbol_short!("PAUSED")).unwrap_or(false) { + if env + .storage() + .instance() + .get(&symbol_short!("PAUSED")) + .unwrap_or(false) + { panic!("Contract is paused"); } @@ -456,7 +471,11 @@ impl EscrowContract { let caller = escrow.sender.clone(); // Handle dispute fee if set - let dispute_fee = env.storage().instance().get(&DISPUTE_FEE_KEY).unwrap_or(0i128); + let dispute_fee = env + .storage() + .instance() + .get(&DISPUTE_FEE_KEY) + .unwrap_or(0i128); if dispute_fee > 0 { let client = token::Client::new(&env, &escrow.token); let caller_balance = client.balance(&caller); @@ -477,7 +496,9 @@ impl EscrowContract { // Store dispute info separately using a simple key pattern let dispute_key = symbol_short!("DISPUTE"); - env.storage().instance().set(&(escrow_id.clone(), dispute_key), &dispute_info); + env.storage() + .instance() + .set(&(escrow_id.clone(), dispute_key), &dispute_info); // Update escrow with dispute let updated_escrow = EscrowConfig { @@ -523,12 +544,16 @@ impl EscrowContract { // Get dispute info let dispute_key = symbol_short!("DISPUTE"); - let dispute: DisputeInfo = env.storage().instance().get(&(escrow_id.clone(), dispute_key)).unwrap(); + let dispute: DisputeInfo = env + .storage() + .instance() + .get(&(escrow_id.clone(), dispute_key)) + .unwrap(); // Check if dispute period has expired (auto-resolution) let current_time = env.ledger().timestamp(); let dispute_expires_at = dispute.initiated_at + dispute.dispute_period; - + if current_time < dispute_expires_at { // Manual resolution - require sender auth for now escrow.sender.require_auth(); @@ -584,12 +609,16 @@ impl EscrowContract { // Get dispute info let dispute_key = symbol_short!("DISPUTE"); - let dispute: DisputeInfo = env.storage().instance().get(&(escrow_id.clone(), dispute_key)).unwrap(); + let dispute: DisputeInfo = env + .storage() + .instance() + .get(&(escrow_id.clone(), dispute_key)) + .unwrap(); // Check if dispute period has expired (auto-resolution) let current_time = env.ledger().timestamp(); let dispute_expires_at = dispute.initiated_at + dispute.dispute_period; - + if current_time < dispute_expires_at { // Manual resolution - require sender auth for now escrow.sender.require_auth(); @@ -645,7 +674,11 @@ impl EscrowContract { // Get dispute info let dispute_key = symbol_short!("DISPUTE"); - let dispute: DisputeInfo = env.storage().instance().get(&(escrow_id.clone(), dispute_key)).unwrap(); + let dispute: DisputeInfo = env + .storage() + .instance() + .get(&(escrow_id.clone(), dispute_key)) + .unwrap(); // Check if dispute period has expired let current_time = env.ledger().timestamp(); @@ -697,13 +730,13 @@ impl EscrowContract { .get(&ESCROW_COUNT_KEY) .unwrap_or(0u32); let mut escrows = Vec::new(&env); - + for i in 0..count { let mut s: HString<12> = HString::new(); s.push_str("escrow_").unwrap(); write!(&mut s, "{}", i).unwrap(); let id = Symbol::new(&env, s.as_str()); - + if env.storage().instance().has(&id) { let escrow: EscrowConfig = env.storage().instance().get(&id).unwrap(); if escrow.status == status { @@ -733,13 +766,13 @@ impl EscrowContract { .get(&ESCROW_COUNT_KEY) .unwrap_or(0u32); let mut escrows = Vec::new(&env); - + for i in 0..count { let mut s: HString<12> = HString::new(); s.push_str("escrow_").unwrap(); write!(&mut s, "{}", i).unwrap(); let id = Symbol::new(&env, s.as_str()); - + if env.storage().instance().has(&id) { let escrow: EscrowConfig = env.storage().instance().get(&id).unwrap(); if escrow.sender == participant || escrow.recipient == participant { @@ -762,35 +795,39 @@ impl EscrowContract { } /// Update dispute period for an active escrow (only by sender before dispute) - pub fn update_dispute_period(env: Env, escrow_id: Symbol, new_dispute_period: u64) -> EscrowInfo { + pub fn update_dispute_period( + env: Env, + escrow_id: Symbol, + new_dispute_period: u64, + ) -> EscrowInfo { let escrow: EscrowConfig = env.storage().instance().get(&escrow_id).unwrap(); - + // Only sender can update and only if escrow is active with no dispute escrow.sender.require_auth(); - + if escrow.status != EscrowStatus::Active { panic!("Escrow is not active"); } - + if escrow.has_dispute { panic!("Cannot update dispute period after dispute initiated"); } - + if new_dispute_period == 0 { panic!("Dispute period must be non-zero"); } - + if escrow.timeout_duration < new_dispute_period { panic!("Dispute period cannot exceed timeout duration"); } - + let updated_escrow = EscrowConfig { dispute_period: new_dispute_period, ..escrow.clone() }; - + env.storage().instance().set(&escrow_id, &updated_escrow); - + EscrowInfo { id: escrow.id, sender: escrow.sender, @@ -811,10 +848,10 @@ impl EscrowContract { if env.storage().instance().has(&ADMIN_KEY) { panic!("Contract already initialized"); } - + admin.require_auth(); env.storage().instance().set(&ADMIN_KEY, &admin); - + // Set default dispute fee to 0 (can be updated by admin) env.storage().instance().set(&DISPUTE_FEE_KEY, &0i128); } @@ -823,17 +860,20 @@ impl EscrowContract { pub fn set_dispute_fee(env: Env, fee: i128) { let admin: Address = env.storage().instance().get(&ADMIN_KEY).unwrap(); admin.require_auth(); - + if fee < 0 { panic!("Dispute fee cannot be negative"); } - + env.storage().instance().set(&DISPUTE_FEE_KEY, &fee); } /// Get current dispute fee pub fn get_dispute_fee(env: Env) -> i128 { - env.storage().instance().get(&DISPUTE_FEE_KEY).unwrap_or(0i128) + env.storage() + .instance() + .get(&DISPUTE_FEE_KEY) + .unwrap_or(0i128) } /// Get contract admin @@ -845,7 +885,7 @@ impl EscrowContract { pub fn transfer_admin(env: Env, new_admin: Address) { let admin: Address = env.storage().instance().get(&ADMIN_KEY).unwrap(); admin.require_auth(); - + env.storage().instance().set(&ADMIN_KEY, &new_admin); } @@ -853,26 +893,35 @@ impl EscrowContract { pub fn set_paused(env: Env, paused: bool) { let admin: Address = env.storage().instance().get(&ADMIN_KEY).unwrap(); admin.require_auth(); - - env.storage().instance().set(&symbol_short!("PAUSED"), &paused); + + env.storage() + .instance() + .set(&symbol_short!("PAUSED"), &paused); } /// Check if contract is paused pub fn is_paused(env: Env) -> bool { - env.storage().instance().get(&symbol_short!("PAUSED")).unwrap_or(false) + env.storage() + .instance() + .get(&symbol_short!("PAUSED")) + .unwrap_or(false) } /// Admin emergency resolution (admin only, for extreme cases) - pub fn admin_resolve_dispute(env: Env, escrow_id: Symbol, resolve_for_recipient: bool) -> EscrowInfo { + pub fn admin_resolve_dispute( + env: Env, + escrow_id: Symbol, + resolve_for_recipient: bool, + ) -> EscrowInfo { let admin: Address = env.storage().instance().get(&ADMIN_KEY).unwrap(); admin.require_auth(); - + let escrow: EscrowConfig = env.storage().instance().get(&escrow_id).unwrap(); - + if escrow.status != EscrowStatus::Disputed { panic!("Escrow is not disputed"); } - + if resolve_for_recipient { // Transfer tokens to recipient let client = token::Client::new(&env, &escrow.token); @@ -881,20 +930,20 @@ impl EscrowContract { &escrow.recipient, &escrow.amount, ); - + let updated_escrow = EscrowConfig { status: EscrowStatus::DisputeResolvedForRecipient, ..escrow.clone() }; env.storage().instance().set(&escrow_id, &updated_escrow); - + crate::event::EventEmitter::emit_dispute_resolved( &env, escrow_id.clone(), escrow.recipient.clone(), true, ); - + EscrowInfo { id: escrow.id, sender: escrow.sender, @@ -915,20 +964,20 @@ impl EscrowContract { &escrow.sender, &escrow.amount, ); - + let updated_escrow = EscrowConfig { status: EscrowStatus::DisputeResolvedForSender, ..escrow.clone() }; env.storage().instance().set(&escrow_id, &updated_escrow); - + crate::event::EventEmitter::emit_dispute_resolved( &env, escrow_id.clone(), escrow.sender.clone(), false, ); - + EscrowInfo { id: escrow.id, sender: escrow.sender, diff --git a/src/pool_manager.rs b/src/pool_manager.rs index d3ddf9d..f8a14e4 100644 --- a/src/pool_manager.rs +++ b/src/pool_manager.rs @@ -1,6 +1,4 @@ -use soroban_sdk::{ - contract, contractimpl, contractmeta, contracttype, log, Address, Env, Vec, -}; +use soroban_sdk::{contract, contractimpl, contractmeta, contracttype, log, Address, Env, Vec}; use crate::conversion::Currency; use crate::utils::{validate_address, validate_positive_amount}; @@ -130,8 +128,6 @@ const DEFAULT_UTILIZATION_WARNING_BPS: u32 = 8000; // 80% const MAX_UTILIZATION_BPS: u32 = 9500; // 95% const BASIS_POINTS_DIVISOR: i128 = 10000; - - #[contractimpl] impl PoolManagerContract { /// Initialize the pool manager @@ -166,9 +162,15 @@ impl PoolManagerContract { // Initialize active currencies list let active_currencies: Vec = Vec::new(&env); - env.storage().instance().set(&PoolDataKey::PoolConfig, &config); - env.storage().instance().set(&PoolDataKey::ActiveCurrencies, &active_currencies); - env.storage().instance().set(&PoolDataKey::PositionCounter, &0u64); + env.storage() + .instance() + .set(&PoolDataKey::PoolConfig, &config); + env.storage() + .instance() + .set(&PoolDataKey::ActiveCurrencies, &active_currencies); + env.storage() + .instance() + .set(&PoolDataKey::PositionCounter, &0u64); log!(&env, "Pool manager initialized by admin: {}", admin); config @@ -183,14 +185,14 @@ impl PoolManagerContract { lock_period: Option, ) -> LiquidityPosition { provider.require_auth(); - + let config = Self::get_pool_config_internal(&env); if config.is_paused { panic!("Pool manager is paused"); } validate_positive_amount(amount).unwrap(); - + if amount < config.min_liquidity_amount || amount > config.max_liquidity_amount { panic!("Amount outside allowed liquidity limits"); } @@ -200,7 +202,7 @@ impl PoolManagerContract { // Get or create pool for currency let mut pool = Self::get_or_create_pool(&env, ¤cy); - + // Get or create provider position let mut position = Self::get_or_create_position(&env, &provider, ¤cy); @@ -208,7 +210,7 @@ impl PoolManagerContract { pool.total_liquidity += amount; pool.available_liquidity += amount; pool.last_activity_at = current_time; - + if position.liquidity_amount == 0 { pool.provider_count += 1; } @@ -225,8 +227,11 @@ impl PoolManagerContract { } // Store position first - env.storage().instance().set(&PoolDataKey::Position(provider.clone(), currency.clone()), &position); - + env.storage().instance().set( + &PoolDataKey::Position(provider.clone(), currency.clone()), + &position, + ); + // Recalculate shares for all providers in this currency pool Self::recalculate_all_shares(&env, ¤cy, pool.total_liquidity); @@ -234,7 +239,9 @@ impl PoolManagerContract { pool.utilization_rate_bps = Self::calculate_utilization_rate(&pool); // Store updates - env.storage().instance().set(&PoolDataKey::Pool(currency.clone()), &pool); + env.storage() + .instance() + .set(&PoolDataKey::Pool(currency.clone()), &pool); // Update active currencies if this is a new pool Self::update_active_currencies(&env, ¤cy); @@ -257,7 +264,10 @@ impl PoolManagerContract { if pool.utilization_rate_bps > config.utilization_warning_bps { Self::publish_pool_event( &env, - PoolManagerEvent::PoolUtilizationWarning(currency.clone(), pool.utilization_rate_bps), + PoolManagerEvent::PoolUtilizationWarning( + currency.clone(), + pool.utilization_rate_bps, + ), ); } @@ -292,7 +302,7 @@ impl PoolManagerContract { // Get provider position let mut position = Self::get_position_internal(&env, &provider, ¤cy); - + if position.lock_until > current_time { panic!("Liquidity is still locked"); } @@ -329,12 +339,17 @@ impl PoolManagerContract { // Store or remove position if position.liquidity_amount == 0 { // Remove position if no liquidity left - env.storage().instance().remove(&PoolDataKey::Position(provider.clone(), currency.clone())); + env.storage() + .instance() + .remove(&PoolDataKey::Position(provider.clone(), currency.clone())); } else { // Store updated position - env.storage().instance().set(&PoolDataKey::Position(provider.clone(), currency.clone()), &position); + env.storage().instance().set( + &PoolDataKey::Position(provider.clone(), currency.clone()), + &position, + ); } - + // Recalculate shares for all providers in this currency pool Self::recalculate_all_shares(&env, ¤cy, pool.total_liquidity); @@ -342,7 +357,9 @@ impl PoolManagerContract { pool.utilization_rate_bps = Self::calculate_utilization_rate(&pool); // Store updates - env.storage().instance().set(&PoolDataKey::Pool(currency.clone()), &pool); + env.storage() + .instance() + .set(&PoolDataKey::Pool(currency.clone()), &pool); // Get updated position for correct share (if still exists) let updated_position = if position.liquidity_amount > 0 { @@ -386,7 +403,7 @@ impl PoolManagerContract { ) -> (LiquidityPool, LiquidityPool) { // This function should be called by the conversion contract // For now, we'll allow any caller but in production this should be restricted - + let current_time = env.ledger().timestamp(); // Update source currency pool (liquidity consumed) @@ -410,8 +427,12 @@ impl PoolManagerContract { to_pool.utilization_rate_bps = Self::calculate_utilization_rate(&to_pool); // Store updates - env.storage().instance().set(&PoolDataKey::Pool(from_currency.clone()), &from_pool); - env.storage().instance().set(&PoolDataKey::Pool(to_currency.clone()), &to_pool); + env.storage() + .instance() + .set(&PoolDataKey::Pool(from_currency.clone()), &from_pool); + env.storage() + .instance() + .set(&PoolDataKey::Pool(to_currency.clone()), &to_pool); // Emit events Self::publish_pool_event( @@ -454,14 +475,19 @@ impl PoolManagerContract { config.admin.require_auth(); let pool = Self::get_pool_internal(&env, ¤cy); - let reward_amount = (total_fee_amount * i128::from(config.provider_reward_rate_bps)) / BASIS_POINTS_DIVISOR; + let reward_amount = + (total_fee_amount * i128::from(config.provider_reward_rate_bps)) / BASIS_POINTS_DIVISOR; if reward_amount <= 0 { return Vec::new(&env); } let rewards: Vec<(Address, i128)> = Vec::new(&env); - let _active_currencies: Vec = env.storage().instance().get(&PoolDataKey::ActiveCurrencies).unwrap_or_else(|| Vec::new(&env)); + let _active_currencies: Vec = env + .storage() + .instance() + .get(&PoolDataKey::ActiveCurrencies) + .unwrap_or_else(|| Vec::new(&env)); // Find all positions for this currency // Note: In a real implementation, you'd want to maintain an index of positions per currency @@ -494,7 +520,10 @@ impl PoolManagerContract { /// Get all active currencies with pools pub fn get_active_currencies(env: Env) -> Vec { - env.storage().instance().get(&PoolDataKey::ActiveCurrencies).unwrap_or_else(|| Vec::new(&env)) + env.storage() + .instance() + .get(&PoolDataKey::ActiveCurrencies) + .unwrap_or_else(|| Vec::new(&env)) } /// Emergency pause functionality @@ -503,7 +532,9 @@ impl PoolManagerContract { config.admin.require_auth(); config.is_paused = true; - env.storage().instance().set(&PoolDataKey::PoolConfig, &config); + env.storage() + .instance() + .set(&PoolDataKey::PoolConfig, &config); Self::publish_pool_event( &env, @@ -520,7 +551,9 @@ impl PoolManagerContract { config.admin.require_auth(); config.is_paused = false; - env.storage().instance().set(&PoolDataKey::PoolConfig, &config); + env.storage() + .instance() + .set(&PoolDataKey::PoolConfig, &config); Self::publish_pool_event( &env, @@ -567,14 +600,22 @@ impl PoolManagerContract { }) } - fn get_position_internal(env: &Env, provider: &Address, currency: &Currency) -> LiquidityPosition { + fn get_position_internal( + env: &Env, + provider: &Address, + currency: &Currency, + ) -> LiquidityPosition { env.storage() .instance() .get(&PoolDataKey::Position(provider.clone(), currency.clone())) .unwrap_or_else(|| panic!("Liquidity position not found")) } - fn get_or_create_position(env: &Env, provider: &Address, currency: &Currency) -> LiquidityPosition { + fn get_or_create_position( + env: &Env, + provider: &Address, + currency: &Currency, + ) -> LiquidityPosition { env.storage() .instance() .get(&PoolDataKey::Position(provider.clone(), currency.clone())) @@ -626,7 +667,9 @@ impl PoolManagerContract { if !found { active_currencies.push_back(currency.clone()); - env.storage().instance().set(&PoolDataKey::ActiveCurrencies, &active_currencies); + env.storage() + .instance() + .set(&PoolDataKey::ActiveCurrencies, &active_currencies); } } @@ -652,9 +695,10 @@ impl PoolManagerContract { if !found { providers.push_back(provider.clone()); - env.storage() - .instance() - .set(&PoolDataKey::CurrencyProviders(currency.clone()), &providers); + env.storage().instance().set( + &PoolDataKey::CurrencyProviders(currency.clone()), + &providers, + ); } } @@ -673,9 +717,10 @@ impl PoolManagerContract { } } - env.storage() - .instance() - .set(&PoolDataKey::CurrencyProviders(currency.clone()), &new_providers); + env.storage().instance().set( + &PoolDataKey::CurrencyProviders(currency.clone()), + &new_providers, + ); } fn recalculate_all_shares(env: &Env, currency: &Currency, total_liquidity: i128) { @@ -689,17 +734,21 @@ impl PoolManagerContract { if let Some(mut position) = env .storage() .instance() - .get::(&PoolDataKey::Position(provider.clone(), currency.clone())) + .get::(&PoolDataKey::Position( + provider.clone(), + currency.clone(), + )) { position.pool_share_bps = if total_liquidity > 0 { Self::calculate_pool_share(position.liquidity_amount, total_liquidity) } else { 0 }; - - env.storage() - .instance() - .set(&PoolDataKey::Position(provider.clone(), currency.clone()), &position); + + env.storage().instance().set( + &PoolDataKey::Position(provider.clone(), currency.clone()), + &position, + ); } } } diff --git a/tests/escrow_tests.rs b/tests/escrow_tests.rs index efc0786..43b94bf 100644 --- a/tests/escrow_tests.rs +++ b/tests/escrow_tests.rs @@ -2,13 +2,9 @@ mod mock_token; -use soroban_sdk::{ - symbol_short, - testutils::{Address as _}, - Address, Env, -}; -use stellar_multisig_contract::escrow::{EscrowClient, EscrowContract, EscrowStatus}; use mock_token::{MockToken, MockTokenClient}; +use soroban_sdk::{symbol_short, testutils::Address as _, Address, Env}; +use stellar_multisig_contract::escrow::{EscrowClient, EscrowContract, EscrowStatus}; fn setup_test_env() -> (Env, Address, Address, Address, Address) { let env = Env::default(); @@ -16,19 +12,25 @@ fn setup_test_env() -> (Env, Address, Address, Address, Address) { let sender = Address::generate(&env); let recipient = Address::generate(&env); - + // Deploy mock token let token_contract_id = env.register(MockToken, ()); let token_client = MockTokenClient::new(&env, &token_contract_id); - + // Initialize token and mint some tokens to sender token_client.initialize(&1_000_000); token_client.mint(&sender, &10_000); - + // Deploy escrow contract let escrow_contract_id = env.register(EscrowContract, ()); - - (env, escrow_contract_id, token_contract_id, sender, recipient) + + ( + env, + escrow_contract_id, + token_contract_id, + sender, + recipient, + ) } #[test] @@ -54,7 +56,7 @@ fn test_create_escrow_success() { assert_eq!(escrow_info.status, EscrowStatus::Active); assert_eq!(escrow_info.dispute_period, 1800); assert!(!escrow_info.has_dispute); - + // Verify token was transferred from sender to escrow contract assert_eq!(token_client.balance(&sender), 10_000 - 500); assert_eq!(token_client.balance(&escrow_contract_id), 500); @@ -67,20 +69,13 @@ fn test_release_escrow_success() { let token_client = MockTokenClient::new(&env, &token_contract_id); // Create escrow - let escrow_info = client.create( - &sender, - &recipient, - &token_contract_id, - &500, - &3600, - &1800, - ); + let escrow_info = client.create(&sender, &recipient, &token_contract_id, &500, &3600, &1800); // Release escrow let released_info = client.release(&escrow_info.id); assert_eq!(released_info.status, EscrowStatus::Released); - + // Verify tokens were transferred to recipient assert_eq!(token_client.balance(&recipient), 500); assert_eq!(token_client.balance(&escrow_contract_id), 0); @@ -92,14 +87,7 @@ fn test_initiate_dispute_by_sender() { let client = EscrowClient::new(&env, &escrow_contract_id); // Create escrow - let escrow_info = client.create( - &sender, - &recipient, - &token_contract_id, - &500, - &3600, - &1800, - ); + let escrow_info = client.create(&sender, &recipient, &token_contract_id, &500, &3600, &1800); // Initiate dispute by sender let disputed_info = client.initiate_dispute(&escrow_info.id, &symbol_short!("FRAUD")); @@ -120,14 +108,7 @@ fn test_duplicate_dispute_initiation() { let client = EscrowClient::new(&env, &escrow_contract_id); // Create escrow - let escrow_info = client.create( - &sender, - &recipient, - &token_contract_id, - &500, - &3600, - &1800, - ); + let escrow_info = client.create(&sender, &recipient, &token_contract_id, &500, &3600, &1800); // Initiate first dispute client.initiate_dispute(&escrow_info.id, &symbol_short!("FRAUD")); @@ -143,14 +124,7 @@ fn test_resolve_dispute_for_recipient() { let token_client = MockTokenClient::new(&env, &token_contract_id); // Create escrow and initiate dispute - let escrow_info = client.create( - &sender, - &recipient, - &token_contract_id, - &500, - &3600, - &1800, - ); + let escrow_info = client.create(&sender, &recipient, &token_contract_id, &500, &3600, &1800); client.initiate_dispute(&escrow_info.id, &symbol_short!("FRAUD")); @@ -161,7 +135,7 @@ fn test_resolve_dispute_for_recipient() { resolved_info.status, EscrowStatus::DisputeResolvedForRecipient ); - + // Verify tokens were transferred to recipient assert_eq!(token_client.balance(&recipient), 500); } @@ -173,14 +147,7 @@ fn test_resolve_dispute_for_sender() { let token_client = MockTokenClient::new(&env, &token_contract_id); // Create escrow and initiate dispute - let escrow_info = client.create( - &sender, - &recipient, - &token_contract_id, - &500, - &3600, - &1800, - ); + let escrow_info = client.create(&sender, &recipient, &token_contract_id, &500, &3600, &1800); client.initiate_dispute(&escrow_info.id, &symbol_short!("FRAUD")); @@ -188,7 +155,7 @@ fn test_resolve_dispute_for_sender() { let resolved_info = client.resolve_dispute_for_sender(&escrow_info.id); assert_eq!(resolved_info.status, EscrowStatus::DisputeResolvedForSender); - + // Verify tokens were returned to sender assert_eq!(token_client.balance(&sender), 10_000); // Original balance restored } @@ -199,14 +166,7 @@ fn test_can_dispute_functionality() { let client = EscrowClient::new(&env, &escrow_contract_id); // Create escrow - let escrow_info = client.create( - &sender, - &recipient, - &token_contract_id, - &500, - &3600, - &1800, - ); + let escrow_info = client.create(&sender, &recipient, &token_contract_id, &500, &3600, &1800); // Should be able to dispute initially assert!(client.can_dispute(&escrow_info.id)); @@ -225,14 +185,7 @@ fn test_release_disputed_escrow() { let client = EscrowClient::new(&env, &escrow_contract_id); // Create escrow and initiate dispute - let escrow_info = client.create( - &sender, - &recipient, - &token_contract_id, - &500, - &3600, - &1800, - ); + let escrow_info = client.create(&sender, &recipient, &token_contract_id, &500, &3600, &1800); client.initiate_dispute(&escrow_info.id, &symbol_short!("FRAUD")); @@ -330,14 +283,7 @@ fn test_paused_contract_validation() { client.set_paused(&true); // Try to create escrow while paused - should fail - client.create( - &sender, - &recipient, - &token_contract_id, - &500, - &3600, - &1800, - ); + client.create(&sender, &recipient, &token_contract_id, &500, &3600, &1800); } #[test] @@ -349,14 +295,7 @@ fn test_query_functions() { assert_eq!(client.get_escrow_count(), 0); // Create an escrow - let escrow_info = client.create( - &sender, - &recipient, - &token_contract_id, - &500, - &3600, - &1800, - ); + let escrow_info = client.create(&sender, &recipient, &token_contract_id, &500, &3600, &1800); // Check count increased assert_eq!(client.get_escrow_count(), 1); @@ -384,4 +323,4 @@ fn test_query_functions() { let disputed_escrows = client.get_escrows_by_status(&EscrowStatus::Disputed); assert_eq!(disputed_escrows.len(), 1); -} \ No newline at end of file +} diff --git a/tests/mock_token.rs b/tests/mock_token.rs index f617671..f55bf5e 100644 --- a/tests/mock_token.rs +++ b/tests/mock_token.rs @@ -12,19 +12,32 @@ pub struct MockToken; #[contractimpl] impl MockToken { pub fn initialize(env: Env, total_supply: i128) { - env.storage().instance().set(&DataKey::TotalSupply, &total_supply); + env.storage() + .instance() + .set(&DataKey::TotalSupply, &total_supply); } pub fn mint(env: Env, to: Address, amount: i128) { let balance = Self::balance(env.clone(), to.clone()); - env.storage().instance().set(&DataKey::Balance(to), &(balance + amount)); - - let total_supply: i128 = env.storage().instance().get(&DataKey::TotalSupply).unwrap_or(0); - env.storage().instance().set(&DataKey::TotalSupply, &(total_supply + amount)); + env.storage() + .instance() + .set(&DataKey::Balance(to), &(balance + amount)); + + let total_supply: i128 = env + .storage() + .instance() + .get(&DataKey::TotalSupply) + .unwrap_or(0); + env.storage() + .instance() + .set(&DataKey::TotalSupply, &(total_supply + amount)); } pub fn balance(env: Env, id: Address) -> i128 { - env.storage().instance().get(&DataKey::Balance(id)).unwrap_or(0) + env.storage() + .instance() + .get(&DataKey::Balance(id)) + .unwrap_or(0) } pub fn transfer(env: Env, from: Address, to: Address, amount: i128) { @@ -37,11 +50,21 @@ impl MockToken { panic!("insufficient balance"); } - env.storage().instance().set(&DataKey::Balance(from), &(from_balance - amount)); - env.storage().instance().set(&DataKey::Balance(to), &(to_balance + amount)); + env.storage() + .instance() + .set(&DataKey::Balance(from), &(from_balance - amount)); + env.storage() + .instance() + .set(&DataKey::Balance(to), &(to_balance + amount)); } - pub fn approve(env: Env, from: Address, spender: Address, amount: i128, _expiration_ledger: u32) { + pub fn approve( + env: Env, + from: Address, + spender: Address, + amount: i128, + _expiration_ledger: u32, + ) { from.require_auth(); // For simplicity, we'll just store the approval without expiration logic let key = (from, spender); @@ -68,9 +91,13 @@ impl MockToken { let to_balance = Self::balance(env.clone(), to.clone()); - env.storage().instance().set(&DataKey::Balance(from.clone()), &(from_balance - amount)); - env.storage().instance().set(&DataKey::Balance(to), &(to_balance + amount)); - + env.storage() + .instance() + .set(&DataKey::Balance(from.clone()), &(from_balance - amount)); + env.storage() + .instance() + .set(&DataKey::Balance(to), &(to_balance + amount)); + let key = (from, spender); env.storage().instance().set(&key, &(allowance - amount)); } @@ -83,10 +110,18 @@ impl MockToken { panic!("insufficient balance"); } - env.storage().instance().set(&DataKey::Balance(from), &(balance - amount)); - - let total_supply: i128 = env.storage().instance().get(&DataKey::TotalSupply).unwrap_or(0); - env.storage().instance().set(&DataKey::TotalSupply, &(total_supply - amount)); + env.storage() + .instance() + .set(&DataKey::Balance(from), &(balance - amount)); + + let total_supply: i128 = env + .storage() + .instance() + .get(&DataKey::TotalSupply) + .unwrap_or(0); + env.storage() + .instance() + .set(&DataKey::TotalSupply, &(total_supply - amount)); } pub fn burn_from(env: Env, spender: Address, from: Address, amount: i128) { @@ -102,13 +137,21 @@ impl MockToken { panic!("insufficient balance"); } - env.storage().instance().set(&DataKey::Balance(from.clone()), &(balance - amount)); - + env.storage() + .instance() + .set(&DataKey::Balance(from.clone()), &(balance - amount)); + let key = (from, spender); env.storage().instance().set(&key, &(allowance - amount)); - - let total_supply: i128 = env.storage().instance().get(&DataKey::TotalSupply).unwrap_or(0); - env.storage().instance().set(&DataKey::TotalSupply, &(total_supply - amount)); + + let total_supply: i128 = env + .storage() + .instance() + .get(&DataKey::TotalSupply) + .unwrap_or(0); + env.storage() + .instance() + .set(&DataKey::TotalSupply, &(total_supply - amount)); } pub fn decimals(_env: Env) -> u32 { @@ -122,4 +165,4 @@ impl MockToken { pub fn symbol(env: Env) -> soroban_sdk::String { soroban_sdk::String::from_str(&env, "MOCK") } -} \ No newline at end of file +} diff --git a/tests/pool_manager_tests.rs b/tests/pool_manager_tests.rs index da026ee..508623e 100644 --- a/tests/pool_manager_tests.rs +++ b/tests/pool_manager_tests.rs @@ -7,8 +7,7 @@ use soroban_sdk::{ use stellar_multisig_contract::{ conversion::Currency, pool_manager::{ - LiquidityPool, LiquidityPosition, PoolManagerConfig, PoolManagerContract, - PoolManagerEvent, + LiquidityPool, LiquidityPosition, PoolManagerConfig, PoolManagerContract, PoolManagerEvent, }, }; @@ -146,7 +145,7 @@ fn test_add_liquidity_multiple_providers() { // Check positions - need to retrieve current position states let current_position1 = client.get_position(&provider1, &Currency::USD); let current_position2 = client.get_position(&provider2, &Currency::USD); - + assert_eq!(current_position1.pool_share_bps, 6000); // 60% assert_eq!(current_position2.pool_share_bps, 4000); // 40% @@ -310,23 +309,19 @@ fn test_update_pool_balance_on_conversion() { // Initialize and add liquidity to both currencies client.initialize_pool_manager(&admin, &1_000_000_000, &100_000_000_000, &86400, &50); - + let usd_amount = 10_000_000_000; let eur_amount = 8_000_000_000; - + client.add_liquidity(&provider, &Currency::USD, &usd_amount, &Some(0)); client.add_liquidity(&provider, &Currency::EUR, &eur_amount, &Some(0)); // Simulate conversion: 1000 USD -> 850 EUR let from_amount = 1_000_000_000; let to_amount = 850_000_000; - - let (from_pool, to_pool) = client.update_pool_on_conversion( - &Currency::USD, - &Currency::EUR, - &from_amount, - &to_amount, - ); + + let (from_pool, to_pool) = + client.update_pool_on_conversion(&Currency::USD, &Currency::EUR, &from_amount, &to_amount); // Check USD pool (source) assert_eq!(from_pool.total_liquidity, usd_amount); @@ -503,7 +498,7 @@ fn test_pool_utilization_calculation() { // Add liquidity to EUR pool first client.add_liquidity(&provider, &Currency::EUR, &total_liquidity, &Some(0)); - + // Simulate 50% utilization through conversion let conversion_amount = 5_000_000_000; // 50 units client.update_pool_on_conversion(