diff --git a/.github/workflows/audit-freeze.yml b/.github/workflows/audit-freeze.yml index 2dd1730b..24203a42 100644 --- a/.github/workflows/audit-freeze.yml +++ b/.github/workflows/audit-freeze.yml @@ -64,6 +64,9 @@ jobs: - name: Run contract tests run: cargo test --locked 2>&1 | tee /tmp/test-output.txt + - name: Run legacy-tests suite + run: cargo test --locked --features legacy-tests 2>&1 | tee -a /tmp/test-output.txt + - name: Upload test output if: always() uses: actions/upload-artifact@v4 diff --git a/.github/workflows/ci-consolidated.yml b/.github/workflows/ci-consolidated.yml index 7b1aa897..8079f887 100644 --- a/.github/workflows/ci-consolidated.yml +++ b/.github/workflows/ci-consolidated.yml @@ -55,6 +55,7 @@ jobs: - uses: actions/checkout@v4 - uses: ./.github/actions/setup-rust - run: cargo test --verbose + - run: cargo test --features legacy-tests --verbose # ── TypeScript type checking ─────────────────────────────────────────── backend-typecheck: diff --git a/.github/workflows/mainnet-checklist.yml b/.github/workflows/mainnet-checklist.yml index 3106f4c0..48c694a2 100644 --- a/.github/workflows/mainnet-checklist.yml +++ b/.github/workflows/mainnet-checklist.yml @@ -65,6 +65,10 @@ jobs: run: cargo test --verbose 2>&1 | tee test-output.txt continue-on-error: false + - name: Run legacy-tests suite + run: cargo test --features legacy-tests --verbose 2>&1 | tee -a test-output.txt + continue-on-error: false + - name: Summarise test results if: always() run: | diff --git a/Cargo.toml b/Cargo.toml index b28fd441..cd66a790 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ soroban-sdk = { version = "26.1.0", features = ["testutils"] } proptest = "1.4" criterion = "0.8" # Used only by the testnet-integration test suite (gated by feature flag). -reqwest = { version = "0.12.5", features = ["json", "blocking"] } +reqwest = { version = "0.12.5", features = ["json", "blocking", "rustls-tls"], default-features = false } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tokio = { version = "1", features = ["rt", "macros", "time"] } diff --git a/src/lib.rs b/src/lib.rs index bc06c571..29c5f169 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -563,7 +563,7 @@ impl SwiftRemitContract { sender.require_auth(); // SR-123: Apply abuse-protection sliding-window rate limit and cooldown for Transfer. - check_rate_limit(&env, &sender, ActionType::Transfer)?; + abuse_protection::check_rate_limit(&env, &sender, ActionType::Transfer)?; check_cooldown(&env, &sender, ActionType::Transfer)?; let default_currency = String::from_str(&env, DEFAULT_DAILY_LIMIT_CURRENCY); @@ -704,10 +704,24 @@ impl SwiftRemitContract { from_country: Option, to_country: Option, ) -> Result { + // SR-128: Block while a migration is in progress (parity with create_remittance). + if crate::storage::is_migration_in_progress(&env) { + return Err(ContractError::MigrationInProgress); + } // #1264: Block new corridor-remittance creation while the circuit-breaker is active. validate_not_paused(&env)?; validate_create_remittance_request(&env, &sender, &agent, amount)?; + // SR-128: Enforce minimum-agent-reputation gate (parity with create_remittance). + let min_rep = storage::get_min_agent_reputation(&env); + if min_rep > 0 { + let rep = storage::compute_agent_reputation(&storage::get_agent_stats(&env, &agent)); + if rep < min_rep { + events::emit_agent_suspended(&env, agent.clone(), rep, min_rep); + return Err(ContractError::BelowMinReputation); + } + } + sender.require_auth(); let limit_currency = String::from_str(&env, DEFAULT_DAILY_LIMIT_CURRENCY); @@ -716,6 +730,14 @@ impl SwiftRemitContract { .unwrap_or_else(|| String::from_str(&env, DEFAULT_DAILY_LIMIT_COUNTRY)); enforce_daily_send_limit(&env, &sender, &limit_currency, &limit_country, amount)?; + // SR-128: Enforce corridor/global volume cap (parity with create_remittance). + storage::check_and_increment_corridor_volume( + &env, + &limit_currency, + &limit_country, + amount, + )?; + let corridor = match (&from_country, &to_country) { (Some(from), Some(to)) => storage::get_fee_corridor(&env, from, to), _ => None, @@ -764,9 +786,13 @@ impl SwiftRemitContract { set_remittance(&env, remittance_id, &remittance); set_payout_commitment(&env, remittance_id, &payout_commitment); set_remittance_counter(&env, remittance_id); + storage::increment_remittance_count(&env)?; set_transfer_state(&env, remittance_id, RemittanceStatus::Pending)?; storage::record_sender_volume(&env, &sender, amount, env.ledger().timestamp())?; + + // SR-128: Index under both sender and agent (parity with create_remittance). storage::append_sender_remittance(&env, &sender, remittance_id); + storage::append_agent_remittance(&env, &agent, remittance_id); Ok(remittance_id) } @@ -1030,7 +1056,7 @@ impl SwiftRemitContract { // SR-123: Apply abuse-protection per-action sliding-window rate limit and cooldown // for Settlement. This complements the simpler storage::check_settlement_rate_limit // above with the more sophisticated per-action, decaying-cooldown mechanism. - check_rate_limit(&env, &remittance.agent, ActionType::Settlement)?; + abuse_protection::check_rate_limit(&env, &remittance.agent, ActionType::Settlement)?; check_cooldown(&env, &remittance.agent, ActionType::Settlement)?; // Enforce per-agent daily withdrawal cap @@ -1311,7 +1337,12 @@ impl SwiftRemitContract { // Enforce per-agent daily cap storage::check_and_record_agent_withdrawal(&env, &remittance.agent, amount)?; - let fee_breakdown = fee_service::calculate_fees_with_breakdown(&env, remittance.amount, None, None)?; + // SR-129: Use the fee quoted at creation time, not the current global fee + // strategy. Mirrors confirm_payout and resolve_dispute. Re-pricing here + // would produce a different net_payout than what was escrowed if the + // platform fee or strategy changed after creation, or if the original + // remittance qualified for a sender-volume discount. + let fee_breakdown = fee_service::breakdown_from_platform_fee(&env, remittance.amount, remittance.fee)?; let net_payout = fee_breakdown.net_amount; let already_disbursed = storage::get_disbursed_amount(&env, remittance_id); @@ -1442,7 +1473,7 @@ impl SwiftRemitContract { remittance.sender.require_auth(); // SR-123: Apply abuse-protection rate limit and cooldown for Cancellation. - check_rate_limit(&env, &remittance.sender, ActionType::Cancellation)?; + abuse_protection::check_rate_limit(&env, &remittance.sender, ActionType::Cancellation)?; check_cooldown(&env, &remittance.sender, ActionType::Cancellation)?; let usdc_token = get_usdc_token(&env)?; diff --git a/src/storage.rs b/src/storage.rs index 0e4fe9e0..bb92fc39 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -56,6 +56,11 @@ use crate::{ /// Rolling window over which an agent's daily payout cap is enforced. pub const AGENT_CAP_WINDOW_SECONDS: u64 = 86_400; +// SR-126: Maximum number of remittance IDs stored in a single index shard. +// At 8 bytes per u64, 500 entries ≈ 4 KB — well within Soroban's 64 KB +// per-ledger-entry limit and cheap to read/write on the hot path. +pub const REMITTANCE_INDEX_SHARD_SIZE: u32 = 500; + /// Default window, in seconds, during which a failed remittance may be disputed. pub const DEFAULT_DISPUTE_WINDOW_SECONDS: u64 = 72 * 3600; // 72 hours @@ -323,11 +328,28 @@ enum DataKey { // === Remittance Indexes === /// Remittance IDs created by a sender (persistent storage). + /// Legacy flat-list key kept for backward-compatible reads during migration. SenderRemittances(Address), /// Remittance IDs assigned to an agent (persistent storage). + /// Legacy flat-list key kept for backward-compatible reads during migration. AgentRemittances(Address), + // SR-126: Sharded index variants. Each shard stores at most + // REMITTANCE_INDEX_SHARD_SIZE IDs so ledger-entry growth is bounded. + /// One shard of a sender's remittance index: (sender, shard_number). + SenderRemittancesShard(Address, u32), + + /// Running count of remittances indexed for a sender (used to derive the current + /// shard number without loading any shard data). + SenderRemittancesCount(Address), + + /// One shard of an agent's remittance index: (agent, shard_number). + AgentRemittancesShard(Address, u32), + + /// Running count of remittances indexed for an agent. + AgentRemittancesCount(Address), + // === In-Flight Volume === /// Total value of remittances currently in the Processing state (instance storage). TotalProcessingVolume, @@ -2192,15 +2214,31 @@ pub fn add_processing_volume(env: &Env, amount: i128) -> Result<(), ContractErro } /// Appends a remittance ID to the agent's persistent remittance index. +/// +/// SR-126: Writes are sharded — at most `REMITTANCE_INDEX_SHARD_SIZE` IDs +/// are stored per ledger entry, so the write cost stays O(shard_size) rather +/// than O(total IDs ever created by this agent). pub fn append_agent_remittance(env: &Env, agent: &Address, remittance_id: u64) { - let key = DataKey::AgentRemittances(agent.clone()); - let mut ids: soroban_sdk::Vec = env + let count_key = DataKey::AgentRemittancesCount(agent.clone()); + let count: u32 = env .storage() .persistent() - .get(&key) + .get(&count_key) + .unwrap_or(0); + + let shard_index = count / REMITTANCE_INDEX_SHARD_SIZE; + let shard_key = DataKey::AgentRemittancesShard(agent.clone(), shard_index); + + let mut shard: soroban_sdk::Vec = env + .storage() + .persistent() + .get(&shard_key) .unwrap_or_else(|| soroban_sdk::Vec::new(env)); - ids.push_back(remittance_id); - env.storage().persistent().set(&key, &ids); + + shard.push_back(remittance_id); + env.storage().persistent().set(&shard_key, &shard); + + env.storage().persistent().set(&count_key, &(count + 1)); } /// Appends a partial payout record to the remittance's disbursement history. @@ -2221,15 +2259,31 @@ pub fn append_partial_payout_record( } /// Appends a remittance ID to the sender's persistent remittance index. +/// +/// SR-126: Writes are sharded — at most `REMITTANCE_INDEX_SHARD_SIZE` IDs +/// are stored per ledger entry, so the write cost stays O(shard_size) rather +/// than O(total IDs ever created by this sender). pub fn append_sender_remittance(env: &Env, sender: &Address, remittance_id: u64) { - let key = DataKey::SenderRemittances(sender.clone()); - let mut ids: soroban_sdk::Vec = env + let count_key = DataKey::SenderRemittancesCount(sender.clone()); + let count: u32 = env .storage() .persistent() - .get(&key) + .get(&count_key) + .unwrap_or(0); + + let shard_index = count / REMITTANCE_INDEX_SHARD_SIZE; + let shard_key = DataKey::SenderRemittancesShard(sender.clone(), shard_index); + + let mut shard: soroban_sdk::Vec = env + .storage() + .persistent() + .get(&shard_key) .unwrap_or_else(|| soroban_sdk::Vec::new(env)); - ids.push_back(remittance_id); - env.storage().persistent().set(&key, &ids); + + shard.push_back(remittance_id); + env.storage().persistent().set(&shard_key, &shard); + + env.storage().persistent().set(&count_key, &(count + 1)); } /// Checks and records an agent withdrawal against the rolling cap. @@ -2406,11 +2460,42 @@ pub fn get_agent_list(env: &Env) -> soroban_sdk::Vec
{ } /// Returns all remittance IDs for an agent. +/// +/// SR-126: Reads across all shards in order. If no sharded data exists, falls +/// back to the legacy flat `AgentRemittances` key so existing on-chain state +/// continues to work during the upgrade. pub fn get_agent_remittances(env: &Env, agent: &Address) -> soroban_sdk::Vec { - env.storage() + let count_key = DataKey::AgentRemittancesCount(agent.clone()); + let count: u32 = env + .storage() .persistent() - .get(&DataKey::AgentRemittances(agent.clone())) - .unwrap_or_else(|| soroban_sdk::Vec::new(env)) + .get(&count_key) + .unwrap_or(0); + + if count == 0 { + // Fall back to the legacy flat list in case this address was indexed + // before SR-126 sharding was deployed. + return env + .storage() + .persistent() + .get(&DataKey::AgentRemittances(agent.clone())) + .unwrap_or_else(|| soroban_sdk::Vec::new(env)); + } + + let num_shards = (count + REMITTANCE_INDEX_SHARD_SIZE - 1) / REMITTANCE_INDEX_SHARD_SIZE; + let mut all_ids: soroban_sdk::Vec = soroban_sdk::Vec::new(env); + for shard_index in 0..num_shards { + let shard_key = DataKey::AgentRemittancesShard(agent.clone(), shard_index); + let shard: soroban_sdk::Vec = env + .storage() + .persistent() + .get(&shard_key) + .unwrap_or_else(|| soroban_sdk::Vec::new(env)); + for i in 0..shard.len() { + all_ids.push_back(shard.get_unchecked(i)); + } + } + all_ids } /// Returns the current corridor daily cap (0 = no cap configured). @@ -2478,13 +2563,39 @@ pub fn get_remittance_expiry_window(env: &Env) -> u64 { /// Returns all remittance IDs for a sender. /// -/// The caller is responsible for applying pagination (offset/limit) to avoid -/// returning unbounded data in a single call. +/// SR-126: Reads across all shards in order. Falls back to the legacy flat +/// `SenderRemittances` key when no sharded data exists (backward compat). pub fn get_sender_remittances(env: &Env, sender: &Address) -> soroban_sdk::Vec { - env.storage() + let count_key = DataKey::SenderRemittancesCount(sender.clone()); + let count: u32 = env + .storage() .persistent() - .get(&DataKey::SenderRemittances(sender.clone())) - .unwrap_or_else(|| soroban_sdk::Vec::new(env)) + .get(&count_key) + .unwrap_or(0); + + if count == 0 { + // Fall back to the legacy flat list for addresses indexed before SR-126. + return env + .storage() + .persistent() + .get(&DataKey::SenderRemittances(sender.clone())) + .unwrap_or_else(|| soroban_sdk::Vec::new(env)); + } + + let num_shards = (count + REMITTANCE_INDEX_SHARD_SIZE - 1) / REMITTANCE_INDEX_SHARD_SIZE; + let mut all_ids: soroban_sdk::Vec = soroban_sdk::Vec::new(env); + for shard_index in 0..num_shards { + let shard_key = DataKey::SenderRemittancesShard(sender.clone(), shard_index); + let shard: soroban_sdk::Vec = env + .storage() + .persistent() + .get(&shard_key) + .unwrap_or_else(|| soroban_sdk::Vec::new(env)); + for i in 0..shard.len() { + all_ids.push_back(shard.get_unchecked(i)); + } + } + all_ids } /// Returns the total amount currently held in Processing (in-flight) remittances. diff --git a/src/test_contract_upgrade.rs b/src/test_contract_upgrade.rs index e2ef96a5..cecd903d 100644 --- a/src/test_contract_upgrade.rs +++ b/src/test_contract_upgrade.rs @@ -64,8 +64,8 @@ fn test_migrate_preserves_remittance_state() { let (env, client, _, agent, sender) = setup(); env.mock_all_auths(); - let id1 = client.create_remittance(&sender, &agent, &5_000, &None, &None, &None, &None, &None); - let id2 = client.create_remittance(&sender, &agent, &3_000, &None, &None, &None, &None, &None); + let id1 = client.create_remittance(&sender, &agent, &5_000, &None, &None, &None, &None, &None, &None); + let id2 = client.create_remittance(&sender, &agent, &3_000, &None, &None, &None, &None, &None, &None); // Snapshot state before migration. let before1 = client.get_remittance(&id1); @@ -126,7 +126,7 @@ fn test_migrate_preserves_commitment_hashes() { env.mock_all_auths(); let id = - client.create_remittance(&sender, &agent, &10_000, &None, &None, &None, &None, &None); + client.create_remittance(&sender, &agent, &10_000, &None, &None, &None, &None, &None, &None); // Compute deterministic commitment hash before migration. let hash_before = client @@ -151,7 +151,7 @@ fn test_migrate_preserves_accumulated_fees() { env.mock_all_auths(); client.set_kyc_approved(&sender, &true, &u64::MAX); - let id = client.create_remittance(&sender, &agent, &8_000, &None, &None, &None, &None, &None); + let id = client.create_remittance(&sender, &agent, &8_000, &None, &None, &None, &None, &None, &None); // Fees accrue when the payout is confirmed, not when the remittance is created. client.confirm_payout(&agent, &id, &None, &None); @@ -173,9 +173,9 @@ fn test_migrate_preserves_remittance_count() { let (env, client, _, agent, sender) = setup(); env.mock_all_auths(); - client.create_remittance(&sender, &agent, &1_000, &None, &None, &None, &None, &None); - client.create_remittance(&sender, &agent, &2_000, &None, &None, &None, &None, &None); - client.create_remittance(&sender, &agent, &3_000, &None, &None, &None, &None, &None); + client.create_remittance(&sender, &agent, &1_000, &None, &None, &None, &None, &None, &None); + client.create_remittance(&sender, &agent, &2_000, &None, &None, &None, &None, &None, &None); + client.create_remittance(&sender, &agent, &3_000, &None, &None, &None, &None, &None, &None); let count_before = client.get_remittance_count(); diff --git a/src/test_dispute.rs b/src/test_dispute.rs index c950f9d6..94ca1cbb 100644 --- a/src/test_dispute.rs +++ b/src/test_dispute.rs @@ -71,7 +71,7 @@ fn setup_failed_remittance() -> DisputeFixture<'static> { contract.initialize(&admin, &token.address, &250u32, &0u64, &0u32, &admin); contract.register_agent(&agent, &None); - let remittance_id = contract.create_remittance(&sender, &agent, &1_000i128, &None, &None, &None, &None, &None); + let remittance_id = contract.create_remittance(&sender, &agent, &1_000i128, &None, &None, &None, &None, &None, &None); // Agent marks the remittance as failed contract.mark_failed(&remittance_id); @@ -107,7 +107,7 @@ fn test_mark_failed_transitions_to_failed() { contract.initialize(&admin, &token.address, &250u32, &0u64, &0u32, &admin); contract.register_agent(&agent, &None); - let id = contract.create_remittance(&sender, &agent, &1_000i128, &None, &None, &None, &None, &None); + let id = contract.create_remittance(&sender, &agent, &1_000i128, &None, &None, &None, &None, &None, &None); let sender_before = balance(&env, &token, &sender); let agent_before = balance(&env, &token, &agent); let contract_before = balance(&env, &token, &contract.address); @@ -139,7 +139,7 @@ fn test_mark_failed_on_completed_remittance_rejected() { contract.set_kyc_approved(&sender, &true, &u64::MAX); - let id = contract.create_remittance(&sender, &agent, &1_000i128, &None, &None, &None, &None, &None); + let id = contract.create_remittance(&sender, &agent, &1_000i128, &None, &None, &None, &None, &None, &None); contract.confirm_payout(&agent, &id, &None, &None); let result = contract.try_mark_failed(&id); @@ -184,7 +184,7 @@ fn test_raise_dispute_on_non_failed_remittance_rejected() { contract.register_agent(&agent, &None); // Remittance is still Pending — not Failed - let id = contract.create_remittance(&sender, &agent, &1_000i128, &None, &None, &None, &None, &None); + let id = contract.create_remittance(&sender, &agent, &1_000i128, &None, &None, &None, &None, &None, &None); let hash = evidence_hash(&env); let result = contract.try_raise_dispute(&id, &hash); @@ -311,7 +311,7 @@ fn test_resolve_dispute_non_admin_rejected() { contract2.initialize(&admin2, &token2.address, &250u32, &0u64, &0u32, &admin2); contract2.register_agent(&agent2, &None); - let id2 = contract2.create_remittance(&sender2, &agent2, &1_000i128, &None, &None, &None, &None, &None); + let id2 = contract2.create_remittance(&sender2, &agent2, &1_000i128, &None, &None, &None, &None, &None, &None); contract2.mark_failed(&id2); contract2.raise_dispute(&id2, &evidence_hash(&env2)); diff --git a/src/test_features_589_592.rs b/src/test_features_589_592.rs index 8534cca2..ddba6aec 100644 --- a/src/test_features_589_592.rs +++ b/src/test_features_589_592.rs @@ -41,7 +41,7 @@ fn setup() -> F<'static> { } fn remit(f: &F, amount: i128) -> u64 { - f.c.create_remittance(&f.sender, &f.agent, &amount, &None, &None, &None, &None, &None) + f.c.create_remittance(&f.sender, &f.agent, &amount, &None, &None, &None, &None, &None, &None) } // ── #589 Multi-currency ─────────────────────────────────────────────────────── @@ -58,14 +58,14 @@ fn remit(f: &F, amount: i128) -> u64 { let t2 = make_token(&f.env, &f.admin); t2.mint(&f.sender, &5_000); f.c.add_whitelisted_token(&t2.address); - let id = f.c.create_remittance(&f.sender, &f.agent, &1_000, &None, &Some(t2.address.clone()), &None, &None, &None); + let id = f.c.create_remittance(&f.sender, &f.agent, &1_000, &None, &Some(t2.address.clone()), &None, &None, &None, &None); assert_eq!(f.c.get_remittance(&id).token, t2.address); } #[test] fn test_589_unwhitelisted_token_rejected() { let f = setup(); let bad = make_token(&f.env, &f.admin); - let r = f.c.try_create_remittance(&f.sender, &f.agent, &1_000, &None, &Some(bad.address.clone()), &None, &None, &None); + let r = f.c.try_create_remittance(&f.sender, &f.agent, &1_000, &None, &Some(bad.address.clone()), &None, &None, &None, &None); assert_eq!(r, Err(Ok(ContractError::TokenNotWhitelisted))); } @@ -161,7 +161,7 @@ fn remit(f: &F, amount: i128) -> u64 { let f = setup(); f.c.set_min_agent_reputation(&50u32); // New agent has reputation 100, should pass - let r = f.c.try_create_remittance(&f.sender, &f.agent, &1_000, &None, &None, &None, &None, &None); + let r = f.c.try_create_remittance(&f.sender, &f.agent, &1_000, &None, &None, &None, &None, &None, &None); assert!(r.is_ok()); } diff --git a/src/test_invariants.rs b/src/test_invariants.rs index 5d8cbaf0..1f87d1bb 100644 --- a/src/test_invariants.rs +++ b/src/test_invariants.rs @@ -91,7 +91,7 @@ proptest! { let sender_before = token.balance(&sender); - let id = contract.create_remittance(&sender, &agent, &amount, &None, &None, &None, &None, &None); + let id = contract.create_remittance(&sender, &agent, &amount, &None, &None, &None, &None, &None, &None); // Contract must hold exactly the escrowed amount prop_assert_eq!( @@ -136,7 +136,7 @@ proptest! { contract.set_kyc_approved(&sender, &true, &u64::MAX); contract.assign_role(&admin, &agent, &crate::Role::Settler); - let id = contract.create_remittance(&sender, &agent, &amount, &None, &None, &None, &None, &None); + let id = contract.create_remittance(&sender, &agent, &amount, &None, &None, &None, &None, &None, &None); let total_before = token.balance(&sender) + token.balance(&contract.address) @@ -187,7 +187,7 @@ proptest! { contract.set_kyc_approved(&sender, &true, &u64::MAX); let sender_before = token.balance(&sender); - let id = contract.create_remittance(&sender, &agent, &amount, &None, &None, &None, &None, &None); + let id = contract.create_remittance(&sender, &agent, &amount, &None, &None, &None, &None, &None, &None); contract.cancel_remittance(&id); @@ -236,7 +236,7 @@ proptest! { contract.register_agent(&agent, &None); contract.set_kyc_approved(&sender, &true, &u64::MAX); - let id = contract.create_remittance(&sender, &agent, &amount, &None, &None, &None, &None, &None); + let id = contract.create_remittance(&sender, &agent, &amount, &None, &None, &None, &None, &None, &None); let r = contract.get_remittance(&id); prop_assert_eq!( @@ -269,7 +269,7 @@ proptest! { contract.set_kyc_approved(&sender, &true, &u64::MAX); contract.assign_role(&admin, &agent, &crate::Role::Settler); - let id = contract.create_remittance(&sender, &agent, &amount, &None, &None, &None, &None, &None); + let id = contract.create_remittance(&sender, &agent, &amount, &None, &None, &None, &None, &None, &None); contract.confirm_payout(&agent, &id, &None, &None); prop_assert_eq!(contract.get_remittance(&id).status, RemittanceStatus::Completed); @@ -306,7 +306,7 @@ proptest! { contract.register_agent(&agent, &None); contract.set_kyc_approved(&sender, &true, &u64::MAX); - let id = contract.create_remittance(&sender, &agent, &amount, &None, &None, &None, &None, &None); + let id = contract.create_remittance(&sender, &agent, &amount, &None, &None, &None, &None, &None, &None); contract.cancel_remittance(&id); prop_assert_eq!(contract.get_remittance(&id).status, RemittanceStatus::Cancelled); @@ -354,7 +354,7 @@ proptest! { // Intentionally NOT registering `unregistered_agent` let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - contract.create_remittance(&sender, &unregistered_agent, &amount, &None, &None, &None, &None, &None); + contract.create_remittance(&sender, &unregistered_agent, &amount, &None, &None, &None, &None, &None, &None); })); prop_assert!( @@ -411,7 +411,7 @@ proptest! { contract.register_agent(&agent, &None); contract.set_kyc_approved(&sender, &true, &u64::MAX); - let id = contract.create_remittance(&sender, &agent, &amount, &None, &None, &None, &None, &None); + let id = contract.create_remittance(&sender, &agent, &amount, &None, &None, &None, &None, &None, &None); let r = contract.get_remittance(&id); prop_assert!(r.fee >= 0, "Fee must be non-negative"); @@ -611,7 +611,7 @@ proptest! { Op::CreateRemittance { amount_seed } => { let amount = 1i128 + (amount_seed as i128 % 500_000); let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - contract.create_remittance(&sender, &agent, &amount, &None, &None, &None, &None, &None) + contract.create_remittance(&sender, &agent, &amount, &None, &None, &None, &None, &None, &None) })); if let Ok(id) = result { rems.push(RemModel { id, amount, disbursed: 0, open: true });