diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7730091a..cf5221d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,7 +51,7 @@ jobs: echo "Installed via Homebrew" else echo "Homebrew install failed, installing via cargo..." - cargo install --locked --version 21.0.0 soroban-cli + cargo install --locked --version 21.1.0 soroban-cli fi fi # Try both commands in case Homebrew installs it as 'stellar' diff --git a/Cargo.toml b/Cargo.toml index 213e6882..b75c9080 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ default-members = [ resolver = "2" [dependencies] -soroban-sdk = "21.0.0" +soroban-sdk = "21.1.1" remittance_split = { path = "./remittance_split" } savings_goals = { path = "./savings_goals" } bill_payments = { path = "./bill_payments" } @@ -45,7 +45,7 @@ reporting = { path = "./reporting" } orchestrator = { path = "./orchestrator" } [dev-dependencies] -soroban-sdk = { version = "21.0.0", features = ["testutils"] } +soroban-sdk = { version = "21.1.1", features = ["testutils"] } [profile.release] opt-level = "z" overflow-checks = true diff --git a/README.md b/README.md index 7f409c93..a8ff1900 100644 --- a/README.md +++ b/README.md @@ -403,6 +403,21 @@ Manages family roles, spending controls, multisig approvals, and emergency trans For full design details, see [docs/family-wallet-design.md](docs/family-wallet-design.md). +### Data Migration + +Utilities for contract data export and import (JSON, Binary, CSV). + +**Key Functions:** + +- `export_to_csv`: Export savings goals to a tabular CSV format. +- `import_goals_from_csv`: Import goals from CSV with strict schema validation. +- `export_to_json` / `import_from_json`: Versioned snapshot migration. + +**CSV Hardening:** +- Strict header validation (exact match and order). +- Row-level type and value validation (e.g., non-negative amounts). +- Rejection of ambiguous or malformed data. + ## Events All contracts emit events for important state changes, enabling real-time tracking and frontend integration. Events follow Soroban best practices and include: diff --git a/bill_payments/Cargo.toml b/bill_payments/Cargo.toml index ec3dbff0..a3949adf 100644 --- a/bill_payments/Cargo.toml +++ b/bill_payments/Cargo.toml @@ -7,12 +7,12 @@ edition = "2021" crate-type = ["cdylib", "rlib"] [dependencies] -soroban-sdk = "21.0.0" +soroban-sdk = "21.1.1" remitwise-common = { path = "../remitwise-common" } [dev-dependencies] proptest = "1.10.0" -soroban-sdk = { version = "21.0.0", features = ["testutils"] } +soroban-sdk = { version = "21.1.1", features = ["testutils"] } testutils = { path = "../testutils" } diff --git a/bill_payments/README.md b/bill_payments/README.md index 1a8521bb..e89333e2 100644 --- a/bill_payments/README.md +++ b/bill_payments/README.md @@ -31,11 +31,12 @@ This section provides a minimal example of how to interact with the Bill Payment let bill_id = client.create_bill( &owner_address, &String::from_str(&env, "Internet Bill"), - &500_0000000, - &(env.ledger().timestamp() + 2592000), - &false, - &0, - &String::from_str(&env, "XLM") + &500_0000000, + &(env.ledger().timestamp() + 2592000), + &false, + &0, + &None, + &String::from_str(&env, "XLM"), ); ``` @@ -74,7 +75,8 @@ pub struct Bill { - `BillAlreadyPaid = 2`: Attempting to pay an already paid bill - `InvalidAmount = 3`: Amount is zero or negative - `InvalidFrequency = 4`: Recurring bill has zero frequency -- `Unauthorized = 5`: Caller is not the bill owner +- `Unauthorized = 5`: Caller is not the bill owner (or not the archived row owner where required) +- `InconsistentBillData = 15`: Map key and embedded `id` disagree, or restore would collide with inconsistent active state (see Archive and restore) ### Functions @@ -223,9 +225,51 @@ let bills_allocation = split_amounts.get(2).unwrap(); // bills percentage ### With Insurance Contract Bills can represent insurance premiums, working alongside the insurance contract for comprehensive financial management. -## Security Considerations +## Archive and restore (Issue #272) -- All functions require proper authorization -- Owners can only manage their own bills -- Input validation prevents invalid states -- Storage TTL is managed to prevent bloat \ No newline at end of file +Paid bills can be moved to **archived** storage, restored to active storage, or **cleaned up** (permanently removed from the archive). These flows enforce **owner affinity**: only the recorded bill owner can mutate or read sensitive archive data for their rows. + +### Functions + +| Function | Authorization | Notes | +|----------|----------------|--------| +| `archive_paid_bills(caller, before_timestamp)` | `caller.require_auth()` | Archives only rows where `bill.owner == caller`, `bill.paid`, `paid_at < before_timestamp`, and **map key `id` matches `bill.id`** (integrity). | +| `restore_bill(caller, bill_id)` | `caller.require_auth()` | Requires archived row exists, `ArchivedBill.owner == caller`, **`ArchivedBill.id == bill_id`**, and no conflicting active bill at `bill_id`. | +| `bulk_cleanup_bills(caller, before_timestamp)` | `caller.require_auth()` | Deletes only archived rows with `owner == caller`, `archived_at < before_timestamp`, and **`id` key matches `ArchivedBill.id`**. | +| `get_archived_bills(owner, cursor, limit)` | **`owner.require_auth()`** | Listing is not anonymous; the `owner` address must sign. | +| `get_archived_bill(caller, bill_id)` | **`caller.require_auth()`** | Returns `Ok(archived)` only if the archived row exists **and** `archived.owner == caller` and ids are consistent; otherwise `BillNotFound`, `Unauthorized`, or `InconsistentBillData`. | + +### Error code + +- **`InconsistentBillData = 15`**: Thrown when a stored `Bill` / `ArchivedBill` has an `id` field that does not match its map key, when restoring would collide with an existing active row for the same id, or when cleanup detects the same mismatch. This blocks silent cross-owner or corrupted-key exploitation. + +### NatSpec-style notes (contract source) + +The contract uses `@notice`, `@param`, `@return`, `@dev`, and `@errors` on archive-related entrypoints in `bill_payments/src/lib.rs` for reviewers and tooling. + +### Client migration + +- **`get_archived_bill`**: Signature is now `(env, caller, bill_id) -> Result` (previously a public `Option` by `bill_id` only). Callers must pass the **authenticated owner** as `caller`. + +## Security considerations + +- **Authorization**: Mutating functions use `require_auth()` on the acting address; archive listings require the **same** owner address to authorize reads. +- **Owner affinity**: Archive, restore, and cleanup iterate or select rows **only** for `caller` / authenticated `owner`; cross-owner manipulation returns `Unauthorized` or fails integrity checks. +- **Consistency**: Key/id checks reduce the risk of inconsistent maps being used to move or read another user’s data. +- **Input validation**: Amounts, due dates, and recurrence rules are validated on create/update paths. +- **Storage TTL**: Instance and archive TTL bumps keep data available without unbounded retention of abandoned state. + +### Validation (tests) + +Run from the workspace root: + +```bash +cargo test -p bill_payments +``` + +All `bill_payments` unit tests, integration tests under `bill_payments/tests/`, and Issue #272 cases in `bill_payments/src/test.rs` should pass. Focused security tests cover: non-owner `get_archived_bill` / `restore_bill`, and `bulk_cleanup` not removing another owner’s archived rows. + +**Security assumptions** + +- Stellar account signatures are unforgeable; `require_auth()` correctly binds the caller to the intended `Address`. +- Archive state is only produced through `archive_paid_bills` (paid bills, owner-checked); callers should treat `InconsistentBillData` as a fatal integrity signal and investigate off-chain. \ No newline at end of file diff --git a/bill_payments/proptest-regressions/lib.txt b/bill_payments/proptest-regressions/lib.txt new file mode 100644 index 00000000..057e8fcd --- /dev/null +++ b/bill_payments/proptest-regressions/lib.txt @@ -0,0 +1,8 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc e423d8ab667995ae14b0f72357096ef13d5841ca50ec0bf841863d911e540f49 # shrinks to now = 2000000, n_overdue = 1, n_future = 0 +cc 98a9604dfd893d4ba76f8439d887e9062933542f533a9563c9a7ab619e62c37e # shrinks to base_due = 1000000, pay_offset = 1, freq_days = 1 diff --git a/bill_payments/src/lib.rs b/bill_payments/src/lib.rs index cd64fec6..52062973 100644 --- a/bill_payments/src/lib.rs +++ b/bill_payments/src/lib.rs @@ -3,8 +3,8 @@ use remitwise_common::{ clamp_limit, EventCategory, EventPriority, RemitwiseEvents, ARCHIVE_BUMP_AMOUNT, - ARCHIVE_LIFETIME_THRESHOLD, CONTRACT_VERSION, DEFAULT_PAGE_LIMIT, INSTANCE_BUMP_AMOUNT, - INSTANCE_LIFETIME_THRESHOLD, MAX_BATCH_SIZE, MAX_PAGE_LIMIT, + ARCHIVE_LIFETIME_THRESHOLD, CONTRACT_VERSION, INSTANCE_BUMP_AMOUNT, + INSTANCE_LIFETIME_THRESHOLD, MAX_BATCH_SIZE, }; use soroban_sdk::{ @@ -12,10 +12,8 @@ use soroban_sdk::{ Symbol, Vec, }; -#[derive(Clone, Debug)] #[contracttype] #[derive(Clone, Debug)] -#[contracttype] pub struct Bill { pub id: u32, pub owner: Address, @@ -57,8 +55,6 @@ pub mod pause_functions { pub const RESTORE: soroban_sdk::Symbol = symbol_short!("restore"); } -const CONTRACT_VERSION: u32 = 1; -const MAX_BATCH_SIZE: u32 = 50; const STORAGE_UNPAID_TOTALS: Symbol = symbol_short!("UNPD_TOT"); #[contracterror] @@ -77,14 +73,14 @@ pub enum Error { BatchValidationFailed = 10, InvalidLimit = 11, InvalidDueDate = 12, - InvalidTag = 12, - EmptyTags = 13, + InvalidTag = 13, + EmptyTags = 14, + /// Storage key and embedded `Bill::id` / `ArchivedBill::id` do not match (integrity check). + InconsistentBillData = 15, } -#[derive(Clone)] -#[contracttype] -#[derive(Clone)] #[contracttype] +#[derive(Clone, Debug)] pub struct ArchivedBill { pub id: u32, pub owner: Address, @@ -100,20 +96,25 @@ pub struct ArchivedBill { /// Paginated result for archived bill queries #[contracttype] -#[derive(Clone)] pub struct ArchivedBillPage { pub items: Vec, - /// 0 means no more pages pub next_cursor: u32, pub count: u32, } #[contracttype] -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum BillEvent { Created, Paid, + Cancelled, + Archived, + Restored, ExternalRefUpdated, +} + +#[contracttype] +#[derive(Clone, Debug)] pub struct StorageStats { pub active_bills: u32, pub archived_bills: u32, @@ -436,7 +437,7 @@ impl BillPayments { }; let bill_owner = bill.owner.clone(); - let bill_external_ref = bill.external_ref.clone(); + let _bill_external_ref = bill.external_ref.clone(); bills.set(next_id, bill); env.storage() .instance() @@ -447,9 +448,6 @@ impl BillPayments { Self::adjust_unpaid_total(&env, &bill_owner, amount); // Emit event for audit trail - env.events().publish( - (symbol_short!("bill"), BillEvent::Created), - (next_id, bill_owner, bill_external_ref), RemitwiseEvents::emit( &env, EventCategory::State, @@ -516,7 +514,7 @@ impl BillPayments { .set(&symbol_short!("NEXT_ID"), &next_id); } - let bill_external_ref = bill.external_ref.clone(); + let _bill_external_ref = bill.external_ref.clone(); let paid_amount = bill.amount; let was_recurring = bill.recurring; bills.set(bill_id, bill); @@ -528,9 +526,6 @@ impl BillPayments { } // Emit event for audit trail - env.events().publish( - (symbol_short!("bill"), BillEvent::Paid), - (bill_id, caller, bill_external_ref), RemitwiseEvents::emit( &env, EventCategory::Transaction, @@ -766,7 +761,18 @@ impl BillPayments { /// /// # Returns /// Vec of all Bill structs - pub fn get_all_bills(env: Env) -> Vec { + pub fn get_all_bills_deprecated(env: Env) -> Vec { + let bills: Map = env + .storage() + .instance() + .get(&symbol_short!("BILLS")) + .unwrap_or_else(|| Map::new(&env)); + let mut result = Vec::new(&env); + for (_, bill) in bills.iter() { + result.push_back(bill); + } + result + } // ----------------------------------------------------------------------- // Backward-compat helpers // ----------------------------------------------------------------------- @@ -793,13 +799,15 @@ impl BillPayments { // Archived bill queries (paginated) // ----------------------------------------------------------------------- - /// Get a page of archived bills for `owner`. + /// @notice Paginated archived bills for the authenticated `owner`. + /// @dev `owner` must call `require_auth()` so listings cannot be scraped for arbitrary addresses. pub fn get_archived_bills( env: Env, owner: Address, cursor: u32, limit: u32, ) -> ArchivedBillPage { + owner.require_auth(); let limit = clamp_limit(limit); let archived: Map = env .storage() @@ -849,13 +857,29 @@ impl BillPayments { } } - pub fn get_archived_bill(env: Env, bill_id: u32) -> Option { + /// @notice Fetch one archived bill by id for an authenticated owner. + /// @param caller Must authorize and must equal `ArchivedBill.owner`. + /// @return `Ok(archived)` when found and owned by `caller`. + /// @errors `BillNotFound`, `Unauthorized`, `InconsistentBillData` + pub fn get_archived_bill( + env: Env, + caller: Address, + bill_id: u32, + ) -> Result { + caller.require_auth(); let archived: Map = env .storage() .instance() .get(&symbol_short!("ARCH_BILL")) .unwrap_or_else(|| Map::new(&env)); - archived.get(bill_id) + let ab = archived.get(bill_id).ok_or(Error::BillNotFound)?; + if ab.owner != caller { + return Err(Error::Unauthorized); + } + if ab.id != bill_id { + return Err(Error::InconsistentBillData); + } + Ok(ab) } // ----------------------------------------------------------------------- @@ -892,6 +916,8 @@ impl BillPayments { Ok(()) } + /// @notice Move paid bills owned by `caller` into the archive store before `before_timestamp`. + /// @dev Enforces `bill.owner == caller`, `bill.id ==` map key, and paid invariants before archiving. pub fn archive_paid_bills( env: Env, caller: Address, @@ -917,6 +943,12 @@ impl BillPayments { let mut to_remove: Vec = Vec::new(&env); for (id, bill) in bills.iter() { + if bill.owner != caller { + continue; + } + if bill.id != id { + return Err(Error::InconsistentBillData); + } if let Some(paid_at) = bill.paid_at { if bill.paid && paid_at < before_timestamp { let archived_bill = ArchivedBill { @@ -960,6 +992,8 @@ impl BillPayments { Ok(archived_count) } + /// @notice Restore an archived bill into active storage for its owner. + /// @dev Confirms archive owner and id/key consistency; refuses if an active bill already occupies `bill_id`. pub fn restore_bill(env: Env, caller: Address, bill_id: u32) -> Result<(), Error> { caller.require_auth(); Self::require_not_paused(&env, pause_functions::RESTORE)?; @@ -975,6 +1009,9 @@ impl BillPayments { if archived_bill.owner != caller { return Err(Error::Unauthorized); } + if archived_bill.id != bill_id { + return Err(Error::InconsistentBillData); + } let mut bills: Map = env .storage() @@ -982,10 +1019,18 @@ impl BillPayments { .get(&symbol_short!("BILLS")) .unwrap_or_else(|| Map::new(&env)); + if let Some(existing) = bills.get(bill_id) { + if existing.owner != archived_bill.owner { + return Err(Error::Unauthorized); + } + return Err(Error::InconsistentBillData); + } + let restored_bill = Bill { id: archived_bill.id, owner: archived_bill.owner.clone(), name: archived_bill.name.clone(), + external_ref: None, // Or logic to recover if stored elsewhere amount: archived_bill.amount, due_date: env.ledger().timestamp() + 2592000, recurring: false, @@ -1020,6 +1065,8 @@ impl BillPayments { Ok(()) } + /// @notice Permanently remove archived rows owned by `caller` older than `before_timestamp`. + /// @dev Skips rows where `ArchivedBill.id` does not match the map key (returns `InconsistentBillData`). pub fn bulk_cleanup_bills( env: Env, caller: Address, @@ -1038,6 +1085,12 @@ impl BillPayments { let mut to_remove: Vec = Vec::new(&env); for (id, bill) in archived.iter() { + if bill.owner != caller { + continue; + } + if bill.id != id { + return Err(Error::InconsistentBillData); + } if bill.archived_at < before_timestamp { to_remove.push_back(id); deleted_count += 1; @@ -1111,6 +1164,7 @@ impl BillPayments { id: next_id, owner: bill.owner.clone(), name: bill.name.clone(), + external_ref: bill.external_ref.clone(), amount: bill.amount, due_date: next_due_date, recurring: true, @@ -1211,7 +1265,7 @@ impl BillPayments { cursor: u32, limit: u32, ) -> BillPage { - let limit = Self::clamp_limit(limit); + let limit = clamp_limit(limit); let bills: Map = env .storage() .instance() @@ -1245,7 +1299,7 @@ impl BillPayments { cursor: u32, limit: u32, ) -> BillPage { - let limit = Self::clamp_limit(limit); + let limit = clamp_limit(limit); let bills: Map = env .storage() .instance() @@ -1374,6 +1428,7 @@ impl BillPayments { #[cfg(test)] mod test { use super::*; + use remitwise_common::MAX_PAGE_LIMIT; use proptest::prelude::*; use soroban_sdk::{ testutils::{Address as _, Ledger}, @@ -1401,6 +1456,7 @@ mod test { &(env.ledger().timestamp() + 86400 * (i as u64 + 1)), &false, &0, + &None, &String::from_str(env, "XLM"), ); ids.push_back(id); @@ -1635,6 +1691,7 @@ mod test { &(env.ledger().timestamp() + 86400 * (i as u64 + 1)), &false, &0, + &None, &String::from_str(&env, "XLM"), ); client.create_bill( @@ -1644,6 +1701,7 @@ mod test { &(env.ledger().timestamp() + 86400 * (i as u64 + 1)), &false, &0, + &None, &String::from_str(&env, "XLM"), ); } @@ -1717,6 +1775,7 @@ mod test { &due_date, // 20000 &false, &0, + &None, &String::from_str(&env, "XLM"), ); } @@ -1833,6 +1892,7 @@ mod test { &base_due_date, &true, // recurring &1, // frequency_days = 1 + &None, &String::from_str(&env, "XLM"), ); @@ -1867,6 +1927,7 @@ mod test { &base_due_date, &true, // recurring &30, // frequency_days = 30 + &None, &String::from_str(&env, "XLM"), ); @@ -1904,6 +1965,7 @@ mod test { &base_due_date, &true, // recurring &365, // frequency_days = 365 + &None, &String::from_str(&env, "XLM"), ); @@ -1945,6 +2007,7 @@ mod test { &base_due_date, &true, &30, + &None, &String::from_str(&env, "XLM"), ); @@ -1976,6 +2039,7 @@ mod test { &base_due_date, &true, // recurring &30, // frequency_days = 30 + &None, &String::from_str(&env, "XLM"), ); @@ -2025,6 +2089,7 @@ mod test { &base_due_date, &true, // recurring &30, // frequency_days = 30 + &None, &String::from_str(&env, "XLM"), ); @@ -2071,6 +2136,7 @@ mod test { &base_due_date, &true, // recurring &30, // frequency_days = 30 + &None, &String::from_str(&env, "XLM"), ); @@ -2109,6 +2175,7 @@ mod test { &1_000_000, &true, &frequency, + &None, &String::from_str(&env, "XLM"), ); @@ -2145,6 +2212,7 @@ mod test { &1_000_000, &true, &30, + &None, &String::from_str(&env, "XLM"), ); @@ -2180,6 +2248,7 @@ mod test { &1_000_000, &true, &30, + &None, &String::from_str(&env, "XLM"), ); @@ -2220,6 +2289,7 @@ mod test { &base_due, &true, &freq, + &None, &String::from_str(&env, "XLM"), ); @@ -2245,13 +2315,15 @@ mod test { n_future in 0usize..6usize, ) { let env = make_env(); - env.ledger().set_timestamp(now); + // Bills must be created with due_date >= ledger time; advance ledger before querying overdue. + let t_create = now.saturating_sub(500_000); + env.ledger().set_timestamp(t_create); env.mock_all_auths(); let cid = env.register_contract(None, BillPayments); let client = BillPaymentsClient::new(&env, &cid); let owner = Address::generate(&env); - // Create bills with due_date < now (overdue) + // Create bills that will be overdue once ledger == `now` (due < now, due > t_create). for i in 0..n_overdue { client.create_bill( &owner, @@ -2260,11 +2332,12 @@ mod test { &(now - 1 - i as u64), &false, &0, + &None, &String::from_str(&env, "XLM"), ); } - // Create bills with due_date >= now (not overdue) + // Not overdue at query time `now`: due_date >= now for i in 0..n_future { client.create_bill( &owner, @@ -2273,10 +2346,12 @@ mod test { &(now + 1 + i as u64), &false, &0, + &None, &String::from_str(&env, "XLM"), ); } + env.ledger().set_timestamp(now); let page = client.get_overdue_bills(&0, &50); for bill in page.items.iter() { prop_assert!(bill.due_date < now, "returned bill must be past due"); @@ -2307,6 +2382,7 @@ mod test { &(now + i as u64), // due_date >= now — strict less-than is required to be overdue &false, &0, + &None, &String::from_str(&env, "XLM"), ); } @@ -2332,7 +2408,7 @@ mod test { ) { let env = make_env(); let pay_time = base_due + pay_offset; - env.ledger().set_timestamp(pay_time); + env.ledger().set_timestamp(base_due); env.mock_all_auths(); let cid = env.register_contract(None, BillPayments); let client = BillPaymentsClient::new(&env, &cid); @@ -2345,9 +2421,11 @@ mod test { &base_due, &true, &freq_days, + &None, &String::from_str(&env, "XLM"), ); + env.ledger().set_timestamp(pay_time); client.pay_bill(&owner, &bill_id); let next_bill = client.get_bill(&2).unwrap(); @@ -2389,11 +2467,27 @@ mod test { // 3. Execution: Attempt to create bills with invalid dates // Added '¤cy' as the final argument to both calls - let result_past = - client.try_create_bill(&owner, &name, &1000, &past_due_date, &false, &0, ¤cy); + let result_past = client.try_create_bill( + &owner, + &name, + &1000, + &past_due_date, + &false, + &0, + &None, + ¤cy, + ); - let result_zero = - client.try_create_bill(&owner, &name, &1000, &zero_due_date, &false, &0, ¤cy); + let result_zero = client.try_create_bill( + &owner, + &name, + &1000, + &zero_due_date, + &false, + &0, + &None, + ¤cy, + ); // 4. Assertions assert!( @@ -2445,6 +2539,7 @@ mod test { &due_date, &false, &0, + &None, &String::from_str(&env, "XLM"), ); @@ -2474,6 +2569,7 @@ mod test { &due_date, &false, &0, + &None, &String::from_str(&env, "XLM"), ); @@ -2509,6 +2605,7 @@ mod test { &overdue_target, &false, &0, + &None, &String::from_str(&env, "XLM"), ); @@ -2521,6 +2618,7 @@ mod test { &due_now_target, &false, &0, + &None, &String::from_str(&env, "XLM"), ); @@ -2555,6 +2653,7 @@ mod test { &due_date, &false, &0, + &None, &String::from_str(&env, "XLM"), ); @@ -2569,3 +2668,7 @@ mod test { ); } } + +#[cfg(test)] +#[path = "test.rs"] +mod archive_restore_issue_272_tests; diff --git a/bill_payments/src/test.rs b/bill_payments/src/test.rs index e36bae8b..70b77117 100644 --- a/bill_payments/src/test.rs +++ b/bill_payments/src/test.rs @@ -1,2741 +1,136 @@ - use testutils::{set_ledger_time, setup_test_env}; -#[cfg(test)] -mod testsuit { - proptest! { - #[test] - fn prop_overdue_bills_all_due_dates_less_than_now( - now in 1_000_000u64..10_000_000u64, - n_overdue in 1usize..10, - n_future in 0usize..10 - ) { - let env = Env::default(); - set_time(&env, now); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - env.mock_all_auths(); +//! Issue #272 — Archive / restore ownership hardening. +//! Focused security cases; the main suite lives in `lib.rs` under `#[cfg(test)] mod test`. - // Create overdue bills - for i in 0..n_overdue { - client.create_bill( - &owner, - &String::from_str(&env, &format!("Overdue{}", i)), - &100, - &(now - 1 - i as u64), // due_date < now - &false, - &0, - ); - env.mock_all_auths(); - } +use crate::{BillPayments, BillPaymentsClient, Error}; +use soroban_sdk::testutils::Address as _; +use soroban_sdk::{Address, Env, String}; - // Create future bills - for i in 0..n_future { - client.create_bill( - &owner, - &String::from_str(&env, &format!("Future{}", i)), - &100, - &(now + 1 + i as u64), // due_date > now - &false, - &0, - ); - env.mock_all_auths(); - } - - let overdue = client.get_overdue_bills(&owner); - // All overdue bills should have due_date < now - for bill in overdue.iter() { - assert!(bill.due_date < now, "Bill due_date {} not less than now {}", bill.due_date, now); - } - // The number of overdue bills should match n_overdue - assert_eq!(overdue.len(), n_overdue); - } - } - use crate::*; - use soroban_sdk::testutils::{Address as AddressTrait, Ledger, LedgerInfo}; - use soroban_sdk::Env; - use proptest::prelude::*; - - // Removed local set_time in favor of testutils::set_ledger_time - - #[test] - fn test_create_bill_succeeds() { - setup_test_env!(env, BillPayments, client, owner); - - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Electricity"), - &1000, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - &None, - &String::from_str(&env, "XLM"), - ); - - assert_eq!(bill_id, 1); - - let bill = client.get_bill(&1); - assert!(bill.is_some()); - let bill = bill.unwrap(); - assert_eq!(bill.amount, 1000); - assert!(!bill.paid); - assert!(bill.external_ref.is_none()); - } - - #[test] - fn test_create_bill_invalid_amount_fails() { - setup_test_env!(env, BillPayments, client, owner); - let result = client.try_create_bill( - &owner, - &String::from_str(&env, "Invalid"), - &0, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - &None, - &String::from_str(&env, "XLM"), - ); - - assert_eq!(result, Err(Ok(Error::InvalidAmount))); - } - - #[test] - fn test_create_recurring_bill_invalid_frequency() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - let result = client.try_create_bill( - &owner, - &String::from_str(&env, "Monthly"), - &500, - &1000000, - &true, - &0, - &String::from_str(&env, "XLM"), - &None, - &String::from_str(&env, "XLM"), - ); - - assert_eq!(result, Err(Ok(Error::InvalidFrequency))); - } - - #[test] - fn test_create_bill_negative_amount() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - let result = client.try_create_bill( - &owner, - &String::from_str(&env, "Invalid"), - &-100, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - &None, - &String::from_str(&env, "XLM"), - ); - - assert_eq!(result, Err(Ok(Error::InvalidAmount))); - } - - #[test] - fn test_pay_bill() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Water"), - &500, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - &None, - &String::from_str(&env, "XLM"), - ); - - env.mock_all_auths(); - client.pay_bill(&owner, &bill_id); - - let bill = client.get_bill(&bill_id).unwrap(); - assert!(bill.paid); - - assert!(bill.paid_at.is_some()); - } - - #[test] - fn test_recurring_bill() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - env.mock_all_auths(); - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Rent"), - &10000, - &1000000, - &true, - &30, - &String::from_str(&env, "XLM"), - &None, - &String::from_str(&env, "XLM"), - ); - - env.mock_all_auths(); - client.pay_bill(&owner, &bill_id); - - // Check original bill is paid - let bill = client.get_bill(&bill_id).unwrap(); - assert!(bill.paid); - - // Check next recurring bill was created - let bill2 = client.get_bill(&2).unwrap(); - assert!(!bill2.paid); - - assert_eq!(bill2.amount, 10000); - assert_eq!(bill2.due_date, 1000000 + (30 * 86400)); - } - - #[test] - fn test_get_unpaid_bills() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - env.mock_all_auths(); - client.create_bill( - &owner, - &String::from_str(&env, "Bill1"), - &100, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - &None, - &String::from_str(&env, "XLM"), - ); - env.mock_all_auths(); - client.create_bill( - &owner, - &String::from_str(&env, "Bill2"), - &200, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - &None, - &String::from_str(&env, "XLM"), - ); - env.mock_all_auths(); - client.create_bill( - &owner, - &String::from_str(&env, "Bill3"), - &300, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - &None, - &String::from_str(&env, "XLM"), - ); - env.mock_all_auths(); - client.pay_bill(&owner, &1); - - let unpaid = client.get_unpaid_bills(&owner); - assert_eq!(unpaid.len(), 2); - } - - #[test] - fn test_get_total_unpaid() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - env.mock_all_auths(); - client.create_bill( - &owner, - &String::from_str(&env, "Bill1"), - &100, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - &None, - &String::from_str(&env, "XLM"), - ); - env.mock_all_auths(); - client.create_bill( - &owner, - &String::from_str(&env, "Bill2"), - &200, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - &None, - &String::from_str(&env, "XLM"), - ); - env.mock_all_auths(); - client.create_bill( - &owner, - &String::from_str(&env, "Bill3"), - &300, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - &None, - &String::from_str(&env, "XLM"), - ); - env.mock_all_auths(); - client.pay_bill(&owner, &1); - - let total = client.get_total_unpaid(&owner); - assert_eq!(total, 500); // 200 + 300 - } - - #[test] - fn test_pay_nonexistent_bill() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - let result = client.try_pay_bill(&owner, &999); - assert_eq!(result, Err(Ok(Error::BillNotFound))); - } - - #[test] - fn test_pay_already_paid_bill() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - env.mock_all_auths(); - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Test"), - &100, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - &None, - &String::from_str(&env, "XLM"), - ); - env.mock_all_auths(); - client.pay_bill(&owner, &bill_id); - let result = client.try_pay_bill(&owner, &bill_id); - assert_eq!(result, Err(Ok(Error::BillAlreadyPaid))); - } - - #[test] - fn test_get_overdue_bills_succeeds() { - let env = Env::default(); - set_ledger_time(&env, 1, 2_000_000); - - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - env.mock_all_auths(); - // Create bills with different due dates - client.create_bill( - &owner, - &String::from_str(&env, "Overdue1"), - &100, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - &None, - &String::from_str(&env, "XLM"), - ); - env.mock_all_auths(); - client.create_bill( - &owner, - &String::from_str(&env, "Overdue2"), - &200, - &1500000, - &false, - &0, - &String::from_str(&env, "XLM"), - &None, - &String::from_str(&env, "XLM"), - ); - env.mock_all_auths(); - client.create_bill( - &owner, - &String::from_str(&env, "Future"), - &300, - &3000000, - &false, - &0, - &String::from_str(&env, "XLM"), - &None, - &String::from_str(&env, "XLM"), - ); - - let overdue = client.get_overdue_bills(&owner); - assert_eq!(overdue.len(), 2); // Only first two are overdue - } - - #[test] - fn test_cancel_bill() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - env.mock_all_auths(); - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Test"), - &100, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - env.mock_all_auths(); - client.cancel_bill(&owner, &bill_id); - - // Verify cancelled bill is completely removed from storage - assert!( - client.get_bill(&bill_id).is_none(), - "cancelled bill should return None" - ); - - // Create another bill and verify its ID is distinct and cancelled bill still returns None - env.mock_all_auths(); - let new_bill_id = client.create_bill( - &owner, - &String::from_str(&env, "New Bill"), - &200, - &2000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - assert_ne!(bill_id, new_bill_id, "new bill should have different ID"); - assert!( - client.get_bill(&new_bill_id).is_some(), - "new bill should exist" - ); - assert!( - client.get_bill(&bill_id).is_none(), - "cancelled bill should still return None" - ); - &None, - &String::from_str(&env, "XLM"), - ); - env.mock_all_auths(); - client.cancel_bill(&owner, &bill_id); - let bill = client.get_bill(&bill_id); - assert!(bill.is_none()); - } - - #[test] - fn test_cancel_bill_owner_succeeds() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - env.mock_all_auths(); - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Test"), - &100, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - env.mock_all_auths(); - client.cancel_bill(&owner, &bill_id); - - // Verify owner can successfully cancel their own bill and it's removed - assert!( - client.get_bill(&bill_id).is_none(), - "bill should be removed after owner cancellation" - ); - ); - env.mock_all_auths(); - client.cancel_bill(&owner, &bill_id); - let bill = client.get_bill(&bill_id); - assert!(bill.is_none()); - } - - #[test] - fn test_cancel_bill_unauthorized_fails() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - let other = ::generate(&env); - - env.mock_all_auths(); - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Water"), - &500, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - - let result = client.try_cancel_bill(&other, &bill_id); - assert_eq!(result, Err(Ok(Error::Unauthorized))); - } - - #[test] - fn test_cancel_nonexistent_bill() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - env.mock_all_auths(); - let result = client.try_cancel_bill(&owner, &999); - assert_eq!(result, Err(Ok(Error::BillNotFound))); - } - - #[test] - fn test_set_external_ref_success() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Internet"), - &150, - &1000000, - &false, - &0, - &None, - ); - - let ref_id = Some(String::from_str(&env, "BILL-EXT-123")); - env.mock_all_auths(); - client.set_external_ref(&owner, &bill_id, &ref_id); - - let bill = client.get_bill(&bill_id).unwrap(); - assert_eq!(bill.external_ref, ref_id); - } - - #[test] - fn test_set_external_ref_unauthorized() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - let other = ::generate(&env); - - env.mock_all_auths(); - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Internet"), - &150, - &1000000, - &false, - &0, - &None, - ); - - env.mock_all_auths(); - let result = client.try_set_external_ref( - &other, - &bill_id, - &Some(String::from_str(&env, "BILL-EXT-123")), - ); - assert_eq!(result, Err(Ok(Error::Unauthorized))); - } - - #[test] - fn test_multiple_recurring_payments() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - env.mock_all_auths(); - // Create recurring bill - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Subscription"), - &999, - &1000000, - &true, - &30, - &String::from_str(&env, "XLM"), - &None, - &String::from_str(&env, "XLM"), - ); - env.mock_all_auths(); - // Pay first bill - creates second - client.pay_bill(&owner, &bill_id); - let bill2 = client.get_bill(&2).unwrap(); - assert!(!bill2.paid); - assert_eq!(bill2.due_date, 1000000 + (30 * 86400)); - env.mock_all_auths(); - // Pay second bill - creates third - client.pay_bill(&owner, &2); - let bill3 = client.get_bill(&3).unwrap(); - assert!(!bill3.paid); - assert_eq!(bill3.due_date, 1000000 + (60 * 86400)); - } - - #[test] - #[allow(deprecated)] - fn test_get_all_bills_admin_only() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - let admin = ::generate(&env); - - env.mock_all_auths(); - - // Set up pause admin - client.set_pause_admin(&admin, &admin); - - client.create_bill( - &owner, - &String::from_str(&env, "Bill1"), - &100, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - &None, - &String::from_str(&env, "XLM"), - ); - client.create_bill( - &owner, - &String::from_str(&env, "Bill2"), - &200, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - &None, - &String::from_str(&env, "XLM"), - ); - client.create_bill( - &owner, - &String::from_str(&env, "Bill3"), - &300, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - &None, - &String::from_str(&env, "XLM"), - ); - client.pay_bill(&owner, &1); - - // Admin can see all 3 bills - let all = client.get_all_bills(&admin); - assert_eq!(all.len(), 3); - } - #[test] - fn test_pay_bill_unauthorized() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - let other = ::generate(&env); - - env.mock_all_auths(); - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Water"), - &500, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - - let result = client.try_pay_bill(&other, &bill_id); - assert_eq!(result, Err(Ok(Error::Unauthorized))); - } - - #[test] - fn test_recurring_bill_cancellation() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Rent"), - &1000, - &1000000, - &true, // Recurring - &30, - &String::from_str(&env, "XLM"), - ); - - // Cancel the bill - client.cancel_bill(&owner, &bill_id); - - // Verify it's gone - let bill = client.get_bill(&bill_id); - assert!(bill.is_none()); - - // Verify paying it fails - let result = client.try_pay_bill(&owner, &bill_id); - assert_eq!(result, Err(Ok(Error::BillNotFound))); - } - - #[test] - fn test_pay_overdue_bill_succeeds() { - let env = Env::default(); - set_ledger_time(&env, 1, 2_000_000); // Set time past due date - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Late"), - &500, - &1000000, // Due in past - &false, - &0, - &String::from_str(&env, "XLM"), - ); - - // Verify it shows up in overdue - let overdue = client.get_overdue_bills(&owner); - assert_eq!(overdue.len(), 1); - - // Pay it - client.pay_bill(&owner, &bill_id); - - // Verify it's no longer overdue (because it's paid) - let overdue_after = client.get_overdue_bills(&owner); - assert_eq!(overdue_after.len(), 0); - } - - #[test] - fn test_short_recurrence() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Daily"), - &10, - &1000000, - &true, // Recurring - &1, // Daily - &String::from_str(&env, "XLM"), - ); - - client.pay_bill(&owner, &bill_id); - - let next_bill = client.get_bill(&2).unwrap(); - assert_eq!(next_bill.due_date, 1000000 + 86400); // Exactly 1 day later - } - - #[test] - fn test_get_all_bills_for_owner_basic() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - client.create_bill( - &owner, - &String::from_str(&env, "Electricity"), - &100, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - client.create_bill( - &owner, - &String::from_str(&env, "Water"), - &200, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - - let bills = client.get_all_bills_for_owner(&owner); - assert_eq!(bills.len(), 2); - for bill in bills.iter() { - assert_eq!(bill.owner, owner); - } - } - - #[test] - fn test_get_all_bills_for_owner_isolation() { - // Alice's bills must NOT appear when Bob queries, and vice versa - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let alice = ::generate(&env); - let bob = ::generate(&env); - - env.mock_all_auths(); - client.create_bill( - &alice, - &String::from_str(&env, "Alice Rent"), - &1000, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - client.create_bill( - &alice, - &String::from_str(&env, "Alice Water"), - &200, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - client.create_bill( - &bob, - &String::from_str(&env, "Bob Internet"), - &50, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - - let alice_bills = client.get_all_bills_for_owner(&alice); - let bob_bills = client.get_all_bills_for_owner(&bob); - - // Alice sees only her 2 bills - assert_eq!(alice_bills.len(), 2); - for bill in alice_bills.iter() { - assert_eq!(bill.owner, alice, "Alice received a bill she doesn't own"); - } - - // Bob sees only his 1 bill - assert_eq!(bob_bills.len(), 1); - assert_eq!(bob_bills.get(0).unwrap().owner, bob); - } - - #[test] - fn test_get_all_bills_for_owner_empty() { - // Owner with no bills gets an empty vec, not someone else's bills - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let alice = ::generate(&env); - let bob = ::generate(&env); - - env.mock_all_auths(); - client.create_bill( - &alice, - &String::from_str(&env, "Alice Bill"), - &500, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - - // Bob never created a bill - let bob_bills = client.get_all_bills_for_owner(&bob); - assert_eq!(bob_bills.len(), 0); - } - - #[test] - fn test_get_all_bills_for_owner_after_pay() { - // Paid bills still belong to owner — they should still appear - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Paid Bill"), - &300, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - client.pay_bill(&owner, &bill_id); - - let bills = client.get_all_bills_for_owner(&owner); - assert_eq!(bills.len(), 1); - assert!(bills.get(0).unwrap().paid); - } - - #[test] - fn test_get_all_bills_for_owner_after_cancel() { - // Cancelled bills are removed — owner query must reflect that - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "To Cancel"), - &100, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - client.create_bill( - &owner, - &String::from_str(&env, "Keep"), - &200, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - client.cancel_bill(&owner, &bill_id); - - let bills = client.get_all_bills_for_owner(&owner); - assert_eq!(bills.len(), 1); - assert_eq!(bills.get(0).unwrap().amount, 200); - } - - #[test] - #[allow(deprecated)] - fn test_get_all_bills_non_admin_fails() { - // Non-admin calling get_all_bills (admin endpoint) must get Unauthorized - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let admin = ::generate(&env); - let alice = ::generate(&env); - - env.mock_all_auths(); - client.set_pause_admin(&admin, &admin); - client.create_bill( - &alice, - &String::from_str(&env, "Alice Bill"), - &100, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - - // Alice tries to call the admin-only endpoint - let result = client.try_get_all_bills(&alice); - assert_eq!(result.unwrap_err().unwrap(), Error::Unauthorized); - } - - #[test] - #[allow(deprecated)] - fn test_get_all_bills_no_admin_set_fails() { - // If no pause admin is set at all, get_all_bills must return Unauthorized - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let alice = ::generate(&env); - - env.mock_all_auths(); - - let result = client.try_get_all_bills(&alice); - assert_eq!(result.unwrap_err().unwrap(), Error::Unauthorized); - } - - // NOTE: The following schedule-related tests are commented out because the - // BillPayments contract does not implement create_schedule, modify_schedule, - // cancel_schedule, execute_due_schedules, get_schedule, or get_schedules methods. - // These tests were added to main before the contract methods were implemented. - // Uncomment once the schedule functionality is added to the contract. - - /* - #[test] - fn test_create_schedule() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - set_time(&env, 1000); - - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Electricity"), - &1000, - &2000, - &false, - &0, - ); - - let schedule_id = client.create_schedule(&owner, &bill_id, &3000, &86400); - assert_eq!(schedule_id, 1); - - let schedule = client.get_schedule(&schedule_id); - assert!(schedule.is_some()); - let schedule = schedule.unwrap(); - assert_eq!(schedule.next_due, 3000); - assert_eq!(schedule.interval, 86400); - assert!(schedule.active); - } - - #[test] - fn test_modify_schedule() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - set_time(&env, 1000); - - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Electricity"), - &1000, - &2000, - &false, - &0, - ); - - let schedule_id = client.create_schedule(&owner, &bill_id, &3000, &86400); - client.modify_schedule(&owner, &schedule_id, &4000, &172800); - - let schedule = client.get_schedule(&schedule_id).unwrap(); - assert_eq!(schedule.next_due, 4000); - assert_eq!(schedule.interval, 172800); - } - - #[test] - fn test_cancel_schedule() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - set_time(&env, 1000); - - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Electricity"), - &1000, - &2000, - &false, - &0, - ); - - let schedule_id = client.create_schedule(&owner, &bill_id, &3000, &86400); - client.cancel_schedule(&owner, &schedule_id); - - let schedule = client.get_schedule(&schedule_id).unwrap(); - assert!(!schedule.active); - } - - #[test] - fn test_execute_due_schedules() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - set_time(&env, 1000); - - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Electricity"), - &1000, - &2000, - &false, - &0, - ); - - let schedule_id = client.create_schedule(&owner, &bill_id, &3000, &0); - - set_time(&env, 3500); - let executed = client.execute_due_schedules(); - - assert_eq!(executed.len(), 1); - assert_eq!(executed.get(0).unwrap(), schedule_id); - - let bill = client.get_bill(&bill_id).unwrap(); - assert!(bill.paid); - } - - #[test] - fn test_execute_recurring_schedule() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - set_time(&env, 1000); - - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Electricity"), - &1000, - &2000, - &true, - &30, - ); - - let schedule_id = client.create_schedule(&owner, &bill_id, &3000, &86400); - - set_time(&env, 3500); - client.execute_due_schedules(); - - let schedule = client.get_schedule(&schedule_id).unwrap(); - assert!(schedule.active); - assert_eq!(schedule.next_due, 3000 + 86400); - } - - #[test] - fn test_execute_missed_schedules() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - set_time(&env, 1000); - - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Electricity"), - &1000, - &2000, - &true, - &30, - ); - - let schedule_id = client.create_schedule(&owner, &bill_id, &3000, &86400); - - set_time(&env, 3000 + 86400 * 3 + 100); - client.execute_due_schedules(); - - let schedule = client.get_schedule(&schedule_id).unwrap(); - assert_eq!(schedule.missed_count, 3); - assert!(schedule.next_due > 3000 + 86400 * 3); - } - - #[test] - fn test_schedule_validation_past_date() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - set_time(&env, 5000); - - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Electricity"), - &1000, - &6000, - &false, - &0, - ); - - let result = client.try_create_schedule(&owner, &bill_id, &3000, &86400); - assert!(result.is_err()); - } - - #[test] - fn test_get_schedules() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - set_time(&env, 1000); - - let bill_id1 = client.create_bill( - &owner, - &String::from_str(&env, "Electricity"), - &1000, - &2000, - &false, - &0, - ); - - let bill_id2 = client.create_bill( - &owner, - &String::from_str(&env, "Water"), - &500, - &2000, - &false, - &0, - ); - - client.create_schedule(&owner, &bill_id1, &3000, &86400); - client.create_schedule(&owner, &bill_id2, &4000, &172800); - - let schedules = client.get_schedules(&owner); - assert_eq!(schedules.len(), 2); - } - */ - #[test] - fn test_create_bill_emits_event() { - use soroban_sdk::testutils::Events; - use soroban_sdk::{symbol_short, vec, IntoVal}; - - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - - client.create_bill( - &owner, - &String::from_str(&env, "Electricity"), - &1000, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - - let events = env.events().all(); - assert!(events.len() > 0); - let last_event = events.last().unwrap(); - - client.create_bill( - &owner, - &String::from_str(&env, "Water Bill"), - &500, - &5000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - let expected_topics = vec![ - &env, - symbol_short!("Remitwise").into_val(&env), - 1u32.into_val(&env), // EventCategory::State - 1u32.into_val(&env), // EventPriority::Medium - symbol_short!("created").into_val(&env), - ]; - - assert_eq!(last_event.1, expected_topics); - - let data: (u32, soroban_sdk::Address, i128, u64) = - soroban_sdk::FromVal::from_val(&env, &last_event.2); - assert_eq!(data, (1u32, owner.clone(), 1000i128, 1000000u64)); - - assert_eq!(last_event.0, contract_id.clone()); - } - - #[test] - fn test_pay_bill_emits_event() { - use soroban_sdk::testutils::Events; - use soroban_sdk::{symbol_short, vec, IntoVal}; - - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - // Phase 1: Create first bill at seq 100 - // TTL goes from 100 → 518,400. live_until = 518,500 - let id1 = client.create_bill( - &owner, - &String::from_str(&env, "Rent"), - &2000, - &1_100_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - - // Phase 2: Advance to seq 510,000 (TTL = 8,500 < 17,280) - // create_bill re-extends → live_until = 1,028,400 - env.ledger().set(LedgerInfo { - protocol_version: 20, - sequence_number: 510_000, - timestamp: 510_000, - network_id: [0; 32], - base_reserve: 10, - min_temp_entry_ttl: 100, - min_persistent_entry_ttl: 100, - max_entry_ttl: 700_000, - }); - - let id2 = client.create_bill( - &owner, - &String::from_str(&env, "Internet"), - &100, - &1_200_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - - // Phase 3: Advance to seq 1,020,000 (TTL = 8,400 < 17,280) - // pay_bill re-extends → live_until = 1,538,400 - env.ledger().set(LedgerInfo { - protocol_version: 20, - sequence_number: 1_020_000, - timestamp: 1_020_000, - network_id: [0; 32], - base_reserve: 10, - min_temp_entry_ttl: 100, - min_persistent_entry_ttl: 100, - max_entry_ttl: 700_000, - }); - - // Pay second bill to refresh TTL once more - client.pay_bill(&owner, &id2); - - // Both bills should still be accessible - let bill1 = client.get_bill(&id1); - assert!( - bill1.is_some(), - "First bill must persist across ledger advancements" - ); - assert_eq!(bill1.unwrap().amount, 2000); - - let bill2 = client.get_bill(&id2); - assert!( - bill2.is_some(), - "Second bill must persist across ledger advancements" - ); - assert!(bill2.unwrap().paid, "Second bill should be marked paid"); - - // TTL should be fully refreshed - let ttl = env.as_contract(&contract_id, || env.storage().instance().get_ttl()); - assert!( - ttl >= 518_400, - "Instance TTL ({}) must remain >= 518,400 after repeated operations", - ttl - ); - } - - /// Verify that archive_paid_bills extends instance TTL and archives data. - /// - /// Note: both `extend_instance_ttl` and `extend_archive_ttl` operate on - /// instance() storage. Since `extend_instance_ttl` is called first in - /// `archive_paid_bills`, it bumps the TTL above the shared threshold - /// (17,280), making the subsequent `extend_archive_ttl` a no-op. - /// This test verifies the instance TTL is at least INSTANCE_BUMP_AMOUNT - /// and that archived data is accessible. - #[test] - fn test_archive_ttl_extended_on_archive_paid_bills() { - let env = Env::default(); - env.mock_all_auths(); - - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Electricity"), - &1000, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - client.pay_bill(&owner, &1); - - // Advance ledger so TTL drops below threshold - // After pay_bill at seq 100: live_until = 518,500 - // At seq 510,000: TTL = 8,500 < 17,280 → archive will re-extend - env.ledger().set(LedgerInfo { - protocol_version: 20, - sequence_number: 510_000, - timestamp: 510_000, - network_id: [0; 32], - base_reserve: 10, - min_temp_entry_ttl: 100, - min_persistent_entry_ttl: 100, - max_entry_ttl: 3_000_000, - }); - - // archive_paid_bills calls extend_instance_ttl then extend_archive_ttl - let archived = client.archive_paid_bills(&owner, &600_000); - assert_eq!(archived, 1); - - let ttl = env.as_contract(&contract_id, || env.storage().instance().get_ttl()); - assert!( - ttl >= 518_400, - "Instance TTL ({}) must be >= INSTANCE_BUMP_AMOUNT (518,400) after archiving", - ttl - ); - - env.mock_all_auths(); - - client.pay_bill(&owner, &bill_id); - - let events = env.events().all(); - let last_event = events.last().unwrap(); - - let id1 = client.create_bill( - &owner, - &String::from_str(&env, "Gas"), - &300, - &600_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - let id2 = client.create_bill( - &owner, - &String::from_str(&env, "Water"), - &200, - &600_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - let expected_topics = vec![ - &env, - symbol_short!("Remitwise").into_val(&env), - 0u32.into_val(&env), // EventCategory::Transaction - 2u32.into_val(&env), // EventPriority::High - symbol_short!("paid").into_val(&env), - ]; - - assert_eq!(last_event.1, expected_topics); - - let data: (u32, soroban_sdk::Address, i128) = - soroban_sdk::FromVal::from_val(&env, &last_event.2); - assert_eq!(data, (bill_id, owner.clone(), 1000i128)); - - assert_eq!(last_event.0, contract_id.clone()); - } - - #[test] - fn test_get_overdue_bills_owner_scoped() { - let env = Env::default(); - set_time(&env, 2_000_000); - - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let alice = ::generate(&env); - let bob = ::generate(&env); - - env.mock_all_auths(); - - // Alice has 2 overdue bills - client.create_bill( - &alice, - &String::from_str(&env, "Alice Overdue1"), - &100, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - client.create_bill( - &alice, - &String::from_str(&env, "Alice Overdue2"), - &200, - &1_500_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - - // Bob has 1 overdue bill - client.create_bill( - &bob, - &String::from_str(&env, "Bob Overdue"), - &300, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - - // Alice has 1 future bill (not overdue) - client.create_bill( - &alice, - &String::from_str(&env, "Alice Future"), - &400, - &3_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - - let alice_overdue = client.get_overdue_bills(&alice); - let bob_overdue = client.get_overdue_bills(&bob); - - // Alice sees only her 2 overdue bills, not Bob's - assert_eq!(alice_overdue.len(), 2); - for bill in alice_overdue.iter() { - assert_eq!(bill.owner, alice); - } - - // Bob sees only his 1 overdue bill, not Alice's - assert_eq!(bob_overdue.len(), 1); - assert_eq!(bob_overdue.get(0).unwrap().owner, bob); - } - - #[test] - #[should_panic(expected = "HostError: Error(Auth, InvalidAction)")] - fn test_create_bill_non_owner_auth_failure() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - let other = ::generate(&env); - - // Do not mock auth for other, attempt to create bill for owner as other - // Wait, if other calls, it's just a call. The contract will check owner.require_auth(). - // If owner didn't authorize, it panics. - client.create_bill( - &owner, - &String::from_str(&env, "Water"), - &500, - &1000000, - &false, - &0, - ); - } - - #[test] - #[should_panic(expected = "HostError: Error(Auth, InvalidAction)")] - fn test_pay_bill_non_owner_auth_failure() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - let other = ::generate(&env); - - client.mock_auths(&[soroban_sdk::testutils::MockAuth { - address: &owner, - invoke: &soroban_sdk::testutils::MockAuthInvoke { - contract: &contract_id, - fn_name: "create_bill", - args: ( - &owner, - String::from_str(&env, "Water"), - 500i128, - 1000000u64, - false, - 0u32, - ) - .into_val(&env), - sub_invokes: &[], - }, - }]); - - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Water"), - &500, - &1000000, - &false, - &0, - ); - - // other tries to pay the bill for owner - client.pay_bill(&owner, &bill_id); - } - - #[test] - #[should_panic(expected = "HostError: Error(Auth, InvalidAction)")] - fn test_cancel_bill_non_owner_auth_failure() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - let other = ::generate(&env); - - client.mock_auths(&[soroban_sdk::testutils::MockAuth { - address: &owner, - invoke: &soroban_sdk::testutils::MockAuthInvoke { - contract: &contract_id, - fn_name: "create_bill", - args: ( - &owner, - String::from_str(&env, "Water"), - 500i128, - 1000000u64, - false, - 0u32, - ) - .into_val(&env), - sub_invokes: &[], - }, - }]); - - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Water"), - &500, - &1000000, - &false, - &0, - ); - - // other tries to cancel the bill for owner - client.cancel_bill(&owner, &bill_id); - } - - // ----------------------------------------------------------------------- - // RECURRING BILLS DATE MATH TESTS - // ----------------------------------------------------------------------- - // These tests verify the core date math for recurring bills: - // next_due_date = due_date + (frequency_days * 86400) - // Ensures paid_at does not affect next bill's due_date calculation. - // ----------------------------------------------------------------------- - // RECURRING BILLS DATE MATH TESTS - // ----------------------------------------------------------------------- - // These tests verify the core date math for recurring bills: - // next_due_date = due_date + (frequency_days * 86400) - // Ensures paid_at does not affect next bill's due_date calculation. - - #[test] - fn test_recurring_date_math_frequency_1_day() { - // Test: frequency_days = 1 → next due date is +1 day (86400 seconds) - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - let base_due_date = 1_000_000u64; - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Daily Bill"), - &100, - &base_due_date, - &true, // recurring - &1, // frequency_days = 1 - &String::from_str(&env, "XLM"), - ); - - // Pay the bill - env.mock_all_auths(); - client.pay_bill(&owner, &bill_id); - - // Verify next bill's due_date = base_due_date + (1 * 86400) - let next_bill = client.get_bill(&2).unwrap(); - assert!(!next_bill.paid, "Next bill should be unpaid"); - assert_eq!( - next_bill.due_date, - base_due_date + 86400, - "Next due date should be exactly 1 day later" - ); - assert_eq!(next_bill.frequency_days, 1, "Frequency should be preserved"); - } - - #[test] - fn test_recurring_date_math_frequency_30_days() { - // Test: frequency_days = 30 → next due date is +30 days (2,592,000 seconds) - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - let base_due_date = 1_000_000u64; - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Monthly Bill"), - &500, - &base_due_date, - &true, // recurring - &30, // frequency_days = 30 - &String::from_str(&env, "XLM"), - ); - - // Pay the bill - env.mock_all_auths(); - client.pay_bill(&owner, &bill_id); - - // Verify next bill's due_date = base_due_date + (30 * 86400) - let next_bill = client.get_bill(&2).unwrap(); - assert!(!next_bill.paid, "Next bill should be unpaid"); - let expected_due_date = base_due_date + (30u64 * 86400); - assert_eq!( - next_bill.due_date, expected_due_date, - "Next due date should be exactly 30 days later" - ); - assert_eq!( - next_bill.frequency_days, 30, - "Frequency should be preserved" - ); - } - - #[test] - fn test_recurring_date_math_frequency_365_days() { - // Test: frequency_days = 365 → next due date is +365 days (31,536,000 seconds) - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - let base_due_date = 1_000_000u64; - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Annual Bill"), - &1200, - &base_due_date, - &true, // recurring - &365, // frequency_days = 365 - &String::from_str(&env, "XLM"), - ); - - // Pay the bill - env.mock_all_auths(); - client.pay_bill(&owner, &bill_id); - - // Verify next bill's due_date = base_due_date + (365 * 86400) - let next_bill = client.get_bill(&2).unwrap(); - assert!(!next_bill.paid, "Next bill should be unpaid"); - let expected_due_date = base_due_date + (365u64 * 86400); - assert_eq!( - next_bill.due_date, expected_due_date, - "Next due date should be exactly 365 days later" - ); - assert_eq!( - next_bill.frequency_days, 365, - "Frequency should be preserved" - ); - } - - // #[test] - // fn test_recurring_date_math_paid_at_does_not_affect_next_due() { - // let env = Env::default(); - - // // FORCE reset to a very small number first - // env.ledger().set_timestamp(100); - - // let contract_id = env.register_contract(None, BillPayments); - // let client = BillPaymentsClient::new(&env, &contract_id); - // let owner = Address::generate(&env); - // env.mock_all_auths(); - - // // Now current_time (100) is definitely < base_due_date (1,000,000) - // let base_due_date = 1_000_000u64; - // let bill_id = client.create_bill( - // &owner, - // &String::from_str(&env, "Late Payment Test"), - // &300, - // &base_due_date, - // &true, - // &30, - // &String::from_str(&env, "XLM"), - // ); - - // // Warp to late payment time - // env.ledger().set_timestamp(1_000_500); - // client.pay_bill(&owner, &bill_id); - - // let next_bill = client.get_bill(&2).unwrap(); - // let expected_due_date = base_due_date + (30u64 * 86400); - // assert_eq!(next_bill.due_date, expected_due_date); - // } - - - #[test] - fn test_recurring_date_math_multiple_pay_cycles_3rd_bill() { - // Test: Multiple pay cycles - verify 3rd bill's due date advances correctly - // Bill 1: due_date=1000000, frequency=30 - // Bill 2: due_date=1000000 + (30*86400) - // Bill 3: due_date=1000000 + (60*86400) - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - let base_due_date = 1_000_000u64; - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Three-Cycle Bill"), - &150, - &base_due_date, - &true, // recurring - &30, // frequency_days = 30 - &String::from_str(&env, "XLM"), - ); - - // Pay first bill - env.mock_all_auths(); - client.pay_bill(&owner, &bill_id); - - // Pay second bill - env.mock_all_auths(); - client.pay_bill(&owner, &2); - - // Pay third bill - env.mock_all_auths(); - client.pay_bill(&owner, &3); - - // Verify third bill is now paid - let bill3_paid = client.get_bill(&3).unwrap(); - assert!(bill3_paid.paid); - - // Verify fourth bill was created with correct due_date - let bill4 = client.get_bill(&4).unwrap(); - let expected_bill4_due = base_due_date + (90u64 * 86400); // 3 * 30 days - assert_eq!( - bill4.due_date, expected_bill4_due, - "Bill 4 due_date should be base + (90*86400)" - ); - assert!(!bill4.paid); - } - - #[test] - fn test_recurring_date_math_early_payment_does_not_affect_schedule() { - // Test: Paying a bill EARLY should not affect the next bill's due_date - // Bill 1: due_date=1000000, paid at time=500000 (paid 500000 seconds early) - // Bill 2: due_date should still be 1000000 + (30*86400) - let env = Env::default(); - set_time(&env, 500_000); // Set time BEFORE due date - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - let base_due_date = 1_000_000u64; - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Early Payment Test"), - &200, - &base_due_date, - &true, // recurring - &30, // frequency_days = 30 - &String::from_str(&env, "XLM"), - ); - - // Pay the bill early (at time 500_000) - env.mock_all_auths(); - client.pay_bill(&owner, &bill_id); - - // Verify original bill has paid_at set to early time - let paid_bill = client.get_bill(&bill_id).unwrap(); - assert!(paid_bill.paid); - assert_eq!(paid_bill.paid_at, Some(500_000)); - - // Verify next bill's due_date is still based on original due_date - let next_bill = client.get_bill(&2).unwrap(); - let expected_due_date = base_due_date + (30u64 * 86400); - assert_eq!( - next_bill.due_date, expected_due_date, - "Next due date should not be affected by early payment" - ); - } - - #[test] - fn test_recurring_date_math_preserves_frequency_across_cycles() { - // Test: frequency_days is preserved across all recurring cycles - // Verify that Bill 1, 2, 3 all have the same frequency_days value - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - let frequency = 7u32; // Weekly - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Weekly Bill"), - &50, - &1_000_000, - &true, - &frequency, - &String::from_str(&env, "XLM"), - ); - - // Pay first bill - env.mock_all_auths(); - client.pay_bill(&owner, &bill_id); - - // Pay second bill - env.mock_all_auths(); - client.pay_bill(&owner, &2); - - // Verify all bills have the same frequency_days - let bill1 = client.get_bill(&1).unwrap(); - let bill2 = client.get_bill(&2).unwrap(); - let bill3 = client.get_bill(&3).unwrap(); - - assert_eq!(bill1.frequency_days, frequency); - assert_eq!(bill2.frequency_days, frequency); - assert_eq!(bill3.frequency_days, frequency); - } - - #[test] - fn test_recurring_date_math_amount_preserved_across_cycles() { - // Test: Bill amount is preserved across all recurring cycles - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - let amount = 999i128; - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Fixed Amount Bill"), - &amount, - &1_000_000, - &true, - &30, - &String::from_str(&env, "XLM"), - ); - - // Pay first bill - env.mock_all_auths(); - client.pay_bill(&owner, &bill_id); - - // Pay second bill - env.mock_all_auths(); - client.pay_bill(&owner, &2); - - // Verify all bills have the same amount - let bill1 = client.get_bill(&1).unwrap(); - let bill2 = client.get_bill(&2).unwrap(); - let bill3 = client.get_bill(&3).unwrap(); - - assert_eq!(bill1.amount, amount); - assert_eq!(bill2.amount, amount); - assert_eq!(bill3.amount, amount); - } - - #[test] - fn test_recurring_date_math_name_preserved_across_cycles() { - // Test: Bill name is preserved across all recurring cycles - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - let name = String::from_str(&env, "Rent Payment"); - let bill_id = client.create_bill( - &owner, - &name, - &1000, - &1_000_000, - &true, - &30, - &String::from_str(&env, "XLM"), - ); - - // Pay first bill - env.mock_all_auths(); - client.pay_bill(&owner, &bill_id); - - // Pay second bill - env.mock_all_auths(); - client.pay_bill(&owner, &2); - - // Verify all bills have the same name - let bill1 = client.get_bill(&1).unwrap(); - let bill2 = client.get_bill(&2).unwrap(); - let bill3 = client.get_bill(&3).unwrap(); - - assert_eq!(bill1.name, name); - assert_eq!(bill2.name, name); - assert_eq!(bill3.name, name); - } - - #[test] - fn test_recurring_date_math_owner_preserved_across_cycles() { - // Test: Bill owner is preserved across all recurring cycles - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Owner Test"), - &100, - &1_000_000, - &true, - &30, - &String::from_str(&env, "XLM"), - ); - - // Pay first bill - env.mock_all_auths(); - client.pay_bill(&owner, &bill_id); - - // Pay second bill - env.mock_all_auths(); - client.pay_bill(&owner, &2); - - // Verify all bills have the same owner - let bill1 = client.get_bill(&1).unwrap(); - let bill2 = client.get_bill(&2).unwrap(); - let bill3 = client.get_bill(&3).unwrap(); - - assert_eq!(bill1.owner, owner); - assert_eq!(bill2.owner, owner); - assert_eq!(bill3.owner, owner); - } - - #[test] - fn test_recurring_date_math_exact_calculation_verification() { - // Test: Verify exact date math calculation with known values - // due_date = 1_000_000 - // frequency_days = 14 - // Expected: 1_000_000 + (14 * 86400) = 1_000_000 + 1_209_600 = 2_209_600 - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - let base_due = 1_000_000u64; - let freq = 14u32; - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Math Verification"), - &100, - &base_due, - &true, - &freq, - &String::from_str(&env, "XLM"), - ); - - env.mock_all_auths(); - client.pay_bill(&owner, &bill_id); - - let next_bill = client.get_bill(&2).unwrap(); - let expected = 1_000_000u64 + (14u64 * 86400); - assert_eq!(next_bill.due_date, expected); - assert_eq!(next_bill.due_date, 2_209_600); - } - - // ══════════════════════════════════════════════════════════════════════ - // Time & Ledger Drift Resilience Tests (#158) - // - // Assumptions documented here: - // - A bill is overdue when due_date < current_time (strict less-than). - // - At exactly due_date the bill is NOT yet overdue. - // - Stellar ledger timestamps are monotonically increasing in production. - // ══════════════════════════════════════════════════════════════════════ - - /// Bill is NOT overdue when ledger timestamp == due_date (inclusive boundary). - #[test] - fn test_time_drift_bill_not_overdue_at_exact_due_date() { - let due_date = 1_000_000u64; - let env = Env::default(); - set_time(&env, due_date); - - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - client.create_bill( - &owner, - &String::from_str(&env, "Power"), - &200, - &due_date, - &false, - &0, - ); - - let page = client.get_overdue_bills(&0, &100); - assert_eq!( - page.count, 0, - "Bill must not appear overdue when current_time == due_date" - ); - } - - /// Bill becomes overdue exactly one second after due_date. - #[test] - fn test_time_drift_bill_overdue_one_second_after_due_date() { - let due_date = 1_000_000u64; - let env = Env::default(); - set_time(&env, due_date); - - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - client.create_bill( - &owner, - &String::from_str(&env, "Internet"), - &150, - &due_date, - &false, - &0, - ); - - // Not yet overdue at due_date - let page = client.get_overdue_bills(&0, &100); - assert_eq!(page.count, 0); - - // Advance one second past due_date - set_time(&env, due_date + 1); - let page = client.get_overdue_bills(&0, &100); - assert_eq!( - page.count, 1, - "Bill must appear overdue exactly one second past due_date" - ); - } - - /// Mix of past-due, exactly-due, and future bills: only past-due appears. - // #[test] - // fn test_time_drift_overdue_boundary_mixed_bills() { - // let env = Env::default(); - // let contract_id = env.register_contract(None, BillPayments); - // let client = BillPaymentsClient::new(&env, &contract_id); - // let owner = ::generate(&env); - // env.mock_all_auths(); - - // // 1. Set time to a starting point - // let start_time = 2_000_000u64; - // env.ledger().set_timestamp(start_time); - - // // 2. Create bills with relative due dates - // // All these due dates are >= current_time (2,000,000), so validation passes. - - // // This will become overdue later - // client.create_bill( - // &owner, - // &String::from_str(&env, "Overdue"), - // &100, - // &2000001, // T+1 - // &false, - // &0, - // &String::from_str(&env, "XLM"), - // ); - - // // This will be exactly due later - // client.create_bill( - // &owner, - // &String::from_str(&env, "DueNow"), - // &200, - // &2000005, // T+5 - // &false, - // &0, - // &String::from_str(&env, "XLM"), - // ); - - // // This will stay in the future - // client.create_bill( - // &owner, - // &String::from_str(&env, "Future"), - // &300, - // &2000010, // T+10 - // &false, - // &0, - // &String::from_str(&env, "XLM"), - // ); - - // // 3. WARP TIME forward to 2,000,005 - // // Now: - // // - Bill 1 (2000001) is < 2000005 (OVERDUE) - // // - Bill 2 (2000005) is == 2000005 (NOT OVERDUE) - // // - Bill 3 (2000010) is > 2000005 (NOT OVERDUE) - // env.ledger().set_timestamp(2000005); - - // let page = client.get_overdue_bills(&0, &100); - - // assert_eq!( - // page.count, 1, - // "Only the bill with due_date < current_time must appear overdue" - // ); - // assert_eq!( - // page.items.get(0).unwrap().amount, - // 100, - // "Overdue bill must be the one with due_date < current_time" - // ); - // } - - /// Full-day boundary: bill created at due_date, queried one day later, is overdue. - #[test] - fn test_time_drift_overdue_full_day_boundary() { - let day = 86400u64; - let due_date = 1_000_000u64; - let env = Env::default(); - set_time(&env, due_date); - - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - client.create_bill( - &owner, - &String::from_str(&env, "Monthly Rent"), - &5000, - &due_date, - &false, - &0, - ); - - // Still not overdue at due_date - let page = client.get_overdue_bills(&0, &100); - assert_eq!(page.count, 0); - - // One full day later – must be overdue - set_time(&env, due_date + day); - let page = client.get_overdue_bills(&0, &100); - assert_eq!( - page.count, 1, - "Bill must be overdue one full day past due_date" - ); - } - - // --------------------------------------------------------------------------- - // Tests — Issue #6: get_total_unpaid edge cases - // - // get_total_unpaid(env, owner) returns the sum of `amount` for all unpaid - // bills belonging to `owner`. These tests make the zero, single, multiple, - // after-pay, all-paid, and isolation cases explicit and documented. - // - // Paste this block inside the existing `mod testsuit { ... }` in your test - // file, alongside the other test functions. - // --------------------------------------------------------------------------- - - // --- No bills: owner who has never created a bill should get 0 --- - - #[test] - fn test_get_total_unpaid_no_bills_returns_zero() { - // An owner who has never created any bill must get 0, not a panic or - // a spurious non-zero value from another owner's data. - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - - let total = client.get_total_unpaid(&owner); - assert_eq!(total, 0, "owner with no bills must have total_unpaid == 0"); - } - - // --- All bills paid: owner whose every bill is paid should get 0 --- - - #[test] - fn test_get_total_unpaid_all_bills_paid_returns_zero() { - // Create several bills and pay them all; the total must then be 0. - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - - let id1 = client.create_bill( - &owner, - &String::from_str(&env, "Electricity"), - &400, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - let id2 = client.create_bill( - &owner, - &String::from_str(&env, "Water"), - &600, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - - client.pay_bill(&owner, &id1); - client.pay_bill(&owner, &id2); - - let total = client.get_total_unpaid(&owner); - assert_eq!( - total, 0, - "owner with all bills paid must have total_unpaid == 0" - ); - } - - // --- One unpaid bill: total equals that bill's amount --- - - #[test] - fn test_get_total_unpaid_one_unpaid_bill() { - // Exactly one unpaid bill; total_unpaid must equal its amount. - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - - client.create_bill( - &owner, - &String::from_str(&env, "Rent"), - &1000, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - - let total = client.get_total_unpaid(&owner); - assert_eq!( - total, 1000, - "one unpaid bill of 1000 must yield total_unpaid == 1000" - ); - } - - // --- Multiple unpaid bills: total equals the sum of all amounts --- - - #[test] - fn test_get_total_unpaid_multiple_unpaid_bills() { - // Three bills with amounts 100, 200, 300 → total must be 600. - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - - client.create_bill( - &owner, - &String::from_str(&env, "Bill A"), - &100, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - client.create_bill( - &owner, - &String::from_str(&env, "Bill B"), - &200, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - client.create_bill( - &owner, - &String::from_str(&env, "Bill C"), - &300, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - - let total = client.get_total_unpaid(&owner); - assert_eq!( - total, 600, - "three unpaid bills (100 + 200 + 300) must yield total_unpaid == 600" - ); - } - - // --- After paying one bill: total decreases by that bill's amount --- - - #[test] - fn test_get_total_unpaid_decreases_after_pay() { - // Create bills of 100, 200, 300; pay the 200 bill. - // Total must drop from 600 to 400 (100 + 300). - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - - client.create_bill( - &owner, - &String::from_str(&env, "Bill A"), - &100, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - let id_b = client.create_bill( - &owner, - &String::from_str(&env, "Bill B"), - &200, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - client.create_bill( - &owner, - &String::from_str(&env, "Bill C"), - &300, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - - // Confirm starting total - assert_eq!(client.get_total_unpaid(&owner), 600); - - // Pay the 200-unit bill - client.pay_bill(&owner, &id_b); - - let total = client.get_total_unpaid(&owner); - assert_eq!( - total, 400, - "after paying the 200 bill, total_unpaid must be 400 (100 + 300)" - ); - } - - // --- All paid (incremental): total reaches 0 as each bill is paid --- - - #[test] - fn test_get_total_unpaid_reaches_zero_as_bills_paid_incrementally() { - // Pay bills one by one and verify the running total after each payment. - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - - let id1 = client.create_bill( - &owner, - &String::from_str(&env, "Bill 1"), - &100, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - let id2 = client.create_bill( - &owner, - &String::from_str(&env, "Bill 2"), - &200, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - let id3 = client.create_bill( - &owner, - &String::from_str(&env, "Bill 3"), - &300, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - - assert_eq!(client.get_total_unpaid(&owner), 600); - - client.pay_bill(&owner, &id1); - assert_eq!( - client.get_total_unpaid(&owner), - 500, - "after paying 100-bill: 500 remaining" - ); - - client.pay_bill(&owner, &id2); - assert_eq!( - client.get_total_unpaid(&owner), - 300, - "after paying 200-bill: 300 remaining" - ); - - client.pay_bill(&owner, &id3); - assert_eq!( - client.get_total_unpaid(&owner), - 0, - "after paying all bills: total_unpaid must be 0" - ); - } - - // --- Isolation: owner_a's total is unaffected by owner_b's bills --- - - #[test] - fn test_get_total_unpaid_isolation_between_owners() { - // Bills belonging to owner_b must not appear in owner_a's total, and - // vice versa. - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner_a = ::generate(&env); - let owner_b = ::generate(&env); - - env.mock_all_auths(); - - // owner_a: two bills totalling 500 - client.create_bill( - &owner_a, - &String::from_str(&env, "A - Rent"), - &300, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - client.create_bill( - &owner_a, - &String::from_str(&env, "A - Water"), - &200, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - - // owner_b: one bill of 9999 - client.create_bill( - &owner_b, - &String::from_str(&env, "B - Internet"), - &9999, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - - let total_a = client.get_total_unpaid(&owner_a); - let total_b = client.get_total_unpaid(&owner_b); - - assert_eq!( - total_a, 500, - "owner_a's total_unpaid must be 500 (300 + 200), not influenced by owner_b" - ); - assert_eq!( - total_b, 9999, - "owner_b's total_unpaid must be 9999, not influenced by owner_a" - ); - } - - // --- Isolation after cross-owner payment: paying owner_b's bill does not - // change owner_a's total --- - - #[test] - fn test_get_total_unpaid_paying_other_owner_bill_has_no_effect() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner_a = ::generate(&env); - let owner_b = ::generate(&env); - - env.mock_all_auths(); - - client.create_bill( - &owner_a, - &String::from_str(&env, "A - Electricity"), - &750, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - let id_b = client.create_bill( - &owner_b, - &String::from_str(&env, "B - Gas"), - &1234, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - - // Pay owner_b's bill - client.pay_bill(&owner_b, &id_b); - - // owner_a's total must be unchanged - let total_a = client.get_total_unpaid(&owner_a); - assert_eq!( - total_a, 750, - "paying owner_b's bill must not affect owner_a's total_unpaid" - ); - - // owner_b's total must now be 0 - let total_b = client.get_total_unpaid(&owner_b); - assert_eq!(total_b, 0, "owner_b's total_unpaid must be 0 after payment"); - } - - // --- Cancelled bill is excluded from the total --- - - #[test] - fn test_get_total_unpaid_excludes_cancelled_bills() { - // A cancelled bill is removed from storage entirely, so it must not - // appear in the total. - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - - let id_keep = client.create_bill( - &owner, - &String::from_str(&env, "Keep"), - &500, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - let id_cancel = client.create_bill( - &owner, - &String::from_str(&env, "Cancel Me"), - &9000, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - - assert_eq!(client.get_total_unpaid(&owner), 9500); - - client.cancel_bill(&owner, &id_cancel); - - let total = client.get_total_unpaid(&owner); - assert_eq!( - total, 500, - "cancelled bill must not contribute to total_unpaid" - ); - - // Sanity: the kept bill is still there - assert!(client.get_bill(&id_keep).is_some()); - } - - // --- Minimum positive amount: a single bill of 1 --- - - #[test] - fn test_get_total_unpaid_minimum_amount() { - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - - client.create_bill( - &owner, - &String::from_str(&env, "Tiny Bill"), - &1, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - - let total = client.get_total_unpaid(&owner); - assert_eq!( - total, 1, - "single bill of amount 1 must yield total_unpaid == 1" - ); - } - - // --- Large amounts: verify no arithmetic overflow in the sum --- - - #[test] - fn test_get_total_unpaid_large_amounts_no_overflow() { - // Use amounts near i128::MAX / 2 to verify the summation does not panic - // or wrap. The contract uses plain addition, so this confirms the runtime - // handles large i128 values correctly. - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - - let big: i128 = i128::MAX / 4; // safely summable twice without overflow - - client.create_bill( - &owner, - &String::from_str(&env, "Big Bill 1"), - &big, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); - client.create_bill( - &owner, - &String::from_str(&env, "Big Bill 2"), - &big, - &1_000_000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); +fn env_with_auth() -> Env { + let env = Env::default(); + env.mock_all_auths(); + env +} - let total = client.get_total_unpaid(&owner); - assert_eq!( - total, - big * 2, - "sum of two large amounts must equal big * 2" - ); +#[test] +fn issue272_get_archived_bill_rejects_wrong_caller() { + let env = env_with_auth(); + let cid = env.register_contract(None, BillPayments); + let client = BillPaymentsClient::new(&env, &cid); + let owner = Address::generate(&env); + let attacker = Address::generate(&env); + + let due = env.ledger().timestamp() + 10_000; + let bill_id = client.create_bill( + &owner, + &String::from_str(&env, "ArchTest"), + &100i128, + &due, + &false, + &0u32, + &None, + &String::from_str(&env, "XLM"), + ); + client.pay_bill(&owner, &bill_id); + client.archive_paid_bills(&owner, &(env.ledger().timestamp() + 1)); + + match client.try_get_archived_bill(&attacker, &bill_id) { + Err(Ok(Error::Unauthorized)) => {} + _ => panic!("expected contract error Unauthorized for non-owner get_archived_bill"), } +} - // --- Recurring bill creates a new unpaid bill: total includes the new one --- - - #[test] - fn test_get_total_unpaid_includes_new_recurring_bill_after_pay() { - // Paying a recurring bill marks the original paid AND creates a new - // unpaid bill. The total must reflect the new unpaid bill's amount. - let env = Env::default(); - let contract_id = env.register_contract(None, BillPayments); - let client = BillPaymentsClient::new(&env, &contract_id); - let owner = ::generate(&env); - - env.mock_all_auths(); - - let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Monthly Subscription"), - &500, - &1_000_000, - &true, // recurring - &30, - ); - - // Before payment: one unpaid bill of 500 - assert_eq!(client.get_total_unpaid(&owner), 500); - - // Pay it: original becomes paid, a new unpaid bill of 500 is created - client.pay_bill(&owner, &bill_id); +#[test] +fn issue272_get_archived_bill_succeeds_for_owner() { + let env = env_with_auth(); + let cid = env.register_contract(None, BillPayments); + let client = BillPaymentsClient::new(&env, &cid); + let owner = Address::generate(&env); + + let due = env.ledger().timestamp() + 10_000; + let bill_id = client.create_bill( + &owner, + &String::from_str(&env, "Owned"), + &50i128, + &due, + &false, + &0u32, + &None, + &String::from_str(&env, "USDC"), + ); + client.pay_bill(&owner, &bill_id); + client.archive_paid_bills(&owner, &(env.ledger().timestamp() + 1)); + + let ab = client.get_archived_bill(&owner, &bill_id); + assert_eq!(ab.owner, owner); + assert_eq!(ab.id, bill_id); +} - // Total must still be 500 (the new recurring bill, not the paid one) - let total = client.get_total_unpaid(&owner); - assert_eq!( - total, 500, - "after paying a recurring bill, the newly created bill must appear in total_unpaid" - ); - } +#[test] +fn issue272_restore_bill_rejects_non_owner() { + let env = env_with_auth(); + let cid = env.register_contract(None, BillPayments); + let client = BillPaymentsClient::new(&env, &cid); + let owner = Address::generate(&env); + let attacker = Address::generate(&env); + + let due = env.ledger().timestamp() + 10_000; + let bill_id = client.create_bill( + &owner, + &String::from_str(&env, "R1"), + &100i128, + &due, + &false, + &0u32, + &None, + &String::from_str(&env, "XLM"), + ); + client.pay_bill(&owner, &bill_id); + client.archive_paid_bills(&owner, &(env.ledger().timestamp() + 1)); + + assert_eq!( + client.try_restore_bill(&attacker, &bill_id), + Err(Ok(Error::Unauthorized)) + ); } +#[test] +fn issue272_bulk_cleanup_does_not_remove_other_owner_archives() { + let env = env_with_auth(); + let cid = env.register_contract(None, BillPayments); + let client = BillPaymentsClient::new(&env, &cid); + let a = Address::generate(&env); + let b = Address::generate(&env); + + let due = env.ledger().timestamp() + 20_000; + let id_a = client.create_bill( + &a, + &String::from_str(&env, "A"), + &10i128, + &due, + &false, + &0u32, + &None, + &String::from_str(&env, "XLM"), + ); + let id_b = client.create_bill( + &b, + &String::from_str(&env, "B"), + &20i128, + &due, + &false, + &0u32, + &None, + &String::from_str(&env, "XLM"), + ); + client.pay_bill(&a, &id_a); + client.pay_bill(&b, &id_b); + + let ts = env.ledger().timestamp() + 1; + client.archive_paid_bills(&a, &ts); + client.archive_paid_bills(&b, &ts); + + let cutoff = env.ledger().timestamp() + 86_400; + assert_eq!(client.bulk_cleanup_bills(&a, &cutoff), 1); + let still = client.get_archived_bill(&b, &id_b); + assert_eq!(still.owner, b); } diff --git a/bill_payments/test_snapshots/test/test_create_bill_invalid_due_date.1.json b/bill_payments/test_snapshots/test/test_create_bill_invalid_due_date.1.json index ee3509cb..f0542989 100644 --- a/bill_payments/test_snapshots/test/test_create_bill_invalid_due_date.1.json +++ b/bill_payments/test_snapshots/test/test_create_bill_invalid_due_date.1.json @@ -114,6 +114,7 @@ { "u32": 0 }, + "void", { "string": "" } @@ -222,6 +223,7 @@ { "u32": 0 }, + "void", { "string": "" } @@ -275,6 +277,7 @@ { "u32": 0 }, + "void", { "string": "" } @@ -383,6 +386,7 @@ { "u32": 0 }, + "void", { "string": "" } diff --git a/bill_payments/test_snapshots/test/test_get_all_bills_for_owner_includes_paid.1.json b/bill_payments/test_snapshots/test/test_get_all_bills_for_owner_includes_paid.1.json index 4f239c11..5d914425 100644 --- a/bill_payments/test_snapshots/test/test_get_all_bills_for_owner_includes_paid.1.json +++ b/bill_payments/test_snapshots/test/test_get_all_bills_for_owner_includes_paid.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -74,6 +75,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -114,6 +116,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -154,6 +157,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -194,6 +198,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -332,6 +337,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -393,6 +404,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -438,6 +457,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -497,6 +522,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -542,6 +575,12 @@ "u64": 259200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -601,6 +640,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -646,6 +693,12 @@ "u64": 345600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -705,6 +758,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -750,6 +811,12 @@ "u64": 432000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -809,6 +876,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1150,6 +1225,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1269,6 +1345,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1388,6 +1465,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1507,6 +1585,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1626,6 +1705,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1904,6 +1984,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1965,6 +2051,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -2005,6 +2099,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2064,6 +2164,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -2104,6 +2212,12 @@ "u64": 259200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2163,6 +2277,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -2203,6 +2325,12 @@ "u64": 345600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2262,6 +2390,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -2302,6 +2438,12 @@ "u64": 432000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2361,6 +2503,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_get_archived_bills_pagination.1.json b/bill_payments/test_snapshots/test/test_get_archived_bills_pagination.1.json index 54911283..2bd722e2 100644 --- a/bill_payments/test_snapshots/test/test_get_archived_bills_pagination.1.json +++ b/bill_payments/test_snapshots/test/test_get_archived_bills_pagination.1.json @@ -56,6 +56,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -96,6 +97,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -136,6 +138,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -176,6 +179,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -216,6 +220,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -256,6 +261,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -420,8 +426,56 @@ } ] ], - [], - [] + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "get_archived_bills", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "u32": 0 + }, + { + "u32": 4 + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "get_archived_bills", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "u32": 4 + }, + { + "u32": 4 + } + ] + } + }, + "sub_invocations": [] + } + ] + ] ], "ledger": { "protocol_version": 21, @@ -526,6 +580,14 @@ "val": { "u64": 0 } + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -594,6 +656,14 @@ "val": { "u64": 0 } + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -662,6 +732,14 @@ "val": { "u64": 0 } + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -730,6 +808,14 @@ "val": { "u64": 0 } + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -798,6 +884,14 @@ "val": { "u64": 0 } + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -866,6 +960,14 @@ "val": { "u64": 0 } + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1180,6 +1282,39 @@ 6311999 ] ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 2781962168096793370 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 2781962168096793370 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], [ { "contract_data": { @@ -1411,6 +1546,39 @@ 6311999 ] ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 7270604957039011794 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 7270604957039011794 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], [ { "contract_data": { @@ -1563,6 +1731,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1682,6 +1851,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1801,6 +1971,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1920,6 +2091,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -2039,6 +2211,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -2158,6 +2331,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -3031,6 +3205,14 @@ "val": { "u64": 0 } + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -3094,6 +3276,14 @@ "val": { "u64": 0 } + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -3157,6 +3347,14 @@ "val": { "u64": 0 } + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -3220,6 +3418,14 @@ "val": { "u64": 0 } + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -3368,6 +3574,14 @@ "val": { "u64": 0 } + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -3431,6 +3645,14 @@ "val": { "u64": 0 } + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_get_overdue_bills_not_overdue.1.json b/bill_payments/test_snapshots/test/test_get_overdue_bills_not_overdue.1.json index 109c3be0..67948800 100644 --- a/bill_payments/test_snapshots/test/test_get_overdue_bills_not_overdue.1.json +++ b/bill_payments/test_snapshots/test/test_get_overdue_bills_not_overdue.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -74,6 +75,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -114,6 +116,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -206,6 +209,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -265,6 +274,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -310,6 +327,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -369,6 +392,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -414,6 +445,12 @@ "u64": 259200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -473,6 +510,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -682,6 +727,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -801,6 +847,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -920,6 +967,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } diff --git a/bill_payments/test_snapshots/test/test_get_overdue_bills_pagination.1.json b/bill_payments/test_snapshots/test/test_get_overdue_bills_pagination.1.json index 3eefbf4e..2cabb67f 100644 --- a/bill_payments/test_snapshots/test/test_get_overdue_bills_pagination.1.json +++ b/bill_payments/test_snapshots/test/test_get_overdue_bills_pagination.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -74,6 +75,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -114,6 +116,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -154,6 +157,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -194,6 +198,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -234,6 +239,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -327,6 +333,12 @@ "u64": 20000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -386,6 +398,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -431,6 +451,12 @@ "u64": 20000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -490,6 +516,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -535,6 +569,12 @@ "u64": 20000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -594,6 +634,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -639,6 +687,12 @@ "u64": 20000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -698,6 +752,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -743,6 +805,12 @@ "u64": 20000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -802,6 +870,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -847,6 +923,12 @@ "u64": 20000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -906,6 +988,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1214,6 +1304,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1333,6 +1424,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1452,6 +1544,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1571,6 +1664,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1690,6 +1784,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1809,6 +1904,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1988,6 +2084,12 @@ "u64": 20000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2047,6 +2149,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -2087,6 +2197,12 @@ "u64": 20000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2146,6 +2262,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -2186,6 +2310,12 @@ "u64": 20000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2245,6 +2375,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -2285,6 +2423,12 @@ "u64": 20000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2344,6 +2488,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -2466,6 +2618,12 @@ "u64": 20000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2525,6 +2683,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -2565,6 +2731,12 @@ "u64": 20000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2624,6 +2796,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_get_unpaid_bills_excludes_other_owner.1.json b/bill_payments/test_snapshots/test/test_get_unpaid_bills_excludes_other_owner.1.json index a3a545f5..94e4b765 100644 --- a/bill_payments/test_snapshots/test/test_get_unpaid_bills_excludes_other_owner.1.json +++ b/bill_payments/test_snapshots/test/test_get_unpaid_bills_excludes_other_owner.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -74,6 +75,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -114,6 +116,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -154,6 +157,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -194,6 +198,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -286,6 +291,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -345,6 +356,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -390,6 +409,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -449,6 +474,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -494,6 +527,12 @@ "u64": 259200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -553,6 +592,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -598,6 +645,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -657,6 +710,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -702,6 +763,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -761,6 +828,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1047,6 +1122,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1166,6 +1242,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1285,6 +1362,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1404,6 +1482,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1523,6 +1602,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1705,6 +1785,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1764,6 +1850,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -1804,6 +1898,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1863,6 +1963,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -1903,6 +2011,12 @@ "u64": 259200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1962,6 +2076,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_get_unpaid_bills_excludes_paid.1.json b/bill_payments/test_snapshots/test/test_get_unpaid_bills_excludes_paid.1.json index 7418c04f..3beb3dc0 100644 --- a/bill_payments/test_snapshots/test/test_get_unpaid_bills_excludes_paid.1.json +++ b/bill_payments/test_snapshots/test/test_get_unpaid_bills_excludes_paid.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -74,6 +75,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -114,6 +116,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -154,6 +157,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -268,6 +272,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -327,6 +337,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -372,6 +390,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -433,6 +457,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -478,6 +510,12 @@ "u64": 259200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -537,6 +575,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -582,6 +628,12 @@ "u64": 345600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -641,6 +693,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -916,6 +976,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1035,6 +1096,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1154,6 +1216,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1273,6 +1336,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1551,6 +1615,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1610,6 +1680,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -1650,6 +1728,12 @@ "u64": 259200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1709,6 +1793,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -1749,6 +1841,12 @@ "u64": 345600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1808,6 +1906,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_get_unpaid_bills_multiple_pages.1.json b/bill_payments/test_snapshots/test/test_get_unpaid_bills_multiple_pages.1.json index f29e9033..b42ce7f1 100644 --- a/bill_payments/test_snapshots/test/test_get_unpaid_bills_multiple_pages.1.json +++ b/bill_payments/test_snapshots/test/test_get_unpaid_bills_multiple_pages.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -74,6 +75,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -114,6 +116,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -154,6 +157,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -194,6 +198,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -234,6 +239,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -274,6 +280,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -368,6 +375,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -427,6 +440,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -472,6 +493,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -531,6 +558,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -576,6 +611,12 @@ "u64": 259200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -635,6 +676,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -680,6 +729,12 @@ "u64": 345600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -739,6 +794,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -784,6 +847,12 @@ "u64": 432000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -843,6 +912,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -888,6 +965,12 @@ "u64": 518400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -947,6 +1030,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -992,6 +1083,12 @@ "u64": 604800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1051,6 +1148,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1392,6 +1497,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1511,6 +1617,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1630,6 +1737,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1749,6 +1857,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1868,6 +1977,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1987,6 +2097,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -2106,6 +2217,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -2288,6 +2400,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2347,6 +2465,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -2387,6 +2513,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2446,6 +2578,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -2486,6 +2626,12 @@ "u64": 259200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2545,6 +2691,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -2670,6 +2824,12 @@ "u64": 345600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2729,6 +2889,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -2769,6 +2937,12 @@ "u64": 432000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2828,6 +3002,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -2868,6 +3050,12 @@ "u64": 518400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2927,6 +3115,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -3052,6 +3248,12 @@ "u64": 604800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -3111,6 +3313,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_get_unpaid_bills_owner_isolation_after_one_pays.1.json b/bill_payments/test_snapshots/test/test_get_unpaid_bills_owner_isolation_after_one_pays.1.json index cb7ff64d..5fe67055 100644 --- a/bill_payments/test_snapshots/test/test_get_unpaid_bills_owner_isolation_after_one_pays.1.json +++ b/bill_payments/test_snapshots/test/test_get_unpaid_bills_owner_isolation_after_one_pays.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -74,6 +75,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -114,6 +116,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -154,6 +157,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -269,6 +273,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -330,6 +340,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -375,6 +393,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -434,6 +458,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -479,6 +511,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -538,6 +576,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -583,6 +629,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -642,6 +694,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -928,6 +988,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1047,6 +1108,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1166,6 +1228,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1285,6 +1348,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1563,6 +1627,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1622,6 +1692,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1747,6 +1825,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1806,6 +1890,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -1846,6 +1938,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1905,6 +2003,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_get_unpaid_bills_owner_isolation_all_paid_other_owner_unpaid.1.json b/bill_payments/test_snapshots/test/test_get_unpaid_bills_owner_isolation_all_paid_other_owner_unpaid.1.json index 9823c857..ab0e37a5 100644 --- a/bill_payments/test_snapshots/test/test_get_unpaid_bills_owner_isolation_all_paid_other_owner_unpaid.1.json +++ b/bill_payments/test_snapshots/test/test_get_unpaid_bills_owner_isolation_all_paid_other_owner_unpaid.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -74,6 +75,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -114,6 +116,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -154,6 +157,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -194,6 +198,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -353,6 +358,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -414,6 +425,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -459,6 +478,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -520,6 +545,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -565,6 +598,12 @@ "u64": 259200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -626,6 +665,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -671,6 +718,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -730,6 +783,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -775,6 +836,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -834,6 +901,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1219,6 +1294,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1338,6 +1414,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1457,6 +1534,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1576,6 +1654,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1695,6 +1774,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -2249,6 +2329,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2308,6 +2394,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -2348,6 +2442,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2407,6 +2507,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_get_unpaid_bills_owner_isolation_bidirectional.1.json b/bill_payments/test_snapshots/test/test_get_unpaid_bills_owner_isolation_bidirectional.1.json index ac5bf5a6..16a54747 100644 --- a/bill_payments/test_snapshots/test/test_get_unpaid_bills_owner_isolation_bidirectional.1.json +++ b/bill_payments/test_snapshots/test/test_get_unpaid_bills_owner_isolation_bidirectional.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -74,6 +75,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -114,6 +116,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -154,6 +157,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -194,6 +198,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -287,6 +292,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -346,6 +357,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -391,6 +410,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -450,6 +475,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -495,6 +528,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -554,6 +593,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -599,6 +646,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -658,6 +711,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -703,6 +764,12 @@ "u64": 259200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -762,6 +829,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1048,6 +1123,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1167,6 +1243,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1286,6 +1363,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1405,6 +1483,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1524,6 +1603,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1706,6 +1786,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1765,6 +1851,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -1805,6 +1899,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1864,6 +1964,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1989,6 +2097,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2048,6 +2162,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -2088,6 +2210,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2147,6 +2275,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -2187,6 +2323,12 @@ "u64": 259200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2246,6 +2388,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_get_unpaid_bills_owner_isolation_one_owner_no_bills.1.json b/bill_payments/test_snapshots/test/test_get_unpaid_bills_owner_isolation_one_owner_no_bills.1.json index 30048ef4..13ad84c3 100644 --- a/bill_payments/test_snapshots/test/test_get_unpaid_bills_owner_isolation_one_owner_no_bills.1.json +++ b/bill_payments/test_snapshots/test/test_get_unpaid_bills_owner_isolation_one_owner_no_bills.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -74,6 +75,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -114,6 +116,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -207,6 +210,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -266,6 +275,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -311,6 +328,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -370,6 +393,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -415,6 +446,12 @@ "u64": 259200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -474,6 +511,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -683,6 +728,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -802,6 +848,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -921,6 +968,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1187,6 +1235,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1246,6 +1300,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -1286,6 +1348,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1345,6 +1413,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -1385,6 +1461,12 @@ "u64": 259200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1444,6 +1526,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_get_unpaid_bills_owner_isolation_pagination_does_not_leak.1.json b/bill_payments/test_snapshots/test/test_get_unpaid_bills_owner_isolation_pagination_does_not_leak.1.json index bb567965..414d1014 100644 --- a/bill_payments/test_snapshots/test/test_get_unpaid_bills_owner_isolation_pagination_does_not_leak.1.json +++ b/bill_payments/test_snapshots/test/test_get_unpaid_bills_owner_isolation_pagination_does_not_leak.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -74,6 +75,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -114,6 +116,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -154,6 +157,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -194,6 +198,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -234,6 +239,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -274,6 +280,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -314,6 +321,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -407,6 +415,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -466,6 +480,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -511,6 +533,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -570,6 +598,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -615,6 +651,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -674,6 +716,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -719,6 +769,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -778,6 +834,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -823,6 +887,12 @@ "u64": 259200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -882,6 +952,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -927,6 +1005,12 @@ "u64": 259200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -986,6 +1070,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1031,6 +1123,12 @@ "u64": 345600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1090,6 +1188,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1135,6 +1241,12 @@ "u64": 345600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1194,6 +1306,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1579,6 +1699,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1698,6 +1819,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1817,6 +1939,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1936,6 +2059,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -2055,6 +2179,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -2174,6 +2299,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -2293,6 +2419,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -2412,6 +2539,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -2594,6 +2722,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2653,6 +2787,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -2693,6 +2835,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2752,6 +2900,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -2877,6 +3033,12 @@ "u64": 259200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2936,6 +3098,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -2976,6 +3146,12 @@ "u64": 345600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -3035,6 +3211,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_get_unpaid_bills_single_page.1.json b/bill_payments/test_snapshots/test/test_get_unpaid_bills_single_page.1.json index b2ea8e33..ae54b84d 100644 --- a/bill_payments/test_snapshots/test/test_get_unpaid_bills_single_page.1.json +++ b/bill_payments/test_snapshots/test/test_get_unpaid_bills_single_page.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -74,6 +75,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -114,6 +116,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -154,6 +157,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -194,6 +198,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -286,6 +291,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -345,6 +356,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -390,6 +409,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -449,6 +474,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -494,6 +527,12 @@ "u64": 259200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -553,6 +592,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -598,6 +645,12 @@ "u64": 345600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -657,6 +710,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -702,6 +763,12 @@ "u64": 432000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -761,6 +828,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1036,6 +1111,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1155,6 +1231,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1274,6 +1351,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1393,6 +1471,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1512,6 +1591,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1694,6 +1774,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1753,6 +1839,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -1793,6 +1887,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1852,6 +1952,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -1892,6 +2000,12 @@ "u64": 259200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1951,6 +2065,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -1991,6 +2113,12 @@ "u64": 345600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2050,6 +2178,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -2090,6 +2226,12 @@ "u64": 432000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2149,6 +2291,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_limit_clamped_to_max.1.json b/bill_payments/test_snapshots/test/test_limit_clamped_to_max.1.json index aa4344c7..ddf81e2d 100644 --- a/bill_payments/test_snapshots/test/test_limit_clamped_to_max.1.json +++ b/bill_payments/test_snapshots/test/test_limit_clamped_to_max.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -74,6 +75,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -114,6 +116,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -154,6 +157,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -194,6 +198,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -234,6 +239,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -274,6 +280,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -314,6 +321,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -354,6 +362,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -394,6 +403,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -434,6 +444,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -474,6 +485,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -514,6 +526,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -554,6 +567,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -594,6 +608,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -634,6 +649,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -674,6 +690,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -714,6 +731,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -754,6 +772,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -794,6 +813,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -834,6 +854,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -874,6 +895,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -914,6 +936,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -954,6 +977,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -994,6 +1018,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1034,6 +1059,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1074,6 +1100,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1114,6 +1141,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1154,6 +1182,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1194,6 +1223,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1234,6 +1264,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1274,6 +1305,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1314,6 +1346,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1354,6 +1387,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1394,6 +1428,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1434,6 +1469,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1474,6 +1510,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1514,6 +1551,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1554,6 +1592,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1594,6 +1633,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1634,6 +1674,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1674,6 +1715,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1714,6 +1756,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1754,6 +1797,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1794,6 +1838,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1834,6 +1879,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1874,6 +1920,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1914,6 +1961,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1954,6 +2002,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1994,6 +2043,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -2034,6 +2084,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -2074,6 +2125,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -2114,6 +2166,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -2154,6 +2207,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -2194,6 +2248,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -2286,6 +2341,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2345,6 +2406,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -2390,6 +2459,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2449,6 +2524,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -2494,6 +2577,12 @@ "u64": 259200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2553,6 +2642,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -2598,6 +2695,12 @@ "u64": 345600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2657,6 +2760,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -2702,6 +2813,12 @@ "u64": 432000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2761,6 +2878,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -2806,6 +2931,12 @@ "u64": 518400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2865,6 +2996,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -2910,6 +3049,12 @@ "u64": 604800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2969,6 +3114,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -3014,6 +3167,12 @@ "u64": 691200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -3073,6 +3232,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -3118,6 +3285,12 @@ "u64": 777600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -3177,6 +3350,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -3222,6 +3403,12 @@ "u64": 864000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -3281,6 +3468,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -3326,6 +3521,12 @@ "u64": 950400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -3385,6 +3586,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -3430,6 +3639,12 @@ "u64": 1036800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -3489,6 +3704,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -3534,6 +3757,12 @@ "u64": 1123200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -3593,6 +3822,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -3638,6 +3875,12 @@ "u64": 1209600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -3697,6 +3940,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -3742,6 +3993,12 @@ "u64": 1296000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -3801,6 +4058,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -3846,6 +4111,12 @@ "u64": 1382400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -3905,6 +4176,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -3950,6 +4229,12 @@ "u64": 1468800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -4009,6 +4294,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -4054,6 +4347,12 @@ "u64": 1555200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -4113,6 +4412,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -4158,6 +4465,12 @@ "u64": 1641600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -4217,6 +4530,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -4262,6 +4583,12 @@ "u64": 1728000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -4321,6 +4648,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -4366,6 +4701,12 @@ "u64": 1814400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -4425,6 +4766,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -4470,6 +4819,12 @@ "u64": 1900800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -4529,6 +4884,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -4574,6 +4937,12 @@ "u64": 1987200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -4633,6 +5002,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -4680,10 +5057,16 @@ }, { "key": { - "symbol": "frequency_days" + "symbol": "external_ref" }, - "val": { - "u32": 0 + "val": "void" + }, + { + "key": { + "symbol": "frequency_days" + }, + "val": { + "u32": 0 } }, { @@ -4737,6 +5120,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -4782,6 +5173,12 @@ "u64": 2160000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -4841,6 +5238,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -4886,6 +5291,12 @@ "u64": 2246400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -4945,6 +5356,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -4990,6 +5409,12 @@ "u64": 2332800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -5049,6 +5474,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -5094,6 +5527,12 @@ "u64": 2419200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -5153,6 +5592,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -5198,6 +5645,12 @@ "u64": 2505600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -5257,6 +5710,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -5302,6 +5763,12 @@ "u64": 2592000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -5361,6 +5828,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -5406,6 +5881,12 @@ "u64": 2678400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -5465,6 +5946,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -5510,6 +5999,12 @@ "u64": 2764800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -5569,6 +6064,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -5614,6 +6117,12 @@ "u64": 2851200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -5673,6 +6182,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -5718,6 +6235,12 @@ "u64": 2937600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -5777,6 +6300,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -5822,6 +6353,12 @@ "u64": 3024000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -5881,6 +6418,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -5926,6 +6471,12 @@ "u64": 3110400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -5985,6 +6536,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -6030,6 +6589,12 @@ "u64": 3196800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -6089,6 +6654,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -6134,6 +6707,12 @@ "u64": 3283200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -6193,6 +6772,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -6238,6 +6825,12 @@ "u64": 3369600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -6297,6 +6890,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -6342,6 +6943,12 @@ "u64": 3456000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -6401,6 +7008,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -6446,6 +7061,12 @@ "u64": 3542400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -6505,6 +7126,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -6550,6 +7179,12 @@ "u64": 3628800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -6609,6 +7244,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -6654,6 +7297,12 @@ "u64": 3715200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -6713,6 +7362,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -6758,6 +7415,12 @@ "u64": 3801600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -6817,6 +7480,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -6862,6 +7533,12 @@ "u64": 3888000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -6921,6 +7598,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -6966,6 +7651,12 @@ "u64": 3974400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -7025,6 +7716,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -7070,6 +7769,12 @@ "u64": 4060800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -7129,6 +7834,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -7174,6 +7887,12 @@ "u64": 4147200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -7233,6 +7952,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -7278,6 +8005,12 @@ "u64": 4233600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -7337,6 +8070,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -7382,6 +8123,12 @@ "u64": 4320000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -7441,6 +8188,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -7486,6 +8241,12 @@ "u64": 4406400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -7545,6 +8306,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -7590,6 +8359,12 @@ "u64": 4492800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -7649,6 +8424,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -7694,6 +8477,12 @@ "u64": 4579200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -7753,6 +8542,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -7798,6 +8595,12 @@ "u64": 4665600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -7857,6 +8660,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -7902,6 +8713,12 @@ "u64": 4752000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -7961,6 +8778,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -9886,6 +10711,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -10005,6 +10831,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -10124,6 +10951,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -10243,6 +11071,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -10362,6 +11191,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -10481,6 +11311,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -10600,6 +11431,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -10719,6 +11551,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -10838,6 +11671,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -10957,6 +11791,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -11076,6 +11911,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -11195,6 +12031,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -11314,6 +12151,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -11433,6 +12271,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -11552,6 +12391,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -11671,6 +12511,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -11790,6 +12631,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -11909,6 +12751,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -12028,6 +12871,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -12147,6 +12991,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -12266,6 +13111,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -12385,6 +13231,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -12504,6 +13351,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -12623,6 +13471,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -12742,6 +13591,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -12861,6 +13711,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -12980,6 +13831,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -13099,6 +13951,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -13218,6 +14071,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -13337,6 +14191,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -13456,6 +14311,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -13575,6 +14431,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -13694,6 +14551,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -13813,6 +14671,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -13932,6 +14791,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -14051,6 +14911,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -14170,6 +15031,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -14289,6 +15151,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -14408,6 +15271,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -14527,6 +15391,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -14646,6 +15511,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -14765,6 +15631,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -14884,6 +15751,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -15003,6 +15871,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -15122,6 +15991,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -15241,6 +16111,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -15360,6 +16231,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -15479,6 +16351,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -15598,6 +16471,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -15717,6 +16591,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -15836,6 +16711,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -15955,6 +16831,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -16074,6 +16951,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -16193,6 +17071,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -16312,6 +17191,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -16494,6 +17374,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -16553,6 +17439,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -16593,6 +17487,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -16652,6 +17552,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -16692,6 +17600,12 @@ "u64": 259200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -16751,6 +17665,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -16791,6 +17713,12 @@ "u64": 345600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -16850,6 +17778,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -16890,6 +17826,12 @@ "u64": 432000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -16949,6 +17891,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -16989,6 +17939,12 @@ "u64": 518400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -17048,6 +18004,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -17088,6 +18052,12 @@ "u64": 604800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -17147,6 +18117,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -17187,6 +18165,12 @@ "u64": 691200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -17246,6 +18230,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -17286,6 +18278,12 @@ "u64": 777600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -17345,6 +18343,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -17385,6 +18391,12 @@ "u64": 864000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -17444,6 +18456,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -17484,6 +18504,12 @@ "u64": 950400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -17543,6 +18569,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -17583,6 +18617,12 @@ "u64": 1036800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -17642,6 +18682,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -17682,6 +18730,12 @@ "u64": 1123200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -17741,6 +18795,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -17781,6 +18843,12 @@ "u64": 1209600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -17840,6 +18908,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -17882,10 +18958,16 @@ }, { "key": { - "symbol": "frequency_days" + "symbol": "external_ref" }, - "val": { - "u32": 0 + "val": "void" + }, + { + "key": { + "symbol": "frequency_days" + }, + "val": { + "u32": 0 } }, { @@ -17939,6 +19021,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -17979,6 +19069,12 @@ "u64": 1382400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -18038,6 +19134,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -18078,6 +19182,12 @@ "u64": 1468800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -18137,6 +19247,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -18177,6 +19295,12 @@ "u64": 1555200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -18236,6 +19360,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -18276,6 +19408,12 @@ "u64": 1641600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -18335,6 +19473,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -18375,6 +19521,12 @@ "u64": 1728000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -18434,6 +19586,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -18474,6 +19634,12 @@ "u64": 1814400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -18533,6 +19699,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -18573,6 +19747,12 @@ "u64": 1900800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -18632,6 +19812,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -18672,6 +19860,12 @@ "u64": 1987200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -18731,6 +19925,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -18771,6 +19973,12 @@ "u64": 2073600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -18830,6 +20038,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -18870,6 +20086,12 @@ "u64": 2160000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -18929,6 +20151,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -18969,6 +20199,12 @@ "u64": 2246400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -19028,6 +20264,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -19068,6 +20312,12 @@ "u64": 2332800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -19127,6 +20377,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -19167,6 +20425,12 @@ "u64": 2419200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -19226,6 +20490,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -19266,6 +20538,12 @@ "u64": 2505600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -19325,6 +20603,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -19365,6 +20651,12 @@ "u64": 2592000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -19424,6 +20716,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -19464,6 +20764,12 @@ "u64": 2678400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -19523,6 +20829,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -19563,6 +20877,12 @@ "u64": 2764800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -19622,6 +20942,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -19662,6 +20990,12 @@ "u64": 2851200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -19721,6 +21055,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -19761,6 +21103,12 @@ "u64": 2937600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -19820,6 +21168,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -19860,6 +21216,12 @@ "u64": 3024000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -19919,6 +21281,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -19959,6 +21329,12 @@ "u64": 3110400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -20018,6 +21394,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -20058,6 +21442,12 @@ "u64": 3196800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -20117,6 +21507,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -20157,6 +21555,12 @@ "u64": 3283200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -20216,6 +21620,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -20256,6 +21668,12 @@ "u64": 3369600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -20315,6 +21733,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -20355,6 +21781,12 @@ "u64": 3456000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -20414,6 +21846,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -20454,6 +21894,12 @@ "u64": 3542400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -20513,6 +21959,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -20553,6 +22007,12 @@ "u64": 3628800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -20612,6 +22072,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -20652,6 +22120,12 @@ "u64": 3715200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -20711,6 +22185,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -20751,6 +22233,12 @@ "u64": 3801600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -20810,6 +22298,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -20850,6 +22346,12 @@ "u64": 3888000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -20909,6 +22411,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -20949,6 +22459,12 @@ "u64": 3974400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -21008,6 +22524,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -21048,6 +22572,12 @@ "u64": 4060800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -21107,6 +22637,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -21147,6 +22685,12 @@ "u64": 4147200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -21206,6 +22750,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -21246,6 +22798,12 @@ "u64": 4233600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -21305,6 +22863,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -21345,6 +22911,12 @@ "u64": 4320000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -21404,6 +22976,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_limit_zero_uses_default.1.json b/bill_payments/test_snapshots/test/test_limit_zero_uses_default.1.json index 077c5be2..384265ba 100644 --- a/bill_payments/test_snapshots/test/test_limit_zero_uses_default.1.json +++ b/bill_payments/test_snapshots/test/test_limit_zero_uses_default.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -74,6 +75,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -114,6 +116,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -206,6 +209,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -265,6 +274,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -310,6 +327,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -369,6 +392,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -414,6 +445,12 @@ "u64": 259200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -473,6 +510,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -682,6 +727,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -801,6 +847,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -920,6 +967,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1102,6 +1150,12 @@ "u64": 86400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1161,6 +1215,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -1201,6 +1263,12 @@ "u64": 172800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1260,6 +1328,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -1300,6 +1376,12 @@ "u64": 259200 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1359,6 +1441,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_recurring_date_math_amount_preserved_across_cycles.1.json b/bill_payments/test_snapshots/test/test_recurring_date_math_amount_preserved_across_cycles.1.json index f2db08da..54de3729 100644 --- a/bill_payments/test_snapshots/test/test_recurring_date_math_amount_preserved_across_cycles.1.json +++ b/bill_payments/test_snapshots/test/test_recurring_date_math_amount_preserved_across_cycles.1.json @@ -34,6 +34,7 @@ { "u32": 30 }, + "void", { "string": "XLM" } @@ -172,6 +173,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -233,6 +240,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -278,6 +293,12 @@ "u64": 3592000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -339,6 +360,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -384,6 +413,12 @@ "u64": 6184000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -443,6 +478,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -652,6 +695,7 @@ { "u32": 30 }, + "void", { "string": "XLM" } @@ -1000,6 +1044,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1061,6 +1111,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1147,6 +1205,12 @@ "u64": 3592000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1208,6 +1272,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1294,6 +1366,12 @@ "u64": 6184000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1353,6 +1431,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_recurring_date_math_early_payment_does_not_affect_schedule.1.json b/bill_payments/test_snapshots/test/test_recurring_date_math_early_payment_does_not_affect_schedule.1.json index 7e87b896..0d3fefee 100644 --- a/bill_payments/test_snapshots/test/test_recurring_date_math_early_payment_does_not_affect_schedule.1.json +++ b/bill_payments/test_snapshots/test/test_recurring_date_math_early_payment_does_not_affect_schedule.1.json @@ -34,6 +34,7 @@ { "u32": 30 }, + "void", { "string": "XLM" } @@ -149,6 +150,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -210,6 +217,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -255,6 +270,12 @@ "u64": 3592000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -314,6 +335,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -490,6 +519,7 @@ { "u32": 30 }, + "void", { "string": "XLM" } @@ -742,6 +772,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -803,6 +839,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -889,6 +933,12 @@ "u64": 3592000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -948,6 +998,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_recurring_date_math_exact_calculation_verification.1.json b/bill_payments/test_snapshots/test/test_recurring_date_math_exact_calculation_verification.1.json index 7a47e415..76828efb 100644 --- a/bill_payments/test_snapshots/test/test_recurring_date_math_exact_calculation_verification.1.json +++ b/bill_payments/test_snapshots/test/test_recurring_date_math_exact_calculation_verification.1.json @@ -34,6 +34,7 @@ { "u32": 14 }, + "void", { "string": "XLM" } @@ -148,6 +149,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -209,6 +216,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -254,6 +269,12 @@ "u64": 2209600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -313,6 +334,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -489,6 +518,7 @@ { "u32": 14 }, + "void", { "string": "XLM" } @@ -741,6 +771,12 @@ "u64": 2209600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -800,6 +836,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_recurring_date_math_frequency_1_day.1.json b/bill_payments/test_snapshots/test/test_recurring_date_math_frequency_1_day.1.json index 65098fc4..5fb0ba9a 100644 --- a/bill_payments/test_snapshots/test/test_recurring_date_math_frequency_1_day.1.json +++ b/bill_payments/test_snapshots/test/test_recurring_date_math_frequency_1_day.1.json @@ -34,6 +34,7 @@ { "u32": 1 }, + "void", { "string": "XLM" } @@ -148,6 +149,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -209,6 +216,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -254,6 +269,12 @@ "u64": 1086400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -313,6 +334,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -489,6 +518,7 @@ { "u32": 1 }, + "void", { "string": "XLM" } @@ -741,6 +771,12 @@ "u64": 1086400 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -800,6 +836,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_recurring_date_math_frequency_30_days.1.json b/bill_payments/test_snapshots/test/test_recurring_date_math_frequency_30_days.1.json index c552250b..f286fd2e 100644 --- a/bill_payments/test_snapshots/test/test_recurring_date_math_frequency_30_days.1.json +++ b/bill_payments/test_snapshots/test/test_recurring_date_math_frequency_30_days.1.json @@ -34,6 +34,7 @@ { "u32": 30 }, + "void", { "string": "XLM" } @@ -148,6 +149,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -209,6 +216,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -254,6 +269,12 @@ "u64": 3592000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -313,6 +334,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -489,6 +518,7 @@ { "u32": 30 }, + "void", { "string": "XLM" } @@ -741,6 +771,12 @@ "u64": 3592000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -800,6 +836,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_recurring_date_math_frequency_365_days.1.json b/bill_payments/test_snapshots/test/test_recurring_date_math_frequency_365_days.1.json index c0b92fca..509c676f 100644 --- a/bill_payments/test_snapshots/test/test_recurring_date_math_frequency_365_days.1.json +++ b/bill_payments/test_snapshots/test/test_recurring_date_math_frequency_365_days.1.json @@ -34,6 +34,7 @@ { "u32": 365 }, + "void", { "string": "XLM" } @@ -148,6 +149,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -209,6 +216,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -254,6 +269,12 @@ "u64": 32536000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -313,6 +334,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -489,6 +518,7 @@ { "u32": 365 }, + "void", { "string": "XLM" } @@ -741,6 +771,12 @@ "u64": 32536000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -800,6 +836,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_recurring_date_math_multiple_pay_cycles_2nd_bill.1.json b/bill_payments/test_snapshots/test/test_recurring_date_math_multiple_pay_cycles_2nd_bill.1.json index f29a6a71..a90389f8 100644 --- a/bill_payments/test_snapshots/test/test_recurring_date_math_multiple_pay_cycles_2nd_bill.1.json +++ b/bill_payments/test_snapshots/test/test_recurring_date_math_multiple_pay_cycles_2nd_bill.1.json @@ -34,6 +34,7 @@ { "u32": 30 }, + "void", { "string": "XLM" } @@ -172,6 +173,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -233,6 +240,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -278,6 +293,12 @@ "u64": 3592000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -339,6 +360,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -384,6 +413,12 @@ "u64": 6184000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -443,6 +478,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -652,6 +695,7 @@ { "u32": 30 }, + "void", { "string": "XLM" } @@ -904,6 +948,12 @@ "u64": 3592000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -963,6 +1013,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1145,6 +1203,12 @@ "u64": 3592000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1206,6 +1270,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1292,6 +1364,12 @@ "u64": 6184000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1351,6 +1429,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_recurring_date_math_multiple_pay_cycles_3rd_bill.1.json b/bill_payments/test_snapshots/test/test_recurring_date_math_multiple_pay_cycles_3rd_bill.1.json index 0476b724..740e3b19 100644 --- a/bill_payments/test_snapshots/test/test_recurring_date_math_multiple_pay_cycles_3rd_bill.1.json +++ b/bill_payments/test_snapshots/test/test_recurring_date_math_multiple_pay_cycles_3rd_bill.1.json @@ -34,6 +34,7 @@ { "u32": 30 }, + "void", { "string": "XLM" } @@ -193,6 +194,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -254,6 +261,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -299,6 +314,12 @@ "u64": 3592000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -360,6 +381,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -405,6 +434,12 @@ "u64": 6184000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -466,6 +501,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -511,6 +554,12 @@ "u64": 8776000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -570,6 +619,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -812,6 +869,7 @@ { "u32": 30 }, + "void", { "string": "XLM" } @@ -1256,6 +1314,12 @@ "u64": 6184000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1317,6 +1381,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1403,6 +1475,12 @@ "u64": 8776000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1462,6 +1540,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_recurring_date_math_owner_preserved_across_cycles.1.json b/bill_payments/test_snapshots/test/test_recurring_date_math_owner_preserved_across_cycles.1.json index 7b37e8b2..000f8dc8 100644 --- a/bill_payments/test_snapshots/test/test_recurring_date_math_owner_preserved_across_cycles.1.json +++ b/bill_payments/test_snapshots/test/test_recurring_date_math_owner_preserved_across_cycles.1.json @@ -34,6 +34,7 @@ { "u32": 30 }, + "void", { "string": "XLM" } @@ -172,6 +173,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -233,6 +240,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -278,6 +293,12 @@ "u64": 3592000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -339,6 +360,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -384,6 +413,12 @@ "u64": 6184000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -443,6 +478,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -652,6 +695,7 @@ { "u32": 30 }, + "void", { "string": "XLM" } @@ -1000,6 +1044,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1061,6 +1111,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1147,6 +1205,12 @@ "u64": 3592000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1208,6 +1272,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1294,6 +1366,12 @@ "u64": 6184000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1353,6 +1431,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_recurring_date_math_paid_at_does_not_affect_next_due.1.json b/bill_payments/test_snapshots/test/test_recurring_date_math_paid_at_does_not_affect_next_due.1.json index a766a8dd..e314ed18 100644 --- a/bill_payments/test_snapshots/test/test_recurring_date_math_paid_at_does_not_affect_next_due.1.json +++ b/bill_payments/test_snapshots/test/test_recurring_date_math_paid_at_does_not_affect_next_due.1.json @@ -34,6 +34,7 @@ { "u32": 30 }, + "void", { "string": "XLM" } @@ -148,6 +149,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -209,6 +216,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -254,6 +269,12 @@ "u64": 3592000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -313,6 +334,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -489,6 +518,7 @@ { "u32": 30 }, + "void", { "string": "XLM" } @@ -741,6 +771,12 @@ "u64": 3592000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -800,6 +836,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_recurring_date_math_preserves_frequency_across_cycles.1.json b/bill_payments/test_snapshots/test/test_recurring_date_math_preserves_frequency_across_cycles.1.json index ffa9cfb8..ed3a6c6b 100644 --- a/bill_payments/test_snapshots/test/test_recurring_date_math_preserves_frequency_across_cycles.1.json +++ b/bill_payments/test_snapshots/test/test_recurring_date_math_preserves_frequency_across_cycles.1.json @@ -34,6 +34,7 @@ { "u32": 7 }, + "void", { "string": "XLM" } @@ -172,6 +173,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -233,6 +240,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -278,6 +293,12 @@ "u64": 1604800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -339,6 +360,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -384,6 +413,12 @@ "u64": 2209600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -443,6 +478,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -652,6 +695,7 @@ { "u32": 7 }, + "void", { "string": "XLM" } @@ -1000,6 +1044,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1061,6 +1111,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1147,6 +1205,12 @@ "u64": 1604800 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1208,6 +1272,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1294,6 +1366,12 @@ "u64": 2209600 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1353,6 +1431,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_time_drift_bill_not_overdue_at_exact_due_date.1.json b/bill_payments/test_snapshots/test/test_time_drift_bill_not_overdue_at_exact_due_date.1.json index d7e35755..7b9b6297 100644 --- a/bill_payments/test_snapshots/test/test_time_drift_bill_not_overdue_at_exact_due_date.1.json +++ b/bill_payments/test_snapshots/test/test_time_drift_bill_not_overdue_at_exact_due_date.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -126,6 +127,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -185,6 +192,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -328,6 +343,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } diff --git a/bill_payments/test_snapshots/test/test_time_drift_bill_overdue_one_second_after_due_date.1.json b/bill_payments/test_snapshots/test/test_time_drift_bill_overdue_one_second_after_due_date.1.json index 4e541eb0..d3f319d7 100644 --- a/bill_payments/test_snapshots/test/test_time_drift_bill_overdue_one_second_after_due_date.1.json +++ b/bill_payments/test_snapshots/test/test_time_drift_bill_overdue_one_second_after_due_date.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -127,6 +128,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -186,6 +193,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -329,6 +344,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -589,6 +605,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -648,6 +670,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test/test_time_drift_overdue_boundary_mixed_bills.1.json b/bill_payments/test_snapshots/test/test_time_drift_overdue_boundary_mixed_bills.1.json index 611d94c7..24a75d86 100644 --- a/bill_payments/test_snapshots/test/test_time_drift_overdue_boundary_mixed_bills.1.json +++ b/bill_payments/test_snapshots/test/test_time_drift_overdue_boundary_mixed_bills.1.json @@ -4,6 +4,88 @@ "nonce": 0 }, "auth": [ + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "create_bill", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "string": "Overdue" + }, + { + "i128": { + "hi": 0, + "lo": 100 + } + }, + { + "u64": 1500000 + }, + { + "bool": false + }, + { + "u32": 0 + }, + "void", + { + "string": "XLM" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "create_bill", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "string": "DueNow" + }, + { + "i128": { + "hi": 0, + "lo": 200 + } + }, + { + "u64": 2000000 + }, + { + "bool": false + }, + { + "u32": 0 + }, + "void", + { + "string": "XLM" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], [] ], "ledger": { @@ -38,14 +120,354 @@ "executable": { "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" }, - "storage": null + "storage": [ + { + "key": { + "symbol": "BILLS" + }, + "val": { + "map": [ + { + "key": { + "u32": 1 + }, + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 100 + } + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": 1000000 + } + }, + { + "key": { + "symbol": "currency" + }, + "val": { + "string": "XLM" + } + }, + { + "key": { + "symbol": "due_date" + }, + "val": { + "u64": 1500000 + } + }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, + { + "key": { + "symbol": "frequency_days" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "name" + }, + "val": { + "string": "Overdue" + } + }, + { + "key": { + "symbol": "owner" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "paid" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "paid_at" + }, + "val": "void" + }, + { + "key": { + "symbol": "recurring" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "schedule_id" + }, + "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } + } + ] + } + }, + { + "key": { + "u32": 2 + }, + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 200 + } + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": 1000000 + } + }, + { + "key": { + "symbol": "currency" + }, + "val": { + "string": "XLM" + } + }, + { + "key": { + "symbol": "due_date" + }, + "val": { + "u64": 2000000 + } + }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, + { + "key": { + "symbol": "frequency_days" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "u32": 2 + } + }, + { + "key": { + "symbol": "name" + }, + "val": { + "string": "DueNow" + } + }, + { + "key": { + "symbol": "owner" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "paid" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "paid_at" + }, + "val": "void" + }, + { + "key": { + "symbol": "recurring" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "schedule_id" + }, + "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } + } + ] + } + } + ] + } + }, + { + "key": { + "symbol": "NEXT_ID" + }, + "val": { + "u32": 2 + } + }, + { + "key": { + "symbol": "UNPD_TOT" + }, + "val": { + "map": [ + { + "key": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + "val": { + "i128": { + "hi": 0, + "lo": 300 + } + } + } + ] + } + } + ] } } } }, "ext": "v0" }, - 4095 + 518400 + ] + ], + [ + { + "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 ] ], [ @@ -66,7 +488,7 @@ }, "ext": "v0" }, - 4095 + 518400 ] ] ] @@ -105,7 +527,7 @@ } }, { - "u64": 1999999 + "u64": 1500000 }, { "bool": false @@ -113,6 +535,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -127,26 +550,46 @@ "event": { "ext": "v0", "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", - "type_": "diagnostic", + "type_": "contract", "body": { "v0": { "topics": [ { - "symbol": "fn_return" + "symbol": "Remitwise" }, { - "symbol": "create_bill" + "u32": 1 + }, + { + "u32": 1 + }, + { + "symbol": "created" } ], "data": { - "error": { - "contract": 12 - } + "vec": [ + { + "u32": 1 + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "i128": { + "hi": 0, + "lo": 100 + } + }, + { + "u64": 1500000 + } + ] } } } }, - "failed_call": true + "failed_call": false }, { "event": { @@ -157,21 +600,19 @@ "v0": { "topics": [ { - "symbol": "error" + "symbol": "fn_return" }, { - "error": { - "contract": 12 - } + "symbol": "create_bill" } ], "data": { - "string": "escalating Ok(ScErrorType::Contract) frame-exit to Err" + "u32": 1 } } } }, - "failed_call": true + "failed_call": false }, { "event": { @@ -182,49 +623,86 @@ "v0": { "topics": [ { - "symbol": "error" + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" }, { - "error": { - "contract": 12 + "symbol": "create_bill" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "string": "DueNow" + }, + { + "i128": { + "hi": 0, + "lo": 200 + } + }, + { + "u64": 2000000 + }, + { + "bool": false + }, + { + "u32": 0 + }, + "void", + { + "string": "XLM" } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "Remitwise" + }, + { + "u32": 1 + }, + { + "u32": 1 + }, + { + "symbol": "created" } ], "data": { "vec": [ { - "string": "contract call failed" + "u32": 2 }, { - "symbol": "create_bill" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" }, { - "vec": [ - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" - }, - { - "string": "Overdue" - }, - { - "i128": { - "hi": 0, - "lo": 100 - } - }, - { - "u64": 1999999 - }, - { - "bool": false - }, - { - "u32": 0 - }, - { - "string": "XLM" - } - ] + "i128": { + "hi": 0, + "lo": 200 + } + }, + { + "u64": 2000000 } ] } @@ -233,6 +711,29 @@ }, "failed_call": false }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "create_bill" + } + ], + "data": { + "u32": 2 + } + } + } + }, + "failed_call": false + }, { "event": { "ext": "v0", @@ -242,16 +743,186 @@ "v0": { "topics": [ { - "symbol": "error" + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" }, { - "error": { - "contract": 12 + "symbol": "get_overdue_bills" + } + ], + "data": { + "vec": [ + { + "u32": 0 + }, + { + "u32": 100 } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "get_overdue_bills" } ], "data": { - "string": "escalating error to panic" + "map": [ + { + "key": { + "symbol": "count" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "items" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 100 + } + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": 1000000 + } + }, + { + "key": { + "symbol": "currency" + }, + "val": { + "string": "XLM" + } + }, + { + "key": { + "symbol": "due_date" + }, + "val": { + "u64": 1500000 + } + }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, + { + "key": { + "symbol": "frequency_days" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "name" + }, + "val": { + "string": "Overdue" + } + }, + { + "key": { + "symbol": "owner" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "paid" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "paid_at" + }, + "val": "void" + }, + { + "key": { + "symbol": "recurring" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "schedule_id" + }, + "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "next_cursor" + }, + "val": { + "u32": 0 + } + } + ] } } } diff --git a/bill_payments/test_snapshots/test/test_time_drift_overdue_full_day_boundary.1.json b/bill_payments/test_snapshots/test/test_time_drift_overdue_full_day_boundary.1.json index 71f517d4..3ce07723 100644 --- a/bill_payments/test_snapshots/test/test_time_drift_overdue_full_day_boundary.1.json +++ b/bill_payments/test_snapshots/test/test_time_drift_overdue_full_day_boundary.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -127,6 +128,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -186,6 +193,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -329,6 +344,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -589,6 +605,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -648,6 +670,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test_archive_large_amount_bill.1.json b/bill_payments/test_snapshots/test_archive_large_amount_bill.1.json index 61a841c4..d269011b 100644 --- a/bill_payments/test_snapshots/test_archive_large_amount_bill.1.json +++ b/bill_payments/test_snapshots/test_archive_large_amount_bill.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -88,7 +89,28 @@ } ] ], - [] + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "get_archived_bill", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "u32": 1 + } + ] + } + }, + "sub_invocations": [] + } + ] + ] ], "ledger": { "protocol_version": 21, @@ -193,6 +215,14 @@ "val": { "u64": 1000000 } + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -367,6 +397,39 @@ 100000 ] ], + [ + { + "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" + }, + 100000 + ] + ], [ { "contract_data": { @@ -465,6 +528,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -750,7 +814,14 @@ } ], "data": { - "u32": 1 + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "u32": 1 + } + ] } } } @@ -832,6 +903,14 @@ "val": { "u64": 1000000 } + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test_batch_pay_large_bills.1.json b/bill_payments/test_snapshots/test_batch_pay_large_bills.1.json index e841c0b5..c869a1e1 100644 --- a/bill_payments/test_snapshots/test_batch_pay_large_bills.1.json +++ b/bill_payments/test_snapshots/test_batch_pay_large_bills.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -74,6 +75,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -114,6 +116,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -154,6 +157,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -194,6 +198,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -328,6 +333,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -389,6 +400,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -434,6 +453,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -495,6 +520,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -540,6 +573,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -601,6 +640,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -646,6 +693,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -707,6 +760,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -752,6 +813,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -813,6 +880,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1176,6 +1251,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1295,6 +1371,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1414,6 +1491,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1533,6 +1611,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -1652,6 +1731,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -2126,6 +2206,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2187,6 +2273,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -2273,6 +2367,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2334,6 +2434,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -2420,6 +2528,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2481,6 +2595,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -2567,6 +2689,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2628,6 +2756,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -2714,6 +2850,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2775,6 +2917,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test_create_bill_near_max_i128.1.json b/bill_payments/test_snapshots/test_create_bill_near_max_i128.1.json index 8ab67bfe..9b542a77 100644 --- a/bill_payments/test_snapshots/test_create_bill_near_max_i128.1.json +++ b/bill_payments/test_snapshots/test_create_bill_near_max_i128.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -126,6 +127,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -185,6 +192,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -328,6 +343,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -484,6 +500,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -543,6 +565,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test_edge_case_i128_max_minus_one.1.json b/bill_payments/test_snapshots/test_edge_case_i128_max_minus_one.1.json index b62d9b7f..8e319871 100644 --- a/bill_payments/test_snapshots/test_edge_case_i128_max_minus_one.1.json +++ b/bill_payments/test_snapshots/test_edge_case_i128_max_minus_one.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -126,6 +127,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -185,6 +192,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -328,6 +343,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -484,6 +500,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -543,6 +565,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test_get_total_unpaid_overflow_panics.1.json b/bill_payments/test_snapshots/test_get_total_unpaid_overflow_panics.1.json index 4c04edbd..36e47cdb 100644 --- a/bill_payments/test_snapshots/test_get_total_unpaid_overflow_panics.1.json +++ b/bill_payments/test_snapshots/test_get_total_unpaid_overflow_panics.1.json @@ -34,46 +34,7 @@ { "u32": 0 }, - { - "string": "XLM" - } - ] - } - }, - "sub_invocations": [] - } - ] - ], - [ - [ - "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", - { - "function": { - "contract_fn": { - "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", - "function_name": "create_bill", - "args": [ - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" - }, - { - "string": "Bill2" - }, - { - "i128": { - "hi": 4611686018427387904, - "lo": 999 - } - }, - { - "u64": 1000000 - }, - { - "bool": false - }, - { - "u32": 0 - }, + "void", { "string": "XLM" } @@ -166,6 +127,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -225,110 +192,14 @@ "symbol": "schedule_id" }, "val": "void" - } - ] - } - }, - { - "key": { - "u32": 2 - }, - "val": { - "map": [ - { - "key": { - "symbol": "amount" - }, - "val": { - "i128": { - "hi": 4611686018427387904, - "lo": 999 - } - } - }, - { - "key": { - "symbol": "created_at" - }, - "val": { - "u64": 0 - } - }, - { - "key": { - "symbol": "currency" - }, - "val": { - "string": "XLM" - } - }, - { - "key": { - "symbol": "due_date" - }, - "val": { - "u64": 1000000 - } - }, - { - "key": { - "symbol": "frequency_days" - }, - "val": { - "u32": 0 - } - }, - { - "key": { - "symbol": "id" - }, - "val": { - "u32": 2 - } - }, - { - "key": { - "symbol": "name" - }, - "val": { - "string": "Bill2" - } - }, - { - "key": { - "symbol": "owner" - }, - "val": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" - } - }, - { - "key": { - "symbol": "paid" - }, - "val": { - "bool": false - } - }, - { - "key": { - "symbol": "paid_at" - }, - "val": "void" }, { "key": { - "symbol": "recurring" + "symbol": "tags" }, "val": { - "bool": false + "vec": [] } - }, - { - "key": { - "symbol": "schedule_id" - }, - "val": "void" } ] } @@ -341,7 +212,7 @@ "symbol": "NEXT_ID" }, "val": { - "u32": 2 + "u32": 1 } }, { @@ -356,8 +227,8 @@ }, "val": { "i128": { - "hi": 9223372036854775807, - "lo": 18446744073709551615 + "hi": 4611686018427387904, + "lo": 999 } } } @@ -407,39 +278,6 @@ 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": { @@ -505,6 +343,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -624,6 +463,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -638,31 +478,25 @@ "event": { "ext": "v0", "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", - "type_": "contract", + "type_": "diagnostic", "body": { "v0": { "topics": [ { - "symbol": "Remitwise" - }, - { - "u32": 1 - }, - { - "u32": 1 - }, - { - "symbol": "created" + "symbol": "log" } ], "data": { "vec": [ { - "u32": 2 + "string": "caught panic 'overflow' from contract function 'Symbol(obj#59)'" }, { "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" }, + { + "string": "Bill2" + }, { "i128": { "hi": 4611686018427387904, @@ -671,13 +505,23 @@ }, { "u64": 1000000 + }, + { + "bool": false + }, + { + "u32": 0 + }, + "void", + { + "string": "XLM" } ] } } } }, - "failed_call": false + "failed_call": true }, { "event": { @@ -688,19 +532,21 @@ "v0": { "topics": [ { - "symbol": "fn_return" + "symbol": "error" }, { - "symbol": "create_bill" + "error": { + "wasm_vm": "invalid_action" + } } ], "data": { - "u32": 2 + "string": "caught error from function" } } } }, - "failed_call": false + "failed_call": true }, { "event": { @@ -711,17 +557,52 @@ "v0": { "topics": [ { - "symbol": "fn_call" - }, - { - "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + "symbol": "error" }, { - "symbol": "get_total_unpaid" + "error": { + "wasm_vm": "invalid_action" + } } ], "data": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + "vec": [ + { + "string": "contract call failed" + }, + { + "symbol": "create_bill" + }, + { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "string": "Bill2" + }, + { + "i128": { + "hi": 4611686018427387904, + "lo": 999 + } + }, + { + "u64": 1000000 + }, + { + "bool": false + }, + { + "u32": 0 + }, + "void", + { + "string": "XLM" + } + ] + } + ] } } } @@ -731,23 +612,22 @@ { "event": { "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "contract_id": null, "type_": "diagnostic", "body": { "v0": { "topics": [ { - "symbol": "fn_return" + "symbol": "error" }, { - "symbol": "get_total_unpaid" + "error": { + "wasm_vm": "invalid_action" + } } ], "data": { - "i128": { - "hi": 9223372036854775807, - "lo": 18446744073709551615 - } + "string": "escalating error to panic" } } } diff --git a/bill_payments/test_snapshots/test_get_total_unpaid_with_two_large_bills.1.json b/bill_payments/test_snapshots/test_get_total_unpaid_with_two_large_bills.1.json index 1fc1b135..8db1a88d 100644 --- a/bill_payments/test_snapshots/test_get_total_unpaid_with_two_large_bills.1.json +++ b/bill_payments/test_snapshots/test_get_total_unpaid_with_two_large_bills.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -74,6 +75,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -166,6 +168,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -225,6 +233,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -270,6 +286,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -329,6 +351,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -505,6 +535,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -624,6 +655,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } diff --git a/bill_payments/test_snapshots/test_multiple_large_bills_different_owners.1.json b/bill_payments/test_snapshots/test_multiple_large_bills_different_owners.1.json index b7b1963a..9d44e0ed 100644 --- a/bill_payments/test_snapshots/test_multiple_large_bills_different_owners.1.json +++ b/bill_payments/test_snapshots/test_multiple_large_bills_different_owners.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -74,6 +75,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -167,6 +169,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -226,6 +234,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -271,6 +287,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -330,6 +352,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -517,6 +547,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -636,6 +667,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } diff --git a/bill_payments/test_snapshots/test_notification_flow.1.json b/bill_payments/test_snapshots/test_notification_flow.1.json index cf921acd..30b4f5fe 100644 --- a/bill_payments/test_snapshots/test_notification_flow.1.json +++ b/bill_payments/test_snapshots/test_notification_flow.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -147,6 +148,12 @@ "u64": 1234567890 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -208,6 +215,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -384,6 +399,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } diff --git a/bill_payments/test_snapshots/test_pagination_with_large_amounts.1.json b/bill_payments/test_snapshots/test_pagination_with_large_amounts.1.json index 6c28d489..aa90b9b7 100644 --- a/bill_payments/test_snapshots/test_pagination_with_large_amounts.1.json +++ b/bill_payments/test_snapshots/test_pagination_with_large_amounts.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -74,6 +75,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -114,6 +116,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -154,6 +157,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -194,6 +198,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -234,6 +239,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -274,6 +280,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -314,6 +321,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -354,6 +362,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -394,6 +403,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -434,6 +444,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -474,6 +485,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -514,6 +526,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -554,6 +567,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -594,6 +608,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -687,6 +702,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -746,6 +767,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -791,6 +820,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -850,6 +885,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -895,6 +938,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -954,6 +1003,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -999,6 +1056,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1058,6 +1121,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1103,6 +1174,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1162,6 +1239,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1207,6 +1292,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1266,6 +1357,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1311,6 +1410,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1370,6 +1475,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1415,6 +1528,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1474,6 +1593,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1519,6 +1646,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1578,6 +1711,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1623,6 +1764,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1682,6 +1829,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1727,6 +1882,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1786,6 +1947,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1831,6 +2000,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1890,6 +2065,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -1935,6 +2118,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -1994,6 +2183,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -2039,6 +2236,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2098,6 +2301,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -2143,6 +2354,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -2202,6 +2419,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -2807,6 +3032,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -2926,6 +3152,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -3045,6 +3272,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -3164,6 +3392,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -3283,6 +3512,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -3402,6 +3632,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -3521,6 +3752,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -3640,6 +3872,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -3759,6 +3992,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -3878,6 +4112,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -3997,6 +4232,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -4116,6 +4352,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -4235,6 +4472,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -4354,6 +4592,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -4473,6 +4712,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -4655,6 +4895,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -4714,6 +4960,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -4754,6 +5008,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -4813,6 +5073,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -4853,6 +5121,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -4912,6 +5186,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -4952,6 +5234,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -5011,6 +5299,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -5051,6 +5347,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -5110,6 +5412,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -5150,6 +5460,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -5209,6 +5525,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -5249,6 +5573,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -5308,6 +5638,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -5348,6 +5686,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -5407,6 +5751,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -5447,6 +5799,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -5506,6 +5864,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -5546,6 +5912,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -5605,6 +5977,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -5730,6 +6110,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -5789,6 +6175,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -5829,6 +6223,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -5888,6 +6288,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -5928,6 +6336,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -5987,6 +6401,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -6027,6 +6449,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -6086,6 +6514,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] }, @@ -6126,6 +6562,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -6185,6 +6627,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test_pay_bill_with_large_amount.1.json b/bill_payments/test_snapshots/test_pay_bill_with_large_amount.1.json index c8b577d0..8af27cd9 100644 --- a/bill_payments/test_snapshots/test_pay_bill_with_large_amount.1.json +++ b/bill_payments/test_snapshots/test_pay_bill_with_large_amount.1.json @@ -34,6 +34,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -148,6 +149,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -209,6 +216,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -385,6 +400,7 @@ { "u32": 0 }, + "void", { "string": "XLM" } @@ -637,6 +653,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -698,6 +720,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/test_snapshots/test_recurring_bill_with_large_amount.1.json b/bill_payments/test_snapshots/test_recurring_bill_with_large_amount.1.json index 009bf39f..a1dcb409 100644 --- a/bill_payments/test_snapshots/test_recurring_bill_with_large_amount.1.json +++ b/bill_payments/test_snapshots/test_recurring_bill_with_large_amount.1.json @@ -34,6 +34,7 @@ { "u32": 30 }, + "void", { "string": "XLM" } @@ -149,6 +150,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -210,6 +217,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -255,6 +270,12 @@ "u64": 3592000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -314,6 +335,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -490,6 +519,7 @@ { "u32": 30 }, + "void", { "string": "XLM" } @@ -742,6 +772,12 @@ "u64": 1000000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -803,6 +839,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } @@ -889,6 +933,12 @@ "u64": 3592000 } }, + { + "key": { + "symbol": "external_ref" + }, + "val": "void" + }, { "key": { "symbol": "frequency_days" @@ -948,6 +998,14 @@ "symbol": "schedule_id" }, "val": "void" + }, + { + "key": { + "symbol": "tags" + }, + "val": { + "vec": [] + } } ] } diff --git a/bill_payments/tests/gas_bench.rs b/bill_payments/tests/gas_bench.rs index fc9f950e..8a943b46 100644 --- a/bill_payments/tests/gas_bench.rs +++ b/bill_payments/tests/gas_bench.rs @@ -52,9 +52,10 @@ fn bench_get_total_unpaid_worst_case() { &owner, &name, &100i128, - &1_000_000u64, // Due date is 1,000,000 + &1_000_000u64, &false, &0u32, + &None, &String::from_str(&env, "XLM"), ); } diff --git a/bill_payments/tests/stress_test_large_amounts.rs b/bill_payments/tests/stress_test_large_amounts.rs index e4221884..9c6e35c1 100644 --- a/bill_payments/tests/stress_test_large_amounts.rs +++ b/bill_payments/tests/stress_test_large_amounts.rs @@ -13,22 +13,9 @@ //! - No explicit caps are imposed by the contract, but overflow will panic use bill_payments::{BillPayments, BillPaymentsClient}; -use soroban_sdk::testutils::{Address as AddressTrait, Ledger, LedgerInfo}; +use soroban_sdk::testutils::Address as AddressTrait; use soroban_sdk::{Env, String}; - -fn set_time(env: &Env, timestamp: u64) { - let proto = env.ledger().protocol_version(); - env.ledger().set(LedgerInfo { - protocol_version: proto, - sequence_number: 1, - timestamp, - network_id: [0; 32], - base_reserve: 10, - min_temp_entry_ttl: 1, - min_persistent_entry_ttl: 1, - max_entry_ttl: 100000, - }); -} +use testutils::set_ledger_time; #[test] fn test_create_bill_near_max_i128() { @@ -43,14 +30,16 @@ fn test_create_bill_near_max_i128() { let large_amount = i128::MAX / 2; let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Large Bill"), - &large_amount, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); + &owner, + &String::from_str(&env, + "Large Bill"), + &large_amount, + &1000000, + &false, + &0, + &None, + &String::from_str(&env, "XLM") + ); let bill = client.get_bill(&bill_id).unwrap(); assert_eq!(bill.amount, large_amount); @@ -69,14 +58,16 @@ fn test_pay_bill_with_large_amount() { let large_amount = i128::MAX / 2; let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Large Bill"), - &large_amount, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); + &owner, + &String::from_str(&env, + "Large Bill"), + &large_amount, + &1000000, + &false, + &0, + &None, + &String::from_str(&env, "XLM") + ); env.mock_all_auths(); client.pay_bill(&owner, &bill_id); @@ -98,14 +89,16 @@ fn test_recurring_bill_with_large_amount() { let large_amount = i128::MAX / 2; let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Large Recurring"), - &large_amount, - &1000000, - &true, - &30, - &String::from_str(&env, "XLM"), - ); + &owner, + &String::from_str(&env, + "Large Recurring"), + &large_amount, + &1000000, + &true, + &30, + &None, + &String::from_str(&env, "XLM") + ); env.mock_all_auths(); client.pay_bill(&owner, &bill_id); @@ -134,25 +127,29 @@ fn test_get_total_unpaid_with_two_large_bills() { let amount = i128::MAX / 4; client.create_bill( - &owner, - &String::from_str(&env, "Bill1"), - &amount, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); + &owner, + &String::from_str(&env, + "Bill1"), + &amount, + &1000000, + &false, + &0, + &None, + &String::from_str(&env, "XLM") + ); env.mock_all_auths(); client.create_bill( - &owner, - &String::from_str(&env, "Bill2"), - &amount, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); + &owner, + &String::from_str(&env, + "Bill2"), + &amount, + &1000000, + &false, + &0, + &None, + &String::from_str(&env, "XLM") + ); let total = client.get_total_unpaid(&owner); assert_eq!(total, amount + amount); @@ -172,25 +169,29 @@ fn test_get_total_unpaid_overflow_panics() { let amount = i128::MAX / 2 + 1000; client.create_bill( - &owner, - &String::from_str(&env, "Bill1"), - &amount, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); + &owner, + &String::from_str(&env, + "Bill1"), + &amount, + &1000000, + &false, + &0, + &None, + &String::from_str(&env, "XLM") + ); env.mock_all_auths(); client.create_bill( - &owner, - &String::from_str(&env, "Bill2"), - &amount, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); + &owner, + &String::from_str(&env, + "Bill2"), + &amount, + &1000000, + &false, + &0, + &None, + &String::from_str(&env, "XLM") + ); // This should panic due to overflow client.get_total_unpaid(&owner); @@ -210,25 +211,29 @@ fn test_multiple_large_bills_different_owners() { // Each owner can have large bills independently client.create_bill( - &owner1, - &String::from_str(&env, "Owner1 Bill"), - &large_amount, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); + &owner1, + &String::from_str(&env, + "Owner1 Bill"), + &large_amount, + &1000000, + &false, + &0, + &None, + &String::from_str(&env, "XLM") + ); env.mock_all_auths(); client.create_bill( - &owner2, - &String::from_str(&env, "Owner2 Bill"), - &large_amount, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); + &owner2, + &String::from_str(&env, + "Owner2 Bill"), + &large_amount, + &1000000, + &false, + &0, + &None, + &String::from_str(&env, "XLM") + ); let total1 = client.get_total_unpaid(&owner1); let total2 = client.get_total_unpaid(&owner2); @@ -240,7 +245,7 @@ fn test_multiple_large_bills_different_owners() { #[test] fn test_archive_large_amount_bill() { let env = Env::default(); - set_time(&env, 1000000); + set_ledger_time(&env, 1, 1000000); let contract_id = env.register_contract(None, BillPayments); let client = BillPaymentsClient::new(&env, &contract_id); @@ -251,14 +256,16 @@ fn test_archive_large_amount_bill() { let large_amount = i128::MAX / 2; let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Large Bill"), - &large_amount, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); + &owner, + &String::from_str(&env, + "Large Bill"), + &large_amount, + &1000000, + &false, + &0, + &None, + &String::from_str(&env, "XLM") + ); env.mock_all_auths(); client.pay_bill(&owner, &bill_id); @@ -267,7 +274,7 @@ fn test_archive_large_amount_bill() { let before_timestamp: u64 = 2_000_000; client.archive_paid_bills(&owner, &before_timestamp); - let archived = client.get_archived_bill(&bill_id).unwrap(); + let archived = client.get_archived_bill(&owner, &bill_id); assert_eq!(archived.amount, large_amount); } @@ -287,12 +294,16 @@ fn test_batch_pay_large_bills() { for i in 0..5 { let bill_id = client.create_bill( &owner, - &String::from_str(&env, &format!("Bill{}", i)), + &String::from_str(&env, + &format!("Bill{}", + i)), &amount, &1000000, &false, &0, - &String::from_str(&env, "XLM"), + &None, + &String::from_str(&env, + "XLM") ); bill_ids.push_back(bill_id); env.mock_all_auths(); @@ -311,34 +322,6 @@ fn test_batch_pay_large_bills() { } } -// #[test] -// fn test_overdue_bills_with_large_amounts() { -// let env = Env::default(); -// set_time(&env, 2_000_000); - -// let contract_id = env.register_contract(None, BillPayments); -// let client = BillPaymentsClient::new(&env, &contract_id); -// let owner = ::generate(&env); - -// env.mock_all_auths(); - -// let large_amount = i128::MAX / 2; - -// client.create_bill( -// &owner, -// &String::from_str(&env, "Overdue Large"), -// &large_amount, -// &1000000, // Past due -// &false, -// &0, -// &String::from_str(&env, "XLM"), -// ); - -// let page = client.get_overdue_bills(&0, &10); -// assert_eq!(page.count, 1); -// assert_eq!(page.items.get(0).unwrap().amount, large_amount); -// } - #[test] fn test_edge_case_i128_max_minus_one() { let env = Env::default(); @@ -352,14 +335,16 @@ fn test_edge_case_i128_max_minus_one() { let edge_amount = i128::MAX - 1; let bill_id = client.create_bill( - &owner, - &String::from_str(&env, "Edge Case"), - &edge_amount, - &1000000, - &false, - &0, - &String::from_str(&env, "XLM"), - ); + &owner, + &String::from_str(&env, + "Edge Case"), + &edge_amount, + &1000000, + &false, + &0, + &None, + &String::from_str(&env, "XLM") + ); let bill = client.get_bill(&bill_id).unwrap(); assert_eq!(bill.amount, edge_amount); @@ -380,12 +365,16 @@ fn test_pagination_with_large_amounts() { for i in 0..15 { client.create_bill( &owner, - &String::from_str(&env, &format!("Bill{}", i)), + &String::from_str(&env, + &format!("Bill{}", + i)), &large_amount, &1000000, &false, &0, - &String::from_str(&env, "XLM"), + &None, + &String::from_str(&env, + "XLM") ); env.mock_all_auths(); } diff --git a/bill_payments/tests/stress_tests.rs b/bill_payments/tests/stress_tests.rs index eb512c4a..7e029a30 100644 --- a/bill_payments/tests/stress_tests.rs +++ b/bill_payments/tests/stress_tests.rs @@ -79,12 +79,25 @@ fn stress_200_bills_single_user() { let due_date = 2_000_000_000u64; // far future for _ in 0..200 { - client.create_bill(&owner, &name, &100i128, &due_date, &false, &0u32); + client.create_bill( + &owner, + &name, + &100i128, + &due_date, + &false, + &0u32, + &None, + &String::from_str(&env, "XLM") + ); } // Verify aggregate total let total = client.get_total_unpaid(&owner); - assert_eq!(total, 200 * 100i128, "get_total_unpaid must sum all 200 bills"); + assert_eq!( + total, + 200 * 100i128, + "get_total_unpaid must sum all 200 bills" + ); // Exhaust all pages with MAX_PAGE_LIMIT (50) — should take exactly 4 pages let mut collected = 0u32; @@ -122,7 +135,16 @@ fn stress_instance_ttl_valid_after_200_bills() { let due_date = 2_000_000_000u64; for _ in 0..200 { - client.create_bill(&owner, &name, &100i128, &due_date, &false, &0u32); + client.create_bill( + &owner, + &name, + &100i128, + &due_date, + &false, + &0u32, + &None, + &String::from_str(&env, "XLM") + ); } let ttl = env.as_contract(&contract_id, || env.storage().instance().get_ttl()); @@ -155,7 +177,16 @@ fn stress_bills_across_10_users() { for user in &users { for _ in 0..BILLS_PER_USER { - client.create_bill(user, &name, &AMOUNT_PER_BILL, &due_date, &false, &0u32); + client.create_bill( + user, + &name, + &AMOUNT_PER_BILL, + &due_date, + &false, + &0u32, + &None, + &String::from_str(&env, "XLM") + ); } } @@ -208,7 +239,16 @@ fn stress_ttl_re_bumped_after_ledger_advancement() { // Phase 1: create 50 bills — TTL is set to INSTANCE_BUMP_AMOUNT for _ in 0..50 { - client.create_bill(&owner, &name, &100i128, &due_date, &false, &0u32); + client.create_bill( + &owner, + &name, + &100i128, + &due_date, + &false, + &0u32, + &None, + &String::from_str(&env, "XLM") + ); } let ttl_batch1 = env.as_contract(&contract_id, || env.storage().instance().get_ttl()); @@ -239,7 +279,16 @@ fn stress_ttl_re_bumped_after_ledger_advancement() { ); // Phase 3: one more create_bill triggers extend_ttl → re-bumped - client.create_bill(&owner, &name, &100i128, &due_date, &false, &0u32); + client.create_bill( + &owner, + &name, + &100i128, + &due_date, + &false, + &0u32, + &None, + &String::from_str(&env, "XLM") + ); let ttl_rebumped = env.as_contract(&contract_id, || env.storage().instance().get_ttl()); assert!( @@ -261,7 +310,16 @@ fn stress_ttl_re_bumped_by_pay_bill_after_ledger_advancement() { let due_date = 2_000_000_000u64; // Create one bill to initialise instance storage - let bill_id = client.create_bill(&owner, &name, &500i128, &due_date, &false, &0u32); + let bill_id = client.create_bill( + &owner, + &name, + &500i128, + &due_date, + &false, + &0u32, + &None, + &String::from_str(&env, "XLM") + ); // Advance ledger so TTL drops below threshold env.ledger().set(LedgerInfo { @@ -307,7 +365,16 @@ fn stress_archive_100_paid_bills() { // Create 100 bills (IDs 1..=100) for _ in 0..100 { - client.create_bill(&owner, &name, &200i128, &due_date, &false, &0u32); + client.create_bill( + &owner, + &name, + &200i128, + &due_date, + &false, + &0u32, + &None, + &String::from_str(&env, "XLM") + ); } // Pay all 100 bills (non-recurring, so no new bills created) @@ -328,8 +395,14 @@ fn stress_archive_100_paid_bills() { // Verify storage stats let stats = client.get_storage_stats(); - assert_eq!(stats.active_bills, 0, "No active bills should remain after full archive"); - assert_eq!(stats.archived_bills, 100, "Storage stats must show 100 archived bills"); + assert_eq!( + stats.active_bills, 0, + "No active bills should remain after full archive" + ); + assert_eq!( + stats.archived_bills, 100, + "Storage stats must show 100 archived bills" + ); // Verify paginated access to archived bills let mut archived_seen = 0u32; @@ -381,7 +454,16 @@ fn stress_archive_across_5_users() { for (i, user) in users.iter().enumerate() { let first = next_id; for _ in 0..BILLS_PER_USER { - client.create_bill(user, &name, &100i128, &due_date, &false, &0u32); + client.create_bill( + user, + &name, + &100i128, + &due_date, + &false, + &0u32, + &None, + &String::from_str(&env, "XLM") + ); next_id += 1; } let last = next_id - 1; @@ -393,10 +475,13 @@ fn stress_archive_across_5_users() { client.pay_bill(&users[((id - 1) / BILLS_PER_USER) as usize], &id); } - // Archive using first user as caller (any authenticated address may archive) - let archived = client.archive_paid_bills(&users[0], &2_000_000_000u64); + // Each owner must archive their own paid bills (owner affinity). + let mut archived_total = 0u32; + for user in users.iter() { + archived_total += client.archive_paid_bills(user, &2_000_000_000u64); + } assert_eq!( - archived, + archived_total, N_USERS as u32 * BILLS_PER_USER, "All {} bills across {} users must be archived", N_USERS * BILLS_PER_USER as usize, @@ -425,7 +510,16 @@ fn bench_get_unpaid_bills_first_page_of_200() { let due_date = 2_000_000_000u64; for _ in 0..200 { - client.create_bill(&owner, &name, &100i128, &due_date, &false, &0u32); + client.create_bill( + &owner, + &name, + &100i128, + &due_date, + &false, + &0u32, + &None, + &String::from_str(&env, "XLM") + ); } let (cpu, mem, page) = measure(&env, || client.get_unpaid_bills(&owner, &0u32, &50u32)); @@ -450,7 +544,16 @@ fn bench_get_unpaid_bills_last_page_of_200() { let due_date = 2_000_000_000u64; for _ in 0..200 { - client.create_bill(&owner, &name, &100i128, &due_date, &false, &0u32); + client.create_bill( + &owner, + &name, + &100i128, + &due_date, + &false, + &0u32, + &None, + &String::from_str(&env, "XLM") + ); } // Navigate to the last page cursor @@ -481,14 +584,24 @@ fn bench_archive_paid_bills_100() { let due_date = 1_700_000_000u64; for _ in 0..100 { - client.create_bill(&owner, &name, &100i128, &due_date, &false, &0u32); + client.create_bill( + &owner, + &name, + &100i128, + &due_date, + &false, + &0u32, + &None, + &String::from_str(&env, "XLM") + ); } for id in 1u32..=100 { client.pay_bill(&owner, &id); } - let (cpu, mem, result) = - measure(&env, || client.archive_paid_bills(&owner, &2_000_000_000u64)); + let (cpu, mem, result) = measure(&env, || { + client.archive_paid_bills(&owner, &2_000_000_000u64) + }); assert_eq!(result, 100); println!( @@ -509,7 +622,16 @@ fn bench_get_total_unpaid_200_bills() { let due_date = 2_000_000_000u64; for _ in 0..200 { - client.create_bill(&owner, &name, &100i128, &due_date, &false, &0u32); + client.create_bill( + &owner, + &name, + &100i128, + &due_date, + &false, + &0u32, + &None, + &String::from_str(&env, "XLM") + ); } let expected = 200i128 * 100; diff --git a/bill_payments/tests/test_notifications.rs b/bill_payments/tests/test_notifications.rs index 43e8b332..552c059b 100644 --- a/bill_payments/tests/test_notifications.rs +++ b/bill_payments/tests/test_notifications.rs @@ -20,14 +20,15 @@ fn test_notification_flow() { // Create Bill let bill_id = client.create_bill( - &user, - &soroban_sdk::String::from_str(&e, "Electricity"), - &1000, - &1234567890, - &false, - &0, - &soroban_sdk::String::from_str(&e, "XLM"), - ); + &user, + &soroban_sdk::String::from_str(&e, "Electricity"), + &1000, + &1234567890, + &false, + &0u32, + &None, + &soroban_sdk::String::from_str(&e, "XLM") + ); // VERIFY: Get Events let all_events = e.events().all(); diff --git a/data_migration/src/lib.rs b/data_migration/src/lib.rs index 224c46ee..a08925d3 100644 --- a/data_migration/src/lib.rs +++ b/data_migration/src/lib.rs @@ -13,6 +13,17 @@ use std::collections::HashMap; /// Current schema version for migration compatibility. pub const SCHEMA_VERSION: u32 = 1; +/// Expected headers for Savings Goals CSV export/import. +pub const SAVINGS_GOAL_HEADERS: &[&str] = &[ + "id", + "owner", + "name", + "target_amount", + "current_amount", + "target_date", + "locked", +]; + /// Minimum supported schema version for import. pub const MIN_SUPPORTED_VERSION: u32 = 1; @@ -107,7 +118,10 @@ impl ExportSnapshot { /// Compute SHA256 checksum of the payload (canonical JSON). pub fn compute_checksum(&self) -> String { let mut hasher = Sha256::new(); - hasher.update(serde_json::to_vec(&self.payload).unwrap_or_else(|_| panic!("payload must be serializable"))); + hasher.update( + serde_json::to_vec(&self.payload) + .unwrap_or_else(|_| panic!("payload must be serializable")), + ); hex::encode(hasher.finalize().as_ref()) } @@ -260,13 +274,68 @@ pub fn import_from_binary(bytes: &[u8]) -> Result Result, MigrationError> { - let mut rdr = csv::Reader::from_reader(bytes); + let mut rdr = csv::ReaderBuilder::new() + .has_headers(true) + .from_reader(bytes); + + // 1. Strict Header Validation + { + let headers = rdr + .headers() + .map_err(|e| MigrationError::InvalidFormat(format!("Failed to read headers: {}", e)))?; + + if headers.len() != SAVINGS_GOAL_HEADERS.len() { + return Err(MigrationError::ValidationFailed(format!( + "Header length mismatch: expected {}, found {}", + SAVINGS_GOAL_HEADERS.len(), + headers.len() + ))); + } + + for (i, expected) in SAVINGS_GOAL_HEADERS.iter().enumerate() { + if headers.get(i) != Some(expected) { + return Err(MigrationError::ValidationFailed(format!( + "Header mismatch at column {}: expected '{}', found '{:?}'", + i, + expected, + headers.get(i) + ))); + } + } + } + let mut goals = Vec::new(); - for result in rdr.deserialize() { - let record: CsvGoalRow = - result.map_err(|e| MigrationError::DeserializeError(e.to_string()))?; + for (row_idx, result) in rdr.deserialize().enumerate() { + let record: CsvGoalRow = result.map_err(|e| { + MigrationError::DeserializeError(format!("Row {} error: {}", row_idx + 1, e)) + })?; + + // 2. Extra Row-Level Validation (Internal Consistency) + if record.target_amount < 0 { + return Err(MigrationError::ValidationFailed(format!( + "Row {}: target_amount cannot be negative", + row_idx + 1 + ))); + } + if record.current_amount < 0 { + return Err(MigrationError::ValidationFailed(format!( + "Row {}: current_amount cannot be negative", + row_idx + 1 + ))); + } + goals.push(SavingsGoalExport { id: record.id, owner: record.owner, @@ -277,6 +346,13 @@ pub fn import_goals_from_csv(bytes: &[u8]) -> Result, Mig locked: record.locked, }); } + + if goals.is_empty() { + return Err(MigrationError::ValidationFailed( + "CSV contains no data rows".into(), + )); + } + Ok(goals) } @@ -440,4 +516,67 @@ mod tests { let MigrationEvent::V1(v1) = loaded; assert_eq!(v1.version, SCHEMA_VERSION); } + + #[test] + fn test_csv_import_wrong_headers_fails() { + let csv_data = "id,owner,bad_header,target_amount,current_amount,target_date,locked\n1,G1,Name,1000,500,2000000000,true"; + let result = import_goals_from_csv(csv_data.as_bytes()); + assert!(result.is_err()); + if let Err(MigrationError::ValidationFailed(msg)) = result { + assert!(msg.contains("Header mismatch at column 2")); + } else { + panic!("Expected ValidationFailed error"); + } + } + + #[test] + fn test_csv_import_missing_columns_fails() { + let csv_data = "id,owner,name,target_amount,current_amount,target_date\n1,G1,Emergency,1000,500,2000000000"; + let result = import_goals_from_csv(csv_data.as_bytes()); + assert!(result.is_err()); + if let Err(MigrationError::ValidationFailed(msg)) = result { + assert!(msg.contains("Header length mismatch")); + } else { + panic!("Expected ValidationFailed error"); + } + } + + #[test] + fn test_csv_import_malformed_row_data_fails() { + let csv_data = "id,owner,name,target_amount,current_amount,target_date,locked\n1,G1,Emergency,not_a_number,500,2000000000,true"; + let result = import_goals_from_csv(csv_data.as_bytes()); + assert!(result.is_err()); + assert!(matches!(result, Err(MigrationError::DeserializeError(_)))); + } + + #[test] + fn test_csv_import_negative_amounts_fails() { + let csv_data = "id,owner,name,target_amount,current_amount,target_date,locked\n1,G1,Emergency,-1000,500,2000000000,true"; + let result = import_goals_from_csv(csv_data.as_bytes()); + assert!(result.is_err()); + if let Err(MigrationError::ValidationFailed(msg)) = result { + assert!(msg.contains("target_amount cannot be negative")); + } else { + panic!("Expected ValidationFailed error"); + } + } + + #[test] + fn test_csv_import_empty_file_fails() { + let csv_data = ""; + let result = import_goals_from_csv(csv_data.as_bytes()); + assert!(result.is_err()); + } + + #[test] + fn test_csv_import_only_headers_fails() { + let csv_data = "id,owner,name,target_amount,current_amount,target_date,locked"; + let result = import_goals_from_csv(csv_data.as_bytes()); + assert!(result.is_err()); + if let Err(MigrationError::ValidationFailed(msg)) = result { + assert!(msg.contains("CSV contains no data rows")); + } else { + panic!("Expected ValidationFailed error"); + } + } } diff --git a/emergency_killswitch/Cargo.toml b/emergency_killswitch/Cargo.toml index ab7f219e..f8aca4db 100644 --- a/emergency_killswitch/Cargo.toml +++ b/emergency_killswitch/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" crate-type = ["cdylib", "rlib"] [dependencies] -soroban-sdk = "21.0.0" +soroban-sdk = "21.1.1" [dev-dependencies] -soroban-sdk = { version = "21.0.0", features = ["testutils"] } +soroban-sdk = { version = "21.1.1", features = ["testutils"] } diff --git a/examples/bill_payments_example.rs b/examples/bill_payments_example.rs index dee3c269..0def3019 100644 --- a/examples/bill_payments_example.rs +++ b/examples/bill_payments_example.rs @@ -1,5 +1,5 @@ -use soroban_sdk::{Env, Address, String, testutils::Address as _}; use bill_payments::{BillPayments, BillPaymentsClient}; +use soroban_sdk::{testutils::Address as _, Address, Env, String}; fn main() { // 1. Setup the Soroban environment @@ -22,14 +22,21 @@ fn main() { let currency = String::from_str(&env, "USD"); println!("Creating bill: '{}' for {} {}", bill_name, amount, currency); - let bill_id = client.create_bill(&owner, &bill_name, &amount, &due_date, &false, &0, ¤cy).unwrap(); + let bill_id = client + .create_bill( + &owner, &bill_name, &amount, &due_date, &false, &0, ¤cy, + ) + .unwrap(); println!("Bill created successfully with ID: {}", bill_id); // 5. [Read] List unpaid bills let bill_page = client.get_unpaid_bills(&owner, &0, &5); println!("\nUnpaid Bills for {:?}:", owner); for bill in bill_page.items.iter() { - println!(" ID: {}, Name: {}, Amount: {} {}", bill.id, bill.name, bill.amount, bill.currency); + println!( + " ID: {}, Name: {}, Amount: {} {}", + bill.id, bill.name, bill.amount, bill.currency + ); } // 6. [Write] Pay the bill diff --git a/examples/family_wallet_example.rs b/examples/family_wallet_example.rs index 0781ebda..8b5368fc 100644 --- a/examples/family_wallet_example.rs +++ b/examples/family_wallet_example.rs @@ -1,5 +1,5 @@ -use soroban_sdk::{Env, Address, Vec, testutils::Address as _}; -use family_wallet::{FamilyWallet, FamilyWalletClient, FamilyRole}; +use family_wallet::{FamilyRole, FamilyWallet, FamilyWalletClient}; +use soroban_sdk::{testutils::Address as _, Address, Env, Vec}; fn main() { // 1. Setup the Soroban environment @@ -22,21 +22,23 @@ fn main() { let mut initial_members = Vec::new(&env); initial_members.push_back(owner.clone()); initial_members.push_back(member1.clone()); - + client.init(&owner, &initial_members); println!("Wallet initialized successfully!"); // 5. [Read] Check roles of members let owner_member = client.get_member(&owner).unwrap(); println!("\nOwner Role: {:?}", owner_member.role); - + let m1_member = client.get_member(&member1).unwrap(); println!("Member 1 Role: {:?}", m1_member.role); // 6. [Write] Add a new family member with a specific role and spending limit println!("\nAdding new member: {:?}", member2); let spending_limit = 1000i128; - client.add_member(&owner, &member2, &FamilyRole::Member, &spending_limit).unwrap(); + client + .add_member(&owner, &member2, &FamilyRole::Member, &spending_limit) + .unwrap(); println!("Member added successfully!"); // 7. [Read] Verify the new member diff --git a/examples/insurance_example.rs b/examples/insurance_example.rs index 591ec493..31d00036 100644 --- a/examples/insurance_example.rs +++ b/examples/insurance_example.rs @@ -1,5 +1,5 @@ -use soroban_sdk::{Env, Address, String, testutils::Address as _}; use insurance::{Insurance, InsuranceClient}; +use soroban_sdk::{testutils::Address as _, Address, Env, String}; fn main() { // 1. Setup the Soroban environment @@ -21,15 +21,29 @@ fn main() { let monthly_premium = 200i128; let coverage_amount = 50000i128; - println!("Creating policy: '{}' with premium: {} and coverage: {}", policy_name, monthly_premium, coverage_amount); - let policy_id = client.create_policy(&owner, &policy_name, &coverage_type, &monthly_premium, &coverage_amount).unwrap(); + println!( + "Creating policy: '{}' with premium: {} and coverage: {}", + policy_name, monthly_premium, coverage_amount + ); + let policy_id = client + .create_policy( + &owner, + &policy_name, + &coverage_type, + &monthly_premium, + &coverage_amount, + ) + .unwrap(); println!("Policy created successfully with ID: {}", policy_id); // 5. [Read] List active policies let policy_page = client.get_active_policies(&owner, &0, &5); println!("\nActive Policies for {:?}:", owner); for policy in policy_page.items.iter() { - println!(" ID: {}, Name: {}, Premium: {}, Coverage: {}", policy.id, policy.name, policy.monthly_premium, policy.coverage_amount); + println!( + " ID: {}, Name: {}, Premium: {}, Coverage: {}", + policy.id, policy.name, policy.monthly_premium, policy.coverage_amount + ); } // 6. [Write] Pay a premium @@ -39,7 +53,10 @@ fn main() { // 7. [Read] Verify policy status (next payment date updated) let policy = client.get_policy(&policy_id).unwrap(); - println!("Next Payment Date (Timestamp): {}", policy.next_payment_date); + println!( + "Next Payment Date (Timestamp): {}", + policy.next_payment_date + ); println!("\nExample completed successfully!"); } diff --git a/examples/orchestrator_example.rs b/examples/orchestrator_example.rs index 206bd282..af243161 100644 --- a/examples/orchestrator_example.rs +++ b/examples/orchestrator_example.rs @@ -1,5 +1,5 @@ -use soroban_sdk::{Env, Address, testutils::Address as _}; use orchestrator::{Orchestrator, OrchestratorClient}; +use soroban_sdk::{testutils::Address as _, Address, Env}; fn main() { // 1. Setup the Soroban environment @@ -12,7 +12,7 @@ fn main() { // 3. Generate mock addresses for all participants and contracts let caller = Address::generate(&env); - + // Contract addresses let family_wallet_addr = Address::generate(&env); let remittance_split_addr = Address::generate(&env); @@ -30,7 +30,10 @@ fn main() { // 4. [Write] Execute a complete remittance flow // This coordinates splitting the amount and paying into downstream contracts let total_amount = 5000i128; - println!("Executing complete remittance flow for amount: {}", total_amount); + println!( + "Executing complete remittance flow for amount: {}", + total_amount + ); println!("Orchestrating across:"); println!(" - Savings Goal ID: {}", goal_id); println!(" - Bill ID: {}", bill_id); @@ -38,7 +41,7 @@ fn main() { // In this dry-run example, we show the call signature. // In a full test environment, you would first set up the state in the dependent contracts. - + /* client.execute_remittance_flow( &caller, diff --git a/examples/remittance_split_example.rs b/examples/remittance_split_example.rs index c7bdbb87..e1d0312b 100644 --- a/examples/remittance_split_example.rs +++ b/examples/remittance_split_example.rs @@ -1,5 +1,5 @@ -use soroban_sdk::{Env, Address, testutils::Address as _}; use remittance_split::{RemittanceSplit, RemittanceSplitClient}; +use soroban_sdk::{testutils::Address as _, Address, Env}; fn main() { // 1. Setup the Soroban environment @@ -30,7 +30,10 @@ fn main() { // 6. [Write] Simulate a remittance distribution let total_amount = 1000i128; - println!("\nCalculating allocation for total amount: {}", total_amount); + println!( + "\nCalculating allocation for total amount: {}", + total_amount + ); let allocations = client.calculate_split(&total_amount); println!("Allocations:"); diff --git a/examples/reporting_example.rs b/examples/reporting_example.rs index bb2026be..e9a9ce41 100644 --- a/examples/reporting_example.rs +++ b/examples/reporting_example.rs @@ -1,5 +1,5 @@ -use soroban_sdk::{Env, Address, testutils::Address as _}; -use reporting::{ReportingClient, Category}; +use reporting::{Category, ReportingClient}; +use soroban_sdk::{testutils::Address as _, Address, Env}; // Mock contracts for the reporting example // In a real scenario, these would be the actual deployed contract IDs @@ -17,7 +17,7 @@ fn main() { // 3. Generate mock addresses for dependencies and admin let admin = Address::generate(&env); let user = Address::generate(&env); - + // Dependencies let split_addr = Address::generate(&env); let savings_addr = Address::generate(&env); @@ -33,24 +33,26 @@ fn main() { // 5. [Write] Configure contract addresses println!("Configuring dependency addresses..."); - client.configure_addresses( - &admin, - &split_addr, - &savings_addr, - &bills_addr, - &insurance_addr, - &family_addr - ).unwrap(); + client + .configure_addresses( + &admin, + &split_addr, + &savings_addr, + &bills_addr, + &insurance_addr, + &family_addr, + ) + .unwrap(); println!("Addresses configured successfully!"); // 6. [Read] Generate a mock report - // Note: In this environment, calling reports that query other contracts + // Note: In this environment, calling reports that query other contracts // would require those contracts to be registered at the provided addresses. // For simplicity in this standalone example, we'll focus on the configuration and health score calculation // if the logic allows it without full cross-contract state. - + // However, since we're using Env::default(), we can actually register simple mocks if needed. - // But for a clear "runnable example" that doesn't get too complex, + // But for a clear "runnable example" that doesn't get too complex, // showing the setup and a successful call is the primary goal. println!("\nReporting contract is now ready to generate financial insights."); diff --git a/examples/savings_goals_example.rs b/examples/savings_goals_example.rs index 67bb1dd3..24900e2d 100644 --- a/examples/savings_goals_example.rs +++ b/examples/savings_goals_example.rs @@ -1,5 +1,5 @@ -use soroban_sdk::{Env, Address, String, testutils::Address as _}; use savings_goals::{SavingsGoalContract, SavingsGoalContractClient}; +use soroban_sdk::{testutils::Address as _, Address, Env, String}; fn main() { // 1. Setup the Soroban environment @@ -20,8 +20,13 @@ fn main() { let target_amount = 5000i128; let target_date = env.ledger().timestamp() + 31536000; // 1 year from now - println!("Creating savings goal: '{}' with target: {}", goal_name, target_amount); - let goal_id = client.create_goal(&owner, &goal_name, &target_amount, &target_date).unwrap(); + println!( + "Creating savings goal: '{}' with target: {}", + goal_name, target_amount + ); + let goal_id = client + .create_goal(&owner, &goal_name, &target_amount, &target_date) + .unwrap(); println!("Goal created successfully with ID: {}", goal_id); // 5. [Read] Fetch the goal to check progress diff --git a/family_wallet/Cargo.toml b/family_wallet/Cargo.toml index c7c60148..87b04915 100644 --- a/family_wallet/Cargo.toml +++ b/family_wallet/Cargo.toml @@ -7,10 +7,10 @@ edition = "2021" crate-type = ["cdylib", "rlib"] [dependencies] -soroban-sdk = "21.0.0" +soroban-sdk = "21.1.1" remitwise-common = { path = "../remitwise-common" } [dev-dependencies] -soroban-sdk = { version = "21.0.0", features = ["testutils"] } +soroban-sdk = { version = "21.1.1", features = ["testutils"] } testutils = { path = "../testutils" } diff --git a/family_wallet/src/lib.rs b/family_wallet/src/lib.rs index 3e93d959..c9abcbf1 100644 --- a/family_wallet/src/lib.rs +++ b/family_wallet/src/lib.rs @@ -575,7 +575,9 @@ impl FamilyWallet { .get(&symbol_short!("PEND_TXS")) .unwrap_or_else(|| panic!("Pending transactions map not initialized")); - let mut pending_tx = pending_txs.get(tx_id).unwrap_or_else(|| panic!("Transaction not found")); + let mut pending_tx = pending_txs + .get(tx_id) + .unwrap_or_else(|| panic!("Transaction not found")); let current_time = env.ledger().timestamp(); if current_time > pending_tx.expires_at { @@ -1501,7 +1503,9 @@ impl FamilyWallet { .instance() .get(&symbol_short!("MEMBERS")) .unwrap_or_else(|| panic!("Wallet not initialized")); - let member = members.get(caller.clone()).unwrap_or_else(|| panic!("Not a family member")); + let member = members + .get(caller.clone()) + .unwrap_or_else(|| panic!("Not a family member")); if Self::role_has_expired(env, caller) { panic!("Role has expired"); } diff --git a/insurance/Cargo.toml b/insurance/Cargo.toml index 412a27b2..99666a7b 100644 --- a/insurance/Cargo.toml +++ b/insurance/Cargo.toml @@ -7,12 +7,12 @@ edition = "2021" crate-type = ["cdylib", "rlib"] [dependencies] -soroban-sdk = "21.0.0" +soroban-sdk = "21.1.1" remitwise-common = { path = "../remitwise-common" } [dev-dependencies] proptest = "1.10.0" -soroban-sdk = { version = "21.0.0", features = ["testutils"] } +soroban-sdk = { version = "21.1.1", features = ["testutils"] } testutils = { path = "../testutils" } diff --git a/insurance/src/lib.rs b/insurance/src/lib.rs index ff63d858..18031efd 100644 --- a/insurance/src/lib.rs +++ b/insurance/src/lib.rs @@ -3,7 +3,6 @@ use soroban_sdk::{ contract, contracterror, contractimpl, contracttype, symbol_short, Address, Env, Map, String, Symbol, Vec, - contract, contracterror, contractimpl, contracttype, symbol_short, Address, Env, Map, String, Symbol, Vec, }; use remitwise_common::CoverageType; @@ -81,16 +80,11 @@ pub mod pause_functions { /// Insurance policy data structure with owner tracking for access control #[derive(Clone)] #[contracttype] -#[derive(Clone)] -#[contracttype] -#[derive(Clone)] -#[contracttype] pub struct InsurancePolicy { pub id: u32, pub owner: Address, pub name: String, pub external_ref: Option, - pub coverage_type: String, pub coverage_type: CoverageType, pub monthly_premium: i128, pub coverage_amount: i128, @@ -129,17 +123,7 @@ pub struct PremiumSchedule { pub missed_count: u32, } -#[contracterror] -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -#[repr(u32)] -pub enum InsuranceError { - InvalidPremium = 1, - InvalidCoverage = 2, - PolicyNotFound = 3, - PolicyInactive = 4, - Unauthorized = 5, - BatchTooLarge = 6, -} + @@ -243,7 +227,6 @@ impl Insurance { } pub fn pause(env: Env, caller: Address) -> Result<(), InsuranceError> { caller.require_auth(); - let admin = Self::get_pause_admin(&env).unwrap_or_else(|| panic!("No pause admin set")); let admin = Self::get_pause_admin(&env).ok_or(InsuranceError::Unauthorized)?; if admin != caller { return Err(InsuranceError::Unauthorized); @@ -257,7 +240,6 @@ impl Insurance { } pub fn unpause(env: Env, caller: Address) -> Result<(), InsuranceError> { caller.require_auth(); - let admin = Self::get_pause_admin(&env).unwrap_or_else(|| panic!("No pause admin set")); let admin = Self::get_pause_admin(&env).ok_or(InsuranceError::Unauthorized)?; if admin != caller { return Err(InsuranceError::Unauthorized); @@ -376,205 +358,7 @@ impl Insurance { Ok(()) } - // ----------------------------------------------------------------------- - // Tag management - // ----------------------------------------------------------------------- - - fn validate_tags(tags: &Vec) { - if tags.is_empty() { - panic!("Tags cannot be empty"); - } - for tag in tags.iter() { - if tag.len() == 0 || tag.len() > 32 { - panic!("Tag must be between 1 and 32 characters"); - } - } - } - - pub fn add_tags_to_policy( - env: Env, - caller: Address, - policy_id: u32, - tags: Vec, - ) { - caller.require_auth(); - Self::validate_tags(&tags); - Self::extend_instance_ttl(&env); - - let mut policies: Map = env - .storage() - .instance() - .get(&symbol_short!("POLICIES")) - .unwrap_or_else(|| Map::new(&env)); - - let mut policy = policies.get(policy_id).expect("Policy not found"); - - if policy.owner != caller { - panic!("Only the policy owner can add tags"); - } - - for tag in tags.iter() { - policy.tags.push_back(tag); - } - - policies.set(policy_id, policy); - env.storage() - .instance() - .set(&symbol_short!("POLICIES"), &policies); - - env.events().publish( - (symbol_short!("insure"), symbol_short!("tags_add")), - (policy_id, caller, tags), - ); - } - - pub fn remove_tags_from_policy( - env: Env, - caller: Address, - policy_id: u32, - tags: Vec, - ) { - caller.require_auth(); - Self::validate_tags(&tags); - Self::extend_instance_ttl(&env); - - let mut policies: Map = env - .storage() - .instance() - .get(&symbol_short!("POLICIES")) - .unwrap_or_else(|| Map::new(&env)); - - let mut policy = policies.get(policy_id).expect("Policy not found"); - - if policy.owner != caller { - panic!("Only the policy owner can remove tags"); - } - - let mut new_tags = Vec::new(&env); - for existing_tag in policy.tags.iter() { - let mut should_keep = true; - for remove_tag in tags.iter() { - if existing_tag == remove_tag { - should_keep = false; - break; - } - } - if should_keep { - new_tags.push_back(existing_tag); - } - } - - policy.tags = new_tags; - policies.set(policy_id, policy); - env.storage() - .instance() - .set(&symbol_short!("POLICIES"), &policies); - - env.events().publish( - (symbol_short!("insure"), symbol_short!("tags_rem")), - (policy_id, caller, tags), - ); - } - - // ----------------------------------------------------------------------- - // Tag management - // ----------------------------------------------------------------------- - - fn validate_tags(tags: &Vec) { - if tags.is_empty() { - panic!("Tags cannot be empty"); - } - for tag in tags.iter() { - if tag.len() == 0 || tag.len() > 32 { - panic!("Tag must be between 1 and 32 characters"); - } - } - } - - pub fn add_tags_to_policy( - env: Env, - caller: Address, - policy_id: u32, - tags: Vec, - ) { - caller.require_auth(); - Self::validate_tags(&tags); - Self::extend_instance_ttl(&env); - - let mut policies: Map = env - .storage() - .instance() - .get(&symbol_short!("POLICIES")) - .unwrap_or_else(|| Map::new(&env)); - - let mut policy = policies.get(policy_id).expect("Policy not found"); - - if policy.owner != caller { - panic!("Only the policy owner can add tags"); - } - - for tag in tags.iter() { - policy.tags.push_back(tag); - } - - policies.set(policy_id, policy); - env.storage() - .instance() - .set(&symbol_short!("POLICIES"), &policies); - - env.events().publish( - (symbol_short!("insure"), symbol_short!("tags_add")), - (policy_id, caller, tags), - ); - } - - pub fn remove_tags_from_policy( - env: Env, - caller: Address, - policy_id: u32, - tags: Vec, - ) { - caller.require_auth(); - Self::validate_tags(&tags); - Self::extend_instance_ttl(&env); - let mut policies: Map = env - .storage() - .instance() - .get(&symbol_short!("POLICIES")) - .unwrap_or_else(|| Map::new(&env)); - - let mut policy = policies.get(policy_id).expect("Policy not found"); - - if policy.owner != caller { - panic!("Only the policy owner can remove tags"); - } - - let mut new_tags = Vec::new(&env); - for existing_tag in policy.tags.iter() { - let mut should_keep = true; - for remove_tag in tags.iter() { - if existing_tag == remove_tag { - should_keep = false; - break; - } - } - if should_keep { - new_tags.push_back(existing_tag); - } - } - - policy.tags = new_tags; - policies.set(policy_id, policy); - env.storage() - .instance() - .set(&symbol_short!("POLICIES"), &policies); - - env.events().publish( - (symbol_short!("insure"), symbol_short!("tags_rem")), - (policy_id, caller, tags), - ); - } // ----------------------------------------------------------------------- // Tag management @@ -706,7 +490,6 @@ impl Insurance { monthly_premium: i128, coverage_amount: i128, external_ref: Option, - ) -> u32 { ) -> Result { owner.require_auth(); Self::require_not_paused(&env, pause_functions::CREATE_POLICY)?; @@ -772,7 +555,6 @@ impl Insurance { env.events().publish( (symbol_short!("insure"), InsuranceEvent::PolicyCreated), (next_id, policy_owner, policy_external_ref), - (next_id, owner), ); Ok(next_id) @@ -829,7 +611,6 @@ impl Insurance { }; env.events().publish((PREMIUM_PAID,), event); - policies.set(policy_id, policy); policies.set(policy_id, policy.clone()); env.storage() .instance() @@ -885,7 +666,6 @@ impl Insurance { let current_time = env.ledger().timestamp(); let mut paid_count = 0; for id in policy_ids.iter() { - let mut policy = policies.get(id).unwrap_or_else(|| panic!("Policy not found")); let mut policy = policies_map.get(id).unwrap(); policy.next_payment_date = current_time + (30 * 86400); let event = PremiumPaidEvent { @@ -1008,7 +788,6 @@ impl Insurance { .get(&symbol_short!("POLICIES")) .unwrap_or_else(|| Map::new(&env)); - let mut policy = policies.get(policy_id).unwrap_or_else(|| panic!("Policy not found")); let mut policy = policies .get(policy_id) .ok_or(InsuranceError::PolicyNotFound)?; @@ -1019,8 +798,7 @@ impl Insurance { let was_active = policy.active; policy.active = false; - let policy_external_ref = policy.external_ref.clone(); - policies.set(policy_id, policy); + let _policy_external_ref = policy.external_ref.clone(); let premium_amount = policy.monthly_premium; policies.set(policy_id, policy.clone()); env.storage() @@ -1036,12 +814,8 @@ impl Insurance { timestamp: env.ledger().timestamp(), }; env.events().publish((POLICY_DEACTIVATED,), event); - env.events().publish( - (symbol_short!("insure"), InsuranceEvent::PolicyDeactivated), - (policy_id, caller, policy_external_ref), - ); - true + Ok(true) } /// Set or clear an external reference ID for a policy @@ -1086,11 +860,9 @@ impl Insurance { env.events().publish( (symbol_short!("insure"), InsuranceEvent::ExternalRefUpdated), (policy_id, caller, external_ref), - (symbol_short!("insuranc"), InsuranceEvent::PolicyDeactivated), - (policy_id, caller), ); - Ok(true) + true } /// Extend the TTL of instance storage @@ -1130,70 +902,41 @@ impl Insurance { // ----------------------------------------------------------------------- pub fn create_premium_schedule( env: Env, - owner: Address, + caller: Address, policy_id: u32, next_due: u64, interval: u64, ) -> Result { - // Changed to Result - owner.require_auth(); + caller.require_auth(); Self::require_not_paused(&env, pause_functions::CREATE_SCHED)?; - let name = String::from_str(&env, "Health Insurance"); - let coverage_type = String::from_str(&env, "health"); - let monthly_premium = 100; - let coverage_amount = 10000; - let external_ref = Some(String::from_str(&env, "POLICY-EXT-1")); + let current_time = env.ledger().timestamp(); + if next_due <= current_time { + return Err(InsuranceError::InvalidTimestamp); + } + + Self::extend_instance_ttl(&env); - let policy_id = client.create_policy( - &owner, - &name, - &coverage_type, - &monthly_premium, - &coverage_amount, - &external_ref, - ); let mut policies: Map = env .storage() .instance() .get(&symbol_short!("POLICIES")) .unwrap_or_else(|| Map::new(&env)); - let mut policy = policies.get(policy_id).unwrap_or_else(|| panic!("Policy not found")); let mut policy = policies .get(policy_id) .ok_or(InsuranceError::PolicyNotFound)?; - if policy.owner != owner { + if policy.owner != caller { return Err(InsuranceError::Unauthorized); } - let policy = client.get_policy(&policy_id).unwrap(); - assert_eq!(policy.id, 1); - assert_eq!(policy.owner, owner); - assert_eq!(policy.name, name); - assert_eq!(policy.external_ref, external_ref); - assert_eq!(policy.coverage_type, coverage_type); - assert_eq!(policy.monthly_premium, monthly_premium); - assert_eq!(policy.coverage_amount, coverage_amount); - assert!(policy.active); - assert_eq!(policy.next_payment_date, 1000000000 + (30 * 86400)); - } - let current_time = env.ledger().timestamp(); - if next_due <= current_time { - return Err(InsuranceError::InvalidTimestamp); - } - - Self::extend_instance_ttl(&env); - let mut schedules: Map = env .storage() .instance() .get(&symbol_short!("PREM_SCH")) .unwrap_or_else(|| Map::new(&env)); - client.create_policy(&owner, &name, &coverage_type, &0, &10000, &None); - } let next_schedule_id = env .storage() .instance() @@ -1203,7 +946,7 @@ impl Insurance { let schedule = PremiumSchedule { id: next_schedule_id, - owner: owner.clone(), + owner: caller.clone(), policy_id, next_due, interval, @@ -1216,8 +959,6 @@ impl Insurance { policy.schedule_id = Some(next_schedule_id); - client.create_policy(&owner, &name, &coverage_type, &-100, &10000, &None); - } schedules.set(next_schedule_id, schedule); env.storage() .instance() @@ -1233,7 +974,7 @@ impl Insurance { env.events().publish( (symbol_short!("insure"), InsuranceEvent::ScheduleCreated), - (next_schedule_id, owner), + (next_schedule_id, caller), ); Ok(next_schedule_id) @@ -1264,7 +1005,6 @@ impl Insurance { .get(&symbol_short!("PREM_SCH")) .unwrap_or_else(|| Map::new(&env)); - let mut schedule = schedules.get(schedule_id).unwrap_or_else(|| panic!("Schedule not found")); let mut schedule = schedules .get(schedule_id) .ok_or(InsuranceError::PolicyNotFound)?; @@ -1307,7 +1047,6 @@ impl Insurance { .get(&symbol_short!("PREM_SCH")) .unwrap_or_else(|| Map::new(&env)); - let mut schedule = schedules.get(schedule_id).unwrap_or_else(|| panic!("Schedule not found")); let mut schedule = schedules .get(schedule_id) .ok_or(InsuranceError::PolicyNotFound)?; @@ -1486,11 +1225,13 @@ mod test_events { assert_eq!(page.next_cursor, 0); } - client.create_policy(&owner, &name, &coverage_type, &100, &0, &None); #[test] fn test_get_active_policies_single_page() { let env = make_env(); env.mock_all_auths(); + let contract_id = env.register_contract(None, Insurance); + let client = InsuranceClient::new(&env, &contract_id); + let owner = Address::generate(&env); // Use the .try_ version of the function to capture the error result let result = client.try_create_policy( @@ -1554,30 +1295,7 @@ mod test_events { assert_eq!(page3.count, 1); assert_eq!(page3.next_cursor, 0); } - // Create a policy - let policy_id = client.create_policy( - &owner, - &String::from_str(&env, "Emergency Coverage"), - &String::from_str(&env, "emergency"), - &75, - &25000, - ); - env.mock_all_auths(); - - let name = String::from_str(&env, "Health Insurance"); - let coverage_type = String::from_str(&env, "health"); - let policy_id = client.create_policy(&owner, &name, &coverage_type, &100, &10000, &None); - let ids = setup_policies(&env, &client, &owner, 4); - // Deactivate policy #2 - client.deactivate_policy(&owner, &ids.get(1).unwrap()); - - let page = client.get_active_policies(&owner, &0, &10); - assert_eq!(page.count, 3); // only 3 active - for p in page.items.iter() { - assert!(p.active, "only active policies should be returned"); - } - } #[test] fn test_get_active_policies_multi_owner_isolation() { @@ -1638,42 +1356,9 @@ mod test_events { let client = InsuranceClient::new(&env, &contract_id); let owner = Address::generate(&env); - // Create multiple policies - let name1 = String::from_str(&env, "Health Insurance"); - let coverage_type1 = String::from_str(&env, "health"); - let policy_id1 = client.create_policy(&owner, &name1, &coverage_type1, &100, &10000, &None); - - let name2 = String::from_str(&env, "Emergency Insurance"); - let coverage_type2 = String::from_str(&env, "emergency"); - let policy_id2 = client.create_policy(&owner, &name2, &coverage_type2, &200, &20000, &None); - - let name3 = String::from_str(&env, "Life Insurance"); - let coverage_type3 = String::from_str(&env, "life"); - let policy_id3 = client.create_policy(&owner, &name3, &coverage_type3, &300, &30000, &None); - let policy_id = client.create_policy( - client.create_policy( - &owner, - &String::from_str(&env, "Health Insurance"), - &CoverageType::Health, - &String::from_str(&env, "Policy 1"), - &String::from_str(&env, "health"), - &100, - &50000, - ); - client.create_policy( - &owner, - &String::from_str(&env, "Policy 2"), - &String::from_str(&env, "life"), - &200, - &100000, - ); - client.create_policy( - &owner, - &String::from_str(&env, "Policy 3"), - &String::from_str(&env, "emergency"), - &75, - &25000, - ); + let name = String::from_str(&env, "Health Insurance"); + let coverage_type = CoverageType::Health; + let policy_id = client.create_policy(&owner, &name, &coverage_type, &100, &50000, &None); client.pay_premium(&owner, &policy_id); @@ -1883,8 +1568,6 @@ mod test_events { } #[test] - fn test_multiple_policies_management() { - let env = create_test_env(); fn test_policy_data_persists_across_ledger_advancements() { let env = Env::default(); env.mock_all_auths(); @@ -1913,19 +1596,7 @@ mod test_events { &75000, ); - for (i, policy_name) in policy_names.iter().enumerate() { - let premium = ((i + 1) as i128) * 100; - let coverage = ((i + 1) as i128) * 10000; - let policy_id = client.create_policy( - &owner, - policy_name, - &coverage_type, - &premium, - &coverage, - &None, - ); - policy_ids.push_back(policy_id); - } + // Phase 2: Advance to seq 510,000 (TTL = 8,500 < 17,280) env.ledger().set(LedgerInfo { protocol_version: 20, diff --git a/insurance/tests/stress_tests.rs b/insurance/tests/stress_tests.rs index 0063a6ad..4b001787 100644 --- a/insurance/tests/stress_tests.rs +++ b/insurance/tests/stress_tests.rs @@ -106,11 +106,18 @@ fn stress_200_policies_single_user() { cursor = page.next_cursor; } - assert_eq!(collected, 200, "Pagination must return all 200 active policies"); + assert_eq!( + collected, 200, + "Pagination must return all 200 active policies" + ); // get_active_policies sets next_cursor = last_returned_id; when a page is exactly // full the caller receives a non-zero cursor that produces a trailing empty page, // so the round-trip count is pages = ceil(200/50) + 1 trailing = 5. - assert!(pages >= 4 && pages <= 5, "Expected 4-5 pages for 200 policies at limit 50, got {}", pages); + assert!( + pages >= 4 && pages <= 5, + "Expected 4-5 pages for 200 policies at limit 50, got {}", + pages + ); } /// Create 200 policies and verify instance TTL remains valid after the instance diff --git a/integration_tests/Cargo.toml b/integration_tests/Cargo.toml index db4f4f5b..6b8c5882 100644 --- a/integration_tests/Cargo.toml +++ b/integration_tests/Cargo.toml @@ -5,11 +5,11 @@ edition = "2021" publish = false [dependencies] -soroban-sdk = "21.0.0" +soroban-sdk = "21.1.1" remittance_split = { path = "../remittance_split" } savings_goals = { path = "../savings_goals" } bill_payments = { path = "../bill_payments" } insurance = { path = "../insurance" } [dev-dependencies] -soroban-sdk = { version = "21.0.0", features = ["testutils"] } +soroban-sdk = { version = "21.1.1", features = ["testutils"] } diff --git a/integration_tests/tests/multi_contract_integration.rs b/integration_tests/tests/multi_contract_integration.rs index f035891e..91b7cf0f 100644 --- a/integration_tests/tests/multi_contract_integration.rs +++ b/integration_tests/tests/multi_contract_integration.rs @@ -266,7 +266,7 @@ fn test_multiple_entities_creation() { /// to the shared `RemitwiseEvents` helper. #[test] fn test_event_topic_compliance_across_contracts() { - use soroban_sdk::{symbol_short, Vec, IntoVal}; + use soroban_sdk::{symbol_short, IntoVal, Vec}; let env = Env::default(); env.mock_all_auths(); @@ -290,7 +290,12 @@ fn test_event_topic_compliance_across_contracts() { remittance_client.initialize_split(&user, &0u64, &40u32, &30u32, &20u32, &10u32); let goal_name = SorobanString::from_str(&env, "Compliance Goal"); - let _ = savings_client.create_goal(&user, &goal_name, &1000i128, &(env.ledger().timestamp() + 86400)); + let _ = savings_client.create_goal( + &user, + &goal_name, + &1000i128, + &(env.ledger().timestamp() + 86400), + ); let bill_name = SorobanString::from_str(&env, "Compliance Bill"); let _ = bills_client.create_bill( @@ -309,7 +314,10 @@ fn test_event_topic_compliance_across_contracts() { // Collect published events let events = env.events().all(); - assert!(events.len() > 0, "No events were emitted by the sample actions"); + assert!( + events.len() > 0, + "No events were emitted by the sample actions" + ); // Validate each event's topics conform to Remitwise schema let mut non_compliant = Vec::new(&env); @@ -317,7 +325,8 @@ fn test_event_topic_compliance_across_contracts() { for ev in events.iter() { let topics = &ev.1; // Expect topics to be a vector of length 4 starting with symbol_short!("Remitwise") - let ok = topics.len() == 4 && topics.get(0).unwrap() == symbol_short!("Remitwise").into_val(&env); + let ok = topics.len() == 4 + && topics.get(0).unwrap() == symbol_short!("Remitwise").into_val(&env); if !ok { non_compliant.push_back(ev.clone()); } diff --git a/orchestrator/Cargo.toml b/orchestrator/Cargo.toml index 82b00378..e1a28c66 100644 --- a/orchestrator/Cargo.toml +++ b/orchestrator/Cargo.toml @@ -7,10 +7,10 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -soroban-sdk = "21.0.0" +soroban-sdk = "21.1.1" [dev-dependencies] -soroban-sdk = { version = "21.0.0", features = ["testutils"] } +soroban-sdk = { version = "21.1.1", features = ["testutils"] } testutils = { path = "../testutils" } diff --git a/remittance_split/Cargo.toml b/remittance_split/Cargo.toml index d69e465c..6942cf54 100644 --- a/remittance_split/Cargo.toml +++ b/remittance_split/Cargo.toml @@ -7,8 +7,8 @@ edition = "2021" crate-type = ["cdylib", "rlib"] [dependencies] -soroban-sdk = "21.0.0" +soroban-sdk = "21.1.1" [dev-dependencies] -soroban-sdk = { version = "21.0.0", features = ["testutils"] } +soroban-sdk = { version = "21.1.1", features = ["testutils"] } testutils = { path = "../testutils" } diff --git a/remittance_split/src/test.rs b/remittance_split/src/test.rs index 93e1753c..86336aae 100644 --- a/remittance_split/src/test.rs +++ b/remittance_split/src/test.rs @@ -2,8 +2,8 @@ use super::*; use soroban_sdk::{ - testutils::{Address as AddressTrait, Events, Ledger}, testutils::storage::Instance as StorageInstance, + testutils::{Address as AddressTrait, Events, Ledger}, token::{StellarAssetClient, TokenClient}, Address, Env, Symbol, TryFromVal, }; @@ -15,7 +15,9 @@ use soroban_sdk::{ /// Register a native Stellar asset (SAC) and return (contract_id, admin). /// The admin is the issuer; we mint `amount` to `recipient`. fn setup_token(env: &Env, admin: &Address, recipient: &Address, amount: i128) -> Address { - let token_id = env.register_stellar_asset_contract_v2(admin.clone()).address(); + let token_id = env + .register_stellar_asset_contract_v2(admin.clone()) + .address(); let sac = StellarAssetClient::new(env, &token_id); sac.mint(recipient, &amount); token_id @@ -68,7 +70,10 @@ fn test_initialize_split_invalid_sum() { let token_id = setup_token(&env, &token_admin, &owner, 0); let result = client.try_initialize_split(&owner, &0, &token_id, &50, &50, &10, &0); - assert_eq!(result, Err(Ok(RemittanceSplitError::PercentagesDoNotSumTo100))); + assert_eq!( + result, + Err(Ok(RemittanceSplitError::PercentagesDoNotSumTo100)) + ); } #[test] @@ -163,7 +168,10 @@ fn test_update_split_percentages_must_sum_to_100() { client.initialize_split(&owner, &0, &token_id, &50, &30, &15, &5); let result = client.try_update_split(&owner, &1, &60, &30, &15, &5); - assert_eq!(result, Err(Ok(RemittanceSplitError::PercentagesDoNotSumTo100))); + assert_eq!( + result, + Err(Ok(RemittanceSplitError::PercentagesDoNotSumTo100)) + ); } // --------------------------------------------------------------------------- @@ -377,7 +385,10 @@ fn test_distribute_usdc_untrusted_token_rejected() { let evil_token = Address::generate(&env); let accounts = make_accounts(&env); let result = client.try_distribute_usdc(&evil_token, &owner, &1, &accounts, &1_000); - assert_eq!(result, Err(Ok(RemittanceSplitError::UntrustedTokenContract))); + assert_eq!( + result, + Err(Ok(RemittanceSplitError::UntrustedTokenContract)) + ); } // --------------------------------------------------------------------------- @@ -404,7 +415,10 @@ fn test_distribute_usdc_self_transfer_spending_rejected() { insurance: Address::generate(&env), }; let result = client.try_distribute_usdc(&token_id, &owner, &1, &accounts, &1_000); - assert_eq!(result, Err(Ok(RemittanceSplitError::SelfTransferNotAllowed))); + assert_eq!( + result, + Err(Ok(RemittanceSplitError::SelfTransferNotAllowed)) + ); } #[test] @@ -426,7 +440,10 @@ fn test_distribute_usdc_self_transfer_savings_rejected() { insurance: Address::generate(&env), }; let result = client.try_distribute_usdc(&token_id, &owner, &1, &accounts, &1_000); - assert_eq!(result, Err(Ok(RemittanceSplitError::SelfTransferNotAllowed))); + assert_eq!( + result, + Err(Ok(RemittanceSplitError::SelfTransferNotAllowed)) + ); } #[test] @@ -448,7 +465,10 @@ fn test_distribute_usdc_self_transfer_bills_rejected() { insurance: Address::generate(&env), }; let result = client.try_distribute_usdc(&token_id, &owner, &1, &accounts, &1_000); - assert_eq!(result, Err(Ok(RemittanceSplitError::SelfTransferNotAllowed))); + assert_eq!( + result, + Err(Ok(RemittanceSplitError::SelfTransferNotAllowed)) + ); } #[test] @@ -470,7 +490,10 @@ fn test_distribute_usdc_self_transfer_insurance_rejected() { insurance: owner.clone(), }; let result = client.try_distribute_usdc(&token_id, &owner, &1, &accounts, &1_000); - assert_eq!(result, Err(Ok(RemittanceSplitError::SelfTransferNotAllowed))); + assert_eq!( + result, + Err(Ok(RemittanceSplitError::SelfTransferNotAllowed)) + ); } // --------------------------------------------------------------------------- @@ -665,9 +688,9 @@ fn test_distribute_usdc_multiple_rounds() { let token = TokenClient::new(&env, &token_id); assert_eq!(token.balance(&accounts.spending), 1_500); // 3 * 500 - assert_eq!(token.balance(&accounts.savings), 900); // 3 * 300 - assert_eq!(token.balance(&accounts.bills), 450); // 3 * 150 - assert_eq!(token.balance(&accounts.insurance), 150); // 3 * 50 + assert_eq!(token.balance(&accounts.savings), 900); // 3 * 300 + assert_eq!(token.balance(&accounts.bills), 450); // 3 * 150 + assert_eq!(token.balance(&accounts.insurance), 150); // 3 * 50 assert_eq!(token.balance(&owner), 0); } @@ -861,5 +884,8 @@ fn test_instance_ttl_extended_on_initialize_split() { client.initialize_split(&owner, &0, &token_id, &50, &30, &15, &5); let ttl = env.as_contract(&contract_id, || env.storage().instance().get_ttl()); - assert!(ttl >= 518_400, "TTL must be >= INSTANCE_BUMP_AMOUNT after init"); + assert!( + ttl >= 518_400, + "TTL must be >= INSTANCE_BUMP_AMOUNT after init" + ); } diff --git a/remittance_split/tests/fuzz_tests.rs b/remittance_split/tests/fuzz_tests.rs index 7150aaf9..aa6cd358 100644 --- a/remittance_split/tests/fuzz_tests.rs +++ b/remittance_split/tests/fuzz_tests.rs @@ -81,7 +81,11 @@ fn fuzz_calculate_split_sum_preservation() { let amounts = client.calculate_split(&total_amount); let sum: i128 = amounts.iter().sum(); - assert_eq!(sum, total_amount, "Sum mismatch for percentages {}%/{}%/{}%/{}%", sp, sg, sb, si); + assert_eq!( + sum, total_amount, + "Sum mismatch for percentages {}%/{}%/{}%/{}%", + sp, sg, sb, si + ); assert!(amounts.iter().all(|a| a >= 0), "Negative amount detected"); } } @@ -126,7 +130,11 @@ fn fuzz_rounding_behavior() { for amount in &[100i128, 1000, 9999, 123456] { let amounts = client.calculate_split(amount); let sum: i128 = amounts.iter().sum(); - assert_eq!(sum, *amount, "Rounding error for amount {} with {}%/{}%/{}%/{}%", amount, sp, sg, sb, si); + assert_eq!( + sum, *amount, + "Rounding error for amount {} with {}%/{}%/{}%/{}%", + amount, sp, sg, sb, si + ); } } } @@ -167,7 +175,11 @@ fn fuzz_invalid_percentages() { let total = sp + sg + sb + si; let result = try_init(&client, &env, &owner, sp, sg, sb, si); if total != 100 { - assert!(result.is_err(), "Expected error for percentages summing to {}", total); + assert!( + result.is_err(), + "Expected error for percentages summing to {}", + total + ); } } } @@ -182,7 +194,12 @@ fn fuzz_large_amounts() { init(&client, &env, &owner, 25, 25, 25, 25); - for amount in &[i128::MAX / 1000, i128::MAX / 100, 1_000_000_000_000i128, 999_999_999_999i128] { + for amount in &[ + i128::MAX / 1000, + i128::MAX / 100, + 1_000_000_000_000i128, + 999_999_999_999i128, + ] { if let Ok(_) = client.try_calculate_split(amount) { let amounts = client.calculate_split(amount); let sum: i128 = amounts.iter().sum(); @@ -213,9 +230,17 @@ fn fuzz_single_category_splits() { let sum: i128 = amounts.iter().sum(); assert_eq!(sum, 1000); - if sp == 100 { assert_eq!(amounts.get(0).unwrap(), 1000); } - if sg == 100 { assert_eq!(amounts.get(1).unwrap(), 1000); } - if sb == 100 { assert_eq!(amounts.get(2).unwrap(), 1000); } - if si == 100 { assert_eq!(amounts.get(3).unwrap(), 1000); } + if sp == 100 { + assert_eq!(amounts.get(0).unwrap(), 1000); + } + if sg == 100 { + assert_eq!(amounts.get(1).unwrap(), 1000); + } + if sb == 100 { + assert_eq!(amounts.get(2).unwrap(), 1000); + } + if si == 100 { + assert_eq!(amounts.get(3).unwrap(), 1000); + } } } diff --git a/remittance_split/tests/stress_test_large_amounts.rs b/remittance_split/tests/stress_test_large_amounts.rs index 1de9d05d..355b0235 100644 --- a/remittance_split/tests/stress_test_large_amounts.rs +++ b/remittance_split/tests/stress_test_large_amounts.rs @@ -10,7 +10,15 @@ fn dummy_token(env: &Env) -> Address { Address::generate(env) } -fn init(client: &RemittanceSplitClient, env: &Env, owner: &Address, s: u32, g: u32, b: u32, i: u32) { +fn init( + client: &RemittanceSplitClient, + env: &Env, + owner: &Address, + s: u32, + g: u32, + b: u32, + i: u32, +) { let token = dummy_token(env); client.initialize_split(owner, &0, &token, &s, &g, &b, &i); } @@ -174,7 +182,13 @@ fn test_sequential_large_calculations() { init(&client, &env, &owner, 50, 30, 15, 5); - for amount in &[i128::MAX / 1000, i128::MAX / 500, i128::MAX / 200, i128::MAX / 150, i128::MAX / 100] { + for amount in &[ + i128::MAX / 1000, + i128::MAX / 500, + i128::MAX / 200, + i128::MAX / 150, + i128::MAX / 100, + ] { let result = client.try_calculate_split(amount); assert!(result.is_ok(), "Failed for amount: {}", amount); let splits = result.unwrap().unwrap(); @@ -195,7 +209,11 @@ fn test_checked_arithmetic_prevents_silent_overflow() { for amount in &[i128::MAX / 40, i128::MAX / 30, i128::MAX] { let result = client.try_calculate_split(amount); - assert!(result.is_err(), "Should have detected overflow for amount: {}", amount); + assert!( + result.is_err(), + "Should have detected overflow for amount: {}", + amount + ); } } diff --git a/remitwise-common/Cargo.toml b/remitwise-common/Cargo.toml index 27a93bcb..f803d981 100644 --- a/remitwise-common/Cargo.toml +++ b/remitwise-common/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" publish = false [dependencies] -soroban-sdk = "21.0.0" +soroban-sdk = "21.1.1" [lib] crate-type = ["cdylib", "rlib"] \ No newline at end of file diff --git a/reporting/Cargo.toml b/reporting/Cargo.toml index 8fe34d3d..57e6654d 100644 --- a/reporting/Cargo.toml +++ b/reporting/Cargo.toml @@ -7,9 +7,9 @@ edition = "2021" crate-type = ["cdylib", "rlib"] [dependencies] -soroban-sdk = "21.0.0" +soroban-sdk = "21.1.1" remitwise-common = { path = "../remitwise-common" } [dev-dependencies] -soroban-sdk = { version = "21.0.0", features = ["testutils"] } +soroban-sdk = { version = "21.1.1", features = ["testutils"] } testutils = { path = "../testutils" } diff --git a/reporting/src/lib.rs b/reporting/src/lib.rs index 189bd209..e5e1e1e1 100644 --- a/reporting/src/lib.rs +++ b/reporting/src/lib.rs @@ -294,14 +294,6 @@ pub struct PolicyPage { pub count: u32, } -#[contracttype] -#[derive(Clone)] -pub struct PolicyPage { - pub items: Vec, - pub next_cursor: u32, - pub count: u32, -} - #[contract] pub struct ReportingContract; diff --git a/reporting/src/tests.rs b/reporting/src/tests.rs index 91820601..bf13f5e1 100644 --- a/reporting/src/tests.rs +++ b/reporting/src/tests.rs @@ -1,4 +1,4 @@ -use testutils::{set_ledger_time}; +use testutils::set_ledger_time; // Mock contracts for testing mod remittance_split { diff --git a/savings_goals/Cargo.toml b/savings_goals/Cargo.toml index 0ffb4e5e..05f76dee 100644 --- a/savings_goals/Cargo.toml +++ b/savings_goals/Cargo.toml @@ -7,10 +7,10 @@ edition = "2021" crate-type = ["cdylib", "rlib"] [dependencies] -soroban-sdk = "21.0.0" +soroban-sdk = "21.1.1" [dev-dependencies] -soroban-sdk = { version = "21.0.0", features = ["testutils"] } +soroban-sdk = { version = "21.1.1", features = ["testutils"] } testutils = { path = "../testutils" } diff --git a/savings_goals/src/lib.rs b/savings_goals/src/lib.rs index 9ebdfc45..f1446b9c 100644 --- a/savings_goals/src/lib.rs +++ b/savings_goals/src/lib.rs @@ -88,7 +88,7 @@ pub struct SavingsSchedule { } #[contracttype] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub enum SavingsGoalsError { InvalidAmount = 1, GoalNotFound = 2, @@ -296,7 +296,9 @@ impl SavingsGoalContract { pub fn pause(env: Env, caller: Address) { caller.require_auth(); - let admin = Self::get_pause_admin(&env).ok_or(SavingsGoalsError::Unauthorized).unwrap(); + let admin = Self::get_pause_admin(&env) + .ok_or(SavingsGoalsError::Unauthorized) + .unwrap(); if admin != caller { panic!("Unauthorized"); } @@ -309,7 +311,9 @@ impl SavingsGoalContract { pub fn unpause(env: Env, caller: Address) { caller.require_auth(); - let admin = Self::get_pause_admin(&env).ok_or(SavingsGoalsError::Unauthorized).unwrap(); + let admin = Self::get_pause_admin(&env) + .ok_or(SavingsGoalsError::Unauthorized) + .unwrap(); if admin != caller { panic!("Unauthorized"); } @@ -329,7 +333,9 @@ impl SavingsGoalContract { pub fn pause_function(env: Env, caller: Address, func: Symbol) { caller.require_auth(); - let admin = Self::get_pause_admin(&env).ok_or(SavingsGoalsError::Unauthorized).unwrap(); + let admin = Self::get_pause_admin(&env) + .ok_or(SavingsGoalsError::Unauthorized) + .unwrap(); if admin != caller { panic!("Unauthorized"); } @@ -346,7 +352,9 @@ impl SavingsGoalContract { pub fn unpause_function(env: Env, caller: Address, func: Symbol) { caller.require_auth(); - let admin = Self::get_pause_admin(&env).ok_or(SavingsGoalsError::Unauthorized).unwrap(); + let admin = Self::get_pause_admin(&env) + .ok_or(SavingsGoalsError::Unauthorized) + .unwrap(); if admin != caller { panic!("Unauthorized"); } @@ -427,12 +435,7 @@ impl SavingsGoalContract { } } - pub fn add_tags_to_goal( - env: Env, - caller: Address, - goal_id: u32, - tags: Vec, - ) { + pub fn add_tags_to_goal(env: Env, caller: Address, goal_id: u32, tags: Vec) { caller.require_auth(); Self::validate_tags(&tags); Self::extend_instance_ttl(&env); @@ -467,12 +470,7 @@ impl SavingsGoalContract { Self::append_audit(&env, symbol_short!("add_tags"), &caller, true); } - pub fn remove_tags_from_goal( - env: Env, - caller: Address, - goal_id: u32, - tags: Vec, - ) { + pub fn remove_tags_from_goal(env: Env, caller: Address, goal_id: u32, tags: Vec) { caller.require_auth(); Self::validate_tags(&tags); Self::extend_instance_ttl(&env); @@ -740,12 +738,10 @@ impl SavingsGoalContract { if goal.owner != caller { panic!("Batch validation failed"); } - goal.current_amount = match goal - .current_amount - .checked_add(item.amount) { - Some(v) => v, - None => panic!("overflow"), - }; + goal.current_amount = match goal.current_amount.checked_add(item.amount) { + Some(v) => v, + None => panic!("overflow"), + }; let new_total = goal.current_amount; let was_completed = new_total >= goal.target_amount; let previously_completed = (new_total - item.amount) >= goal.target_amount; @@ -1033,7 +1029,9 @@ impl SavingsGoalContract { let mut result = Vec::new(&env); for i in start_index..end_index { - let goal_id = ids.get(i).unwrap_or_else(|| panic!("Pagination index out of sync")); + let goal_id = ids + .get(i) + .unwrap_or_else(|| panic!("Pagination index out of sync")); let goal = goals .get(goal_id) .unwrap_or_else(|| panic!("Pagination index out of sync")); @@ -1506,12 +1504,10 @@ impl SavingsGoalContract { } if let Some(mut goal) = goals.get(schedule.goal_id) { - goal.current_amount = match goal - .current_amount - .checked_add(schedule.amount) { - Some(v) => v, - None => panic!("overflow"), - }; + goal.current_amount = match goal.current_amount.checked_add(schedule.amount) { + Some(v) => v, + None => panic!("overflow"), + }; let is_completed = goal.current_amount >= goal.target_amount; goals.set(schedule.goal_id, goal.clone()); diff --git a/savings_goals/src/test.rs b/savings_goals/src/test.rs index 25153e33..cbc6d7cb 100644 --- a/savings_goals/src/test.rs +++ b/savings_goals/src/test.rs @@ -95,7 +95,11 @@ fn test_init_idempotent_does_not_wipe_goals() { assert_eq!(goal_after_second_init.current_amount, 0); let all_goals = client.get_all_goals(&owner_a); - assert_eq!(all_goals.len(), 1, "get_all_goals must still return the one goal"); + assert_eq!( + all_goals.len(), + 1, + "get_all_goals must still return the one goal" + ); // Verify NEXT_ID was not reset: next created goal must get goal_id == 2, not 1 let name2 = String::from_str(&env, "Second Goal"); @@ -1819,9 +1823,18 @@ fn test_get_all_goals_filters_by_owner() { // Verify goal IDs for owner_a are correct let goal_a_ids: Vec = goals_a.iter().map(|g| g.id).collect(); - assert!(goal_a_ids.contains(&goal_a1), "Goals for A should contain goal_a1"); - assert!(goal_a_ids.contains(&goal_a2), "Goals for A should contain goal_a2"); - assert!(goal_a_ids.contains(&goal_a3), "Goals for A should contain goal_a3"); + assert!( + goal_a_ids.contains(&goal_a1), + "Goals for A should contain goal_a1" + ); + assert!( + goal_a_ids.contains(&goal_a2), + "Goals for A should contain goal_a2" + ); + assert!( + goal_a_ids.contains(&goal_a3), + "Goals for A should contain goal_a3" + ); // Get all goals for owner_b let goals_b = client.get_all_goals(&owner_b); @@ -1838,8 +1851,14 @@ fn test_get_all_goals_filters_by_owner() { // Verify goal IDs for owner_b are correct let goal_b_ids: Vec = goals_b.iter().map(|g| g.id).collect(); - assert!(goal_b_ids.contains(&goal_b1), "Goals for B should contain goal_b1"); - assert!(goal_b_ids.contains(&goal_b2), "Goals for B should contain goal_b2"); + assert!( + goal_b_ids.contains(&goal_b1), + "Goals for B should contain goal_b1" + ); + assert!( + goal_b_ids.contains(&goal_b2), + "Goals for B should contain goal_b2" + ); // Verify that goal IDs between owner_a and owner_b are disjoint for goal_a_id in &goal_a_ids { diff --git a/savings_goals/tests/stress_tests.rs b/savings_goals/tests/stress_tests.rs index 85569330..714e1018 100644 --- a/savings_goals/tests/stress_tests.rs +++ b/savings_goals/tests/stress_tests.rs @@ -82,7 +82,11 @@ fn stress_200_goals_single_user() { // Verify via get_all_goals (unbounded) let all_goals = client.get_all_goals(&owner); - assert_eq!(all_goals.len(), 200, "get_all_goals must return all 200 goals"); + assert_eq!( + all_goals.len(), + 200, + "get_all_goals must return all 200 goals" + ); // Verify via paginated get_goals (MAX_PAGE_LIMIT = 50 → 4 pages) let mut collected = 0u32; @@ -103,11 +107,18 @@ fn stress_200_goals_single_user() { cursor = page.next_cursor; } - assert_eq!(collected, 200, "Paginated get_goals must return all 200 goals"); + assert_eq!( + collected, 200, + "Paginated get_goals must return all 200 goals" + ); // get_goals sets next_cursor = last_returned_id; when a page is exactly full the // caller receives a non-zero cursor that produces a trailing empty page, so the // number of round-trips is pages = ceil(200/50) + 1 trailing = 5. - assert!(pages >= 4 && pages <= 5, "Expected 4-5 pages for 200 goals at limit 50, got {}", pages); + assert!( + pages >= 4 && pages <= 5, + "Expected 4-5 pages for 200 goals at limit 50, got {}", + pages + ); } /// Create 200 goals and verify instance TTL stays valid after the instance Map @@ -404,7 +415,10 @@ fn stress_data_persists_across_multiple_ledger_advancements() { // TTL must still be positive let ttl = env.as_contract(&contract_id, || env.storage().instance().get_ttl()); - assert!(ttl > 0, "Instance TTL must be > 0 after all ledger advancements"); + assert!( + ttl > 0, + "Instance TTL must be > 0 after all ledger advancements" + ); } // --------------------------------------------------------------------------- diff --git a/scenarios/Cargo.toml b/scenarios/Cargo.toml index e7a4f5d0..0257f73b 100644 --- a/scenarios/Cargo.toml +++ b/scenarios/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" publish = false [dependencies] -soroban-sdk = { version = "21.0.0", features = ["testutils"] } +soroban-sdk = { version = "21.1.1", features = ["testutils"] } testutils = { path = "../testutils" } remittance_split = { path = "../remittance_split" } savings_goals = { path = "../savings_goals" } diff --git a/scenarios/src/lib.rs b/scenarios/src/lib.rs index 57b22e96..5bed3a83 100644 --- a/scenarios/src/lib.rs +++ b/scenarios/src/lib.rs @@ -1,8 +1,8 @@ pub mod tests { - use soroban_sdk::Env; - use testutils::set_ledger_time; use soroban_sdk::testutils::{Ledger, LedgerInfo}; use soroban_sdk::Env; + use soroban_sdk::Env; + use testutils::set_ledger_time; pub fn setup_env() -> Env { let env = Env::default(); diff --git a/testutils/Cargo.toml b/testutils/Cargo.toml index 4851acc0..b447871a 100644 --- a/testutils/Cargo.toml +++ b/testutils/Cargo.toml @@ -5,4 +5,4 @@ edition = "2021" publish = false [dependencies] -soroban-sdk = { version = "21.0.0", features = ["testutils"] } +soroban-sdk = { version = "21.1.1", features = ["testutils"] }