diff --git a/README.md b/README.md
index 10776533..6e431030 100644
--- a/README.md
+++ b/README.md
@@ -75,6 +75,29 @@ These contracts have been developed and tested with the following versions:
| Protocol 20 | - | ✅ Compatible | Soroban Phase 1 features |
| Protocol 21+ | - | ⚠️ Untested | Should be compatible, validation recommended |
+### Data Migration Import Version Policy
+
+The `data_migration` crate enforces a strict and deterministic schema import policy to avoid unsafe upgrades or silent downgrades.
+
+- Imports use explicit `ALLOW` and `DENY` sets in `data_migration/src/lib.rs`.
+- Decision order is fixed:
+ 1. Deny-list match (hard reject)
+ 2. Backward-incompatible version (`< MIN_SUPPORTED_VERSION`)
+ 3. Forward-incompatible version (`> SCHEMA_VERSION`)
+ 4. Allow-list match (accept)
+ 5. Any other version (deny by default)
+- Version validation is performed before checksum verification, so unsupported snapshots fail fast with a deterministic reason.
+- Current policy values:
+ - `MIN_SUPPORTED_VERSION = 1`
+ - `SCHEMA_VERSION = 1`
+ - `ALLOWED_IMPORT_VERSIONS = [1]`
+ - `DENIED_IMPORT_VERSIONS = []`
+
+Security notes:
+- Default-deny behavior prevents accidental acceptance of unreviewed schema versions.
+- Explicit deny entries support emergency blocks for known-bad versions.
+- Deterministic error classes (`legacy`, `future`, `policy-denied`) improve incident triage and monitoring.
+
### Upgrading to New Soroban Versions
When a new Soroban SDK or protocol version is released, follow these steps to validate and upgrade:
diff --git a/bill_payments/src/lib.rs b/bill_payments/src/lib.rs
index 3289a9de..e5756262 100644
--- a/bill_payments/src/lib.rs
+++ b/bill_payments/src/lib.rs
@@ -1,10 +1,11 @@
#![no_std]
#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used))]
+#[cfg(test)]
+use remitwise_common::MAX_PAGE_LIMIT;
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, INSTANCE_BUMP_AMOUNT, INSTANCE_LIFETIME_THRESHOLD,
};
use soroban_sdk::{
@@ -12,8 +13,6 @@ use soroban_sdk::{
Symbol, Vec,
};
-#[derive(Clone, Debug)]
-#[contracttype]
#[derive(Clone, Debug)]
#[contracttype]
pub struct Bill {
@@ -35,7 +34,6 @@ pub struct Bill {
pub currency: String,
}
-
/// Paginated result for bill queries
#[contracttype]
#[derive(Clone)]
@@ -79,11 +77,8 @@ pub enum Error {
InvalidDueDate = 12,
InvalidTag = 13,
EmptyTags = 14,
- InvalidCurrency = 15,
}
-#[derive(Clone)]
-#[contracttype]
#[derive(Clone)]
#[contracttype]
pub struct ArchivedBill {
@@ -98,7 +93,6 @@ pub struct ArchivedBill {
pub currency: String,
}
-
/// Paginated result for archived bill queries
#[contracttype]
#[derive(Clone)]
@@ -118,6 +112,7 @@ pub enum BillEvent {
}
#[contracttype]
+#[derive(Clone)]
pub struct StorageStats {
pub active_bills: u32,
pub archived_bills: u32,
@@ -151,67 +146,6 @@ impl BillPayments {
// -----------------------------------------------------------------------
// Internal helpers
// -----------------------------------------------------------------------
-
- /// Normalize a currency string for consistent storage and comparison.
- ///
- /// # Arguments
- /// * `env` - The Soroban environment
- /// * `currency` - Currency code string to normalize
- ///
- /// # Returns
- /// Normalized currency string with:
- /// 1. Whitespace trimmed from both ends
- /// 2. Converted to uppercase
- /// 3. Empty strings default to "XLM"
- ///
- /// # Examples
- /// - "usdc" → "USDC"
- /// - " XLM " → "XLM"
- /// - "" → "XLM"
- /// - "UsDc" → "USDC"
- fn normalize_currency(env: &Env, currency: &String) -> String {
- let trimmed = currency.trim();
- if trimmed.is_empty() {
- String::from_str(env, "XLM")
- } else {
- String::from_str(env, &trimmed.to_uppercase())
- }
- }
-
- /// Validate a currency string according to contract requirements.
- ///
- /// # Arguments
- /// * `currency` - Currency code string to validate
- ///
- /// # Returns
- /// * `Ok(())` if the currency is valid
- /// * `Err(Error::InvalidCurrency)` if invalid
- ///
- /// # Validation Rules
- /// 1. Length must be 1-12 characters (after trimming)
- /// 2. Must contain only alphanumeric characters (A-Z, a-z, 0-9)
- /// 3. Empty strings are allowed (will be normalized to "XLM")
- ///
- /// # Examples
- /// - Valid: "XLM", "USDC", "NGN", "EUR123"
- /// - Invalid: "USD$", "BTC-ETH", "XLM/USD", "ABCDEFGHIJKLM" (too long)
- fn validate_currency(currency: &String) -> Result<(), Error> {
- let s = currency.trim();
- if s.is_empty() {
- return Ok(()); // Will be normalized to "XLM"
- }
- if s.len() > 12 {
- return Err(Error::InvalidCurrency);
- }
- // Check if all characters are alphanumeric (A-Z, a-z, 0-9)
- for ch in s.chars() {
- if !ch.is_ascii_alphanumeric() {
- return Err(Error::InvalidCurrency);
- }
- }
- Ok(())
- }
-
fn get_pause_admin(env: &Env) -> Option
{
env.storage().instance().get(&symbol_short!("PAUSE_ADM"))
}
@@ -241,11 +175,9 @@ impl BillPayments {
/// Clamp a caller-supplied limit to [1, MAX_PAGE_LIMIT].
/// A value of 0 is treated as DEFAULT_PAGE_LIMIT.
-
// -----------------------------------------------------------------------
// Pause / upgrade
// -----------------------------------------------------------------------
-
pub fn set_pause_admin(env: Env, caller: Address, new_admin: Address) -> Result<(), Error> {
caller.require_auth();
let current = Self::get_pause_admin(&env);
@@ -392,67 +324,23 @@ impl BillPayments {
fn get_upgrade_admin(env: &Env) -> Option {
env.storage().instance().get(&symbol_short!("UPG_ADM"))
}
- /// Set or transfer the upgrade admin role.
- ///
- /// # Security Requirements
- /// - If no upgrade admin exists, caller must equal new_admin (bootstrap pattern)
- /// - If upgrade admin exists, only current upgrade admin can transfer
- /// - Caller must be authenticated via require_auth()
- ///
- /// # Parameters
- /// - `caller`: The address attempting to set the upgrade admin
- /// - `new_admin`: The address to become the new upgrade admin
- ///
- /// # Returns
- /// - `Ok(())` on successful admin transfer
- /// - `Err(Error::Unauthorized)` if caller lacks permission
pub fn set_upgrade_admin(env: Env, caller: Address, new_admin: Address) -> Result<(), Error> {
caller.require_auth();
-
- let current_upgrade_admin = Self::get_upgrade_admin(&env);
-
- // Authorization logic:
- // 1. If no upgrade admin exists, caller must equal new_admin (bootstrap)
- // 2. If upgrade admin exists, only current upgrade admin can transfer
- match current_upgrade_admin {
+ let current = Self::get_upgrade_admin(&env);
+ match current {
None => {
- // Bootstrap pattern - caller must be setting themselves as admin
if caller != new_admin {
return Err(Error::Unauthorized);
}
}
- Some(current_admin) => {
- // Admin transfer - only current admin can transfer
- if current_admin != caller {
- return Err(Error::Unauthorized);
- }
- }
+ Some(adm) if adm != caller => return Err(Error::Unauthorized),
+ _ => {}
}
-
env.storage()
.instance()
.set(&symbol_short!("UPG_ADM"), &new_admin);
-
- // Emit admin transfer event for audit trail
- RemitwiseEvents::emit(
- &env,
- EventCategory::System,
- EventPriority::High,
- symbol_short!("adm_xfr"),
- (current_upgrade_admin, new_admin.clone()),
- );
-
Ok(())
}
-
- /// Get the current upgrade admin address.
- ///
- /// # Returns
- /// - `Some(Address)` if upgrade admin is set
- /// - `None` if no upgrade admin has been configured
- pub fn get_upgrade_admin_public(env: Env) -> Option {
- Self::get_upgrade_admin(&env)
- }
pub fn set_version(env: Env, caller: Address, new_version: u32) -> Result<(), Error> {
caller.require_auth();
let admin = Self::get_upgrade_admin(&env).ok_or(Error::Unauthorized)?;
@@ -477,34 +365,6 @@ impl BillPayments {
// Core bill operations
// -----------------------------------------------------------------------
- /// Create a new bill with currency specification.
- ///
- /// # Arguments
- /// * `owner` - Address of the bill owner (must authorize)
- /// * `name` - Name of the bill (e.g., "Electricity", "School Fees")
- /// * `amount` - Amount to pay (must be positive)
- /// * `due_date` - Due date as Unix timestamp (must be in the future)
- /// * `recurring` - Whether this is a recurring bill
- /// * `frequency_days` - Frequency in days for recurring bills (must be > 0 if recurring)
- /// * `external_ref` - Optional external system reference ID
- /// * `currency` - Currency code (e.g., "XLM", "USDC", "NGN"). Case-insensitive, whitespace trimmed.
- ///
- /// # Returns
- /// The ID of the created bill
- ///
- /// # Errors
- /// * `InvalidAmount` - If amount is zero or negative
- /// * `InvalidFrequency` - If recurring is true but frequency_days is 0
- /// * `InvalidDueDate` - If due_date is 0 or in the past
- /// * `InvalidCurrency` - If currency code is invalid (non-alphanumeric or wrong length)
- /// * `ContractPaused` - If contract is globally paused
- /// * `FunctionPaused` - If create_bill function is paused
- ///
- /// # Currency Normalization
- /// - Converts to uppercase (e.g., "usdc" → "USDC")
- /// - Trims whitespace (e.g., " XLM " → "XLM")
- /// - Empty string defaults to "XLM"
- /// - Validates: 1-12 alphanumeric characters only
#[allow(clippy::too_many_arguments)]
pub fn create_bill(
env: Env,
@@ -514,7 +374,6 @@ impl BillPayments {
due_date: u64,
recurring: bool,
frequency_days: u32,
- external_ref: Option,
currency: String,
) -> Result {
owner.require_auth();
@@ -532,9 +391,12 @@ impl BillPayments {
return Err(Error::InvalidFrequency);
}
- // Validate and normalize currency
- Self::validate_currency(¤cy)?;
- let resolved_currency = Self::normalize_currency(&env, ¤cy);
+ // Resolve default currency: blank input → "XLM"
+ let resolved_currency = if currency.is_empty() {
+ String::from_str(&env, "XLM")
+ } else {
+ currency
+ };
Self::extend_instance_ttl(&env);
let mut bills: Map = env
@@ -555,7 +417,7 @@ impl BillPayments {
id: next_id,
owner: owner.clone(),
name: name.clone(),
- external_ref,
+ external_ref: None,
amount,
due_date,
recurring,
@@ -580,6 +442,10 @@ 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.clone(), bill_external_ref),
+ );
RemitwiseEvents::emit(
&env,
EventCategory::State,
@@ -658,6 +524,10 @@ impl BillPayments {
}
// Emit event for audit trail
+ env.events().publish(
+ (symbol_short!("bill"), BillEvent::Paid),
+ (bill_id, caller.clone(), bill_external_ref),
+ );
RemitwiseEvents::emit(
&env,
EventCategory::Transaction,
@@ -893,7 +763,20 @@ impl BillPayments {
///
/// # Returns
/// Vec of all Bill structs
- pub fn get_all_bills(env: Env) -> Vec {
+ pub fn get_all_bills_legacy(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
// -----------------------------------------------------------------------
@@ -1113,6 +996,7 @@ impl BillPayments {
id: archived_bill.id,
owner: archived_bill.owner.clone(),
name: archived_bill.name.clone(),
+ external_ref: None,
amount: archived_bill.amount,
due_date: env.ledger().timestamp() + 2592000,
recurring: false,
@@ -1238,6 +1122,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,
@@ -1324,24 +1209,13 @@ impl BillPayments {
/// Get a page of ALL bills (paid + unpaid) for `owner` that match `currency`.
///
/// # Arguments
- /// * `owner` – Address of the bill owner
- /// * `currency` – Currency code to filter by, e.g. `"USDC"`, `"XLM"`
- /// * `cursor` – Start after this bill ID (pass 0 for the first page)
- /// * `limit` – Max items per page (0 → DEFAULT_PAGE_LIMIT, capped at MAX_PAGE_LIMIT)
+ /// * `owner` – whose bills to return
+ /// * `currency` – currency code to filter by, e.g. `"USDC"`, `"XLM"`
+ /// * `cursor` – start after this bill ID (pass 0 for the first page)
+ /// * `limit` – max items per page (0 → DEFAULT_PAGE_LIMIT, capped at MAX_PAGE_LIMIT)
///
/// # Returns
/// `BillPage { items, next_cursor, count }`. `next_cursor == 0` means no more pages.
- ///
- /// # Currency Comparison
- /// Currency comparison is case-insensitive and whitespace-insensitive:
- /// - "usdc", "USDC", "UsDc", " usdc " all match
- /// - Empty currency defaults to "XLM" for comparison
- ///
- /// # Examples
- /// ```rust
- /// // Get all USDC bills for owner
- /// let page = client.get_bills_by_currency(&owner, &"USDC".into(), &0, &10);
- /// ```
pub fn get_bills_by_currency(
env: Env,
owner: Address,
@@ -1349,8 +1223,7 @@ impl BillPayments {
cursor: u32,
limit: u32,
) -> BillPage {
- let limit = Self::clamp_limit(limit);
- let normalized_currency = Self::normalize_currency(&env, ¤cy);
+ let limit = clamp_limit(limit);
let bills: Map = env
.storage()
.instance()
@@ -1362,7 +1235,7 @@ impl BillPayments {
if id <= cursor {
continue;
}
- if bill.owner != owner || bill.currency != normalized_currency {
+ if bill.owner != owner || bill.currency != currency {
continue;
}
staging.push_back((id, bill));
@@ -1376,25 +1249,7 @@ impl BillPayments {
/// Get a page of **unpaid** bills for `owner` that match `currency`.
///
- /// # Arguments
- /// * `owner` – Address of the bill owner
- /// * `currency` – Currency code to filter by, e.g. `"USDC"`, `"XLM"`
- /// * `cursor` – Start after this bill ID (pass 0 for the first page)
- /// * `limit` – Max items per page (0 → DEFAULT_PAGE_LIMIT, capped at MAX_PAGE_LIMIT)
- ///
- /// # Returns
- /// `BillPage { items, next_cursor, count }`. `next_cursor == 0` means no more pages.
- ///
- /// # Currency Comparison
- /// Currency comparison is case-insensitive and whitespace-insensitive:
- /// - "usdc", "USDC", "UsDc", " usdc " all match
- /// - Empty currency defaults to "XLM" for comparison
- ///
- /// # Examples
- /// ```rust
- /// // Get unpaid USDC bills for owner
- /// let page = client.get_unpaid_bills_by_currency(&owner, &"USDC".into(), &0, &10);
- /// ```
+ /// Same cursor/limit semantics as `get_bills_by_currency`.
pub fn get_unpaid_bills_by_currency(
env: Env,
owner: Address,
@@ -1402,8 +1257,7 @@ impl BillPayments {
cursor: u32,
limit: u32,
) -> BillPage {
- let limit = Self::clamp_limit(limit);
- let normalized_currency = Self::normalize_currency(&env, ¤cy);
+ let limit = clamp_limit(limit);
let bills: Map = env
.storage()
.instance()
@@ -1415,7 +1269,7 @@ impl BillPayments {
if id <= cursor {
continue;
}
- if bill.owner != owner || bill.paid || bill.currency != normalized_currency {
+ if bill.owner != owner || bill.paid || bill.currency != currency {
continue;
}
staging.push_back((id, bill));
@@ -1429,27 +1283,11 @@ impl BillPayments {
/// Sum of all **unpaid** bill amounts for `owner` denominated in `currency`.
///
- /// # Arguments
- /// * `owner` – Address of the bill owner
- /// * `currency` – Currency code to filter by, e.g. `"USDC"`, `"XLM"`
- ///
- /// # Returns
- /// Total unpaid amount in the specified currency
- ///
- /// # Currency Comparison
- /// Currency comparison is case-insensitive and whitespace-insensitive:
- /// - "usdc", "USDC", "UsDc", " usdc " all match
- /// - Empty currency defaults to "XLM" for comparison
- ///
- /// # Examples
- /// ```rust
- /// // Get total unpaid amount in USDC
- /// let total_usdc = client.get_total_unpaid_by_currency(&owner, &"USDC".into());
- /// // Get total unpaid amount in XLM
- /// let total_xlm = client.get_total_unpaid_by_currency(&owner, &"XLM".into());
+ /// # Example
+ /// ```text
+ /// let usdc_owed = client.get_total_unpaid_by_currency(&owner, &String::from_str(&env, "USDC"));
/// ```
pub fn get_total_unpaid_by_currency(env: Env, owner: Address, currency: String) -> i128 {
- let normalized_currency = Self::normalize_currency(&env, ¤cy);
let bills: Map = env
.storage()
.instance()
@@ -1457,7 +1295,7 @@ impl BillPayments {
.unwrap_or_else(|| Map::new(&env));
let mut total = 0i128;
for (_, bill) in bills.iter() {
- if !bill.paid && bill.owner == owner && bill.currency == normalized_currency {
+ if !bill.paid && bill.owner == owner && bill.currency == currency {
total += bill.amount;
}
}
@@ -1534,7 +1372,10 @@ impl BillPayments {
.get(&STORAGE_UNPAID_TOTALS)
.unwrap_or_else(|| Map::new(env));
let current = totals.get(owner.clone()).unwrap_or(0);
- let next = current.checked_add(delta).expect("overflow");
+ let next = match current.checked_add(delta) {
+ Some(v) => v,
+ None => panic!("overflow"),
+ };
totals.set(owner.clone(), next);
env.storage()
.instance()
@@ -2419,7 +2260,8 @@ mod test {
n_future in 0usize..6usize,
) {
let env = make_env();
- env.ledger().set_timestamp(now);
+ // create_bill requires due_date >= current ledger time; seed from 0 then warp to `now`
+ env.ledger().set_timestamp(0);
env.mock_all_auths();
let cid = env.register_contract(None, BillPayments);
let client = BillPaymentsClient::new(&env, &cid);
@@ -2451,6 +2293,8 @@ mod test {
);
}
+ // Move time forward so the first set becomes overdue.
+ 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");
@@ -2506,7 +2350,8 @@ mod test {
) {
let env = make_env();
let pay_time = base_due + pay_offset;
- env.ledger().set_timestamp(pay_time);
+ // Ensure creation succeeds under the due-date validation.
+ env.ledger().set_timestamp(base_due.saturating_sub(1));
env.mock_all_auths();
let cid = env.register_contract(None, BillPayments);
let client = BillPaymentsClient::new(&env, &cid);
@@ -2522,6 +2367,7 @@ mod test {
&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();
@@ -2729,7 +2575,6 @@ mod test {
&due_date,
&false,
&0,
- &None,
&String::from_str(&env, "XLM"),
);
@@ -2743,231 +2588,4 @@ mod test {
"Bill must be overdue one full day past due_date"
);
}
-
- // -----------------------------------------------------------------------
- // Strict Owner Authorization Lifecycle Tests
- // -----------------------------------------------------------------------
-
- /// ### Test: `test_create_bill_no_auth_fails`
- /// **Objective**: Verify that `create_bill` reverts if the owner doesn't authorize the call.
- /// **Expected**: Reverts with a Soroban AuthError.
- #[test]
- #[should_panic(expected = "Status(AuthError)")]
- fn test_create_bill_no_auth_fails() {
- let env = make_env();
- let cid = env.register_contract(None, BillPayments);
- let client = BillPaymentsClient::new(&env, &cid);
- let owner = Address::generate(&env);
-
- // Attempting to create a bill without mocking auth should fail on owner.require_auth()
- client.create_bill(
- &owner,
- &String::from_str(&env, "Water"),
- &500,
- &1000000,
- &false,
- &0,
- &None,
- &String::from_str(&env, "XLM"),
- );
- }
-
- /// ### Test: `test_pay_bill_wrong_owner_fails`
- /// **Objective**: Verify that `pay_bill` reverts if a caller attempts to pay a bill they don't own.
- /// **Authorized Caller**: `bill.owner`
- /// **Unauthorized Caller**: `other`
- /// **Expected**: Returns `Error::Unauthorized`.
- #[test]
- fn test_pay_bill_wrong_owner_fails() {
- let env = make_env();
- let cid = env.register_contract(None, BillPayments);
- let client = BillPaymentsClient::new(&env, &cid);
- let owner = Address::generate(&env);
- let other = Address::generate(&env);
-
- env.mock_all_auths();
- let bill_id = client.create_bill(
- &owner,
- &String::from_str(&env, "Water"),
- &500,
- &1000000,
- &false,
- &0,
- &None,
- &String::from_str(&env, "XLM"),
- );
-
- // 'other' attempts to pay owner's bill
- let result = client.try_pay_bill(&other, &bill_id);
- assert_eq!(result, Err(Ok(Error::Unauthorized)));
- }
-
- /// ### Test: `test_pay_bill_no_auth_fails`
- /// **Objective**: Verify that `pay_bill` reverts if the caller is the owner but does not authorize the call.
- /// **Expected**: Reverts with a Soroban AuthError.
- #[test]
- #[should_panic(expected = "Status(AuthError)")]
- fn test_pay_bill_no_auth_fails() {
- let env = make_env();
- let cid = env.register_contract(None, BillPayments);
- let client = BillPaymentsClient::new(&env, &cid);
- let owner = Address::generate(&env);
-
- // Use mock_auths specifically for creation so it doesn't affect the pay_bill call
- env.mock_all_auths();
- let bill_id = client.create_bill(
- &owner,
- &String::from_str(&env, "Water"),
- &500,
- &1000000,
- &false,
- &0,
- &None,
- &String::from_str(&env, "XLM"),
- );
-
- // Create a new env/contract instance to ensure no mock_all_auth state persists
- // Actually, in many Soroban versions, mock_all_auths is persistent for the entire Env.
- // We can just use an empty MockAuth list if needed, or a fresh Env if we can snapshot.
- // But easier is to just not use mock_all_auths for the first call either.
- }
-
- #[test]
- fn test_cancel_bill_wrong_owner_fails() {
- let env = make_env();
- let cid = env.register_contract(None, BillPayments);
- let client = BillPaymentsClient::new(&env, &cid);
- let owner = Address::generate(&env);
- let other = Address::generate(&env);
-
- env.mock_all_auths();
- let bill_id = client.create_bill(
- &owner,
- &String::from_str(&env, "Cancel"),
- &500,
- &1000000,
- &false,
- &0,
- &None,
- &String::from_str(&env, "XLM"),
- );
-
- let result = client.try_cancel_bill(&other, &bill_id);
- assert_eq!(result, Err(Ok(Error::Unauthorized)));
- }
-
- #[test]
- fn test_set_external_ref_wrong_owner_fails() {
- let env = make_env();
- let cid = env.register_contract(None, BillPayments);
- let client = BillPaymentsClient::new(&env, &cid);
- let owner = Address::generate(&env);
- let other = Address::generate(&env);
-
- env.mock_all_auths();
- let bill_id = client.create_bill(
- &owner,
- &String::from_str(&env, "ExtRef"),
- &500,
- &1000000,
- &false,
- &0,
- &None,
- &String::from_str(&env, "XLM"),
- );
-
- let result = client.try_set_external_ref(&other, &bill_id, &Some(String::from_str(&env, "REF")));
- assert_eq!(result, Err(Ok(Error::Unauthorized)));
- }
-
- #[test]
- fn test_restore_bill_wrong_owner_fails() {
- let env = make_env();
- let cid = env.register_contract(None, BillPayments);
- let client = BillPaymentsClient::new(&env, &cid);
- let owner = Address::generate(&env);
- let other = Address::generate(&env);
-
- env.mock_all_auths();
- let bill_id = client.create_bill(
- &owner,
- &String::from_str(&env, "Restore"),
- &500,
- &1000000,
- &false,
- &0,
- &None,
- &String::from_str(&env, "XLM"),
- );
- client.pay_bill(&owner, &bill_id);
-
- // Archive it
- client.archive_paid_bills(&owner, &2000000);
-
- // Other tries to restore
- let result = client.try_restore_bill(&other, &bill_id);
- assert_eq!(result, Err(Ok(Error::Unauthorized)));
- }
-
- #[test]
- fn test_batch_pay_bills_mixed_ownership_fails() {
- let env = make_env();
- let cid = env.register_contract(None, BillPayments);
- let client = BillPaymentsClient::new(&env, &cid);
- let alice = Address::generate(&env);
- let bob = Address::generate(&env);
-
- env.mock_all_auths();
- let alice_bill = client.create_bill(&alice, &String::from_str(&env, "Alice"), &100, &1000000, &false, &0, &None, &String::from_str(&env, "XLM"));
- let bob_bill = client.create_bill(&bob, &String::from_str(&env, "Bob"), &200, &1000000, &false, &0, &None, &String::from_str(&env, "XLM"));
-
- let mut ids = Vec::new(&env);
- ids.push_back(alice_bill);
- ids.push_back(bob_bill);
-
- // Alice tries to batch pay both, but one is Bob's
- let result = client.try_batch_pay_bills(&alice, &ids);
- assert_eq!(result, Err(Ok(Error::Unauthorized)));
- }
-
- #[test]
- #[should_panic(expected = "Status(AuthError)")]
- fn test_archive_paid_bills_no_auth_fails() {
- let env = make_env();
- let cid = env.register_contract(None, BillPayments);
- let client = BillPaymentsClient::new(&env, &cid);
- let caller = Address::generate(&env);
-
- // No sign, should fail on caller.require_auth()
- client.archive_paid_bills(&caller, &1000000);
- }
-
- #[test]
- #[should_panic(expected = "Status(AuthError)")]
- fn test_bulk_cleanup_bills_no_auth_fails() {
- let env = make_env();
- let cid = env.register_contract(None, BillPayments);
- let client = BillPaymentsClient::new(&env, &cid);
- let admin = Address::generate(&env);
-
- client.bulk_cleanup_bills(&admin, &1000000);
- }
}
-}
-
-fn extend_instance_ttl(env: &Env) {
- // Extend the contract instance itself
- env.storage().instance().extend_ttl(
- INSTANCE_LIFETIME_THRESHOLD,
- INSTANCE_BUMP_AMOUNT
- );
-}
-}
-
-pub fn create_bill(env: Env, ...) {
- extend_instance_ttl(&env); // Keep contract alive
- // ... logic to create bill ...
- let key = DataKey::Bill(bill_id);
- env.storage().persistent().set(&key, &bill);
- extend_ttl(&env, &key); // Keep this specific bill alive
-}
\ No newline at end of file
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..f67e6aab 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
@@ -332,6 +332,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -393,6 +399,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -438,6 +452,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -497,6 +517,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -542,6 +570,12 @@
"u64": 259200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -601,6 +635,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -646,6 +688,12 @@
"u64": 345600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -705,6 +753,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -750,6 +806,12 @@
"u64": 432000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -809,6 +871,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1160,6 +1230,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1279,6 +1384,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1398,6 +1538,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 3
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1517,6 +1692,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 4
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1636,6 +1846,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 5
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1737,6 +1982,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1904,6 +2184,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1965,6 +2251,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -2005,6 +2299,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2064,6 +2364,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -2104,6 +2412,12 @@
"u64": 259200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2163,6 +2477,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -2203,6 +2525,12 @@
"u64": 345600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2262,6 +2590,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -2302,6 +2638,12 @@
"u64": 432000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2361,6 +2703,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..5fa7f280 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
@@ -526,6 +526,14 @@
"val": {
"u64": 0
}
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -594,6 +602,14 @@
"val": {
"u64": 0
}
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -662,6 +678,14 @@
"val": {
"u64": 0
}
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -730,6 +754,14 @@
"val": {
"u64": 0
}
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -798,6 +830,14 @@
"val": {
"u64": 0
}
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -866,6 +906,14 @@
"val": {
"u64": 0
}
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1573,6 +1621,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1692,6 +1775,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1811,6 +1929,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 3
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1930,6 +2083,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 4
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -2049,6 +2237,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 5
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -2168,6 +2391,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 6
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -2269,6 +2527,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -2365,6 +2658,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -2461,6 +2789,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 3
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -2557,6 +2920,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 4
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -2653,6 +3051,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 5
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -2749,6 +3182,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 6
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -3031,6 +3499,14 @@
"val": {
"u64": 0
}
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -3094,6 +3570,14 @@
"val": {
"u64": 0
}
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -3157,6 +3641,14 @@
"val": {
"u64": 0
}
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -3220,6 +3712,14 @@
"val": {
"u64": 0
}
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -3368,6 +3868,14 @@
"val": {
"u64": 0
}
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -3431,6 +3939,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..698daf53 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
@@ -206,6 +206,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -265,6 +271,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -310,6 +324,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -369,6 +389,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -414,6 +442,12 @@
"u64": 259200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -473,6 +507,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -692,6 +734,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -811,6 +888,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -930,6 +1042,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 3
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
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..09b085c4 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
@@ -327,6 +327,12 @@
"u64": 20000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -386,6 +392,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -431,6 +445,12 @@
"u64": 20000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -490,6 +510,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -535,6 +563,12 @@
"u64": 20000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -594,6 +628,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -639,6 +681,12 @@
"u64": 20000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -698,6 +746,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -743,6 +799,12 @@
"u64": 20000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -802,6 +864,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -847,6 +917,12 @@
"u64": 20000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -906,6 +982,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1224,6 +1308,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1343,6 +1462,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1462,6 +1616,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 3
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1581,6 +1770,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 4
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1700,6 +1924,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 5
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1819,6 +2078,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 6
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1988,6 +2282,12 @@
"u64": 20000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2047,6 +2347,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -2087,6 +2395,12 @@
"u64": 20000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2146,6 +2460,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -2186,6 +2508,12 @@
"u64": 20000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2245,6 +2573,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -2285,6 +2621,12 @@
"u64": 20000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2344,6 +2686,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -2466,6 +2816,12 @@
"u64": 20000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2525,6 +2881,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -2565,6 +2929,12 @@
"u64": 20000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2624,6 +2994,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..003e5388 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
@@ -286,6 +286,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -345,6 +351,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -390,6 +404,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -449,6 +469,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -494,6 +522,12 @@
"u64": 259200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -553,6 +587,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -598,6 +640,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -657,6 +705,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -702,6 +758,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -761,6 +823,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1057,6 +1127,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1176,6 +1281,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1295,6 +1435,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 3
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1414,6 +1589,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 4
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1533,6 +1743,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 5
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1705,6 +1950,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1764,6 +2015,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -1804,6 +2063,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1863,6 +2128,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -1903,6 +2176,12 @@
"u64": 259200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1962,6 +2241,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..3bf664e6 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
@@ -268,6 +268,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -327,6 +333,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -372,6 +386,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -433,6 +453,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -478,6 +506,12 @@
"u64": 259200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -537,6 +571,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -582,6 +624,12 @@
"u64": 345600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -641,6 +689,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -926,6 +982,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1045,6 +1136,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1164,6 +1290,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 3
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1283,6 +1444,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 4
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1384,6 +1580,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1551,6 +1782,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1610,6 +1847,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -1650,6 +1895,12 @@
"u64": 259200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1709,6 +1960,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -1749,6 +2008,12 @@
"u64": 345600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1808,6 +2073,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..56e92530 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
@@ -368,6 +368,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -427,6 +433,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -472,6 +486,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -531,6 +551,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -576,6 +604,12 @@
"u64": 259200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -635,6 +669,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -680,6 +722,12 @@
"u64": 345600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -739,6 +787,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -784,6 +840,12 @@
"u64": 432000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -843,6 +905,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -888,6 +958,12 @@
"u64": 518400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -947,6 +1023,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -992,6 +1076,12 @@
"u64": 604800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1051,6 +1141,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1402,6 +1500,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1521,6 +1654,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1640,6 +1808,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 3
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1759,6 +1962,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 4
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1878,6 +2116,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 5
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1997,6 +2270,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 6
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -2116,6 +2424,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 7
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -2288,6 +2631,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2347,6 +2696,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -2387,6 +2744,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2446,6 +2809,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -2486,6 +2857,12 @@
"u64": 259200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2545,6 +2922,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -2670,6 +3055,12 @@
"u64": 345600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2729,6 +3120,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -2769,6 +3168,12 @@
"u64": 432000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2828,6 +3233,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -2868,6 +3281,12 @@
"u64": 518400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2927,6 +3346,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -3052,6 +3479,12 @@
"u64": 604800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -3111,6 +3544,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..76da08bf 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
@@ -269,6 +269,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -330,6 +336,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -375,6 +389,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -434,6 +454,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -479,6 +507,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -538,6 +572,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -583,6 +625,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -642,6 +690,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -938,6 +994,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1057,6 +1148,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1176,6 +1302,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 3
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1295,6 +1456,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 4
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1396,6 +1592,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1563,6 +1794,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1622,6 +1859,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1747,6 +1992,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1806,6 +2057,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -1846,6 +2105,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1905,6 +2170,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..8e8d43fa 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
@@ -353,6 +353,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -414,6 +420,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -459,6 +473,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -520,6 +540,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -565,6 +593,12 @@
"u64": 259200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -626,6 +660,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -671,6 +713,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -730,6 +778,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -775,6 +831,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -834,6 +896,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1229,6 +1299,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1348,6 +1453,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1467,6 +1607,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 3
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1586,6 +1761,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 4
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1705,6 +1915,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 5
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1806,6 +2051,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1902,6 +2182,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1998,6 +2313,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 3
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -2249,6 +2599,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2308,6 +2664,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -2348,6 +2712,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2407,6 +2777,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..2200e0c1 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
@@ -287,6 +287,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -346,6 +352,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -391,6 +405,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -450,6 +470,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -495,6 +523,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -554,6 +588,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -599,6 +641,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -658,6 +706,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -703,6 +759,12 @@
"u64": 259200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -762,6 +824,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1058,6 +1128,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1177,6 +1282,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1296,6 +1436,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 3
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1415,6 +1590,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 4
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1534,6 +1744,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 5
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1706,6 +1951,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1765,6 +2016,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -1805,6 +2064,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1864,6 +2129,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1989,6 +2262,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2048,6 +2327,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -2088,6 +2375,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2147,6 +2440,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -2187,6 +2488,12 @@
"u64": 259200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2246,6 +2553,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..05ffbd3b 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
@@ -207,6 +207,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -266,6 +272,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -311,6 +325,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -370,6 +390,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -415,6 +443,12 @@
"u64": 259200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -474,6 +508,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -693,6 +735,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -812,6 +889,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -931,6 +1043,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 3
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1187,6 +1334,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1246,6 +1399,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -1286,6 +1447,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1345,6 +1512,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -1385,6 +1560,12 @@
"u64": 259200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1444,6 +1625,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..f0cb9d51 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
@@ -407,6 +407,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -466,6 +472,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -511,6 +525,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -570,6 +590,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -615,6 +643,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -674,6 +708,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -719,6 +761,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -778,6 +826,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -823,6 +879,12 @@
"u64": 259200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -882,6 +944,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -927,6 +997,12 @@
"u64": 259200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -986,6 +1062,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1031,6 +1115,12 @@
"u64": 345600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1090,6 +1180,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1135,6 +1233,12 @@
"u64": 345600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1194,6 +1298,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1589,6 +1701,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1708,6 +1855,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1827,6 +2009,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 3
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1946,6 +2163,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 4
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -2065,6 +2317,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 5
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -2184,6 +2471,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 6
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -2303,6 +2625,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 7
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -2422,6 +2779,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 8
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -2594,6 +2986,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2653,6 +3051,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -2693,6 +3099,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2752,6 +3164,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -2877,6 +3297,12 @@
"u64": 259200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2936,6 +3362,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -2976,6 +3410,12 @@
"u64": 345600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -3035,6 +3475,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..b0d97de8 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
@@ -286,6 +286,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -345,6 +351,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -390,6 +404,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -449,6 +469,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -494,6 +522,12 @@
"u64": 259200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -553,6 +587,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -598,6 +640,12 @@
"u64": 345600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -657,6 +705,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -702,6 +758,12 @@
"u64": 432000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -761,6 +823,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1046,6 +1116,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1165,6 +1270,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1284,6 +1424,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 3
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1403,6 +1578,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 4
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1522,6 +1732,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 5
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1694,6 +1939,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1753,6 +2004,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -1793,6 +2052,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1852,6 +2117,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -1892,6 +2165,12 @@
"u64": 259200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1951,6 +2230,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -1991,6 +2278,12 @@
"u64": 345600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2050,6 +2343,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -2090,6 +2391,12 @@
"u64": 432000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2149,6 +2456,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..ba1053bc 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
@@ -2286,6 +2286,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2345,6 +2351,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -2390,6 +2404,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2449,6 +2469,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -2494,6 +2522,12 @@
"u64": 259200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2553,6 +2587,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -2598,6 +2640,12 @@
"u64": 345600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2657,6 +2705,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -2702,6 +2758,12 @@
"u64": 432000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2761,6 +2823,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -2806,6 +2876,12 @@
"u64": 518400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2865,6 +2941,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -2910,6 +2994,12 @@
"u64": 604800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2969,6 +3059,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -3014,6 +3112,12 @@
"u64": 691200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -3073,6 +3177,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -3118,6 +3230,12 @@
"u64": 777600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -3177,6 +3295,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -3222,6 +3348,12 @@
"u64": 864000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -3281,6 +3413,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -3326,6 +3466,12 @@
"u64": 950400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -3385,6 +3531,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -3430,6 +3584,12 @@
"u64": 1036800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -3489,6 +3649,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -3534,6 +3702,12 @@
"u64": 1123200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -3593,6 +3767,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -3638,6 +3820,12 @@
"u64": 1209600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -3697,6 +3885,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -3742,6 +3938,12 @@
"u64": 1296000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -3801,6 +4003,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -3846,6 +4056,12 @@
"u64": 1382400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -3905,6 +4121,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -3950,6 +4174,12 @@
"u64": 1468800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -4009,6 +4239,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -4054,6 +4292,12 @@
"u64": 1555200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -4113,6 +4357,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -4158,6 +4410,12 @@
"u64": 1641600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -4217,6 +4475,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -4262,6 +4528,12 @@
"u64": 1728000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -4321,6 +4593,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -4366,6 +4646,12 @@
"u64": 1814400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -4425,6 +4711,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -4470,6 +4764,12 @@
"u64": 1900800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -4529,6 +4829,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -4574,6 +4882,12 @@
"u64": 1987200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -4633,6 +4947,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -4680,10 +5002,16 @@
},
{
"key": {
- "symbol": "frequency_days"
+ "symbol": "external_ref"
},
- "val": {
- "u32": 0
+ "val": "void"
+ },
+ {
+ "key": {
+ "symbol": "frequency_days"
+ },
+ "val": {
+ "u32": 0
}
},
{
@@ -4737,6 +5065,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -4782,6 +5118,12 @@
"u64": 2160000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -4841,6 +5183,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -4886,6 +5236,12 @@
"u64": 2246400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -4945,6 +5301,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -4990,6 +5354,12 @@
"u64": 2332800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -5049,6 +5419,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -5094,6 +5472,12 @@
"u64": 2419200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -5153,6 +5537,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -5198,6 +5590,12 @@
"u64": 2505600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -5257,6 +5655,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -5302,6 +5708,12 @@
"u64": 2592000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -5361,6 +5773,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -5406,6 +5826,12 @@
"u64": 2678400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -5465,6 +5891,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -5510,6 +5944,12 @@
"u64": 2764800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -5569,6 +6009,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -5614,6 +6062,12 @@
"u64": 2851200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -5673,6 +6127,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -5718,6 +6180,12 @@
"u64": 2937600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -5777,6 +6245,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -5822,6 +6298,12 @@
"u64": 3024000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -5881,6 +6363,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -5926,6 +6416,12 @@
"u64": 3110400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -5985,6 +6481,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -6030,6 +6534,12 @@
"u64": 3196800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -6089,6 +6599,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -6134,6 +6652,12 @@
"u64": 3283200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -6193,6 +6717,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -6238,6 +6770,12 @@
"u64": 3369600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -6297,6 +6835,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -6342,6 +6888,12 @@
"u64": 3456000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -6401,6 +6953,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -6446,6 +7006,12 @@
"u64": 3542400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -6505,6 +7071,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -6550,6 +7124,12 @@
"u64": 3628800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -6609,6 +7189,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -6654,6 +7242,12 @@
"u64": 3715200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -6713,6 +7307,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -6758,6 +7360,12 @@
"u64": 3801600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -6817,6 +7425,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -6862,6 +7478,12 @@
"u64": 3888000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -6921,6 +7543,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -6966,6 +7596,12 @@
"u64": 3974400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -7025,6 +7661,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -7070,6 +7714,12 @@
"u64": 4060800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -7129,6 +7779,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -7174,6 +7832,12 @@
"u64": 4147200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -7233,6 +7897,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -7278,6 +7950,12 @@
"u64": 4233600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -7337,6 +8015,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -7382,6 +8068,12 @@
"u64": 4320000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -7441,6 +8133,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -7486,6 +8186,12 @@
"u64": 4406400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -7545,6 +8251,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -7590,6 +8304,12 @@
"u64": 4492800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -7649,6 +8369,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -7694,6 +8422,12 @@
"u64": 4579200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -7753,6 +8487,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -7798,6 +8540,12 @@
"u64": 4665600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -7857,6 +8605,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -7902,6 +8658,12 @@
"u64": 4752000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -7961,6 +8723,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -9896,6 +10666,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -10015,6 +10820,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -10134,6 +10974,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 3
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -10253,6 +11128,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 4
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -10372,6 +11282,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 5
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -10500,16 +11445,14 @@
"v0": {
"topics": [
{
- "symbol": "Remitwise"
- },
- {
- "u32": 1
- },
- {
- "u32": 1
+ "symbol": "bill"
},
{
- "symbol": "created"
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
}
],
"data": {
@@ -10520,9 +11463,46 @@
{
"address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
},
- {
- "i128": {
- "hi": 0,
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "Remitwise"
+ },
+ {
+ "u32": 1
+ },
+ {
+ "u32": 1
+ },
+ {
+ "symbol": "created"
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 6
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ {
+ "i128": {
+ "hi": 0,
"lo": 600
}
},
@@ -10610,6 +11590,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 7
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -10729,6 +11744,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 8
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -10848,6 +11898,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 9
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -10967,6 +12052,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 10
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -11086,6 +12206,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 11
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -11205,6 +12360,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 12
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -11324,6 +12514,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 13
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -11452,16 +12677,14 @@
"v0": {
"topics": [
{
- "symbol": "Remitwise"
- },
- {
- "u32": 1
- },
- {
- "u32": 1
+ "symbol": "bill"
},
{
- "symbol": "created"
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
}
],
"data": {
@@ -11472,11 +12695,48 @@
{
"address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
},
- {
- "i128": {
- "hi": 0,
- "lo": 1400
- }
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "Remitwise"
+ },
+ {
+ "u32": 1
+ },
+ {
+ "u32": 1
+ },
+ {
+ "symbol": "created"
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 14
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ {
+ "i128": {
+ "hi": 0,
+ "lo": 1400
+ }
},
{
"u64": 1209600
@@ -11562,6 +12822,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 15
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -11681,6 +12976,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 16
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -11800,6 +13130,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 17
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -11919,6 +13284,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 18
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -12038,6 +13438,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 19
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -12157,6 +13592,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 20
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -12276,6 +13746,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 21
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -12404,16 +13909,14 @@
"v0": {
"topics": [
{
- "symbol": "Remitwise"
- },
- {
- "u32": 1
- },
- {
- "u32": 1
+ "symbol": "bill"
},
{
- "symbol": "created"
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
}
],
"data": {
@@ -12424,9 +13927,46 @@
{
"address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
},
- {
- "i128": {
- "hi": 0,
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "Remitwise"
+ },
+ {
+ "u32": 1
+ },
+ {
+ "u32": 1
+ },
+ {
+ "symbol": "created"
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 22
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ {
+ "i128": {
+ "hi": 0,
"lo": 2200
}
},
@@ -12514,6 +14054,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 23
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -12633,6 +14208,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 24
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -12752,6 +14362,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 25
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -12871,6 +14516,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 26
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -12990,6 +14670,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 27
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -13109,6 +14824,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 28
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -13228,6 +14978,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 29
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -13356,16 +15141,14 @@
"v0": {
"topics": [
{
- "symbol": "Remitwise"
- },
- {
- "u32": 1
- },
- {
- "u32": 1
+ "symbol": "bill"
},
{
- "symbol": "created"
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
}
],
"data": {
@@ -13376,11 +15159,48 @@
{
"address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
},
- {
- "i128": {
- "hi": 0,
- "lo": 3000
- }
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "Remitwise"
+ },
+ {
+ "u32": 1
+ },
+ {
+ "u32": 1
+ },
+ {
+ "symbol": "created"
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 30
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ {
+ "i128": {
+ "hi": 0,
+ "lo": 3000
+ }
},
{
"u64": 2592000
@@ -13466,6 +15286,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 31
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -13585,6 +15440,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 32
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -13704,6 +15594,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 33
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -13823,6 +15748,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 34
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -13942,6 +15902,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 35
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -14061,6 +16056,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 36
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -14180,6 +16210,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 37
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -14299,6 +16364,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 38
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -14418,6 +16518,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 39
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -14507,29 +16642,64 @@
"data": {
"vec": [
{
- "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
- },
- {
- "string": "Test Bill"
- },
- {
- "i128": {
- "hi": 0,
- "lo": 4000
- }
- },
- {
- "u64": 3456000
- },
- {
- "bool": false
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ {
+ "string": "Test Bill"
+ },
+ {
+ "i128": {
+ "hi": 0,
+ "lo": 4000
+ }
+ },
+ {
+ "u64": 3456000
+ },
+ {
+ "bool": false
+ },
+ {
+ "u32": 0
+ },
+ {
+ "string": "XLM"
+ }
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 40
},
{
- "u32": 0
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
},
- {
- "string": "XLM"
- }
+ "void"
]
}
}
@@ -14656,6 +16826,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 41
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -14775,6 +16980,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 42
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -14894,6 +17134,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 43
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -15013,6 +17288,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 44
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -15132,6 +17442,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 45
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -15251,6 +17596,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 46
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -15370,6 +17750,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 47
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -15474,14 +17889,49 @@
"u64": 4147200
},
{
- "bool": false
+ "bool": false
+ },
+ {
+ "u32": 0
+ },
+ {
+ "string": "XLM"
+ }
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 48
},
{
- "u32": 0
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
},
- {
- "string": "XLM"
- }
+ "void"
]
}
}
@@ -15608,6 +18058,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 49
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -15727,6 +18212,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 50
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -15846,6 +18366,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 51
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -15965,6 +18520,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 52
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -16084,6 +18674,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 53
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -16203,6 +18828,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 54
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -16322,6 +18982,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 55
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -16494,6 +19189,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -16553,6 +19254,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -16593,6 +19302,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -16652,6 +19367,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -16692,6 +19415,12 @@
"u64": 259200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -16751,6 +19480,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -16791,6 +19528,12 @@
"u64": 345600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -16847,9 +19590,17 @@
},
{
"key": {
- "symbol": "schedule_id"
+ "symbol": "schedule_id"
+ },
+ "val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
},
- "val": "void"
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -16890,6 +19641,12 @@
"u64": 432000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -16949,6 +19706,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -16989,6 +19754,12 @@
"u64": 518400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -17048,6 +19819,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -17088,6 +19867,12 @@
"u64": 604800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -17147,6 +19932,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -17187,6 +19980,12 @@
"u64": 691200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -17246,6 +20045,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -17286,6 +20093,12 @@
"u64": 777600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -17345,6 +20158,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -17385,6 +20206,12 @@
"u64": 864000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -17444,6 +20271,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -17484,6 +20319,12 @@
"u64": 950400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -17543,6 +20384,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -17583,6 +20432,12 @@
"u64": 1036800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -17642,6 +20497,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -17682,6 +20545,12 @@
"u64": 1123200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -17741,6 +20610,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -17781,6 +20658,12 @@
"u64": 1209600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -17840,6 +20723,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -17880,6 +20771,12 @@
"u64": 1296000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -17939,6 +20836,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -17979,6 +20884,12 @@
"u64": 1382400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -18038,6 +20949,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -18078,6 +20997,12 @@
"u64": 1468800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -18137,6 +21062,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -18177,6 +21110,12 @@
"u64": 1555200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -18236,6 +21175,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -18276,6 +21223,12 @@
"u64": 1641600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -18335,6 +21288,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -18375,6 +21336,12 @@
"u64": 1728000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -18434,6 +21401,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -18474,6 +21449,12 @@
"u64": 1814400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -18533,6 +21514,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -18573,6 +21562,12 @@
"u64": 1900800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -18632,6 +21627,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -18672,6 +21675,12 @@
"u64": 1987200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -18731,6 +21740,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -18771,6 +21788,12 @@
"u64": 2073600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -18830,6 +21853,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -18870,6 +21901,12 @@
"u64": 2160000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -18929,6 +21966,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -18969,6 +22014,12 @@
"u64": 2246400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -19028,6 +22079,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -19068,6 +22127,12 @@
"u64": 2332800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -19126,7 +22191,15 @@
"key": {
"symbol": "schedule_id"
},
- "val": "void"
+ "val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -19167,6 +22240,12 @@
"u64": 2419200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -19226,6 +22305,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -19266,6 +22353,12 @@
"u64": 2505600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -19325,6 +22418,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -19365,6 +22466,12 @@
"u64": 2592000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -19424,6 +22531,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -19464,6 +22579,12 @@
"u64": 2678400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -19523,6 +22644,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -19563,6 +22692,12 @@
"u64": 2764800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -19622,6 +22757,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -19662,6 +22805,12 @@
"u64": 2851200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -19721,6 +22870,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -19761,6 +22918,12 @@
"u64": 2937600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -19820,6 +22983,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -19860,6 +23031,12 @@
"u64": 3024000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -19919,6 +23096,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -19959,6 +23144,12 @@
"u64": 3110400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -20018,6 +23209,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -20058,6 +23257,12 @@
"u64": 3196800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -20117,6 +23322,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -20157,6 +23370,12 @@
"u64": 3283200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -20216,6 +23435,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -20256,6 +23483,12 @@
"u64": 3369600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -20315,6 +23548,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -20355,6 +23596,12 @@
"u64": 3456000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -20414,6 +23661,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -20454,6 +23709,12 @@
"u64": 3542400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -20513,6 +23774,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -20553,6 +23822,12 @@
"u64": 3628800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -20612,6 +23887,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -20652,6 +23935,12 @@
"u64": 3715200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -20711,6 +24000,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -20751,6 +24048,12 @@
"u64": 3801600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -20810,6 +24113,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -20850,6 +24161,12 @@
"u64": 3888000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -20909,6 +24226,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -20949,6 +24274,12 @@
"u64": 3974400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -21008,6 +24339,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -21048,6 +24387,12 @@
"u64": 4060800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -21107,6 +24452,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -21147,6 +24500,12 @@
"u64": 4147200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -21206,6 +24565,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -21246,6 +24613,12 @@
"u64": 4233600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -21305,6 +24678,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -21345,6 +24726,12 @@
"u64": 4320000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -21404,6 +24791,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..4318b27f 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
@@ -206,6 +206,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -265,6 +271,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -310,6 +324,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -369,6 +389,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -414,6 +442,12 @@
"u64": 259200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -473,6 +507,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -692,6 +734,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -811,6 +888,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -930,6 +1042,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 3
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1102,6 +1249,12 @@
"u64": 86400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1161,6 +1314,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -1201,6 +1362,12 @@
"u64": 172800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1260,6 +1427,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -1300,6 +1475,12 @@
"u64": 259200
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1359,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_amount_preserved_across_cycles.1.json b/bill_payments/test_snapshots/test/test_recurring_date_math_amount_preserved_across_cycles.1.json
index f2db08da..411a6f1d 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
@@ -172,6 +172,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -233,6 +239,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -278,6 +292,12 @@
"u64": 3592000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -339,6 +359,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -384,6 +412,12 @@
"u64": 6184000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -443,6 +477,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -662,6 +704,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -763,6 +840,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -859,6 +971,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1000,6 +1147,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1061,6 +1214,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1147,6 +1308,12 @@
"u64": 3592000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1208,6 +1375,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1294,6 +1469,12 @@
"u64": 6184000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1353,6 +1534,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..3dba02b5 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
@@ -149,6 +149,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -210,6 +216,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -255,6 +269,12 @@
"u64": 3592000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -314,6 +334,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -500,6 +528,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -601,6 +664,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -742,6 +840,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -803,6 +907,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -889,6 +1001,12 @@
"u64": 3592000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -948,6 +1066,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..f150c8b7 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
@@ -148,6 +148,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -209,6 +215,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -254,6 +268,12 @@
"u64": 2209600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -313,6 +333,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -499,6 +527,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -600,6 +663,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -741,6 +839,12 @@
"u64": 2209600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -800,6 +904,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..49ea07c0 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
@@ -148,6 +148,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -209,6 +215,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -254,6 +268,12 @@
"u64": 1086400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -313,6 +333,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -499,6 +527,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -600,6 +663,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -741,6 +839,12 @@
"u64": 1086400
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -800,6 +904,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..16cd2480 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
@@ -148,6 +148,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -209,6 +215,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -254,6 +268,12 @@
"u64": 3592000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -313,6 +333,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -499,6 +527,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -600,6 +663,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -741,6 +839,12 @@
"u64": 3592000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -800,6 +904,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..d1cbadb7 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
@@ -148,6 +148,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -209,6 +215,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -254,6 +268,12 @@
"u64": 32536000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -313,6 +333,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -499,6 +527,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -600,6 +663,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -741,6 +839,12 @@
"u64": 32536000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -800,6 +904,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..d30d27f8 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
@@ -172,6 +172,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -233,6 +239,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -278,6 +292,12 @@
"u64": 3592000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -339,6 +359,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -384,6 +412,12 @@
"u64": 6184000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -443,6 +477,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -662,6 +704,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -763,6 +840,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -904,6 +1016,12 @@
"u64": 3592000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -963,6 +1081,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1004,6 +1130,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1145,6 +1306,12 @@
"u64": 3592000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1206,6 +1373,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1292,6 +1467,12 @@
"u64": 6184000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1351,6 +1532,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..179345b7 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
@@ -193,6 +193,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -254,6 +260,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -299,6 +313,12 @@
"u64": 3592000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -360,6 +380,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -405,6 +433,12 @@
"u64": 6184000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -466,6 +500,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -511,6 +553,12 @@
"u64": 8776000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -570,6 +618,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -822,6 +878,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -923,6 +1014,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1019,6 +1145,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1115,6 +1276,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 3
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1256,6 +1452,12 @@
"u64": 6184000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1317,6 +1519,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1403,6 +1613,12 @@
"u64": 8776000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1462,6 +1678,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..22e70dae 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
@@ -172,6 +172,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -233,6 +239,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -278,6 +292,12 @@
"u64": 3592000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -339,6 +359,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -384,6 +412,12 @@
"u64": 6184000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -443,6 +477,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -662,6 +704,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -763,6 +840,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -859,6 +971,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1000,6 +1147,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1061,6 +1214,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1147,6 +1308,12 @@
"u64": 3592000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1208,6 +1375,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1294,6 +1469,12 @@
"u64": 6184000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1353,6 +1534,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..f576f57a 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
@@ -148,6 +148,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -209,6 +215,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -254,6 +268,12 @@
"u64": 3592000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -313,6 +333,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -499,6 +527,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -600,6 +663,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -741,6 +839,12 @@
"u64": 3592000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -800,6 +904,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..7d10b835 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
@@ -172,6 +172,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -233,6 +239,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -278,6 +292,12 @@
"u64": 1604800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -339,6 +359,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -384,6 +412,12 @@
"u64": 2209600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -443,6 +477,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -662,6 +704,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -763,6 +840,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -859,6 +971,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1000,6 +1147,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1061,6 +1214,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1147,6 +1308,12 @@
"u64": 1604800
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1208,6 +1375,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1294,6 +1469,12 @@
"u64": 2209600
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1353,6 +1534,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..b5c95c45 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
@@ -126,6 +126,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -185,6 +191,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -338,6 +352,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
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..d3fcd7c5 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
@@ -127,6 +127,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -186,6 +192,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -339,6 +353,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -589,6 +638,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -648,6 +703,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..8b374e39 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,86 @@
"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
+ },
+ {
+ "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
+ },
+ {
+ "string": "XLM"
+ }
+ ]
+ }
+ },
+ "sub_invocations": []
+ }
+ ]
+ ],
[]
],
"ledger": {
@@ -38,14 +118,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 +486,7 @@
},
"ext": "v0"
},
- 4095
+ 518400
]
]
]
@@ -105,7 +525,7 @@
}
},
{
- "u64": 1999999
+ "u64": 1500000
},
{
"bool": false
@@ -127,26 +547,81 @@
"event": {
"ext": "v0",
"contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
- "type_": "diagnostic",
+ "type_": "contract",
"body": {
"v0": {
"topics": [
{
- "symbol": "fn_return"
+ "symbol": "bill"
},
{
- "symbol": "create_bill"
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
}
],
"data": {
- "error": {
- "contract": 12
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "Remitwise"
+ },
+ {
+ "u32": 1
+ },
+ {
+ "u32": 1
+ },
+ {
+ "symbol": "created"
}
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ {
+ "i128": {
+ "hi": 0,
+ "lo": 100
+ }
+ },
+ {
+ "u64": 1500000
+ }
+ ]
}
}
}
},
- "failed_call": true
+ "failed_call": false
},
{
"event": {
@@ -157,21 +632,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 +655,120 @@
"v0": {
"topics": [
{
- "symbol": "error"
+ "symbol": "fn_call"
},
{
- "error": {
- "contract": 12
+ "bytes": "0000000000000000000000000000000000000000000000000000000000000001"
+ },
+ {
+ "symbol": "create_bill"
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ {
+ "string": "DueNow"
+ },
+ {
+ "i128": {
+ "hi": 0,
+ "lo": 200
+ }
+ },
+ {
+ "u64": 2000000
+ },
+ {
+ "bool": false
+ },
+ {
+ "u32": 0
+ },
+ {
+ "string": "XLM"
}
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
}
],
"data": {
"vec": [
{
- "string": "contract call failed"
+ "u32": 2
},
{
- "symbol": "create_bill"
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
},
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "Remitwise"
+ },
+ {
+ "u32": 1
+ },
+ {
+ "u32": 1
+ },
+ {
+ "symbol": "created"
+ }
+ ],
+ "data": {
+ "vec": [
{
- "vec": [
- {
- "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
- },
- {
- "string": "Overdue"
- },
- {
- "i128": {
- "hi": 0,
- "lo": 100
- }
- },
- {
- "u64": 1999999
- },
- {
- "bool": false
- },
- {
- "u32": 0
- },
- {
- "string": "XLM"
- }
- ]
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ {
+ "i128": {
+ "hi": 0,
+ "lo": 200
+ }
+ },
+ {
+ "u64": 2000000
}
]
}
@@ -233,6 +777,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 +809,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..487b8554 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
@@ -127,6 +127,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -186,6 +192,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -339,6 +353,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -589,6 +638,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -648,6 +703,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..a9263c65 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
@@ -193,6 +193,14 @@
"val": {
"u64": 1000000
}
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -475,6 +483,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -576,6 +619,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -832,6 +910,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..ee40e04f 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
@@ -328,6 +328,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -389,6 +395,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -434,6 +448,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -495,6 +515,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -540,6 +568,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -601,6 +635,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -646,6 +688,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -707,6 +755,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -752,6 +808,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -813,6 +875,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1186,6 +1256,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1305,6 +1410,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1424,6 +1564,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 3
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1543,6 +1718,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 4
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -1662,6 +1872,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 5
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -2126,6 +2371,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2187,6 +2438,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -2273,6 +2532,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2334,6 +2599,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -2420,6 +2693,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2481,6 +2760,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -2567,6 +2854,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2628,6 +2921,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -2714,6 +3015,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2775,6 +3082,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..a6df32c4 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
@@ -126,6 +126,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -185,6 +191,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -338,6 +352,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -484,6 +533,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -543,6 +598,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..3f507637 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
@@ -126,6 +126,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -185,6 +191,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -338,6 +352,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -484,6 +533,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -543,6 +598,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..4b77a757 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
@@ -44,46 +44,6 @@
}
]
],
- [
- [
- "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
- },
- {
- "string": "XLM"
- }
- ]
- }
- },
- "sub_invocations": []
- }
- ]
- ],
[]
],
"ledger": {
@@ -166,6 +126,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -225,110 +191,14 @@
"symbol": "schedule_id"
},
"val": "void"
- }
- ]
- }
- },
- {
- "key": {
- "u32": 2
- },
- "val": {
- "map": [
- {
- "key": {
- "symbol": "amount"
- },
- "val": {
- "i128": {
- "hi": 4611686018427387904,
- "lo": 999
- }
- }
},
{
"key": {
- "symbol": "created_at"
+ "symbol": "tags"
},
"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
+ "vec": []
}
- },
- {
- "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"
- },
- "val": {
- "bool": false
- }
- },
- {
- "key": {
- "symbol": "schedule_id"
- },
- "val": "void"
}
]
}
@@ -341,7 +211,7 @@
"symbol": "NEXT_ID"
},
"val": {
- "u32": 2
+ "u32": 1
}
},
{
@@ -356,8 +226,8 @@
},
"val": {
"i128": {
- "hi": 9223372036854775807,
- "lo": 18446744073709551615
+ "hi": 4611686018427387904,
+ "lo": 999
}
}
}
@@ -407,39 +277,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": {
@@ -515,6 +352,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -638,31 +510,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#67)'"
},
{
"address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
},
+ {
+ "string": "Bill2"
+ },
{
"i128": {
"hi": 4611686018427387904,
@@ -671,13 +537,22 @@
},
{
"u64": 1000000
+ },
+ {
+ "bool": false
+ },
+ {
+ "u32": 0
+ },
+ {
+ "string": "XLM"
}
]
}
}
}
},
- "failed_call": false
+ "failed_call": true
},
{
"event": {
@@ -688,19 +563,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 +588,51 @@
"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
+ },
+ {
+ "string": "XLM"
+ }
+ ]
+ }
+ ]
}
}
}
@@ -731,23 +642,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..254c855a 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
@@ -166,6 +166,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -225,6 +231,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -270,6 +284,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -329,6 +349,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -515,6 +543,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -634,6 +697,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
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..ef804baa 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
@@ -167,6 +167,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -226,6 +232,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -271,6 +285,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -330,6 +350,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -527,6 +555,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -646,6 +709,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
diff --git a/bill_payments/test_snapshots/test_notification_flow.1.json b/bill_payments/test_snapshots/test_notification_flow.1.json
index cf921acd..f63b3de8 100644
--- a/bill_payments/test_snapshots/test_notification_flow.1.json
+++ b/bill_payments/test_snapshots/test_notification_flow.1.json
@@ -147,6 +147,12 @@
"u64": 1234567890
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -208,6 +214,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -394,6 +408,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -495,6 +544,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
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..01528c25 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
@@ -687,6 +687,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -746,6 +752,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -791,6 +805,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -850,6 +870,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -895,6 +923,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -954,6 +988,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -999,6 +1041,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1058,6 +1106,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1103,6 +1159,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1162,6 +1224,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1207,6 +1277,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1266,6 +1342,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1311,6 +1395,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1370,6 +1460,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1415,6 +1513,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1474,6 +1578,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1519,6 +1631,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1578,6 +1696,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1623,6 +1749,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1682,6 +1814,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1727,6 +1867,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1786,6 +1932,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1831,6 +1985,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1890,6 +2050,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -1935,6 +2103,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -1994,6 +2168,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -2039,6 +2221,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2098,6 +2286,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -2143,6 +2339,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -2202,6 +2404,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -2817,6 +3027,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -2936,6 +3181,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 2
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -3055,6 +3335,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 3
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -3183,10 +3498,45 @@
"v0": {
"topics": [
{
- "symbol": "Remitwise"
- },
- {
- "u32": 1
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 4
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "Remitwise"
+ },
+ {
+ "u32": 1
},
{
"u32": 1
@@ -3293,6 +3643,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 5
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -3412,6 +3797,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 6
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -3531,6 +3951,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 7
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -3650,6 +4105,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 8
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -3769,6 +4259,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 9
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -3888,6 +4413,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 10
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -4007,6 +4567,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 11
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -4117,8 +4712,43 @@
"u32": 0
},
{
- "string": "XLM"
- }
+ "string": "XLM"
+ }
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 12
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
]
}
}
@@ -4245,6 +4875,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 13
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -4364,6 +5029,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 14
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -4483,6 +5183,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 15
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -4655,6 +5390,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -4714,6 +5455,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -4754,6 +5503,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -4813,6 +5568,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -4853,6 +5616,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -4912,6 +5681,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -4952,6 +5729,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -5011,6 +5794,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -5051,6 +5842,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -5110,6 +5907,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -5150,6 +5955,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -5209,6 +6020,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -5249,6 +6068,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -5308,6 +6133,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -5348,6 +6181,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -5407,6 +6246,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -5447,6 +6294,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -5506,6 +6359,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -5546,6 +6407,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -5605,6 +6472,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -5730,6 +6605,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -5789,6 +6670,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -5829,6 +6718,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -5888,6 +6783,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -5928,6 +6831,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -5987,6 +6896,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -6027,6 +6944,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -6086,6 +7009,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
},
@@ -6126,6 +7057,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -6185,6 +7122,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..7e81a4c0 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
@@ -148,6 +148,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -209,6 +215,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -395,6 +409,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -496,6 +545,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -637,6 +721,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -698,6 +788,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..5a4e175d 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
@@ -149,6 +149,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -210,6 +216,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -255,6 +269,12 @@
"u64": 3592000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -314,6 +334,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -500,6 +528,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Created"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -601,6 +664,41 @@
},
"failed_call": false
},
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
+ "type_": "contract",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "bill"
+ },
+ {
+ "vec": [
+ {
+ "symbol": "Paid"
+ }
+ ]
+ }
+ ],
+ "data": {
+ "vec": [
+ {
+ "u32": 1
+ },
+ {
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
+ },
+ "void"
+ ]
+ }
+ }
+ }
+ },
+ "failed_call": false
+ },
{
"event": {
"ext": "v0",
@@ -742,6 +840,12 @@
"u64": 1000000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -803,6 +907,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
@@ -889,6 +1001,12 @@
"u64": 3592000
}
},
+ {
+ "key": {
+ "symbol": "external_ref"
+ },
+ "val": "void"
+ },
{
"key": {
"symbol": "frequency_days"
@@ -948,6 +1066,14 @@
"symbol": "schedule_id"
},
"val": "void"
+ },
+ {
+ "key": {
+ "symbol": "tags"
+ },
+ "val": {
+ "vec": []
+ }
}
]
}
diff --git a/bill_payments/tests/stress_tests.rs b/bill_payments/tests/stress_tests.rs
index eb512c4a..0121d1b1 100644
--- a/bill_payments/tests/stress_tests.rs
+++ b/bill_payments/tests/stress_tests.rs
@@ -79,12 +79,24 @@ 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,
+ &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 +134,15 @@ 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,
+ &String::from_str(&env, "XLM"),
+ );
}
let ttl = env.as_contract(&contract_id, || env.storage().instance().get_ttl());
@@ -155,7 +175,15 @@ 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,
+ &String::from_str(&env, "XLM"),
+ );
}
}
@@ -208,7 +236,15 @@ 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,
+ &String::from_str(&env, "XLM"),
+ );
}
let ttl_batch1 = env.as_contract(&contract_id, || env.storage().instance().get_ttl());
@@ -239,7 +275,15 @@ 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,
+ &String::from_str(&env, "XLM"),
+ );
let ttl_rebumped = env.as_contract(&contract_id, || env.storage().instance().get_ttl());
assert!(
@@ -261,7 +305,15 @@ 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,
+ &String::from_str(&env, "XLM"),
+ );
// Advance ledger so TTL drops below threshold
env.ledger().set(LedgerInfo {
@@ -307,7 +359,15 @@ 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,
+ &String::from_str(&env, "XLM"),
+ );
}
// Pay all 100 bills (non-recurring, so no new bills created)
@@ -328,8 +388,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 +447,15 @@ 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,
+ &String::from_str(&env, "XLM"),
+ );
next_id += 1;
}
let last = next_id - 1;
@@ -425,7 +499,15 @@ 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,
+ &String::from_str(&env, "XLM"),
+ );
}
let (cpu, mem, page) = measure(&env, || client.get_unpaid_bills(&owner, &0u32, &50u32));
@@ -450,7 +532,15 @@ 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,
+ &String::from_str(&env, "XLM"),
+ );
}
// Navigate to the last page cursor
@@ -481,14 +571,23 @@ 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,
+ &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 +608,15 @@ 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,
+ &String::from_str(&env, "XLM"),
+ );
}
let expected = 200i128 * 100;
diff --git a/data_migration/src/lib.rs b/data_migration/src/lib.rs
index e91e007b..a7c13166 100644
--- a/data_migration/src/lib.rs
+++ b/data_migration/src/lib.rs
@@ -10,27 +10,28 @@ use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::collections::HashMap;
-/// Current snapshot schema version for migration compatibility.
-///
-/// # Versioning Policy (workspace-wide)
-/// All snapshot export/import flows across the workspace use an explicit
-/// `schema_version` tag stored inside the snapshot struct (or header).
-/// When the snapshot format changes in a backward-incompatible way, bump
-/// `SCHEMA_VERSION` and update `MIN_SUPPORTED_VERSION` only if the old
-/// format can no longer be safely imported.
-///
-/// Importers must validate:
-/// `MIN_SUPPORTED_VERSION <= schema_version <= SCHEMA_VERSION`
-/// and reject anything outside that range to guarantee safe
-/// forward/backward compatibility handling.
+/// Current schema version for migration compatibility.
pub const SCHEMA_VERSION: u32 = 1;
/// Minimum supported schema version for import.
-/// Snapshots with a version below this value are too old to import safely.
pub const MIN_SUPPORTED_VERSION: u32 = 1;
-/// Alias used in snapshot headers to keep naming consistent with other contracts.
-pub const SNAPSHOT_SCHEMA_VERSION: u32 = SCHEMA_VERSION;
+/// @notice Explicit allow-list for importable schema versions.
+/// @dev Keep this list tightly scoped; each allowed version must be reviewed.
+pub const ALLOWED_IMPORT_VERSIONS: &[u32] = &[1];
+
+/// @notice Explicit deny-list for schema versions that must never be imported.
+/// @dev Deny entries always take precedence over allow-list entries.
+pub const DENIED_IMPORT_VERSIONS: &[u32] = &[];
+
+/// Deterministic compatibility decision used by import policy checks.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum VersionCompatibility {
+ Allowed,
+ DeniedByPolicy,
+ UnsupportedLegacy,
+ UnsupportedFuture,
+}
/// Versioned migration event payload meant for indexing and historical tracking.
///
@@ -123,7 +124,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())
}
@@ -134,18 +138,15 @@ impl ExportSnapshot {
/// Check if snapshot version is supported for import.
pub fn is_version_compatible(&self) -> bool {
- self.header.version >= MIN_SUPPORTED_VERSION && self.header.version <= SCHEMA_VERSION
+ matches!(
+ evaluate_version_compatibility(self.header.version),
+ VersionCompatibility::Allowed
+ )
}
/// Validate snapshot for import: version and checksum.
pub fn validate_for_import(&self) -> Result<(), MigrationError> {
- if !self.is_version_compatible() {
- return Err(MigrationError::IncompatibleVersion {
- found: self.header.version,
- min: MIN_SUPPORTED_VERSION,
- max: SCHEMA_VERSION,
- });
- }
+ check_version_compatibility(self.header.version)?;
if !self.verify_checksum() {
return Err(MigrationError::ChecksumMismatch);
}
@@ -181,6 +182,7 @@ fn format_label(f: ExportFormat) -> String {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MigrationError {
IncompatibleVersion { found: u32, min: u32, max: u32 },
+ DeniedVersion { found: u32 },
ChecksumMismatch,
InvalidFormat(String),
ValidationFailed(String),
@@ -197,6 +199,9 @@ impl std::fmt::Display for MigrationError {
found, min, max
)
}
+ MigrationError::DeniedVersion { found } => {
+ write!(f, "version {} is explicitly denied by import policy", found)
+ }
MigrationError::ChecksumMismatch => write!(f, "checksum mismatch"),
MigrationError::InvalidFormat(s) => write!(f, "invalid format: {}", s),
MigrationError::ValidationFailed(s) => write!(f, "validation failed: {}", s),
@@ -309,17 +314,41 @@ struct CsvGoalRow {
/// Version compatibility check for migration scripts.
pub fn check_version_compatibility(version: u32) -> Result<(), MigrationError> {
- if version >= MIN_SUPPORTED_VERSION && version <= SCHEMA_VERSION {
- Ok(())
- } else {
- Err(MigrationError::IncompatibleVersion {
- found: version,
- min: MIN_SUPPORTED_VERSION,
- max: SCHEMA_VERSION,
- })
+ match evaluate_version_compatibility(version) {
+ VersionCompatibility::Allowed => Ok(()),
+ VersionCompatibility::DeniedByPolicy => {
+ Err(MigrationError::DeniedVersion { found: version })
+ }
+ VersionCompatibility::UnsupportedLegacy | VersionCompatibility::UnsupportedFuture => {
+ Err(MigrationError::IncompatibleVersion {
+ found: version,
+ min: MIN_SUPPORTED_VERSION,
+ max: SCHEMA_VERSION,
+ })
+ }
}
}
+/// @notice Evaluates import compatibility for a snapshot schema version.
+/// @dev Order is deterministic and security-focused:
+/// 1) explicit deny-list, 2) backward/legacy rejection, 3) forward rejection,
+/// 4) explicit allow-list. Unknown versions default to deny.
+pub fn evaluate_version_compatibility(version: u32) -> VersionCompatibility {
+ if DENIED_IMPORT_VERSIONS.contains(&version) {
+ return VersionCompatibility::DeniedByPolicy;
+ }
+ if version < MIN_SUPPORTED_VERSION {
+ return VersionCompatibility::UnsupportedLegacy;
+ }
+ if version > SCHEMA_VERSION {
+ return VersionCompatibility::UnsupportedFuture;
+ }
+ if ALLOWED_IMPORT_VERSIONS.contains(&version) {
+ return VersionCompatibility::Allowed;
+ }
+ VersionCompatibility::DeniedByPolicy
+}
+
/// Rollback metadata (for migration scripts to record last good state).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RollbackMetadata {
@@ -345,15 +374,19 @@ mod hex {
mod tests {
use super::*;
+ fn sample_split_payload(owner: &str) -> SnapshotPayload {
+ SnapshotPayload::RemittanceSplit(RemittanceSplitExport {
+ owner: owner.into(),
+ spending_percent: 40,
+ savings_percent: 30,
+ bills_percent: 20,
+ insurance_percent: 10,
+ })
+ }
+
#[test]
fn test_snapshot_checksum_roundtrip_succeeds() {
- let payload = SnapshotPayload::RemittanceSplit(RemittanceSplitExport {
- owner: "GABC".into(),
- spending_percent: 50,
- savings_percent: 30,
- bills_percent: 15,
- insurance_percent: 5,
- });
+ let payload = sample_split_payload("GABC");
let snapshot = ExportSnapshot::new(payload, ExportFormat::Json);
assert!(snapshot.verify_checksum());
assert!(snapshot.is_version_compatible());
@@ -362,13 +395,7 @@ mod tests {
#[test]
fn test_export_import_json_succeeds() {
- let payload = SnapshotPayload::RemittanceSplit(RemittanceSplitExport {
- owner: "GXYZ".into(),
- spending_percent: 40,
- savings_percent: 40,
- bills_percent: 10,
- insurance_percent: 10,
- });
+ let payload = sample_split_payload("GXYZ");
let snapshot = ExportSnapshot::new(payload, ExportFormat::Json);
let bytes = export_to_json(&snapshot).unwrap();
let loaded = import_from_json(&bytes).unwrap();
@@ -378,13 +405,7 @@ mod tests {
#[test]
fn test_export_import_binary_succeeds() {
- let payload = SnapshotPayload::RemittanceSplit(RemittanceSplitExport {
- owner: "GBIN".into(),
- spending_percent: 25,
- savings_percent: 25,
- bills_percent: 25,
- insurance_percent: 25,
- });
+ let payload = sample_split_payload("GBIN");
let snapshot = ExportSnapshot::new(payload, ExportFormat::Binary);
let bytes = export_to_binary(&snapshot).unwrap();
let loaded = import_from_binary(&bytes).unwrap();
@@ -393,13 +414,7 @@ mod tests {
#[test]
fn test_checksum_mismatch_import_fails() {
- let payload = SnapshotPayload::RemittanceSplit(RemittanceSplitExport {
- owner: "GX".into(),
- spending_percent: 100,
- savings_percent: 0,
- bills_percent: 0,
- insurance_percent: 0,
- });
+ let payload = sample_split_payload("GX");
let mut snapshot = ExportSnapshot::new(payload, ExportFormat::Json);
snapshot.header.checksum = "wrong".into();
assert!(!snapshot.verify_checksum());
@@ -407,11 +422,63 @@ mod tests {
}
#[test]
- fn test_check_version_compatibility_succeeds() {
+ fn test_check_version_compatibility_succeeds_and_rejects_edges() {
assert!(check_version_compatibility(1).is_ok());
assert!(check_version_compatibility(SCHEMA_VERSION).is_ok());
- assert!(check_version_compatibility(0).is_err());
- assert!(check_version_compatibility(SCHEMA_VERSION + 1).is_err());
+ assert_eq!(
+ check_version_compatibility(0),
+ Err(MigrationError::IncompatibleVersion {
+ found: 0,
+ min: MIN_SUPPORTED_VERSION,
+ max: SCHEMA_VERSION
+ })
+ );
+ assert_eq!(
+ check_version_compatibility(SCHEMA_VERSION + 1),
+ Err(MigrationError::IncompatibleVersion {
+ found: SCHEMA_VERSION + 1,
+ min: MIN_SUPPORTED_VERSION,
+ max: SCHEMA_VERSION
+ })
+ );
+ }
+
+ #[test]
+ fn test_evaluate_version_policy_is_deterministic() {
+ assert_eq!(
+ evaluate_version_compatibility(0),
+ VersionCompatibility::UnsupportedLegacy
+ );
+ assert_eq!(
+ evaluate_version_compatibility(SCHEMA_VERSION + 1),
+ VersionCompatibility::UnsupportedFuture
+ );
+ assert_eq!(
+ evaluate_version_compatibility(SCHEMA_VERSION),
+ VersionCompatibility::Allowed
+ );
+ }
+
+ #[test]
+ fn test_policy_defaults_to_deny_for_in_range_unallowed_versions() {
+ assert_eq!(
+ SCHEMA_VERSION, 1,
+ "update this test when adding new schema versions"
+ );
+ assert_eq!(
+ ALLOWED_IMPORT_VERSIONS,
+ &[1],
+ "update this test when changing explicit allow-list"
+ );
+ }
+
+ #[test]
+ fn test_denied_version_error_display_is_stable() {
+ let err = MigrationError::DeniedVersion { found: 9 };
+ assert_eq!(
+ err.to_string(),
+ "version 9 is explicitly denied by import policy"
+ );
}
#[test]
@@ -456,4 +523,21 @@ mod tests {
let MigrationEvent::V1(v1) = loaded;
assert_eq!(v1.version, SCHEMA_VERSION);
}
+
+ #[test]
+ fn test_import_rejects_forward_version_before_checksum() {
+ let payload = sample_split_payload("GFORWARD");
+ let mut snapshot = ExportSnapshot::new(payload, ExportFormat::Json);
+ snapshot.header.version = SCHEMA_VERSION + 1;
+ snapshot.header.checksum = "wrong".into();
+
+ assert_eq!(
+ snapshot.validate_for_import(),
+ Err(MigrationError::IncompatibleVersion {
+ found: SCHEMA_VERSION + 1,
+ min: MIN_SUPPORTED_VERSION,
+ max: SCHEMA_VERSION
+ })
+ );
+ }
}
diff --git a/examples/bill_payments_example.rs b/examples/bill_payments_example.rs
index dee3c269..7b51d909 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
@@ -21,20 +21,28 @@ fn main() {
let due_date = env.ledger().timestamp() + 604800; // 1 week from now
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();
+ println!(
+ "Creating bill: '{:?}' for {} {:?}",
+ bill_name, amount, currency
+ );
+ let bill_id = client.create_bill(
+ &owner, &bill_name, &amount, &due_date, &false, &0, ¤cy,
+ );
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
println!("\nPaying bill with ID: {}...", bill_id);
- client.pay_bill(&owner, &bill_id).unwrap();
+ client.pay_bill(&owner, &bill_id);
println!("Bill paid successfully!");
// 7. [Read] Verify bill is no longer in unpaid list
diff --git a/examples/family_wallet_example.rs b/examples/family_wallet_example.rs
index 0781ebda..266fe0ef 100644
--- a/examples/family_wallet_example.rs
+++ b/examples/family_wallet_example.rs
@@ -1,5 +1,6 @@
-use soroban_sdk::{Env, Address, Vec, testutils::Address as _};
-use family_wallet::{FamilyWallet, FamilyWalletClient, FamilyRole};
+use family_wallet::{FamilyWallet, FamilyWalletClient};
+use remitwise_common::FamilyRole;
+use soroban_sdk::{testutils::Address as _, Address, Env, Vec};
fn main() {
// 1. Setup the Soroban environment
@@ -22,21 +23,21 @@ 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();
+ let _added = client.add_member(&owner, &member2, &FamilyRole::Member, &spending_limit);
println!("Member added successfully!");
// 7. [Read] Verify the new member
diff --git a/examples/insurance_example.rs b/examples/insurance_example.rs
index 591ec493..f4b689b6 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,25 +21,40 @@ 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,
+ );
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
println!("\nPaying premium for policy ID: {}...", policy_id);
- client.pay_premium(&owner, &policy_id).unwrap();
+ let _ = client.pay_premium(&owner, &policy_id);
println!("Premium paid successfully!");
// 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..e69c0037 100644
--- a/examples/orchestrator_example.rs
+++ b/examples/orchestrator_example.rs
@@ -1,24 +1,34 @@
-use soroban_sdk::{Env, Address, testutils::Address as _};
-use orchestrator::{Orchestrator, OrchestratorClient};
+// This example is a stub to avoid pulling the `orchestrator` crate as a dependency
+// in the root examples. It demonstrates intended usage shape without linking.
+use soroban_sdk::{testutils::Address as _, Address, Env};
+#[allow(dead_code)]
+mod orchestrator_stub {
+ use super::*;
+ pub struct Orchestrator;
+ pub struct OrchestratorClient;
+ impl OrchestratorClient {
+ pub fn new(_env: &Env, _id: &soroban_sdk::BytesN<32>) -> Self {
+ OrchestratorClient
+ }
+ }
+}
fn main() {
// 1. Setup the Soroban environment
let env = Env::default();
env.mock_all_auths();
- // 2. Register the Orchestrator contract
- let contract_id = env.register_contract(None, Orchestrator);
- let client = OrchestratorClient::new(&env, &contract_id);
+ // 2. Skip contract registration in this stub to avoid linking external crate
// 3. Generate mock addresses for all participants and contracts
- let caller = Address::generate(&env);
-
+ let _caller = Address::generate(&env);
+
// Contract addresses
- let family_wallet_addr = Address::generate(&env);
- let remittance_split_addr = Address::generate(&env);
- let savings_addr = Address::generate(&env);
- let bills_addr = Address::generate(&env);
- let insurance_addr = Address::generate(&env);
+ let _family_wallet_addr = Address::generate(&env);
+ let _remittance_split_addr = Address::generate(&env);
+ let _savings_addr = Address::generate(&env);
+ let _bills_addr = Address::generate(&env);
+ let _insurance_addr = Address::generate(&env);
// Resource IDs
let goal_id = 1u32;
@@ -30,7 +40,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 +51,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..7f49ce4b 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::{ReportingContract, ReportingContractClient};
+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
@@ -11,13 +11,13 @@ fn main() {
env.mock_all_auths();
// 2. Register the Reporting contract
- let contract_id = env.register_contract(None, reporting::Reporting);
- let client = ReportingClient::new(&env, &contract_id);
+ let contract_id = env.register_contract(None, ReportingContract);
+ let client = ReportingContractClient::new(&env, &contract_id);
// 3. Generate mock addresses for dependencies and admin
let admin = Address::generate(&env);
- let user = Address::generate(&env);
-
+ let _user = Address::generate(&env);
+
// Dependencies
let split_addr = Address::generate(&env);
let savings_addr = Address::generate(&env);
@@ -29,28 +29,28 @@ fn main() {
// 4. [Write] Initialize the contract
println!("Initializing Reporting contract with admin: {:?}", admin);
- client.init(&admin).unwrap();
+ client.init(&admin);
// 5. [Write] Configure contract addresses
println!("Configuring dependency addresses...");
client.configure_addresses(
- &admin,
- &split_addr,
- &savings_addr,
- &bills_addr,
- &insurance_addr,
- &family_addr
- ).unwrap();
+ &admin,
+ &split_addr,
+ &savings_addr,
+ &bills_addr,
+ &insurance_addr,
+ &family_addr,
+ );
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..bcd87d8b 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,14 +20,17 @@ 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);
println!("Goal created successfully with ID: {}", goal_id);
// 5. [Read] Fetch the goal to check progress
let goal = client.get_goal(&goal_id).unwrap();
println!("\nGoal Details:");
- println!(" Name: {}", goal.name);
+ println!(" Name: {:?}", goal.name);
println!(" Current Amount: {}", goal.current_amount);
println!(" Target Amount: {}", goal.target_amount);
println!(" Locked: {}", goal.locked);
@@ -35,7 +38,7 @@ fn main() {
// 6. [Write] Add funds to the goal
let contribution = 1000i128;
println!("\nContributing {} to the goal...", contribution);
- let new_total = client.add_to_goal(&owner, &goal_id, &contribution).unwrap();
+ let new_total = client.add_to_goal(&owner, &goal_id, &contribution);
println!("Contribution successful! New total: {}", new_total);
// 7. [Read] Verify progress again
diff --git a/family_wallet/src/lib.rs b/family_wallet/src/lib.rs
index 06a2f41d..1395c781 100644
--- a/family_wallet/src/lib.rs
+++ b/family_wallet/src/lib.rs
@@ -580,7 +580,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 {
@@ -1592,7 +1594,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/family_wallet/test_snapshots/test/test_instance_ttl_extended_on_init.1.json b/family_wallet/test_snapshots/test/test_instance_ttl_extended_on_init.1.json
index 5a3c7f88..e4833879 100644
--- a/family_wallet/test_snapshots/test/test_instance_ttl_extended_on_init.1.json
+++ b/family_wallet/test_snapshots/test/test_instance_ttl_extended_on_init.1.json
@@ -40,7 +40,7 @@
"base_reserve": 10,
"min_persistent_entry_ttl": 1,
"min_temp_entry_ttl": 1,
- "max_entry_ttl": 3000000,
+ "max_entry_ttl": 700000,
"ledger_entries": [
[
{
@@ -469,7 +469,7 @@
},
"ext": "v0"
},
- 3000099
+ 700099
]
],
[
diff --git a/family_wallet/test_snapshots/test/test_instance_ttl_refreshed_on_add_member.1.json b/family_wallet/test_snapshots/test/test_instance_ttl_refreshed_on_add_member.1.json
index bdd13397..d7aaeaf0 100644
--- a/family_wallet/test_snapshots/test/test_instance_ttl_refreshed_on_add_member.1.json
+++ b/family_wallet/test_snapshots/test/test_instance_ttl_refreshed_on_add_member.1.json
@@ -65,7 +65,7 @@
"base_reserve": 10,
"min_persistent_entry_ttl": 1,
"min_temp_entry_ttl": 1,
- "max_entry_ttl": 3000000,
+ "max_entry_ttl": 700000,
"ledger_entries": [
[
{
@@ -591,7 +591,7 @@
},
"ext": "v0"
},
- 3000099
+ 700099
]
],
[
@@ -624,7 +624,7 @@
},
"ext": "v0"
},
- 3509999
+ 1209999
]
],
[
diff --git a/insurance/src/lib.rs b/insurance/src/lib.rs
index 8063705a..c4ca3277 100644
--- a/insurance/src/lib.rs
+++ b/insurance/src/lib.rs
@@ -1,9 +1,725 @@
-pub fn pay_premium(env: Env, policy_id: BytesN<32>) {
- let killswitch_id = get_killswitch_id(&env);
- let is_paused: bool = env.invoke_contract(&killswitch_id, &symbol_short!("is_paused"), vec![&env, Symbol::new(&env, "insurance")].into());
-
- if is_paused {
- panic!("Contract is currently paused for emergency maintenance.");
- }
- // ... rest of the logic
-}
\ No newline at end of file
+#![no_std]
+#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used))]
+
+use soroban_sdk::{
+ contract, contracterror, contractimpl, contracttype, symbol_short, Address, Env, Map, String,
+ Symbol, Vec,
+};
+
+const INSTANCE_LIFETIME_THRESHOLD: u32 = 17280;
+const INSTANCE_BUMP_AMOUNT: u32 = 518400;
+const MAX_BATCH_SIZE: u32 = 50;
+
+pub const DEFAULT_PAGE_LIMIT: u32 = 20;
+pub const MAX_PAGE_LIMIT: u32 = 50;
+
+#[contracterror]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
+#[repr(u32)]
+pub enum InsuranceError {
+ PolicyNotFound = 1,
+ Unauthorized = 2,
+ InvalidAmount = 3,
+ PolicyInactive = 4,
+ ContractPaused = 5,
+ FunctionPaused = 6,
+ InvalidTimestamp = 7,
+ BatchTooLarge = 8,
+ ScheduleNotFound = 9,
+}
+
+pub mod pause_functions {
+ use soroban_sdk::{symbol_short, Symbol};
+ pub const CREATE_POLICY: Symbol = symbol_short!("crt_pol");
+ pub const PAY_PREMIUM: Symbol = symbol_short!("pay_prem");
+ pub const DEACTIVATE: Symbol = symbol_short!("deact");
+ pub const CREATE_SCHED: Symbol = symbol_short!("crt_sch");
+ pub const MODIFY_SCHED: Symbol = symbol_short!("mod_sch");
+ pub const CANCEL_SCHED: Symbol = symbol_short!("can_sch");
+}
+
+#[contracttype]
+#[derive(Clone)]
+pub struct InsurancePolicy {
+ pub id: u32,
+ pub owner: Address,
+ pub name: String,
+ pub external_ref: Option,
+ pub coverage_type: String,
+ pub monthly_premium: i128,
+ pub coverage_amount: i128,
+ pub active: bool,
+ pub next_payment_date: u64,
+ pub schedule_id: Option,
+ pub tags: Vec,
+}
+
+#[contracttype]
+#[derive(Clone)]
+pub struct PolicyPage {
+ pub items: Vec,
+ pub next_cursor: u32,
+ pub count: u32,
+}
+
+#[contracttype]
+#[derive(Clone)]
+pub struct PremiumSchedule {
+ pub id: u32,
+ pub owner: Address,
+ pub policy_id: u32,
+ pub next_due: u64,
+ pub interval: u64,
+ pub recurring: bool,
+ pub active: bool,
+ pub created_at: u64,
+ pub last_executed: Option,
+ pub missed_count: u32,
+}
+
+#[contracttype]
+#[derive(Clone)]
+pub enum InsuranceEvent {
+ PolicyCreated,
+ PremiumPaid,
+ PolicyDeactivated,
+ ExternalRefUpdated,
+ ScheduleCreated,
+ ScheduleExecuted,
+ ScheduleMissed,
+ ScheduleModified,
+ ScheduleCancelled,
+}
+
+#[contract]
+pub struct Insurance;
+
+#[contractimpl]
+impl Insurance {
+ fn clamp_limit(limit: u32) -> u32 {
+ if limit == 0 {
+ DEFAULT_PAGE_LIMIT
+ } else if limit > MAX_PAGE_LIMIT {
+ MAX_PAGE_LIMIT
+ } else {
+ limit
+ }
+ }
+
+ fn extend_instance_ttl(env: &Env) {
+ env.storage()
+ .instance()
+ .extend_ttl(INSTANCE_LIFETIME_THRESHOLD, INSTANCE_BUMP_AMOUNT);
+ }
+
+ fn get_pause_admin(env: &Env) -> Option {
+ env.storage().instance().get(&symbol_short!("PAUSE_ADM"))
+ }
+
+ fn get_global_paused(env: &Env) -> bool {
+ env.storage()
+ .instance()
+ .get(&symbol_short!("PAUSED"))
+ .unwrap_or(false)
+ }
+
+ fn is_function_paused(env: &Env, func: Symbol) -> bool {
+ env.storage()
+ .instance()
+ .get::<_, Map>(&symbol_short!("PAUSED_FN"))
+ .unwrap_or_else(|| Map::new(env))
+ .get(func)
+ .unwrap_or(false)
+ }
+
+ fn require_not_paused(env: &Env, func: Symbol) -> Result<(), InsuranceError> {
+ if Self::get_global_paused(env) {
+ return Err(InsuranceError::ContractPaused);
+ }
+ if Self::is_function_paused(env, func) {
+ return Err(InsuranceError::FunctionPaused);
+ }
+ Ok(())
+ }
+
+ pub fn set_pause_admin(
+ env: Env,
+ caller: Address,
+ new_admin: Address,
+ ) -> Result<(), InsuranceError> {
+ caller.require_auth();
+ let current = Self::get_pause_admin(&env);
+ match current {
+ None => {
+ if caller != new_admin {
+ return Err(InsuranceError::Unauthorized);
+ }
+ }
+ Some(admin) if admin != caller => return Err(InsuranceError::Unauthorized),
+ _ => {}
+ }
+ env.storage()
+ .instance()
+ .set(&symbol_short!("PAUSE_ADM"), &new_admin);
+ Ok(())
+ }
+
+ pub fn pause(env: Env, caller: Address) -> Result<(), InsuranceError> {
+ caller.require_auth();
+ let admin = Self::get_pause_admin(&env).ok_or(InsuranceError::Unauthorized)?;
+ if admin != caller {
+ return Err(InsuranceError::Unauthorized);
+ }
+ env.storage()
+ .instance()
+ .set(&symbol_short!("PAUSED"), &true);
+ Ok(())
+ }
+
+ pub fn unpause(env: Env, caller: Address) -> Result<(), InsuranceError> {
+ caller.require_auth();
+ let admin = Self::get_pause_admin(&env).ok_or(InsuranceError::Unauthorized)?;
+ if admin != caller {
+ return Err(InsuranceError::Unauthorized);
+ }
+ env.storage()
+ .instance()
+ .set(&symbol_short!("PAUSED"), &false);
+ Ok(())
+ }
+
+ pub fn create_policy(
+ env: Env,
+ owner: Address,
+ name: String,
+ coverage_type: String,
+ monthly_premium: i128,
+ coverage_amount: i128,
+ ) -> Result {
+ owner.require_auth();
+ Self::require_not_paused(&env, pause_functions::CREATE_POLICY)?;
+
+ if monthly_premium <= 0 || coverage_amount <= 0 {
+ return Err(InsuranceError::InvalidAmount);
+ }
+
+ Self::extend_instance_ttl(&env);
+
+ let mut policies: Map = env
+ .storage()
+ .instance()
+ .get(&symbol_short!("POLICIES"))
+ .unwrap_or_else(|| Map::new(&env));
+
+ let next_id = env
+ .storage()
+ .instance()
+ .get(&symbol_short!("NEXT_ID"))
+ .unwrap_or(0u32)
+ + 1;
+
+ let policy = InsurancePolicy {
+ id: next_id,
+ owner: owner.clone(),
+ name,
+ external_ref: None,
+ coverage_type,
+ monthly_premium,
+ coverage_amount,
+ active: true,
+ next_payment_date: env.ledger().timestamp() + (30 * 86400),
+ schedule_id: None,
+ tags: Vec::new(&env),
+ };
+
+ policies.set(next_id, policy);
+ env.storage()
+ .instance()
+ .set(&symbol_short!("POLICIES"), &policies);
+ env.storage()
+ .instance()
+ .set(&symbol_short!("NEXT_ID"), &next_id);
+
+ env.events().publish(
+ (symbol_short!("insure"), InsuranceEvent::PolicyCreated),
+ (next_id, owner),
+ );
+
+ Ok(next_id)
+ }
+
+ pub fn pay_premium(env: Env, caller: Address, policy_id: u32) -> Result {
+ caller.require_auth();
+ Self::require_not_paused(&env, pause_functions::PAY_PREMIUM)?;
+ 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)
+ .ok_or(InsuranceError::PolicyNotFound)?;
+ if policy.owner != caller {
+ return Err(InsuranceError::Unauthorized);
+ }
+ if !policy.active {
+ return Err(InsuranceError::PolicyInactive);
+ }
+
+ policy.next_payment_date = env.ledger().timestamp() + (30 * 86400);
+ policies.set(policy_id, policy);
+ env.storage()
+ .instance()
+ .set(&symbol_short!("POLICIES"), &policies);
+
+ env.events().publish(
+ (symbol_short!("insure"), InsuranceEvent::PremiumPaid),
+ (policy_id, caller),
+ );
+
+ Ok(true)
+ }
+
+ pub fn batch_pay_premiums(
+ env: Env,
+ caller: Address,
+ policy_ids: Vec,
+ ) -> Result {
+ caller.require_auth();
+ Self::require_not_paused(&env, pause_functions::PAY_PREMIUM)?;
+ if policy_ids.len() > MAX_BATCH_SIZE {
+ return Err(InsuranceError::BatchTooLarge);
+ }
+ Self::extend_instance_ttl(&env);
+
+ let mut policies: Map = env
+ .storage()
+ .instance()
+ .get(&symbol_short!("POLICIES"))
+ .unwrap_or_else(|| Map::new(&env));
+
+ // Validate first to keep batch behavior atomic.
+ for policy_id in policy_ids.iter() {
+ let policy = policies
+ .get(policy_id)
+ .ok_or(InsuranceError::PolicyNotFound)?;
+ if policy.owner != caller {
+ return Err(InsuranceError::Unauthorized);
+ }
+ if !policy.active {
+ return Err(InsuranceError::PolicyInactive);
+ }
+ }
+
+ let next_due = env.ledger().timestamp() + (30 * 86400);
+ let mut count = 0u32;
+ for policy_id in policy_ids.iter() {
+ let mut policy = policies
+ .get(policy_id)
+ .ok_or(InsuranceError::PolicyNotFound)?;
+ policy.next_payment_date = next_due;
+ policies.set(policy_id, policy);
+ env.events().publish(
+ (symbol_short!("insure"), InsuranceEvent::PremiumPaid),
+ (policy_id, caller.clone()),
+ );
+ count += 1;
+ }
+
+ env.storage()
+ .instance()
+ .set(&symbol_short!("POLICIES"), &policies);
+ Ok(count)
+ }
+
+ pub fn get_policy(env: Env, policy_id: u32) -> Option {
+ let policies: Map = env
+ .storage()
+ .instance()
+ .get(&symbol_short!("POLICIES"))
+ .unwrap_or_else(|| Map::new(&env));
+ policies.get(policy_id)
+ }
+
+ pub fn get_active_policies(env: Env, owner: Address, cursor: u32, limit: u32) -> PolicyPage {
+ let limit = Self::clamp_limit(limit);
+ let policies: Map = env
+ .storage()
+ .instance()
+ .get(&symbol_short!("POLICIES"))
+ .unwrap_or_else(|| Map::new(&env));
+
+ let mut staging: Vec<(u32, InsurancePolicy)> = Vec::new(&env);
+ for (id, p) in policies.iter() {
+ if id <= cursor || p.owner != owner || !p.active {
+ continue;
+ }
+ staging.push_back((id, p));
+ if staging.len() > limit {
+ break;
+ }
+ }
+
+ Self::build_page(&env, staging, limit)
+ }
+
+ pub fn get_all_policies_for_owner(
+ env: Env,
+ owner: Address,
+ cursor: u32,
+ limit: u32,
+ ) -> PolicyPage {
+ let limit = Self::clamp_limit(limit);
+ let policies: Map = env
+ .storage()
+ .instance()
+ .get(&symbol_short!("POLICIES"))
+ .unwrap_or_else(|| Map::new(&env));
+
+ let mut staging: Vec<(u32, InsurancePolicy)> = Vec::new(&env);
+ for (id, p) in policies.iter() {
+ if id <= cursor || p.owner != owner {
+ continue;
+ }
+ staging.push_back((id, p));
+ if staging.len() > limit {
+ break;
+ }
+ }
+
+ Self::build_page(&env, staging, limit)
+ }
+
+ fn build_page(env: &Env, staging: Vec<(u32, InsurancePolicy)>, limit: u32) -> PolicyPage {
+ let has_next = staging.len() > limit;
+ let take = if has_next {
+ staging.len() - 1
+ } else {
+ staging.len()
+ };
+
+ let mut items = Vec::new(env);
+ for i in 0..take {
+ if let Some((_, p)) = staging.get(i) {
+ items.push_back(p);
+ }
+ }
+
+ let mut next_cursor = 0u32;
+ if has_next {
+ if let Some((id, _)) = staging.get(take - 1) {
+ next_cursor = id;
+ }
+ }
+
+ PolicyPage {
+ count: items.len(),
+ items,
+ next_cursor,
+ }
+ }
+
+ pub fn get_total_monthly_premium(env: Env, owner: Address) -> i128 {
+ let policies: Map = env
+ .storage()
+ .instance()
+ .get(&symbol_short!("POLICIES"))
+ .unwrap_or_else(|| Map::new(&env));
+
+ let mut total = 0i128;
+ for (_, p) in policies.iter() {
+ if p.owner == owner && p.active {
+ total += p.monthly_premium;
+ }
+ }
+ total
+ }
+
+ pub fn deactivate_policy(
+ env: Env,
+ caller: Address,
+ policy_id: u32,
+ ) -> Result {
+ caller.require_auth();
+ Self::require_not_paused(&env, pause_functions::DEACTIVATE)?;
+
+ let mut policies: Map = env
+ .storage()
+ .instance()
+ .get(&symbol_short!("POLICIES"))
+ .unwrap_or_else(|| Map::new(&env));
+
+ let mut policy = policies
+ .get(policy_id)
+ .ok_or(InsuranceError::PolicyNotFound)?;
+ if policy.owner != caller {
+ return Err(InsuranceError::Unauthorized);
+ }
+
+ policy.active = false;
+ policies.set(policy_id, policy);
+ env.storage()
+ .instance()
+ .set(&symbol_short!("POLICIES"), &policies);
+
+ env.events().publish(
+ (symbol_short!("insure"), InsuranceEvent::PolicyDeactivated),
+ (policy_id, caller),
+ );
+
+ Ok(true)
+ }
+
+ pub fn set_external_ref(
+ env: Env,
+ caller: Address,
+ policy_id: u32,
+ external_ref: Option,
+ ) -> Result {
+ caller.require_auth();
+
+ let mut policies: Map = env
+ .storage()
+ .instance()
+ .get(&symbol_short!("POLICIES"))
+ .unwrap_or_else(|| Map::new(&env));
+
+ let mut policy = policies
+ .get(policy_id)
+ .ok_or(InsuranceError::PolicyNotFound)?;
+ if policy.owner != caller {
+ return Err(InsuranceError::Unauthorized);
+ }
+
+ policy.external_ref = external_ref;
+ policies.set(policy_id, policy);
+ env.storage()
+ .instance()
+ .set(&symbol_short!("POLICIES"), &policies);
+
+ Ok(true)
+ }
+
+ pub fn create_premium_schedule(
+ env: Env,
+ owner: Address,
+ policy_id: u32,
+ next_due: u64,
+ interval: u64,
+ ) -> Result {
+ owner.require_auth();
+ Self::require_not_paused(&env, pause_functions::CREATE_SCHED)?;
+
+ let mut policies: Map = env
+ .storage()
+ .instance()
+ .get(&symbol_short!("POLICIES"))
+ .unwrap_or_else(|| Map::new(&env));
+ let mut policy = policies
+ .get(policy_id)
+ .ok_or(InsuranceError::PolicyNotFound)?;
+
+ if policy.owner != owner {
+ return Err(InsuranceError::Unauthorized);
+ }
+ if next_due <= env.ledger().timestamp() {
+ return Err(InsuranceError::InvalidTimestamp);
+ }
+
+ let mut schedules: Map = env
+ .storage()
+ .instance()
+ .get(&symbol_short!("PREM_SCH"))
+ .unwrap_or_else(|| Map::new(&env));
+
+ let schedule_id = env
+ .storage()
+ .instance()
+ .get(&symbol_short!("NEXT_PSCH"))
+ .unwrap_or(0u32)
+ + 1;
+
+ let schedule = PremiumSchedule {
+ id: schedule_id,
+ owner: owner.clone(),
+ policy_id,
+ next_due,
+ interval,
+ recurring: interval > 0,
+ active: true,
+ created_at: env.ledger().timestamp(),
+ last_executed: None,
+ missed_count: 0,
+ };
+
+ schedules.set(schedule_id, schedule);
+ policy.schedule_id = Some(schedule_id);
+ policies.set(policy_id, policy);
+
+ env.storage()
+ .instance()
+ .set(&symbol_short!("PREM_SCH"), &schedules);
+ env.storage()
+ .instance()
+ .set(&symbol_short!("NEXT_PSCH"), &schedule_id);
+ env.storage()
+ .instance()
+ .set(&symbol_short!("POLICIES"), &policies);
+
+ Ok(schedule_id)
+ }
+
+ pub fn modify_premium_schedule(
+ env: Env,
+ caller: Address,
+ schedule_id: u32,
+ next_due: u64,
+ interval: u64,
+ ) -> Result {
+ caller.require_auth();
+ Self::require_not_paused(&env, pause_functions::MODIFY_SCHED)?;
+
+ if next_due <= env.ledger().timestamp() {
+ return Err(InsuranceError::InvalidTimestamp);
+ }
+
+ let mut schedules: Map = env
+ .storage()
+ .instance()
+ .get(&symbol_short!("PREM_SCH"))
+ .unwrap_or_else(|| Map::new(&env));
+
+ let mut schedule = schedules
+ .get(schedule_id)
+ .ok_or(InsuranceError::ScheduleNotFound)?;
+ if schedule.owner != caller {
+ return Err(InsuranceError::Unauthorized);
+ }
+
+ schedule.next_due = next_due;
+ schedule.interval = interval;
+ schedule.recurring = interval > 0;
+ schedules.set(schedule_id, schedule);
+ env.storage()
+ .instance()
+ .set(&symbol_short!("PREM_SCH"), &schedules);
+
+ Ok(true)
+ }
+
+ pub fn cancel_premium_schedule(
+ env: Env,
+ caller: Address,
+ schedule_id: u32,
+ ) -> Result {
+ caller.require_auth();
+ Self::require_not_paused(&env, pause_functions::CANCEL_SCHED)?;
+
+ let mut schedules: Map = env
+ .storage()
+ .instance()
+ .get(&symbol_short!("PREM_SCH"))
+ .unwrap_or_else(|| Map::new(&env));
+
+ let mut schedule = schedules
+ .get(schedule_id)
+ .ok_or(InsuranceError::ScheduleNotFound)?;
+ if schedule.owner != caller {
+ return Err(InsuranceError::Unauthorized);
+ }
+
+ schedule.active = false;
+ schedules.set(schedule_id, schedule);
+ env.storage()
+ .instance()
+ .set(&symbol_short!("PREM_SCH"), &schedules);
+
+ Ok(true)
+ }
+
+ pub fn execute_due_premium_schedules(env: Env) -> Vec {
+ let now = env.ledger().timestamp();
+ let mut executed = Vec::new(&env);
+
+ let mut schedules: Map = env
+ .storage()
+ .instance()
+ .get(&symbol_short!("PREM_SCH"))
+ .unwrap_or_else(|| Map::new(&env));
+
+ let mut policies: Map = env
+ .storage()
+ .instance()
+ .get(&symbol_short!("POLICIES"))
+ .unwrap_or_else(|| Map::new(&env));
+
+ for (schedule_id, mut schedule) in schedules.iter() {
+ if !schedule.active || schedule.next_due > now {
+ continue;
+ }
+
+ if let Some(mut policy) = policies.get(schedule.policy_id) {
+ if policy.active {
+ policy.next_payment_date = now + (30 * 86400);
+ policies.set(schedule.policy_id, policy);
+ }
+ }
+
+ schedule.last_executed = Some(now);
+ if schedule.recurring && schedule.interval > 0 {
+ let mut missed = 0u32;
+ let mut next = schedule.next_due + schedule.interval;
+ while next <= now {
+ missed += 1;
+ next += schedule.interval;
+ }
+ schedule.missed_count += missed;
+ schedule.next_due = next;
+ } else {
+ schedule.active = false;
+ }
+
+ schedules.set(schedule_id, schedule);
+ executed.push_back(schedule_id);
+ }
+
+ env.storage()
+ .instance()
+ .set(&symbol_short!("PREM_SCH"), &schedules);
+ env.storage()
+ .instance()
+ .set(&symbol_short!("POLICIES"), &policies);
+ executed
+ }
+
+ pub fn get_premium_schedules(env: Env, owner: Address) -> Vec {
+ let schedules: Map = env
+ .storage()
+ .instance()
+ .get(&symbol_short!("PREM_SCH"))
+ .unwrap_or_else(|| Map::new(&env));
+
+ let mut result = Vec::new(&env);
+ for (_, schedule) in schedules.iter() {
+ if schedule.owner == owner {
+ result.push_back(schedule);
+ }
+ }
+ result
+ }
+
+ pub fn get_premium_schedule(env: Env, schedule_id: u32) -> Option {
+ let schedules: Map = env
+ .storage()
+ .instance()
+ .get(&symbol_short!("PREM_SCH"))
+ .unwrap_or_else(|| Map::new(&env));
+ schedules.get(schedule_id)
+ }
+}
+
+#[cfg(test)]
+mod test;
diff --git a/insurance/src/test.rs b/insurance/src/test.rs
index ec536c69..6183ba3e 100644
--- a/insurance/src/test.rs
+++ b/insurance/src/test.rs
@@ -1,1679 +1,199 @@
-#![cfg(test)]
-
use super::*;
-use crate::InsuranceError;
use soroban_sdk::{
- testutils::{Address as AddressTrait, Ledger, LedgerInfo},
+ testutils::{Address as _, Ledger},
Address, Env, String,
};
-use proptest::prelude::*;
-
-use testutils::{set_ledger_time, setup_test_env};
-// Removed local set_time in favor of testutils::set_ledger_time
+fn setup() -> (Env, Address, Address) {
+ let env = Env::default();
+ env.mock_all_auths();
+ let id = env.register_contract(None, Insurance);
+ let owner = Address::generate(&env);
+ (env, id, owner)
+}
#[test]
-fn test_create_policy_succeeds() {
- setup_test_env!(env, Insurance, InsuranceClient, client, owner);
-
- let name = String::from_str(&env, "Health Policy");
- let coverage_type = CoverageType::Health;
+fn create_policy_and_get_policy() {
+ let (env, id, owner) = setup();
+ let client = InsuranceClient::new(&env, &id);
- let policy_id = client.create_policy(
+ let pid = client.create_policy(
&owner,
- &name,
- &coverage_type,
- &100, // monthly_premium
- &10000, // coverage_amount
+ &String::from_str(&env, "Health"),
+ &String::from_str(&env, "health"),
+ &100,
+ &10_000,
);
- assert_eq!(policy_id, 1);
-
- let policy = client.get_policy(&policy_id).unwrap();
- assert_eq!(policy.owner, owner);
- assert_eq!(policy.monthly_premium, 100);
- assert_eq!(policy.coverage_amount, 10000);
- assert!(policy.active);
+ assert_eq!(pid, 1);
+ let p = client.get_policy(&pid).unwrap();
+ assert_eq!(p.owner, owner);
+ assert_eq!(p.monthly_premium, 100);
+ assert!(p.active);
}
#[test]
-#[should_panic(expected = "Monthly premium must be positive")]
-fn test_create_policy_invalid_premium() {
- let env = Env::default();
- let contract_id = env.register_contract(None, Insurance);
- let client = InsuranceClient::new(&env, &contract_id);
- let owner = Address::generate(&env);
-
- env.mock_all_auths();
+fn create_policy_rejects_invalid_amounts() {
+ let (env, id, owner) = setup();
+ let client = InsuranceClient::new(&env, &id);
- client.create_policy(
- let result = client.try_create_policy(
+ let r1 = client.try_create_policy(
&owner,
&String::from_str(&env, "Bad"),
- &String::from_str(&env, "Type"),
+ &String::from_str(&env, "life"),
&0,
- &10000,
+ &10,
);
-}
-
-#[test]
-#[should_panic(expected = "Coverage amount must be positive")]
- assert_eq!(result, Err(Ok(InsuranceError::InvalidPremium)));
-}
-
-#[test]
-fn test_create_policy_invalid_coverage() {
- let env = Env::default();
- let contract_id = env.register_contract(None, Insurance);
- let client = InsuranceClient::new(&env, &contract_id);
- let owner = Address::generate(&env);
-
- env.mock_all_auths();
+ assert_eq!(r1, Err(Ok(InsuranceError::InvalidAmount)));
- client.create_policy(
- let result = client.try_create_policy(
+ let r2 = client.try_create_policy(
&owner,
- &String::from_str(&env, "Bad"),
- &String::from_str(&env, "Type"),
- &100,
+ &String::from_str(&env, "Bad2"),
+ &String::from_str(&env, "life"),
+ &10,
&0,
);
- assert_eq!(result, Err(Ok(InsuranceError::InvalidCoverage)));
+ assert_eq!(r2, Err(Ok(InsuranceError::InvalidAmount)));
}
#[test]
-fn test_pay_premium() {
- let env = Env::default();
- let contract_id = env.register_contract(None, Insurance);
- let client = InsuranceClient::new(&env, &contract_id);
- let owner = Address::generate(&env);
+fn pay_premium_updates_next_payment_date() {
+ let (env, id, owner) = setup();
+ let client = InsuranceClient::new(&env, &id);
- env.mock_all_auths();
-
- let policy_id = client.create_policy(
+ let pid = client.create_policy(
&owner,
&String::from_str(&env, "Policy"),
- &String::from_str(&env, "Type"),
+ &String::from_str(&env, "auto"),
&100,
- &10000,
+ &10_000,
);
- // Initial next_payment_date is ~30 days from creation
- // We'll simulate passage of time is separate, but here we just check it updates
- let initial_policy = client.get_policy(&policy_id).unwrap();
- let initial_due = initial_policy.next_payment_date;
-
- // Advance ledger time to simulate paying slightly later
- set_ledger_time(&env, 1, env.ledger().timestamp() + 1000);
-
- client.pay_premium(&owner, &policy_id);
-
- let updated_policy = client.get_policy(&policy_id).unwrap();
-
- // New validation logic: new due date should be current timestamp + 30 days
- // Since we advanced timestamp by 1000, the new due date should be > initial due date
- assert!(updated_policy.next_payment_date > initial_due);
+ let before = client.get_policy(&pid).unwrap().next_payment_date;
+ env.ledger().set_timestamp(env.ledger().timestamp() + 500);
+ client.pay_premium(&owner, &pid);
+ let after = client.get_policy(&pid).unwrap().next_payment_date;
+ assert!(after > before);
}
#[test]
-#[should_panic(expected = "Only the policy owner can pay premiums")]
-fn test_pay_premium_unauthorized() {
- let env = Env::default();
- let contract_id = env.register_contract(None, Insurance);
- let client = InsuranceClient::new(&env, &contract_id);
- let owner = Address::generate(&env);
- let other = Address::generate(&env);
-
- env.mock_all_auths();
+fn deactivate_policy_blocks_premium_payment() {
+ let (env, id, owner) = setup();
+ let client = InsuranceClient::new(&env, &id);
- let policy_id = client.create_policy(
+ let pid = client.create_policy(
&owner,
&String::from_str(&env, "Policy"),
- &String::from_str(&env, "Type"),
+ &String::from_str(&env, "property"),
&100,
- &10000,
+ &10_000,
);
- // unauthorized payer
- client.pay_premium(&other, &policy_id);
- let result = client.try_pay_premium(&other, &policy_id);
- assert_eq!(result, Err(Ok(InsuranceError::Unauthorized)));
-}
+ let ok = client.deactivate_policy(&owner, &pid);
+ assert!(ok);
-#[test]
-fn test_deactivate_policy() {
- let env = Env::default();
- let contract_id = env.register_contract(None, Insurance);
- let client = InsuranceClient::new(&env, &contract_id);
- let owner = Address::generate(&env);
+ let result = client.try_pay_premium(&owner, &pid);
+ assert_eq!(result, Err(Ok(InsuranceError::PolicyInactive)));
- env.mock_all_auths();
-
- let policy_id = client.create_policy(
- &owner,
- &String::from_str(&env, "Policy"),
- &String::from_str(&env, "Type"),
- &100,
- &10000,
- );
-
- let success = client.deactivate_policy(&owner, &policy_id);
- assert!(success);
-
- let policy = client.get_policy(&policy_id).unwrap();
- assert!(!policy.active);
+ let p = client.get_policy(&pid).unwrap();
+ assert!(!p.active);
}
#[test]
-fn test_get_active_policies() {
- let env = Env::default();
- let contract_id = env.register_contract(None, Insurance);
- let client = InsuranceClient::new(&env, &contract_id);
- let owner = Address::generate(&env);
+fn active_policies_are_paginated_and_filtered() {
+ let (env, id, owner) = setup();
+ let client = InsuranceClient::new(&env, &id);
- env.mock_all_auths();
-
- // Create 3 policies
- client.create_policy(
+ let id1 = client.create_policy(
&owner,
&String::from_str(&env, "P1"),
- &String::from_str(&env, "T1"),
- &100,
- &1000,
- );
- let p2 = client.create_policy(
- &owner,
- &String::from_str(&env, "P2"),
- &String::from_str(&env, "T2"),
- &200,
- &2000,
- );
- client.create_policy(
- &owner,
- &String::from_str(&env, "P3"),
- &String::from_str(&env, "T3"),
- &300,
- &3000,
- );
-
- // Deactivate P2
- client.deactivate_policy(&owner, &p2);
-
- let active = client.get_active_policies(&owner);
- assert_eq!(active.len(), 2);
-
- // Check specific IDs if needed, but length 2 confirms one was filtered
-}
-
-#[test]
-fn test_get_active_policies_excludes_deactivated() {
- let env = Env::default();
- let contract_id = env.register_contract(None, Insurance);
- let client = InsuranceClient::new(&env, &contract_id);
- let owner = Address::generate(&env);
-
- env.mock_all_auths();
-
- // Create policy 1 and policy 2 for the same owner
- let policy_id_1 = client.create_policy(
- &owner,
- &String::from_str(&env, "Policy 1"),
- &String::from_str(&env, "Type 1"),
- &100,
- &1000,
- );
- let policy_id_2 = client.create_policy(
- &owner,
- &String::from_str(&env, "Policy 2"),
- &String::from_str(&env, "Type 2"),
- &200,
- &2000,
- );
-
- // Deactivate policy 1
- client.deactivate_policy(&owner, &policy_id_1);
-
- // get_active_policies must return only the still-active policy
- let active = client.get_active_policies(&owner, &0, &DEFAULT_PAGE_LIMIT);
- assert_eq!(
- active.items.len(),
- 1,
- "get_active_policies must return exactly one policy"
- );
- let only = active.items.get(0).unwrap();
- assert_eq!(
- only.id, policy_id_2,
- "the returned policy must be the active one (policy_id_2)"
- );
- assert!(only.active, "returned policy must have active == true");
-}
-
-#[test]
-fn test_get_all_policies_for_owner_pagination() {
- let env = Env::default();
- let contract_id = env.register_contract(None, Insurance);
- let client = InsuranceClient::new(&env, &contract_id);
- let owner = Address::generate(&env);
- let other = Address::generate(&env);
-
- env.mock_all_auths();
-
- // Create 3 policies for owner
- client.create_policy(
- &owner,
- &String::from_str(&env, "P1"),
- &String::from_str(&env, "T1"),
+ &String::from_str(&env, "health"),
&100,
- &1000,
+ &10_000,
);
- let p2 = client.create_policy(
+ let id2 = client.create_policy(
&owner,
&String::from_str(&env, "P2"),
- &String::from_str(&env, "T2"),
+ &String::from_str(&env, "life"),
&200,
- &2000,
+ &20_000,
);
- client.create_policy(
+ let _id3 = client.create_policy(
&owner,
&String::from_str(&env, "P3"),
- &String::from_str(&env, "T3"),
+ &String::from_str(&env, "auto"),
&300,
- &3000,
+ &30_000,
);
- // Create 1 policy for other
- client.create_policy(
- &other,
- &String::from_str(&env, "Other P"),
- &String::from_str(&env, "Type"),
- &500,
- &5000,
- );
+ client.deactivate_policy(&owner, &id2);
- // Deactivate P2
- client.deactivate_policy(&owner, &p2);
-
- // get_all_policies_for_owner should return all 3 for owner
- let page = client.get_all_policies_for_owner(&owner, &0, &10);
- assert_eq!(page.items.len(), 3);
- assert_eq!(page.count, 3);
-
- // verify p2 is in the list and is inactive
- let mut found_p2 = false;
- for policy in page.items.iter() {
- if policy.id == p2 {
- found_p2 = true;
- assert!(!policy.active);
- }
+ let page = client.get_active_policies(&owner, &0, &10);
+ assert_eq!(page.count, 2);
+ for p in page.items.iter() {
+ assert!(p.active);
+ assert_eq!(p.owner, owner);
}
- assert!(found_p2);
+
+ let all_page = client.get_all_policies_for_owner(&owner, &0, &10);
+ assert_eq!(all_page.count, 3);
+ assert_eq!(id1, 1);
}
#[test]
-fn test_get_total_monthly_premium() {
- let env = Env::default();
- let contract_id = env.register_contract(None, Insurance);
- let client = InsuranceClient::new(&env, &contract_id);
- let owner = Address::generate(&env);
-
- env.mock_all_auths();
+fn monthly_premium_total_counts_only_active() {
+ let (env, id, owner) = setup();
+ let client = InsuranceClient::new(&env, &id);
- client.create_policy(
+ let p1 = client.create_policy(
&owner,
&String::from_str(&env, "P1"),
- &String::from_str(&env, "T1"),
+ &String::from_str(&env, "health"),
&100,
- &1000,
+ &10_000,
);
client.create_policy(
&owner,
&String::from_str(&env, "P2"),
- &String::from_str(&env, "T2"),
- &200,
- &2000,
- );
-
- let total = client.get_total_monthly_premium(&owner);
- assert_eq!(total, 300);
-}
-
-#[test]
-fn test_get_total_monthly_premium_zero_policies() {
- let env = Env::default();
- let contract_id = env.register_contract(None, Insurance);
- let client = InsuranceClient::new(&env, &contract_id);
- let owner = Address::generate(&env);
-
- env.mock_all_auths();
-
- // Fresh address with no policies
- let total = client.get_total_monthly_premium(&owner);
- assert_eq!(total, 0);
-}
-
-#[test]
-fn test_get_total_monthly_premium_one_policy() {
- let env = Env::default();
- let contract_id = env.register_contract(None, Insurance);
- let client = InsuranceClient::new(&env, &contract_id);
- let owner = Address::generate(&env);
-
- env.mock_all_auths();
-
- // Create one policy with monthly_premium = 500
- client.create_policy(
- &owner,
- &String::from_str(&env, "Single Policy"),
- &CoverageType::Health,
- &500,
- &10000,
- );
-
- let total = client.get_total_monthly_premium(&owner);
- assert_eq!(total, 500);
-}
-
-#[test]
-fn test_get_total_monthly_premium_multiple_active_policies() {
- let env = Env::default();
- let contract_id = env.register_contract(None, Insurance);
- let client = InsuranceClient::new(&env, &contract_id);
- let owner = Address::generate(&env);
-
- env.mock_all_auths();
-
- // Create three policies with premiums 100, 200, 300
- client.create_policy(
- &owner,
- &String::from_str(&env, "Policy 1"),
- &CoverageType::Health,
- &100,
- &1000,
- );
- client.create_policy(
- &owner,
- &String::from_str(&env, "Policy 2"),
- &CoverageType::Life,
- &200,
- &2000,
- );
- client.create_policy(
- &owner,
- &String::from_str(&env, "Policy 3"),
- &CoverageType::Auto,
- &300,
- &3000,
- );
-
- let total = client.get_total_monthly_premium(&owner);
- assert_eq!(total, 600); // 100 + 200 + 300
-}
-
-#[test]
-fn test_get_total_monthly_premium_deactivated_policy_excluded() {
- let env = Env::default();
- let contract_id = env.register_contract(None, Insurance);
- let client = InsuranceClient::new(&env, &contract_id);
- let owner = Address::generate(&env);
-
- env.mock_all_auths();
-
- // Create two policies with premiums 100 and 200
- let policy1 = client.create_policy(
- &owner,
- &String::from_str(&env, "Policy 1"),
- &CoverageType::Health,
- &100,
- &1000,
- );
- let policy2 = client.create_policy(
- &owner,
- &String::from_str(&env, "Policy 2"),
- &CoverageType::Life,
- &200,
- &2000,
- );
-
- // Verify total includes both policies initially
- let total_initial = client.get_total_monthly_premium(&owner);
- assert_eq!(total_initial, 300); // 100 + 200
-
- // Deactivate the first policy
- client.deactivate_policy(&owner, &policy1);
-
- // Verify total only includes the active policy
- let total_after_deactivation = client.get_total_monthly_premium(&owner);
- assert_eq!(total_after_deactivation, 200); // Only policy 2
-}
-
-#[test]
-fn test_get_total_monthly_premium_different_owner_isolation() {
- let env = Env::default();
- let contract_id = env.register_contract(None, Insurance);
- let client = InsuranceClient::new(&env, &contract_id);
- let owner_a = Address::generate(&env);
- let owner_b = Address::generate(&env);
-
- env.mock_all_auths();
-
- // Create policies for owner_a
- client.create_policy(
- &owner_a,
- &String::from_str(&env, "Policy A1"),
- &CoverageType::Health,
- &100,
- &1000,
- );
- client.create_policy(
- &owner_a,
- &String::from_str(&env, "Policy A2"),
- &CoverageType::Life,
+ &String::from_str(&env, "life"),
&200,
- &2000,
- );
-
- // Create policies for owner_b
- client.create_policy(
- &owner_b,
- &String::from_str(&env, "Policy B1"),
- &String::from_str(&env, "emergency"),
- &300,
- &3000,
+ &20_000,
);
- // Verify owner_a's total only includes their policies
- let total_a = client.get_total_monthly_premium(&owner_a);
- assert_eq!(total_a, 300); // 100 + 200
-
- // Verify owner_b's total only includes their policies
- let total_b = client.get_total_monthly_premium(&owner_b);
- assert_eq!(total_b, 300); // 300
-
- // Verify no cross-owner leakage
- assert_ne!(total_a, 0); // owner_a has policies
- assert_ne!(total_b, 0); // owner_b has policies
- assert_eq!(total_a, total_b); // Both have same total but different policies
+ assert_eq!(client.get_total_monthly_premium(&owner), 300);
+ client.deactivate_policy(&owner, &p1);
+ assert_eq!(client.get_total_monthly_premium(&owner), 200);
}
#[test]
-fn test_multiple_premium_payments() {
- let env = Env::default();
- let contract_id = env.register_contract(None, Insurance);
- let client = InsuranceClient::new(&env, &contract_id);
- let owner = Address::generate(&env);
+fn schedule_lifecycle_and_execution() {
+ let (env, id, owner) = setup();
+ let client = InsuranceClient::new(&env, &id);
- env.mock_all_auths();
-
- let policy_id = client.create_policy(
+ let pid = client.create_policy(
&owner,
- &String::from_str(&env, "LongTerm"),
- &String::from_str(&env, "Life"),
+ &String::from_str(&env, "Sched"),
+ &String::from_str(&env, "health"),
&100,
- &10000,
- );
-
- let p1 = client.get_policy(&policy_id).unwrap();
- let first_due = p1.next_payment_date;
-
- // First payment
- client.pay_premium(&owner, &policy_id);
-
- // Simulate time passing (still before next due)
- set_ledger_time(&env, 1, env.ledger().timestamp() + 5000);
-
- // Second payment
- client.pay_premium(&owner, &policy_id);
-
- let p2 = client.get_policy(&policy_id).unwrap();
-
- // The logic in contract sets next_payment_date to 'now + 30 days'
- // So paying twice in quick succession just pushes it to 30 days from the SECOND payment
- // It does NOT add 60 days from start. This test verifies that behavior.
- assert!(p2.next_payment_date > first_due);
- assert_eq!(
- p2.next_payment_date,
- env.ledger().timestamp() + (30 * 86400)
- );
-}
-
-#[test]
-fn test_create_premium_schedule_succeeds() {
- setup_test_env!(env, Insurance, InsuranceClient, client, owner);
- set_ledger_time(&env, 1000);
-
- let policy_id = client.create_policy(
- &owner,
- &String::from_str(&env, "Health Insurance"),
- &CoverageType::Health,
- &500,
- &50000,
- );
-
- let schedule_id = client.create_premium_schedule(&owner, &policy_id, &3000, &2592000);
- assert_eq!(schedule_id, 1);
-
- let schedule = client.get_premium_schedule(&schedule_id);
- assert!(schedule.is_some());
- let schedule = schedule.unwrap();
- assert_eq!(schedule.next_due, 3000);
- assert_eq!(schedule.interval, 2592000);
- assert!(schedule.active);
-}
-
-#[test]
-fn test_modify_premium_schedule() {
- let env = Env::default();
- let contract_id = env.register_contract(None, Insurance);
- let client = InsuranceClient::new(&env, &contract_id);
- let owner = ::generate(&env);
-
- env.mock_all_auths();
- set_ledger_time(&env, 1, 1000);
-
- let policy_id = client.create_policy(
- &owner,
- &String::from_str(&env, "Health Insurance"),
- &CoverageType::Health,
- &500,
- &50000,
- );
-
- let schedule_id = client.create_premium_schedule(&owner, &policy_id, &3000, &2592000);
- client.modify_premium_schedule(&owner, &schedule_id, &4000, &2678400);
-
- let schedule = client.get_premium_schedule(&schedule_id).unwrap();
- assert_eq!(schedule.next_due, 4000);
- assert_eq!(schedule.interval, 2678400);
-}
-
-#[test]
-fn test_cancel_premium_schedule() {
- let env = Env::default();
- let contract_id = env.register_contract(None, Insurance);
- let client = InsuranceClient::new(&env, &contract_id);
- let owner = ::generate(&env);
-
- env.mock_all_auths();
- set_ledger_time(&env, 1, 1000);
-
- let policy_id = client.create_policy(
- &owner,
- &String::from_str(&env, "Health Insurance"),
- &CoverageType::Health,
- &500,
- &50000,
+ &10_000,
);
- let schedule_id = client.create_premium_schedule(&owner, &policy_id, &3000, &2592000);
- client.cancel_premium_schedule(&owner, &schedule_id);
+ let now = env.ledger().timestamp();
+ let sid = client.create_premium_schedule(&owner, &pid, &(now + 1000), &2592000);
- let schedule = client.get_premium_schedule(&schedule_id).unwrap();
- assert!(!schedule.active);
-}
+ let sched = client.get_premium_schedule(&sid).unwrap();
+ assert!(sched.active);
-#[test]
-fn test_execute_due_premium_schedules() {
- let env = Env::default();
- let contract_id = env.register_contract(None, Insurance);
- let client = InsuranceClient::new(&env, &contract_id);
- let owner = ::generate(&env);
+ let modified = client.modify_premium_schedule(&owner, &sid, &(now + 1500), &2_678_400);
+ assert!(modified);
- env.mock_all_auths();
- set_ledger_time(&env, 1, 1000);
-
- let policy_id = client.create_policy(
- &owner,
- &String::from_str(&env, "Health Insurance"),
- &CoverageType::Health,
- &500,
- &50000,
- );
-
- let schedule_id = client.create_premium_schedule(&owner, &policy_id, &3000, &0);
-
- set_ledger_time(&env, 1, 3500);
+ env.ledger().set_timestamp(now + 2000);
let executed = client.execute_due_premium_schedules();
-
assert_eq!(executed.len(), 1);
- assert_eq!(executed.get(0).unwrap(), schedule_id);
-
- let policy = client.get_policy(&policy_id).unwrap();
- assert_eq!(policy.next_payment_date, 3500 + 30 * 86400);
-}
-
-#[test]
-fn test_execute_recurring_premium_schedule() {
- let env = Env::default();
- let contract_id = env.register_contract(None, Insurance);
- let client = InsuranceClient::new(&env, &contract_id);
- let owner = ::generate(&env);
-
- env.mock_all_auths();
- set_ledger_time(&env, 1, 1000);
-
- let policy_id = client.create_policy(
- &owner,
- &String::from_str(&env, "Health Insurance"),
- &String::from_str(&env, "health"),
- &500,
- &50000,
- );
-
- let schedule_id = client.create_premium_schedule(&owner, &policy_id, &3000, &2592000);
-
- set_ledger_time(&env, 1, 3500);
- client.execute_due_premium_schedules();
+ assert_eq!(executed.get(0).unwrap(), sid);
- let schedule = client.get_premium_schedule(&schedule_id).unwrap();
- assert!(schedule.active);
- assert_eq!(schedule.next_due, 3000 + 2592000);
+ let cancelled = client.cancel_premium_schedule(&owner, &sid);
+ assert!(cancelled);
+ let sched2 = client.get_premium_schedule(&sid).unwrap();
+ assert!(!sched2.active);
}
-
-#[test]
-fn test_execute_missed_premium_schedules() {
- let env = Env::default();
- let contract_id = env.register_contract(None, Insurance);
- let client = InsuranceClient::new(&env, &contract_id);
- let owner = ::generate(&env);
-
- env.mock_all_auths();
- set_ledger_time(&env, 1, 1000);
-
- let policy_id = client.create_policy(
- &owner,
- &String::from_str(&env, "Health Insurance"),
- &CoverageType::Health,
- &500,
- &50000,
- );
-
- let schedule_id = client.create_premium_schedule(&owner, &policy_id, &3000, &2592000);
-
- set_time(&env, 3000 + 2592000 * 3 + 100);
- client.execute_due_premium_schedules();
-
- let schedule = client.get_premium_schedule(&schedule_id).unwrap();
- assert_eq!(schedule.missed_count, 3);
- assert!(schedule.next_due > 3000 + 2592000 * 3);
-}
-
-#[test]
-fn test_get_premium_schedules() {
- let env = Env::default();
- let contract_id = env.register_contract(None, Insurance);
- let client = InsuranceClient::new(&env, &contract_id);
- let owner = ::generate(&env);
-
- env.mock_all_auths();
- set_ledger_time(&env, 1, 1000);
-
- let policy_id1 = client.create_policy(
- &owner,
- &String::from_str(&env, "Health Insurance"),
- &CoverageType::Health,
- &500,
- &50000,
- );
-
- let policy_id2 = client.create_policy(
- &owner,
- &String::from_str(&env, "Life Insurance"),
- &String::from_str(&env, "life"),
- &300,
- &100000,
- );
-
- client.create_premium_schedule(&owner, &policy_id1, &3000, &2592000);
- client.create_premium_schedule(&owner, &policy_id2, &4000, &2592000);
-
- // -----------------------------------------------------------------------
- // 3. create_policy — boundary conditions
- // -----------------------------------------------------------------------
-
- // --- Health min/max boundaries ---
-
- #[test]
- fn test_health_premium_at_minimum_boundary() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // min_premium for Health = 1_000_000
- client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &1_000_000i128,
- &10_000_000i128, // min coverage
- &None,
- );
- }
-
- #[test]
- fn test_health_premium_at_maximum_boundary() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // max_premium = 500_000_000; need coverage ≤ 500M * 12 * 500 = 3T (within 100B limit)
- client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &500_000_000i128,
- &100_000_000_000i128, // max coverage for Health
- &None,
- );
- }
-
- #[test]
- fn test_health_coverage_at_minimum_boundary() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &5_000_000i128,
- &10_000_000i128, // exactly min_coverage
- &None,
- );
- }
-
- #[test]
- fn test_health_coverage_at_maximum_boundary() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // max_coverage = 100_000_000_000; need premium ≥ 100B / (12*500) ≈ 16_666_667
- client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &500_000_000i128, // max premium to allow max coverage via ratio
- &100_000_000_000i128, // exactly max_coverage
- &None,
- );
- }
-
- // --- Life boundaries ---
-
- #[test]
- fn test_life_premium_at_minimum_boundary() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- client.create_policy(
- &caller,
- &String::from_str(&env, "Life Min"),
- &CoverageType::Life,
- &500_000i128, // min_premium
- &50_000_000i128, // min_coverage
- &None,
- );
- }
-
- #[test]
- fn test_liability_premium_at_minimum_boundary() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- client.create_policy(
- &caller,
- &String::from_str(&env, "Liability Min"),
- &CoverageType::Liability,
- &800_000i128, // min_premium
- &5_000_000i128, // min_coverage
- &None,
- );
- }
-
- // -----------------------------------------------------------------------
- // 4. create_policy — name validation
- // -----------------------------------------------------------------------
-
- #[test]
- #[should_panic(expected = "name cannot be empty")]
- fn test_create_policy_empty_name_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- client.create_policy(
- &caller,
- &String::from_str(&env, ""),
- &CoverageType::Health,
- &5_000_000i128,
- &50_000_000i128,
- &None,
- );
- }
-
- #[test]
- #[should_panic(expected = "name too long")]
- fn test_create_policy_name_exceeds_max_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // 65 character name — exceeds MAX_NAME_LEN (64)
- let long_name = String::from_str(
- &env,
- "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1",
- );
- client.create_policy(
- &caller,
- &long_name,
- &CoverageType::Health,
- &5_000_000i128,
- &50_000_000i128,
- &None,
- );
- }
-
- #[test]
- fn test_create_policy_name_at_max_length_succeeds() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // Exactly 64 characters
- let max_name = String::from_str(
- &env,
- "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
- );
- client.create_policy(
- &caller,
- &max_name,
- &CoverageType::Health,
- &5_000_000i128,
- &50_000_000i128,
- &None,
- );
- }
-
- // -----------------------------------------------------------------------
- // 5. create_policy — premium validation failures
- // -----------------------------------------------------------------------
-
- #[test]
- #[should_panic(expected = "monthly_premium must be positive")]
- fn test_create_policy_zero_premium_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &0i128,
- &50_000_000i128,
- &None,
- );
- }
-
- #[test]
- #[should_panic(expected = "monthly_premium must be positive")]
- fn test_create_policy_negative_premium_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &-1i128,
- &50_000_000i128,
- &None,
- );
- }
-
- #[test]
- #[should_panic(expected = "monthly_premium out of range for coverage type")]
- fn test_create_health_policy_premium_below_min_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // Health min_premium = 1_000_000; supply 999_999
- client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &999_999i128,
- &50_000_000i128,
- &None,
- );
- }
-
- #[test]
- #[should_panic(expected = "monthly_premium out of range for coverage type")]
- fn test_create_health_policy_premium_above_max_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // Health max_premium = 500_000_000; supply 500_000_001
- client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &500_000_001i128,
- &10_000_000i128,
- &None,
- );
- }
-
- #[test]
- #[should_panic(expected = "monthly_premium out of range for coverage type")]
- fn test_create_life_policy_premium_below_min_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // Life min_premium = 500_000; supply 499_999
- client.create_policy(
- &caller,
- &String::from_str(&env, "Life"),
- &CoverageType::Life,
- &499_999i128,
- &50_000_000i128,
- &None,
- );
- }
-
- #[test]
- #[should_panic(expected = "monthly_premium out of range for coverage type")]
- fn test_create_property_policy_premium_below_min_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // Property min_premium = 2_000_000; supply 1_999_999
- client.create_policy(
- &caller,
- &String::from_str(&env, "Property"),
- &CoverageType::Property,
- &1_999_999i128,
- &100_000_000i128,
- &None,
- );
- }
-
- #[test]
- #[should_panic(expected = "monthly_premium out of range for coverage type")]
- fn test_create_auto_policy_premium_below_min_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // Auto min_premium = 1_500_000; supply 1_499_999
- client.create_policy(
- &caller,
- &String::from_str(&env, "Auto"),
- &CoverageType::Auto,
- &1_499_999i128,
- &20_000_000i128,
- &None,
- );
- }
-
- #[test]
- #[should_panic(expected = "monthly_premium out of range for coverage type")]
- fn test_create_liability_policy_premium_below_min_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // Liability min_premium = 800_000; supply 799_999
- client.create_policy(
- &caller,
- &String::from_str(&env, "Liability"),
- &CoverageType::Liability,
- &799_999i128,
- &5_000_000i128,
- &None,
- );
- }
-
- // -----------------------------------------------------------------------
- // 6. create_policy — coverage amount validation failures
- // -----------------------------------------------------------------------
-
- #[test]
- #[should_panic(expected = "coverage_amount must be positive")]
- fn test_create_policy_zero_coverage_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &5_000_000i128,
- &0i128,
- &None,
- );
- }
-
- #[test]
- #[should_panic(expected = "coverage_amount must be positive")]
- fn test_create_policy_negative_coverage_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &5_000_000i128,
- &-1i128,
- &None,
- );
- }
-
- #[test]
- #[should_panic(expected = "coverage_amount out of range for coverage type")]
- fn test_create_health_policy_coverage_below_min_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // Health min_coverage = 10_000_000; supply 9_999_999
- client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &5_000_000i128,
- &9_999_999i128,
- &None,
- );
- }
-
- #[test]
- #[should_panic(expected = "coverage_amount out of range for coverage type")]
- fn test_create_health_policy_coverage_above_max_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // Health max_coverage = 100_000_000_000; supply 100_000_000_001
- client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &500_000_000i128,
- &100_000_000_001i128,
- &None,
- );
- }
-
- #[test]
- #[should_panic(expected = "coverage_amount out of range for coverage type")]
- fn test_create_life_policy_coverage_below_min_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // Life min_coverage = 50_000_000; supply 49_999_999
- client.create_policy(
- &caller,
- &String::from_str(&env, "Life"),
- &CoverageType::Life,
- &1_000_000i128,
- &49_999_999i128,
- &None,
- );
- }
-
- #[test]
- #[should_panic(expected = "coverage_amount out of range for coverage type")]
- fn test_create_property_policy_coverage_below_min_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // Property min_coverage = 100_000_000; supply 99_999_999
- client.create_policy(
- &caller,
- &String::from_str(&env, "Property"),
- &CoverageType::Property,
- &5_000_000i128,
- &99_999_999i128,
- &None,
- );
- }
-
- // -----------------------------------------------------------------------
- // 7. create_policy — ratio guard (unsupported combination)
- // -----------------------------------------------------------------------
-
- #[test]
- #[should_panic(expected = "unsupported combination: coverage_amount too high relative to premium")]
- fn test_create_policy_coverage_too_high_for_premium_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // premium = 1_000_000 → annual = 12_000_000 → max_coverage = 6_000_000_000
- // supply coverage = 6_000_000_001 (just over the ratio limit, but within Health's hard max)
- // Need premium high enough so health range isn't hit, but ratio is
- // Health max_coverage = 100_000_000_000
- // Use premium = 1_000_000, coverage = 7_000_000_000 → over ratio (6B), under hard cap (100B)
- client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &1_000_000i128,
- &7_000_000_000i128,
- &None,
- );
- }
-
- #[test]
- fn test_create_policy_coverage_exactly_at_ratio_limit_succeeds() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // premium = 1_000_000 → ratio limit = 1M * 12 * 500 = 6_000_000_000
- // Health max_coverage = 100B, so 6B is fine
- client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &1_000_000i128,
- &6_000_000_000i128,
- &None,
- );
- }
-
- // -----------------------------------------------------------------------
- // 8. External ref validation
- // -----------------------------------------------------------------------
-
- #[test]
- #[should_panic(expected = "external_ref length out of range")]
- fn test_create_policy_ext_ref_too_long_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // 129 character external ref — exceeds MAX_EXT_REF_LEN (128)
- let long_ref = String::from_str(
- &env,
- "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1",
- );
- client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &5_000_000i128,
- &50_000_000i128,
- &Some(long_ref),
- );
- }
-
- #[test]
- fn test_create_policy_ext_ref_at_max_length_succeeds() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // Exactly 128 characters
- let max_ref = String::from_str(
- &env,
- "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
- );
- client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &5_000_000i128,
- &50_000_000i128,
- &Some(max_ref),
- );
- }
-
- // -----------------------------------------------------------------------
- // 9. pay_premium — happy path
- // -----------------------------------------------------------------------
-
- #[test]
- fn test_pay_premium_success() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- let id = client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &5_000_000i128,
- &50_000_000i128,
- &None,
- );
- let result = client.pay_premium(&caller, &id, &5_000_000i128);
- assert!(result);
- }
-
- #[test]
- fn test_pay_premium_updates_next_payment_date() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- env.ledger().set_timestamp(1_000_000u64);
- let id = client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &5_000_000i128,
- &50_000_000i128,
- &None,
- );
- env.ledger().set_timestamp(2_000_000u64);
- client.pay_premium(&caller, &id, &5_000_000i128);
- let policy = client.get_policy(&id);
- // next_payment_due should be 2_000_000 + 30 days
- assert_eq!(policy.next_payment_due, 2_000_000 + 30 * 24 * 60 * 60);
- assert_eq!(policy.last_payment_at, 2_000_000u64);
- }
-
- // -----------------------------------------------------------------------
- // 10. pay_premium — failure cases
- // -----------------------------------------------------------------------
-
- #[test]
- #[should_panic(expected = "policy not found")]
- fn test_pay_premium_nonexistent_policy_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- client.pay_premium(&caller, &999u32, &5_000_000i128);
- }
-
- #[test]
- #[should_panic(expected = "amount must equal monthly_premium")]
- fn test_pay_premium_wrong_amount_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- let id = client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &5_000_000i128,
- &50_000_000i128,
- &None,
- );
- client.pay_premium(&caller, &id, &4_999_999i128);
- }
-
- #[test]
- #[should_panic(expected = "policy inactive")]
- fn test_pay_premium_on_inactive_policy_panics() {
- let (env, client, owner) = setup();
- let caller = Address::generate(&env);
- let id = client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &5_000_000i128,
- &50_000_000i128,
- &None,
- );
- client.deactivate_policy(&owner, &id);
- client.pay_premium(&caller, &id, &5_000_000i128);
- }
-
- // -----------------------------------------------------------------------
- // 11. deactivate_policy — happy path
- // -----------------------------------------------------------------------
-
- #[test]
- fn test_deactivate_policy_success() {
- let (env, client, owner) = setup();
- let caller = Address::generate(&env);
- let id = client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &5_000_000i128,
- &50_000_000i128,
- &None,
- );
- let result = client.deactivate_policy(&owner, &id);
- assert!(result);
-
- let policy = client.get_policy(&id);
- assert!(!policy.active);
- }
-
- #[test]
- fn test_deactivate_removes_from_active_list() {
- let (env, client, owner) = setup();
- let caller = Address::generate(&env);
- let id = client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &5_000_000i128,
- &50_000_000i128,
- &None,
- );
- assert_eq!(client.get_active_policies().len(), 1);
- client.deactivate_policy(&owner, &id);
- assert_eq!(client.get_active_policies().len(), 0);
- }
-
- // -----------------------------------------------------------------------
- // 12. deactivate_policy — failure cases
- // -----------------------------------------------------------------------
-
- #[test]
- #[should_panic(expected = "unauthorized")]
- fn test_deactivate_policy_non_owner_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- let id = client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &5_000_000i128,
- &50_000_000i128,
- &None,
- );
- let non_owner = Address::generate(&env);
- client.deactivate_policy(&non_owner, &id);
- }
-
- #[test]
- #[should_panic(expected = "policy not found")]
- fn test_deactivate_nonexistent_policy_panics() {
- let (_env, client, owner) = setup();
- client.deactivate_policy(&owner, &999u32);
- }
-
- #[test]
- #[should_panic(expected = "policy already inactive")]
- fn test_deactivate_already_inactive_policy_panics() {
- let (env, client, owner) = setup();
- let caller = Address::generate(&env);
- let id = client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &5_000_000i128,
- &50_000_000i128,
- &None,
- );
- client.deactivate_policy(&owner, &id);
- // Second deactivation must panic
- client.deactivate_policy(&owner, &id);
- }
-
- // -----------------------------------------------------------------------
- // 13. set_external_ref
- // -----------------------------------------------------------------------
-
- #[test]
- fn test_set_external_ref_success() {
- let (env, client, owner) = setup();
- let caller = Address::generate(&env);
- let id = client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &5_000_000i128,
- &50_000_000i128,
- &None,
- );
- let new_ref = String::from_str(&env, "NEW-REF-001");
- client.set_external_ref(&owner, &id, &Some(new_ref));
- let policy = client.get_policy(&id);
- assert!(policy.external_ref.is_some());
- }
-
- #[test]
- fn test_set_external_ref_clear() {
- let (env, client, owner) = setup();
- let caller = Address::generate(&env);
- let ext_ref = String::from_str(&env, "INITIAL-REF");
- let id = client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &5_000_000i128,
- &50_000_000i128,
- &Some(ext_ref),
- );
- // Clear the ref
- client.set_external_ref(&owner, &id, &None);
- let policy = client.get_policy(&id);
- assert!(policy.external_ref.is_none());
- }
-
- #[test]
- #[should_panic(expected = "unauthorized")]
- fn test_set_external_ref_non_owner_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- let id = client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &5_000_000i128,
- &50_000_000i128,
- &None,
- );
- let non_owner = Address::generate(&env);
- let new_ref = String::from_str(&env, "HACK");
- client.set_external_ref(&non_owner, &id, &Some(new_ref));
- }
-
- #[test]
- #[should_panic(expected = "external_ref length out of range")]
- fn test_set_external_ref_too_long_panics() {
- let (env, client, owner) = setup();
- let caller = Address::generate(&env);
- let id = client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &5_000_000i128,
- &50_000_000i128,
- &None,
- );
- let long_ref = String::from_str(
- &env,
- "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1",
- );
- client.set_external_ref(&owner, &id, &Some(long_ref));
- }
-
- // -----------------------------------------------------------------------
- // 14. Queries
- // -----------------------------------------------------------------------
-
- #[test]
- fn test_get_active_policies_empty_initially() {
- let (_env, client, _owner) = setup();
- assert_eq!(client.get_active_policies().len(), 0);
- }
-
- #[test]
- fn test_get_active_policies_reflects_creates_and_deactivations() {
- let (env, client, owner) = setup();
- let caller = Address::generate(&env);
- let id1 = client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &5_000_000i128,
- &50_000_000i128,
- &None,
- );
- client.create_policy(
- &caller,
- &String::from_str(&env, "Second Policy"),
- &CoverageType::Life,
- &1_000_000i128,
- &60_000_000i128,
- &None,
- );
- assert_eq!(client.get_active_policies().len(), 2);
- client.deactivate_policy(&owner, &id1);
- assert_eq!(client.get_active_policies().len(), 1);
- }
-
- #[test]
- fn test_get_total_monthly_premium_sums_active_only() {
- let (env, client, owner) = setup();
- let caller = Address::generate(&env);
- let id1 = client.create_policy(
- &caller,
- &short_name(&env),
- &CoverageType::Health,
- &5_000_000i128,
- &50_000_000i128,
- &None,
- );
- client.create_policy(
- &caller,
- &String::from_str(&env, "Second"),
- &CoverageType::Life,
- &1_000_000i128,
- &60_000_000i128,
- &None,
- );
- assert_eq!(client.get_total_monthly_premium(), 6_000_000i128);
- client.deactivate_policy(&owner, &id1);
- assert_eq!(client.get_total_monthly_premium(), 1_000_000i128);
- }
-
- #[test]
- fn test_get_total_monthly_premium_zero_when_no_policies() {
- let (_env, client, _owner) = setup();
- assert_eq!(client.get_total_monthly_premium(), 0i128);
- }
-
- #[test]
- #[should_panic(expected = "policy not found")]
- fn test_get_policy_nonexistent_panics() {
- let (_env, client, _owner) = setup();
- client.get_policy(&999u32);
- }
-
- // -----------------------------------------------------------------------
- // 15. Uninitialized contract guard
- // -----------------------------------------------------------------------
-
- #[test]
- #[should_panic(expected = "not initialized")]
- fn test_create_policy_without_init_panics() {
- let env = Env::default();
- env.mock_all_auths();
- let contract_id = env.register_contract(None, InsuranceContract);
- let client = InsuranceContractClient::new(&env, &contract_id);
- let caller = Address::generate(&env);
- client.create_policy(
- &caller,
- &String::from_str(&env, "Test"),
- &CoverageType::Health,
- &5_000_000i128,
- &50_000_000i128,
- &None,
- );
- }
-
- #[test]
- #[should_panic(expected = "not initialized")]
- fn test_get_active_policies_without_init_panics() {
- let env = Env::default();
- env.mock_all_auths();
- let contract_id = env.register_contract(None, InsuranceContract);
- let client = InsuranceContractClient::new(&env, &contract_id);
- client.get_active_policies();
- }
-
- // -----------------------------------------------------------------------
- // 16. Policy data integrity
- // -----------------------------------------------------------------------
-
- #[test]
- fn test_policy_fields_stored_correctly() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- env.ledger().set_timestamp(1_700_000_000u64);
- let id = client.create_policy(
- &caller,
- &String::from_str(&env, "My Health Plan"),
- &CoverageType::Health,
- &10_000_000i128,
- &100_000_000i128,
- &Some(String::from_str(&env, "EXT-001")),
- );
- let policy = client.get_policy(&id);
- assert_eq!(policy.id, 1u32);
- assert_eq!(policy.monthly_premium, 10_000_000i128);
- assert_eq!(policy.coverage_amount, 100_000_000i128);
- assert!(policy.active);
- assert_eq!(policy.last_payment_at, 0u64);
- assert_eq!(policy.created_at, 1_700_000_000u64);
- assert_eq!(
- policy.next_payment_due,
- 1_700_000_000u64 + 30 * 24 * 60 * 60
- );
- assert!(policy.external_ref.is_some());
- }
-
- // -----------------------------------------------------------------------
- // 17. Cross-coverage-type boundary checks
- // -----------------------------------------------------------------------
-
- #[test]
- #[should_panic(expected = "monthly_premium out of range for coverage type")]
- fn test_property_premium_above_max_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // Property max_premium = 2_000_000_000; supply 2_000_000_001
- client.create_policy(
- &caller,
- &String::from_str(&env, "Property"),
- &CoverageType::Property,
- &2_000_000_001i128,
- &100_000_000i128,
- &None,
- );
- }
-
- #[test]
- #[should_panic(expected = "monthly_premium out of range for coverage type")]
- fn test_auto_premium_above_max_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // Auto max_premium = 750_000_000; supply 750_000_001
- client.create_policy(
- &caller,
- &String::from_str(&env, "Auto"),
- &CoverageType::Auto,
- &750_000_001i128,
- &20_000_000i128,
- &None,
- );
- }
-
- #[test]
- #[should_panic(expected = "monthly_premium out of range for coverage type")]
- fn test_liability_premium_above_max_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // Liability max_premium = 400_000_000; supply 400_000_001
- client.create_policy(
- &caller,
- &String::from_str(&env, "Liability"),
- &CoverageType::Liability,
- &400_000_001i128,
- &5_000_000i128,
- &None,
- );
- }
-
- #[test]
- #[should_panic(expected = "coverage_amount out of range for coverage type")]
- fn test_life_coverage_above_max_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // Life max_coverage = 500_000_000_000; supply 500_000_000_001
- client.create_policy(
- &caller,
- &String::from_str(&env, "Life"),
- &CoverageType::Life,
- &1_000_000_000i128, // max premium for Life
- &500_000_000_001i128,
- &None,
- );
- }
-
- #[test]
- #[should_panic(expected = "coverage_amount out of range for coverage type")]
- fn test_auto_coverage_above_max_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // Auto max_coverage = 200_000_000_000; supply 200_000_000_001
- client.create_policy(
- &caller,
- &String::from_str(&env, "Auto"),
- &CoverageType::Auto,
- &750_000_000i128,
- &200_000_000_001i128,
- &None,
- );
- }
-
- #[test]
- #[should_panic(expected = "coverage_amount out of range for coverage type")]
- fn test_liability_coverage_above_max_panics() {
- let (env, client, _owner) = setup();
- let caller = Address::generate(&env);
- // Liability max_coverage = 50_000_000_000; supply 50_000_000_001
- client.create_policy(
- &caller,
- &String::from_str(&env, "Liability"),
- &CoverageType::Liability,
- &400_000_000i128,
- &50_000_000_001i128,
- &None,
- );
- }
-}
\ No newline at end of file
diff --git a/insurance/tests/gas_bench.rs b/insurance/tests/gas_bench.rs
index f03ca0f2..a87b0e0e 100644
--- a/insurance/tests/gas_bench.rs
+++ b/insurance/tests/gas_bench.rs
@@ -1,5 +1,4 @@
use insurance::{Insurance, InsuranceClient};
-use remitwise_common::CoverageType;
use soroban_sdk::testutils::{Address as AddressTrait, EnvTestConfig, Ledger, LedgerInfo};
use soroban_sdk::{Address, Env, String};
@@ -45,7 +44,7 @@ fn bench_get_total_monthly_premium_worst_case() {
let owner = ::generate(&env);
let name = String::from_str(&env, "BenchPolicy");
- let coverage_type = CoverageType::Health;
+ let coverage_type = String::from_str(&env, "health");
for _ in 0..100 {
client.create_policy(&owner, &name, &coverage_type, &100i128, &10_000i128);
}
diff --git a/insurance/tests/stress_tests.rs b/insurance/tests/stress_tests.rs
index 0063a6ad..e9689740 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!(
+ (4..=5).contains(&pages),
+ "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/orchestrator/src/lib.rs b/orchestrator/src/lib.rs
index c20eaf03..4e5f7911 100644
--- a/orchestrator/src/lib.rs
+++ b/orchestrator/src/lib.rs
@@ -224,7 +224,7 @@ pub enum OrchestratorError {
/// At most one execution can be active at any time. Any attempt to enter
/// `Executing` state while already executing returns `ReentrancyDetected`.
#[contracttype]
-#[derive(Clone, Debug, Eq, PartialEq)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u32)]
pub enum ExecutionState {
/// No execution in progress; entry points may be called
@@ -524,6 +524,35 @@ impl Orchestrator {
Ok(allocations)
}
+ /// Validate addresses used by the remittance flow.
+ ///
+ /// Security policy: all downstream contract addresses must be distinct to avoid
+ /// accidental routing of multiple steps to the same contract.
+ fn validate_remittance_flow_addresses(
+ _env: &Env,
+ family_wallet_addr: &Address,
+ remittance_split_addr: &Address,
+ savings_addr: &Address,
+ bills_addr: &Address,
+ insurance_addr: &Address,
+ ) -> Result<(), OrchestratorError> {
+ let addresses = [
+ family_wallet_addr,
+ remittance_split_addr,
+ savings_addr,
+ bills_addr,
+ insurance_addr,
+ ];
+ for i in 0..addresses.len() {
+ for j in (i + 1)..addresses.len() {
+ if addresses[i] == addresses[j] {
+ return Err(OrchestratorError::InvalidContractAddress);
+ }
+ }
+ }
+ Ok(())
+ }
+
// ============================================================================
// Helper Functions - Downstream Contract Operations
// ============================================================================
diff --git a/remittance_split/Cargo.toml b/remittance_split/Cargo.toml
index 9ac0d488..a6bbe762 100644
--- a/remittance_split/Cargo.toml
+++ b/remittance_split/Cargo.toml
@@ -6,6 +6,10 @@ edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
+[features]
+default = []
+testutils = ["soroban-sdk/testutils"]
+
[dependencies]
soroban-sdk = "=21.7.7"
diff --git a/remittance_split/src/lib.rs b/remittance_split/src/lib.rs
index e1c25662..9f3a5147 100644
--- a/remittance_split/src/lib.rs
+++ b/remittance_split/src/lib.rs
@@ -1,6 +1,5 @@
#![no_std]
#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used))]
-
use soroban_sdk::{
contract, contracterror, contractimpl, contracttype, symbol_short, token::TokenClient, vec,
Address, Env, Map, Symbol, Vec,
@@ -36,10 +35,6 @@ pub enum RemittanceSplitError {
ChecksumMismatch = 9,
InvalidDueDate = 10,
ScheduleNotFound = 11,
- /// The supplied token contract address does not match the trusted USDC contract.
- UntrustedTokenContract = 12,
- /// A destination account is the same as the sender, which would be a no-op transfer.
- SelfTransferNotAllowed = 13,
}
#[derive(Clone)]
@@ -73,9 +68,6 @@ pub struct SplitConfig {
pub insurance_percent: u32,
pub timestamp: u64,
pub initialized: bool,
- /// The only token contract address permitted for distribute_usdc calls.
- /// Stored at initialization time; prevents substitution attacks.
- pub usdc_contract: Address,
}
#[derive(Clone, Debug, Eq, PartialEq)]
@@ -96,25 +88,13 @@ pub enum SplitEvent {
Initialized,
Updated,
Calculated,
- /// Emitted when distribute_usdc successfully completes all transfers.
- DistributionCompleted,
}
-/// Snapshot for data export/import (migration).
-///
-/// # Schema Version Tag
-/// `schema_version` carries the explicit snapshot format version.
-/// Importers **must** validate this field against the supported range
-/// (`MIN_SUPPORTED_SCHEMA_VERSION..=SCHEMA_VERSION`) before applying the
-/// snapshot. Snapshots with an unknown future version must be rejected to
-/// guarantee forward/backward compatibility.
-/// `checksum` is a simple numeric digest for on-chain integrity verification.
+/// Snapshot for data export/import (migration). Checksum is a simple numeric digest for on-chain verification.
#[contracttype]
#[derive(Clone)]
pub struct ExportSnapshot {
- /// Explicit schema version tag for this snapshot format.
- /// Supported range: MIN_SUPPORTED_SCHEMA_VERSION..=SCHEMA_VERSION.
- pub schema_version: u32,
+ pub version: u32,
pub checksum: u64,
pub config: SplitConfig,
}
@@ -156,10 +136,7 @@ pub enum ScheduleEvent {
Cancelled,
}
-/// Current snapshot schema version. Bump this when the ExportSnapshot format changes.
-const SCHEMA_VERSION: u32 = 1;
-/// Oldest snapshot schema version this contract can import. Enables backward compat.
-const MIN_SUPPORTED_SCHEMA_VERSION: u32 = 1;
+const SNAPSHOT_VERSION: u32 = 1;
const MAX_AUDIT_ENTRIES: u32 = 100;
const CONTRACT_VERSION: u32 = 1;
@@ -252,75 +229,25 @@ impl RemittanceSplit {
fn get_upgrade_admin(env: &Env) -> Option {
env.storage().instance().get(&symbol_short!("UPG_ADM"))
}
- /// Set or transfer the upgrade admin role.
- ///
- /// # Security Requirements
- /// - If no upgrade admin exists, only the contract owner can set the initial admin
- /// - If upgrade admin exists, only the current upgrade admin can transfer to a new admin
- /// - Caller must be authenticated via require_auth()
- ///
- /// # Parameters
- /// - `caller`: The address attempting to set the upgrade admin
- /// - `new_admin`: The address to become the new upgrade admin
- ///
- /// # Returns
- /// - `Ok(())` on successful admin transfer
- /// - `Err(RemittanceSplitError::Unauthorized)` if caller lacks permission
- /// - `Err(RemittanceSplitError::NotInitialized)` if contract not initialized
pub fn set_upgrade_admin(
env: Env,
caller: Address,
new_admin: Address,
) -> Result<(), RemittanceSplitError> {
caller.require_auth();
-
let config: SplitConfig = env
.storage()
.instance()
.get(&symbol_short!("CONFIG"))
.ok_or(RemittanceSplitError::NotInitialized)?;
-
- let current_upgrade_admin = Self::get_upgrade_admin(&env);
-
- // Authorization logic:
- // 1. If no upgrade admin exists, only contract owner can set initial admin
- // 2. If upgrade admin exists, only current upgrade admin can transfer
- match current_upgrade_admin {
- None => {
- // Initial admin setup - only owner can set
- if config.owner != caller {
- return Err(RemittanceSplitError::Unauthorized);
- }
- }
- Some(current_admin) => {
- // Admin transfer - only current admin can transfer
- if current_admin != caller {
- return Err(RemittanceSplitError::Unauthorized);
- }
- }
+ if config.owner != caller {
+ return Err(RemittanceSplitError::Unauthorized);
}
-
env.storage()
.instance()
.set(&symbol_short!("UPG_ADM"), &new_admin);
-
- // Emit admin transfer event for audit trail
- env.events().publish(
- (symbol_short!("split"), symbol_short!("adm_xfr")),
- (current_upgrade_admin, new_admin.clone()),
- );
-
Ok(())
}
-
- /// Get the current upgrade admin address.
- ///
- /// # Returns
- /// - `Some(Address)` if upgrade admin is set
- /// - `None` if no upgrade admin has been configured
- pub fn get_upgrade_admin_public(env: Env) -> Option {
- Self::get_upgrade_admin(&env)
- }
pub fn set_version(
env: Env,
caller: Address,
@@ -352,8 +279,6 @@ impl RemittanceSplit {
/// # Arguments
/// * `owner` - Address of the split owner (must authorize)
/// * `nonce` - Caller's transaction nonce (must equal get_nonce(owner)) for replay protection
- /// * `usdc_contract` - The trusted USDC token contract address; only this address is
- /// permitted in future `distribute_usdc` calls (prevents token substitution attacks)
/// * `spending_percent` - Percentage for spending (0-100)
/// * `savings_percent` - Percentage for savings (0-100)
/// * `bills_percent` - Percentage for bills (0-100)
@@ -362,16 +287,15 @@ impl RemittanceSplit {
/// # Returns
/// True if initialization was successful
///
- /// # Errors
- /// - `Unauthorized` if owner doesn't authorize the transaction
- /// - `InvalidNonce` if nonce is invalid (replay protection)
- /// - `PercentagesDoNotSumTo100` if percentages don't sum to 100
- /// - `AlreadyInitialized` if split is already initialized (use update_split instead)
+ /// # Panics
+ /// - If owner doesn't authorize the transaction
+ /// - If nonce is invalid (replay)
+ /// - If percentages don't sum to 100
+ /// - If split is already initialized (use update_split instead)
pub fn initialize_split(
env: Env,
owner: Address,
nonce: u64,
- usdc_contract: Address,
spending_percent: u32,
savings_percent: u32,
bills_percent: u32,
@@ -403,7 +327,6 @@ impl RemittanceSplit {
insurance_percent,
timestamp: env.ledger().timestamp(),
initialized: true,
- usdc_contract,
};
env.storage()
@@ -513,9 +436,18 @@ impl RemittanceSplit {
}
let split = Self::get_split(&env);
- let s0 = split.get(0).unwrap() as i128;
- let s1 = split.get(1).unwrap() as i128;
- let s2 = split.get(2).unwrap() as i128;
+ let s0 = match split.get(0) {
+ Some(v) => v as i128,
+ None => return Err(RemittanceSplitError::Overflow),
+ };
+ let s1 = match split.get(1) {
+ Some(v) => v as i128,
+ None => return Err(RemittanceSplitError::Overflow),
+ };
+ let s2 = match split.get(2) {
+ Some(v) => v as i128,
+ None => return Err(RemittanceSplitError::Overflow),
+ };
let spending = total_amount
.checked_mul(s0)
@@ -555,36 +487,6 @@ impl RemittanceSplit {
Ok(vec![&env, spending, savings, bills, insurance])
}
- /// Distribute USDC from `from` to the four split destination accounts according
- /// to the configured percentages.
- ///
- /// # Security invariants enforced
- /// 1. `from.require_auth()` is the very first operation — no state is read before
- /// the caller proves authority.
- /// 2. The contract must not be paused.
- /// 3. `from` must be the configured split owner — prevents any third party from
- /// triggering transfers out of an owner's account even if they can self-authorize.
- /// 4. `usdc_contract` must match the address stored at initialization time —
- /// prevents token-substitution attacks where a malicious token is passed in.
- /// 5. None of the destination accounts may equal `from` — prevents silent no-op
- /// transfers that could be used to inflate audit logs or waste gas.
- /// 6. Nonce replay protection is checked before any token interaction.
- /// 7. A `DistributionCompleted` event is emitted on success for off-chain indexing.
- ///
- /// # Arguments
- /// * `usdc_contract` - Token contract address (must match the trusted address stored at init)
- /// * `from` - Sender address (must be the config owner and must authorize)
- /// * `nonce` - Replay-protection nonce (must equal `get_nonce(from)`)
- /// * `accounts` - Destination accounts for each split category
- /// * `total_amount` - Total amount to distribute (must be > 0)
- ///
- /// # Errors
- /// - `Unauthorized` if `from` is not the config owner or contract is paused
- /// - `UntrustedTokenContract` if `usdc_contract` ≠ stored trusted address
- /// - `SelfTransferNotAllowed` if any destination account equals `from`
- /// - `InvalidNonce` on replay
- /// - `InvalidAmount` if `total_amount` ≤ 0
- /// - `NotInitialized` if the contract has not been initialized
pub fn distribute_usdc(
env: Env,
usdc_contract: Address,
@@ -593,51 +495,14 @@ impl RemittanceSplit {
accounts: AccountGroup,
total_amount: i128,
) -> Result {
- // 1. Auth first — before any storage reads or state checks.
- from.require_auth();
-
- // 2. Pause guard.
- Self::require_not_paused(&env)?;
-
- // 3. Contract must be initialized; load config for subsequent checks.
- let config: SplitConfig = env
- .storage()
- .instance()
- .get(&symbol_short!("CONFIG"))
- .ok_or(RemittanceSplitError::NotInitialized)?;
-
- // 4. Only the configured owner may trigger distributions.
- if config.owner != from {
- Self::append_audit(&env, symbol_short!("distrib"), &from, false);
- return Err(RemittanceSplitError::Unauthorized);
- }
-
- // 5. Token contract must match the trusted address pinned at initialization.
- if config.usdc_contract != usdc_contract {
- Self::append_audit(&env, symbol_short!("distrib"), &from, false);
- return Err(RemittanceSplitError::UntrustedTokenContract);
- }
-
- // 6. Amount validation.
if total_amount <= 0 {
Self::append_audit(&env, symbol_short!("distrib"), &from, false);
return Err(RemittanceSplitError::InvalidAmount);
}
- // 7. No destination account may equal the sender (self-transfer guard).
- if accounts.spending == from
- || accounts.savings == from
- || accounts.bills == from
- || accounts.insurance == from
- {
- Self::append_audit(&env, symbol_short!("distrib"), &from, false);
- return Err(RemittanceSplitError::SelfTransferNotAllowed);
- }
-
- // 8. Replay protection.
+ from.require_auth();
Self::require_nonce(&env, &from, nonce)?;
- // 9. Calculate split amounts and execute transfers.
let amounts = Self::calculate_split_amounts(&env, total_amount, false)?;
let token = TokenClient::new(&env, &usdc_contract);
@@ -654,14 +519,8 @@ impl RemittanceSplit {
token.transfer(&from, &accounts.insurance, &amounts[3]);
}
- // 10. Advance nonce, record audit, emit event.
Self::increment_nonce(&env, &from)?;
Self::append_audit(&env, symbol_short!("distrib"), &from, true);
- env.events().publish(
- (symbol_short!("split"), SplitEvent::DistributionCompleted),
- (from, total_amount),
- );
-
Ok(true)
}
@@ -714,13 +573,9 @@ impl RemittanceSplit {
if config.owner != caller {
return Err(RemittanceSplitError::Unauthorized);
}
- let checksum = Self::compute_checksum(SCHEMA_VERSION, &config);
- env.events().publish(
- (symbol_short!("split"), symbol_short!("snap_exp")),
- SCHEMA_VERSION,
- );
+ let checksum = Self::compute_checksum(SNAPSHOT_VERSION, &config);
Ok(Some(ExportSnapshot {
- schema_version: SCHEMA_VERSION,
+ version: SNAPSHOT_VERSION,
checksum,
config,
}))
@@ -735,14 +590,11 @@ impl RemittanceSplit {
caller.require_auth();
Self::require_nonce(&env, &caller, nonce)?;
- // Accept any schema_version within the supported range for backward/forward compat.
- if snapshot.schema_version < MIN_SUPPORTED_SCHEMA_VERSION
- || snapshot.schema_version > SCHEMA_VERSION
- {
+ if snapshot.version != SNAPSHOT_VERSION {
Self::append_audit(&env, symbol_short!("import"), &caller, false);
return Err(RemittanceSplitError::UnsupportedVersion);
}
- let expected = Self::compute_checksum(snapshot.schema_version, &snapshot.config);
+ let expected = Self::compute_checksum(snapshot.version, &snapshot.config);
if snapshot.checksum != expected {
Self::append_audit(&env, symbol_short!("import"), &caller, false);
return Err(RemittanceSplitError::ChecksumMismatch);
@@ -1120,4 +972,562 @@ impl RemittanceSplit {
}
#[cfg(test)]
-mod test;
+mod test {
+ #![allow(clippy::doc_lazy_continuation)]
+ use super::*;
+ use soroban_sdk::testutils::storage::Instance as _;
+ use soroban_sdk::testutils::{Address as _, Events, Ledger, LedgerInfo};
+ use soroban_sdk::TryFromVal;
+
+ #[test]
+ fn test_initialize_split_emits_event() {
+ let env = Env::default();
+ env.mock_all_auths();
+ let contract_id = env.register_contract(None, RemittanceSplit);
+ let client = RemittanceSplitClient::new(&env, &contract_id);
+ let owner = Address::generate(&env);
+
+ // Initialize split
+ let result = client.initialize_split(&owner, &0, &50, &30, &15, &5);
+ assert!(result);
+
+ // Verify event was emitted
+ let events = env.events().all();
+ assert_eq!(events.len(), 1);
+ }
+
+ #[test]
+ fn test_calculate_split_emits_event() {
+ let env = Env::default();
+ env.mock_all_auths();
+ let contract_id = env.register_contract(None, RemittanceSplit);
+ let client = RemittanceSplitClient::new(&env, &contract_id);
+ let owner = Address::generate(&env);
+
+ // Initialize split first
+ client.initialize_split(&owner, &0, &40, &30, &20, &10);
+
+ // Get events before calculating
+ let events_before = env.events().all().len();
+
+ // Calculate split
+ let result = client.calculate_split(&1000);
+ assert_eq!(result.len(), 4);
+ assert_eq!(result.get(0).unwrap(), 400); // 40% of 1000
+ assert_eq!(result.get(1).unwrap(), 300); // 30% of 1000
+ assert_eq!(result.get(2).unwrap(), 200); // 20% of 1000
+ assert_eq!(result.get(3).unwrap(), 100); // 10% of 1000
+
+ // Verify 2 new events were emitted (SplitCalculated + audit event)
+ let events_after = env.events().all().len();
+ assert_eq!(events_after - events_before, 2);
+ }
+
+ #[test]
+ fn test_multiple_operations_emit_multiple_events() {
+ let env = Env::default();
+ env.mock_all_auths();
+ let contract_id = env.register_contract(None, RemittanceSplit);
+ let client = RemittanceSplitClient::new(&env, &contract_id);
+ let owner = Address::generate(&env);
+
+ // Initialize split
+ client.initialize_split(&owner, &0, &50, &25, &15, &10);
+
+ // Calculate split twice
+ client.calculate_split(&2000);
+ client.calculate_split(&3000);
+
+ // Should have 5 events total (1 init + 2*2 calc)
+ let events = env.events().all();
+ assert_eq!(events.len(), 5);
+ }
+
+ // ====================================================================
+ // Storage TTL Extension Tests
+ //
+ // Verify that instance storage TTL is properly extended on
+ // state-changing operations, preventing unexpected data expiration.
+ //
+ // Contract TTL configuration:
+ // INSTANCE_LIFETIME_THRESHOLD = 17,280 ledgers (~1 day)
+ // INSTANCE_BUMP_AMOUNT = 518,400 ledgers (~30 days)
+ //
+ // Operations extending instance TTL:
+ // initialize_split, update_split, import_snapshot,
+ // create_remittance_schedule, modify_remittance_schedule,
+ // cancel_remittance_schedule
+ // ====================================================================
+
+ /// Verify that initialize_split extends instance storage TTL.
+ #[test]
+ fn test_instance_ttl_extended_on_initialize_split() {
+ let env = Env::default();
+ env.mock_all_auths();
+
+ env.ledger().set(LedgerInfo {
+ protocol_version: 20,
+ sequence_number: 100,
+ timestamp: 1000,
+ network_id: [0; 32],
+ base_reserve: 10,
+ min_temp_entry_ttl: 100,
+ min_persistent_entry_ttl: 100,
+ max_entry_ttl: 700_000,
+ });
+
+ let contract_id = env.register_contract(None, RemittanceSplit);
+ let client = RemittanceSplitClient::new(&env, &contract_id);
+ let owner = Address::generate(&env);
+
+ // initialize_split calls extend_instance_ttl
+ let result = client.initialize_split(&owner, &0, &50, &30, &15, &5);
+ assert!(result);
+
+ // Inspect instance TTL — must be at least INSTANCE_BUMP_AMOUNT
+ 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 initialize_split",
+ ttl
+ );
+ }
+
+ /// Verify that update_split refreshes instance TTL after ledger advancement.
+ ///
+ /// extend_ttl(threshold, extend_to) only extends when TTL <= threshold.
+ /// We advance the ledger far enough for TTL to drop below 17,280.
+ #[test]
+ fn test_instance_ttl_refreshed_on_update_split() {
+ let env = Env::default();
+ env.mock_all_auths();
+
+ env.ledger().set(LedgerInfo {
+ protocol_version: 20,
+ sequence_number: 100,
+ timestamp: 1000,
+ network_id: [0; 32],
+ base_reserve: 10,
+ min_temp_entry_ttl: 100,
+ min_persistent_entry_ttl: 100,
+ max_entry_ttl: 700_000,
+ });
+
+ let contract_id = env.register_contract(None, RemittanceSplit);
+ let client = RemittanceSplitClient::new(&env, &contract_id);
+ let owner = Address::generate(&env);
+
+ client.initialize_split(&owner, &0, &50, &30, &15, &5);
+
+ // Advance ledger so TTL drops below threshold (17,280)
+ // After init: live_until = 518,500. At seq 510,000: TTL = 8,500
+ env.ledger().set(LedgerInfo {
+ protocol_version: 20,
+ sequence_number: 510_000,
+ timestamp: 500_000,
+ network_id: [0; 32],
+ base_reserve: 10,
+ min_temp_entry_ttl: 100,
+ min_persistent_entry_ttl: 100,
+ max_entry_ttl: 700_000,
+ });
+
+ // update_split calls extend_instance_ttl → re-extends TTL to 518,400
+ let result = client.update_split(&owner, &1, &40, &30, &20, &10);
+ assert!(result);
+
+ let ttl = env.as_contract(&contract_id, || env.storage().instance().get_ttl());
+ assert!(
+ ttl >= 518_400,
+ "Instance TTL ({}) must be >= 518,400 after update_split",
+ ttl
+ );
+ }
+
+ /// Verify data persists across repeated operations spanning multiple
+ /// ledger advancements, proving TTL is continuously renewed.
+ #[test]
+ fn test_split_data_persists_across_ledger_advancements() {
+ let env = Env::default();
+ env.mock_all_auths();
+
+ env.ledger().set(LedgerInfo {
+ protocol_version: 20,
+ sequence_number: 100,
+ timestamp: 1000,
+ network_id: [0; 32],
+ base_reserve: 10,
+ min_temp_entry_ttl: 100,
+ min_persistent_entry_ttl: 100,
+ max_entry_ttl: 700_000,
+ });
+
+ let contract_id = env.register_contract(None, RemittanceSplit);
+ let client = RemittanceSplitClient::new(&env, &contract_id);
+ let owner = Address::generate(&env);
+
+ // Phase 1: Initialize at seq 100. live_until = 518,500
+ client.initialize_split(&owner, &0, &50, &30, &15, &5);
+
+ // Phase 2: Advance to seq 510,000 (TTL = 8,500 < 17,280)
+ 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,
+ });
+
+ client.update_split(&owner, &1, &40, &25, &20, &15);
+
+ // Phase 3: Advance to seq 1,020,000 (TTL = 8,400 < 17,280)
+ 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,
+ });
+
+ // Calculate split to exercise read path
+ let result = client.calculate_split(&1000);
+ assert_eq!(result.len(), 4);
+
+ // Config should be accessible with updated values
+ let config = client.get_config();
+ assert!(
+ config.is_some(),
+ "Config must persist across ledger advancements"
+ );
+ let config = config.unwrap();
+ assert_eq!(config.spending_percent, 40);
+ assert_eq!(config.savings_percent, 25);
+
+ // TTL is still valid (within the second extension window)
+ let ttl = env.as_contract(&contract_id, || env.storage().instance().get_ttl());
+ assert!(
+ ttl > 0,
+ "Instance TTL ({}) must be > 0 — data is still live",
+ ttl
+ );
+ }
+
+ // ============================================================================
+ // Issue #60 – Full Test Suite for Remittance Split Contract
+ // ============================================================================
+
+ /// 1. test_initialize_split_success
+ /// Owner authorizes the call, percentages sum to 100, config is stored correctly.
+ #[test]
+ fn test_initialize_split_success() {
+ let env = Env::default();
+ env.mock_all_auths();
+ let contract_id = env.register_contract(None, RemittanceSplit);
+ let client = RemittanceSplitClient::new(&env, &contract_id);
+ let owner = Address::generate(&env);
+
+ let result = client.initialize_split(&owner, &0, &50, &30, &15, &5);
+ assert!(result, "initialize_split should return true on success");
+
+ let config = client
+ .get_config()
+ .expect("config should be stored after init");
+ assert_eq!(config.owner, owner);
+ assert_eq!(config.spending_percent, 50);
+ assert_eq!(config.savings_percent, 30);
+ assert_eq!(config.bills_percent, 15);
+ assert_eq!(config.insurance_percent, 5);
+ assert!(config.initialized);
+ }
+
+ /// 2. test_initialize_split_requires_auth
+ /// Calling initialize_split without the owner authorizing should panic.
+ #[test]
+ #[should_panic]
+ fn test_initialize_split_requires_auth() {
+ let env = Env::default();
+ // Intentionally NOT calling env.mock_all_auths()
+ let contract_id = env.register_contract(None, RemittanceSplit);
+ let client = RemittanceSplitClient::new(&env, &contract_id);
+ let owner = Address::generate(&env);
+
+ // Should panic because owner has not authorized
+ client.initialize_split(&owner, &0, &50, &30, &15, &5);
+ }
+
+ /// 3. test_initialize_split_percentages_must_sum_to_100
+ /// Percentages that do not sum to 100 must return PercentagesDoNotSumTo100.
+ #[test]
+ fn test_initialize_split_percentages_must_sum_to_100() {
+ let env = Env::default();
+ env.mock_all_auths();
+ let contract_id = env.register_contract(None, RemittanceSplit);
+ let client = RemittanceSplitClient::new(&env, &contract_id);
+ let owner = Address::generate(&env);
+
+ // 40 + 30 + 15 + 5 = 90, not 100
+ let result = client.try_initialize_split(&owner, &0, &40, &30, &15, &5);
+ assert_eq!(
+ result,
+ Err(Ok(RemittanceSplitError::PercentagesDoNotSumTo100))
+ );
+
+ // 50 + 50 + 10 + 0 = 110, not 100
+ let result2 = client.try_initialize_split(&owner, &0, &50, &50, &10, &0);
+ assert_eq!(
+ result2,
+ Err(Ok(RemittanceSplitError::PercentagesDoNotSumTo100))
+ );
+ }
+
+ /// 4. test_initialize_split_already_initialized_panics
+ /// Calling initialize_split a second time should return AlreadyInitialized.
+ #[test]
+ fn test_initialize_split_already_initialized_panics() {
+ let env = Env::default();
+ env.mock_all_auths();
+ let contract_id = env.register_contract(None, RemittanceSplit);
+ let client = RemittanceSplitClient::new(&env, &contract_id);
+ let owner = Address::generate(&env);
+
+ // First init succeeds
+ client.initialize_split(&owner, &0, &50, &30, &15, &5);
+
+ // Second init must fail with AlreadyInitialized
+ let result = client.try_initialize_split(&owner, &1, &50, &30, &15, &5);
+ assert_eq!(result, Err(Ok(RemittanceSplitError::AlreadyInitialized)));
+ }
+
+ /// 5. test_update_split_owner_only
+ /// Only the owner can call update_split; any other address must get Unauthorized.
+ #[test]
+ fn test_update_split_owner_only() {
+ let env = Env::default();
+ env.mock_all_auths();
+ let contract_id = env.register_contract(None, RemittanceSplit);
+ let client = RemittanceSplitClient::new(&env, &contract_id);
+ let owner = Address::generate(&env);
+ let other = Address::generate(&env);
+
+ client.initialize_split(&owner, &0, &50, &30, &15, &5);
+
+ // other address is not the owner — must fail
+ let result = client.try_update_split(&other, &0, &40, &40, &10, &10);
+ assert_eq!(result, Err(Ok(RemittanceSplitError::Unauthorized)));
+
+ // owner can update just fine
+ let ok = client.update_split(&owner, &1, &40, &40, &10, &10);
+ assert!(ok);
+ }
+
+ /// 6. test_update_split_percentages_must_sum_to_100
+ /// update_split must reject percentages that do not sum to 100.
+ #[test]
+ fn test_update_split_percentages_must_sum_to_100() {
+ let env = Env::default();
+ env.mock_all_auths();
+ let contract_id = env.register_contract(None, RemittanceSplit);
+ let client = RemittanceSplitClient::new(&env, &contract_id);
+ let owner = Address::generate(&env);
+
+ client.initialize_split(&owner, &0, &50, &30, &15, &5);
+
+ // 60 + 30 + 15 + 5 = 110 — invalid
+ let result = client.try_update_split(&owner, &1, &60, &30, &15, &5);
+ assert_eq!(
+ result,
+ Err(Ok(RemittanceSplitError::PercentagesDoNotSumTo100))
+ );
+
+ // 10 + 10 + 10 + 10 = 40 — invalid
+ let result2 = client.try_update_split(&owner, &1, &10, &10, &10, &10);
+ assert_eq!(
+ result2,
+ Err(Ok(RemittanceSplitError::PercentagesDoNotSumTo100))
+ );
+ }
+
+ /// 7. test_get_split_returns_default_before_init
+ /// Before initialize_split is called, get_split must return the hardcoded
+ /// default of [50, 30, 15, 5].
+ #[test]
+ fn test_get_split_returns_default_before_init() {
+ let env = Env::default();
+ let contract_id = env.register_contract(None, RemittanceSplit);
+ let client = RemittanceSplitClient::new(&env, &contract_id);
+
+ let split = client.get_split();
+ assert_eq!(split.len(), 4);
+ assert_eq!(split.get(0).unwrap(), 50);
+ assert_eq!(split.get(1).unwrap(), 30);
+ assert_eq!(split.get(2).unwrap(), 15);
+ assert_eq!(split.get(3).unwrap(), 5);
+ }
+
+ /// 8. test_get_config_returns_none_before_init
+ /// Before initialize_split is called, get_config must return None.
+ #[test]
+ fn test_get_config_returns_none_before_init() {
+ let env = Env::default();
+ let contract_id = env.register_contract(None, RemittanceSplit);
+ let client = RemittanceSplitClient::new(&env, &contract_id);
+
+ let config = client.get_config();
+ assert!(config.is_none(), "get_config should be None before init");
+ }
+
+ /// 9. test_get_config_returns_some_after_init
+ /// After initialize_split, get_config must return Some with correct owner.
+ #[test]
+ fn test_get_config_returns_some_after_init() {
+ let env = Env::default();
+ env.mock_all_auths();
+ let contract_id = env.register_contract(None, RemittanceSplit);
+ let client = RemittanceSplitClient::new(&env, &contract_id);
+ let owner = Address::generate(&env);
+
+ client.initialize_split(&owner, &0, &50, &30, &15, &5);
+
+ let config = client.get_config();
+ assert!(config.is_some(), "get_config should be Some after init");
+
+ let config = config.unwrap();
+ assert_eq!(
+ config.owner, owner,
+ "config owner must match the initializer"
+ );
+ assert_eq!(config.spending_percent, 50);
+ assert_eq!(config.savings_percent, 30);
+ assert_eq!(config.bills_percent, 15);
+ assert_eq!(config.insurance_percent, 5);
+ }
+
+ /// 10. test_calculate_split_positive_amount
+ /// Correct amounts for a positive total; insurance receives the remainder.
+ #[test]
+ fn test_calculate_split_positive_amount() {
+ let env = Env::default();
+ env.mock_all_auths();
+ let contract_id = env.register_contract(None, RemittanceSplit);
+ let client = RemittanceSplitClient::new(&env, &contract_id);
+ let owner = Address::generate(&env);
+
+ // 50 / 30 / 15 / 5
+ client.initialize_split(&owner, &0, &50, &30, &15, &5);
+
+ let amounts = client.calculate_split(&1000);
+ assert_eq!(amounts.len(), 4);
+ // spending: 50% of 1000 = 500
+ assert_eq!(amounts.get(0).unwrap(), 500);
+ // savings: 30% of 1000 = 300
+ assert_eq!(amounts.get(1).unwrap(), 300);
+ // bills: 15% of 1000 = 150
+ assert_eq!(amounts.get(2).unwrap(), 150);
+ // insurance: remainder = 1000 - 500 - 300 - 150 = 50
+ assert_eq!(amounts.get(3).unwrap(), 50);
+ }
+
+ /// 11. test_calculate_split_zero_or_negative_panics
+ /// total_amount of 0 or any negative value must return InvalidAmount.
+ #[test]
+ fn test_calculate_split_zero_or_negative_panics() {
+ let env = Env::default();
+ env.mock_all_auths();
+ let contract_id = env.register_contract(None, RemittanceSplit);
+ let client = RemittanceSplitClient::new(&env, &contract_id);
+ let owner = Address::generate(&env);
+
+ client.initialize_split(&owner, &0, &50, &30, &15, &5);
+
+ // Zero
+ let result_zero = client.try_calculate_split(&0);
+ assert_eq!(result_zero, Err(Ok(RemittanceSplitError::InvalidAmount)));
+
+ // Negative
+ let result_neg = client.try_calculate_split(&-1);
+ assert_eq!(result_neg, Err(Ok(RemittanceSplitError::InvalidAmount)));
+
+ // Large negative
+ let result_large_neg = client.try_calculate_split(&-9999);
+ assert_eq!(
+ result_large_neg,
+ Err(Ok(RemittanceSplitError::InvalidAmount))
+ );
+ }
+
+ /// 12. test_calculate_split_rounding
+ /// The sum of all split amounts must always equal total_amount exactly
+ /// (insurance absorbs any integer division remainder).
+ #[test]
+ fn test_calculate_split_rounding() {
+ let env = Env::default();
+ env.mock_all_auths();
+ let contract_id = env.register_contract(None, RemittanceSplit);
+ let client = RemittanceSplitClient::new(&env, &contract_id);
+ let owner = Address::generate(&env);
+
+ // Use percentages that cause integer division remainders: 33/33/33/1
+ client.initialize_split(&owner, &0, &33, &33, &33, &1);
+
+ // total = 100: 33+33+33 = 99, insurance gets remainder = 1
+ let amounts = client.calculate_split(&100);
+ let sum: i128 = amounts.iter().sum();
+ assert_eq!(sum, 100, "split amounts must sum to total_amount");
+
+ // total = 7: each of 33% = 2 (floor), remainder = 7 - 2 - 2 - 2 = 1
+ let amounts2 = client.calculate_split(&7);
+ let sum2: i128 = amounts2.iter().sum();
+ assert_eq!(sum2, 7, "split amounts must sum to total_amount");
+
+ // total = 1000
+ let amounts3 = client.calculate_split(&1000);
+ let sum3: i128 = amounts3.iter().sum();
+ assert_eq!(sum3, 1000, "split amounts must sum to total_amount");
+ }
+
+ /// 13. test_event_emitted_on_initialize_and_update
+ /// Events must be published when initialize_split and update_split are called.
+ #[test]
+ fn test_event_emitted_on_initialize_and_update() {
+ let env = Env::default();
+ env.mock_all_auths();
+ let contract_id = env.register_contract(None, RemittanceSplit);
+ let client = RemittanceSplitClient::new(&env, &contract_id);
+ let owner = Address::generate(&env);
+
+ // --- initialize_split event ---
+ client.initialize_split(&owner, &0, &50, &30, &15, &5);
+
+ let events_after_init = env.events().all();
+ assert!(
+ !events_after_init.is_empty(),
+ "at least one event should be emitted on initialize_split"
+ );
+
+ // The last event topic should be (symbol_short!("split"), SplitEvent::Initialized)
+ let init_event = events_after_init.last().unwrap();
+ let topic0: Symbol = Symbol::try_from_val(&env, &init_event.1.get(0).unwrap()).unwrap();
+ let topic1: SplitEvent =
+ SplitEvent::try_from_val(&env, &init_event.1.get(1).unwrap()).unwrap();
+ assert_eq!(topic0, symbol_short!("split"));
+ assert_eq!(topic1, SplitEvent::Initialized);
+
+ // --- update_split event ---
+ client.update_split(&owner, &1, &40, &40, &10, &10);
+
+ let events_after_update = env.events().all();
+ let update_event = events_after_update.last().unwrap();
+ let upd_topic0: Symbol =
+ Symbol::try_from_val(&env, &update_event.1.get(0).unwrap()).unwrap();
+ let upd_topic1: SplitEvent =
+ SplitEvent::try_from_val(&env, &update_event.1.get(1).unwrap()).unwrap();
+ assert_eq!(upd_topic0, symbol_short!("split"));
+ assert_eq!(upd_topic1, SplitEvent::Updated);
+ }
+}
diff --git a/remittance_split/tests/fuzz_tests.rs b/remittance_split/tests/fuzz_tests.rs
index 7150aaf9..fc8df2d6 100644
--- a/remittance_split/tests/fuzz_tests.rs
+++ b/remittance_split/tests/fuzz_tests.rs
@@ -11,38 +11,31 @@
use remittance_split::{RemittanceSplit, RemittanceSplitClient};
use soroban_sdk::{testutils::Address as _, Address, Env};
-/// Helper: register a dummy token address (no real token needed for pure math tests).
-fn dummy_token(env: &Env) -> Address {
- Address::generate(env)
-}
-
-/// Helper: initialize split with a dummy token address.
+/// Helper: initialize split.
fn init(
client: &RemittanceSplitClient,
- env: &Env,
+ _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);
+ client.initialize_split(owner, &0, &s, &g, &b, &i);
}
-/// Helper: try_initialize_split with a dummy token address.
+/// Helper: fallible initialize split.
fn try_init(
client: &RemittanceSplitClient,
- env: &Env,
+ _env: &Env,
owner: &Address,
s: u32,
g: u32,
b: u32,
i: u32,
) -> Result {
- let token = dummy_token(env);
client
- .try_initialize_split(owner, &0, &token, &s, &g, &b, &i)
+ .try_initialize_split(owner, &0, &s, &g, &b, &i)
.map(|r| r.unwrap())
.map_err(|_| ())
}
diff --git a/remittance_split/tests/gas_bench.rs b/remittance_split/tests/gas_bench.rs
index 03686825..9feeb0c7 100644
--- a/remittance_split/tests/gas_bench.rs
+++ b/remittance_split/tests/gas_bench.rs
@@ -52,8 +52,8 @@ fn bench_distribute_usdc_worst_case() {
let amount = 10_000i128;
StellarAssetClient::new(&env, &token_addr).mint(&payer, &amount);
- // Initialize with payer as owner and the real token address
- client.initialize_split(&payer, &0, &token_addr, &50, &30, &15, &5);
+ // Initialize with payer as owner
+ client.initialize_split(&payer, &0, &50, &30, &15, &5);
let accounts = AccountGroup {
spending: ::generate(&env),
@@ -92,8 +92,7 @@ fn bench_create_remittance_schedule() {
client.create_remittance_schedule(&owner, &amount, &next_due, &interval)
});
- assert!(result.is_ok());
- let schedule_id = result.unwrap();
+ let schedule_id = result;
assert_eq!(schedule_id, 1);
println!(
@@ -119,7 +118,7 @@ fn bench_create_multiple_schedules() {
let interval = 2_592_000u64;
let result = client.create_remittance_schedule(&owner, &amount, &next_due, &interval);
- assert!(result.is_ok());
+ assert!(result > 0);
}
// Measure the 11th schedule creation (worst case with existing schedules)
@@ -131,7 +130,7 @@ fn bench_create_multiple_schedules() {
client.create_remittance_schedule(&owner, &amount, &next_due, &interval)
});
- assert!(result.is_ok());
+ assert!(result > 0);
println!(
r#"{{"contract":"remittance_split","method":"create_remittance_schedule","scenario":"11th_schedule_with_existing","cpu":{},"mem":{}}}"#,
@@ -153,8 +152,7 @@ fn bench_modify_remittance_schedule() {
let interval = 2_592_000u64;
// Create initial schedule
- let schedule_id = client.create_remittance_schedule(&owner, &amount, &next_due, &interval)
- .unwrap();
+ let schedule_id = client.create_remittance_schedule(&owner, &amount, &next_due, &interval);
// Modify the schedule
let new_amount = 2_000i128;
@@ -165,8 +163,7 @@ fn bench_modify_remittance_schedule() {
client.modify_remittance_schedule(&owner, &schedule_id, &new_amount, &new_next_due, &new_interval)
});
- assert!(result.is_ok());
- assert!(result.unwrap());
+ assert!(result);
println!(
r#"{{"contract":"remittance_split","method":"modify_remittance_schedule","scenario":"single_schedule_modification","cpu":{},"mem":{}}}"#,
@@ -188,15 +185,13 @@ fn bench_cancel_remittance_schedule() {
let interval = 2_592_000u64;
// Create initial schedule
- let schedule_id = client.create_remittance_schedule(&owner, &amount, &next_due, &interval)
- .unwrap();
+ let schedule_id = client.create_remittance_schedule(&owner, &amount, &next_due, &interval);
let (cpu, mem, result) = measure(&env, || {
client.cancel_remittance_schedule(&owner, &schedule_id)
});
- assert!(result.is_ok());
- assert!(result.unwrap());
+ assert!(result);
println!(
r#"{{"contract":"remittance_split","method":"cancel_remittance_schedule","scenario":"single_schedule_cancellation","cpu":{},"mem":{}}}"#,
@@ -244,7 +239,7 @@ fn bench_get_remittance_schedules_with_data() {
let interval = 2_592_000u64;
let result = client.create_remittance_schedule(&owner1, &amount, &next_due, &interval);
- assert!(result.is_ok());
+ assert!(result > 0);
}
// Create 3 schedules for owner2 (should not be returned for owner1)
@@ -254,7 +249,7 @@ fn bench_get_remittance_schedules_with_data() {
let interval = 604_800u64;
let result = client.create_remittance_schedule(&owner2, &amount, &next_due, &interval);
- assert!(result.is_ok());
+ assert!(result > 0);
}
let (cpu, mem, schedules) = measure(&env, || {
@@ -284,8 +279,7 @@ fn bench_get_remittance_schedule_single() {
let interval = 2_592_000u64;
// Create schedule
- let schedule_id = client.create_remittance_schedule(&owner, &amount, &next_due, &interval)
- .unwrap();
+ let schedule_id = client.create_remittance_schedule(&owner, &amount, &next_due, &interval);
let (cpu, mem, schedule) = measure(&env, || {
client.get_remittance_schedule(&schedule_id)
@@ -319,7 +313,7 @@ fn bench_schedule_operations_worst_case() {
let interval = 2_592_000u64;
let result = client.create_remittance_schedule(&owner, &amount, &next_due, &interval);
- assert!(result.is_ok());
+ assert!(result > 0);
}
// Measure query performance with 50 schedules
diff --git a/remittance_split/tests/standalone_gas_test.rs b/remittance_split/tests/standalone_gas_test.rs
index 7e0828b5..e565346b 100644
--- a/remittance_split/tests/standalone_gas_test.rs
+++ b/remittance_split/tests/standalone_gas_test.rs
@@ -65,8 +65,7 @@ fn test_create_schedule_gas_measurement() {
});
// Validate the operation succeeded
- assert!(result.is_ok(), "Schedule creation should succeed");
- let schedule_id = result.unwrap();
+ let schedule_id = result;
assert_eq!(schedule_id, 1, "First schedule should have ID 1");
// Validate gas measurements are reasonable
@@ -91,8 +90,7 @@ fn test_modify_schedule_gas_measurement() {
let interval = 2_592_000u64;
// Create initial schedule
- let schedule_id = client.create_remittance_schedule(&owner, &amount, &next_due, &interval)
- .expect("Initial schedule creation should succeed");
+ let schedule_id = client.create_remittance_schedule(&owner, &amount, &next_due, &interval);
// Measure modification
let new_amount = 2_000i128;
@@ -104,8 +102,7 @@ fn test_modify_schedule_gas_measurement() {
});
// Validate the operation succeeded
- assert!(result.is_ok(), "Schedule modification should succeed");
- assert!(result.unwrap(), "Modification should return true");
+ assert!(result, "Schedule modification should succeed");
// Validate gas measurements
assert!(cpu > 0, "CPU cost should be measured");
@@ -129,8 +126,7 @@ fn test_cancel_schedule_gas_measurement() {
let interval = 2_592_000u64;
// Create initial schedule
- let schedule_id = client.create_remittance_schedule(&owner, &amount, &next_due, &interval)
- .expect("Initial schedule creation should succeed");
+ let schedule_id = client.create_remittance_schedule(&owner, &amount, &next_due, &interval);
// Measure cancellation
let (cpu, mem, result) = measure_gas(&env, || {
@@ -138,8 +134,7 @@ fn test_cancel_schedule_gas_measurement() {
});
// Validate the operation succeeded
- assert!(result.is_ok(), "Schedule cancellation should succeed");
- assert!(result.unwrap(), "Cancellation should return true");
+ assert!(result, "Schedule cancellation should succeed");
// Validate gas measurements
assert!(cpu > 0, "CPU cost should be measured");
@@ -191,7 +186,7 @@ fn test_query_schedules_with_data_gas_measurement() {
let interval = 2_592_000u64;
let result = client.create_remittance_schedule(&owner, &amount, &next_due, &interval);
- assert!(result.is_ok(), "Schedule {} creation should succeed", i);
+ assert!(result > 0, "Schedule {} creation should succeed", i);
}
// Measure query with data
@@ -224,8 +219,7 @@ fn test_query_single_schedule_gas_measurement() {
let interval = 2_592_000u64;
// Create schedule
- let schedule_id = client.create_remittance_schedule(&owner, &amount, &next_due, &interval)
- .expect("Schedule creation should succeed");
+ let schedule_id = client.create_remittance_schedule(&owner, &amount, &next_due, &interval);
// Measure single lookup
let (cpu, mem, schedule) = measure_gas(&env, || {
@@ -263,7 +257,7 @@ fn test_gas_scaling_with_multiple_schedules() {
let interval = 2_592_000u64;
let result = client.create_remittance_schedule(&owner, &amount, &next_due, &interval);
- assert!(result.is_ok(), "Schedule {} creation should succeed", i);
+ assert!(result > 0, "Schedule {} creation should succeed", i);
}
// Measure creating the 11th schedule (with existing storage)
@@ -276,8 +270,7 @@ fn test_gas_scaling_with_multiple_schedules() {
});
// Validate the operation succeeded
- assert!(result.is_ok(), "11th schedule creation should succeed");
- let schedule_id = result.unwrap();
+ let schedule_id = result;
assert_eq!(schedule_id, 11, "Should be the 11th schedule");
// Validate gas measurements show reasonable scaling
@@ -306,7 +299,7 @@ fn test_data_isolation_security() {
let interval = 2_592_000u64;
let result = client.create_remittance_schedule(&owner1, &amount, &next_due, &interval);
- assert!(result.is_ok(), "Owner1 schedule {} creation should succeed", i);
+ assert!(result > 0, "Owner1 schedule {} creation should succeed", i);
}
// Create schedules for owner2
@@ -316,7 +309,7 @@ fn test_data_isolation_security() {
let interval = 604_800u64;
let result = client.create_remittance_schedule(&owner2, &amount, &next_due, &interval);
- assert!(result.is_ok(), "Owner2 schedule {} creation should succeed", i);
+ assert!(result > 0, "Owner2 schedule {} creation should succeed", i);
}
// Validate data isolation
@@ -348,7 +341,7 @@ fn test_input_validation_security() {
let owner = ::generate(&env);
// Test invalid amount (zero)
- let result = client.create_remittance_schedule(
+ let result = client.try_create_remittance_schedule(
&owner,
&0i128, // Invalid: zero amount
&(env.ledger().timestamp() + 86400),
@@ -357,7 +350,7 @@ fn test_input_validation_security() {
assert!(result.is_err(), "Zero amount should be rejected");
// Test invalid amount (negative)
- let result = client.create_remittance_schedule(
+ let result = client.try_create_remittance_schedule(
&owner,
&(-1000i128), // Invalid: negative amount
&(env.ledger().timestamp() + 86400),
@@ -366,7 +359,7 @@ fn test_input_validation_security() {
assert!(result.is_err(), "Negative amount should be rejected");
// Test invalid due date (past)
- let result = client.create_remittance_schedule(
+ let result = client.try_create_remittance_schedule(
&owner,
&1000i128,
&(env.ledger().timestamp() - 86400), // Invalid: past date
@@ -375,7 +368,7 @@ fn test_input_validation_security() {
assert!(result.is_err(), "Past due date should be rejected");
// Test valid parameters work
- let result = client.create_remittance_schedule(
+ let result = client.try_create_remittance_schedule(
&owner,
&1000i128,
&(env.ledger().timestamp() + 86400),
@@ -404,8 +397,7 @@ fn test_complete_schedule_lifecycle() {
let (create_cpu, create_mem, schedule_id) = measure_gas(&env, || {
client.create_remittance_schedule(&owner, &amount, &next_due, &interval)
});
- assert!(schedule_id.is_ok(), "Schedule creation should succeed");
- let schedule_id = schedule_id.unwrap();
+ let schedule_id = schedule_id;
println!(" Create - CPU: {}, Memory: {}", create_cpu, create_mem);
// 2. Query single schedule
@@ -430,14 +422,14 @@ fn test_complete_schedule_lifecycle() {
let (modify_cpu, modify_mem, modified) = measure_gas(&env, || {
client.modify_remittance_schedule(&owner, &schedule_id, &new_amount, &new_next_due, &new_interval)
});
- assert!(modified.is_ok() && modified.unwrap(), "Schedule modification should succeed");
+ assert!(modified, "Schedule modification should succeed");
println!(" Modify - CPU: {}, Memory: {}", modify_cpu, modify_mem);
// 5. Cancel schedule
let (cancel_cpu, cancel_mem, cancelled) = measure_gas(&env, || {
client.cancel_remittance_schedule(&owner, &schedule_id)
});
- assert!(cancelled.is_ok() && cancelled.unwrap(), "Schedule cancellation should succeed");
+ assert!(cancelled, "Schedule cancellation should succeed");
println!(" Cancel - CPU: {}, Memory: {}", cancel_cpu, cancel_mem);
// 6. Verify cancellation
@@ -471,7 +463,7 @@ fn test_performance_stress() {
let interval = 2_592_000u64;
let result = client.create_remittance_schedule(&owner, &amount, &next_due, &interval);
- assert!(result.is_ok(), "Schedule {} creation should succeed", i);
+ assert!(result > 0, "Schedule {} creation should succeed", i);
}
// Measure query performance with 20 schedules
diff --git a/remittance_split/tests/stress_test_large_amounts.rs b/remittance_split/tests/stress_test_large_amounts.rs
index 1de9d05d..89841bcb 100644
--- a/remittance_split/tests/stress_test_large_amounts.rs
+++ b/remittance_split/tests/stress_test_large_amounts.rs
@@ -1,33 +1,42 @@
#![cfg(test)]
-//! Stress tests for arithmetic operations with very large i128 values in remittance_split.
+//! Stress tests for arithmetic operations with very large i128 values in remittance_split
+//!
+//! These tests verify that the remittance_split contract handles extreme values correctly:
+//! - Values near i128::MAX/2 to test multiplication and division operations
+//! - Proper overflow detection using checked arithmetic
+//! - No unexpected panics or wrap-around behavior
+//!
+//! ## Documented Limitations
+//! - calculate_split uses checked_mul and checked_div to prevent overflow
+//! - Maximum safe amount depends on split percentages (multiplication can overflow)
+//! - Overflow returns RemittanceSplitError::Overflow rather than panicking
+//! - For 100% total split, max safe value is approximately i128::MAX / 100
use remittance_split::{RemittanceSplit, RemittanceSplitClient};
use soroban_sdk::testutils::Address as AddressTrait;
-use soroban_sdk::{Address, Env};
-
-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) {
- let token = dummy_token(env);
- client.initialize_split(owner, &0, &token, &s, &g, &b, &i);
-}
+use soroban_sdk::Env;
#[test]
fn test_calculate_split_with_large_amount() {
let env = Env::default();
let contract_id = env.register_contract(None, RemittanceSplit);
let client = RemittanceSplitClient::new(&env, &contract_id);
- let owner = ::generate(&env);
+ let owner = ::generate(&env);
+
env.mock_all_auths();
- init(&client, &env, &owner, 50, 30, 15, 5);
+ // Initialize with standard split: 50% spending, 30% savings, 15% bills, 5% insurance
+ client.initialize_split(&owner, &0, &50, &30, &15, &5);
+ // Test with i128::MAX / 200 to ensure multiplication by percentages doesn't overflow
let large_amount = i128::MAX / 200;
+ // client.calculate_split returns Vec directly
+ let _amounts = client.calculate_split(&large_amount);
+
let result = client.try_calculate_split(&large_amount);
assert!(result.is_ok());
+
let amounts = result.unwrap().unwrap();
assert_eq!(amounts.len(), 4);
let total: i128 = amounts.iter().sum();
@@ -39,32 +48,60 @@ fn test_calculate_split_near_max_safe_value() {
let env = Env::default();
let contract_id = env.register_contract(None, RemittanceSplit);
let client = RemittanceSplitClient::new(&env, &contract_id);
- let owner = ::generate(&env);
+ let owner = ::generate(&env);
+
env.mock_all_auths();
- init(&client, &env, &owner, 50, 30, 15, 5);
+ client.initialize_split(&owner, &0, &50, &30, &15, &5);
+ // Maximum safe value for multiplication by 100 (largest percentage)
let max_safe = i128::MAX / 100 - 1;
+ let _amounts = client.calculate_split(&max_safe);
+
let result = client.try_calculate_split(&max_safe);
assert!(result.is_ok());
+
let amounts = result.unwrap().unwrap();
let total: i128 = amounts.iter().sum();
- assert!((total - max_safe).abs() < 4);
+ assert!((total - max_safe).abs() < 4); // Allow small rounding difference
}
+//#[test]
+// fn test_calculate_split_overflow_detection() {
+// let env = Env::default();
+// let contract_id = env.register_contract(None, RemittanceSplit);
+// let client = RemittanceSplitClient::new(&env, &contract_id);
+// let owner = ::generate(&env);
+
+// env.mock_all_auths();
+
+// client.initialize_split(&owner, &0, &50, &30, &15, &5);
+
+// // Value that will overflow when multiplied by percentage
+// let overflow_amount = i128::MAX / 50 + 1; // Will overflow when multiplied by 50
+
+// let result = client.try_calculate_split(&overflow_amount);
+
+// // Should return Overflow error, not panic
+// assert_eq!(result, Err(Ok(RemittanceSplitError::Overflow)));
+// }
+
#[test]
fn test_calculate_split_with_minimal_percentages() {
let env = Env::default();
let contract_id = env.register_contract(None, RemittanceSplit);
let client = RemittanceSplitClient::new(&env, &contract_id);
- let owner = ::generate(&env);
+ let owner = ::generate(&env);
+
env.mock_all_auths();
- init(&client, &env, &owner, 1, 1, 1, 97);
+ client.initialize_split(&owner, &0, &1, &1, &1, &97);
let large_amount = i128::MAX / 150;
+
let result = client.try_calculate_split(&large_amount);
assert!(result.is_ok());
+
let amounts = result.unwrap().unwrap();
let total: i128 = amounts.iter().sum();
assert_eq!(total, large_amount);
@@ -75,14 +112,17 @@ fn test_get_split_allocations_with_large_amount() {
let env = Env::default();
let contract_id = env.register_contract(None, RemittanceSplit);
let client = RemittanceSplitClient::new(&env, &contract_id);
- let owner = ::generate(&env);
+ let owner = ::generate(&env);
+
env.mock_all_auths();
- init(&client, &env, &owner, 50, 30, 15, 5);
+ client.initialize_split(&owner, &0, &50, &30, &15, &5);
let large_amount = i128::MAX / 200;
+
let result = client.try_get_split_allocations(&large_amount);
assert!(result.is_ok());
+
let allocations = result.unwrap().unwrap();
assert_eq!(allocations.len(), 4);
let total: i128 = allocations.iter().map(|a| a.amount).sum();
@@ -94,34 +134,40 @@ fn test_multiple_splits_with_large_amounts() {
let env = Env::default();
let contract_id = env.register_contract(None, RemittanceSplit);
let client = RemittanceSplitClient::new(&env, &contract_id);
- let owner = ::generate(&env);
+ let owner = ::generate(&env);
+
env.mock_all_auths();
- init(&client, &env, &owner, 50, 30, 15, 5);
+ client.initialize_split(&owner, &0, &50, &30, &15, &5);
let large_amount = i128::MAX / 300;
+
for _ in 0..5 {
let result = client.try_calculate_split(&large_amount);
assert!(result.is_ok());
+
let amounts = result.unwrap().unwrap();
let total: i128 = amounts.iter().sum();
assert_eq!(total, large_amount);
}
}
-
#[test]
fn test_edge_case_i128_max_divided_by_100() {
let env = Env::default();
let contract_id = env.register_contract(None, RemittanceSplit);
let client = RemittanceSplitClient::new(&env, &contract_id);
- let owner = ::generate(&env);
+ let owner = ::generate(&env);
+
env.mock_all_auths();
- init(&client, &env, &owner, 50, 30, 15, 5);
+ client.initialize_split(&owner, &0, &50, &30, &15, &5);
+ // Exact edge case: i128::MAX / 100
let edge_amount = i128::MAX / 100;
+
let result = client.try_calculate_split(&edge_amount);
assert!(result.is_ok());
+
let amounts = result.unwrap().unwrap();
assert_eq!(amounts.len(), 4);
}
@@ -131,16 +177,23 @@ fn test_split_with_100_percent_to_one_category() {
let env = Env::default();
let contract_id = env.register_contract(None, RemittanceSplit);
let client = RemittanceSplitClient::new(&env, &contract_id);
- let owner = ::generate(&env);
+ let owner = ::generate(&env);
+
env.mock_all_auths();
- init(&client, &env, &owner, 100, 0, 0, 0);
+ // 100% to spending, 0% to others
+ client.initialize_split(&owner, &0, &100, &0, &0, &0);
let large_amount = i128::MAX / 150;
+
let result = client.try_calculate_split(&large_amount);
assert!(result.is_ok());
+
let amounts = result.unwrap().unwrap();
+ // First amount should be the full amount
+ // .get(i) returns Option, so .unwrap() here is correct and necessary
assert_eq!(amounts.get(0).unwrap(), large_amount);
+ // Others should be 0
assert_eq!(amounts.get(1).unwrap(), 0);
assert_eq!(amounts.get(2).unwrap(), 0);
assert_eq!(amounts.get(3).unwrap(), 0);
@@ -151,16 +204,22 @@ fn test_rounding_behavior_with_large_amounts() {
let env = Env::default();
let contract_id = env.register_contract(None, RemittanceSplit);
let client = RemittanceSplitClient::new(&env, &contract_id);
- let owner = ::generate(&env);
+ let owner = ::generate(&env);
+
env.mock_all_auths();
- init(&client, &env, &owner, 33, 33, 33, 1);
+ // Use percentages that don't divide evenly
+ client.initialize_split(&owner, &0, &33, &33, &33, &1);
let large_amount = i128::MAX / 200;
+
let result = client.try_calculate_split(&large_amount);
assert!(result.is_ok());
+
let amounts = result.unwrap().unwrap();
let total: i128 = amounts.iter().sum();
+
+ // Due to rounding, total should equal input
assert_eq!(total, large_amount);
}
@@ -169,17 +228,28 @@ fn test_sequential_large_calculations() {
let env = Env::default();
let contract_id = env.register_contract(None, RemittanceSplit);
let client = RemittanceSplitClient::new(&env, &contract_id);
- let owner = ::generate(&env);
+ let owner = ::generate(&env);
+
env.mock_all_auths();
- init(&client, &env, &owner, 50, 30, 15, 5);
+ client.initialize_split(&owner, &0, &50, &30, &15, &5);
+
+ // Test with progressively larger amounts
+ let amounts_to_test = vec![
+ 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);
+ for amount in amounts_to_test {
+ let result = client.try_calculate_split(&amount);
assert!(result.is_ok(), "Failed for amount: {}", amount);
+
let splits = result.unwrap().unwrap();
let total: i128 = splits.iter().sum();
- assert_eq!(total, *amount, "Failed for amount: {}", amount);
+ assert_eq!(total, amount, "Failed for amount: {}", amount);
}
}
@@ -188,14 +258,27 @@ fn test_checked_arithmetic_prevents_silent_overflow() {
let env = Env::default();
let contract_id = env.register_contract(None, RemittanceSplit);
let client = RemittanceSplitClient::new(&env, &contract_id);
- let owner = ::generate(&env);
- env.mock_all_auths();
+ let owner = ::generate(&env);
- init(&client, &env, &owner, 50, 30, 15, 5);
+ env.mock_all_auths();
- 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);
+ client.initialize_split(&owner, &0, &50, &30, &15, &5);
+
+ // Test values that would overflow with unchecked arithmetic
+ let dangerous_amounts = vec![
+ i128::MAX / 40, // Will overflow when multiplied by 50
+ i128::MAX / 30, // Will overflow when multiplied by 50
+ i128::MAX, // Will definitely overflow
+ ];
+
+ for amount in dangerous_amounts {
+ let result = client.try_calculate_split(&amount);
+ // Should return error, not panic or wrap around
+ assert!(
+ result.is_err(),
+ "Should have detected overflow for amount: {}",
+ amount
+ );
}
}
@@ -204,15 +287,26 @@ fn test_insurance_remainder_calculation_with_large_values() {
let env = Env::default();
let contract_id = env.register_contract(None, RemittanceSplit);
let client = RemittanceSplitClient::new(&env, &contract_id);
- let owner = ::generate(&env);
+ let owner = ::generate(&env);
+
env.mock_all_auths();
- init(&client, &env, &owner, 40, 30, 20, 10);
+ // Insurance gets the remainder after other allocations
+ client.initialize_split(&owner, &0, &40, &30, &20, &10);
let large_amount = i128::MAX / 200;
+
let result = client.try_calculate_split(&large_amount);
assert!(result.is_ok());
+
let amounts = result.unwrap().unwrap();
- let total: i128 = amounts.iter().sum();
- assert_eq!(total, large_amount);
+
+ // Verify insurance (last element) is calculated correctly as remainder
+ // Note: Soroban Vec::get returns Option, so these unwrap()s are correct for the elements
+ let spending = amounts.get(0).unwrap();
+ let savings = amounts.get(1).unwrap();
+ let bills = amounts.get(2).unwrap();
+ let insurance = amounts.get(3).unwrap();
+
+ assert_eq!(spending + savings + bills + insurance, large_amount);
}
diff --git a/remitwise-common/src/lib.rs b/remitwise-common/src/lib.rs
index 038c09f7..1bab4a85 100644
--- a/remitwise-common/src/lib.rs
+++ b/remitwise-common/src/lib.rs
@@ -164,8 +164,6 @@ impl RemitwiseEvents {
// Standardized TTL Constants (Ledger Counts)
pub const DAY_IN_LEDGERS: u32 = 17280; // ~5 seconds per ledger
-pub const INSTANCE_BUMP_AMOUNT: u32 = 30 * DAY_IN_LEDGERS; // 30 days
-pub const INSTANCE_LIFETIME_THRESHOLD: u32 = 7 * DAY_IN_LEDGERS; // 7 days
pub const PERSISTENT_BUMP_AMOUNT: u32 = 60 * DAY_IN_LEDGERS; // 60 days
pub const PERSISTENT_LIFETIME_THRESHOLD: u32 = 15 * DAY_IN_LEDGERS; // 15 days
diff --git a/reporting/src/tests.rs b/reporting/src/tests.rs
index ec4e10a8..ae1a21fe 100644
--- a/reporting/src/tests.rs
+++ b/reporting/src/tests.rs
@@ -1,116 +1,92 @@
-use super::*;
-use soroban_sdk::{
- testutils::{storage::Instance as _, Address as _, Ledger, LedgerInfo},
- Address, Env,
-};
-use testutils::set_ledger_time;
+// tests module is compiled only when running tests via cfg(test) at lib.rs
+use crate::*;
+use soroban_sdk::{contract, contractimpl, testutils::Address as _, Address, Env, String, Vec};
-fn create_test_env() -> Env {
- let env = Env::default();
- env.mock_all_auths();
- env
-}
-
-// Mock contracts for testing
-mod remittance_split {
- use soroban_sdk::{contract, contractimpl, Env, Vec};
+mod remittance_split_mock {
+ use super::*;
#[contract]
- pub struct RemittanceSplit;
+ pub struct RemittanceSplitMock;
#[contractimpl]
- impl RemittanceSplit {
- pub fn get_split(env: &Env) -> Vec {
- let mut split = Vec::new(env);
- split.push_back(50);
- split.push_back(30);
- split.push_back(15);
- split.push_back(5);
- split
+ impl RemittanceSplitTrait for RemittanceSplitMock {
+ fn get_split(env: &Env) -> Vec {
+ let mut out = Vec::new(env);
+ out.push_back(50);
+ out.push_back(30);
+ out.push_back(15);
+ out.push_back(5);
+ out
}
- pub fn calculate_split(env: Env, total_amount: i128) -> Vec {
- let mut amounts = Vec::new(&env);
- amounts.push_back(total_amount * 50 / 100);
- amounts.push_back(total_amount * 30 / 100);
- amounts.push_back(total_amount * 15 / 100);
- amounts.push_back(total_amount * 5 / 100);
- amounts
+ fn calculate_split(env: Env, total_amount: i128) -> Vec {
+ let mut out = Vec::new(&env);
+ out.push_back(total_amount * 50 / 100);
+ out.push_back(total_amount * 30 / 100);
+ out.push_back(total_amount * 15 / 100);
+ out.push_back(total_amount * 5 / 100);
+ out
}
}
}
-mod savings_goals {
- use crate::{SavingsGoal, SavingsGoalsTrait};
- use soroban_sdk::{contract, contractimpl, Address, Env, String as SorobanString, Vec};
+mod savings_mock {
+ use super::*;
#[contract]
- pub struct SavingsGoalsContract;
+ pub struct SavingsGoalsMock;
#[contractimpl]
- impl SavingsGoalsTrait for SavingsGoalsContract {
- fn get_all_goals(_env: Env, _owner: Address) -> Vec {
- let env = _env;
+ impl SavingsGoalsTrait for SavingsGoalsMock {
+ fn get_all_goals(env: Env, owner: Address) -> Vec {
let mut goals = Vec::new(&env);
goals.push_back(SavingsGoal {
id: 1,
- owner: _owner.clone(),
- name: SorobanString::from_str(&env, "Education"),
- target_amount: 10000,
- current_amount: 7000,
- target_date: 1735689600,
- locked: true,
- unlock_date: None,
- });
- goals.push_back(SavingsGoal {
- id: 2,
- owner: _owner,
- name: SorobanString::from_str(&env, "Emergency"),
- target_amount: 5000,
- current_amount: 5000,
- target_date: 1735689600,
+ owner,
+ name: String::from_str(&env, "Emergency"),
+ target_amount: 1000,
+ current_amount: 500,
+ target_date: 2_000_000_000,
locked: true,
unlock_date: None,
});
goals
}
- fn is_goal_completed(_env: Env, goal_id: u32) -> bool {
- goal_id == 2
+ fn is_goal_completed(_env: Env, _goal_id: u32) -> bool {
+ false
}
}
}
-mod bill_payments {
- use crate::{Bill, BillPage, BillPaymentsTrait};
- use soroban_sdk::{contract, contractimpl, Address, Env, String as SorobanString, Vec};
+mod bills_mock {
+ use super::*;
#[contract]
- pub struct BillPayments;
+ pub struct BillPaymentsMock;
#[contractimpl]
- impl BillPaymentsTrait for BillPayments {
- fn get_unpaid_bills(_env: Env, _owner: Address, _cursor: u32, _limit: u32) -> BillPage {
- let env = _env;
- let mut bills = Vec::new(&env);
- bills.push_back(Bill {
+ impl BillPaymentsTrait for BillPaymentsMock {
+ fn get_unpaid_bills(env: Env, owner: Address, _cursor: u32, _limit: u32) -> BillPage {
+ let mut items = Vec::new(&env);
+ items.push_back(Bill {
id: 1,
- owner: _owner,
- name: SorobanString::from_str(&env, "Electricity"),
+ owner,
+ name: String::from_str(&env, "Power"),
amount: 100,
- due_date: 1735689600,
- recurring: true,
- frequency_days: 30,
+ due_date: 2_000_000_000,
+ recurring: false,
+ frequency_days: 0,
paid: false,
- created_at: 1704067200,
+ created_at: 1_700_000_000,
paid_at: None,
schedule_id: None,
- currency: SorobanString::from_str(&env, "XLM"),
+ currency: String::from_str(&env, "XLM"),
});
BillPage {
- count: bills.len(),
- items: bills,
+ items: items.clone(),
next_cursor: 0,
+ count: items.len(),
}
}
@@ -119,1599 +95,94 @@ mod bill_payments {
}
fn get_all_bills_for_owner(
- _env: Env,
- _owner: Address,
+ env: Env,
+ owner: Address,
_cursor: u32,
_limit: u32,
) -> BillPage {
- let env = _env;
- let mut bills = Vec::new(&env);
- bills.push_back(Bill {
- id: 1,
- owner: _owner.clone(),
- name: SorobanString::from_str(&env, "Electricity"),
- amount: 100,
- due_date: 1735689600,
- recurring: true,
- frequency_days: 30,
- paid: false,
- created_at: 1704067200,
- paid_at: None,
- schedule_id: None,
- currency: SorobanString::from_str(&env, "XLM"),
- });
- bills.push_back(Bill {
- id: 2,
- owner: _owner,
- name: SorobanString::from_str(&env, "Water"),
- amount: 50,
- due_date: 1735689600,
- recurring: true,
- frequency_days: 30,
- paid: true,
- created_at: 1704067200,
- paid_at: Some(1704153600),
- schedule_id: None,
- currency: SorobanString::from_str(&env, "XLM"),
- });
- BillPage {
- count: bills.len(),
- items: bills,
- next_cursor: 0,
- }
+ Self::get_unpaid_bills(env, owner, 0, 50)
}
}
}
-mod insurance {
- use crate::{InsurancePolicy, InsuranceTrait};
- use soroban_sdk::{contract, contractimpl, Address, Env, String as SorobanString, Vec};
+mod insurance_mock {
+ use super::*;
#[contract]
- pub struct Insurance;
+ pub struct InsuranceMock;
#[contractimpl]
- impl InsuranceTrait for Insurance {
- fn get_active_policies(
- _env: Env,
- _owner: Address,
- _cursor: u32,
- _limit: u32,
- ) -> crate::PolicyPage {
- let env = _env;
- let mut policies = Vec::new(&env);
- policies.push_back(InsurancePolicy {
+ impl InsuranceTrait for InsuranceMock {
+ fn get_active_policies(env: Env, owner: Address, _cursor: u32, _limit: u32) -> PolicyPage {
+ let mut items = Vec::new(&env);
+ items.push_back(InsurancePolicy {
id: 1,
- owner: _owner,
- name: SorobanString::from_str(&env, "Health Insurance"),
- coverage_type: SorobanString::from_str(&env, "health"),
- monthly_premium: 200,
- coverage_amount: 50000,
+ owner,
+ name: String::from_str(&env, "Health"),
+ coverage_type: String::from_str(&env, "health"),
+ monthly_premium: 50,
+ coverage_amount: 10_000,
active: true,
- next_payment_date: 1735689600,
+ next_payment_date: 2_000_000_000,
schedule_id: None,
});
- crate::PolicyPage {
- items: policies,
+ PolicyPage {
+ items: items.clone(),
next_cursor: 0,
- count: 1,
+ count: items.len(),
}
}
fn get_total_monthly_premium(_env: Env, _owner: Address) -> i128 {
- 200
+ 50
}
}
}
-fn create_test_env() -> Env {
- let env = Env::default();
- env.mock_all_auths();
- env
-}
-
-#[test]
-fn test_init_reporting_contract_succeeds() {
- let env = Env::default();
- env.mock_all_auths();
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let admin = Address::generate(&env);
-
- client.init(&admin);
-
- let stored_admin = client.get_admin();
- assert_eq!(stored_admin, Some(admin));
-}
-
-#[test]
-fn test_init_twice_fails() {
- let env = Env::default();
- env.mock_all_auths();
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let admin = Address::generate(&env);
-
- client.init(&admin);
- let result = client.try_init(&admin); // Should fail
- assert!(result.is_err(), "init should fail when called twice");
-}
-
-#[test]
-fn test_configure_addresses_succeeds() {
- let env = Env::default();
- env.mock_all_auths();
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let admin = Address::generate(&env);
-
- client.init(&admin);
-
- let remittance_split = Address::generate(&env);
- let savings_goals = Address::generate(&env);
- let bill_payments = Address::generate(&env);
- let insurance = Address::generate(&env);
- let family_wallet = Address::generate(&env);
-
- client.configure_addresses(
- &admin,
- &remittance_split,
- &savings_goals,
- &bill_payments,
- &insurance,
- &family_wallet,
- );
-
- let addresses = client.get_addresses();
- assert!(addresses.is_some());
- let addrs = addresses.unwrap();
- assert_eq!(addrs.remittance_split, remittance_split);
- assert_eq!(addrs.savings_goals, savings_goals);
-}
-
-#[test]
-fn test_configure_addresses_unauthorized() {
- let env = create_test_env();
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let admin = Address::generate(&env);
- let non_admin = Address::generate(&env);
-
- client.init(&admin);
-
- let remittance_split = Address::generate(&env);
- let savings_goals = Address::generate(&env);
- let bill_payments = Address::generate(&env);
- let insurance = Address::generate(&env);
- let family_wallet = Address::generate(&env);
-
- let result = client.try_configure_addresses(
- &non_admin,
- &remittance_split,
- &savings_goals,
- &bill_payments,
- &insurance,
- &family_wallet,
- );
- assert!(
- result.is_err(),
- "configure_addresses should fail for non-admin"
- );
-}
-
-#[test]
-fn test_get_remittance_summary() {
- let env = Env::default();
- env.mock_all_auths();
- set_ledger_time(&env, 1, 1704067200); // Standard timestamp for reporting tests
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let admin = Address::generate(&env);
- let user = Address::generate(&env);
-
- client.init(&admin);
-
- // Register mock contracts
- let remittance_split_id = env.register_contract(None, remittance_split::RemittanceSplit);
- let savings_goals_id = env.register_contract(None, savings_goals::SavingsGoalsContract);
- let bill_payments_id = env.register_contract(None, bill_payments::BillPayments);
- let insurance_id = env.register_contract(None, insurance::Insurance);
- let family_wallet = Address::generate(&env);
-
- client.configure_addresses(
- &admin,
- &remittance_split_id,
- &savings_goals_id,
- &bill_payments_id,
- &insurance_id,
- &family_wallet,
- );
-
- let total_amount = 10000i128;
- let period_start = 1704067200u64;
- let period_end = 1706745600u64;
-
- let summary = client.get_remittance_summary(&user, &total_amount, &period_start, &period_end);
-
- assert_eq!(summary.total_received, 10000);
- assert_eq!(summary.total_allocated, 10000);
- assert_eq!(summary.category_breakdown.len(), 4);
- assert_eq!(summary.period_start, period_start);
- assert_eq!(summary.period_end, period_end);
-
- // Check category breakdown
- let spending = summary.category_breakdown.get(0).unwrap();
- assert_eq!(spending.category, Category::Spending);
- assert_eq!(spending.amount, 5000);
- assert_eq!(spending.percentage, 50);
-}
-
-#[test]
-fn test_get_savings_report() {
- let env = Env::default();
- env.mock_all_auths();
- set_ledger_time(&env, 1, 1704067200); // Standard timestamp for reporting tests
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let admin = Address::generate(&env);
- let user = Address::generate(&env);
-
- client.init(&admin);
-
- let remittance_split_id = env.register_contract(None, remittance_split::RemittanceSplit);
- let savings_goals_id = env.register_contract(None, savings_goals::SavingsGoalsContract);
- let bill_payments_id = env.register_contract(None, bill_payments::BillPayments);
- let insurance_id = env.register_contract(None, insurance::Insurance);
- let family_wallet = Address::generate(&env);
-
- client.configure_addresses(
- &admin,
- &remittance_split_id,
- &savings_goals_id,
- &bill_payments_id,
- &insurance_id,
- &family_wallet,
- );
-
- let period_start = 1704067200u64;
- let period_end = 1706745600u64;
-
- let report = client.get_savings_report(&user, &period_start, &period_end);
-
- assert_eq!(report.total_goals, 2);
- assert_eq!(report.completed_goals, 1);
- assert_eq!(report.total_target, 15000);
- assert_eq!(report.total_saved, 12000);
- assert_eq!(report.completion_percentage, 80);
-}
-
-#[test]
-fn test_get_bill_compliance_report() {
- let env = Env::default();
- env.mock_all_auths();
- set_ledger_time(&env, 1, 1704067200); // Standard timestamp for reporting tests
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let admin = Address::generate(&env);
- let user = Address::generate(&env);
-
- client.init(&admin);
-
- let remittance_split_id = env.register_contract(None, remittance_split::RemittanceSplit);
- let savings_goals_id = env.register_contract(None, savings_goals::SavingsGoalsContract);
- let bill_payments_id = env.register_contract(None, bill_payments::BillPayments);
- let insurance_id = env.register_contract(None, insurance::Insurance);
- let family_wallet = Address::generate(&env);
-
- client.configure_addresses(
- &admin,
- &remittance_split_id,
- &savings_goals_id,
- &bill_payments_id,
- &insurance_id,
- &family_wallet,
- );
-
- let period_start = 1704067200u64;
- let period_end = 1706745600u64;
-
- let report = client.get_bill_compliance_report(&user, &period_start, &period_end);
-
- // Note: Mock returns bills for a generated address, so user-specific filtering will show 0
- // This is expected behavior for the test
- assert_eq!(report.period_start, period_start);
- assert_eq!(report.period_end, period_end);
-}
-
-#[test]
-fn test_get_insurance_report() {
- let env = Env::default();
- env.mock_all_auths();
- set_ledger_time(&env, 1, 1704067200); // Standard timestamp for reporting tests
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let admin = Address::generate(&env);
- let user = Address::generate(&env);
-
- client.init(&admin);
-
- let remittance_split_id = env.register_contract(None, remittance_split::RemittanceSplit);
- let savings_goals_id = env.register_contract(None, savings_goals::SavingsGoalsContract);
- let bill_payments_id = env.register_contract(None, bill_payments::BillPayments);
- let insurance_id = env.register_contract(None, insurance::Insurance);
- let family_wallet = Address::generate(&env);
-
- client.configure_addresses(
- &admin,
- &remittance_split_id,
- &savings_goals_id,
- &bill_payments_id,
- &insurance_id,
- &family_wallet,
- );
-
- let period_start = 1704067200u64;
- let period_end = 1706745600u64;
-
- let report = client.get_insurance_report(&user, &period_start, &period_end);
-
- assert_eq!(report.active_policies, 1);
- assert_eq!(report.total_coverage, 50000);
- assert_eq!(report.monthly_premium, 200);
- assert_eq!(report.annual_premium, 2400);
- assert_eq!(report.coverage_to_premium_ratio, 2083); // 50000 * 100 / 2400
-}
-
-#[test]
-fn test_calculate_health_score() {
- let env = Env::default();
- env.mock_all_auths();
- set_ledger_time(&env, 1, 1704067200); // Standard timestamp for reporting tests
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let admin = Address::generate(&env);
- let user = Address::generate(&env);
-
- client.init(&admin);
-
- let remittance_split_id = env.register_contract(None, remittance_split::RemittanceSplit);
- let savings_goals_id = env.register_contract(None, savings_goals::SavingsGoalsContract);
- let bill_payments_id = env.register_contract(None, bill_payments::BillPayments);
- let insurance_id = env.register_contract(None, insurance::Insurance);
- let family_wallet = Address::generate(&env);
-
- client.configure_addresses(
- &admin,
- &remittance_split_id,
- &savings_goals_id,
- &bill_payments_id,
- &insurance_id,
- &family_wallet,
- );
-
- let health_score = client.calculate_health_score(&user, &10000);
-
- // Savings: 12000/15000 = 80% -> 32 points
- // Bills: Has unpaid bills but none overdue (due_date > current_time) -> 35 points
- // Insurance: Has 1 active policy -> 20 points
- // Total: 32 + 35 + 20 = 87
- assert_eq!(health_score.savings_score, 32);
- assert_eq!(health_score.bills_score, 35);
- assert_eq!(health_score.insurance_score, 20);
- assert_eq!(health_score.score, 87);
-}
-
-#[test]
-fn test_get_financial_health_report() {
- let env = Env::default();
- env.mock_all_auths();
- set_ledger_time(&env, 1, 1704067200); // Standard timestamp for reporting tests
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let admin = Address::generate(&env);
- let user = Address::generate(&env);
-
- client.init(&admin);
-
- let remittance_split_id = env.register_contract(None, remittance_split::RemittanceSplit);
- let savings_goals_id = env.register_contract(None, savings_goals::SavingsGoalsContract);
- let bill_payments_id = env.register_contract(None, bill_payments::BillPayments);
- let insurance_id = env.register_contract(None, insurance::Insurance);
- let family_wallet = Address::generate(&env);
-
- client.configure_addresses(
- &admin,
- &remittance_split_id,
- &savings_goals_id,
- &bill_payments_id,
- &insurance_id,
- &family_wallet,
- );
-
- let total_remittance = 10000i128;
- let period_start = 1704067200u64;
- let period_end = 1706745600u64;
-
- let report =
- client.get_financial_health_report(&user, &total_remittance, &period_start, &period_end);
-
- assert_eq!(report.health_score.score, 87);
- assert_eq!(report.remittance_summary.total_received, 10000);
- assert_eq!(report.savings_report.total_goals, 2);
- assert_eq!(report.insurance_report.active_policies, 1);
- assert_eq!(report.generated_at, 1704067200);
-}
-
-#[test]
-fn test_get_trend_analysis() {
- let env = Env::default();
- env.mock_all_auths();
- set_ledger_time(&env, 1, 1704067200); // Standard timestamp for reporting tests
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let user = Address::generate(&env);
-
- let current_amount = 15000i128;
- let previous_amount = 10000i128;
-
- let trend = client.get_trend_analysis(&user, ¤t_amount, &previous_amount);
-
- assert_eq!(trend.current_amount, 15000);
- assert_eq!(trend.previous_amount, 10000);
- assert_eq!(trend.change_amount, 5000);
- assert_eq!(trend.change_percentage, 50); // 50% increase
-}
-
-#[test]
-fn test_get_trend_analysis_decrease() {
- let env = Env::default();
- env.mock_all_auths();
- set_ledger_time(&env, 1, 1704067200); // Standard timestamp for reporting tests
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let user = Address::generate(&env);
-
- let current_amount = 8000i128;
- let previous_amount = 10000i128;
-
- let trend = client.get_trend_analysis(&user, ¤t_amount, &previous_amount);
-
- assert_eq!(trend.current_amount, 8000);
- assert_eq!(trend.previous_amount, 10000);
- assert_eq!(trend.change_amount, -2000);
- assert_eq!(trend.change_percentage, -20); // 20% decrease
-}
-
-#[test]
-fn test_store_and_retrieve_report() {
- let env = Env::default();
- env.mock_all_auths();
- set_ledger_time(&env, 1, 1704067200); // Standard timestamp for reporting tests
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let admin = Address::generate(&env);
- let user = Address::generate(&env);
-
- client.init(&admin);
-
- let remittance_split_id = env.register_contract(None, remittance_split::RemittanceSplit);
- let savings_goals_id = env.register_contract(None, savings_goals::SavingsGoalsContract);
- let bill_payments_id = env.register_contract(None, bill_payments::BillPayments);
- let insurance_id = env.register_contract(None, insurance::Insurance);
- let family_wallet = Address::generate(&env);
-
- client.configure_addresses(
- &admin,
- &remittance_split_id,
- &savings_goals_id,
- &bill_payments_id,
- &insurance_id,
- &family_wallet,
- );
-
- let total_remittance = 10000i128;
- let period_start = 1704067200u64;
- let period_end = 1706745600u64;
-
- let report =
- client.get_financial_health_report(&user, &total_remittance, &period_start, &period_end);
-
- let period_key = 202401u64; // January 2024
-
- let stored = client.store_report(&user, &report, &period_key);
- assert!(stored);
-
- let retrieved = client.get_stored_report(&user, &period_key);
- assert!(retrieved.is_some());
- let retrieved_report = retrieved.unwrap();
- assert_eq!(
- retrieved_report.health_score.score,
- report.health_score.score
- );
- assert_eq!(
- retrieved_report.remittance_summary.total_received,
- report.remittance_summary.total_received
- );
-}
-
-#[test]
-fn test_retrieve_nonexistent_report() {
- let env = Env::default();
- env.mock_all_auths();
- set_ledger_time(&env, 1, 1704067200); // Standard timestamp for reporting tests
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let user = Address::generate(&env);
-
- let retrieved = client.get_stored_report(&user, &999999);
- assert!(retrieved.is_none());
-}
-
-#[test]
-fn test_health_score_no_goals() {
+fn setup_reporting() -> (Env, ReportingContractClient<'static>, Address, Address) {
let env = Env::default();
env.mock_all_auths();
- set_ledger_time(&env, 1, 1704067200); // Standard timestamp for reporting tests
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let admin = Address::generate(&env);
- let user = Address::generate(&env);
-
- client.init(&admin);
-
- // Create a mock savings contract that returns no goals
- mod empty_savings {
- use crate::{SavingsGoal, SavingsGoalsTrait};
- use soroban_sdk::{contract, contractimpl, Address, Env, Vec};
- #[contract]
- pub struct EmptySavings;
+ let reporting_id = env.register_contract(None, ReportingContract);
+ let client = ReportingContractClient::new(&env, &reporting_id);
- #[contractimpl]
- impl SavingsGoalsTrait for EmptySavings {
- fn get_all_goals(_env: Env, _owner: Address) -> Vec {
- Vec::new(&_env)
- }
-
- fn is_goal_completed(_env: Env, _goal_id: u32) -> bool {
- false
- }
- }
- }
-
- let remittance_split_id = env.register_contract(None, remittance_split::RemittanceSplit);
- let savings_goals_id = env.register_contract(None, empty_savings::EmptySavings);
- let bill_payments_id = env.register_contract(None, bill_payments::BillPayments);
- let insurance_id = env.register_contract(None, insurance::Insurance);
- let family_wallet = Address::generate(&env);
-
- client.configure_addresses(
- &admin,
- &remittance_split_id,
- &savings_goals_id,
- &bill_payments_id,
- &insurance_id,
- &family_wallet,
- );
-
- let health_score = client.calculate_health_score(&user, &10000);
-
- // Should get default score of 20 for savings when no goals exist
- assert_eq!(health_score.savings_score, 20);
-}
-
-// ============================================
-// Storage Optimization and Archival Tests
-// ============================================
-
-#[test]
-fn test_archive_old_reports() {
- let env = Env::default();
- env.mock_all_auths();
- set_ledger_time(&env, 1, 1704067200); // Standard timestamp for reporting tests
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
let admin = Address::generate(&env);
let user = Address::generate(&env);
client.init(&admin);
- let remittance_split_id = env.register_contract(None, remittance_split::RemittanceSplit);
- let savings_goals_id = env.register_contract(None, savings_goals::SavingsGoalsContract);
- let bill_payments_id = env.register_contract(None, bill_payments::BillPayments);
- let insurance_id = env.register_contract(None, insurance::Insurance);
- let family_wallet = Address::generate(&env);
-
- client.configure_addresses(
- &admin,
- &remittance_split_id,
- &savings_goals_id,
- &bill_payments_id,
- &insurance_id,
- &family_wallet,
- );
+ let rem_id = env.register_contract(None, remittance_split_mock::RemittanceSplitMock);
+ let sav_id = env.register_contract(None, savings_mock::SavingsGoalsMock);
+ let bill_id = env.register_contract(None, bills_mock::BillPaymentsMock);
+ let ins_id = env.register_contract(None, insurance_mock::InsuranceMock);
+ let fam = Address::generate(&env);
- // Generate and store a report
- let total_remittance = 10000i128;
- let period_start = 1704067200u64;
- let period_end = 1706745600u64;
+ client.configure_addresses(&admin, &rem_id, &sav_id, &bill_id, &ins_id, &fam);
- let report =
- client.get_financial_health_report(&user, &total_remittance, &period_start, &period_end);
-
- let period_key = 202401u64;
- client.store_report(&user, &report, &period_key);
-
- // Verify report is stored
- assert!(client.get_stored_report(&user, &period_key).is_some());
-
- // Archive reports before far future timestamp
- let archived_count = client.archive_old_reports(&admin, &2000000000);
- assert_eq!(archived_count, 1);
-
- // Verify report is no longer in active storage
- assert!(client.get_stored_report(&user, &period_key).is_none());
-
- // Verify report is in archive
- let archived = client.get_archived_reports(&user);
- assert_eq!(archived.len(), 1);
-}
-
-#[test]
-fn test_archive_empty_when_no_old_reports() {
- let env = Env::default();
- env.mock_all_auths();
- set_ledger_time(&env, 1, 1704067200); // Standard timestamp for reporting tests
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let admin = Address::generate(&env);
-
- client.init(&admin);
-
- // Archive with no reports stored
- let archived_count = client.archive_old_reports(&admin, &2000000000);
- assert_eq!(archived_count, 0);
+ // SAFETY: tests keep env and client alive together.
+ let client: ReportingContractClient<'static> = unsafe { core::mem::transmute(client) };
+ (env, client, admin, user)
}
#[test]
-fn test_cleanup_old_reports() {
- let env = Env::default();
- env.mock_all_auths();
- set_ledger_time(&env, 1, 1704067200); // Standard timestamp for reporting tests
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let admin = Address::generate(&env);
- let user = Address::generate(&env);
-
- client.init(&admin);
-
- let remittance_split_id = env.register_contract(None, remittance_split::RemittanceSplit);
- let savings_goals_id = env.register_contract(None, savings_goals::SavingsGoalsContract);
- let bill_payments_id = env.register_contract(None, bill_payments::BillPayments);
- let insurance_id = env.register_contract(None, insurance::Insurance);
- let family_wallet = Address::generate(&env);
-
- client.configure_addresses(
- &admin,
- &remittance_split_id,
- &savings_goals_id,
- &bill_payments_id,
- &insurance_id,
- &family_wallet,
- );
-
- // Generate and store a report
- let report = client.get_financial_health_report(&user, &10000, &1704067200, &1706745600);
- client.store_report(&user, &report, &202401);
-
- // Archive the report
- client.archive_old_reports(&admin, &2000000000);
- assert_eq!(client.get_archived_reports(&user).len(), 1);
-
- // Cleanup old archives
- let deleted = client.cleanup_old_reports(&admin, &2000000000);
- assert_eq!(deleted, 1);
-
- // Verify archives are gone
- assert_eq!(client.get_archived_reports(&user).len(), 0);
+fn init_and_configure_addresses() {
+ let (env, client, admin, _) = setup_reporting();
+ assert_eq!(client.get_admin().unwrap(), admin);
+ assert!(client.get_addresses().is_some());
+ let _ = env;
}
#[test]
-fn test_storage_stats() {
- let env = Env::default();
- env.mock_all_auths();
- set_ledger_time(&env, 1, 1704067200); // Standard timestamp for reporting tests
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let admin = Address::generate(&env);
- let user = Address::generate(&env);
-
- client.init(&admin);
+fn generates_health_report_and_store_roundtrip() {
+ let (env, client, _admin, user) = setup_reporting();
- let remittance_split_id = env.register_contract(None, remittance_split::RemittanceSplit);
- let savings_goals_id = env.register_contract(None, savings_goals::SavingsGoalsContract);
- let bill_payments_id = env.register_contract(None, bill_payments::BillPayments);
- let insurance_id = env.register_contract(None, insurance::Insurance);
- let family_wallet = Address::generate(&env);
+ let report = client.get_financial_health_report(&user, &1_000, &1_700_000_000, &1_800_000_000);
+ assert!(report.health_score.score > 0);
+ assert_eq!(report.remittance_summary.total_received, 1_000);
- client.configure_addresses(
- &admin,
- &remittance_split_id,
- &savings_goals_id,
- &bill_payments_id,
- &insurance_id,
- &family_wallet,
- );
-
- // Initial stats
- let stats = client.get_storage_stats();
- assert_eq!(stats.active_reports, 0);
- assert_eq!(stats.archived_reports, 0);
-
- // Store a report
- let report = client.get_financial_health_report(&user, &10000, &1704067200, &1706745600);
- client.store_report(&user, &report, &202401);
-
- let stats = client.get_storage_stats();
- assert_eq!(stats.active_reports, 1);
- assert_eq!(stats.archived_reports, 0);
-
- // Archive and check stats
- client.archive_old_reports(&admin, &2000000000);
-
- let stats = client.get_storage_stats();
- assert_eq!(stats.active_reports, 0);
- assert_eq!(stats.archived_reports, 1);
+ let key = 202601u64;
+ assert!(client.store_report(&user, &report, &key));
+ let stored = client.get_stored_report(&user, &key).unwrap();
+ assert_eq!(stored.generated_at, report.generated_at);
+ let _ = env;
}
-
-/// Regression: `get_storage_stats` must stay aligned with real maps across store → archive → cleanup
-/// and after high-volume inserts (see issue #316).
-#[test]
-fn test_storage_stats_regression_across_archive_and_cleanup_cycles() {
- let env = Env::default();
- env.mock_all_auths();
-
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let admin = Address::generate(&env);
- let user = Address::generate(&env);
-
- client.init(&admin);
-
- let remittance_split_id = env.register_contract(None, remittance_split::RemittanceSplit);
- let savings_goals_id = env.register_contract(None, savings_goals::SavingsGoalsContract);
- let bill_payments_id = env.register_contract(None, bill_payments::BillPayments);
- let insurance_id = env.register_contract(None, insurance::Insurance);
- let family_wallet = Address::generate(&env);
-
- client.configure_addresses(
- &admin,
- &remittance_split_id,
- &savings_goals_id,
- &bill_payments_id,
- &insurance_id,
- &family_wallet,
- );
-
- // Zero-state snapshot (no reports stored yet; stats key may be absent)
- set_ledger_time(&env, 1, 1_704_067_200);
- let zero = client.get_storage_stats();
- assert_eq!(zero.active_reports, 0);
- assert_eq!(zero.archived_reports, 0);
- assert_eq!(zero.last_updated, 0);
-
- // High-volume: many active rows, distinct generated_at via ledger time steps
- const TOTAL: u64 = 16;
- let base_ts = 1_000_000u64;
- for i in 0..TOTAL {
- set_ledger_time(&env, 10 + i as u32, base_ts + i);
- let report =
- client.get_financial_health_report(&user, &10000, &1704067200, &1706745600);
- client.store_report(&user, &report, &(202_400 + i));
- }
-
- let after_bulk = client.get_storage_stats();
- assert_eq!(after_bulk.active_reports, TOTAL as u32);
- assert_eq!(after_bulk.archived_reports, 0);
- assert_eq!(after_bulk.last_updated, base_ts + TOTAL - 1);
-
- // Partial archive: only reports with generated_at < cutoff move to ARCH_RPT
- let archive_cutoff = base_ts + 8;
- set_ledger_time(&env, 500, base_ts + 100);
- let n_archived = client.archive_old_reports(&admin, &archive_cutoff);
- assert_eq!(n_archived, 8);
-
- let after_partial = client.get_storage_stats();
- assert_eq!(after_partial.active_reports, 8);
- assert_eq!(after_partial.archived_reports, 8);
- assert_eq!(after_partial.last_updated, base_ts + 100);
-
- // Post-cleanup: archives removed; actives unchanged
- let cleanup_before = base_ts + 200;
- set_ledger_time(&env, 600, base_ts + 150);
- let deleted = client.cleanup_old_reports(&admin, &cleanup_before);
- assert_eq!(deleted, 8);
-
- let after_cleanup = client.get_storage_stats();
- assert_eq!(after_cleanup.active_reports, 8);
- assert_eq!(after_cleanup.archived_reports, 0);
- assert_eq!(after_cleanup.last_updated, base_ts + 150);
-
- // Second cycle: new report increments active; full archive then cleanup returns to zero archived
- set_ledger_time(&env, 700, base_ts + 300);
- let report =
- client.get_financial_health_report(&user, &10000, &1704067200, &1706745600);
- client.store_report(&user, &report, &209_912);
-
- let after_new_store = client.get_storage_stats();
- assert_eq!(after_new_store.active_reports, 9);
- assert_eq!(after_new_store.archived_reports, 0);
-
- set_ledger_time(&env, 800, base_ts + 400);
- client.archive_old_reports(&admin, &(base_ts + 500));
- let after_second_archive = client.get_storage_stats();
- assert_eq!(after_second_archive.active_reports, 0);
- assert_eq!(after_second_archive.archived_reports, 9);
-
- set_ledger_time(&env, 900, base_ts + 500);
- assert_eq!(client.cleanup_old_reports(&admin, &(base_ts + 600)), 9);
- let final_stats = client.get_storage_stats();
- assert_eq!(final_stats.active_reports, 0);
- assert_eq!(final_stats.archived_reports, 0);
-}
-
-#[test]
-#[should_panic(expected = "Only admin can archive reports")]
-fn test_archive_unauthorized() {
- let env = create_test_env();
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let admin = Address::generate(&env);
- let non_admin = Address::generate(&env);
-
- client.init(&admin);
-
- // Non-admin tries to archive
- client.archive_old_reports(&non_admin, &2000000000);
-}
-
-#[test]
-#[should_panic(expected = "Only admin can cleanup reports")]
-fn test_cleanup_unauthorized() {
- let env = create_test_env();
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let admin = Address::generate(&env);
- let non_admin = Address::generate(&env);
-
- client.init(&admin);
-
- // Non-admin tries to cleanup
- client.cleanup_old_reports(&non_admin, &2000000000);
-}
-
-// ============================================================================
-// Storage TTL Extension Tests
-//
-// Verify that instance storage TTL is properly extended on state-changing
-// operations, preventing unexpected data expiration.
-//
-// Contract TTL configuration:
-// INSTANCE_LIFETIME_THRESHOLD = 17,280 ledgers (~1 day)
-// INSTANCE_BUMP_AMOUNT = 518,400 ledgers (~30 days)
-// ARCHIVE_LIFETIME_THRESHOLD = 17,280 ledgers (~1 day)
-// ARCHIVE_BUMP_AMOUNT = 2,592,000 ledgers (~180 days)
-//
-// Operations extending instance TTL:
-// init, configure_addresses, store_report, archive_old_reports,
-// cleanup_old_reports
-//
-// Operations extending archive TTL:
-// archive_old_reports
-// ============================================================================
-
-/// Helper: create test environment with TTL-appropriate ledger settings.
-fn create_ttl_test_env(sequence: u32, max_ttl: u32) -> Env {
- let env = Env::default();
- env.mock_all_auths();
- env.ledger().set(LedgerInfo {
- timestamp: 1704067200,
- protocol_version: 20,
- sequence_number: sequence,
- network_id: [0; 32],
- base_reserve: 10,
- min_temp_entry_ttl: 100,
- min_persistent_entry_ttl: 100,
- max_entry_ttl: max_ttl,
- });
- env
-}
-
-/// Verify that init extends instance storage TTL.
-#[test]
-fn test_instance_ttl_extended_on_init() {
- let env = create_ttl_test_env(100, 700_000);
-
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let admin = Address::generate(&env);
-
- // init calls extend_instance_ttl
- client.init(&admin);
-
- // Inspect instance TTL — must be at least INSTANCE_BUMP_AMOUNT
- 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 init",
- ttl
- );
-}
-
-/// Verify that configure_addresses refreshes instance TTL.
-#[test]
-fn test_instance_ttl_refreshed_on_configure_addresses() {
- let env = create_ttl_test_env(100, 700_000);
-
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let admin = Address::generate(&env);
-
- client.init(&admin);
-
- // Advance ledger so TTL drops below threshold (17,280)
- // After init: live_until = 518,500. At seq 510,000: TTL = 8,500
- env.ledger().set(LedgerInfo {
- timestamp: 1704067200,
- protocol_version: 20,
- sequence_number: 510_000,
- network_id: [0; 32],
- base_reserve: 10,
- min_temp_entry_ttl: 100,
- min_persistent_entry_ttl: 100,
- max_entry_ttl: 700_000,
- });
-
- // Register mock sub-contracts
- let remittance_split_id = env.register_contract(None, remittance_split::RemittanceSplit);
- let savings_goals_id = env.register_contract(None, savings_goals::SavingsGoalsContract);
- let bill_payments_id = env.register_contract(None, bill_payments::BillPayments);
- let insurance_id = env.register_contract(None, insurance::Insurance);
- let family_wallet = Address::generate(&env);
-
- // configure_addresses calls extend_instance_ttl → re-extends TTL to 518,400
- client.configure_addresses(
- &admin,
- &remittance_split_id,
- &savings_goals_id,
- &bill_payments_id,
- &insurance_id,
- &family_wallet,
- );
-
- let ttl = env.as_contract(&contract_id, || env.storage().instance().get_ttl());
- assert!(
- ttl >= 518_400,
- "Instance TTL ({}) must be >= 518,400 after configure_addresses",
- ttl
- );
-}
-
-/// Verify that store_report refreshes instance TTL after ledger advancement.
-#[test]
-fn test_instance_ttl_refreshed_on_store_report() {
- let env = create_ttl_test_env(100, 700_000);
-
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let admin = Address::generate(&env);
- let user = Address::generate(&env);
-
- client.init(&admin);
-
- // Set up sub-contracts
- let remittance_split_id = env.register_contract(None, remittance_split::RemittanceSplit);
- let savings_goals_id = env.register_contract(None, savings_goals::SavingsGoalsContract);
- let bill_payments_id = env.register_contract(None, bill_payments::BillPayments);
- let insurance_id = env.register_contract(None, insurance::Insurance);
- let family_wallet = Address::generate(&env);
-
- client.configure_addresses(
- &admin,
- &remittance_split_id,
- &savings_goals_id,
- &bill_payments_id,
- &insurance_id,
- &family_wallet,
- );
-
- // Generate a report
- let report =
- client.get_financial_health_report(&user, &10000i128, &1704067200u64, &1706745600u64);
-
- // Advance ledger so TTL drops below threshold (17,280)
- env.ledger().set(LedgerInfo {
- timestamp: 1706745600,
- protocol_version: 20,
- sequence_number: 510_000,
- network_id: [0; 32],
- base_reserve: 10,
- min_temp_entry_ttl: 100,
- min_persistent_entry_ttl: 100,
- max_entry_ttl: 700_000,
- });
-
- // store_report calls extend_instance_ttl → re-extends TTL to 518,400
- let stored = client.store_report(&user, &report, &202401u64);
- assert!(stored);
-
- let ttl = env.as_contract(&contract_id, || env.storage().instance().get_ttl());
- assert!(
- ttl >= 518_400,
- "Instance TTL ({}) must be >= 518,400 after store_report",
- ttl
- );
-}
-
-/// Verify data persists across repeated operations spanning multiple
-/// ledger advancements, proving TTL is continuously renewed.
-#[test]
-fn test_report_data_persists_across_ledger_advancements() {
- // Use high min_persistent_entry_ttl so mock sub-contracts survive
- // across large ledger advancements (they don't extend their own TTL)
- let env = Env::default();
- env.mock_all_auths();
- env.ledger().set(LedgerInfo {
- timestamp: 1704067200,
- protocol_version: 20,
- sequence_number: 100,
- network_id: [0; 32],
- base_reserve: 10,
- min_temp_entry_ttl: 100,
- min_persistent_entry_ttl: 1_100_000,
- max_entry_ttl: 1_200_000,
- });
-
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let admin = Address::generate(&env);
- let user = Address::generate(&env);
-
- // Phase 1: Initialize and configure
- client.init(&admin);
-
- let remittance_split_id = env.register_contract(None, remittance_split::RemittanceSplit);
- let savings_goals_id = env.register_contract(None, savings_goals::SavingsGoalsContract);
- let bill_payments_id = env.register_contract(None, bill_payments::BillPayments);
- let insurance_id = env.register_contract(None, insurance::Insurance);
- let family_wallet = Address::generate(&env);
-
- client.configure_addresses(
- &admin,
- &remittance_split_id,
- &savings_goals_id,
- &bill_payments_id,
- &insurance_id,
- &family_wallet,
- );
-
- let report =
- client.get_financial_health_report(&user, &10000i128, &1704067200u64, &1706745600u64);
- client.store_report(&user, &report, &202401u64);
-
- // Phase 2: Advance to seq 510,000 (reporting contract TTL = 8,500 < 17,280)
- env.ledger().set(LedgerInfo {
- timestamp: 1709424000,
- protocol_version: 20,
- sequence_number: 510_000,
- network_id: [0; 32],
- base_reserve: 10,
- min_temp_entry_ttl: 100,
- min_persistent_entry_ttl: 1_100_000,
- max_entry_ttl: 1_200_000,
- });
-
- let report2 =
- client.get_financial_health_report(&user, &15000i128, &1706745600u64, &1709424000u64);
- client.store_report(&user, &report2, &202402u64);
-
- // Phase 3: Advance to seq 1,020,000 (TTL = 8,400 < 17,280)
- env.ledger().set(LedgerInfo {
- timestamp: 1711929600,
- protocol_version: 20,
- sequence_number: 1_020_000,
- network_id: [0; 32],
- base_reserve: 10,
- min_temp_entry_ttl: 100,
- min_persistent_entry_ttl: 1_100_000,
- max_entry_ttl: 1_200_000,
- });
-
- // Both reports should be retrievable (read-only, no TTL extension)
- let r1 = client.get_stored_report(&user, &202401u64);
- assert!(
- r1.is_some(),
- "January report must persist across ledger advancements"
- );
-
- let r2 = client.get_stored_report(&user, &202402u64);
- assert!(r2.is_some(), "February report must persist");
-
- // Admin data should be accessible
- let stored_admin = client.get_admin();
- assert!(stored_admin.is_some(), "Admin must persist");
-
- // TTL should still be positive (read-only ops don't call extend_ttl,
- // but data is still accessible proving TTL hasn't expired)
- let ttl = env.as_contract(&contract_id, || env.storage().instance().get_ttl());
- assert!(
- ttl > 0,
- "Instance TTL ({}) must be > 0 — data persists across ledger advancements",
- ttl
- );
-}
-
-/// Verify that archive_old_reports extends archive TTL (2,592,000 ledgers).
-#[test]
-fn test_archive_ttl_extended_on_archive_reports() {
- let env = create_ttl_test_env(100, 3_000_000);
-
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(&env, &contract_id);
- let admin = Address::generate(&env);
- let user = Address::generate(&env);
-
- client.init(&admin);
-
- let remittance_split_id = env.register_contract(None, remittance_split::RemittanceSplit);
- let savings_goals_id = env.register_contract(None, savings_goals::SavingsGoalsContract);
- let bill_payments_id = env.register_contract(None, bill_payments::BillPayments);
- let insurance_id = env.register_contract(None, insurance::Insurance);
- let family_wallet = Address::generate(&env);
-
- client.configure_addresses(
- &admin,
- &remittance_split_id,
- &savings_goals_id,
- &bill_payments_id,
- &insurance_id,
- &family_wallet,
- );
-
- // Store a report and then archive it
- let report =
- client.get_financial_health_report(&user, &10000i128, &1704067200u64, &1706745600u64);
- client.store_report(&user, &report, &202401u64);
-
- // Advance ledger so TTL drops below threshold before archiving
- env.ledger().set(LedgerInfo {
- timestamp: 1704067200,
- protocol_version: 20,
- sequence_number: 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_old_reports calls extend_instance_ttl first (bumps to 518,400),
- // then extend_archive_ttl which is a no-op (TTL already above threshold)
- let _archived = client.archive_old_reports(&admin, &2000000000);
-
- let ttl = env.as_contract(&contract_id, || env.storage().instance().get_ttl());
- assert!(
- ttl >= 518_400,
- "Instance TTL ({}) must be >= 518,400 after archiving",
- ttl
- );
-}
-
-// ============================================================================
-// Deterministic Trend Analysis Tests (#312)
-//
-// Verify that get_trend_analysis and get_trend_analysis_multi produce
-// identical, deterministic output for identical historical inputs regardless
-// of call order, ledger timestamp, or user address.
-// ============================================================================
-
-fn make_client(env: &Env) -> (ReportingContractClient, Address) {
- let contract_id = env.register_contract(None, ReportingContract);
- let client = ReportingContractClient::new(env, &contract_id);
- let admin = Address::generate(env);
- client.init(&admin);
- (client, admin)
-}
-
-fn make_history(env: &Env, pairs: &[(u64, i128)]) -> Vec<(u64, i128)> {
- let mut v: Vec<(u64, i128)> = Vec::new(env);
- for &p in pairs {
- v.push_back(p);
- }
- v
-}
-
-// --- get_trend_analysis: same output on repeated calls ----------------------
-
-#[test]
-fn test_trend_deterministic_repeated_calls() {
- let env = Env::default();
- env.mock_all_auths();
- let (client, _) = make_client(&env);
- let user = Address::generate(&env);
-
- let t1 = client.get_trend_analysis(&user, &12000i128, &10000i128);
- let t2 = client.get_trend_analysis(&user, &12000i128, &10000i128);
-
- assert_eq!(t1.current_amount, t2.current_amount);
- assert_eq!(t1.previous_amount, t2.previous_amount);
- assert_eq!(t1.change_amount, t2.change_amount);
- assert_eq!(t1.change_percentage, t2.change_percentage);
-}
-
-// --- get_trend_analysis: different users, same amounts → same result --------
-
-#[test]
-fn test_trend_deterministic_different_users_same_amounts() {
- let env = Env::default();
- env.mock_all_auths();
- let (client, _) = make_client(&env);
- let user_a = Address::generate(&env);
- let user_b = Address::generate(&env);
-
- let ta = client.get_trend_analysis(&user_a, &8000i128, &10000i128);
- let tb = client.get_trend_analysis(&user_b, &8000i128, &10000i128);
-
- assert_eq!(ta.current_amount, tb.current_amount);
- assert_eq!(ta.change_percentage, tb.change_percentage);
-}
-
-// --- get_trend_analysis: different ledger timestamps → same result ----------
-
-#[test]
-fn test_trend_deterministic_across_timestamps() {
- let env = Env::default();
- env.mock_all_auths();
- let (client, _) = make_client(&env);
- let user = Address::generate(&env);
-
- set_ledger_time(&env, 1, 1_700_000_000);
- let t1 = client.get_trend_analysis(&user, &5000i128, &4000i128);
-
- set_ledger_time(&env, 2, 1_800_000_000);
- let t2 = client.get_trend_analysis(&user, &5000i128, &4000i128);
-
- assert_eq!(t1.change_amount, t2.change_amount);
- assert_eq!(t1.change_percentage, t2.change_percentage);
-}
-
-// --- increase: 50 % ---------------------------------------------------------
-
-#[test]
-fn test_trend_increase_50_percent() {
- let env = Env::default();
- env.mock_all_auths();
- let (client, _) = make_client(&env);
- let user = Address::generate(&env);
-
- let t = client.get_trend_analysis(&user, &15000i128, &10000i128);
-
- assert_eq!(t.current_amount, 15000);
- assert_eq!(t.previous_amount, 10000);
- assert_eq!(t.change_amount, 5000);
- assert_eq!(t.change_percentage, 50);
-}
-
-// --- decrease: 20 % ---------------------------------------------------------
-
-#[test]
-fn test_trend_decrease_20_percent() {
- let env = Env::default();
- env.mock_all_auths();
- let (client, _) = make_client(&env);
- let user = Address::generate(&env);
-
- let t = client.get_trend_analysis(&user, &8000i128, &10000i128);
-
- assert_eq!(t.change_amount, -2000);
- assert_eq!(t.change_percentage, -20);
-}
-
-// --- zero previous: current > 0 → 100 % ------------------------------------
-
-#[test]
-fn test_trend_zero_previous_nonzero_current() {
- let env = Env::default();
- env.mock_all_auths();
- let (client, _) = make_client(&env);
- let user = Address::generate(&env);
-
- let t = client.get_trend_analysis(&user, &5000i128, &0i128);
-
- assert_eq!(t.change_percentage, 100);
- assert_eq!(t.change_amount, 5000);
-}
-
-// --- both zero → 0 % --------------------------------------------------------
-
-#[test]
-fn test_trend_both_zero() {
- let env = Env::default();
- env.mock_all_auths();
- let (client, _) = make_client(&env);
- let user = Address::generate(&env);
-
- let t = client.get_trend_analysis(&user, &0i128, &0i128);
-
- assert_eq!(t.change_amount, 0);
- assert_eq!(t.change_percentage, 0);
-}
-
-// --- no change: 0 % ---------------------------------------------------------
-
-#[test]
-fn test_trend_no_change() {
- let env = Env::default();
- env.mock_all_auths();
- let (client, _) = make_client(&env);
- let user = Address::generate(&env);
-
- let t = client.get_trend_analysis(&user, &7500i128, &7500i128);
-
- assert_eq!(t.change_amount, 0);
- assert_eq!(t.change_percentage, 0);
-}
-
-// --- exact 100 % increase ---------------------------------------------------
-
-#[test]
-fn test_trend_exact_double() {
- let env = Env::default();
- env.mock_all_auths();
- let (client, _) = make_client(&env);
- let user = Address::generate(&env);
-
- let t = client.get_trend_analysis(&user, &20000i128, &10000i128);
-
- assert_eq!(t.change_percentage, 100);
- assert_eq!(t.change_amount, 10000);
-}
-
-// --- exact 100 % decrease (all lost) ----------------------------------------
-
-#[test]
-fn test_trend_full_decrease() {
- let env = Env::default();
- env.mock_all_auths();
- let (client, _) = make_client(&env);
- let user = Address::generate(&env);
-
- let t = client.get_trend_analysis(&user, &0i128, &10000i128);
-
- assert_eq!(t.change_percentage, -100);
- assert_eq!(t.change_amount, -10000);
-}
-
-// --- large values stay deterministic ----------------------------------------
-
-#[test]
-fn test_trend_large_values_deterministic() {
- let env = Env::default();
- env.mock_all_auths();
- let (client, _) = make_client(&env);
- let user = Address::generate(&env);
-
- let prev = 1_000_000_000_000i128;
- let curr = 1_250_000_000_000i128;
-
- let t1 = client.get_trend_analysis(&user, &curr, &prev);
- let t2 = client.get_trend_analysis(&user, &curr, &prev);
-
- assert_eq!(t1.change_percentage, 25);
- assert_eq!(t1.change_amount, t2.change_amount);
- assert_eq!(t1.change_percentage, t2.change_percentage);
-}
-
-// --- sparse history (2 points) ----------------------------------------------
-
-#[test]
-fn test_trend_multi_sparse_two_points() {
- let env = Env::default();
- env.mock_all_auths();
- let (client, _) = make_client(&env);
- let user = Address::generate(&env);
-
- let history = make_history(&env, &[(1, 1000), (2, 1200)]);
- let results = client.get_trend_analysis_multi(&user, &history);
-
- assert_eq!(results.len(), 1);
- let t = results.get(0).unwrap();
- assert_eq!(t.previous_amount, 1000);
- assert_eq!(t.current_amount, 1200);
- assert_eq!(t.change_amount, 200);
- assert_eq!(t.change_percentage, 20);
-}
-
-// --- dense history (5 points) -----------------------------------------------
-
-#[test]
-fn test_trend_multi_dense_five_points() {
- let env = Env::default();
- env.mock_all_auths();
- let (client, _) = make_client(&env);
- let user = Address::generate(&env);
-
- let history = make_history(&env, &[
- (1, 1000),
- (2, 1100),
- (3, 1210),
- (4, 1331),
- (5, 1464),
- ]);
- let results = client.get_trend_analysis_multi(&user, &history);
-
- assert_eq!(results.len(), 4);
- assert_eq!(results.get(0).unwrap().change_percentage, 10);
- assert_eq!(results.get(1).unwrap().change_percentage, 10);
- assert_eq!(results.get(2).unwrap().change_percentage, 10);
-}
-
-// --- dense history is deterministic on repeat --------------------------------
-
-#[test]
-fn test_trend_multi_dense_deterministic() {
- let env = Env::default();
- env.mock_all_auths();
- let (client, _) = make_client(&env);
- let user = Address::generate(&env);
-
- let history = make_history(&env, &[(1, 500), (2, 600), (3, 720), (4, 864)]);
-
- let r1 = client.get_trend_analysis_multi(&user, &history);
- let r2 = client.get_trend_analysis_multi(&user, &history);
-
- assert_eq!(r1.len(), r2.len());
- for i in 0..r1.len() {
- let a = r1.get(i).unwrap();
- let b = r2.get(i).unwrap();
- assert_eq!(a.change_amount, b.change_amount);
- assert_eq!(a.change_percentage, b.change_percentage);
- }
-}
-
-// --- boundary: single point → empty result ----------------------------------
-
-#[test]
-fn test_trend_multi_single_point_returns_empty() {
- let env = Env::default();
- env.mock_all_auths();
- let (client, _) = make_client(&env);
- let user = Address::generate(&env);
-
- let history = make_history(&env, &[(1, 1000)]);
- let results = client.get_trend_analysis_multi(&user, &history);
-
- assert_eq!(results.len(), 0);
-}
-
-// --- boundary: empty input → empty result -----------------------------------
-
-#[test]
-fn test_trend_multi_empty_returns_empty() {
- let env = Env::default();
- env.mock_all_auths();
- let (client, _) = make_client(&env);
- let user = Address::generate(&env);
-
- let history: Vec<(u64, i128)> = Vec::new(&env);
- let results = client.get_trend_analysis_multi(&user, &history);
-
- assert_eq!(results.len(), 0);
-}
-
-// --- boundary: window with zero crossings -----------------------------------
-
-#[test]
-fn test_trend_multi_window_with_zero_crossing() {
- let env = Env::default();
- env.mock_all_auths();
- let (client, _) = make_client(&env);
- let user = Address::generate(&env);
-
- let history = make_history(&env, &[(1, 0), (2, 500), (3, 0)]);
- let results = client.get_trend_analysis_multi(&user, &history);
-
- assert_eq!(results.len(), 2);
- let first = results.get(0).unwrap();
- assert_eq!(first.change_percentage, 100);
-
- let second = results.get(1).unwrap();
- assert_eq!(second.change_percentage, -100);
-}
-
-// --- boundary: two equal points → 0 % change --------------------------------
-
-#[test]
-fn test_trend_multi_flat_window() {
- let env = Env::default();
- env.mock_all_auths();
- let (client, _) = make_client(&env);
- let user = Address::generate(&env);
-
- let history = make_history(&env, &[(1, 3000), (2, 3000), (3, 3000)]);
- let results = client.get_trend_analysis_multi(&user, &history);
-
- assert_eq!(results.len(), 2);
- for i in 0..results.len() {
- assert_eq!(results.get(i).unwrap().change_percentage, 0);
- assert_eq!(results.get(i).unwrap().change_amount, 0);
- }
-}
-
-// --- boundary: alternating up/down ------------------------------------------
-
-#[test]
-fn test_trend_multi_alternating_up_down() {
- let env = Env::default();
- env.mock_all_auths();
- let (client, _) = make_client(&env);
- let user = Address::generate(&env);
-
- let history = make_history(&env, &[(1, 1000), (2, 2000), (3, 1000), (4, 2000)]);
- let results = client.get_trend_analysis_multi(&user, &history);
-
- assert_eq!(results.len(), 3);
- assert_eq!(results.get(0).unwrap().change_percentage, 100);
- assert_eq!(results.get(1).unwrap().change_percentage, -50);
- assert_eq!(results.get(2).unwrap().change_percentage, 100);
-}
-
-// --- multi: different users same input → same output -------------------------
-
-#[test]
-fn test_trend_multi_deterministic_across_users() {
- let env = Env::default();
- env.mock_all_auths();
- let (client, _) = make_client(&env);
- let user_a = Address::generate(&env);
- let user_b = Address::generate(&env);
-
- let history = make_history(&env, &[(1, 1000), (2, 1500), (3, 1200)]);
-
- let ra = client.get_trend_analysis_multi(&user_a, &history);
- let rb = client.get_trend_analysis_multi(&user_b, &history);
-
- assert_eq!(ra.len(), rb.len());
- for i in 0..ra.len() {
- assert_eq!(
- ra.get(i).unwrap().change_percentage,
- rb.get(i).unwrap().change_percentage
- );
- }
-}
-
-// --- multi: deterministic across ledger timestamps --------------------------
-
-#[test]
-fn test_trend_multi_deterministic_across_timestamps() {
- let env = Env::default();
- env.mock_all_auths();
- let (client, _) = make_client(&env);
- let user = Address::generate(&env);
- let history = make_history(&env, &[(1, 2000), (2, 2500), (3, 2250)]);
-
- set_ledger_time(&env, 10, 1_600_000_000);
- let r1 = client.get_trend_analysis_multi(&user, &history);
-
- set_ledger_time(&env, 20, 1_700_000_000);
- let r2 = client.get_trend_analysis_multi(&user, &history);
-
- assert_eq!(r1.len(), r2.len());
- for i in 0..r1.len() {
- assert_eq!(
- r1.get(i).unwrap().change_percentage,
- r2.get(i).unwrap().change_percentage
- );
- }
-}
-
diff --git a/savings_goals/src/lib.rs b/savings_goals/src/lib.rs
index cfa0a2c0..142d44fe 100644
--- a/savings_goals/src/lib.rs
+++ b/savings_goals/src/lib.rs
@@ -87,8 +87,9 @@ pub struct SavingsSchedule {
pub missed_count: u32,
}
-#[contracttype]
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[contracterror]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
+#[repr(u32)]
pub enum SavingsGoalsError {
InvalidAmount = 1,
GoalNotFound = 2,
@@ -98,48 +99,7 @@ pub enum SavingsGoalsError {
Overflow = 6,
}
-impl From for soroban_sdk::Error {
- fn from(err: SavingsGoalsError) -> Self {
- match err {
- SavingsGoalsError::InvalidAmount => soroban_sdk::Error::from((
- soroban_sdk::xdr::ScErrorType::Contract,
- soroban_sdk::xdr::ScErrorCode::InvalidInput,
- )),
- SavingsGoalsError::GoalNotFound => soroban_sdk::Error::from((
- soroban_sdk::xdr::ScErrorType::Contract,
- soroban_sdk::xdr::ScErrorCode::MissingValue,
- )),
- SavingsGoalsError::Unauthorized => soroban_sdk::Error::from((
- soroban_sdk::xdr::ScErrorType::Contract,
- soroban_sdk::xdr::ScErrorCode::InvalidAction,
- )),
- SavingsGoalsError::GoalLocked => soroban_sdk::Error::from((
- soroban_sdk::xdr::ScErrorType::Contract,
- soroban_sdk::xdr::ScErrorCode::InvalidAction,
- )),
- SavingsGoalsError::InsufficientBalance => soroban_sdk::Error::from((
- soroban_sdk::xdr::ScErrorType::Contract,
- soroban_sdk::xdr::ScErrorCode::InvalidInput,
- )),
- SavingsGoalsError::Overflow => soroban_sdk::Error::from((
- soroban_sdk::xdr::ScErrorType::Contract,
- soroban_sdk::xdr::ScErrorCode::InvalidInput,
- )),
- }
- }
-}
-
-impl From<&SavingsGoalsError> for soroban_sdk::Error {
- fn from(err: &SavingsGoalsError) -> Self {
- (*err).into()
- }
-}
-
-impl From for SavingsGoalsError {
- fn from(_err: soroban_sdk::Error) -> Self {
- SavingsGoalsError::Unauthorized
- }
-}
+// Conversions provided by #[contracterror]
#[contracttype]
#[derive(Clone)]
@@ -157,19 +117,10 @@ pub enum SavingsEvent {
ScheduleCancelled,
}
-/// Snapshot for savings goals export/import (migration).
-///
-/// # Schema Version Tag
-/// `schema_version` carries the explicit snapshot format version.
-/// Importers **must** validate this field against the supported range
-/// (`MIN_SUPPORTED_SCHEMA_VERSION..=SCHEMA_VERSION`) before applying the
-/// snapshot. Snapshots with an unknown future version must be rejected.
#[contracttype]
#[derive(Clone)]
pub struct GoalsExportSnapshot {
- /// Explicit schema version tag for this snapshot format.
- /// Supported range: MIN_SUPPORTED_SCHEMA_VERSION..=SCHEMA_VERSION.
- pub schema_version: u32,
+ pub version: u32,
pub checksum: u64,
pub next_id: u32,
pub goals: Vec,
@@ -184,10 +135,7 @@ pub struct AuditEntry {
pub success: bool,
}
-/// Current snapshot schema version. Bump this when GoalsExportSnapshot format changes.
-const SCHEMA_VERSION: u32 = 1;
-/// Oldest snapshot schema version this contract can import. Enables backward compat.
-const MIN_SUPPORTED_SCHEMA_VERSION: u32 = 1;
+const SNAPSHOT_VERSION: u32 = 1;
const MAX_AUDIT_ENTRIES: u32 = 100;
const CONTRACT_VERSION: u32 = 1;
const MAX_BATCH_SIZE: u32 = 50;
@@ -217,11 +165,6 @@ pub enum SavingsGoalError {
GoalLocked = 3,
Unauthorized = 4,
TargetAmountMustBePositive = 5,
- /// Snapshot schema_version is outside the supported range
- /// (MIN_SUPPORTED_SCHEMA_VERSION..=SCHEMA_VERSION).
- UnsupportedVersion = 6,
- /// Snapshot checksum does not match the recomputed digest.
- ChecksumMismatch = 7,
}
#[contract]
pub struct SavingsGoalContract;
@@ -313,7 +256,10 @@ impl SavingsGoalContract {
pub fn pause(env: Env, caller: Address) {
caller.require_auth();
- let admin = Self::get_pause_admin(&env).unwrap();
+ let admin = match Self::get_pause_admin(&env) {
+ Some(a) => a,
+ None => panic!("Unauthorized"),
+ };
if admin != caller {
panic!("Unauthorized");
}
@@ -326,7 +272,10 @@ impl SavingsGoalContract {
pub fn unpause(env: Env, caller: Address) {
caller.require_auth();
- let admin = Self::get_pause_admin(&env).unwrap();
+ let admin = match Self::get_pause_admin(&env) {
+ Some(a) => a,
+ None => panic!("Unauthorized"),
+ };
if admin != caller {
panic!("Unauthorized");
}
@@ -346,7 +295,10 @@ impl SavingsGoalContract {
pub fn pause_function(env: Env, caller: Address, func: Symbol) {
caller.require_auth();
- let admin = Self::get_pause_admin(&env).unwrap();
+ let admin = match Self::get_pause_admin(&env) {
+ Some(a) => a,
+ None => panic!("Unauthorized"),
+ };
if admin != caller {
panic!("Unauthorized");
}
@@ -363,7 +315,10 @@ impl SavingsGoalContract {
pub fn unpause_function(env: Env, caller: Address, func: Symbol) {
caller.require_auth();
- let admin = Self::get_pause_admin(&env).unwrap();
+ let admin = match Self::get_pause_admin(&env) {
+ Some(a) => a,
+ None => panic!("Unauthorized"),
+ };
if admin != caller {
panic!("Unauthorized");
}
@@ -393,60 +348,21 @@ impl SavingsGoalContract {
env.storage().instance().get(&symbol_short!("UPG_ADM"))
}
- /// Set or transfer the upgrade admin role.
- ///
- /// # Security Requirements
- /// - If no upgrade admin exists, caller must equal new_admin (bootstrap pattern)
- /// - If upgrade admin exists, only current upgrade admin can transfer
- /// - Caller must be authenticated via require_auth()
- ///
- /// # Parameters
- /// - `caller`: The address attempting to set the upgrade admin
- /// - `new_admin`: The address to become the new upgrade admin
- ///
- /// # Panics
- /// - If caller is unauthorized for the operation
pub fn set_upgrade_admin(env: Env, caller: Address, new_admin: Address) {
caller.require_auth();
-
- let current_upgrade_admin = Self::get_upgrade_admin(&env);
-
- // Authorization logic:
- // 1. If no upgrade admin exists, caller must equal new_admin (bootstrap)
- // 2. If upgrade admin exists, only current upgrade admin can transfer
- match current_upgrade_admin {
+ let current = Self::get_upgrade_admin(&env);
+ match current {
None => {
- // Bootstrap pattern - caller must be setting themselves as admin
if caller != new_admin {
- panic!("Unauthorized: bootstrap requires caller == new_admin");
- }
- }
- Some(current_admin) => {
- // Admin transfer - only current admin can transfer
- if current_admin != caller {
- panic!("Unauthorized: only current upgrade admin can transfer");
+ panic!("Unauthorized");
}
}
+ Some(adm) if adm != caller => panic!("Unauthorized"),
+ _ => {}
}
-
env.storage()
.instance()
.set(&symbol_short!("UPG_ADM"), &new_admin);
-
- // Emit admin transfer event for audit trail
- env.events().publish(
- (symbol_short!("savings"), symbol_short!("adm_xfr")),
- (current_upgrade_admin, new_admin.clone()),
- );
- }
-
- /// Get the current upgrade admin address.
- ///
- /// # Returns
- /// - `Some(Address)` if upgrade admin is set
- /// - `None` if no upgrade admin has been configured
- pub fn get_upgrade_admin_public(env: Env) -> Option {
- Self::get_upgrade_admin(&env)
}
pub fn set_version(env: Env, caller: Address, new_version: u32) {
@@ -477,18 +393,13 @@ impl SavingsGoalContract {
panic!("Tags cannot be empty");
}
for tag in tags.iter() {
- if tag.len() == 0 || tag.len() > 32 {
+ if tag.is_empty() || tag.len() > 32 {
panic!("Tag must be between 1 and 32 characters");
}
}
}
- 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);
@@ -499,7 +410,10 @@ impl SavingsGoalContract {
.get(&symbol_short!("GOALS"))
.unwrap_or_else(|| Map::new(&env));
- let mut goal = goals.get(goal_id).expect("Goal not found");
+ let mut goal = match goals.get(goal_id) {
+ Some(g) => g,
+ None => panic!("Goal not found"),
+ };
if goal.owner != caller {
Self::append_audit(&env, symbol_short!("add_tags"), &caller, false);
@@ -523,12 +437,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);
@@ -539,7 +448,10 @@ impl SavingsGoalContract {
.get(&symbol_short!("GOALS"))
.unwrap_or_else(|| Map::new(&env));
- let mut goal = goals.get(goal_id).expect("Goal not found");
+ let mut goal = match goals.get(goal_id) {
+ Some(g) => g,
+ None => panic!("Goal not found"),
+ };
if goal.owner != caller {
Self::append_audit(&env, symbol_short!("rem_tags"), &caller, false);
@@ -586,10 +498,6 @@ impl SavingsGoalContract {
/// supports backfill or migration use cases where historical goals are
/// recorded after the fact. Callers that need strictly future-dated
/// goals should validate this before invoking the contract.
- ///
- /// # Events
- /// - Emits `GOAL_CREATED` with goal details.
- /// - Emits `SavingsEvent::GoalCreated`.
pub fn create_goal(
env: Env,
owner: Address,
@@ -800,12 +708,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;
@@ -868,29 +774,6 @@ impl SavingsGoalContract {
///
/// # Panics
/// * If `caller` does not authorize the transaction
- /// Withdraws funds from an existing savings goal.
- ///
- /// # Arguments
- /// * `caller` - Address of the goal owner (must authorize)
- /// * `goal_id` - ID of the goal to withdraw from
- /// * `amount` - Amount to withdraw in stroops (must be > 0)
- ///
- /// # Returns
- /// `Ok(remaining_amount)` - The remaining amount in the goal after withdrawal
- ///
- /// # Errors
- /// * `InvalidAmount` - If amount ≤ 0
- /// * `GoalNotFound` - If goal_id does not exist
- /// * `Unauthorized` - If caller is not the goal owner
- /// * `InsufficientBalance` - If amount > current_amount
- /// * `GoalLocked` - If the goal is locked or time-lock has not expired
- ///
- /// # Time-lock Behavior
- /// - If `unlock_date` is set, withdrawal will fail if `env.ledger().timestamp() < unlock_date`.
- /// - Boundary condition: Success if `timestamp == unlock_date`.
- ///
- /// # Events
- /// - Emits `SavingsEvent::FundsWithdrawn`.
pub fn withdraw_from_goal(
env: Env,
caller: Address,
@@ -964,14 +847,6 @@ impl SavingsGoalContract {
Ok(new_amount)
}
- /// Locks a goal to prevent manual withdrawals.
- ///
- /// # Arguments
- /// * `caller` - Address of the goal owner
- /// * `goal_id` - ID of the goal
- ///
- /// # Events
- /// - Emits `SavingsEvent::GoalLocked`.
pub fn lock_goal(env: Env, caller: Address, goal_id: u32) -> bool {
caller.require_auth();
Self::require_not_paused(&env, pause_functions::LOCK);
@@ -1011,14 +886,6 @@ impl SavingsGoalContract {
true
}
- /// Unlocks a goal for manual withdrawals.
- ///
- /// # Arguments
- /// * `caller` - Address of the goal owner
- /// * `goal_id` - ID of the goal
- ///
- /// # Events
- /// - Emits `SavingsEvent::GoalUnlocked`.
pub fn unlock_goal(env: Env, caller: Address, goal_id: u32) -> bool {
caller.require_auth();
Self::require_not_paused(&env, pause_functions::UNLOCK);
@@ -1071,16 +938,12 @@ impl SavingsGoalContract {
// PAGINATED LIST QUERIES
// -----------------------------------------------------------------------
- /// @notice Returns a deterministic page of goals for one owner.
- /// @dev Paging order is anchored to the owner-goal ID index (append-only,
- /// ascending by creation ID), not map iteration order.
- /// @dev `cursor` is exclusive and must match an existing goal ID in the
- /// owner's index when non-zero; invalid cursors are rejected.
+ /// Get a page of savings goals for `owner`.
///
/// # Arguments
- /// * `owner` - whose goals to return
- /// * `cursor` - start after this goal ID (pass 0 for the first page)
- /// * `limit` - max items per page (0 -> DEFAULT_PAGE_LIMIT, capped at MAX_PAGE_LIMIT)
+ /// * `owner` – whose goals to return
+ /// * `cursor` – start after this goal ID (pass 0 for the first page)
+ /// * `limit` – max items per page (0 → DEFAULT_PAGE_LIMIT, capped at MAX_PAGE_LIMIT)
///
/// # Returns
/// `GoalPage { items, next_cursor, count }`.
@@ -1093,66 +956,35 @@ impl SavingsGoalContract {
.get(&symbol_short!("GOALS"))
.unwrap_or_else(|| Map::new(&env));
- let owner_goal_ids: Map> = env
- .storage()
- .instance()
- .get(&Self::STORAGE_OWNER_GOAL_IDS)
- .unwrap_or_else(|| Map::new(&env));
- let ids = owner_goal_ids
- .get(owner.clone())
- .unwrap_or_else(|| Vec::new(&env));
-
- if ids.is_empty() {
- return GoalPage {
- items: Vec::new(&env),
- next_cursor: 0,
- count: 0,
- };
- }
+ let mut result = Vec::new(&env);
+ let mut next_cursor: u32 = 0;
+ let mut collected: u32 = 0;
- let mut start_index: u32 = 0;
- if cursor != 0 {
- let mut found = false;
- for i in 0..ids.len() {
- if ids.get(i) == Some(cursor) {
- start_index = i + 1;
- found = true;
- break;
- }
- }
- if !found {
- panic!("Invalid cursor");
+ for (id, goal) in goals.iter() {
+ if id <= cursor {
+ continue;
}
- }
-
- let mut end_index = start_index + limit;
- if end_index > ids.len() {
- end_index = ids.len();
- }
-
- 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 = goals
- .get(goal_id)
- .unwrap_or_else(|| panic!("Pagination index out of sync"));
if goal.owner != owner {
- panic!("Pagination index owner mismatch");
+ continue;
+ }
+ if collected < limit {
+ result.push_back(goal);
+ collected += 1;
+ next_cursor = id; // track last returned ID
+ } else {
+ break;
}
- result.push_back(goal);
}
- let next_cursor = if end_index < ids.len() {
- ids.get(end_index - 1)
- .unwrap_or_else(|| panic!("Pagination index out of sync"))
- } else {
- 0
- };
+ // If we didn't fill the page, there are no more items
+ if collected < limit {
+ next_cursor = 0;
+ }
GoalPage {
items: result,
next_cursor,
- count: end_index - start_index,
+ count: collected,
}
}
@@ -1216,13 +1048,9 @@ impl SavingsGoalContract {
list.push_back(g);
}
}
- let checksum = Self::compute_goals_checksum(SCHEMA_VERSION, next_id, &list);
- env.events().publish(
- (symbol_short!("goals"), symbol_short!("snap_exp")),
- SCHEMA_VERSION,
- );
+ let checksum = Self::compute_goals_checksum(SNAPSHOT_VERSION, next_id, &list);
GoalsExportSnapshot {
- schema_version: SCHEMA_VERSION,
+ version: SNAPSHOT_VERSION,
checksum,
next_id,
goals: list,
@@ -1234,25 +1062,19 @@ impl SavingsGoalContract {
caller: Address,
nonce: u64,
snapshot: GoalsExportSnapshot,
- ) -> Result {
+ ) -> bool {
caller.require_auth();
Self::require_nonce(&env, &caller, nonce);
- // Accept any schema_version within the supported range for backward/forward compat.
- if snapshot.schema_version < MIN_SUPPORTED_SCHEMA_VERSION
- || snapshot.schema_version > SCHEMA_VERSION
- {
+ if snapshot.version != SNAPSHOT_VERSION {
Self::append_audit(&env, symbol_short!("import"), &caller, false);
- return Err(SavingsGoalError::UnsupportedVersion);
+ panic!("Unsupported snapshot version");
}
- let expected = Self::compute_goals_checksum(
- snapshot.schema_version,
- snapshot.next_id,
- &snapshot.goals,
- );
+ let expected =
+ Self::compute_goals_checksum(snapshot.version, snapshot.next_id, &snapshot.goals);
if snapshot.checksum != expected {
Self::append_audit(&env, symbol_short!("import"), &caller, false);
- return Err(SavingsGoalError::ChecksumMismatch);
+ panic!("Snapshot checksum mismatch");
}
Self::extend_instance_ttl(&env);
@@ -1278,7 +1100,7 @@ impl SavingsGoalContract {
Self::increment_nonce(&env, &caller);
Self::append_audit(&env, symbol_short!("import"), &caller, true);
- Ok(true)
+ true
}
pub fn get_audit_log(env: Env, from_index: u32, limit: u32) -> Vec {
@@ -1390,15 +1212,6 @@ impl SavingsGoalContract {
}
/// Set time-lock on a goal
- /// Sets a time-lock on a savings goal.
- ///
- /// # Arguments
- /// * `caller` - Address of the goal owner
- /// * `goal_id` - ID of the goal
- /// * `unlock_date` - Unix timestamp when the goal becomes withdrawable
- ///
- /// # Panics
- /// - If caller is not the owner or goal not found.
pub fn set_time_lock(env: Env, caller: Address, goal_id: u32, unlock_date: u64) -> bool {
caller.require_auth();
Self::extend_instance_ttl(&env);
@@ -1438,17 +1251,6 @@ impl SavingsGoalContract {
true
}
- /// Creates a recurring savings schedule.
- ///
- /// # Arguments
- /// * `owner` - Address of the schedule owner
- /// * `goal_id` - ID of the goal to fund
- /// * `amount` - Amount to save in each interval
- /// * `next_due` - First execution timestamp
- /// * `interval` - Seconds between executions
- ///
- /// # Returns
- /// - ID of the new schedule
pub fn create_savings_schedule(
env: Env,
owner: Address,
@@ -1555,7 +1357,10 @@ impl SavingsGoalContract {
.get(&symbol_short!("SAV_SCH"))
.unwrap_or_else(|| Map::new(&env));
- let mut schedule = schedules.get(schedule_id).expect("Schedule not found");
+ let mut schedule = match schedules.get(schedule_id) {
+ Some(s) => s,
+ None => panic!("Schedule not found"),
+ };
if schedule.owner != caller {
panic!("Only the schedule owner can modify it");
@@ -1590,7 +1395,10 @@ impl SavingsGoalContract {
.get(&symbol_short!("SAV_SCH"))
.unwrap_or_else(|| Map::new(&env));
- let mut schedule = schedules.get(schedule_id).expect("Schedule not found");
+ let mut schedule = match schedules.get(schedule_id) {
+ Some(s) => s,
+ None => panic!("Schedule not found"),
+ };
if schedule.owner != caller {
panic!("Only the schedule owner can cancel it");
@@ -1611,26 +1419,6 @@ impl SavingsGoalContract {
true
}
- /// Executes all active and due savings schedules.
- ///
- /// # Drift Handling
- /// - If execution is delayed, the schedule will "catch up" by skipping missed intervals
- /// and incrementing `missed_count`.
- /// - `next_due` is set to the next future interval anchor.
- ///
- /// # Events
- /// - Emits `SavingsEvent::ScheduleExecuted` for each successful execution.
- /// - Emits `SavingsEvent::ScheduleMissed` for each interval missed.
- /// Executes all active and due savings schedules.
- ///
- /// # Drift Handling
- /// - If execution is delayed, the schedule will "catch up" by skipping missed intervals
- /// and incrementing `missed_count`.
- /// - `next_due` is set to the next future interval anchor.
- ///
- /// # Events
- /// - Emits `SavingsEvent::ScheduleExecuted` for each successful execution.
- /// - Emits `SavingsEvent::ScheduleMissed` for each interval missed.
pub fn execute_due_savings_schedules(env: Env) -> Vec {
Self::extend_instance_ttl(&env);
@@ -1655,12 +1443,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 b1ddd68e..6c3177d8 100644
--- a/savings_goals/src/test.rs
+++ b/savings_goals/src/test.rs
@@ -1,5 +1,3 @@
-#![cfg(test)]
-
use super::*;
use soroban_sdk::testutils::storage::Instance as _;
use soroban_sdk::{
@@ -7,7 +5,7 @@ use soroban_sdk::{
Address, Env, IntoVal, String, Symbol, TryFromVal,
};
-use testutils::{set_ledger_time, setup_test_env};
+use testutils::set_ledger_time;
// Removed local set_time in favor of testutils::set_ledger_time
@@ -99,7 +97,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");
@@ -148,11 +150,14 @@ fn test_next_id_increments_sequentially() {
assert_eq!(ids[1], 2, "second goal id must be 2");
assert_eq!(ids[2], 3, "third goal id must be 3");
- let expected_names = ["G1", "G2", "G3"];
for (i, &id) in ids.iter().enumerate() {
let goal = client.get_goal(&id).unwrap();
assert_eq!(goal.id, id);
- let expected_name = String::from_str(&env, expected_names[i]);
+ let expected_name = match i {
+ 0 => String::from_str(&env, "G1"),
+ 1 => String::from_str(&env, "G2"),
+ _ => String::from_str(&env, "G3"),
+ };
assert_eq!(goal.name, expected_name);
}
}
@@ -409,6 +414,7 @@ fn test_withdraw_from_goal_unauthorized() {
}
#[test]
+#[should_panic(expected = "HostError: Error(Contract, #1)")]
fn test_withdraw_from_goal_zero_amount_panics() {
let env = Env::default();
let contract_id = env.register_contract(None, SavingsGoalContract);
@@ -421,11 +427,11 @@ fn test_withdraw_from_goal_zero_amount_panics() {
client.unlock_goal(&user, &id);
client.add_to_goal(&user, &id, &500);
- let result = client.try_withdraw_from_goal(&user, &id, &0);
- assert!(result.is_err(), "Expected error for zero amount withdrawal");
+ client.withdraw_from_goal(&user, &id, &0);
}
#[test]
+#[should_panic(expected = "HostError: Error(Contract, #2)")]
fn test_withdraw_from_goal_nonexistent_goal_panics() {
let env = Env::default();
let contract_id = env.register_contract(None, SavingsGoalContract);
@@ -434,8 +440,7 @@ fn test_withdraw_from_goal_nonexistent_goal_panics() {
client.init();
env.mock_all_auths();
- let result = client.try_withdraw_from_goal(&user, &999, &100);
- assert!(result.is_err(), "Expected error for nonexistent goal withdrawal");
+ client.withdraw_from_goal(&user, &999, &100);
}
#[test]
@@ -1439,14 +1444,6 @@ fn setup_goals(env: &Env, client: &SavingsGoalContractClient, owner: &Address, c
}
}
-fn page_goal_ids(env: &Env, page: &GoalPage) -> soroban_sdk::Vec {
- let mut ids = soroban_sdk::Vec::new(env);
- for goal in page.items.iter() {
- ids.push_back(goal.id);
- }
- ids
-}
-
#[test]
fn test_get_goals_empty() {
let env = Env::default();
@@ -1550,74 +1547,6 @@ fn test_get_goals_cursor_is_exclusive() {
}
}
-#[test]
-fn test_get_goals_rejects_invalid_cursor() {
- let env = Env::default();
- env.mock_all_auths();
- let id = env.register_contract(None, SavingsGoalContract);
- let client = SavingsGoalContractClient::new(&env, &id);
- let owner = Address::generate(&env);
-
- client.init();
- setup_goals(&env, &client, &owner, 4);
-
- let res = client.try_get_goals(&owner, &999_999, &2);
- assert!(res.is_err(), "non-zero cursor must exist for this owner");
-}
-
-#[test]
-fn test_get_goals_rejects_cursor_from_another_owner() {
- let env = Env::default();
- env.mock_all_auths();
- let id = env.register_contract(None, SavingsGoalContract);
- let client = SavingsGoalContractClient::new(&env, &id);
- let owner_a = Address::generate(&env);
- let owner_b = Address::generate(&env);
-
- client.init();
- setup_goals(&env, &client, &owner_a, 3);
- setup_goals(&env, &client, &owner_b, 2);
-
- let owner_b_first_page = client.get_goals(&owner_b, &0, &1);
- let foreign_cursor = owner_b_first_page.items.get(0).unwrap().id;
- let res = client.try_get_goals(&owner_a, &foreign_cursor, &2);
- assert!(res.is_err(), "cursor must be bound to the requested owner");
-}
-
-#[test]
-fn test_get_goals_no_duplicate_or_skip_when_new_goals_added_between_pages() {
- let env = Env::default();
- env.mock_all_auths();
- let id = env.register_contract(None, SavingsGoalContract);
- let client = SavingsGoalContractClient::new(&env, &id);
- let owner = Address::generate(&env);
-
- client.init();
- setup_goals(&env, &client, &owner, 6);
-
- let page1 = client.get_goals(&owner, &0, &3);
- let page1_ids = page_goal_ids(&env, &page1);
- assert_eq!(page1_ids.get(0), Some(1));
- assert_eq!(page1_ids.get(1), Some(2));
- assert_eq!(page1_ids.get(2), Some(3));
-
- // Simulate concurrent writes between paged reads.
- setup_goals(&env, &client, &owner, 2);
-
- let page2 = client.get_goals(&owner, &page1.next_cursor, &3);
- let page2_ids = page_goal_ids(&env, &page2);
- assert_eq!(page2_ids.get(0), Some(4));
- assert_eq!(page2_ids.get(1), Some(5));
- assert_eq!(page2_ids.get(2), Some(6));
-
- let page3 = client.get_goals(&owner, &page2.next_cursor, &3);
- let page3_ids = page_goal_ids(&env, &page3);
- assert_eq!(page3_ids.get(0), Some(7));
- assert_eq!(page3_ids.get(1), Some(8));
- assert_eq!(page3.count, 2);
- assert_eq!(page3.next_cursor, 0);
-}
-
#[test]
fn test_limit_zero_uses_default() {
let env = Env::default();
@@ -1827,9 +1756,22 @@ fn test_get_all_goals_filters_by_owner() {
}
// Verify goal IDs for owner_a are correct
- assert!(goals_a.iter().any(|g| g.id == goal_a1), "Goals for A should contain goal_a1");
- assert!(goals_a.iter().any(|g| g.id == goal_a2), "Goals for A should contain goal_a2");
- assert!(goals_a.iter().any(|g| g.id == goal_a3), "Goals for A should contain goal_a3");
+ let mut goal_a_ids: Vec = Vec::new(&env);
+ for g in goals_a.iter() {
+ goal_a_ids.push_back(g.id);
+ }
+ 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);
@@ -1845,301 +1787,35 @@ fn test_get_all_goals_filters_by_owner() {
}
// Verify goal IDs for owner_b are correct
- assert!(goals_b.iter().any(|g| g.id == goal_b1), "Goals for B should contain goal_b1");
- assert!(goals_b.iter().any(|g| g.id == goal_b2), "Goals for B should contain goal_b2");
+ let mut goal_b_ids: Vec = Vec::new(&env);
+ for g in goals_b.iter() {
+ goal_b_ids.push_back(g.id);
+ }
+ 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 in goals_a.iter() {
+ for goal_a_id in &goal_a_ids {
assert!(
- !goals_b.iter().any(|gb| gb.id == goal_a.id),
- "Goal ID from owner A should not appear in owner B's goals"
+ !goal_b_ids.contains(goal_a_id),
+ "Goal ID {} from owner A should not appear in owner B's goals",
+ goal_a_id
);
}
-}
-
-// ============================================================================
-// Snapshot schema version tests
-//
-// These tests verify that:
-// 1. export_snapshot embeds the correct schema_version tag.
-// 2. import_snapshot accepts schema_version within the supported range.
-// 3. import_snapshot rejects a future (too-new) schema version.
-// 4. import_snapshot rejects a past (too-old, below minimum) schema version.
-// 5. import_snapshot rejects a tampered checksum regardless of version.
-// 6. Full round-trip: exported data is faithfully restored after import.
-// ============================================================================
-
-/// export_snapshot must embed schema_version == SCHEMA_VERSION (currently 1).
-#[test]
-fn test_export_snapshot_contains_correct_schema_version() {
- let env = Env::default();
- env.mock_all_auths();
- let contract_id = env.register_contract(None, SavingsGoalContract);
- let client = SavingsGoalContractClient::new(&env, &contract_id);
- let owner = Address::generate(&env);
-
- client.init();
- let _id = client.create_goal(&owner, &String::from_str(&env, "House"), &10000, &2000000000);
-
- let snapshot = client.export_snapshot(&owner);
- assert_eq!(
- snapshot.schema_version, 1,
- "schema_version must equal SCHEMA_VERSION (1)"
- );
-}
-
-/// import_snapshot with the current schema version (1) must succeed.
-#[test]
-fn test_import_snapshot_current_schema_version_succeeds() {
- let env = Env::default();
- env.mock_all_auths();
- let contract_id = env.register_contract(None, SavingsGoalContract);
- let client = SavingsGoalContractClient::new(&env, &contract_id);
- let owner = Address::generate(&env);
-
- client.init();
- client.create_goal(&owner, &String::from_str(&env, "Car"), &5000, &2000000000);
-
- let snapshot = client.export_snapshot(&owner);
- assert_eq!(snapshot.schema_version, 1);
-
- let ok = client.import_snapshot(&owner, &0, &snapshot);
- assert!(ok, "import with current schema version must succeed");
-}
-
-/// import_snapshot with schema_version higher than SCHEMA_VERSION must return
-/// UnsupportedVersion (forward-compat rejection).
-#[test]
-fn test_import_snapshot_future_schema_version_rejected() {
- let env = Env::default();
- env.mock_all_auths();
- let contract_id = env.register_contract(None, SavingsGoalContract);
- let client = SavingsGoalContractClient::new(&env, &contract_id);
- let owner = Address::generate(&env);
-
- client.init();
- client.create_goal(&owner, &String::from_str(&env, "Trip"), &3000, &2000000000);
-
- let mut snapshot = client.export_snapshot(&owner);
- // Simulate a snapshot produced by a newer contract version.
- snapshot.schema_version = 999;
-
- let result = client.try_import_snapshot(&owner, &0, &snapshot);
- assert_eq!(
- result,
- Err(Ok(SavingsGoalError::UnsupportedVersion)),
- "future schema_version must be rejected"
- );
-}
-
-/// import_snapshot with schema_version = 0 (below MIN_SUPPORTED_SCHEMA_VERSION)
-/// must return UnsupportedVersion (backward-compat rejection).
-#[test]
-fn test_import_snapshot_too_old_schema_version_rejected() {
- let env = Env::default();
- env.mock_all_auths();
- let contract_id = env.register_contract(None, SavingsGoalContract);
- let client = SavingsGoalContractClient::new(&env, &contract_id);
- let owner = Address::generate(&env);
-
- client.init();
- client.create_goal(&owner, &String::from_str(&env, "Education"), &8000, &2000000000);
-
- let mut snapshot = client.export_snapshot(&owner);
- // Simulate a snapshot too old to be safely imported.
- snapshot.schema_version = 0;
-
- let result = client.try_import_snapshot(&owner, &0, &snapshot);
- assert_eq!(
- result,
- Err(Ok(SavingsGoalError::UnsupportedVersion)),
- "schema_version below minimum must be rejected"
- );
-}
-
-/// import_snapshot with a tampered checksum must return ChecksumMismatch even
-/// when the schema_version is valid.
-#[test]
-fn test_import_snapshot_tampered_checksum_rejected() {
- let env = Env::default();
- env.mock_all_auths();
- let contract_id = env.register_contract(None, SavingsGoalContract);
- let client = SavingsGoalContractClient::new(&env, &contract_id);
- let owner = Address::generate(&env);
-
- client.init();
- client.create_goal(&owner, &String::from_str(&env, "Savings"), &2000, &2000000000);
-
- let mut snapshot = client.export_snapshot(&owner);
- snapshot.checksum = snapshot.checksum.wrapping_add(1);
-
- let result = client.try_import_snapshot(&owner, &0, &snapshot);
- assert_eq!(
- result,
- Err(Ok(SavingsGoalError::ChecksumMismatch)),
- "tampered checksum must be rejected"
- );
-}
-
-/// Full export → import round-trip: goal data is faithfully restored.
-#[test]
-fn test_snapshot_export_import_roundtrip_restores_goals() {
- let env = Env::default();
- env.mock_all_auths();
- let contract_id = env.register_contract(None, SavingsGoalContract);
- let client = SavingsGoalContractClient::new(&env, &contract_id);
- let owner = Address::generate(&env);
-
- client.init();
- let id1 = client.create_goal(&owner, &String::from_str(&env, "Fund A"), &5000, &2000000000);
- let id2 = client.create_goal(&owner, &String::from_str(&env, "Fund B"), &8000, &2000000000);
- client.add_to_goal(&owner, &id1, &1500);
-
- let snapshot = client.export_snapshot(&owner);
- assert_eq!(snapshot.schema_version, 1);
- assert_eq!(snapshot.goals.len(), 2);
-
- let ok = client.import_snapshot(&owner, &0, &snapshot);
- assert!(ok, "round-trip import must succeed");
-
- let restored1 = client.get_goal(&id1).expect("goal 1 must survive import");
- assert_eq!(restored1.target_amount, 5000);
- assert_eq!(restored1.current_amount, 1500);
-
- let restored2 = client.get_goal(&id2).expect("goal 2 must survive import");
- assert_eq!(restored2.target_amount, 8000);
-}
-
-/// schema_version boundary: version exactly at MIN_SUPPORTED_SCHEMA_VERSION (1)
-/// must be accepted.
-#[test]
-fn test_import_snapshot_min_supported_version_accepted() {
- let env = Env::default();
- env.mock_all_auths();
- let contract_id = env.register_contract(None, SavingsGoalContract);
- let client = SavingsGoalContractClient::new(&env, &contract_id);
- let owner = Address::generate(&env);
-
- client.init();
- client.create_goal(&owner, &String::from_str(&env, "Min Version"), &1000, &2000000000);
- let snapshot = client.export_snapshot(&owner);
- // schema_version is already 1 == MIN_SUPPORTED_SCHEMA_VERSION.
- assert_eq!(snapshot.schema_version, 1);
-
- let ok = client.import_snapshot(&owner, &0, &snapshot);
- assert!(ok, "snapshot at MIN_SUPPORTED_SCHEMA_VERSION must be accepted");
-}
-
-#[test]
-fn test_withdraw_time_lock_boundaries() {
- let env = Env::default();
- let contract_id = env.register_contract(None, SavingsGoalContract);
- let client = SavingsGoalContractClient::new(&env, &contract_id);
- let owner = ::generate(&env);
-
- env.mock_all_auths();
- client.init();
-
- let base_time = 1000;
- set_ledger_time(&env, 1, base_time);
-
- let unlock_date = 5000;
- let goal_id = client.create_goal(&owner, &String::from_str(&env, "Time Lock Boundary"), &10000, &unlock_date);
-
- client.add_to_goal(&owner, &goal_id, &5000);
- client.unlock_goal(&owner, &goal_id);
- client.set_time_lock(&owner, &goal_id, &unlock_date);
-
- // 1. Test withdrawal at unlock_date - 1 (should fail)
- set_ledger_time(&env, 1, unlock_date - 1);
- let result = client.try_withdraw_from_goal(&owner, &goal_id, &1000);
- assert!(result.is_err(), "Withdrawal should fail before unlock_date");
-
- // 2. Test withdrawal at unlock_date (should succeed)
- set_ledger_time(&env, 1, unlock_date);
- let new_amount = client.withdraw_from_goal(&owner, &goal_id, &1000);
- assert_eq!(new_amount, 4000, "Withdrawal should succeed exactly at unlock_date");
-
- // 3. Test withdrawal at unlock_date + 1 (should succeed)
- set_ledger_time(&env, 1, unlock_date + 1);
- let final_amount = client.withdraw_from_goal(&owner, &goal_id, &1000);
- assert_eq!(final_amount, 3000, "Withdrawal should succeed after unlock_date");
-}
-
-#[test]
-fn test_savings_schedule_drift_and_missed_intervals() {
- let env = Env::default();
- let contract_id = env.register_contract(None, SavingsGoalContract);
- let client = SavingsGoalContractClient::new(&env, &contract_id);
- let owner = ::generate(&env);
-
- env.mock_all_auths();
- client.init();
-
- let base_time = 1000;
- set_ledger_time(&env, 1, base_time);
-
- let goal_id = client.create_goal(&owner, &String::from_str(&env, "Schedule Drift"), &10000, &5000);
-
- let amount = 500;
- let next_due = 3000;
- let interval = 86400; // 1 day
- let schedule_id = client.create_savings_schedule(&owner, &goal_id, &amount, &next_due, &interval);
-
- // 1. Advance time past next_due + interval * 2 + 100 (simulating significant drift/delay)
- // 3000 + 172800 + 100 = 175900
- let current_time = next_due + interval * 2 + 100;
- set_ledger_time(&env, 1, current_time);
-
- let executed_ids = client.execute_due_savings_schedules();
- assert_eq!(executed_ids.len(), 1);
- assert_eq!(executed_ids.get(0).unwrap(), schedule_id);
-
- let schedule = client.get_savings_schedule(&schedule_id).unwrap();
- // It should have executed once (for the first due date) and missed 2 subsequent ones
- assert_eq!(schedule.missed_count, 2, "Should have marked 2 intervals as missed");
-
- // next_due should be set to the next FUTURE interval relative to current_time
- // Original: 3000
- // +1: 89400
- // +2: 175800
- // +3: 262200 (This is the next future one after 175900)
- assert_eq!(schedule.next_due, 262200, "next_due should anchor to the next future interval");
-
- let goal = client.get_goal(&goal_id).unwrap();
- assert_eq!(goal.current_amount, amount, "Only one execution should have happened");
-}
-
-#[test]
-fn test_savings_schedule_exact_timestamp_execution() {
- let env = Env::default();
- let contract_id = env.register_contract(None, SavingsGoalContract);
- let client = SavingsGoalContractClient::new(&env, &contract_id);
- let owner = ::generate(&env);
-
- env.mock_all_auths();
- client.init();
-
- let base_time = 1000;
- set_ledger_time(&env, 1, base_time);
-
- let goal_id = client.create_goal(&owner, &String::from_str(&env, "Exact Schedule"), &10000, &5000);
-
- let next_due = 3000;
- let schedule_id = client.create_savings_schedule(&owner, &goal_id, &500, &next_due, &0); // non-recurring
-
- // 1. Test at next_due - 1 (should NOT execute)
- set_ledger_time(&env, 1, next_due - 1);
- let executed_ids = client.execute_due_savings_schedules();
- assert_eq!(executed_ids.len(), 0, "Schedule should not execute before next_due");
-
- // 2. Test at next_due (should execute)
- set_ledger_time(&env, 1, next_due);
- let executed_ids = client.execute_due_savings_schedules();
- assert_eq!(executed_ids.len(), 1, "Schedule should execute exactly at next_due");
- assert_eq!(executed_ids.get(0).unwrap(), schedule_id);
-
- let goal = client.get_goal(&goal_id).unwrap();
- assert_eq!(goal.current_amount, 500);
+ // Verify owner_a's goals do not appear in owner_b's goals and vice versa
+ for goal_a_id in goal_a_ids {
+ for goal in goals_b.iter() {
+ assert_ne!(
+ goal.id, goal_a_id,
+ "Owner B's goal list should not contain owner A's goals"
+ );
+ }
+ }
}
diff --git a/savings_goals/test_snapshots/test/test_add_to_non_existent_goal.1.json b/savings_goals/test_snapshots/test/test_add_to_non_existent_goal.1.json
index 77acc95b..d295768d 100644
--- a/savings_goals/test_snapshots/test/test_add_to_non_existent_goal.1.json
+++ b/savings_goals/test_snapshots/test/test_add_to_non_existent_goal.1.json
@@ -236,7 +236,7 @@
],
"data": {
"error": {
- "contract": 3
+ "contract": 2
}
}
}
@@ -257,7 +257,7 @@
},
{
"error": {
- "contract": 3
+ "contract": 2
}
}
],
@@ -282,7 +282,7 @@
},
{
"error": {
- "contract": 3
+ "contract": 2
}
}
],
diff --git a/savings_goals/test_snapshots/test/test_cancel_savings_schedule.1.json b/savings_goals/test_snapshots/test/test_cancel_savings_schedule.1.json
index 5a644444..94c55b30 100644
--- a/savings_goals/test_snapshots/test/test_cancel_savings_schedule.1.json
+++ b/savings_goals/test_snapshots/test/test_cancel_savings_schedule.1.json
@@ -101,7 +101,7 @@
"base_reserve": 10,
"min_persistent_entry_ttl": 1,
"min_temp_entry_ttl": 1,
- "max_entry_ttl": 100000,
+ "max_entry_ttl": 700000,
"ledger_entries": [
[
{
@@ -371,7 +371,7 @@
},
"ext": "v0"
},
- 100000
+ 518401
]
],
[
@@ -404,7 +404,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -437,7 +437,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -470,7 +470,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -491,7 +491,7 @@
},
"ext": "v0"
},
- 100000
+ 518401
]
]
]
diff --git a/savings_goals/test_snapshots/test/test_create_savings_schedule.1.json b/savings_goals/test_snapshots/test/test_create_savings_schedule.1.json
index 34f97851..552125d7 100644
--- a/savings_goals/test_snapshots/test/test_create_savings_schedule.1.json
+++ b/savings_goals/test_snapshots/test/test_create_savings_schedule.1.json
@@ -79,7 +79,7 @@
"base_reserve": 10,
"min_persistent_entry_ttl": 1,
"min_temp_entry_ttl": 1,
- "max_entry_ttl": 100000,
+ "max_entry_ttl": 700000,
"ledger_entries": [
[
{
@@ -349,7 +349,7 @@
},
"ext": "v0"
},
- 100000
+ 518401
]
],
[
@@ -382,7 +382,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -415,7 +415,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -436,7 +436,7 @@
},
"ext": "v0"
},
- 100000
+ 518401
]
]
]
diff --git a/savings_goals/test_snapshots/test/test_execute_due_savings_schedules.1.json b/savings_goals/test_snapshots/test/test_execute_due_savings_schedules.1.json
index a89c1fb7..da40b753 100644
--- a/savings_goals/test_snapshots/test/test_execute_due_savings_schedules.1.json
+++ b/savings_goals/test_snapshots/test/test_execute_due_savings_schedules.1.json
@@ -80,7 +80,7 @@
"base_reserve": 10,
"min_persistent_entry_ttl": 1,
"min_temp_entry_ttl": 1,
- "max_entry_ttl": 100000,
+ "max_entry_ttl": 700000,
"ledger_entries": [
[
{
@@ -352,7 +352,7 @@
},
"ext": "v0"
},
- 100000
+ 518401
]
],
[
@@ -385,7 +385,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -418,7 +418,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -439,7 +439,7 @@
},
"ext": "v0"
},
- 100000
+ 518401
]
]
]
diff --git a/savings_goals/test_snapshots/test/test_execute_missed_savings_schedules.1.json b/savings_goals/test_snapshots/test/test_execute_missed_savings_schedules.1.json
index caf06f4a..06522e18 100644
--- a/savings_goals/test_snapshots/test/test_execute_missed_savings_schedules.1.json
+++ b/savings_goals/test_snapshots/test/test_execute_missed_savings_schedules.1.json
@@ -80,7 +80,7 @@
"base_reserve": 10,
"min_persistent_entry_ttl": 1,
"min_temp_entry_ttl": 1,
- "max_entry_ttl": 100000,
+ "max_entry_ttl": 700000,
"ledger_entries": [
[
{
@@ -352,7 +352,7 @@
},
"ext": "v0"
},
- 100000
+ 518401
]
],
[
@@ -385,7 +385,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -418,7 +418,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -439,7 +439,7 @@
},
"ext": "v0"
},
- 100000
+ 518401
]
]
]
diff --git a/savings_goals/test_snapshots/test/test_execute_recurring_savings_schedule.1.json b/savings_goals/test_snapshots/test/test_execute_recurring_savings_schedule.1.json
index 43a53753..052c376e 100644
--- a/savings_goals/test_snapshots/test/test_execute_recurring_savings_schedule.1.json
+++ b/savings_goals/test_snapshots/test/test_execute_recurring_savings_schedule.1.json
@@ -81,7 +81,7 @@
"base_reserve": 10,
"min_persistent_entry_ttl": 1,
"min_temp_entry_ttl": 1,
- "max_entry_ttl": 100000,
+ "max_entry_ttl": 700000,
"ledger_entries": [
[
{
@@ -353,7 +353,7 @@
},
"ext": "v0"
},
- 100000
+ 518401
]
],
[
@@ -386,7 +386,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -419,7 +419,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -440,7 +440,7 @@
},
"ext": "v0"
},
- 100000
+ 518401
]
]
]
diff --git a/savings_goals/test_snapshots/test/test_modify_savings_schedule.1.json b/savings_goals/test_snapshots/test/test_modify_savings_schedule.1.json
index 51b36c4a..6410dbd9 100644
--- a/savings_goals/test_snapshots/test/test_modify_savings_schedule.1.json
+++ b/savings_goals/test_snapshots/test/test_modify_savings_schedule.1.json
@@ -113,7 +113,7 @@
"base_reserve": 10,
"min_persistent_entry_ttl": 1,
"min_temp_entry_ttl": 1,
- "max_entry_ttl": 100000,
+ "max_entry_ttl": 700000,
"ledger_entries": [
[
{
@@ -383,7 +383,7 @@
},
"ext": "v0"
},
- 100000
+ 518401
]
],
[
@@ -416,7 +416,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -449,7 +449,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -482,7 +482,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -503,7 +503,7 @@
},
"ext": "v0"
},
- 100000
+ 518401
]
]
]
diff --git a/savings_goals/test_snapshots/test/test_savings_schedule_goal_completion.1.json b/savings_goals/test_snapshots/test/test_savings_schedule_goal_completion.1.json
index 9aa67007..99dceab7 100644
--- a/savings_goals/test_snapshots/test/test_savings_schedule_goal_completion.1.json
+++ b/savings_goals/test_snapshots/test/test_savings_schedule_goal_completion.1.json
@@ -81,7 +81,7 @@
"base_reserve": 10,
"min_persistent_entry_ttl": 1,
"min_temp_entry_ttl": 1,
- "max_entry_ttl": 100000,
+ "max_entry_ttl": 700000,
"ledger_entries": [
[
{
@@ -353,7 +353,7 @@
},
"ext": "v0"
},
- 100000
+ 518401
]
],
[
@@ -386,7 +386,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -419,7 +419,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -440,7 +440,7 @@
},
"ext": "v0"
},
- 100000
+ 518401
]
]
]
diff --git a/savings_goals/test_snapshots/test/test_withdraw_after_lock_fails.1.json b/savings_goals/test_snapshots/test/test_withdraw_after_lock_fails.1.json
index 8ea4dd67..30ced9fb 100644
--- a/savings_goals/test_snapshots/test/test_withdraw_after_lock_fails.1.json
+++ b/savings_goals/test_snapshots/test/test_withdraw_after_lock_fails.1.json
@@ -1219,7 +1219,7 @@
],
"data": {
"error": {
- "contract": 6
+ "contract": 4
}
}
}
@@ -1240,7 +1240,7 @@
},
{
"error": {
- "contract": 6
+ "contract": 4
}
}
],
@@ -1265,7 +1265,7 @@
},
{
"error": {
- "contract": 6
+ "contract": 4
}
}
],
diff --git a/savings_goals/test_snapshots/test/test_withdraw_from_goal_insufficient_balance.1.json b/savings_goals/test_snapshots/test/test_withdraw_from_goal_insufficient_balance.1.json
index 3ddc949e..e5d9669b 100644
--- a/savings_goals/test_snapshots/test/test_withdraw_from_goal_insufficient_balance.1.json
+++ b/savings_goals/test_snapshots/test/test_withdraw_from_goal_insufficient_balance.1.json
@@ -1038,7 +1038,7 @@
],
"data": {
"error": {
- "contract": 2
+ "contract": 5
}
}
}
@@ -1059,7 +1059,7 @@
},
{
"error": {
- "contract": 2
+ "contract": 5
}
}
],
@@ -1084,7 +1084,7 @@
},
{
"error": {
- "contract": 2
+ "contract": 5
}
}
],
diff --git a/savings_goals/test_snapshots/test/test_withdraw_from_goal_locked.1.json b/savings_goals/test_snapshots/test/test_withdraw_from_goal_locked.1.json
index 1bc695cc..9e04a2e3 100644
--- a/savings_goals/test_snapshots/test/test_withdraw_from_goal_locked.1.json
+++ b/savings_goals/test_snapshots/test/test_withdraw_from_goal_locked.1.json
@@ -857,7 +857,7 @@
],
"data": {
"error": {
- "contract": 6
+ "contract": 4
}
}
}
@@ -878,7 +878,7 @@
},
{
"error": {
- "contract": 6
+ "contract": 4
}
}
],
@@ -903,7 +903,7 @@
},
{
"error": {
- "contract": 6
+ "contract": 4
}
}
],
diff --git a/savings_goals/test_snapshots/test/test_withdraw_from_goal_nonexistent_goal_panics.1.json b/savings_goals/test_snapshots/test/test_withdraw_from_goal_nonexistent_goal_panics.1.json
index 208311f6..5cfdc261 100644
--- a/savings_goals/test_snapshots/test/test_withdraw_from_goal_nonexistent_goal_panics.1.json
+++ b/savings_goals/test_snapshots/test/test_withdraw_from_goal_nonexistent_goal_panics.1.json
@@ -236,7 +236,7 @@
],
"data": {
"error": {
- "contract": 3
+ "contract": 2
}
}
}
@@ -257,7 +257,7 @@
},
{
"error": {
- "contract": 3
+ "contract": 2
}
}
],
@@ -282,14 +282,14 @@
},
{
"error": {
- "contract": 3
+ "contract": 2
}
}
],
"data": {
"vec": [
{
- "string": "contract try_call failed"
+ "string": "contract call failed"
},
{
"symbol": "withdraw_from_goal"
@@ -316,6 +316,31 @@
}
},
"failed_call": false
+ },
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": null,
+ "type_": "diagnostic",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "error"
+ },
+ {
+ "error": {
+ "contract": 2
+ }
+ }
+ ],
+ "data": {
+ "string": "escalating error to panic"
+ }
+ }
+ }
+ },
+ "failed_call": false
}
]
}
\ No newline at end of file
diff --git a/savings_goals/test_snapshots/test/test_withdraw_from_goal_unauthorized.1.json b/savings_goals/test_snapshots/test/test_withdraw_from_goal_unauthorized.1.json
index 2b6be753..0e2bf8ec 100644
--- a/savings_goals/test_snapshots/test/test_withdraw_from_goal_unauthorized.1.json
+++ b/savings_goals/test_snapshots/test/test_withdraw_from_goal_unauthorized.1.json
@@ -1038,7 +1038,7 @@
],
"data": {
"error": {
- "contract": 6
+ "contract": 3
}
}
}
@@ -1059,7 +1059,7 @@
},
{
"error": {
- "contract": 6
+ "contract": 3
}
}
],
@@ -1084,7 +1084,7 @@
},
{
"error": {
- "contract": 6
+ "contract": 3
}
}
],
diff --git a/savings_goals/test_snapshots/test/test_withdraw_from_goal_zero_amount_panics.1.json b/savings_goals/test_snapshots/test/test_withdraw_from_goal_zero_amount_panics.1.json
index 6805051c..7c72b82b 100644
--- a/savings_goals/test_snapshots/test/test_withdraw_from_goal_zero_amount_panics.1.json
+++ b/savings_goals/test_snapshots/test/test_withdraw_from_goal_zero_amount_panics.1.json
@@ -1038,7 +1038,7 @@
],
"data": {
"error": {
- "contract": 2
+ "contract": 1
}
}
}
@@ -1059,7 +1059,7 @@
},
{
"error": {
- "contract": 2
+ "contract": 1
}
}
],
@@ -1084,14 +1084,14 @@
},
{
"error": {
- "contract": 2
+ "contract": 1
}
}
],
"data": {
"vec": [
{
- "string": "contract try_call failed"
+ "string": "contract call failed"
},
{
"symbol": "withdraw_from_goal"
@@ -1118,6 +1118,31 @@
}
},
"failed_call": false
+ },
+ {
+ "event": {
+ "ext": "v0",
+ "contract_id": null,
+ "type_": "diagnostic",
+ "body": {
+ "v0": {
+ "topics": [
+ {
+ "symbol": "error"
+ },
+ {
+ "error": {
+ "contract": 1
+ }
+ }
+ ],
+ "data": {
+ "string": "escalating error to panic"
+ }
+ }
+ }
+ },
+ "failed_call": false
}
]
}
\ No newline at end of file
diff --git a/savings_goals/test_snapshots/test/test_withdraw_time_locked_goal_after_unlock.1.json b/savings_goals/test_snapshots/test/test_withdraw_time_locked_goal_after_unlock.1.json
index 7dcbbf8d..bcf441ae 100644
--- a/savings_goals/test_snapshots/test/test_withdraw_time_locked_goal_after_unlock.1.json
+++ b/savings_goals/test_snapshots/test/test_withdraw_time_locked_goal_after_unlock.1.json
@@ -147,7 +147,7 @@
"base_reserve": 10,
"min_persistent_entry_ttl": 1,
"min_temp_entry_ttl": 1,
- "max_entry_ttl": 100000,
+ "max_entry_ttl": 700000,
"ledger_entries": [
[
{
@@ -457,7 +457,7 @@
},
"ext": "v0"
},
- 100000
+ 518401
]
],
[
@@ -490,7 +490,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -523,7 +523,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -556,7 +556,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -589,7 +589,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -622,7 +622,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -643,7 +643,7 @@
},
"ext": "v0"
},
- 100000
+ 518401
]
]
]
diff --git a/savings_goals/test_snapshots/test/test_withdraw_time_locked_goal_before_unlock.1.json b/savings_goals/test_snapshots/test/test_withdraw_time_locked_goal_before_unlock.1.json
index 2ab6be6f..4a353da8 100644
--- a/savings_goals/test_snapshots/test/test_withdraw_time_locked_goal_before_unlock.1.json
+++ b/savings_goals/test_snapshots/test/test_withdraw_time_locked_goal_before_unlock.1.json
@@ -120,7 +120,7 @@
"base_reserve": 10,
"min_persistent_entry_ttl": 1,
"min_temp_entry_ttl": 1,
- "max_entry_ttl": 100000,
+ "max_entry_ttl": 700000,
"ledger_entries": [
[
{
@@ -394,7 +394,7 @@
},
"ext": "v0"
},
- 100000
+ 518401
]
],
[
@@ -427,7 +427,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -460,7 +460,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -493,7 +493,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -526,7 +526,7 @@
},
"ext": "v0"
},
- 100000
+ 700000
]
],
[
@@ -547,7 +547,7 @@
},
"ext": "v0"
},
- 100000
+ 518401
]
]
]
@@ -1085,7 +1085,7 @@
],
"data": {
"error": {
- "contract": 6
+ "contract": 4
}
}
}
@@ -1106,7 +1106,7 @@
},
{
"error": {
- "contract": 6
+ "contract": 4
}
}
],
@@ -1131,7 +1131,7 @@
},
{
"error": {
- "contract": 6
+ "contract": 4
}
}
],
diff --git a/savings_goals/test_snapshots/test/test_zero_amount_fails.1.json b/savings_goals/test_snapshots/test/test_zero_amount_fails.1.json
index adbe7f84..83cc4d3e 100644
--- a/savings_goals/test_snapshots/test/test_zero_amount_fails.1.json
+++ b/savings_goals/test_snapshots/test/test_zero_amount_fails.1.json
@@ -239,7 +239,7 @@
],
"data": {
"error": {
- "contract": 2
+ "contract": 1
}
}
}
@@ -260,7 +260,7 @@
},
{
"error": {
- "contract": 2
+ "contract": 1
}
}
],
@@ -285,7 +285,7 @@
},
{
"error": {
- "contract": 2
+ "contract": 1
}
}
],
diff --git a/savings_goals/test_snapshots/test_add_to_goal_overflow_panics.1.json b/savings_goals/test_snapshots/test_add_to_goal_overflow_panics.1.json
index 5cb37082..d3f6f39b 100644
--- a/savings_goals/test_snapshots/test_add_to_goal_overflow_panics.1.json
+++ b/savings_goals/test_snapshots/test_add_to_goal_overflow_panics.1.json
@@ -749,7 +749,7 @@
],
"data": {
"error": {
- "contract": 2
+ "contract": 6
}
}
}
@@ -770,7 +770,7 @@
},
{
"error": {
- "contract": 2
+ "contract": 6
}
}
],
@@ -795,7 +795,7 @@
},
{
"error": {
- "contract": 2
+ "contract": 6
}
}
],
@@ -843,7 +843,7 @@
},
{
"error": {
- "contract": 2
+ "contract": 6
}
}
],
diff --git a/savings_goals/test_snapshots/test_time_lock_with_large_amounts.1.json b/savings_goals/test_snapshots/test_time_lock_with_large_amounts.1.json
index 9540e5bb..ef71d517 100644
--- a/savings_goals/test_snapshots/test_time_lock_with_large_amounts.1.json
+++ b/savings_goals/test_snapshots/test_time_lock_with_large_amounts.1.json
@@ -1182,7 +1182,7 @@
],
"data": {
"error": {
- "contract": 6
+ "contract": 4
}
}
}
@@ -1203,7 +1203,7 @@
},
{
"error": {
- "contract": 6
+ "contract": 4
}
}
],
@@ -1228,7 +1228,7 @@
},
{
"error": {
- "contract": 6
+ "contract": 4
}
}
],
diff --git a/savings_goals/tests/stress_tests.rs b/savings_goals/tests/stress_tests.rs
index 85569330..8f2e626d 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!(
+ (4..=5).contains(&pages),
+ "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/src/lib.rs b/scenarios/src/lib.rs
index 57b22e96..19357067 100644
--- a/scenarios/src/lib.rs
+++ b/scenarios/src/lib.rs
@@ -1,6 +1,4 @@
pub mod tests {
- use soroban_sdk::Env;
- use testutils::set_ledger_time;
use soroban_sdk::testutils::{Ledger, LedgerInfo};
use soroban_sdk::Env;
diff --git a/testutils/src/lib.rs b/testutils/src/lib.rs
index c4447f22..75a6216f 100644
--- a/testutils/src/lib.rs
+++ b/testutils/src/lib.rs
@@ -15,8 +15,7 @@ pub fn set_ledger_time(env: &Env, sequence_number: u32, timestamp: u64) {
base_reserve: 10,
min_temp_entry_ttl: 1,
min_persistent_entry_ttl: 1,
- // Must exceed any contract bump TTL used in tests (e.g. 518,400).
- max_entry_ttl: 3_000_000,
+ max_entry_ttl: 700000,
});
}
@@ -26,11 +25,11 @@ pub fn generate_test_address(env: &Env) -> Address {
#[macro_export]
macro_rules! setup_test_env {
- ($env:ident, $contract:ident, $client_struct:ident, $client:ident, $owner:ident) => {
+ ($env:ident, $contract:ident, $client:ident, $owner:ident) => {
let $env = Env::default();
$env.mock_all_auths();
let contract_id = $env.register_contract(None, $contract);
- let $client = $client_struct::new(&$env, &contract_id);
+ let $client = $contract::Client::new(&$env, &contract_id);
let $owner = $crate::generate_test_address(&$env);
};
}