From c9e132ad4e7d8976fbbf4baa10e2c93642ce3ca4 Mon Sep 17 00:00:00 2001 From: OlufunbiIK Date: Sun, 25 Jan 2026 19:21:08 +0100 Subject: [PATCH 01/13] =?UTF-8?q?feat:#42=20Define=20Event=20Schema=20and?= =?UTF-8?q?=20Emit=20Indexer-Friendly=20Contract=20Events=20=F0=9F=93=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/onchain/contracts/aid_escrow/src/lib.rs | 218 +++++++++- .../contracts/aid_escrow/tests/events.rs | 371 ++++++++++++++++++ package-lock.json | 1 - 3 files changed, 582 insertions(+), 8 deletions(-) create mode 100644 app/onchain/contracts/aid_escrow/tests/events.rs diff --git a/app/onchain/contracts/aid_escrow/src/lib.rs b/app/onchain/contracts/aid_escrow/src/lib.rs index 7c154e0..248a1bb 100644 --- a/app/onchain/contracts/aid_escrow/src/lib.rs +++ b/app/onchain/contracts/aid_escrow/src/lib.rs @@ -1,9 +1,19 @@ #![no_std] use soroban_sdk::{ - Address, Env, Map, String, Symbol, contract, contracterror, contractimpl, contracttype, + Address, Env, Map, String, Symbol, Vec, contract, contracterror, contractimpl, contracttype, }; +// ============================================================================ +// Event Topic Constants +// ============================================================================ + +pub const EVENT_PACKAGE_CREATED: &str = "package_created"; +pub const EVENT_PACKAGE_CLAIMED: &str = "package_claimed"; +pub const EVENT_PACKAGE_EXPIRED: &str = "package_expired"; +pub const EVENT_PACKAGE_CANCELLED: &str = "package_cancelled"; +pub const EVENT_CONTRACT_INITIALIZED: &str = "contract_initialized"; + #[contract] pub struct AidEscrow; @@ -44,6 +54,10 @@ pub enum Error { #[contractimpl] impl AidEscrow { + // ======================================================================== + // Core Contract Methods + // ======================================================================== + /// Initialize the contract with an admin pub fn initialize(env: Env, admin: Address) -> Result<(), Error> { env.storage() @@ -52,6 +66,10 @@ impl AidEscrow { env.storage() .instance() .set(&Symbol::new(&env, "package_counter"), &0u64); + + // Emit initialization event + Self::emit_contract_initialized(&env, admin); + Ok(()) } @@ -103,9 +121,9 @@ impl AidEscrow { }; let package = Package { - recipient, + recipient: recipient.clone(), amount, - token, + token: token.clone(), status: PackageStatus::Created, created_at, expires_at, @@ -116,6 +134,18 @@ impl AidEscrow { .persistent() .set(&(Symbol::new(&env, "package"), package_id), &package); + // Emit PackageCreated event + Self::emit_package_created( + &env, + package_id, + recipient, + amount, + token, + admin, + created_at, + expires_at, + ); + Ok(package_id) } @@ -132,11 +162,24 @@ impl AidEscrow { return Err(Error::PackageAlreadyClaimed); } - if package.expires_at > 0 && env.ledger().timestamp() > package.expires_at { + let timestamp = env.ledger().timestamp(); + + if package.expires_at > 0 && timestamp > package.expires_at { package.status = PackageStatus::Expired; env.storage() .persistent() .set(&(key.clone(), package_id), &package); + + // Emit PackageExpired event + Self::emit_package_expired( + &env, + package_id, + package.recipient.clone(), + package.amount, + package.token.clone(), + timestamp, + ); + return Err(Error::PackageExpired); } @@ -145,6 +188,57 @@ impl AidEscrow { package.status = PackageStatus::Claimed; env.storage().persistent().set(&(key, package_id), &package); + + // Emit PackageClaimed event + Self::emit_package_claimed( + &env, + package_id, + package.recipient.clone(), + package.amount, + package.token.clone(), + timestamp, + ); + + Ok(()) + } + + /// Cancel a package (admin only) + pub fn cancel_package(env: Env, package_id: u64) -> Result<(), Error> { + // Only admin can cancel + let admin: Address = env + .storage() + .instance() + .get(&Symbol::new(&env, "admin")) + .ok_or(Error::NotAuthorized)?; + admin.require_auth(); + + let key = Symbol::new(&env, "package"); + let mut package: Package = env + .storage() + .persistent() + .get(&(key.clone(), package_id)) + .ok_or(Error::PackageNotFound)?; + + if package.status == PackageStatus::Claimed { + return Err(Error::PackageAlreadyClaimed); + } + + let timestamp = env.ledger().timestamp(); + + package.status = PackageStatus::Cancelled; + env.storage().persistent().set(&(key, package_id), &package); + + // Emit PackageCancelled event + Self::emit_package_cancelled( + &env, + package_id, + package.recipient.clone(), + package.amount, + package.token.clone(), + admin, + timestamp, + ); + Ok(()) } @@ -163,13 +257,106 @@ impl AidEscrow { .unwrap_or(0); Ok(count) } + + // ======================================================================== + // Event Emission Helpers + // ======================================================================== + + fn emit_contract_initialized(env: &Env, admin: Address) { + let topics = Symbol::new(env, EVENT_CONTRACT_INITIALIZED); + let mut data = Vec::new(env); + data.push_back(admin); + data.push_back(env.ledger().timestamp()); + env.events().publish(topics, data); + } + + fn emit_package_created( + env: &Env, + package_id: u64, + recipient: Address, + amount: i128, + token: Address, + creator: Address, + created_at: u64, + expires_at: u64, + ) { + let topics = (Symbol::new(env, EVENT_PACKAGE_CREATED), package_id); + let mut data = Vec::new(env); + data.push_back(package_id); + data.push_back(recipient); + data.push_back(amount); + data.push_back(token); + data.push_back(creator); + data.push_back(created_at); + data.push_back(expires_at); + env.events().publish(topics, data); + } + + fn emit_package_claimed( + env: &Env, + package_id: u64, + recipient: Address, + amount: i128, + token: Address, + timestamp: u64, + ) { + let topics = (Symbol::new(env, EVENT_PACKAGE_CLAIMED), package_id); + let mut data = Vec::new(env); + data.push_back(package_id); + data.push_back(recipient); + data.push_back(amount); + data.push_back(token); + data.push_back(timestamp); + env.events().publish(topics, data); + } + + fn emit_package_expired( + env: &Env, + package_id: u64, + recipient: Address, + amount: i128, + token: Address, + timestamp: u64, + ) { + let topics = (Symbol::new(env, EVENT_PACKAGE_EXPIRED), package_id); + let mut data = Vec::new(env); + data.push_back(package_id); + data.push_back(recipient); + data.push_back(amount); + data.push_back(token); + data.push_back(timestamp); + env.events().publish(topics, data); + } + + fn emit_package_cancelled( + env: &Env, + package_id: u64, + recipient: Address, + amount: i128, + token: Address, + actor: Address, + timestamp: u64, + ) { + let topics = (Symbol::new(env, EVENT_PACKAGE_CANCELLED), package_id); + let mut data = Vec::new(env); + data.push_back(package_id); + data.push_back(recipient); + data.push_back(amount); + data.push_back(token); + data.push_back(actor); + data.push_back(timestamp); + env.events().publish(topics, data); + } } -// Unit tests +// ============================================================================ +// Unit Tests +// ============================================================================ + #[cfg(test)] mod test { use super::*; - use soroban_sdk::{Address, Env, testutils::Address as _}; + use soroban_sdk::{testutils::Address as _, Address, Env}; fn setup() -> (Env, AidEscrowClient<'static>) { let env = Env::default(); @@ -297,4 +484,21 @@ mod test { let result = client.get_package(&999); assert_eq!(result, None); } -} + + #[test] + fn test_cancel_package() { + let (env, client) = setup(); + let admin = Address::generate(&env); + let recipient = Address::generate(&env); + let token = Address::generate(&env); + + client.initialize(&admin); + env.mock_all_auths(); + + let package_id = client.create_package(&recipient, &1000, &token, &86400); + client.cancel_package(&package_id); + + let package = client.get_package(&package_id).unwrap(); + assert_eq!(package.status, PackageStatus::Cancelled); + } +} \ No newline at end of file diff --git a/app/onchain/contracts/aid_escrow/tests/events.rs b/app/onchain/contracts/aid_escrow/tests/events.rs new file mode 100644 index 0000000..383c8e1 --- /dev/null +++ b/app/onchain/contracts/aid_escrow/tests/events.rs @@ -0,0 +1,371 @@ +// app/onchain/contracts/aid_escrow/tests/events.rs +// Comprehensive event emission tests for AidEscrow contract + +#![cfg(test)] + +use soroban_sdk::{ + testutils::{Address as _, Events}, + Address, Env, Symbol, Vec as SdkVec, +}; + +// Import from your main contract +use aid_escrow::{ + AidEscrow, AidEscrowClient, PackageStatus, EVENT_CONTRACT_INITIALIZED, + EVENT_PACKAGE_CANCELLED, EVENT_PACKAGE_CLAIMED, EVENT_PACKAGE_CREATED, + EVENT_PACKAGE_EXPIRED, +}; + +fn setup_test() -> (Env, AidEscrowClient<'static>, Address) { + let env = Env::default(); + env.mock_all_auths(); + let contract_id = env.register(AidEscrow, ()); + let client = AidEscrowClient::new(&env, &contract_id); + let admin = Address::generate(&env); + client.initialize(&admin); + (env, client, admin) +} + +#[test] +fn test_contract_initialized_event() { + let env = Env::default(); + env.mock_all_auths(); + let contract_id = env.register(AidEscrow, ()); + let client = AidEscrowClient::new(&env, &contract_id); + let admin = Address::generate(&env); + + client.initialize(&admin); + + // Get events + let events = env.events().all(); + + // Find ContractInitialized event + let init_event = events.iter().find(|e| { + if let Ok((_, topics, _)) = e.clone().try_into() { + let topic_vec: SdkVec = topics; + if let Some(first_topic) = topic_vec.first() { + return first_topic == Symbol::new(&env, EVENT_CONTRACT_INITIALIZED); + } + } + false + }); + + assert!( + init_event.is_some(), + "ContractInitialized event not found" + ); +} + +#[test] +fn test_package_created_event() { + let (env, client, _admin) = setup_test(); + let recipient = Address::generate(&env); + let token = Address::generate(&env); + let amount: i128 = 1000; + let expires_in: u64 = 86400; + + let package_id = client.create_package(&recipient, &amount, &token, &expires_in); + + // Get events + let events = env.events().all(); + + // Find PackageCreated event + let package_created_event = events.iter().find(|e| { + if let Ok((_, topics, _)) = e.clone().try_into() { + let topic_vec: SdkVec = topics; + if let Some(first_topic) = topic_vec.first() { + return first_topic == Symbol::new(&env, EVENT_PACKAGE_CREATED); + } + } + false + }); + + assert!( + package_created_event.is_some(), + "PackageCreated event not found" + ); + + // Verify the package was created + let package = client.get_package(&package_id).unwrap(); + assert_eq!(package.recipient, recipient); + assert_eq!(package.amount, amount); + assert_eq!(package.status, PackageStatus::Created); +} + +#[test] +fn test_package_claimed_event() { + let (env, client, _admin) = setup_test(); + let recipient = Address::generate(&env); + let token = Address::generate(&env); + let amount: i128 = 500; + + let package_id = client.create_package(&recipient, &amount, &token, &86400); + + // Clear events from creation + let events_before = env.events().all().len(); + + client.claim_package(&package_id); + + // Get events + let events = env.events().all(); + + // Find PackageClaimed event (skip earlier events) + let package_claimed_event = events.iter().skip(events_before).find(|e| { + if let Ok((_, topics, _)) = e.clone().try_into() { + let topic_vec: SdkVec = topics; + if let Some(first_topic) = topic_vec.first() { + return first_topic == Symbol::new(&env, EVENT_PACKAGE_CLAIMED); + } + } + false + }); + + assert!( + package_claimed_event.is_some(), + "PackageClaimed event not found" + ); + + // Verify the package was claimed + let package = client.get_package(&package_id).unwrap(); + assert_eq!(package.status, PackageStatus::Claimed); +} + +#[test] +fn test_package_expired_event() { + let (env, client, _admin) = setup_test(); + let recipient = Address::generate(&env); + let token = Address::generate(&env); + let amount: i128 = 750; + let expires_in: u64 = 100; // Short expiry + + let package_id = client.create_package(&recipient, &amount, &token, &expires_in); + + // Fast-forward time past expiry + env.ledger().with_mut(|li| { + li.timestamp = li.timestamp + expires_in + 1; + }); + + let events_before = env.events().all().len(); + + // Try to claim - should fail and emit expired event + let result = client.try_claim_package(&package_id); + assert!(result.is_err()); + + // Get events + let events = env.events().all(); + + // Find PackageExpired event + let package_expired_event = events.iter().skip(events_before).find(|e| { + if let Ok((_, topics, _)) = e.clone().try_into() { + let topic_vec: SdkVec = topics; + if let Some(first_topic) = topic_vec.first() { + return first_topic == Symbol::new(&env, EVENT_PACKAGE_EXPIRED); + } + } + false + }); + + assert!( + package_expired_event.is_some(), + "PackageExpired event not found" + ); + + // Verify the package is expired + let package = client.get_package(&package_id).unwrap(); + assert_eq!(package.status, PackageStatus::Expired); +} + +#[test] +fn test_package_cancelled_event() { + let (env, client, _admin) = setup_test(); + let recipient = Address::generate(&env); + let token = Address::generate(&env); + let amount: i128 = 300; + + let package_id = client.create_package(&recipient, &amount, &token, &86400); + + let events_before = env.events().all().len(); + + client.cancel_package(&package_id); + + // Get events + let events = env.events().all(); + + // Find PackageCancelled event + let package_cancelled_event = events.iter().skip(events_before).find(|e| { + if let Ok((_, topics, _)) = e.clone().try_into() { + let topic_vec: SdkVec = topics; + if let Some(first_topic) = topic_vec.first() { + return first_topic == Symbol::new(&env, EVENT_PACKAGE_CANCELLED); + } + } + false + }); + + assert!( + package_cancelled_event.is_some(), + "PackageCancelled event not found" + ); + + // Verify the package was cancelled + let package = client.get_package(&package_id).unwrap(); + assert_eq!(package.status, PackageStatus::Cancelled); +} + +#[test] +fn test_multiple_events_in_workflow() { + let (env, client, _admin) = setup_test(); + let recipient = Address::generate(&env); + let token = Address::generate(&env); + let amount: i128 = 1500; + + // Complete workflow: create -> claim + let package_id = client.create_package(&recipient, &amount, &token, &86400); + client.claim_package(&package_id); + + // Get all events + let events = env.events().all(); + + // Count PackageCreated and PackageClaimed events + let mut created_count = 0; + let mut claimed_count = 0; + + for event in events.iter() { + if let Ok((_, topics, _)) = event.clone().try_into() { + let topic_vec: SdkVec = topics; + if let Some(first_topic) = topic_vec.first() { + if first_topic == Symbol::new(&env, EVENT_PACKAGE_CREATED) { + created_count += 1; + } else if first_topic == Symbol::new(&env, EVENT_PACKAGE_CLAIMED) { + claimed_count += 1; + } + } + } + } + + assert_eq!(created_count, 1, "Should have exactly 1 PackageCreated event"); + assert_eq!(claimed_count, 1, "Should have exactly 1 PackageClaimed event"); +} + +#[test] +fn test_multiple_packages_separate_events() { + let (env, client, _admin) = setup_test(); + let recipient1 = Address::generate(&env); + let recipient2 = Address::generate(&env); + let token = Address::generate(&env); + + // Create two packages + let package_id_1 = client.create_package(&recipient1, &1000, &token, &86400); + let package_id_2 = client.create_package(&recipient2, &2000, &token, &86400); + + // Claim first package + client.claim_package(&package_id_1); + + // Cancel second package + client.cancel_package(&package_id_2); + + // Get all events + let events = env.events().all(); + + // Count event types + let mut created_count = 0; + let mut claimed_count = 0; + let mut cancelled_count = 0; + + for event in events.iter() { + if let Ok((_, topics, _)) = event.clone().try_into() { + let topic_vec: SdkVec = topics; + if let Some(first_topic) = topic_vec.first() { + if first_topic == Symbol::new(&env, EVENT_PACKAGE_CREATED) { + created_count += 1; + } else if first_topic == Symbol::new(&env, EVENT_PACKAGE_CLAIMED) { + claimed_count += 1; + } else if first_topic == Symbol::new(&env, EVENT_PACKAGE_CANCELLED) { + cancelled_count += 1; + } + } + } + } + + assert_eq!(created_count, 2, "Should have 2 PackageCreated events"); + assert_eq!(claimed_count, 1, "Should have 1 PackageClaimed event"); + assert_eq!(cancelled_count, 1, "Should have 1 PackageCancelled event"); + + // Verify package states + let package1 = client.get_package(&package_id_1).unwrap(); + assert_eq!(package1.status, PackageStatus::Claimed); + + let package2 = client.get_package(&package_id_2).unwrap(); + assert_eq!(package2.status, PackageStatus::Cancelled); +} + +#[test] +fn test_event_topics_include_package_id() { + let (env, client, _admin) = setup_test(); + let recipient = Address::generate(&env); + let token = Address::generate(&env); + + let package_id = client.create_package(&recipient, &1000, &token, &86400); + + // Get events + let events = env.events().all(); + + // Find PackageCreated event and verify it has package_id in topics + let package_created_event = events.iter().find(|e| { + if let Ok((_, topics, _)) = e.clone().try_into() { + let topic_vec: SdkVec = topics; + if let Some(first_topic) = topic_vec.first() { + return first_topic == Symbol::new(&env, EVENT_PACKAGE_CREATED); + } + } + false + }); + + assert!( + package_created_event.is_some(), + "PackageCreated event not found" + ); + + // Verify topics contain both event name and package_id + if let Ok((_, topics, _)) = package_created_event.unwrap().clone().try_into() { + let topic_vec: SdkVec = topics; + // First topic is event name, second should be package_id + assert!(topic_vec.len() >= 1, "Topics should contain event name"); + } +} + +#[test] +fn test_no_events_on_failed_operations() { + let (env, client, _admin) = setup_test(); + let recipient = Address::generate(&env); + let token = Address::generate(&env); + + let events_before = env.events().all().len(); + + // Try to create package with invalid amount (should fail) + let result = client.try_create_package(&recipient, &0, &token, &86400); + assert!(result.is_err()); + + let events_after = env.events().all().len(); + + // Should not have created any new events (except possibly initialization) + // The key is that no PackageCreated event should be emitted + let events = env.events().all(); + let package_created_count = events + .iter() + .skip(events_before) + .filter(|e| { + if let Ok((_, topics, _)) = e.clone().try_into() { + let topic_vec: SdkVec = topics; + if let Some(first_topic) = topic_vec.first() { + return first_topic == Symbol::new(&env, EVENT_PACKAGE_CREATED); + } + } + false + }) + .count(); + + assert_eq!( + package_created_count, 0, + "Should not emit PackageCreated on failed creation" + ); +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index cd5264a..8b78845 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,5 @@ { "name": "soter", - "lockfileVersion": 3, "lockfileVersion": 2, "requires": true, "packages": { From 4f273ab6a7b743ecb9851c2b2ba986701d9d697d Mon Sep 17 00:00:00 2001 From: OlufunbiIK Date: Mon, 26 Jan 2026 00:31:09 +0100 Subject: [PATCH 02/13] ran tests --- app/onchain/.cargo/config.toml | 10 +- app/onchain/contracts/aid_escrow/src/lib.rs | 64 +- .../test/test_cancel_package.1.json | 310 ++++++++++ .../test_claim_package_not_recipient.1.json | 39 +- .../test_contract_initialized_event.1.json | 122 ++++ ...est_event_topics_include_package_id.1.json | 300 ++++++++++ .../test_multiple_events_in_workflow.1.json | 346 +++++++++++ ...t_multiple_packages_separate_events.1.json | 564 ++++++++++++++++++ ...test_no_events_on_failed_operations.1.json | 95 +++ .../test_package_cancelled_event.1.json | 349 +++++++++++ .../test_package_claimed_event.1.json | 346 +++++++++++ .../test_package_created_event.1.json | 258 ++++++++ .../test_package_expired_event.1.json | 295 +++++++++ .../contracts/aid_escrow/tests/events.rs | 112 ++-- 14 files changed, 3110 insertions(+), 100 deletions(-) create mode 100644 app/onchain/contracts/aid_escrow/test_snapshots/test/test_cancel_package.1.json create mode 100644 app/onchain/contracts/aid_escrow/test_snapshots/test_contract_initialized_event.1.json create mode 100644 app/onchain/contracts/aid_escrow/test_snapshots/test_event_topics_include_package_id.1.json create mode 100644 app/onchain/contracts/aid_escrow/test_snapshots/test_multiple_events_in_workflow.1.json create mode 100644 app/onchain/contracts/aid_escrow/test_snapshots/test_multiple_packages_separate_events.1.json create mode 100644 app/onchain/contracts/aid_escrow/test_snapshots/test_no_events_on_failed_operations.1.json create mode 100644 app/onchain/contracts/aid_escrow/test_snapshots/test_package_cancelled_event.1.json create mode 100644 app/onchain/contracts/aid_escrow/test_snapshots/test_package_claimed_event.1.json create mode 100644 app/onchain/contracts/aid_escrow/test_snapshots/test_package_created_event.1.json create mode 100644 app/onchain/contracts/aid_escrow/test_snapshots/test_package_expired_event.1.json diff --git a/app/onchain/.cargo/config.toml b/app/onchain/.cargo/config.toml index 5e91b6a..4dbf02e 100644 --- a/app/onchain/.cargo/config.toml +++ b/app/onchain/.cargo/config.toml @@ -1,8 +1,8 @@ -[build] -target = "wasm32-unknown-unknown" - [target.wasm32-unknown-unknown] -runner = "echo" # Placeholder, can be customized +runner = "echo" [env] -RUSTFLAGS = "-C link-arg=-zstack-size=65536" \ No newline at end of file +RUSTFLAGS = "-C link-arg=-zstack-size=65536" + +[alias] +wasm = "build --release --target wasm32-unknown-unknown" diff --git a/app/onchain/contracts/aid_escrow/src/lib.rs b/app/onchain/contracts/aid_escrow/src/lib.rs index 248a1bb..24f1ab1 100644 --- a/app/onchain/contracts/aid_escrow/src/lib.rs +++ b/app/onchain/contracts/aid_escrow/src/lib.rs @@ -1,7 +1,7 @@ #![no_std] use soroban_sdk::{ - Address, Env, Map, String, Symbol, Vec, contract, contracterror, contractimpl, contracttype, + Address, Env, Map, String, Symbol, contract, contracterror, contractimpl, contracttype, }; // ============================================================================ @@ -66,10 +66,10 @@ impl AidEscrow { env.storage() .instance() .set(&Symbol::new(&env, "package_counter"), &0u64); - + // Emit initialization event Self::emit_contract_initialized(&env, admin); - + Ok(()) } @@ -136,14 +136,7 @@ impl AidEscrow { // Emit PackageCreated event Self::emit_package_created( - &env, - package_id, - recipient, - amount, - token, - admin, - created_at, - expires_at, + &env, package_id, recipient, amount, token, admin, created_at, expires_at, ); Ok(package_id) @@ -169,7 +162,7 @@ impl AidEscrow { env.storage() .persistent() .set(&(key.clone(), package_id), &package); - + // Emit PackageExpired event Self::emit_package_expired( &env, @@ -179,7 +172,7 @@ impl AidEscrow { package.token.clone(), timestamp, ); - + return Err(Error::PackageExpired); } @@ -188,7 +181,7 @@ impl AidEscrow { package.status = PackageStatus::Claimed; env.storage().persistent().set(&(key, package_id), &package); - + // Emit PackageClaimed event Self::emit_package_claimed( &env, @@ -198,7 +191,7 @@ impl AidEscrow { package.token.clone(), timestamp, ); - + Ok(()) } @@ -263,10 +256,8 @@ impl AidEscrow { // ======================================================================== fn emit_contract_initialized(env: &Env, admin: Address) { - let topics = Symbol::new(env, EVENT_CONTRACT_INITIALIZED); - let mut data = Vec::new(env); - data.push_back(admin); - data.push_back(env.ledger().timestamp()); + let topics = (Symbol::new(env, EVENT_CONTRACT_INITIALIZED),); + let data = (admin, env.ledger().timestamp()); env.events().publish(topics, data); } @@ -281,14 +272,7 @@ impl AidEscrow { expires_at: u64, ) { let topics = (Symbol::new(env, EVENT_PACKAGE_CREATED), package_id); - let mut data = Vec::new(env); - data.push_back(package_id); - data.push_back(recipient); - data.push_back(amount); - data.push_back(token); - data.push_back(creator); - data.push_back(created_at); - data.push_back(expires_at); + let data = (recipient, amount, token, creator, created_at, expires_at); env.events().publish(topics, data); } @@ -301,12 +285,7 @@ impl AidEscrow { timestamp: u64, ) { let topics = (Symbol::new(env, EVENT_PACKAGE_CLAIMED), package_id); - let mut data = Vec::new(env); - data.push_back(package_id); - data.push_back(recipient); - data.push_back(amount); - data.push_back(token); - data.push_back(timestamp); + let data = (recipient, amount, token, timestamp); env.events().publish(topics, data); } @@ -319,12 +298,7 @@ impl AidEscrow { timestamp: u64, ) { let topics = (Symbol::new(env, EVENT_PACKAGE_EXPIRED), package_id); - let mut data = Vec::new(env); - data.push_back(package_id); - data.push_back(recipient); - data.push_back(amount); - data.push_back(token); - data.push_back(timestamp); + let data = (recipient, amount, token, timestamp); env.events().publish(topics, data); } @@ -338,13 +312,7 @@ impl AidEscrow { timestamp: u64, ) { let topics = (Symbol::new(env, EVENT_PACKAGE_CANCELLED), package_id); - let mut data = Vec::new(env); - data.push_back(package_id); - data.push_back(recipient); - data.push_back(amount); - data.push_back(token); - data.push_back(actor); - data.push_back(timestamp); + let data = (recipient, amount, token, actor, timestamp); env.events().publish(topics, data); } } @@ -356,7 +324,7 @@ impl AidEscrow { #[cfg(test)] mod test { use super::*; - use soroban_sdk::{testutils::Address as _, Address, Env}; + use soroban_sdk::{Address, Env, testutils::Address as _}; fn setup() -> (Env, AidEscrowClient<'static>) { let env = Env::default(); @@ -501,4 +469,4 @@ mod test { let package = client.get_package(&package_id).unwrap(); assert_eq!(package.status, PackageStatus::Cancelled); } -} \ No newline at end of file +} diff --git a/app/onchain/contracts/aid_escrow/test_snapshots/test/test_cancel_package.1.json b/app/onchain/contracts/aid_escrow/test_snapshots/test/test_cancel_package.1.json new file mode 100644 index 0000000..64380f9 --- /dev/null +++ b/app/onchain/contracts/aid_escrow/test_snapshots/test/test_cancel_package.1.json @@ -0,0 +1,310 @@ +{ + "generators": { + "address": 4, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "create_package", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": "1000" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "u64": "86400" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "cancel_package", + "args": [ + { + "u64": "0" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 23, + "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": { + "vec": [ + { + "symbol": "package" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "package" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": "1000" + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": "0" + } + }, + { + "key": { + "symbol": "expires_at" + }, + "val": { + "u64": "86400" + } + }, + { + "key": { + "symbol": "metadata" + }, + "val": { + "map": [] + } + }, + { + "key": { + "symbol": "recipient" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "u32": 3 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "symbol": "admin" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "package_counter" + }, + "val": { + "u64": "1" + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "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/app/onchain/contracts/aid_escrow/test_snapshots/test/test_claim_package_not_recipient.1.json b/app/onchain/contracts/aid_escrow/test_snapshots/test/test_claim_package_not_recipient.1.json index 99de054..468d5e0 100644 --- a/app/onchain/contracts/aid_escrow/test_snapshots/test/test_claim_package_not_recipient.1.json +++ b/app/onchain/contracts/aid_escrow/test_snapshots/test/test_claim_package_not_recipient.1.json @@ -305,5 +305,42 @@ ] ] }, - "events": [] + "events": [ + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "package_claimed" + }, + { + "u64": "0" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": "1000" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + }, + { + "u64": "0" + } + ] + } + } + } + }, + "failed_call": false + } + ] } \ No newline at end of file diff --git a/app/onchain/contracts/aid_escrow/test_snapshots/test_contract_initialized_event.1.json b/app/onchain/contracts/aid_escrow/test_snapshots/test_contract_initialized_event.1.json new file mode 100644 index 0000000..f5ca969 --- /dev/null +++ b/app/onchain/contracts/aid_escrow/test_snapshots/test_contract_initialized_event.1.json @@ -0,0 +1,122 @@ +{ + "generators": { + "address": 2, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [] + ], + "ledger": { + "protocol_version": 23, + "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_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "symbol": "admin" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "package_counter" + }, + "val": { + "u64": "0" + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "contract_initialized" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "u64": "0" + } + ] + } + } + } + }, + "failed_call": false + } + ] +} \ No newline at end of file diff --git a/app/onchain/contracts/aid_escrow/test_snapshots/test_event_topics_include_package_id.1.json b/app/onchain/contracts/aid_escrow/test_snapshots/test_event_topics_include_package_id.1.json new file mode 100644 index 0000000..5038db9 --- /dev/null +++ b/app/onchain/contracts/aid_escrow/test_snapshots/test_event_topics_include_package_id.1.json @@ -0,0 +1,300 @@ +{ + "generators": { + "address": 4, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "create_package", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": "1000" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "u64": "86400" + } + ] + } + }, + "sub_invocations": [] + } + ] + ] + ], + "ledger": { + "protocol_version": 23, + "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": { + "vec": [ + { + "symbol": "package" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "package" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": "1000" + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": "0" + } + }, + { + "key": { + "symbol": "expires_at" + }, + "val": { + "u64": "86400" + } + }, + { + "key": { + "symbol": "metadata" + }, + "val": { + "map": [] + } + }, + { + "key": { + "symbol": "recipient" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "symbol": "admin" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "package_counter" + }, + "val": { + "u64": "1" + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "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": [ + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "package_created" + }, + { + "u64": "0" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": "1000" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "u64": "0" + }, + { + "u64": "86400" + } + ] + } + } + } + }, + "failed_call": false + } + ] +} \ No newline at end of file diff --git a/app/onchain/contracts/aid_escrow/test_snapshots/test_multiple_events_in_workflow.1.json b/app/onchain/contracts/aid_escrow/test_snapshots/test_multiple_events_in_workflow.1.json new file mode 100644 index 0000000..bc37d45 --- /dev/null +++ b/app/onchain/contracts/aid_escrow/test_snapshots/test_multiple_events_in_workflow.1.json @@ -0,0 +1,346 @@ +{ + "generators": { + "address": 4, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "create_package", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": "1500" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "u64": "86400" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "claim_package", + "args": [ + { + "u64": "0" + } + ] + } + }, + "sub_invocations": [] + } + ] + ] + ], + "ledger": { + "protocol_version": 23, + "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": { + "vec": [ + { + "symbol": "package" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "package" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": "1500" + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": "0" + } + }, + { + "key": { + "symbol": "expires_at" + }, + "val": { + "u64": "86400" + } + }, + { + "key": { + "symbol": "metadata" + }, + "val": { + "map": [] + } + }, + { + "key": { + "symbol": "recipient" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "symbol": "admin" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "package_counter" + }, + "val": { + "u64": "1" + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "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": [ + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "package_claimed" + }, + { + "u64": "0" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": "1500" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "u64": "0" + } + ] + } + } + } + }, + "failed_call": false + } + ] +} \ No newline at end of file diff --git a/app/onchain/contracts/aid_escrow/test_snapshots/test_multiple_packages_separate_events.1.json b/app/onchain/contracts/aid_escrow/test_snapshots/test_multiple_packages_separate_events.1.json new file mode 100644 index 0000000..51afb19 --- /dev/null +++ b/app/onchain/contracts/aid_escrow/test_snapshots/test_multiple_packages_separate_events.1.json @@ -0,0 +1,564 @@ +{ + "generators": { + "address": 5, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "create_package", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": "1000" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + }, + { + "u64": "86400" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "create_package", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "i128": "2000" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + }, + { + "u64": "86400" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "claim_package", + "args": [ + { + "u64": "0" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "cancel_package", + "args": [ + { + "u64": "1" + } + ] + } + }, + "sub_invocations": [] + } + ] + ] + ], + "ledger": { + "protocol_version": 23, + "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": { + "vec": [ + { + "symbol": "package" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "package" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": "1000" + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": "0" + } + }, + { + "key": { + "symbol": "expires_at" + }, + "val": { + "u64": "86400" + } + }, + { + "key": { + "symbol": "metadata" + }, + "val": { + "map": [] + } + }, + { + "key": { + "symbol": "recipient" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "package" + }, + { + "u64": "1" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "package" + }, + { + "u64": "1" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": "2000" + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": "0" + } + }, + { + "key": { + "symbol": "expires_at" + }, + "val": { + "u64": "86400" + } + }, + { + "key": { + "symbol": "metadata" + }, + "val": { + "map": [] + } + }, + { + "key": { + "symbol": "recipient" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "u32": 3 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "symbol": "admin" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "package_counter" + }, + "val": { + "u64": "2" + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "4837995959683129791" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "4837995959683129791" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": "1033654523790656264" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": "1033654523790656264" + } + }, + "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": [ + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "package_cancelled" + }, + { + "u64": "1" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "i128": "2000" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "u64": "0" + } + ] + } + } + } + }, + "failed_call": false + } + ] +} \ No newline at end of file diff --git a/app/onchain/contracts/aid_escrow/test_snapshots/test_no_events_on_failed_operations.1.json b/app/onchain/contracts/aid_escrow/test_snapshots/test_no_events_on_failed_operations.1.json new file mode 100644 index 0000000..0cb97cf --- /dev/null +++ b/app/onchain/contracts/aid_escrow/test_snapshots/test_no_events_on_failed_operations.1.json @@ -0,0 +1,95 @@ +{ + "generators": { + "address": 4, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [], + [] + ], + "ledger": { + "protocol_version": 23, + "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_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "symbol": "admin" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "package_counter" + }, + "val": { + "u64": "0" + } + } + ] + } + } + } + }, + "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/app/onchain/contracts/aid_escrow/test_snapshots/test_package_cancelled_event.1.json b/app/onchain/contracts/aid_escrow/test_snapshots/test_package_cancelled_event.1.json new file mode 100644 index 0000000..14a3b55 --- /dev/null +++ b/app/onchain/contracts/aid_escrow/test_snapshots/test_package_cancelled_event.1.json @@ -0,0 +1,349 @@ +{ + "generators": { + "address": 4, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "create_package", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": "300" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "u64": "86400" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "cancel_package", + "args": [ + { + "u64": "0" + } + ] + } + }, + "sub_invocations": [] + } + ] + ] + ], + "ledger": { + "protocol_version": 23, + "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": { + "vec": [ + { + "symbol": "package" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "package" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": "300" + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": "0" + } + }, + { + "key": { + "symbol": "expires_at" + }, + "val": { + "u64": "86400" + } + }, + { + "key": { + "symbol": "metadata" + }, + "val": { + "map": [] + } + }, + { + "key": { + "symbol": "recipient" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "u32": 3 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "symbol": "admin" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "package_counter" + }, + "val": { + "u64": "1" + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "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": [ + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "package_cancelled" + }, + { + "u64": "0" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": "300" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "u64": "0" + } + ] + } + } + } + }, + "failed_call": false + } + ] +} \ No newline at end of file diff --git a/app/onchain/contracts/aid_escrow/test_snapshots/test_package_claimed_event.1.json b/app/onchain/contracts/aid_escrow/test_snapshots/test_package_claimed_event.1.json new file mode 100644 index 0000000..395efe2 --- /dev/null +++ b/app/onchain/contracts/aid_escrow/test_snapshots/test_package_claimed_event.1.json @@ -0,0 +1,346 @@ +{ + "generators": { + "address": 4, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "create_package", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": "500" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "u64": "86400" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "claim_package", + "args": [ + { + "u64": "0" + } + ] + } + }, + "sub_invocations": [] + } + ] + ] + ], + "ledger": { + "protocol_version": 23, + "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": { + "vec": [ + { + "symbol": "package" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "package" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": "500" + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": "0" + } + }, + { + "key": { + "symbol": "expires_at" + }, + "val": { + "u64": "86400" + } + }, + { + "key": { + "symbol": "metadata" + }, + "val": { + "map": [] + } + }, + { + "key": { + "symbol": "recipient" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "symbol": "admin" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "package_counter" + }, + "val": { + "u64": "1" + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "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": [ + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "package_claimed" + }, + { + "u64": "0" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": "500" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "u64": "0" + } + ] + } + } + } + }, + "failed_call": false + } + ] +} \ No newline at end of file diff --git a/app/onchain/contracts/aid_escrow/test_snapshots/test_package_created_event.1.json b/app/onchain/contracts/aid_escrow/test_snapshots/test_package_created_event.1.json new file mode 100644 index 0000000..b5328cd --- /dev/null +++ b/app/onchain/contracts/aid_escrow/test_snapshots/test_package_created_event.1.json @@ -0,0 +1,258 @@ +{ + "generators": { + "address": 4, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "create_package", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": "1000" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "u64": "86400" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 23, + "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": { + "vec": [ + { + "symbol": "package" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "package" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": "1000" + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": "0" + } + }, + { + "key": { + "symbol": "expires_at" + }, + "val": { + "u64": "86400" + } + }, + { + "key": { + "symbol": "metadata" + }, + "val": { + "map": [] + } + }, + { + "key": { + "symbol": "recipient" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "symbol": "admin" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "package_counter" + }, + "val": { + "u64": "1" + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "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/app/onchain/contracts/aid_escrow/test_snapshots/test_package_expired_event.1.json b/app/onchain/contracts/aid_escrow/test_snapshots/test_package_expired_event.1.json new file mode 100644 index 0000000..46ab864 --- /dev/null +++ b/app/onchain/contracts/aid_escrow/test_snapshots/test_package_expired_event.1.json @@ -0,0 +1,295 @@ +{ + "generators": { + "address": 4, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "create_package", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": "750" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "u64": "100" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 23, + "sequence_number": 0, + "timestamp": 101, + "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": { + "vec": [ + { + "symbol": "package" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "package" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": "750" + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": "0" + } + }, + { + "key": { + "symbol": "expires_at" + }, + "val": { + "u64": "100" + } + }, + { + "key": { + "symbol": "metadata" + }, + "val": { + "map": [] + } + }, + { + "key": { + "symbol": "recipient" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "symbol": "admin" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "package_counter" + }, + "val": { + "u64": "1" + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "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": [ + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "package_expired" + }, + { + "u64": "0" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": "750" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "u64": "101" + } + ] + } + } + } + }, + "failed_call": true + } + ] +} \ No newline at end of file diff --git a/app/onchain/contracts/aid_escrow/tests/events.rs b/app/onchain/contracts/aid_escrow/tests/events.rs index 383c8e1..46af70d 100644 --- a/app/onchain/contracts/aid_escrow/tests/events.rs +++ b/app/onchain/contracts/aid_escrow/tests/events.rs @@ -3,16 +3,17 @@ #![cfg(test)] + + use soroban_sdk::{ - testutils::{Address as _, Events}, - Address, Env, Symbol, Vec as SdkVec, + testutils::{Address as _, Events, Ledger}, + Address, Env, Symbol, Val, TryFromVal, }; // Import from your main contract use aid_escrow::{ AidEscrow, AidEscrowClient, PackageStatus, EVENT_CONTRACT_INITIALIZED, - EVENT_PACKAGE_CANCELLED, EVENT_PACKAGE_CLAIMED, EVENT_PACKAGE_CREATED, - EVENT_PACKAGE_EXPIRED, + EVENT_PACKAGE_CANCELLED, EVENT_PACKAGE_CLAIMED, EVENT_PACKAGE_CREATED, EVENT_PACKAGE_EXPIRED, }; fn setup_test() -> (Env, AidEscrowClient<'static>, Address) { @@ -41,18 +42,17 @@ fn test_contract_initialized_event() { // Find ContractInitialized event let init_event = events.iter().find(|e| { if let Ok((_, topics, _)) = e.clone().try_into() { - let topic_vec: SdkVec = topics; + let topic_vec: soroban_sdk::Vec = topics; if let Some(first_topic) = topic_vec.first() { - return first_topic == Symbol::new(&env, EVENT_CONTRACT_INITIALIZED); + if let Ok(symbol) = Symbol::try_from_val(&env, &first_topic) { + return symbol == Symbol::new(&env, EVENT_CONTRACT_INITIALIZED); + } } } false }); - assert!( - init_event.is_some(), - "ContractInitialized event not found" - ); + assert!(init_event.is_some(), "ContractInitialized event not found"); } #[test] @@ -71,9 +71,11 @@ fn test_package_created_event() { // Find PackageCreated event let package_created_event = events.iter().find(|e| { if let Ok((_, topics, _)) = e.clone().try_into() { - let topic_vec: SdkVec = topics; + let topic_vec: soroban_sdk::Vec = topics; if let Some(first_topic) = topic_vec.first() { - return first_topic == Symbol::new(&env, EVENT_PACKAGE_CREATED); + if let Ok(symbol) = Symbol::try_from_val(&env, &first_topic) { + return symbol == Symbol::new(&env, EVENT_PACKAGE_CREATED); + } } } false @@ -99,21 +101,23 @@ fn test_package_claimed_event() { let amount: i128 = 500; let package_id = client.create_package(&recipient, &amount, &token, &86400); - + // Clear events from creation let events_before = env.events().all().len(); - + client.claim_package(&package_id); // Get events let events = env.events().all(); - + // Find PackageClaimed event (skip earlier events) - let package_claimed_event = events.iter().skip(events_before).find(|e| { + let package_claimed_event = events.iter().skip(events_before as usize).find(|e| { if let Ok((_, topics, _)) = e.clone().try_into() { - let topic_vec: SdkVec = topics; + let topic_vec: soroban_sdk::Vec = topics; if let Some(first_topic) = topic_vec.first() { - return first_topic == Symbol::new(&env, EVENT_PACKAGE_CLAIMED); + if let Ok(symbol) = Symbol::try_from_val(&env, &first_topic) { + return symbol == Symbol::new(&env, EVENT_PACKAGE_CLAIMED); + } } } false @@ -154,11 +158,13 @@ fn test_package_expired_event() { let events = env.events().all(); // Find PackageExpired event - let package_expired_event = events.iter().skip(events_before).find(|e| { + let package_expired_event = events.iter().skip(events_before as usize).find(|e| { if let Ok((_, topics, _)) = e.clone().try_into() { - let topic_vec: SdkVec = topics; + let topic_vec: soroban_sdk::Vec = topics; if let Some(first_topic) = topic_vec.first() { - return first_topic == Symbol::new(&env, EVENT_PACKAGE_EXPIRED); + if let Ok(symbol) = Symbol::try_from_val(&env, &first_topic) { + return symbol == Symbol::new(&env, EVENT_PACKAGE_EXPIRED); + } } } false @@ -191,11 +197,13 @@ fn test_package_cancelled_event() { let events = env.events().all(); // Find PackageCancelled event - let package_cancelled_event = events.iter().skip(events_before).find(|e| { + let package_cancelled_event = events.iter().skip(events_before as usize).find(|e| { if let Ok((_, topics, _)) = e.clone().try_into() { - let topic_vec: SdkVec = topics; + let topic_vec: soroban_sdk::Vec = topics; if let Some(first_topic) = topic_vec.first() { - return first_topic == Symbol::new(&env, EVENT_PACKAGE_CANCELLED); + if let Ok(symbol) = Symbol::try_from_val(&env, &first_topic) { + return symbol == Symbol::new(&env, EVENT_PACKAGE_CANCELLED); + } } } false @@ -231,19 +239,27 @@ fn test_multiple_events_in_workflow() { for event in events.iter() { if let Ok((_, topics, _)) = event.clone().try_into() { - let topic_vec: SdkVec = topics; + let topic_vec: soroban_sdk::Vec = topics; if let Some(first_topic) = topic_vec.first() { - if first_topic == Symbol::new(&env, EVENT_PACKAGE_CREATED) { - created_count += 1; - } else if first_topic == Symbol::new(&env, EVENT_PACKAGE_CLAIMED) { - claimed_count += 1; + if let Ok(symbol) = Symbol::try_from_val(&env, &first_topic) { + if symbol == Symbol::new(&env, EVENT_PACKAGE_CREATED) { + created_count += 1; + } else if symbol == Symbol::new(&env, EVENT_PACKAGE_CLAIMED) { + claimed_count += 1; + } } } } } - assert_eq!(created_count, 1, "Should have exactly 1 PackageCreated event"); - assert_eq!(claimed_count, 1, "Should have exactly 1 PackageClaimed event"); + assert_eq!( + created_count, 1, + "Should have exactly 1 PackageCreated event" + ); + assert_eq!( + claimed_count, 1, + "Should have exactly 1 PackageClaimed event" + ); } #[test] @@ -273,14 +289,16 @@ fn test_multiple_packages_separate_events() { for event in events.iter() { if let Ok((_, topics, _)) = event.clone().try_into() { - let topic_vec: SdkVec = topics; + let topic_vec: soroban_sdk::Vec = topics; if let Some(first_topic) = topic_vec.first() { - if first_topic == Symbol::new(&env, EVENT_PACKAGE_CREATED) { - created_count += 1; - } else if first_topic == Symbol::new(&env, EVENT_PACKAGE_CLAIMED) { - claimed_count += 1; - } else if first_topic == Symbol::new(&env, EVENT_PACKAGE_CANCELLED) { - cancelled_count += 1; + if let Ok(symbol) = Symbol::try_from_val(&env, &first_topic) { + if symbol == Symbol::new(&env, EVENT_PACKAGE_CREATED) { + created_count += 1; + } else if symbol == Symbol::new(&env, EVENT_PACKAGE_CLAIMED) { + claimed_count += 1; + } else if symbol == Symbol::new(&env, EVENT_PACKAGE_CANCELLED) { + cancelled_count += 1; + } } } } @@ -312,9 +330,11 @@ fn test_event_topics_include_package_id() { // Find PackageCreated event and verify it has package_id in topics let package_created_event = events.iter().find(|e| { if let Ok((_, topics, _)) = e.clone().try_into() { - let topic_vec: SdkVec = topics; + let topic_vec: soroban_sdk::Vec = topics; if let Some(first_topic) = topic_vec.first() { - return first_topic == Symbol::new(&env, EVENT_PACKAGE_CREATED); + if let Ok(symbol) = Symbol::try_from_val(&env, &first_topic) { + return symbol == Symbol::new(&env, EVENT_PACKAGE_CREATED); + } } } false @@ -327,7 +347,7 @@ fn test_event_topics_include_package_id() { // Verify topics contain both event name and package_id if let Ok((_, topics, _)) = package_created_event.unwrap().clone().try_into() { - let topic_vec: SdkVec = topics; + let topic_vec: soroban_sdk::Vec = topics; // First topic is event name, second should be package_id assert!(topic_vec.len() >= 1, "Topics should contain event name"); } @@ -345,19 +365,19 @@ fn test_no_events_on_failed_operations() { let result = client.try_create_package(&recipient, &0, &token, &86400); assert!(result.is_err()); - let events_after = env.events().all().len(); - // Should not have created any new events (except possibly initialization) // The key is that no PackageCreated event should be emitted let events = env.events().all(); let package_created_count = events .iter() - .skip(events_before) + .skip(events_before as usize) .filter(|e| { if let Ok((_, topics, _)) = e.clone().try_into() { - let topic_vec: SdkVec = topics; + let topic_vec: soroban_sdk::Vec = topics; if let Some(first_topic) = topic_vec.first() { - return first_topic == Symbol::new(&env, EVENT_PACKAGE_CREATED); + if let Ok(symbol) = Symbol::try_from_val(&env, &first_topic) { + return symbol == Symbol::new(&env, EVENT_PACKAGE_CREATED); + } } } false From 816df5a3e150f7fb0da9076ef79fe7eedbefe7e1 Mon Sep 17 00:00:00 2001 From: OlufunbiIK Date: Mon, 30 Mar 2026 01:48:37 +0100 Subject: [PATCH 03/13] feat:Integrate AI Need Verification (OpenAI/Grok) in VerificationService --- .../src/verification/verification.service.ts | 493 ++++++++++++++---- 1 file changed, 390 insertions(+), 103 deletions(-) diff --git a/app/backend/src/verification/verification.service.ts b/app/backend/src/verification/verification.service.ts index b8776a7..47d62d7 100644 --- a/app/backend/src/verification/verification.service.ts +++ b/app/backend/src/verification/verification.service.ts @@ -16,6 +16,11 @@ import { } from './interfaces/verification-job.interface'; import { AuditService } from '../audit/audit.service'; import { firstValueFrom } from 'rxjs'; +import OpenAI from 'openai'; + +// --------------------------------------------------------------------------- +// OCR service types +// --------------------------------------------------------------------------- interface OCRFieldResult { value: string; @@ -33,6 +38,10 @@ interface OCRResponse { processing_time_ms: number; } +// --------------------------------------------------------------------------- +// Internal claim shape used by verification logic +// --------------------------------------------------------------------------- + interface Claim { id: string; status: string; @@ -42,6 +51,23 @@ interface Claim { evidenceRef?: string | null; } +// --------------------------------------------------------------------------- +// Structured JSON that the AI model must return +// --------------------------------------------------------------------------- + +interface AIVerificationResponse { + score: number; // 0–1 normalised legitimacy score + confidence: number; // 0–1 model confidence + riskLevel: 'low' | 'medium' | 'high'; + factors: string[]; // positive verification signals + riskFactors: string[]; // identified concerns / red-flags + recommendations: string[]; // next steps if human review needed +} + +// --------------------------------------------------------------------------- +// Service +// --------------------------------------------------------------------------- + @Injectable() export class VerificationService { private readonly logger = new Logger(VerificationService.name); @@ -49,6 +75,8 @@ export class VerificationService { private readonly verificationThreshold: number; private readonly aiServiceUrl: string; private readonly aiServiceTimeout: number; + private readonly openaiModel: string; + private readonly openai: OpenAI | null; constructor( @InjectQueue('verification') private verificationQueue: Queue, @@ -70,8 +98,27 @@ export class VerificationService { this.configService.get('AI_SERVICE_TIMEOUT_MS') || '30000', 10, ); + this.openaiModel = + this.configService.get('OPENAI_MODEL') || 'gpt-4o-mini'; + + // Initialise OpenAI client only when a key is present. + // A missing key is not fatal – the fallback path handles it gracefully. + const openAIKey = this.configService.get('OPENAI_API_KEY'); + if (openAIKey) { + this.openai = new OpenAI({ apiKey: openAIKey }); + this.logger.log(`OpenAI client initialised (model: ${this.openaiModel})`); + } else { + this.openai = null; + this.logger.warn( + 'OPENAI_API_KEY not set – AI verification will fall back to mock scoring', + ); + } } + // ------------------------------------------------------------------------- + // Public API + // ------------------------------------------------------------------------- + async enqueueVerification(claimId: string): Promise<{ jobId: string }> { const claim = await this.prisma.claim.findUnique({ where: { id: claimId }, @@ -151,7 +198,8 @@ export class VerificationService { }); this.logger.log( - `Claim ${claimId} verification completed with score ${result.score} (threshold: ${this.verificationThreshold})`, + `Claim ${claimId} verification completed – score ${result.score} ` + + `(threshold: ${this.verificationThreshold})`, ); await this.auditService.record({ @@ -168,115 +216,266 @@ export class VerificationService { return result; } - private generateMockVerification(_claim: unknown): VerificationResult { - const baseScore = 0.6 + Math.random() * 0.35; - const score = Math.min(0.95, Math.max(0.5, baseScore)); + // ------------------------------------------------------------------------- + // Core AI verification pipeline + // ------------------------------------------------------------------------- + + /** + * Orchestrates the full AI-driven verification flow: + * + * 1. If `evidenceRef` looks like a URL, attempt OCR to extract document + * fields from the image/PDF, then pass the extracted text to OpenAI. + * 2. If `evidenceRef` is a plain-text description of need, pass it directly + * to OpenAI (no OCR step needed). + * 3. OpenAI analyses the evidence against humanitarian standards and returns + * a structured JSON verdict. + * 4. If OpenAI is unavailable or returns an unparseable response, the method + * falls back to a deterministic mock result rather than throwing, so the + * queue job never fails silently. + */ + private async performAIVerification( + claim: Claim, + ): Promise { + this.logger.log(`Starting AI verification for claim ${claim.id}`); - const factors = [ - 'Document authenticity verified', - 'Identity cross-reference passed', - 'Historical data consistent', - 'No fraud indicators detected', - ]; + if (!claim.evidenceRef) { + this.logger.warn( + `Claim ${claim.id} has no evidenceRef – applying conservative mock score`, + ); + return this.buildFallbackResult( + 'No evidence provided for this claim. Manual review is required.', + ); + } - const riskLevel: 'low' | 'medium' | 'high' = - score >= 0.8 ? 'low' : score >= 0.65 ? 'medium' : 'high'; + // ------------------------------------------------------------------ + // Step 1 – Gather evidence context + // ------------------------------------------------------------------ + let evidenceContext: string; - return { - score: parseFloat(score.toFixed(3)), - confidence: parseFloat((0.85 + Math.random() * 0.1).toFixed(3)), - details: { - factors: factors.slice(0, Math.floor(Math.random() * 2) + 2), - riskLevel, - recommendations: - riskLevel !== 'low' - ? [ - 'Manual review recommended', - 'Additional documentation may be required', - ] - : undefined, - }, - processedAt: new Date(), - }; + if (this.looksLikeUrl(claim.evidenceRef)) { + // Try OCR; on failure fall back to using the URL as-is in the prompt + evidenceContext = await this.buildOCRContext(claim); + } else { + // evidenceRef is a free-text description of need – use it directly + evidenceContext = `Applicant's stated need:\n${claim.evidenceRef}`; + } + + // ------------------------------------------------------------------ + // Step 2 – Call OpenAI for semantic analysis + // ------------------------------------------------------------------ + if (!this.openai) { + this.logger.warn( + `OpenAI client not available – falling back to mock for claim ${claim.id}`, + ); + return this.buildFallbackResult( + 'AI service not configured. Fallback scoring applied.', + ); + } + + try { + const aiResult = await this.callOpenAI(claim, evidenceContext); + return this.mapAIResponseToResult(aiResult); + } catch (err) { + // AI service unavailable or returned a bad response – degrade gracefully + this.logger.error( + `OpenAI call failed for claim ${claim.id}: ${(err as Error).message}. ` + + `Falling back to mock result.`, + ); + return this.buildFallbackResult( + 'AI analysis could not be completed. Manual review is required.', + ); + } } - private async performAIVerification( + // ------------------------------------------------------------------------- + // OpenAI interaction + // ------------------------------------------------------------------------- + + /** + * Builds a system + user prompt pair grounded in humanitarian standards + * (SPHERE Handbook, ICRC principles of humanity, impartiality, and + * proportionality) and calls the OpenAI chat completions endpoint. + * + * The model is instructed to respond with a single JSON object only – + * no markdown fences, no explanatory prose. + */ + private async callOpenAI( claim: Claim, - ): Promise { + evidenceContext: string, + ): Promise { + const systemPrompt = ` +You are an impartial humanitarian aid verification analyst. Your role is to assess +whether an aid claim is legitimate and the stated need is proportionate. + +You MUST apply the following humanitarian principles in every assessment: +• HUMANITY – prioritise reducing suffering; give benefit of the doubt where evidence + is limited but plausible. +• IMPARTIALITY – base the score solely on evidence and documented need, not on the + identity, nationality, religion, or ethnicity of the applicant. +• PROPORTIONALITY – the requested aid amount must be proportionate to the documented + need (e.g., household size, severity of crisis, local cost-of-living). +• NEUTRALITY – do not take sides; flag political affiliation only if it constitutes + verifiable fraud. + +Scoring rubric (score field, 0–1): + 0.85–1.00 Strong evidence, low risk, aid is proportionate. + 0.70–0.84 Reasonable evidence, minor gaps, likely legitimate. + 0.50–0.69 Insufficient or inconsistent evidence, manual review required. + 0.00–0.49 Significant red flags or clear inconsistencies detected. + +confidence field (0–1): reflect how certain you are given the available evidence. +Lower confidence when evidence is sparse, not when the claim seems unlikely. + +You MUST respond with valid JSON only – no markdown, no prose, no backticks. +The JSON object must have exactly these keys: +{ + "score": , + "confidence": , + "riskLevel": <"low"|"medium"|"high">, + "factors": [, ...], + "riskFactors": [, ...], + "recommendations": [, ...] +} +`.trim(); + + const userPrompt = ` +Claim metadata: +- Claim ID : ${claim.id} +- Campaign ID : ${claim.campaignId} +- Requested amt : ${String(claim.amount)} +- Recipient ref : ${claim.recipientRef} + +Evidence: +${evidenceContext} + +Assess this claim according to the humanitarian standards above and return only +the JSON verdict. +`.trim(); + this.logger.log( - `Calling Python OCR service at ${this.aiServiceUrl}/ai/ocr for claim ${claim.id}`, + `Calling OpenAI (${this.openaiModel}) for claim ${claim.id}`, ); - if (!claim.evidenceRef) { - throw new InternalServerErrorException( - 'No document image found in claim. Cannot perform AI verification.', + const response = await this.openai!.chat.completions.create({ + model: this.openaiModel, + temperature: 0, // deterministic scoring + max_tokens: 512, + response_format: { type: 'json_object' }, + messages: [ + { role: 'system', content: systemPrompt }, + { role: 'user', content: userPrompt }, + ], + }); + + const rawContent = response.choices[0]?.message?.content ?? ''; + + let parsed: AIVerificationResponse; + try { + parsed = JSON.parse(rawContent) as AIVerificationResponse; + } catch { + throw new Error( + `OpenAI returned non-JSON content: ${rawContent.slice(0, 200)}`, ); } - try { - const ocrResponse = await this.callOCRService(claim.evidenceRef); + this.validateAIResponse(parsed, claim.id); - if (!ocrResponse.success || !ocrResponse.data) { - throw new InternalServerErrorException( - `OCR service returned error: ${JSON.stringify(ocrResponse.error)}`, - ); - } + this.logger.log( + `OpenAI verdict for claim ${claim.id}: ` + + `score=${parsed.score}, confidence=${parsed.confidence}, ` + + `riskLevel=${parsed.riskLevel}`, + ); - const { fields } = ocrResponse.data; - const factors: string[] = []; - let totalConfidence = 0; - let fieldCount = 0; + return parsed; + } - if (fields.name?.value) { - factors.push(`Name extracted: ${fields.name.value}`); - totalConfidence += fields.name.confidence; - fieldCount++; - } - if (fields.date_of_birth?.value) { - factors.push(`Date of birth extracted: ${fields.date_of_birth.value}`); - totalConfidence += fields.date_of_birth.confidence; - fieldCount++; - } - if (fields.id_number?.value) { - factors.push(`ID number extracted: ${fields.id_number.value}`); - totalConfidence += fields.id_number.confidence; - fieldCount++; + /** + * Validates that the AI response contains all required fields and that + * numeric values are within the expected 0–1 range. Throws on structural + * issues so the caller can trigger the fallback path. + */ + private validateAIResponse( + resp: AIVerificationResponse, + claimId: string, + ): void { + const requiredKeys: (keyof AIVerificationResponse)[] = [ + 'score', + 'confidence', + 'riskLevel', + 'factors', + 'riskFactors', + 'recommendations', + ]; + for (const key of requiredKeys) { + if (resp[key] === undefined || resp[key] === null) { + throw new Error( + `AI response for claim ${claimId} is missing field "${key}"`, + ); } + } - const avgConfidence = fieldCount > 0 ? totalConfidence / fieldCount : 0; - const baseScore = avgConfidence * 0.9; - const score = Math.min(0.95, Math.max(0.5, baseScore)); + if (resp.score < 0 || resp.score > 1) { + throw new Error( + `AI score out of range for claim ${claimId}: ${resp.score}`, + ); + } + if (resp.confidence < 0 || resp.confidence > 1) { + throw new Error( + `AI confidence out of range for claim ${claimId}: ${resp.confidence}`, + ); + } + if (!['low', 'medium', 'high'].includes(resp.riskLevel)) { + throw new Error( + `AI riskLevel invalid for claim ${claimId}: ${resp.riskLevel}`, + ); + } + } - const riskLevel: 'low' | 'medium' | 'high' = - score >= 0.8 ? 'low' : score >= 0.65 ? 'medium' : 'high'; + // ------------------------------------------------------------------------- + // OCR helpers + // ------------------------------------------------------------------------- + + /** + * Calls the Python OCR micro-service and converts the response into a + * human-readable evidence context string suitable for the AI prompt. + * Falls back to a minimal context string if OCR fails, rather than + * throwing – the AI layer will then assign lower confidence accordingly. + */ + private async buildOCRContext(claim: Claim): Promise { + try { + const ocrResponse = await this.callOCRService(claim.evidenceRef!); - this.logger.log( - `OCR completed for claim ${claim.id}: score=${score}, fields extracted=${fieldCount}`, - ); + if (!ocrResponse.success || !ocrResponse.data) { + this.logger.warn( + `OCR failed for claim ${claim.id}: ` + + `${JSON.stringify(ocrResponse.error)} – continuing with URL only`, + ); + return `Document URL (OCR failed): ${claim.evidenceRef}`; + } - return { - score: parseFloat(score.toFixed(3)), - confidence: parseFloat(avgConfidence.toFixed(3)), - details: { - factors, - riskLevel, - recommendations: - riskLevel !== 'low' - ? [ - 'Manual review recommended', - 'Additional documentation may be required', - ] - : undefined, - }, - processedAt: new Date(), - }; - } catch (error) { - this.logger.error( - `OCR service call failed for claim ${claim.id}: ${error.message}`, - ); - throw new InternalServerErrorException( - `Failed to perform AI verification: ${error.message}`, + const { fields, raw_text } = ocrResponse.data; + + const fieldLines = Object.entries(fields) + .filter(([, v]) => v.value) + .map( + ([k, v]) => + ` ${k.replace(/_/g, ' ')}: "${v.value}" (confidence: ${(v.confidence * 100).toFixed(1)}%)`, + ) + .join('\n'); + + return [ + 'Identity document – extracted fields:', + fieldLines || ' (no structured fields extracted)', + '', + 'Full OCR text:', + raw_text || ' (no raw text extracted)', + ].join('\n'); + } catch (err) { + this.logger.warn( + `OCR service unavailable for claim ${claim.id}: ` + + `${(err as Error).message} – continuing without OCR data`, ); + return `Document URL (OCR service unreachable): ${claim.evidenceRef}`; } } @@ -288,28 +487,120 @@ export class VerificationService { { document_url: documentUrl }, { timeout: this.aiServiceTimeout, - headers: { - 'Content-Type': 'application/json', - }, + headers: { 'Content-Type': 'application/json' }, }, ), ); - return response.data; + return response.data as OCRResponse; } catch (error) { - if (error.response) { + const err = error as { + response?: { status: number; data: unknown }; + code?: string; + message: string; + }; + if (err.response) { throw new Error( - `OCR service returned ${error.response.status}: ${JSON.stringify(error.response.data)}`, + `OCR service returned ${err.response.status}: ` + + `${JSON.stringify(err.response.data)}`, ); - } else if (error.code === 'ECONNREFUSED') { + } else if (err.code === 'ECONNREFUSED') { throw new Error( - `OCR service unavailable at ${this.aiServiceUrl}. Is the Python ai-service running?`, + `OCR service unavailable at ${this.aiServiceUrl}. ` + + `Is the Python ai-service running?`, ); } else { - throw new Error(`OCR service call failed: ${error.message}`); + throw new Error(`OCR service call failed: ${err.message}`); } } } + // ------------------------------------------------------------------------- + // Result builders + // ------------------------------------------------------------------------- + + private mapAIResponseToResult( + ai: AIVerificationResponse, + ): VerificationResult { + return { + score: parseFloat(ai.score.toFixed(3)), + confidence: parseFloat(ai.confidence.toFixed(3)), + details: { + factors: [...ai.factors, ...ai.riskFactors], + riskLevel: ai.riskLevel, + recommendations: + ai.recommendations.length > 0 ? ai.recommendations : undefined, + }, + processedAt: new Date(), + }; + } + + /** + * Conservative fallback result used when the AI service is unavailable or + * returns an invalid response. Score is set just below the default 0.7 + * threshold so the claim lands in manual review rather than being + * auto-approved or auto-rejected. + */ + private buildFallbackResult(reason: string): VerificationResult { + return { + score: 0.55, + confidence: 0.4, + details: { + factors: [reason], + riskLevel: 'medium', + recommendations: [ + 'AI verification unavailable – manual review required', + 'Verify applicant identity and evidence independently', + ], + }, + processedAt: new Date(), + }; + } + + /** Heuristic: treat strings that start with http/https as URLs. */ + private looksLikeUrl(value: string): boolean { + return /^https?:\/\//i.test(value.trim()); + } + + // ------------------------------------------------------------------------- + // Mock (development / test) + // ------------------------------------------------------------------------- + + private generateMockVerification(_claim: unknown): VerificationResult { + const baseScore = 0.6 + Math.random() * 0.35; + const score = Math.min(0.95, Math.max(0.5, baseScore)); + + const factors = [ + 'Document authenticity verified', + 'Identity cross-reference passed', + 'Historical data consistent', + 'No fraud indicators detected', + ]; + + const riskLevel: 'low' | 'medium' | 'high' = + score >= 0.8 ? 'low' : score >= 0.65 ? 'medium' : 'high'; + + return { + score: parseFloat(score.toFixed(3)), + confidence: parseFloat((0.85 + Math.random() * 0.1).toFixed(3)), + details: { + factors: factors.slice(0, Math.floor(Math.random() * 2) + 2), + riskLevel, + recommendations: + riskLevel !== 'low' + ? [ + 'Manual review recommended', + 'Additional documentation may be required', + ] + : undefined, + }, + processedAt: new Date(), + }; + } + + // ------------------------------------------------------------------------- + // CRUD / queue utilities (unchanged) + // ------------------------------------------------------------------------- + create(_createVerificationDto: CreateVerificationDto) { return 'This action adds a new verification'; } @@ -319,14 +610,10 @@ export class VerificationService { } async findOne(id: string) { - const claim = await this.prisma.claim.findUnique({ - where: { id }, - }); - + const claim = await this.prisma.claim.findUnique({ where: { id } }); if (!claim) { throw new NotFoundException(`Claim with ID ${id} not found`); } - return claim; } From 31aaa0fa0e36267855fbb65dd3888f4020cbf758 Mon Sep 17 00:00:00 2001 From: OlufunbiIK Date: Mon, 30 Mar 2026 01:54:21 +0100 Subject: [PATCH 04/13] fix:installing a dependecy --- app/backend/package-lock.json | 22 ++++++++++++++++++++++ app/backend/package.json | 1 + 2 files changed, 23 insertions(+) diff --git a/app/backend/package-lock.json b/app/backend/package-lock.json index 964ea48..e5beb0b 100644 --- a/app/backend/package-lock.json +++ b/app/backend/package-lock.json @@ -31,6 +31,7 @@ "dotenv": "^17.2.3", "helmet": "^8.1.0", "ioredis": "^5.9.2", + "openai": "^6.33.0", "pino": "^10.3.0", "pino-http": "^11.0.0", "pino-pretty": "^13.1.3", @@ -7640,6 +7641,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openai": { + "version": "6.33.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.33.0.tgz", + "integrity": "sha512-xAYN1W3YsDXJWA5F277135YfkEk6H7D3D6vWwRhJ3OEkzRgcyK8z/P5P9Gyi/wB4N8kK9kM5ZjprfvyHagKmpw==", + "license": "Apache-2.0", + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/optionator": { "version": "0.9.4", "dev": true, diff --git a/app/backend/package.json b/app/backend/package.json index eb25fda..f82cb95 100644 --- a/app/backend/package.json +++ b/app/backend/package.json @@ -49,6 +49,7 @@ "dotenv": "^17.2.3", "helmet": "^8.1.0", "ioredis": "^5.9.2", + "openai": "^6.33.0", "pino": "^10.3.0", "pino-http": "^11.0.0", "pino-pretty": "^13.1.3", From da252382d5efbafa66efdc654a1b87eba706a2c9 Mon Sep 17 00:00:00 2001 From: OlufunbiIK Date: Mon, 30 Mar 2026 02:09:46 +0100 Subject: [PATCH 05/13] fix --- package-lock.json | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 8b78845..0000000 --- a/package-lock.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "soter", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "soter" - } - } -} From 53d6059bbca1ac33852cf3ac094e42478128e3db Mon Sep 17 00:00:00 2001 From: OlufunbiIK Date: Mon, 30 Mar 2026 10:00:28 +0100 Subject: [PATCH 06/13] fix:ci/cd fix --- .../src/verification/verification.service.ts | 7 +------ app/onchain/contracts/aid_escrow/src/lib.rs | 20 +++++++++++++++++-- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/app/backend/src/verification/verification.service.ts b/app/backend/src/verification/verification.service.ts index 47d62d7..d8beeb4 100644 --- a/app/backend/src/verification/verification.service.ts +++ b/app/backend/src/verification/verification.service.ts @@ -1,9 +1,4 @@ -import { - Injectable, - NotFoundException, - Logger, - InternalServerErrorException, -} from '@nestjs/common'; +import { Injectable, NotFoundException, Logger } from '@nestjs/common'; import { InjectQueue } from '@nestjs/bullmq'; import { Queue } from 'bullmq'; import { ConfigService } from '@nestjs/config'; diff --git a/app/onchain/contracts/aid_escrow/src/lib.rs b/app/onchain/contracts/aid_escrow/src/lib.rs index 54e50bd..4dfc7b6 100644 --- a/app/onchain/contracts/aid_escrow/src/lib.rs +++ b/app/onchain/contracts/aid_escrow/src/lib.rs @@ -987,6 +987,22 @@ impl AidEscrow { total_expired_cancelled, } } +} + +// --- Tests --- + +#[cfg(test)] +mod tests { + use super::*; + use soroban_sdk::testutils::{Address as _, MockAuth, MockAuthInvoke}; + use soroban_sdk::{testutils::Ledger, Env}; + + fn setup() -> (Env, AidEscrowClient<'static>) { + let env = Env::default(); + let contract_id = env.register_contract(None, AidEscrow); + let client = AidEscrowClient::new(&env, &contract_id); + (env, client) + } #[test] fn test_cancel_package() { @@ -995,8 +1011,8 @@ impl AidEscrow { let recipient = Address::generate(&env); let token = Address::generate(&env); - client.initialize(&admin); env.mock_all_auths(); + client.initialize(&admin); let package_id = client.create_package(&recipient, &1000, &token, &86400); client.cancel_package(&package_id); @@ -1004,4 +1020,4 @@ impl AidEscrow { let package = client.get_package(&package_id).unwrap(); assert_eq!(package.status, PackageStatus::Cancelled); } -} +} \ No newline at end of file From ad18ad2f86a93a4840917e57a002bfec8b68cf6d Mon Sep 17 00:00:00 2001 From: OlufunbiIK Date: Mon, 30 Mar 2026 10:12:10 +0100 Subject: [PATCH 07/13] fix:cicd --- app/onchain/contracts/aid_escrow/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/onchain/contracts/aid_escrow/src/lib.rs b/app/onchain/contracts/aid_escrow/src/lib.rs index 4dfc7b6..0d95743 100644 --- a/app/onchain/contracts/aid_escrow/src/lib.rs +++ b/app/onchain/contracts/aid_escrow/src/lib.rs @@ -995,7 +995,7 @@ impl AidEscrow { mod tests { use super::*; use soroban_sdk::testutils::{Address as _, MockAuth, MockAuthInvoke}; - use soroban_sdk::{testutils::Ledger, Env}; + use soroban_sdk::{Env, testutils::Ledger}; fn setup() -> (Env, AidEscrowClient<'static>) { let env = Env::default(); From 912fb47c2cfab6766b5788cf8af54e017f0b7514 Mon Sep 17 00:00:00 2001 From: OlufunbiIK Date: Mon, 30 Mar 2026 10:22:24 +0100 Subject: [PATCH 08/13] fix --- app/onchain/contracts/aid_escrow/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/onchain/contracts/aid_escrow/src/lib.rs b/app/onchain/contracts/aid_escrow/src/lib.rs index 0d95743..f5ad123 100644 --- a/app/onchain/contracts/aid_escrow/src/lib.rs +++ b/app/onchain/contracts/aid_escrow/src/lib.rs @@ -1020,4 +1020,5 @@ mod tests { let package = client.get_package(&package_id).unwrap(); assert_eq!(package.status, PackageStatus::Cancelled); } -} \ No newline at end of file +} + From 59e3c52ec6c0fbf88968619adf30b32aa9239da6 Mon Sep 17 00:00:00 2001 From: OlufunbiIK Date: Mon, 30 Mar 2026 10:38:56 +0100 Subject: [PATCH 09/13] fix --- app/onchain/contracts/aid_escrow/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/app/onchain/contracts/aid_escrow/src/lib.rs b/app/onchain/contracts/aid_escrow/src/lib.rs index f5ad123..63d18ec 100644 --- a/app/onchain/contracts/aid_escrow/src/lib.rs +++ b/app/onchain/contracts/aid_escrow/src/lib.rs @@ -1021,4 +1021,3 @@ mod tests { assert_eq!(package.status, PackageStatus::Cancelled); } } - From 0d9dd84e0783fcee8d3b95823e7b6068c7860e96 Mon Sep 17 00:00:00 2001 From: OlufunbiIK Date: Mon, 30 Mar 2026 10:47:08 +0100 Subject: [PATCH 10/13] fix --- app/onchain/contracts/aid_escrow/src/lib.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/onchain/contracts/aid_escrow/src/lib.rs b/app/onchain/contracts/aid_escrow/src/lib.rs index 63d18ec..ca8b149 100644 --- a/app/onchain/contracts/aid_escrow/src/lib.rs +++ b/app/onchain/contracts/aid_escrow/src/lib.rs @@ -994,12 +994,12 @@ impl AidEscrow { #[cfg(test)] mod tests { use super::*; - use soroban_sdk::testutils::{Address as _, MockAuth, MockAuthInvoke}; - use soroban_sdk::{Env, testutils::Ledger}; + use soroban_sdk::testutils::Address as _; + use soroban_sdk::Env; fn setup() -> (Env, AidEscrowClient<'static>) { let env = Env::default(); - let contract_id = env.register_contract(None, AidEscrow); + let contract_id = env.register(AidEscrow, ()); let client = AidEscrowClient::new(&env, &contract_id); (env, client) } @@ -1010,14 +1010,16 @@ mod tests { let admin = Address::generate(&env); let recipient = Address::generate(&env); let token = Address::generate(&env); + let operator = admin.clone(); env.mock_all_auths(); - client.initialize(&admin); + client.init(&admin); - let package_id = client.create_package(&recipient, &1000, &token, &86400); + let package_id = + client.create_package(&operator, &1, &recipient, &1000, &token, &86400); client.cancel_package(&package_id); - let package = client.get_package(&package_id).unwrap(); + let package = client.get_package(&package_id); assert_eq!(package.status, PackageStatus::Cancelled); } } From 472509b164ad3f9e6495ecb86a335d911c00a125 Mon Sep 17 00:00:00 2001 From: OlufunbiIK Date: Mon, 30 Mar 2026 10:54:03 +0100 Subject: [PATCH 11/13] fix --- app/onchain/contracts/aid_escrow/src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/onchain/contracts/aid_escrow/src/lib.rs b/app/onchain/contracts/aid_escrow/src/lib.rs index ca8b149..c549847 100644 --- a/app/onchain/contracts/aid_escrow/src/lib.rs +++ b/app/onchain/contracts/aid_escrow/src/lib.rs @@ -994,8 +994,8 @@ impl AidEscrow { #[cfg(test)] mod tests { use super::*; - use soroban_sdk::testutils::Address as _; use soroban_sdk::Env; + use soroban_sdk::testutils::Address as _; fn setup() -> (Env, AidEscrowClient<'static>) { let env = Env::default(); @@ -1015,8 +1015,7 @@ mod tests { env.mock_all_auths(); client.init(&admin); - let package_id = - client.create_package(&operator, &1, &recipient, &1000, &token, &86400); + let package_id = client.create_package(&operator, &1, &recipient, &1000, &token, &86400); client.cancel_package(&package_id); let package = client.get_package(&package_id); From 0809180ed57ed8b63366a5217fc90552505e4e18 Mon Sep 17 00:00:00 2001 From: OlufunbiIK Date: Mon, 30 Mar 2026 11:06:10 +0100 Subject: [PATCH 12/13] fix --- app/onchain/contracts/aid_escrow/src/lib.rs | 24 +++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/app/onchain/contracts/aid_escrow/src/lib.rs b/app/onchain/contracts/aid_escrow/src/lib.rs index c549847..98114e1 100644 --- a/app/onchain/contracts/aid_escrow/src/lib.rs +++ b/app/onchain/contracts/aid_escrow/src/lib.rs @@ -994,8 +994,9 @@ impl AidEscrow { #[cfg(test)] mod tests { use super::*; - use soroban_sdk::Env; use soroban_sdk::testutils::Address as _; + use soroban_sdk::token::{StellarAssetClient, TokenClient}; + use soroban_sdk::Env; fn setup() -> (Env, AidEscrowClient<'static>) { let env = Env::default(); @@ -1009,13 +1010,28 @@ mod tests { let (env, client) = setup(); let admin = Address::generate(&env); let recipient = Address::generate(&env); - let token = Address::generate(&env); - let operator = admin.clone(); + + // Deploy a mock Stellar asset contract to use as the token. + let token_id = env.register_stellar_asset_contract_v2(admin.clone()); + let token = token_id.address(); + let sac = StellarAssetClient::new(&env, &token); + let token_client = TokenClient::new(&env, &token); env.mock_all_auths(); + + // Initialise the escrow contract. client.init(&admin); - let package_id = client.create_package(&operator, &1, &recipient, &1000, &token, &86400); + // Mint 10_000 tokens to admin and fund the escrow pool. + sac.mint(&admin, &10_000); + client.fund(&token, &admin, &5_000); + + // Confirm the contract holds the funded balance. + assert_eq!(token_client.balance(&client.address), 5_000); + + let operator = admin.clone(); + let package_id = + client.create_package(&operator, &1, &recipient, &1000, &token, &86400); client.cancel_package(&package_id); let package = client.get_package(&package_id); From bd1cb5692e64f80596e215941b5d3eb8ff248ca4 Mon Sep 17 00:00:00 2001 From: OlufunbiIK Date: Mon, 30 Mar 2026 11:14:17 +0100 Subject: [PATCH 13/13] fix --- app/onchain/contracts/aid_escrow/src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/onchain/contracts/aid_escrow/src/lib.rs b/app/onchain/contracts/aid_escrow/src/lib.rs index 98114e1..b2a9f72 100644 --- a/app/onchain/contracts/aid_escrow/src/lib.rs +++ b/app/onchain/contracts/aid_escrow/src/lib.rs @@ -994,9 +994,9 @@ impl AidEscrow { #[cfg(test)] mod tests { use super::*; + use soroban_sdk::Env; use soroban_sdk::testutils::Address as _; use soroban_sdk::token::{StellarAssetClient, TokenClient}; - use soroban_sdk::Env; fn setup() -> (Env, AidEscrowClient<'static>) { let env = Env::default(); @@ -1030,8 +1030,7 @@ mod tests { assert_eq!(token_client.balance(&client.address), 5_000); let operator = admin.clone(); - let package_id = - client.create_package(&operator, &1, &recipient, &1000, &token, &86400); + let package_id = client.create_package(&operator, &1, &recipient, &1000, &token, &86400); client.cancel_package(&package_id); let package = client.get_package(&package_id);