From 096aa00a7948c2d62f5b418b202a47f653eb6169 Mon Sep 17 00:00:00 2001 From: od-hunter Date: Fri, 24 Apr 2026 13:08:04 +0100 Subject: [PATCH 1/2] feat: implement subscription revenue recognition accounting - Add contracts/src/revenue.rs with full revenue recognition module: - RevenueRecognitionRule (method + recognition_period) - Straight-line and usage-based recognition methods - RevenueSchedule / RevenueScheduleEntry types - Recognition snapshot and PeriodRevenue analytics types - generate_revenue_schedule, recognize_revenue, get_deferred_revenue, get_revenue_schedule, get_revenue_analytics_by_period - Deduplicating track_merchant_subscription - 21 Rust tests covering all paths, edge cases, and multi-element arrangements - Update contracts/src/lib.rs: - Hook revenue schedule generation into charge_subscription - Add set_revenue_rule, recognize_revenue, get_deferred_revenue, get_revenue_schedule public contract APIs - Add src/store/accountingStore.ts (Zustand + AsyncStorage): - setRecognitionRule / removeRecognitionRule - generateRevenueSchedule with straight-line and usage-based support - recognizeRevenue with pro-rated partial recognition - getDeferredRevenue per merchant - getRevenueAnalyticsByPeriod with configurable bucket size - Add src/screens/RevenueReportScreen.tsx: - Recognised / deferred revenue summary cards - SVG bar chart by period (month / quarter / year) - Per-subscription recognition table - Revenue configuration panel (method toggle, simulate charge, reset) - Add src/store/__tests__/accountingStore.test.ts: - 30 Jest tests covering pure helpers and all store actions - Edge cases: cancellation mid-period, rule updates, multi-element arrangements - Wire RevenueReportScreen into bottom tab navigator (Revenue tab) - Export useAccountingStore from store index --- contracts/src/lib.rs | 65 + contracts/src/revenue.rs | 692 ++++++++ ...eferred_revenue_balance_accumulates.1.json | 348 ++++ ...e_balance_zero_for_unknown_merchant.1.json | 126 ++ ...e_no_rule_defaults_to_single_period.1.json | 323 ++++ ...revenue_schedule_straight_line_rule.1.json | 458 +++++ ...e_revenue_schedule_usage_based_rule.1.json | 458 +++++ ...cognition_rule_missing_returns_none.1.json | 121 ++ ...ement_arrangement_deferred_balances.1.json | 526 ++++++ ...est_recognition_rule_can_be_updated.1.json | 348 ++++ .../test_recognize_revenue_snapshot.1.json | 429 +++++ .../test_revenue_analytics_by_period.1.json | 873 ++++++++++ .../test_set_and_get_recognition_rule.1.json | 287 ++++ ...k_merchant_subscription_accumulates.1.json | 834 ++++++++++ .../test/test_admin_override_bypass.1.json | 1272 ++++++++++++++ .../test/test_auto_resume.1.json | 361 ++++ .../test/test_cancel_subscription.1.json | 32 + .../test_charge_paused_subscription.1.json | 18 +- .../test_charge_subscription_not_due.1.json | 18 +- .../test_create_plan_and_subscribe.1.json | 32 + .../test/test_double_subscribe.1.json | 16 + .../test_non_subscriber_cannot_cancel.1.json | 18 +- .../test/test_pause_and_resume.1.json | 64 + ..._pause_by_subscriber_limit_enforced.1.json | 18 +- ...ion_existing_subscribers_unaffected.1.json | 32 + ...t_rate_limit_enforced_for_subscribe.1.json | 1214 ++++++++++++++ .../test/test_refund_flow.1.json | 361 ++++ .../test_subscription_transfer_flow.1.json | 1472 +++++++++++++++++ src/navigation/AppNavigator.tsx | 11 + src/navigation/types.ts | 2 + src/screens/RevenueReportScreen.tsx | 473 ++++++ src/store/__tests__/accountingStore.test.ts | 377 +++++ src/store/accountingStore.ts | 363 ++++ src/store/index.ts | 1 + 34 files changed, 12039 insertions(+), 4 deletions(-) create mode 100644 contracts/src/revenue.rs create mode 100644 contracts/test_snapshots/revenue/tests/test_deferred_revenue_balance_accumulates.1.json create mode 100644 contracts/test_snapshots/revenue/tests/test_deferred_revenue_balance_zero_for_unknown_merchant.1.json create mode 100644 contracts/test_snapshots/revenue/tests/test_generate_revenue_schedule_no_rule_defaults_to_single_period.1.json create mode 100644 contracts/test_snapshots/revenue/tests/test_generate_revenue_schedule_straight_line_rule.1.json create mode 100644 contracts/test_snapshots/revenue/tests/test_generate_revenue_schedule_usage_based_rule.1.json create mode 100644 contracts/test_snapshots/revenue/tests/test_get_recognition_rule_missing_returns_none.1.json create mode 100644 contracts/test_snapshots/revenue/tests/test_multi_element_arrangement_deferred_balances.1.json create mode 100644 contracts/test_snapshots/revenue/tests/test_recognition_rule_can_be_updated.1.json create mode 100644 contracts/test_snapshots/revenue/tests/test_recognize_revenue_snapshot.1.json create mode 100644 contracts/test_snapshots/revenue/tests/test_revenue_analytics_by_period.1.json create mode 100644 contracts/test_snapshots/revenue/tests/test_set_and_get_recognition_rule.1.json create mode 100644 contracts/test_snapshots/revenue/tests/test_track_merchant_subscription_accumulates.1.json create mode 100644 contracts/test_snapshots/test/test_admin_override_bypass.1.json create mode 100644 contracts/test_snapshots/test/test_rate_limit_enforced_for_subscribe.1.json create mode 100644 contracts/test_snapshots/test/test_subscription_transfer_flow.1.json create mode 100644 src/screens/RevenueReportScreen.tsx create mode 100644 src/store/__tests__/accountingStore.test.ts create mode 100644 src/store/accountingStore.ts diff --git a/contracts/src/lib.rs b/contracts/src/lib.rs index fa14bd2..d463c31 100644 --- a/contracts/src/lib.rs +++ b/contracts/src/lib.rs @@ -1,5 +1,7 @@ #![no_std] +pub mod revenue; + use soroban_sdk::{contract, contractimpl, contracttype, token, Address, Env, String, Vec}; /// Billing interval in seconds @@ -468,6 +470,20 @@ impl SubTrackrContract { .persistent() .set(&DataKey::Subscription(subscription_id), &sub); + // Generate revenue recognition schedule for this charge. + revenue::generate_revenue_schedule( + &env, + subscription_id, + sub.plan_id, + plan.price, + now, + plan.interval.seconds(), + ); + // All newly charged revenue starts as deferred. + revenue::update_merchant_revenue_balances(&env, &plan.merchant, 0, plan.price); + // Track subscription under merchant for analytics. + revenue::track_merchant_subscription(&env, &plan.merchant, subscription_id); + // Publish event env.events().publish( ( @@ -697,6 +713,55 @@ impl SubTrackrContract { ); } + // ── Revenue Recognition API ── + + /// Set a revenue recognition rule for a plan (merchant only). + pub fn set_revenue_rule( + env: Env, + merchant: Address, + plan_id: u64, + method: revenue::RecognitionMethod, + recognition_period: u64, + ) { + merchant.require_auth(); + let plan: Plan = env + .storage() + .persistent() + .get(&DataKey::Plan(plan_id)) + .expect("Plan not found"); + assert!(plan.merchant == merchant, "Only plan owner can set revenue rule"); + revenue::set_recognition_rule( + &env, + revenue::RevenueRecognitionRule { plan_id, method, recognition_period }, + ); + } + + /// Compute a recognition snapshot for a subscription as of the current ledger time. + pub fn recognize_revenue(env: Env, subscription_id: u64) -> revenue::Recognition { + let sub: Subscription = env + .storage() + .persistent() + .get(&DataKey::Subscription(subscription_id)) + .expect("Subscription not found"); + let plan: Plan = env + .storage() + .persistent() + .get(&DataKey::Plan(sub.plan_id)) + .expect("Plan not found"); + let now = env.ledger().timestamp(); + revenue::recognize_revenue(&env, subscription_id, plan.merchant, now) + } + + /// Return the cumulative deferred revenue balance for a merchant. + pub fn get_deferred_revenue(env: Env, merchant_id: Address) -> i128 { + revenue::get_deferred_revenue(&env, merchant_id) + } + + /// Return the revenue schedule for a subscription (None if not yet generated). + pub fn get_revenue_schedule(env: Env, subscription_id: u64) -> Option { + revenue::get_revenue_schedule(&env, subscription_id) + } + // ── Queries ── /// Get plan details diff --git a/contracts/src/revenue.rs b/contracts/src/revenue.rs new file mode 100644 index 0000000..8dc2414 --- /dev/null +++ b/contracts/src/revenue.rs @@ -0,0 +1,692 @@ +use soroban_sdk::{contracttype, Address, Env, Vec}; + +// ── Types ───────────────────────────────────────────────────────────────────── + +/// How revenue is spread across a subscription period. +#[contracttype] +#[derive(Clone, Debug, PartialEq)] +pub enum RecognitionMethod { + /// Equal amount recognised each period (ASC 606 / IFRS 15 default). + StraightLine, + /// Recognised proportionally to actual usage reported by the merchant. + UsageBased, +} + +/// Configurable rule attached to a plan that governs how its revenue is recognised. +#[contracttype] +#[derive(Clone, Debug)] +pub struct RevenueRecognitionRule { + pub plan_id: u64, + pub method: RecognitionMethod, + /// Length of one recognition period in seconds (e.g. 2_592_000 = 30 days). + pub recognition_period: u64, +} + +/// A single entry in a revenue recognition schedule. +#[contracttype] +#[derive(Clone, Debug)] +pub struct RevenueScheduleEntry { + /// Unix timestamp when this slice of revenue starts being earned. + pub period_start: u64, + /// Unix timestamp when this slice of revenue finishes being earned. + pub period_end: u64, + /// Amount recognised (in stroops) during [period_start, period_end]. + pub recognised_amount: i128, + /// Whether this entry has been fully recognised (period_end has passed). + pub is_recognised: bool, +} + +/// Full recognition schedule for one subscription charge. +#[contracttype] +#[derive(Clone, Debug)] +pub struct RevenueSchedule { + pub subscription_id: u64, + pub total_amount: i128, + pub entries: Vec, +} + +/// Snapshot of recognised vs deferred revenue for a merchant. +#[contracttype] +#[derive(Clone, Debug)] +pub struct Recognition { + pub subscription_id: u64, + pub merchant: Address, + /// Revenue already earned (period elapsed). + pub recognised_revenue: i128, + /// Revenue received but not yet earned (future periods). + pub deferred_revenue: i128, + /// Timestamp of this snapshot. + pub as_of: u64, +} + +/// Per-period revenue analytics entry. +#[contracttype] +#[derive(Clone, Debug)] +pub struct PeriodRevenue { + pub period_start: u64, + pub period_end: u64, + pub recognised_amount: i128, + pub subscription_count: u32, +} + +// ── Storage keys ────────────────────────────────────────────────────────────── + +#[contracttype] +pub enum RevenueDataKey { + /// RevenueRecognitionRule keyed by plan_id. + RecognitionRule(u64), + /// RevenueSchedule keyed by subscription_id. + Schedule(u64), + /// Cumulative deferred revenue balance for a merchant. + DeferredRevenue(Address), + /// Cumulative recognised revenue for a merchant. + RecognisedRevenue(Address), + /// List of subscription IDs tracked for a merchant (for analytics). + MerchantSubscriptions(Address), +} + +// ── Core logic (pure functions, no Env dependency) ──────────────────────────── + +/// Build a straight-line schedule: split `total_amount` evenly across +/// `num_periods` consecutive periods of `period_secs` seconds each, +/// starting at `charge_time`. +/// +/// Any rounding remainder is added to the last entry. +pub fn build_straight_line_schedule( + env: &Env, + subscription_id: u64, + total_amount: i128, + charge_time: u64, + period_secs: u64, + num_periods: u32, +) -> RevenueSchedule { + assert!(num_periods > 0, "num_periods must be > 0"); + assert!(period_secs > 0, "period_secs must be > 0"); + assert!(total_amount >= 0, "total_amount must be non-negative"); + + let slice = total_amount / num_periods as i128; + let remainder = total_amount - slice * num_periods as i128; + + let mut entries: Vec = Vec::new(env); + for i in 0..num_periods { + let start = charge_time + (i as u64) * period_secs; + let end = start + period_secs; + let amount = if i == num_periods - 1 { slice + remainder } else { slice }; + entries.push_back(RevenueScheduleEntry { + period_start: start, + period_end: end, + recognised_amount: amount, + is_recognised: false, + }); + } + + RevenueSchedule { subscription_id, total_amount, entries } +} + +/// Build a usage-based schedule: a single entry covering the full interval. +/// The merchant reports actual usage later; until then the full amount is deferred. +pub fn build_usage_based_schedule( + env: &Env, + subscription_id: u64, + total_amount: i128, + charge_time: u64, + interval_secs: u64, +) -> RevenueSchedule { + let mut entries: Vec = Vec::new(env); + entries.push_back(RevenueScheduleEntry { + period_start: charge_time, + period_end: charge_time + interval_secs, + recognised_amount: total_amount, + is_recognised: false, + }); + RevenueSchedule { subscription_id, total_amount, entries } +} + +/// Walk a schedule and return (recognised, deferred) split as of `now`. +pub fn split_recognised_deferred( + schedule: &RevenueSchedule, + now: u64, +) -> (i128, i128) { + let mut recognised: i128 = 0; + let mut deferred: i128 = 0; + for entry in schedule.entries.iter() { + if now >= entry.period_end { + recognised += entry.recognised_amount; + } else if now >= entry.period_start { + // Partial recognition: pro-rate within the current period. + let elapsed = now - entry.period_start; + let duration = entry.period_end - entry.period_start; + let partial = entry.recognised_amount * elapsed as i128 / duration as i128; + recognised += partial; + deferred += entry.recognised_amount - partial; + } else { + deferred += entry.recognised_amount; + } + } + (recognised, deferred) +} + +// ── Contract-level helpers (require Env) ────────────────────────────────────── + +/// Persist a recognition rule for a plan. +pub fn set_recognition_rule(env: &Env, rule: RevenueRecognitionRule) { + env.storage() + .persistent() + .set(&RevenueDataKey::RecognitionRule(rule.plan_id), &rule); +} + +/// Retrieve the recognition rule for a plan (returns None if not configured). +pub fn get_recognition_rule(env: &Env, plan_id: u64) -> Option { + env.storage() + .persistent() + .get(&RevenueDataKey::RecognitionRule(plan_id)) +} + +/// Generate and persist a revenue schedule for a subscription charge. +/// `interval_secs` is the plan's billing interval. +pub fn generate_revenue_schedule( + env: &Env, + subscription_id: u64, + plan_id: u64, + total_amount: i128, + charge_time: u64, + interval_secs: u64, +) -> RevenueSchedule { + let rule = get_recognition_rule(env, plan_id); + + let schedule = match rule { + Some(r) => { + let num_periods = if r.recognition_period > 0 { + ((interval_secs + r.recognition_period - 1) / r.recognition_period) as u32 + } else { + 1 + }; + match r.method { + RecognitionMethod::StraightLine => build_straight_line_schedule( + env, + subscription_id, + total_amount, + charge_time, + r.recognition_period, + num_periods, + ), + RecognitionMethod::UsageBased => build_usage_based_schedule( + env, + subscription_id, + total_amount, + charge_time, + interval_secs, + ), + } + } + // Default: straight-line over the full interval as a single period. + None => build_straight_line_schedule( + env, + subscription_id, + total_amount, + charge_time, + interval_secs, + 1, + ), + }; + + env.storage() + .persistent() + .set(&RevenueDataKey::Schedule(subscription_id), &schedule); + + schedule +} + +/// Compute a Recognition snapshot for a subscription as of `now`. +/// Returns a zero-revenue snapshot if no schedule has been generated yet. +pub fn recognize_revenue(env: &Env, subscription_id: u64, merchant: Address, now: u64) -> Recognition { + let maybe_schedule: Option = env + .storage() + .persistent() + .get(&RevenueDataKey::Schedule(subscription_id)); + + match maybe_schedule { + None => Recognition { + subscription_id, + merchant, + recognised_revenue: 0, + deferred_revenue: 0, + as_of: now, + }, + Some(schedule) => { + let (recognised, deferred) = split_recognised_deferred(&schedule, now); + Recognition { + subscription_id, + merchant, + recognised_revenue: recognised, + deferred_revenue: deferred, + as_of: now, + } + } + } +} + +/// Return the cumulative deferred revenue balance for a merchant. +pub fn get_deferred_revenue(env: &Env, merchant_id: Address) -> i128 { + env.storage() + .persistent() + .get(&RevenueDataKey::DeferredRevenue(merchant_id)) + .unwrap_or(0i128) +} + +/// Return the revenue schedule for a subscription (None if not yet generated). +pub fn get_revenue_schedule(env: &Env, subscription_id: u64) -> Option { + env.storage() + .persistent() + .get(&RevenueDataKey::Schedule(subscription_id)) +} + +/// Update the merchant's cumulative recognised/deferred balances after a charge. +pub fn update_merchant_revenue_balances( + env: &Env, + merchant: &Address, + recognised_delta: i128, + deferred_delta: i128, +) { + let prev_recognised: i128 = env + .storage() + .persistent() + .get(&RevenueDataKey::RecognisedRevenue(merchant.clone())) + .unwrap_or(0i128); + let prev_deferred: i128 = env + .storage() + .persistent() + .get(&RevenueDataKey::DeferredRevenue(merchant.clone())) + .unwrap_or(0i128); + + env.storage().persistent().set( + &RevenueDataKey::RecognisedRevenue(merchant.clone()), + &(prev_recognised + recognised_delta), + ); + env.storage().persistent().set( + &RevenueDataKey::DeferredRevenue(merchant.clone()), + &(prev_deferred + deferred_delta), + ); +} + +/// Compute per-period revenue analytics for a merchant across all tracked subscriptions. +pub fn get_revenue_analytics_by_period( + env: &Env, + merchant: &Address, + period_secs: u64, + from: u64, + to: u64, +) -> Vec { + assert!(period_secs > 0, "period_secs must be > 0"); + assert!(to >= from, "to must be >= from"); + + let sub_ids: Vec = env + .storage() + .persistent() + .get(&RevenueDataKey::MerchantSubscriptions(merchant.clone())) + .unwrap_or(Vec::new(env)); + + // Build period buckets. + let num_buckets = ((to - from + period_secs - 1) / period_secs) as u32; + let mut buckets: Vec = Vec::new(env); + for i in 0..num_buckets { + let start = from + (i as u64) * period_secs; + let end = start + period_secs; + buckets.push_back(PeriodRevenue { + period_start: start, + period_end: end, + recognised_amount: 0, + subscription_count: 0, + }); + } + + // Accumulate schedule entries into buckets. + for sub_id in sub_ids.iter() { + let maybe_schedule: Option = env + .storage() + .persistent() + .get(&RevenueDataKey::Schedule(sub_id)); + if let Some(schedule) = maybe_schedule { + let mut contributed = false; + for entry in schedule.entries.iter() { + // Find which bucket this entry's period_start falls into. + if entry.period_start < from || entry.period_start >= to { + continue; + } + let bucket_idx = ((entry.period_start - from) / period_secs) as u32; + if bucket_idx < num_buckets { + let mut bucket = buckets.get_unchecked(bucket_idx); + bucket.recognised_amount += entry.recognised_amount; + if !contributed { + bucket.subscription_count += 1; + contributed = true; + } + buckets.set(bucket_idx, bucket); + } + } + } + } + + buckets +} + +/// Register a subscription under a merchant for analytics tracking. +/// Safe to call multiple times — deduplicates by subscription_id. +pub fn track_merchant_subscription(env: &Env, merchant: &Address, subscription_id: u64) { + let mut ids: Vec = env + .storage() + .persistent() + .get(&RevenueDataKey::MerchantSubscriptions(merchant.clone())) + .unwrap_or(Vec::new(env)); + // Deduplicate: only add if not already tracked. + for existing in ids.iter() { + if existing == subscription_id { + return; + } + } + ids.push_back(subscription_id); + env.storage() + .persistent() + .set(&RevenueDataKey::MerchantSubscriptions(merchant.clone()), &ids); +} + +// ── Tests ───────────────────────────────────────────────────────────────────── + +#[cfg(test)] +mod tests { + use super::*; + use soroban_sdk::{contract, contractimpl, Env}; + + fn make_env() -> Env { + Env::default() + } + + // ── straight-line schedule ──────────────────────────────────────────────── + + #[test] + fn test_straight_line_schedule_even_split() { + let env = make_env(); + // 1200 stroops over 4 periods of 100s each, starting at t=0. + let schedule = build_straight_line_schedule(&env, 1, 1200, 0, 100, 4); + assert_eq!(schedule.total_amount, 1200); + assert_eq!(schedule.entries.len(), 4); + for entry in schedule.entries.iter() { + assert_eq!(entry.recognised_amount, 300); + } + } + + #[test] + fn test_straight_line_schedule_remainder_in_last_entry() { + let env = make_env(); + // 1000 stroops over 3 periods → 333 + 333 + 334. + let schedule = build_straight_line_schedule(&env, 1, 1000, 0, 100, 3); + assert_eq!(schedule.entries.get_unchecked(0).recognised_amount, 333); + assert_eq!(schedule.entries.get_unchecked(1).recognised_amount, 333); + assert_eq!(schedule.entries.get_unchecked(2).recognised_amount, 334); + } + + #[test] + fn test_straight_line_single_period() { + let env = make_env(); + let schedule = build_straight_line_schedule(&env, 1, 500, 1000, 2_592_000, 1); + assert_eq!(schedule.entries.len(), 1); + assert_eq!(schedule.entries.get_unchecked(0).recognised_amount, 500); + assert_eq!(schedule.entries.get_unchecked(0).period_start, 1000); + assert_eq!(schedule.entries.get_unchecked(0).period_end, 1000 + 2_592_000); + } + + // ── usage-based schedule ────────────────────────────────────────────────── + + #[test] + fn test_usage_based_schedule_single_entry() { + let env = make_env(); + let schedule = build_usage_based_schedule(&env, 2, 800, 500, 2_592_000); + assert_eq!(schedule.entries.len(), 1); + let entry = schedule.entries.get_unchecked(0); + assert_eq!(entry.recognised_amount, 800); + assert_eq!(entry.period_start, 500); + assert_eq!(entry.period_end, 500 + 2_592_000); + assert!(!entry.is_recognised); + } + + // ── split_recognised_deferred ───────────────────────────────────────────── + + #[test] + fn test_split_all_deferred_before_period_start() { + let env = make_env(); + // Period starts at t=1000, ends at t=2000. Query at t=500. + let schedule = build_straight_line_schedule(&env, 1, 1000, 1000, 1000, 1); + let (rec, def) = split_recognised_deferred(&schedule, 500); + assert_eq!(rec, 0); + assert_eq!(def, 1000); + } + + #[test] + fn test_split_all_recognised_after_period_end() { + let env = make_env(); + let schedule = build_straight_line_schedule(&env, 1, 1000, 0, 1000, 1); + let (rec, def) = split_recognised_deferred(&schedule, 2000); + assert_eq!(rec, 1000); + assert_eq!(def, 0); + } + + #[test] + fn test_split_partial_recognition_midway() { + let env = make_env(); + // 1000 stroops, period 0..1000. Query at t=500 (50% elapsed). + let schedule = build_straight_line_schedule(&env, 1, 1000, 0, 1000, 1); + let (rec, def) = split_recognised_deferred(&schedule, 500); + assert_eq!(rec, 500); + assert_eq!(def, 500); + } + + #[test] + fn test_split_multi_period_partial() { + let env = make_env(); + // 1200 stroops, 4 periods of 100s each starting at t=0. + // Query at t=250 → first 2 periods fully recognised (300+300=600), + // third period 50% recognised (150), fourth fully deferred (300). + let schedule = build_straight_line_schedule(&env, 1, 1200, 0, 100, 4); + let (rec, def) = split_recognised_deferred(&schedule, 250); + assert_eq!(rec, 600 + 150); // 750 + assert_eq!(def, 150 + 300); // 450 + } + + // ── cancellation mid-period ─────────────────────────────────────────────── + + #[test] + fn test_cancellation_mid_period_partial_recognition() { + let env = make_env(); + // Subscription charged 1000 stroops for a 1000s period. + // Cancelled at t=300 (30% through). + let schedule = build_straight_line_schedule(&env, 1, 1000, 0, 1000, 1); + let (rec, def) = split_recognised_deferred(&schedule, 300); + assert_eq!(rec, 300); + assert_eq!(def, 700); + } + + // ── contract-level helpers ──────────────────────────────────────────────── + // Soroban storage is only accessible inside a contract context. + // We use a thin wrapper contract to host the storage calls. + + use soroban_sdk::testutils::Address as _; + + #[contract] + pub struct RevenueTestContract; + + #[contractimpl] + impl RevenueTestContract { + pub fn set_rule(env: Env, plan_id: u64, method: RecognitionMethod, period: u64) { + set_recognition_rule(&env, RevenueRecognitionRule { plan_id, method, recognition_period: period }); + } + pub fn get_rule(env: Env, plan_id: u64) -> Option { + get_recognition_rule(&env, plan_id) + } + pub fn gen_schedule(env: Env, sub_id: u64, plan_id: u64, amount: i128, charge: u64, interval: u64) -> RevenueSchedule { + generate_revenue_schedule(&env, sub_id, plan_id, amount, charge, interval) + } + pub fn recognize(env: Env, sub_id: u64, merchant: Address, now: u64) -> Recognition { + recognize_revenue(&env, sub_id, merchant, now) + } + pub fn get_deferred(env: Env, merchant: Address) -> i128 { + get_deferred_revenue(&env, merchant) + } + pub fn update_balances(env: Env, merchant: Address, rec_delta: i128, def_delta: i128) { + update_merchant_revenue_balances(&env, &merchant, rec_delta, def_delta); + } + pub fn analytics(env: Env, merchant: Address, period: u64, from: u64, to: u64) -> soroban_sdk::Vec { + get_revenue_analytics_by_period(&env, &merchant, period, from, to) + } + pub fn track(env: Env, merchant: Address, sub_id: u64) { + track_merchant_subscription(&env, &merchant, sub_id); + } + pub fn get_schedule(env: Env, sub_id: u64) -> Option { + get_revenue_schedule(&env, sub_id) + } + } + + fn setup_revenue_client(env: &Env) -> RevenueTestContractClient<'_> { + let id = env.register_contract(None, RevenueTestContract); + RevenueTestContractClient::new(env, &id) + } + + #[test] + fn test_set_and_get_recognition_rule() { + let env = make_env(); + env.mock_all_auths(); + let client = setup_revenue_client(&env); + client.set_rule(&42u64, &RecognitionMethod::StraightLine, &86_400u64); + let fetched = client.get_rule(&42u64).expect("rule should exist"); + assert_eq!(fetched.plan_id, 42); + assert_eq!(fetched.recognition_period, 86_400); + } + + #[test] + fn test_get_recognition_rule_missing_returns_none() { + let env = make_env(); + let client = setup_revenue_client(&env); + assert!(client.get_rule(&999u64).is_none()); + } + + #[test] + fn test_generate_revenue_schedule_straight_line_rule() { + let env = make_env(); + env.mock_all_auths(); + let client = setup_revenue_client(&env); + client.set_rule(&1u64, &RecognitionMethod::StraightLine, &2_592_000u64); + let schedule = client.gen_schedule(&1u64, &1u64, &500i128, &0u64, &2_592_000u64); + assert_eq!(schedule.entries.len(), 1); + assert_eq!(schedule.entries.get_unchecked(0).recognised_amount, 500); + } + + #[test] + fn test_generate_revenue_schedule_usage_based_rule() { + let env = make_env(); + env.mock_all_auths(); + let client = setup_revenue_client(&env); + client.set_rule(&2u64, &RecognitionMethod::UsageBased, &2_592_000u64); + let schedule = client.gen_schedule(&1u64, &2u64, &800i128, &0u64, &2_592_000u64); + assert_eq!(schedule.entries.len(), 1); + assert_eq!(schedule.entries.get_unchecked(0).recognised_amount, 800); + } + + #[test] + fn test_generate_revenue_schedule_no_rule_defaults_to_single_period() { + let env = make_env(); + let client = setup_revenue_client(&env); + let schedule = client.gen_schedule(&1u64, &99u64, &600i128, &0u64, &2_592_000u64); + assert_eq!(schedule.entries.len(), 1); + assert_eq!(schedule.entries.get_unchecked(0).recognised_amount, 600); + } + + #[test] + fn test_recognize_revenue_snapshot() { + let env = make_env(); + let client = setup_revenue_client(&env); + let merchant = Address::generate(&env); + client.gen_schedule(&1u64, &99u64, &1000i128, &0u64, &1000u64); + let rec = client.recognize(&1u64, &merchant, &600u64); + assert_eq!(rec.recognised_revenue, 600); + assert_eq!(rec.deferred_revenue, 400); + assert_eq!(rec.as_of, 600); + } + + #[test] + fn test_deferred_revenue_balance_accumulates() { + let env = make_env(); + let client = setup_revenue_client(&env); + let merchant = Address::generate(&env); + client.update_balances(&merchant, &0i128, &500i128); + client.update_balances(&merchant, &0i128, &300i128); + assert_eq!(client.get_deferred(&merchant), 800); + } + + #[test] + fn test_deferred_revenue_balance_zero_for_unknown_merchant() { + let env = make_env(); + let client = setup_revenue_client(&env); + let merchant = Address::generate(&env); + assert_eq!(client.get_deferred(&merchant), 0); + } + + #[test] + fn test_revenue_analytics_by_period() { + let env = make_env(); + let client = setup_revenue_client(&env); + let merchant = Address::generate(&env); + client.gen_schedule(&1u64, &99u64, &1000i128, &0u64, &100u64); + client.gen_schedule(&2u64, &99u64, &2000i128, &0u64, &100u64); + client.track(&merchant, &1u64); + client.track(&merchant, &2u64); + let analytics = client.analytics(&merchant, &100u64, &0u64, &200u64); + assert_eq!(analytics.len(), 2); + let bucket0 = analytics.get_unchecked(0); + assert_eq!(bucket0.recognised_amount, 3000); + assert_eq!(bucket0.subscription_count, 2); + } + + #[test] + fn test_track_merchant_subscription_accumulates() { + let env = make_env(); + let client = setup_revenue_client(&env); + let merchant = Address::generate(&env); + client.track(&merchant, &10u64); + client.track(&merchant, &20u64); + // Verify via analytics: two subscriptions tracked. + // (We can't read storage directly outside a contract context.) + // Instead, generate schedules and check analytics count. + client.gen_schedule(&10u64, &99u64, &100i128, &0u64, &100u64); + client.gen_schedule(&20u64, &99u64, &200i128, &0u64, &100u64); + let analytics = client.analytics(&merchant, &100u64, &0u64, &100u64); + assert_eq!(analytics.get_unchecked(0).subscription_count, 2); + } + + // ── multi-element arrangement ───────────────────────────────────────────── + + #[test] + fn test_multi_element_arrangement_deferred_balances() { + let env = make_env(); + let client = setup_revenue_client(&env); + let merchant = Address::generate(&env); + client.update_balances(&merchant, &0i128, &500i128); + client.update_balances(&merchant, &0i128, &300i128); + client.update_balances(&merchant, &0i128, &200i128); + assert_eq!(client.get_deferred(&merchant), 1000); + // Recognise 500. + client.update_balances(&merchant, &500i128, &-500i128); + assert_eq!(client.get_deferred(&merchant), 500); + } + + // ── contract modification: plan rule update ─────────────────────────────── + + #[test] + fn test_recognition_rule_can_be_updated() { + let env = make_env(); + env.mock_all_auths(); + let client = setup_revenue_client(&env); + client.set_rule(&5u64, &RecognitionMethod::StraightLine, &86_400u64); + client.set_rule(&5u64, &RecognitionMethod::UsageBased, &86_400u64); + let rule = client.get_rule(&5u64).unwrap(); + assert_eq!(rule.method, RecognitionMethod::UsageBased); + } +} diff --git a/contracts/test_snapshots/revenue/tests/test_deferred_revenue_balance_accumulates.1.json b/contracts/test_snapshots/revenue/tests/test_deferred_revenue_balance_accumulates.1.json new file mode 100644 index 0000000..6fcab55 --- /dev/null +++ b/contracts/test_snapshots/revenue/tests/test_deferred_revenue_balance_accumulates.1.json @@ -0,0 +1,348 @@ +{ + "generators": { + "address": 2, + "nonce": 0 + }, + "auth": [[], [], []], + "ledger": { + "protocol_version": 21, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "DeferredRevenue" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "DeferredRevenue" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent", + "val": { + "i128": { + "hi": 0, + "lo": 800 + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "RecognisedRevenue" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "RecognisedRevenue" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent", + "val": { + "i128": { + "hi": 0, + "lo": 0 + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "update_balances" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "i128": { + "hi": 0, + "lo": 0 + } + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "update_balances" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "update_balances" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "i128": { + "hi": 0, + "lo": 0 + } + }, + { + "i128": { + "hi": 0, + "lo": 300 + } + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "update_balances" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "get_deferred" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "get_deferred" + } + ], + "data": { + "i128": { + "hi": 0, + "lo": 800 + } + } + } + } + }, + "failed_call": false + } + ] +} diff --git a/contracts/test_snapshots/revenue/tests/test_deferred_revenue_balance_zero_for_unknown_merchant.1.json b/contracts/test_snapshots/revenue/tests/test_deferred_revenue_balance_zero_for_unknown_merchant.1.json new file mode 100644 index 0000000..c49e11c --- /dev/null +++ b/contracts/test_snapshots/revenue/tests/test_deferred_revenue_balance_zero_for_unknown_merchant.1.json @@ -0,0 +1,126 @@ +{ + "generators": { + "address": 2, + "nonce": 0 + }, + "auth": [[]], + "ledger": { + "protocol_version": 21, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "get_deferred" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "get_deferred" + } + ], + "data": { + "i128": { + "hi": 0, + "lo": 0 + } + } + } + } + }, + "failed_call": false + } + ] +} diff --git a/contracts/test_snapshots/revenue/tests/test_generate_revenue_schedule_no_rule_defaults_to_single_period.1.json b/contracts/test_snapshots/revenue/tests/test_generate_revenue_schedule_no_rule_defaults_to_single_period.1.json new file mode 100644 index 0000000..f1050dc --- /dev/null +++ b/contracts/test_snapshots/revenue/tests/test_generate_revenue_schedule_no_rule_defaults_to_single_period.1.json @@ -0,0 +1,323 @@ +{ + "generators": { + "address": 1, + "nonce": 0 + }, + "auth": [[]], + "ledger": { + "protocol_version": 21, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Schedule" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Schedule" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 2592000 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 600 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 600 + } + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "gen_schedule" + } + ], + "data": { + "vec": [ + { + "u64": 1 + }, + { + "u64": 99 + }, + { + "i128": { + "hi": 0, + "lo": 600 + } + }, + { + "u64": 0 + }, + { + "u64": 2592000 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "gen_schedule" + } + ], + "data": { + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 2592000 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 600 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 600 + } + } + } + ] + } + } + } + }, + "failed_call": false + } + ] +} diff --git a/contracts/test_snapshots/revenue/tests/test_generate_revenue_schedule_straight_line_rule.1.json b/contracts/test_snapshots/revenue/tests/test_generate_revenue_schedule_straight_line_rule.1.json new file mode 100644 index 0000000..5627cc9 --- /dev/null +++ b/contracts/test_snapshots/revenue/tests/test_generate_revenue_schedule_straight_line_rule.1.json @@ -0,0 +1,458 @@ +{ + "generators": { + "address": 1, + "nonce": 0 + }, + "auth": [[], []], + "ledger": { + "protocol_version": 21, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "RecognitionRule" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "RecognitionRule" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "method" + }, + "val": { + "vec": [ + { + "symbol": "StraightLine" + } + ] + } + }, + { + "key": { + "symbol": "plan_id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "recognition_period" + }, + "val": { + "u64": 2592000 + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Schedule" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Schedule" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 2592000 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "set_rule" + } + ], + "data": { + "vec": [ + { + "u64": 1 + }, + { + "vec": [ + { + "symbol": "StraightLine" + } + ] + }, + { + "u64": 2592000 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "set_rule" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "gen_schedule" + } + ], + "data": { + "vec": [ + { + "u64": 1 + }, + { + "u64": 1 + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + }, + { + "u64": 0 + }, + { + "u64": 2592000 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "gen_schedule" + } + ], + "data": { + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 2592000 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + ] + } + } + } + }, + "failed_call": false + } + ] +} diff --git a/contracts/test_snapshots/revenue/tests/test_generate_revenue_schedule_usage_based_rule.1.json b/contracts/test_snapshots/revenue/tests/test_generate_revenue_schedule_usage_based_rule.1.json new file mode 100644 index 0000000..2685d05 --- /dev/null +++ b/contracts/test_snapshots/revenue/tests/test_generate_revenue_schedule_usage_based_rule.1.json @@ -0,0 +1,458 @@ +{ + "generators": { + "address": 1, + "nonce": 0 + }, + "auth": [[], []], + "ledger": { + "protocol_version": 21, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "RecognitionRule" + }, + { + "u64": 2 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "RecognitionRule" + }, + { + "u64": 2 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "method" + }, + "val": { + "vec": [ + { + "symbol": "UsageBased" + } + ] + } + }, + { + "key": { + "symbol": "plan_id" + }, + "val": { + "u64": 2 + } + }, + { + "key": { + "symbol": "recognition_period" + }, + "val": { + "u64": 2592000 + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Schedule" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Schedule" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 2592000 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 800 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 800 + } + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "set_rule" + } + ], + "data": { + "vec": [ + { + "u64": 2 + }, + { + "vec": [ + { + "symbol": "UsageBased" + } + ] + }, + { + "u64": 2592000 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "set_rule" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "gen_schedule" + } + ], + "data": { + "vec": [ + { + "u64": 1 + }, + { + "u64": 2 + }, + { + "i128": { + "hi": 0, + "lo": 800 + } + }, + { + "u64": 0 + }, + { + "u64": 2592000 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "gen_schedule" + } + ], + "data": { + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 2592000 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 800 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 800 + } + } + } + ] + } + } + } + }, + "failed_call": false + } + ] +} diff --git a/contracts/test_snapshots/revenue/tests/test_get_recognition_rule_missing_returns_none.1.json b/contracts/test_snapshots/revenue/tests/test_get_recognition_rule_missing_returns_none.1.json new file mode 100644 index 0000000..da2412a --- /dev/null +++ b/contracts/test_snapshots/revenue/tests/test_get_recognition_rule_missing_returns_none.1.json @@ -0,0 +1,121 @@ +{ + "generators": { + "address": 1, + "nonce": 0 + }, + "auth": [[]], + "ledger": { + "protocol_version": 21, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "get_rule" + } + ], + "data": { + "u64": 999 + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "get_rule" + } + ], + "data": "void" + } + } + }, + "failed_call": false + } + ] +} diff --git a/contracts/test_snapshots/revenue/tests/test_multi_element_arrangement_deferred_balances.1.json b/contracts/test_snapshots/revenue/tests/test_multi_element_arrangement_deferred_balances.1.json new file mode 100644 index 0000000..743412f --- /dev/null +++ b/contracts/test_snapshots/revenue/tests/test_multi_element_arrangement_deferred_balances.1.json @@ -0,0 +1,526 @@ +{ + "generators": { + "address": 2, + "nonce": 0 + }, + "auth": [[], [], [], [], [], []], + "ledger": { + "protocol_version": 21, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "DeferredRevenue" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "DeferredRevenue" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent", + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "RecognisedRevenue" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "RecognisedRevenue" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent", + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "update_balances" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "i128": { + "hi": 0, + "lo": 0 + } + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "update_balances" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "update_balances" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "i128": { + "hi": 0, + "lo": 0 + } + }, + { + "i128": { + "hi": 0, + "lo": 300 + } + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "update_balances" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "update_balances" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "i128": { + "hi": 0, + "lo": 0 + } + }, + { + "i128": { + "hi": 0, + "lo": 200 + } + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "update_balances" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "get_deferred" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "get_deferred" + } + ], + "data": { + "i128": { + "hi": 0, + "lo": 1000 + } + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "update_balances" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + }, + { + "i128": { + "hi": -1, + "lo": 18446744073709551116 + } + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "update_balances" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "get_deferred" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "get_deferred" + } + ], + "data": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + } + }, + "failed_call": false + } + ] +} diff --git a/contracts/test_snapshots/revenue/tests/test_recognition_rule_can_be_updated.1.json b/contracts/test_snapshots/revenue/tests/test_recognition_rule_can_be_updated.1.json new file mode 100644 index 0000000..a792b94 --- /dev/null +++ b/contracts/test_snapshots/revenue/tests/test_recognition_rule_can_be_updated.1.json @@ -0,0 +1,348 @@ +{ + "generators": { + "address": 1, + "nonce": 0 + }, + "auth": [[], [], []], + "ledger": { + "protocol_version": 21, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "RecognitionRule" + }, + { + "u64": 5 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "RecognitionRule" + }, + { + "u64": 5 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "method" + }, + "val": { + "vec": [ + { + "symbol": "UsageBased" + } + ] + } + }, + { + "key": { + "symbol": "plan_id" + }, + "val": { + "u64": 5 + } + }, + { + "key": { + "symbol": "recognition_period" + }, + "val": { + "u64": 86400 + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "set_rule" + } + ], + "data": { + "vec": [ + { + "u64": 5 + }, + { + "vec": [ + { + "symbol": "StraightLine" + } + ] + }, + { + "u64": 86400 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "set_rule" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "set_rule" + } + ], + "data": { + "vec": [ + { + "u64": 5 + }, + { + "vec": [ + { + "symbol": "UsageBased" + } + ] + }, + { + "u64": 86400 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "set_rule" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "get_rule" + } + ], + "data": { + "u64": 5 + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "get_rule" + } + ], + "data": { + "map": [ + { + "key": { + "symbol": "method" + }, + "val": { + "vec": [ + { + "symbol": "UsageBased" + } + ] + } + }, + { + "key": { + "symbol": "plan_id" + }, + "val": { + "u64": 5 + } + }, + { + "key": { + "symbol": "recognition_period" + }, + "val": { + "u64": 86400 + } + } + ] + } + } + } + }, + "failed_call": false + } + ] +} diff --git a/contracts/test_snapshots/revenue/tests/test_recognize_revenue_snapshot.1.json b/contracts/test_snapshots/revenue/tests/test_recognize_revenue_snapshot.1.json new file mode 100644 index 0000000..2118166 --- /dev/null +++ b/contracts/test_snapshots/revenue/tests/test_recognize_revenue_snapshot.1.json @@ -0,0 +1,429 @@ +{ + "generators": { + "address": 2, + "nonce": 0 + }, + "auth": [[], []], + "ledger": { + "protocol_version": 21, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Schedule" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Schedule" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 1000 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 1000 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 1000 + } + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "gen_schedule" + } + ], + "data": { + "vec": [ + { + "u64": 1 + }, + { + "u64": 99 + }, + { + "i128": { + "hi": 0, + "lo": 1000 + } + }, + { + "u64": 0 + }, + { + "u64": 1000 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "gen_schedule" + } + ], + "data": { + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 1000 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 1000 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 1000 + } + } + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "recognize" + } + ], + "data": { + "vec": [ + { + "u64": 1 + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "u64": 600 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "recognize" + } + ], + "data": { + "map": [ + { + "key": { + "symbol": "as_of" + }, + "val": { + "u64": 600 + } + }, + { + "key": { + "symbol": "deferred_revenue" + }, + "val": { + "i128": { + "hi": 0, + "lo": 400 + } + } + }, + { + "key": { + "symbol": "merchant" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "recognised_revenue" + }, + "val": { + "i128": { + "hi": 0, + "lo": 600 + } + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 1 + } + } + ] + } + } + } + }, + "failed_call": false + } + ] +} diff --git a/contracts/test_snapshots/revenue/tests/test_revenue_analytics_by_period.1.json b/contracts/test_snapshots/revenue/tests/test_revenue_analytics_by_period.1.json new file mode 100644 index 0000000..af3a12c --- /dev/null +++ b/contracts/test_snapshots/revenue/tests/test_revenue_analytics_by_period.1.json @@ -0,0 +1,873 @@ +{ + "generators": { + "address": 2, + "nonce": 0 + }, + "auth": [[], [], [], [], []], + "ledger": { + "protocol_version": 21, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "MerchantSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "MerchantSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent", + "val": { + "vec": [ + { + "u64": 1 + }, + { + "u64": 2 + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Schedule" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Schedule" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 100 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 1000 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 1000 + } + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Schedule" + }, + { + "u64": 2 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Schedule" + }, + { + "u64": 2 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 100 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 2000 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 2 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 2000 + } + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "gen_schedule" + } + ], + "data": { + "vec": [ + { + "u64": 1 + }, + { + "u64": 99 + }, + { + "i128": { + "hi": 0, + "lo": 1000 + } + }, + { + "u64": 0 + }, + { + "u64": 100 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "gen_schedule" + } + ], + "data": { + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 100 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 1000 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 1000 + } + } + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "gen_schedule" + } + ], + "data": { + "vec": [ + { + "u64": 2 + }, + { + "u64": 99 + }, + { + "i128": { + "hi": 0, + "lo": 2000 + } + }, + { + "u64": 0 + }, + { + "u64": 100 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "gen_schedule" + } + ], + "data": { + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 100 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 2000 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 2 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 2000 + } + } + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "track" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "u64": 1 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "track" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "track" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "u64": 2 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "track" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "analytics" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "u64": 100 + }, + { + "u64": 0 + }, + { + "u64": 200 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "analytics" + } + ], + "data": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 100 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 3000 + } + } + }, + { + "key": { + "symbol": "subscription_count" + }, + "val": { + "u32": 2 + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 200 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 100 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 0 + } + } + }, + { + "key": { + "symbol": "subscription_count" + }, + "val": { + "u32": 0 + } + } + ] + } + ] + } + } + } + }, + "failed_call": false + } + ] +} diff --git a/contracts/test_snapshots/revenue/tests/test_set_and_get_recognition_rule.1.json b/contracts/test_snapshots/revenue/tests/test_set_and_get_recognition_rule.1.json new file mode 100644 index 0000000..b47b2d0 --- /dev/null +++ b/contracts/test_snapshots/revenue/tests/test_set_and_get_recognition_rule.1.json @@ -0,0 +1,287 @@ +{ + "generators": { + "address": 1, + "nonce": 0 + }, + "auth": [[], []], + "ledger": { + "protocol_version": 21, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "RecognitionRule" + }, + { + "u64": 42 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "RecognitionRule" + }, + { + "u64": 42 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "method" + }, + "val": { + "vec": [ + { + "symbol": "StraightLine" + } + ] + } + }, + { + "key": { + "symbol": "plan_id" + }, + "val": { + "u64": 42 + } + }, + { + "key": { + "symbol": "recognition_period" + }, + "val": { + "u64": 86400 + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "set_rule" + } + ], + "data": { + "vec": [ + { + "u64": 42 + }, + { + "vec": [ + { + "symbol": "StraightLine" + } + ] + }, + { + "u64": 86400 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "set_rule" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "get_rule" + } + ], + "data": { + "u64": 42 + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "get_rule" + } + ], + "data": { + "map": [ + { + "key": { + "symbol": "method" + }, + "val": { + "vec": [ + { + "symbol": "StraightLine" + } + ] + } + }, + { + "key": { + "symbol": "plan_id" + }, + "val": { + "u64": 42 + } + }, + { + "key": { + "symbol": "recognition_period" + }, + "val": { + "u64": 86400 + } + } + ] + } + } + } + }, + "failed_call": false + } + ] +} diff --git a/contracts/test_snapshots/revenue/tests/test_track_merchant_subscription_accumulates.1.json b/contracts/test_snapshots/revenue/tests/test_track_merchant_subscription_accumulates.1.json new file mode 100644 index 0000000..2dc8241 --- /dev/null +++ b/contracts/test_snapshots/revenue/tests/test_track_merchant_subscription_accumulates.1.json @@ -0,0 +1,834 @@ +{ + "generators": { + "address": 2, + "nonce": 0 + }, + "auth": [[], [], [], [], []], + "ledger": { + "protocol_version": 21, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "MerchantSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "MerchantSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent", + "val": { + "vec": [ + { + "u64": 10 + }, + { + "u64": 20 + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Schedule" + }, + { + "u64": 10 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Schedule" + }, + { + "u64": 10 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 100 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 100 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 10 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 100 + } + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Schedule" + }, + { + "u64": 20 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Schedule" + }, + { + "u64": 20 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 100 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 200 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 20 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 200 + } + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "track" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "u64": 10 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "track" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "track" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "u64": 20 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "track" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "gen_schedule" + } + ], + "data": { + "vec": [ + { + "u64": 10 + }, + { + "u64": 99 + }, + { + "i128": { + "hi": 0, + "lo": 100 + } + }, + { + "u64": 0 + }, + { + "u64": 100 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "gen_schedule" + } + ], + "data": { + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 100 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 100 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 10 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 100 + } + } + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "gen_schedule" + } + ], + "data": { + "vec": [ + { + "u64": 20 + }, + { + "u64": 99 + }, + { + "i128": { + "hi": 0, + "lo": 200 + } + }, + { + "u64": 0 + }, + { + "u64": 100 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "gen_schedule" + } + ], + "data": { + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 100 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 200 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 20 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 200 + } + } + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "analytics" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "u64": 100 + }, + { + "u64": 0 + }, + { + "u64": 100 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "analytics" + } + ], + "data": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 100 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 300 + } + } + }, + { + "key": { + "symbol": "subscription_count" + }, + "val": { + "u32": 2 + } + } + ] + } + ] + } + } + } + }, + "failed_call": false + } + ] +} diff --git a/contracts/test_snapshots/test/test_admin_override_bypass.1.json b/contracts/test_snapshots/test/test_admin_override_bypass.1.json new file mode 100644 index 0000000..988c14e --- /dev/null +++ b/contracts/test_snapshots/test/test_admin_override_bypass.1.json @@ -0,0 +1,1272 @@ +{ + "generators": { + "address": 5, + "nonce": 0 + }, + "auth": [ + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "initialize", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "create_plan", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "string": "Basic" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + }, + { + "vec": [ + { + "symbol": "Monthly" + } + ] + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "set_rate_limit", + "args": [ + { + "string": "create_plan" + }, + { + "u64": 100 + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "create_plan", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "string": "Admin Plan A" + }, + { + "i128": { + "hi": 0, + "lo": 1 + } + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "vec": [ + { + "symbol": "Monthly" + } + ] + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "create_plan", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "string": "Admin Plan B" + }, + { + "i128": { + "hi": 0, + "lo": 2 + } + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "vec": [ + { + "symbol": "Monthly" + } + ] + } + ] + } + }, + "sub_invocations": [] + } + ] + ] + ], + "ledger": { + "protocol_version": 21, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "MerchantPlans" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "MerchantPlans" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent", + "val": { + "vec": [ + { + "u64": 2 + }, + { + "u64": 3 + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "MerchantPlans" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "MerchantPlans" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent", + "val": { + "vec": [ + { + "u64": 1 + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Plan" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Plan" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "active" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "interval" + }, + "val": { + "vec": [ + { + "symbol": "Monthly" + } + ] + } + }, + { + "key": { + "symbol": "merchant" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + }, + { + "key": { + "symbol": "name" + }, + "val": { + "string": "Basic" + } + }, + { + "key": { + "symbol": "price" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + }, + { + "key": { + "symbol": "subscriber_count" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Plan" + }, + { + "u64": 2 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Plan" + }, + { + "u64": 2 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "active" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "u64": 2 + } + }, + { + "key": { + "symbol": "interval" + }, + "val": { + "vec": [ + { + "symbol": "Monthly" + } + ] + } + }, + { + "key": { + "symbol": "merchant" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "name" + }, + "val": { + "string": "Admin Plan A" + } + }, + { + "key": { + "symbol": "price" + }, + "val": { + "i128": { + "hi": 0, + "lo": 1 + } + } + }, + { + "key": { + "symbol": "subscriber_count" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Plan" + }, + { + "u64": 3 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Plan" + }, + { + "u64": 3 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "active" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "u64": 3 + } + }, + { + "key": { + "symbol": "interval" + }, + "val": { + "vec": [ + { + "symbol": "Monthly" + } + ] + } + }, + { + "key": { + "symbol": "merchant" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "name" + }, + "val": { + "string": "Admin Plan B" + } + }, + { + "key": { + "symbol": "price" + }, + "val": { + "i128": { + "hi": 0, + "lo": 2 + } + } + }, + { + "key": { + "symbol": "subscriber_count" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Admin" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "vec": [ + { + "symbol": "PlanCount" + } + ] + }, + "val": { + "u64": 3 + } + }, + { + "key": { + "vec": [ + { + "symbol": "RateLimit" + }, + { + "string": "create_plan" + } + ] + }, + "val": { + "u64": 100 + } + }, + { + "key": { + "vec": [ + { + "symbol": "SubscriptionCount" + } + ] + }, + "val": { + "u64": 0 + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 1033654523790656264 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 1033654523790656264 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 2032731177588607455 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 2032731177588607455 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 4837995959683129791 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 4837995959683129791 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "initialize" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "initialize" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "create_plan" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "string": "Basic" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + }, + { + "vec": [ + { + "symbol": "Monthly" + } + ] + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "create_plan" + } + ], + "data": { + "u64": 1 + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "set_rate_limit" + } + ], + "data": { + "vec": [ + { + "string": "create_plan" + }, + { + "u64": 100 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "set_rate_limit" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "create_plan" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "string": "Admin Plan A" + }, + { + "i128": { + "hi": 0, + "lo": 1 + } + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "vec": [ + { + "symbol": "Monthly" + } + ] + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "create_plan" + } + ], + "data": { + "u64": 2 + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "create_plan" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "string": "Admin Plan B" + }, + { + "i128": { + "hi": 0, + "lo": 2 + } + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "vec": [ + { + "symbol": "Monthly" + } + ] + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "create_plan" + } + ], + "data": { + "u64": 3 + } + } + } + }, + "failed_call": false + } + ] +} diff --git a/contracts/test_snapshots/test/test_auto_resume.1.json b/contracts/test_snapshots/test/test_auto_resume.1.json index 69d9472..ee062a5 100644 --- a/contracts/test_snapshots/test/test_auto_resume.1.json +++ b/contracts/test_snapshots/test/test_auto_resume.1.json @@ -141,6 +141,54 @@ "min_temp_entry_ttl": 16, "max_entry_ttl": 6312000, "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "DeferredRevenue" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "DeferredRevenue" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent", + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], [ { "contract_data": { @@ -190,6 +238,55 @@ 4095 ] ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "MerchantSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "MerchantSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent", + "val": { + "vec": [ + { + "u64": 1 + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], [ { "contract_data": { @@ -315,6 +412,167 @@ 4095 ] ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "RecognisedRevenue" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "RecognisedRevenue" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent", + "val": { + "i128": { + "hi": 0, + "lo": 0 + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Schedule" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Schedule" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 5356800 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 2764800 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], [ { "contract_data": { @@ -352,6 +610,14 @@ "durability": "persistent", "val": { "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 1 + } + }, { "key": { "symbol": "id" @@ -439,6 +705,14 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" } }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 100000 + } + }, { "key": { "symbol": "total_paid" @@ -1106,6 +1380,14 @@ ], "data": { "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 0 + } + }, { "key": { "symbol": "id" @@ -1193,6 +1475,14 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" } }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 0 + } + }, { "key": { "symbol": "total_paid" @@ -1254,6 +1544,14 @@ ], "data": { "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 0 + } + }, { "key": { "symbol": "id" @@ -1341,6 +1639,14 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" } }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 0 + } + }, { "key": { "symbol": "total_paid" @@ -1445,6 +1751,45 @@ }, "failed_call": false }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "string": "subscription_charged" + }, + { + "u64": 1 + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + }, + { + "u64": 100000 + }, + { + "u64": 2764800 + } + ] + } + } + } + }, + "failed_call": false + }, { "event": { "ext": "v0", @@ -1509,6 +1854,14 @@ ], "data": { "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 1 + } + }, { "key": { "symbol": "id" @@ -1596,6 +1949,14 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" } }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 100000 + } + }, { "key": { "symbol": "total_paid" diff --git a/contracts/test_snapshots/test/test_cancel_subscription.1.json b/contracts/test_snapshots/test/test_cancel_subscription.1.json index e9d09cf..ac99df5 100644 --- a/contracts/test_snapshots/test/test_cancel_subscription.1.json +++ b/contracts/test_snapshots/test/test_cancel_subscription.1.json @@ -329,6 +329,14 @@ "durability": "persistent", "val": { "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 0 + } + }, { "key": { "symbol": "id" @@ -416,6 +424,14 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" } }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 0 + } + }, { "key": { "symbol": "total_paid" @@ -1014,6 +1030,14 @@ ], "data": { "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 0 + } + }, { "key": { "symbol": "id" @@ -1101,6 +1125,14 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" } }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 0 + } + }, { "key": { "symbol": "total_paid" diff --git a/contracts/test_snapshots/test/test_charge_paused_subscription.1.json b/contracts/test_snapshots/test/test_charge_paused_subscription.1.json index 88fb6d0..6f901a3 100644 --- a/contracts/test_snapshots/test/test_charge_paused_subscription.1.json +++ b/contracts/test_snapshots/test/test_charge_paused_subscription.1.json @@ -328,6 +328,14 @@ "durability": "persistent", "val": { "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 0 + } + }, { "key": { "symbol": "id" @@ -415,6 +423,14 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" } }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 0 + } + }, { "key": { "symbol": "total_paid" @@ -1044,7 +1060,7 @@ "data": { "vec": [ { - "string": "caught panic 'Subscription not active' from contract function 'Symbol(obj#231)'" + "string": "caught panic 'Subscription not active' from contract function 'Symbol(obj#277)'" }, { "u64": 1 diff --git a/contracts/test_snapshots/test/test_charge_subscription_not_due.1.json b/contracts/test_snapshots/test/test_charge_subscription_not_due.1.json index 40921dc..4fb5166 100644 --- a/contracts/test_snapshots/test/test_charge_subscription_not_due.1.json +++ b/contracts/test_snapshots/test/test_charge_subscription_not_due.1.json @@ -306,6 +306,14 @@ "durability": "persistent", "val": { "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 0 + } + }, { "key": { "symbol": "id" @@ -393,6 +401,14 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" } }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 0 + } + }, { "key": { "symbol": "total_paid" @@ -902,7 +918,7 @@ "data": { "vec": [ { - "string": "caught panic 'Payment not yet due' from contract function 'Symbol(obj#169)'" + "string": "caught panic 'Payment not yet due' from contract function 'Symbol(obj#185)'" }, { "u64": 1 diff --git a/contracts/test_snapshots/test/test_create_plan_and_subscribe.1.json b/contracts/test_snapshots/test/test_create_plan_and_subscribe.1.json index c25ec6c..65eee24 100644 --- a/contracts/test_snapshots/test/test_create_plan_and_subscribe.1.json +++ b/contracts/test_snapshots/test/test_create_plan_and_subscribe.1.json @@ -309,6 +309,14 @@ "durability": "persistent", "val": { "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 0 + } + }, { "key": { "symbol": "id" @@ -396,6 +404,14 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" } }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 0 + } + }, { "key": { "symbol": "total_paid" @@ -1051,6 +1067,14 @@ ], "data": { "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 0 + } + }, { "key": { "symbol": "id" @@ -1138,6 +1162,14 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" } }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 0 + } + }, { "key": { "symbol": "total_paid" diff --git a/contracts/test_snapshots/test/test_double_subscribe.1.json b/contracts/test_snapshots/test/test_double_subscribe.1.json index 2e95172..1c1d149 100644 --- a/contracts/test_snapshots/test/test_double_subscribe.1.json +++ b/contracts/test_snapshots/test/test_double_subscribe.1.json @@ -306,6 +306,14 @@ "durability": "persistent", "val": { "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 0 + } + }, { "key": { "symbol": "id" @@ -393,6 +401,14 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" } }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 0 + } + }, { "key": { "symbol": "total_paid" diff --git a/contracts/test_snapshots/test/test_non_subscriber_cannot_cancel.1.json b/contracts/test_snapshots/test/test_non_subscriber_cannot_cancel.1.json index 3facf9b..a151522 100644 --- a/contracts/test_snapshots/test/test_non_subscriber_cannot_cancel.1.json +++ b/contracts/test_snapshots/test/test_non_subscriber_cannot_cancel.1.json @@ -306,6 +306,14 @@ "durability": "persistent", "val": { "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 0 + } + }, { "key": { "symbol": "id" @@ -393,6 +401,14 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" } }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 0 + } + }, { "key": { "symbol": "total_paid" @@ -909,7 +925,7 @@ "data": { "vec": [ { - "string": "caught panic 'Only subscriber can cancel' from contract function 'Symbol(obj#171)'" + "string": "caught panic 'Only subscriber can cancel' from contract function 'Symbol(obj#187)'" }, { "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" diff --git a/contracts/test_snapshots/test/test_pause_and_resume.1.json b/contracts/test_snapshots/test/test_pause_and_resume.1.json index b730c01..b0c90e5 100644 --- a/contracts/test_snapshots/test/test_pause_and_resume.1.json +++ b/contracts/test_snapshots/test/test_pause_and_resume.1.json @@ -352,6 +352,14 @@ "durability": "persistent", "val": { "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 0 + } + }, { "key": { "symbol": "id" @@ -439,6 +447,14 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" } }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 0 + } + }, { "key": { "symbol": "total_paid" @@ -1016,6 +1032,14 @@ ], "data": { "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 0 + } + }, { "key": { "symbol": "id" @@ -1103,6 +1127,14 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" } }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 0 + } + }, { "key": { "symbol": "total_paid" @@ -1251,6 +1283,14 @@ ], "data": { "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 0 + } + }, { "key": { "symbol": "id" @@ -1338,6 +1378,14 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" } }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 0 + } + }, { "key": { "symbol": "total_paid" @@ -1476,6 +1524,14 @@ ], "data": { "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 0 + } + }, { "key": { "symbol": "id" @@ -1563,6 +1619,14 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" } }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 0 + } + }, { "key": { "symbol": "total_paid" diff --git a/contracts/test_snapshots/test/test_pause_by_subscriber_limit_enforced.1.json b/contracts/test_snapshots/test/test_pause_by_subscriber_limit_enforced.1.json index 8314d67..9da6079 100644 --- a/contracts/test_snapshots/test/test_pause_by_subscriber_limit_enforced.1.json +++ b/contracts/test_snapshots/test/test_pause_by_subscriber_limit_enforced.1.json @@ -306,6 +306,14 @@ "durability": "persistent", "val": { "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 0 + } + }, { "key": { "symbol": "id" @@ -393,6 +401,14 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" } }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 0 + } + }, { "key": { "symbol": "total_paid" @@ -912,7 +928,7 @@ "data": { "vec": [ { - "string": "caught panic 'Pause duration exceeds limit' from contract function 'Symbol(obj#169)'" + "string": "caught panic 'Pause duration exceeds limit' from contract function 'Symbol(obj#185)'" }, { "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" diff --git a/contracts/test_snapshots/test/test_plan_deactivation_existing_subscribers_unaffected.1.json b/contracts/test_snapshots/test/test_plan_deactivation_existing_subscribers_unaffected.1.json index 8194c7f..0f3b396 100644 --- a/contracts/test_snapshots/test/test_plan_deactivation_existing_subscribers_unaffected.1.json +++ b/contracts/test_snapshots/test/test_plan_deactivation_existing_subscribers_unaffected.1.json @@ -351,6 +351,14 @@ "durability": "persistent", "val": { "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 0 + } + }, { "key": { "symbol": "id" @@ -438,6 +446,14 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" } }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 0 + } + }, { "key": { "symbol": "total_paid" @@ -1285,6 +1301,14 @@ ], "data": { "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 0 + } + }, { "key": { "symbol": "id" @@ -1372,6 +1396,14 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" } }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 0 + } + }, { "key": { "symbol": "total_paid" diff --git a/contracts/test_snapshots/test/test_rate_limit_enforced_for_subscribe.1.json b/contracts/test_snapshots/test/test_rate_limit_enforced_for_subscribe.1.json new file mode 100644 index 0000000..d391b05 --- /dev/null +++ b/contracts/test_snapshots/test/test_rate_limit_enforced_for_subscribe.1.json @@ -0,0 +1,1214 @@ +{ + "generators": { + "address": 5, + "nonce": 0 + }, + "auth": [ + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "initialize", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "create_plan", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "string": "Basic" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + }, + { + "vec": [ + { + "symbol": "Monthly" + } + ] + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "set_rate_limit", + "args": [ + { + "string": "subscribe" + }, + { + "u64": 100 + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "subscribe", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "u64": 1 + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 21, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "MerchantPlans" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "MerchantPlans" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent", + "val": { + "vec": [ + { + "u64": 1 + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Plan" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Plan" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "active" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "interval" + }, + "val": { + "vec": [ + { + "symbol": "Monthly" + } + ] + } + }, + { + "key": { + "symbol": "merchant" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + }, + { + "key": { + "symbol": "name" + }, + "val": { + "string": "Basic" + } + }, + { + "key": { + "symbol": "price" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + }, + { + "key": { + "symbol": "subscriber_count" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Subscription" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Subscription" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "last_charged_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "next_charge_at" + }, + "val": { + "u64": 2592000 + } + }, + { + "key": { + "symbol": "pause_duration" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "paused_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "plan_id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "refund_requested_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 0 + } + } + }, + { + "key": { + "symbol": "started_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "vec": [ + { + "symbol": "Active" + } + ] + } + }, + { + "key": { + "symbol": "subscriber" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "total_paid" + }, + "val": { + "i128": { + "hi": 0, + "lo": 0 + } + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "UserSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "UserSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + "durability": "persistent", + "val": { + "vec": [ + { + "u64": 1 + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Admin" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "vec": [ + { + "symbol": "LastCall" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "string": "subscribe" + } + ] + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "vec": [ + { + "symbol": "PlanCount" + } + ] + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "vec": [ + { + "symbol": "RateLimit" + }, + { + "string": "subscribe" + } + ] + }, + "val": { + "u64": 100 + } + }, + { + "key": { + "vec": [ + { + "symbol": "SubscriptionCount" + } + ] + }, + "val": { + "u64": 1 + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 1033654523790656264 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 1033654523790656264 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": { + "ledger_key_nonce": { + "nonce": 4837995959683129791 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": { + "ledger_key_nonce": { + "nonce": 4837995959683129791 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "initialize" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "initialize" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "create_plan" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "string": "Basic" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + }, + { + "vec": [ + { + "symbol": "Monthly" + } + ] + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "create_plan" + } + ], + "data": { + "u64": 1 + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "set_rate_limit" + } + ], + "data": { + "vec": [ + { + "string": "subscribe" + }, + { + "u64": 100 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "set_rate_limit" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "subscribe" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "u64": 1 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "subscribe" + } + ], + "data": { + "u64": 1 + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "subscribe" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "u64": 1 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "string": "rate_limit_violation" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ], + "data": { + "vec": [ + { + "string": "subscribe" + }, + { + "u64": 0 + }, + { + "u64": 0 + }, + { + "u64": 100 + } + ] + } + } + } + }, + "failed_call": true + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "log" + } + ], + "data": { + "vec": [ + { + "string": "caught panic 'Rate limited: please wait before calling this function again' from contract function 'Symbol(subscribe)'" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "u64": 1 + } + ] + } + } + } + }, + "failed_call": true + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "error" + }, + { + "error": { + "wasm_vm": "invalid_action" + } + } + ], + "data": { + "string": "caught error from function" + } + } + } + }, + "failed_call": true + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "error" + }, + { + "error": { + "wasm_vm": "invalid_action" + } + } + ], + "data": { + "vec": [ + { + "string": "contract call failed" + }, + { + "symbol": "subscribe" + }, + { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "u64": 1 + } + ] + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "error" + }, + { + "error": { + "wasm_vm": "invalid_action" + } + } + ], + "data": { + "string": "escalating error to panic" + } + } + } + }, + "failed_call": false + } + ] +} diff --git a/contracts/test_snapshots/test/test_refund_flow.1.json b/contracts/test_snapshots/test/test_refund_flow.1.json index f7a7d25..e39c75a 100644 --- a/contracts/test_snapshots/test/test_refund_flow.1.json +++ b/contracts/test_snapshots/test/test_refund_flow.1.json @@ -160,6 +160,54 @@ "min_temp_entry_ttl": 16, "max_entry_ttl": 6312000, "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "DeferredRevenue" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "DeferredRevenue" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent", + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], [ { "contract_data": { @@ -209,6 +257,55 @@ 4095 ] ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "MerchantSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "MerchantSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent", + "val": { + "vec": [ + { + "u64": 1 + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], [ { "contract_data": { @@ -334,6 +431,167 @@ 4095 ] ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "RecognisedRevenue" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "RecognisedRevenue" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent", + "val": { + "i128": { + "hi": 0, + "lo": 0 + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Schedule" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Schedule" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 5270400 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 2678400 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], [ { "contract_data": { @@ -371,6 +629,14 @@ "durability": "persistent", "val": { "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 1 + } + }, { "key": { "symbol": "id" @@ -458,6 +724,14 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" } }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 100000 + } + }, { "key": { "symbol": "total_paid" @@ -1111,6 +1385,45 @@ }, "failed_call": false }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "string": "subscription_charged" + }, + { + "u64": 1 + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + }, + { + "u64": 100000 + }, + { + "u64": 2678400 + } + ] + } + } + } + }, + "failed_call": false + }, { "event": { "ext": "v0", @@ -1175,6 +1488,14 @@ ], "data": { "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 1 + } + }, { "key": { "symbol": "id" @@ -1262,6 +1583,14 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" } }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 100000 + } + }, { "key": { "symbol": "total_paid" @@ -1413,6 +1742,14 @@ ], "data": { "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 1 + } + }, { "key": { "symbol": "id" @@ -1500,6 +1837,14 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" } }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 100000 + } + }, { "key": { "symbol": "total_paid" @@ -1641,6 +1986,14 @@ ], "data": { "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 1 + } + }, { "key": { "symbol": "id" @@ -1728,6 +2081,14 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" } }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 100000 + } + }, { "key": { "symbol": "total_paid" diff --git a/contracts/test_snapshots/test/test_subscription_transfer_flow.1.json b/contracts/test_snapshots/test/test_subscription_transfer_flow.1.json new file mode 100644 index 0000000..d53e3de --- /dev/null +++ b/contracts/test_snapshots/test/test_subscription_transfer_flow.1.json @@ -0,0 +1,1472 @@ +{ + "generators": { + "address": 6, + "nonce": 0 + }, + "auth": [ + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "initialize", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "create_plan", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "string": "Basic" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + }, + { + "vec": [ + { + "symbol": "Monthly" + } + ] + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "subscribe", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "u64": 1 + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "request_transfer", + "args": [ + { + "u64": 1 + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "accept_transfer", + "args": [ + { + "u64": 1 + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [], + [], + [] + ], + "ledger": { + "protocol_version": 21, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "MerchantPlans" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "MerchantPlans" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent", + "val": { + "vec": [ + { + "u64": 1 + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Plan" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Plan" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "active" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "interval" + }, + "val": { + "vec": [ + { + "symbol": "Monthly" + } + ] + } + }, + { + "key": { + "symbol": "merchant" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + }, + { + "key": { + "symbol": "name" + }, + "val": { + "string": "Basic" + } + }, + { + "key": { + "symbol": "price" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + }, + { + "key": { + "symbol": "subscriber_count" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Subscription" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Subscription" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "last_charged_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "next_charge_at" + }, + "val": { + "u64": 2592000 + } + }, + { + "key": { + "symbol": "pause_duration" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "paused_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "plan_id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "refund_requested_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 0 + } + } + }, + { + "key": { + "symbol": "started_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "vec": [ + { + "symbol": "Active" + } + ] + } + }, + { + "key": { + "symbol": "subscriber" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + } + }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "total_paid" + }, + "val": { + "i128": { + "hi": 0, + "lo": 0 + } + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "UserSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "UserSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + "durability": "persistent", + "val": { + "vec": [] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "UserSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "UserSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + } + ] + }, + "durability": "persistent", + "val": { + "vec": [ + { + "u64": 1 + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Admin" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "vec": [ + { + "symbol": "PlanCount" + } + ] + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "vec": [ + { + "symbol": "SubscriptionCount" + } + ] + }, + "val": { + "u64": 1 + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": { + "ledger_key_nonce": { + "nonce": 1033654523790656264 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": { + "ledger_key_nonce": { + "nonce": 1033654523790656264 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": { + "ledger_key_nonce": { + "nonce": 4837995959683129791 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": { + "ledger_key_nonce": { + "nonce": 4837995959683129791 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4", + "key": { + "ledger_key_nonce": { + "nonce": 2032731177588607455 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4", + "key": { + "ledger_key_nonce": { + "nonce": 2032731177588607455 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "initialize" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "initialize" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "create_plan" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "string": "Basic" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + }, + { + "vec": [ + { + "symbol": "Monthly" + } + ] + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "create_plan" + } + ], + "data": { + "u64": 1 + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "subscribe" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "u64": 1 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "subscribe" + } + ], + "data": { + "u64": 1 + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "request_transfer" + } + ], + "data": { + "vec": [ + { + "u64": 1 + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "string": "transfer_requested" + }, + { + "u64": 1 + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "request_transfer" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "accept_transfer" + } + ], + "data": { + "vec": [ + { + "u64": 1 + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "string": "transfer_accepted" + }, + { + "u64": 1 + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "accept_transfer" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "get_subscription" + } + ], + "data": { + "u64": 1 + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "get_subscription" + } + ], + "data": { + "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "last_charged_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "next_charge_at" + }, + "val": { + "u64": 2592000 + } + }, + { + "key": { + "symbol": "pause_duration" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "paused_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "plan_id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "refund_requested_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 0 + } + } + }, + { + "key": { + "symbol": "started_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "vec": [ + { + "symbol": "Active" + } + ] + } + }, + { + "key": { + "symbol": "subscriber" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + } + }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "total_paid" + }, + "val": { + "i128": { + "hi": 0, + "lo": 0 + } + } + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "get_user_subscriptions" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "get_user_subscriptions" + } + ], + "data": { + "vec": [] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "get_user_subscriptions" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "get_user_subscriptions" + } + ], + "data": { + "vec": [ + { + "u64": 1 + } + ] + } + } + } + }, + "failed_call": false + } + ] +} diff --git a/src/navigation/AppNavigator.tsx b/src/navigation/AppNavigator.tsx index 1c9a5df..00fba1c 100644 --- a/src/navigation/AppNavigator.tsx +++ b/src/navigation/AppNavigator.tsx @@ -11,6 +11,7 @@ import CryptoPaymentScreen from '../screens/CryptoPaymentScreen'; import SubscriptionDetailScreen from '../screens/SubscriptionDetailScreen'; import AnalyticsScreen from '../screens/AnalyticsScreen'; import SettingsScreen from '../screens/SettingsScreen'; +import RevenueReportScreen from '../screens/RevenueReportScreen'; import { colors } from '../utils/constants'; import { RootStackParamList, TabParamList } from './types'; @@ -95,6 +96,16 @@ const TabNavigator = () => ( ), }} /> + ( + 💰 + ), + }} + /> = { + month: 30 * MS_PER_DAY, + quarter: 90 * MS_PER_DAY, + year: 365 * MS_PER_DAY, +}; + +const BUCKET_MS: Record = { + month: 7 * MS_PER_DAY, // weekly buckets + quarter: 30 * MS_PER_DAY, // monthly buckets + year: 30 * MS_PER_DAY, // monthly buckets +}; + +const BUCKET_LABELS: Record = { + month: ['W1', 'W2', 'W3', 'W4', 'W5'], + quarter: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + year: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], +}; + +const RevenueReportScreen: React.FC = () => { + const { subscriptions } = useSubscriptionStore(); + const { + rules, + schedules, + deferredRevenue, + setRecognitionRule, + removeRecognitionRule, + generateRevenueSchedule, + getRevenueAnalyticsByPeriod, + } = useAccountingStore(); + + const [periodRange, setPeriodRange] = useState('month'); + const [configSubId, setConfigSubId] = useState(null); + + // ── Revenue recognition snapshots ───────────────────────────────────────── + // Computed directly from state slices — no store action calls inside memos. + + const recognitionData = useMemo(() => { + const now = Date.now(); + return subscriptions + .filter((s) => s.isActive) + .map((s) => { + const schedule = schedules[s.id]; + const { recognised, deferred } = schedule + ? splitRecognisedDeferred(schedule, now) + : { recognised: 0, deferred: 0 }; + return { + id: s.id, + name: s.name, + recognised, + deferred, + total: recognised + deferred, + method: rules[s.id]?.method ?? ('straight-line' as RecognitionMethod), + }; + }); + }, [subscriptions, schedules, rules]); + + const totalRecognised = useMemo( + () => recognitionData.reduce((sum, d) => sum + d.recognised, 0), + [recognitionData] + ); + + // Read deferred balance directly from state — always reactive. + const totalDeferred = deferredRevenue['default'] ?? 0; + + // ── Analytics chart data ────────────────────────────────────────────────── + + const chartData = useMemo(() => { + const now = Date.now(); + const from = now - PERIOD_RANGE_MS[periodRange]; + const bucketMs = BUCKET_MS[periodRange]; + const analytics = getRevenueAnalyticsByPeriod(bucketMs, from, now); + return analytics.map((bucket, i) => ({ + label: BUCKET_LABELS[periodRange][i % BUCKET_LABELS[periodRange].length], + amount: bucket.recognisedAmount, + count: bucket.subscriptionCount, + })); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [periodRange, schedules, getRevenueAnalyticsByPeriod]); + + const maxAmount = Math.max(...chartData.map((d) => d.amount), 1); + const barWidth = Math.max(4, (CHART_WIDTH - 40) / Math.max(chartData.length, 1) - 6); + + // ── Config helpers ──────────────────────────────────────────────────────── + + const handleSimulateCharge = useCallback( + (subId: string) => { + const sub = subscriptions.find((s) => s.id === subId); + if (!sub) return; + generateRevenueSchedule(sub.id, sub.price, Date.now(), sub.billingCycle); + Alert.alert('Charge simulated', `Revenue schedule generated for "${sub.name}".`); + }, + [subscriptions, generateRevenueSchedule] + ); + + const handleToggleMethod = useCallback( + (subId: string, current: RecognitionMethod) => { + const next: RecognitionMethod = current === 'straight-line' ? 'usage-based' : 'straight-line'; + const sub = subscriptions.find((s) => s.id === subId); + if (!sub) return; + const intervalMs = billingCycleToMs(sub.billingCycle); + setRecognitionRule({ + subscriptionId: subId, + method: next, + recognitionPeriodMs: intervalMs, + }); + }, + [subscriptions, setRecognitionRule] + ); + + const handleRemoveRule = useCallback( + (subId: string) => { + removeRecognitionRule(subId); + if (configSubId === subId) setConfigSubId(null); + }, + [removeRecognitionRule, configSubId] + ); + + // ── Render ──────────────────────────────────────────────────────────────── + + if (!subscriptions.length) { + return ( + + + 💰 + No Revenue Data + Add subscriptions to track revenue recognition. + + + ); + } + + return ( + + + {/* Header */} + + Revenue Report + Recognition & deferred revenue + + + {/* Summary cards */} + + + Recognised + + ${totalRecognised.toFixed(2)} + + + + Deferred + + ${totalDeferred.toFixed(2)} + + + + + {/* Period selector */} + + {(['month', 'quarter', 'year'] as PeriodRange[]).map((p) => ( + setPeriodRange(p)}> + + {p.charAt(0).toUpperCase() + p.slice(1)} + + + ))} + + + {/* Revenue chart */} + + Recognised Revenue by Period + {chartData.every((d) => d.amount === 0) ? ( + + No recognised revenue yet. Simulate a charge below. + + ) : ( + + + + {chartData.map((data, index) => { + const barHeight = Math.max(2, (data.amount / maxAmount) * (CHART_HEIGHT - 60)); + const x = 35 + index * (barWidth + 6); + const y = CHART_HEIGHT - 30 - barHeight; + return ( + + + + {data.label} + + {data.amount > 0 && ( + + ${data.amount.toFixed(0)} + + )} + + ); + })} + + )} + + + {/* Per-subscription recognition table */} + + Subscription Recognition + {recognitionData.map((item) => ( + + + + {item.name} + + {item.method} + + + ${item.recognised.toFixed(2)} + ↓ ${item.deferred.toFixed(2)} + + + ))} + + + {/* Revenue configuration */} + + Revenue Configuration + + Configure recognition method per subscription. Tap a row to expand. + + {subscriptions + .filter((s) => s.isActive) + .map((sub) => { + const rule = rules[sub.id]; + const method: RecognitionMethod = rule?.method ?? 'straight-line'; + const isExpanded = configSubId === sub.id; + + return ( + + setConfigSubId(isExpanded ? null : sub.id)}> + + {sub.name} + + {isExpanded ? '▲' : '▼'} + + + {isExpanded && ( + + {/* Method toggle */} + + Usage-based + handleToggleMethod(sub.id, method)} + trackColor={{ false: colors.border, true: colors.primary }} + thumbColor={colors.surface} + /> + + + {method === 'straight-line' + ? 'Revenue spread evenly across the billing period (ASC 606 default).' + : 'Revenue deferred until actual usage is reported by the merchant.'} + + + {/* Simulate charge */} + handleSimulateCharge(sub.id)}> + Simulate Charge + + + {/* Remove rule */} + {rule && ( + handleRemoveRule(sub.id)}> + Reset to Default + + )} + + )} + + ); + })} + + + + ); +}; + +// ── Styles ──────────────────────────────────────────────────────────────────── + +const styles = StyleSheet.create({ + container: { flex: 1, backgroundColor: colors.background }, + scrollView: { flex: 1 }, + header: { padding: spacing.lg, paddingBottom: spacing.md }, + title: { ...typography.h1, color: colors.text, marginBottom: spacing.xs }, + subtitle: { ...typography.body, color: colors.textSecondary }, + + summaryRow: { + flexDirection: 'row', + paddingHorizontal: spacing.lg, + marginBottom: spacing.md, + gap: spacing.md, + }, + summaryCard: { flex: 1, alignItems: 'center' }, + summaryLabel: { ...typography.caption, color: colors.textSecondary, marginBottom: spacing.xs }, + summaryValue: { ...typography.h2, fontWeight: '700' }, + + periodRow: { + flexDirection: 'row', + paddingHorizontal: spacing.lg, + marginBottom: spacing.md, + gap: spacing.sm, + }, + periodBtn: { + flex: 1, + paddingVertical: spacing.sm, + borderRadius: borderRadius.md, + backgroundColor: colors.surface, + borderWidth: 1, + borderColor: colors.border, + alignItems: 'center', + }, + periodBtnActive: { backgroundColor: colors.primary, borderColor: colors.primary }, + periodBtnText: { ...typography.body, color: colors.text }, + periodBtnTextActive: { color: colors.text, fontWeight: '600' }, + + chartCard: { marginHorizontal: spacing.lg, marginBottom: spacing.md }, + chartTitle: { ...typography.h3, color: colors.text, marginBottom: spacing.md }, + noDataText: { + ...typography.body, + color: colors.textSecondary, + textAlign: 'center', + paddingVertical: spacing.lg, + }, + + tableCard: { marginHorizontal: spacing.lg, marginBottom: spacing.md }, + tableRow: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingVertical: spacing.sm, + borderBottomWidth: 1, + borderBottomColor: colors.border, + }, + tableLeft: { flex: 1, marginRight: spacing.md }, + tableName: { ...typography.body, color: colors.text, fontWeight: '600' }, + tableMethod: { ...typography.caption, color: colors.textSecondary, marginTop: 2 }, + tableRight: { alignItems: 'flex-end' }, + tableRecognised: { ...typography.body, color: colors.primary, fontWeight: '600' }, + tableDeferred: { ...typography.caption, color: '#FF9800' }, + + configCard: { marginHorizontal: spacing.lg, marginBottom: spacing.xl }, + configHint: { + ...typography.caption, + color: colors.textSecondary, + marginBottom: spacing.md, + }, + configRow: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingVertical: spacing.sm, + borderBottomWidth: 1, + borderBottomColor: colors.border, + }, + configName: { ...typography.body, color: colors.text, flex: 1 }, + configChevron: { ...typography.body, color: colors.textSecondary, marginLeft: spacing.sm }, + configDetail: { + backgroundColor: colors.background, + borderRadius: borderRadius.md, + padding: spacing.md, + marginBottom: spacing.sm, + }, + configDetailRow: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: spacing.sm, + }, + configDetailLabel: { ...typography.body, color: colors.text }, + configMethodDesc: { + ...typography.caption, + color: colors.textSecondary, + marginBottom: spacing.md, + }, + simulateBtn: { + backgroundColor: colors.primary, + borderRadius: borderRadius.md, + paddingVertical: spacing.sm, + alignItems: 'center', + marginBottom: spacing.sm, + }, + simulateBtnText: { ...typography.body, color: colors.text, fontWeight: '600' }, + removeBtn: { + borderWidth: 1, + borderColor: colors.border, + borderRadius: borderRadius.md, + paddingVertical: spacing.sm, + alignItems: 'center', + }, + removeBtnText: { ...typography.body, color: colors.textSecondary }, + + emptyState: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: spacing.xl }, + emptyIcon: { fontSize: 64, marginBottom: spacing.md }, + emptyTitle: { ...typography.h2, color: colors.text, marginBottom: spacing.sm }, + emptyText: { ...typography.body, color: colors.textSecondary, textAlign: 'center' }, +}); + +export default RevenueReportScreen; diff --git a/src/store/__tests__/accountingStore.test.ts b/src/store/__tests__/accountingStore.test.ts new file mode 100644 index 0000000..64284ed --- /dev/null +++ b/src/store/__tests__/accountingStore.test.ts @@ -0,0 +1,377 @@ +/** + * Tests for accountingStore – revenue recognition calculations. + * + * Covers: + * - buildStraightLineSchedule: even split, remainder, single period + * - buildUsageBasedSchedule: single deferred entry + * - splitRecognisedDeferred: all deferred, all recognised, partial, multi-period + * - billingCycleToMs: all billing cycles + * - Store actions: setRecognitionRule, generateRevenueSchedule, recognizeRevenue, + * getDeferredRevenue, getRevenueSchedule, getRevenueAnalyticsByPeriod + * - Edge cases: cancellation mid-period, contract modification (rule update), + * multi-element arrangements, unknown subscription + */ + +import { describe, it, expect, beforeEach } from '@jest/globals'; +import { + buildStraightLineSchedule, + buildUsageBasedSchedule, + splitRecognisedDeferred, + billingCycleToMs, + useAccountingStore, +} from '../accountingStore'; +import { BillingCycle } from '../../types/subscription'; + +// ── Mock AsyncStorage so persist middleware doesn't fail ───────────────────── +jest.mock('@react-native-async-storage/async-storage', () => ({ + setItem: jest.fn(() => Promise.resolve()), + getItem: jest.fn(() => Promise.resolve(null)), + removeItem: jest.fn(() => Promise.resolve()), +})); + +// ── Helpers ─────────────────────────────────────────────────────────────────── + +function resetStore() { + useAccountingStore.getState().reset(); +} + +const MS_PER_DAY = 24 * 60 * 60 * 1000; + +// ═════════════════════════════════════════════════════════════════════════════ +// Pure helpers +// ═════════════════════════════════════════════════════════════════════════════ + +describe('billingCycleToMs', () => { + it('weekly → 7 days', () => { + expect(billingCycleToMs(BillingCycle.WEEKLY)).toBe(7 * MS_PER_DAY); + }); + + it('monthly → ~30 days', () => { + const ms = billingCycleToMs(BillingCycle.MONTHLY); + // Allow ±1 day tolerance for rounding. + expect(ms).toBeGreaterThanOrEqual(29 * MS_PER_DAY); + expect(ms).toBeLessThanOrEqual(31 * MS_PER_DAY); + }); + + it('yearly → 365 days', () => { + expect(billingCycleToMs(BillingCycle.YEARLY)).toBe(365 * MS_PER_DAY); + }); +}); + +// ── buildStraightLineSchedule ───────────────────────────────────────────────── + +describe('buildStraightLineSchedule', () => { + it('splits evenly when divisible', () => { + const schedule = buildStraightLineSchedule('sub-1', 120, 0, 30 * MS_PER_DAY, 4); + expect(schedule.entries).toHaveLength(4); + schedule.entries.forEach((e) => expect(e.recognisedAmount).toBe(30)); + }); + + it('puts remainder in last entry', () => { + // 100 / 3 = 33.33 → 33 + 33 + 34 + const schedule = buildStraightLineSchedule('sub-1', 100, 0, 30 * MS_PER_DAY, 3); + const amounts = schedule.entries.map((e) => e.recognisedAmount); + expect(amounts[0]).toBe(33); + expect(amounts[1]).toBe(33); + expect(amounts[2]).toBeCloseTo(34, 1); + }); + + it('single period covers full interval', () => { + const start = 1_000_000; + const period = 30 * MS_PER_DAY; + const schedule = buildStraightLineSchedule('sub-1', 500, start, period, 1); + expect(schedule.entries).toHaveLength(1); + expect(schedule.entries[0].periodStart).toBe(start); + expect(schedule.entries[0].periodEnd).toBe(start + period); + expect(schedule.entries[0].recognisedAmount).toBe(500); + }); + + it('entries are not yet recognised', () => { + const schedule = buildStraightLineSchedule('sub-1', 200, 0, MS_PER_DAY, 2); + schedule.entries.forEach((e) => expect(e.isRecognised).toBe(false)); + }); + + it('throws when numPeriods is 0', () => { + expect(() => buildStraightLineSchedule('sub-1', 100, 0, MS_PER_DAY, 0)).toThrow(); + }); + + it('throws when periodMs is 0', () => { + expect(() => buildStraightLineSchedule('sub-1', 100, 0, 0, 1)).toThrow(); + }); +}); + +// ── buildUsageBasedSchedule ─────────────────────────────────────────────────── + +describe('buildUsageBasedSchedule', () => { + it('creates a single deferred entry covering the full interval', () => { + const schedule = buildUsageBasedSchedule('sub-2', 800, 500, 30 * MS_PER_DAY); + expect(schedule.entries).toHaveLength(1); + const entry = schedule.entries[0]; + expect(entry.recognisedAmount).toBe(800); + expect(entry.periodStart).toBe(500); + expect(entry.periodEnd).toBe(500 + 30 * MS_PER_DAY); + expect(entry.isRecognised).toBe(false); + }); +}); + +// ── splitRecognisedDeferred ─────────────────────────────────────────────────── + +describe('splitRecognisedDeferred', () => { + it('all deferred before period starts', () => { + const schedule = buildStraightLineSchedule('s', 1000, 1000, 1000, 1); + const { recognised, deferred } = splitRecognisedDeferred(schedule, 500); + expect(recognised).toBe(0); + expect(deferred).toBe(1000); + }); + + it('all recognised after period ends', () => { + const schedule = buildStraightLineSchedule('s', 1000, 0, 1000, 1); + const { recognised, deferred } = splitRecognisedDeferred(schedule, 2000); + expect(recognised).toBe(1000); + expect(deferred).toBe(0); + }); + + it('50% recognised at midpoint', () => { + const schedule = buildStraightLineSchedule('s', 1000, 0, 1000, 1); + const { recognised, deferred } = splitRecognisedDeferred(schedule, 500); + expect(recognised).toBeCloseTo(500, 1); + expect(deferred).toBeCloseTo(500, 1); + }); + + it('multi-period partial recognition', () => { + // 1200 over 4 × 100ms periods. Query at t=250 → 2 full + 50% of 3rd. + const schedule = buildStraightLineSchedule('s', 1200, 0, 100, 4); + const { recognised, deferred } = splitRecognisedDeferred(schedule, 250); + // 300 + 300 + 150 = 750 recognised; 150 + 300 = 450 deferred + expect(recognised).toBeCloseTo(750, 1); + expect(deferred).toBeCloseTo(450, 1); + }); + + it('cancellation mid-period: partial recognition', () => { + // Cancelled at 30% through a 1000ms period. + const schedule = buildStraightLineSchedule('s', 1000, 0, 1000, 1); + const { recognised, deferred } = splitRecognisedDeferred(schedule, 300); + expect(recognised).toBeCloseTo(300, 1); + expect(deferred).toBeCloseTo(700, 1); + }); +}); + +// ═════════════════════════════════════════════════════════════════════════════ +// Store actions +// ═════════════════════════════════════════════════════════════════════════════ + +describe('useAccountingStore', () => { + beforeEach(() => resetStore()); + + // ── setRecognitionRule ──────────────────────────────────────────────────── + + it('setRecognitionRule persists a rule', () => { + useAccountingStore.getState().setRecognitionRule({ + subscriptionId: 'sub-1', + method: 'straight-line', + recognitionPeriodMs: 7 * MS_PER_DAY, + }); + expect(useAccountingStore.getState().rules['sub-1']).toBeDefined(); + expect(useAccountingStore.getState().rules['sub-1'].method).toBe('straight-line'); + }); + + it('setRecognitionRule can be updated (contract modification)', () => { + useAccountingStore.getState().setRecognitionRule({ + subscriptionId: 'sub-1', + method: 'straight-line', + recognitionPeriodMs: 7 * MS_PER_DAY, + }); + useAccountingStore.getState().setRecognitionRule({ + subscriptionId: 'sub-1', + method: 'usage-based', + recognitionPeriodMs: 7 * MS_PER_DAY, + }); + expect(useAccountingStore.getState().rules['sub-1'].method).toBe('usage-based'); + }); + + it('removeRecognitionRule deletes the rule', () => { + useAccountingStore.getState().setRecognitionRule({ + subscriptionId: 'sub-1', + method: 'straight-line', + recognitionPeriodMs: MS_PER_DAY, + }); + useAccountingStore.getState().removeRecognitionRule('sub-1'); + expect(useAccountingStore.getState().rules['sub-1']).toBeUndefined(); + }); + + // ── generateRevenueSchedule ─────────────────────────────────────────────── + + it('generateRevenueSchedule creates a schedule and defers revenue', () => { + const schedule = useAccountingStore + .getState() + .generateRevenueSchedule('sub-1', 100, Date.now(), BillingCycle.MONTHLY); + + expect(schedule.totalAmount).toBe(100); + expect(schedule.entries.length).toBeGreaterThan(0); + // All revenue starts deferred. + expect(useAccountingStore.getState().deferredRevenue['default']).toBe(100); + }); + + it('generateRevenueSchedule uses straight-line rule when set', () => { + const periodMs = 7 * MS_PER_DAY; + useAccountingStore.getState().setRecognitionRule({ + subscriptionId: 'sub-1', + method: 'straight-line', + recognitionPeriodMs: periodMs, + }); + const schedule = useAccountingStore + .getState() + .generateRevenueSchedule('sub-1', 280, 0, BillingCycle.MONTHLY); + // Monthly (~30 days) / 7-day period = ~4-5 entries. + expect(schedule.entries.length).toBeGreaterThanOrEqual(4); + }); + + it('generateRevenueSchedule uses usage-based rule when set', () => { + useAccountingStore.getState().setRecognitionRule({ + subscriptionId: 'sub-1', + method: 'usage-based', + recognitionPeriodMs: 30 * MS_PER_DAY, + }); + const schedule = useAccountingStore + .getState() + .generateRevenueSchedule('sub-1', 500, 0, BillingCycle.MONTHLY); + expect(schedule.entries).toHaveLength(1); + }); + + it('generateRevenueSchedule defaults to single straight-line period with no rule', () => { + const schedule = useAccountingStore + .getState() + .generateRevenueSchedule('sub-1', 200, 0, BillingCycle.MONTHLY); + expect(schedule.entries).toHaveLength(1); + expect(schedule.entries[0].recognisedAmount).toBe(200); + }); + + // ── recognizeRevenue ────────────────────────────────────────────────────── + + it('recognizeRevenue returns zeros for unknown subscription', () => { + const rec = useAccountingStore.getState().recognizeRevenue('unknown'); + expect(rec.recognisedRevenue).toBe(0); + expect(rec.deferredRevenue).toBe(0); + }); + + it('recognizeRevenue returns all deferred immediately after charge', () => { + const chargeDate = Date.now() - 1; // just in the past + useAccountingStore + .getState() + .generateRevenueSchedule('sub-1', 1000, chargeDate, BillingCycle.MONTHLY); + // Query at chargeDate + 1ms → almost nothing recognised yet. + const rec = useAccountingStore.getState().recognizeRevenue('sub-1', chargeDate + 1); + expect(rec.deferredRevenue).toBeGreaterThan(990); + }); + + it('recognizeRevenue returns all recognised after period ends', () => { + const chargeDate = 0; + const intervalMs = billingCycleToMs(BillingCycle.MONTHLY); + useAccountingStore + .getState() + .generateRevenueSchedule('sub-1', 1000, chargeDate, BillingCycle.MONTHLY); + // Query well after period ends. + const rec = useAccountingStore + .getState() + .recognizeRevenue('sub-1', chargeDate + intervalMs + 1); + expect(rec.recognisedRevenue).toBeCloseTo(1000, 1); + expect(rec.deferredRevenue).toBeCloseTo(0, 1); + }); + + // ── getDeferredRevenue ──────────────────────────────────────────────────── + + it('getDeferredRevenue returns 0 for unknown merchant', () => { + expect(useAccountingStore.getState().getDeferredRevenue('merchant-x')).toBe(0); + }); + + it('getDeferredRevenue accumulates across multiple charges', () => { + useAccountingStore + .getState() + .generateRevenueSchedule('sub-1', 100, 0, BillingCycle.MONTHLY, 'merchant-1'); + useAccountingStore + .getState() + .generateRevenueSchedule('sub-2', 200, 0, BillingCycle.MONTHLY, 'merchant-1'); + expect(useAccountingStore.getState().getDeferredRevenue('merchant-1')).toBe(300); + }); + + // ── getRevenueSchedule ──────────────────────────────────────────────────── + + it('getRevenueSchedule returns undefined for unknown subscription', () => { + expect(useAccountingStore.getState().getRevenueSchedule('ghost')).toBeUndefined(); + }); + + it('getRevenueSchedule returns the persisted schedule', () => { + useAccountingStore.getState().generateRevenueSchedule('sub-1', 500, 0, BillingCycle.MONTHLY); + const schedule = useAccountingStore.getState().getRevenueSchedule('sub-1'); + expect(schedule).toBeDefined(); + expect(schedule!.totalAmount).toBe(500); + }); + + // ── getRevenueAnalyticsByPeriod ─────────────────────────────────────────── + + it('getRevenueAnalyticsByPeriod returns empty buckets with no schedules', () => { + const now = Date.now(); + const analytics = useAccountingStore + .getState() + .getRevenueAnalyticsByPeriod(7 * MS_PER_DAY, now - 28 * MS_PER_DAY, now); + expect(analytics).toHaveLength(4); + analytics.forEach((b) => { + expect(b.recognisedAmount).toBe(0); + expect(b.subscriptionCount).toBe(0); + }); + }); + + it('getRevenueAnalyticsByPeriod accumulates amounts into correct buckets', () => { + const from = 0; + const bucketMs = 100; + // Two subscriptions charged at t=0 (bucket 0). + useAccountingStore.getState().generateRevenueSchedule('sub-1', 1000, 0, BillingCycle.MONTHLY); + useAccountingStore.getState().generateRevenueSchedule('sub-2', 2000, 0, BillingCycle.MONTHLY); + + const analytics = useAccountingStore + .getState() + .getRevenueAnalyticsByPeriod(bucketMs, from, from + 200); + + // Both schedules have their single entry starting at t=0 → bucket 0. + expect(analytics[0].recognisedAmount).toBe(3000); + expect(analytics[0].subscriptionCount).toBe(2); + }); + + it('getRevenueAnalyticsByPeriod throws on invalid periodMs', () => { + expect(() => useAccountingStore.getState().getRevenueAnalyticsByPeriod(0, 0, 1000)).toThrow(); + }); + + // ── Multi-element arrangement ───────────────────────────────────────────── + + it('multi-element: deferred balances accumulate per merchant', () => { + useAccountingStore + .getState() + .generateRevenueSchedule('sub-1', 500, 0, BillingCycle.MONTHLY, 'merchant-A'); + useAccountingStore + .getState() + .generateRevenueSchedule('sub-2', 300, 0, BillingCycle.MONTHLY, 'merchant-A'); + useAccountingStore + .getState() + .generateRevenueSchedule('sub-3', 200, 0, BillingCycle.MONTHLY, 'merchant-B'); + + expect(useAccountingStore.getState().getDeferredRevenue('merchant-A')).toBe(800); + expect(useAccountingStore.getState().getDeferredRevenue('merchant-B')).toBe(200); + }); + + // ── reset ───────────────────────────────────────────────────────────────── + + it('reset clears all state', () => { + useAccountingStore.getState().setRecognitionRule({ + subscriptionId: 'sub-1', + method: 'straight-line', + recognitionPeriodMs: MS_PER_DAY, + }); + useAccountingStore.getState().generateRevenueSchedule('sub-1', 100, 0, BillingCycle.MONTHLY); + + useAccountingStore.getState().reset(); + + expect(Object.keys(useAccountingStore.getState().rules)).toHaveLength(0); + expect(Object.keys(useAccountingStore.getState().schedules)).toHaveLength(0); + expect(useAccountingStore.getState().getDeferredRevenue()).toBe(0); + }); +}); diff --git a/src/store/accountingStore.ts b/src/store/accountingStore.ts new file mode 100644 index 0000000..2706cb6 --- /dev/null +++ b/src/store/accountingStore.ts @@ -0,0 +1,363 @@ +/** + * accountingStore – revenue recognition accounting state. + * + * Implements: + * - RevenueRecognitionRule (method + recognition_period) + * - Straight-line and usage-based recognition + * - Deferred revenue tracking + * - Revenue schedule generation + * - Multi-element arrangement accounting + * - Revenue analytics by period + */ + +import { create } from 'zustand'; +import { persist, createJSONStorage } from 'zustand/middleware'; +import AsyncStorage from '@react-native-async-storage/async-storage'; +import { BillingCycle } from '../types/subscription'; + +// ── Domain types ────────────────────────────────────────────────────────────── + +export type RecognitionMethod = 'straight-line' | 'usage-based'; + +export interface RevenueRecognitionRule { + /** Subscription ID this rule applies to. */ + subscriptionId: string; + method: RecognitionMethod; + /** + * Length of one recognition period in milliseconds. + * e.g. 30 * 24 * 60 * 60 * 1000 for 30 days. + */ + recognitionPeriodMs: number; +} + +export interface RevenueScheduleEntry { + periodStart: number; // Unix ms + periodEnd: number; // Unix ms + recognisedAmount: number; // currency units + isRecognised: boolean; +} + +export interface RevenueSchedule { + subscriptionId: string; + totalAmount: number; + chargeDate: number; // Unix ms when the charge occurred + entries: RevenueScheduleEntry[]; +} + +export interface Recognition { + subscriptionId: string; + recognisedRevenue: number; + deferredRevenue: number; + asOf: number; // Unix ms +} + +export interface PeriodRevenue { + periodStart: number; // Unix ms + periodEnd: number; // Unix ms + recognisedAmount: number; + subscriptionCount: number; +} + +// ── Store state & actions ───────────────────────────────────────────────────── + +interface AccountingState { + /** Recognition rules keyed by subscriptionId. */ + rules: Record; + /** Revenue schedules keyed by subscriptionId. */ + schedules: Record; + /** Cumulative deferred revenue per merchantId (or 'default'). */ + deferredRevenue: Record; + /** Cumulative recognised revenue per merchantId (or 'default'). */ + recognisedRevenue: Record; + + // ── Actions ── + + /** Persist a recognition rule for a subscription. */ + setRecognitionRule: (rule: RevenueRecognitionRule) => void; + + /** Remove a recognition rule. */ + removeRecognitionRule: (subscriptionId: string) => void; + + /** + * Generate and persist a revenue schedule for a charge. + * @param subscriptionId Subscription being charged. + * @param totalAmount Amount charged (in currency units). + * @param chargeDate When the charge occurred (Unix ms). + * @param billingCycle Billing cycle of the subscription. + * @param merchantId Merchant receiving the revenue. + */ + generateRevenueSchedule: ( + subscriptionId: string, + totalAmount: number, + chargeDate: number, + billingCycle: BillingCycle, + merchantId?: string + ) => RevenueSchedule; + + /** + * Compute a recognition snapshot for a subscription as of `asOf` (defaults to now). + */ + recognizeRevenue: (subscriptionId: string, asOf?: number) => Recognition; + + /** Return the cumulative deferred revenue for a merchant. */ + getDeferredRevenue: (merchantId?: string) => number; + + /** Return the revenue schedule for a subscription (or undefined). */ + getRevenueSchedule: (subscriptionId: string) => RevenueSchedule | undefined; + + /** + * Compute per-period revenue analytics across all tracked subscriptions. + * @param periodMs Bucket size in milliseconds. + * @param from Range start (Unix ms). + * @param to Range end (Unix ms). + */ + getRevenueAnalyticsByPeriod: (periodMs: number, from: number, to: number) => PeriodRevenue[]; + + /** Flush all accounting data (useful for testing). */ + reset: () => void; +} + +// ── Pure helpers ────────────────────────────────────────────────────────────── + +/** Convert a BillingCycle to its duration in milliseconds. */ +export function billingCycleToMs(cycle: BillingCycle): number { + const MS_PER_DAY = 24 * 60 * 60 * 1000; + switch (cycle) { + case BillingCycle.WEEKLY: + return 7 * MS_PER_DAY; + case BillingCycle.MONTHLY: + // 30.44 average days per month + return Math.round(30.44 * MS_PER_DAY); + case BillingCycle.YEARLY: + return 365 * MS_PER_DAY; + default: + return 30 * MS_PER_DAY; + } +} + +/** + * Build a straight-line schedule: split `totalAmount` evenly across + * `numPeriods` consecutive periods of `periodMs` ms each. + * Any rounding remainder is added to the last entry. + */ +export function buildStraightLineSchedule( + subscriptionId: string, + totalAmount: number, + chargeDate: number, + periodMs: number, + numPeriods: number +): RevenueSchedule { + if (numPeriods <= 0) throw new Error('numPeriods must be > 0'); + if (periodMs <= 0) throw new Error('periodMs must be > 0'); + + const slice = Math.floor((totalAmount / numPeriods) * 100) / 100; + const remainder = Math.round((totalAmount - slice * numPeriods) * 100) / 100; + + const entries: RevenueScheduleEntry[] = Array.from({ length: numPeriods }, (_, i) => ({ + periodStart: chargeDate + i * periodMs, + periodEnd: chargeDate + (i + 1) * periodMs, + recognisedAmount: i === numPeriods - 1 ? slice + remainder : slice, + isRecognised: false, + })); + + return { subscriptionId, totalAmount, chargeDate, entries }; +} + +/** + * Build a usage-based schedule: a single entry covering the full interval. + * Revenue is deferred until the merchant reports actual usage. + */ +export function buildUsageBasedSchedule( + subscriptionId: string, + totalAmount: number, + chargeDate: number, + intervalMs: number +): RevenueSchedule { + return { + subscriptionId, + totalAmount, + chargeDate, + entries: [ + { + periodStart: chargeDate, + periodEnd: chargeDate + intervalMs, + recognisedAmount: totalAmount, + isRecognised: false, + }, + ], + }; +} + +/** + * Walk a schedule and return { recognised, deferred } split as of `now`. + * Partial periods are pro-rated linearly. + */ +export function splitRecognisedDeferred( + schedule: RevenueSchedule, + now: number +): { recognised: number; deferred: number } { + let recognised = 0; + let deferred = 0; + + for (const entry of schedule.entries) { + if (now >= entry.periodEnd) { + recognised += entry.recognisedAmount; + } else if (now >= entry.periodStart) { + const elapsed = now - entry.periodStart; + const duration = entry.periodEnd - entry.periodStart; + const partial = (entry.recognisedAmount * elapsed) / duration; + recognised += partial; + deferred += entry.recognisedAmount - partial; + } else { + deferred += entry.recognisedAmount; + } + } + + return { recognised, deferred }; +} + +// ── Store ───────────────────────────────────────────────────────────────────── + +const STORAGE_KEY = 'subtrackr-accounting'; +const DEFAULT_MERCHANT = 'default'; + +const initialState = { + rules: {} as Record, + schedules: {} as Record, + deferredRevenue: {} as Record, + recognisedRevenue: {} as Record, +}; + +export const useAccountingStore = create()( + persist( + (set, get) => ({ + ...initialState, + + setRecognitionRule: (rule) => { + set((state) => ({ + rules: { ...state.rules, [rule.subscriptionId]: rule }, + })); + }, + + removeRecognitionRule: (subscriptionId) => { + set((state) => { + const rules = { ...state.rules }; + delete rules[subscriptionId]; + return { rules }; + }); + }, + + generateRevenueSchedule: ( + subscriptionId, + totalAmount, + chargeDate, + billingCycle, + merchantId = DEFAULT_MERCHANT + ) => { + const rule = get().rules[subscriptionId]; + const intervalMs = billingCycleToMs(billingCycle); + + let schedule: RevenueSchedule; + + if (rule) { + const numPeriods = Math.max(1, Math.ceil(intervalMs / rule.recognitionPeriodMs)); + if (rule.method === 'straight-line') { + schedule = buildStraightLineSchedule( + subscriptionId, + totalAmount, + chargeDate, + rule.recognitionPeriodMs, + numPeriods + ); + } else { + schedule = buildUsageBasedSchedule(subscriptionId, totalAmount, chargeDate, intervalMs); + } + } else { + // Default: straight-line over the full interval as a single period. + schedule = buildStraightLineSchedule( + subscriptionId, + totalAmount, + chargeDate, + intervalMs, + 1 + ); + } + + set((state) => ({ + schedules: { ...state.schedules, [subscriptionId]: schedule }, + // All newly charged revenue starts as deferred. + deferredRevenue: { + ...state.deferredRevenue, + [merchantId]: (state.deferredRevenue[merchantId] ?? 0) + totalAmount, + }, + })); + + return schedule; + }, + + recognizeRevenue: (subscriptionId, asOf = Date.now()) => { + const schedule = get().schedules[subscriptionId]; + if (!schedule) { + return { + subscriptionId, + recognisedRevenue: 0, + deferredRevenue: 0, + asOf, + }; + } + const { recognised, deferred } = splitRecognisedDeferred(schedule, asOf); + return { + subscriptionId, + recognisedRevenue: recognised, + deferredRevenue: deferred, + asOf, + }; + }, + + getDeferredRevenue: (merchantId = DEFAULT_MERCHANT) => { + return get().deferredRevenue[merchantId] ?? 0; + }, + + getRevenueSchedule: (subscriptionId) => { + return get().schedules[subscriptionId]; + }, + + getRevenueAnalyticsByPeriod: (periodMs, from, to) => { + if (periodMs <= 0) throw new Error('periodMs must be > 0'); + if (to < from) throw new Error('to must be >= from'); + if (to === from) return []; + + const numBuckets = Math.ceil((to - from) / periodMs); + const buckets: PeriodRevenue[] = Array.from({ length: numBuckets }, (_, i) => ({ + periodStart: from + i * periodMs, + periodEnd: from + (i + 1) * periodMs, + recognisedAmount: 0, + subscriptionCount: 0, + })); + + for (const schedule of Object.values(get().schedules)) { + let contributed = false; + for (const entry of schedule.entries) { + if (entry.periodStart < from || entry.periodStart >= to) continue; + const bucketIdx = Math.floor((entry.periodStart - from) / periodMs); + if (bucketIdx >= 0 && bucketIdx < numBuckets) { + buckets[bucketIdx].recognisedAmount += entry.recognisedAmount; + if (!contributed) { + buckets[bucketIdx].subscriptionCount += 1; + contributed = true; + } + } + } + } + + return buckets; + }, + + reset: () => set(initialState), + }), + { + name: STORAGE_KEY, + storage: createJSONStorage(() => AsyncStorage), + } + ) +); diff --git a/src/store/index.ts b/src/store/index.ts index ee82dcd..071ef8e 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,3 +1,4 @@ export { useSubscriptionStore } from './subscriptionStore'; export { useTransactionQueueStore } from './transactionQueueStore'; export { useWalletStore } from './walletStore'; +export { useAccountingStore } from './accountingStore'; From 791d70716dd68ef441e5ac61ea31708352a83ec5 Mon Sep 17 00:00:00 2001 From: od-hunter Date: Fri, 24 Apr 2026 13:54:05 +0100 Subject: [PATCH 2/2] fix: integrate revenue module into workspace contract structure - Fix contracts/Cargo.toml: remove duplicate [package] section, make it a proper workspace root only - Move revenue module from contracts/src/ to contracts/subscription/src/ adapted for the new storage-contract delegation pattern - Add revenue StorageKey variants to contracts/types/src/lib.rs: RevenueRecognitionRule, RevenueSchedule, RevenueDeferredBalance, RevenueRecognisedBalance, RevenueMerchantSubscriptions - Hook revenue schedule generation into charge_subscription in contracts/subscription/src/lib.rs - Add revenue public API to SubTrackrSubscription: set_revenue_rule, recognize_revenue, get_deferred_revenue, get_revenue_schedule - Fix clippy::manual_div_ceil in revenue.rs (use .div_ceil()) - Add #[allow(clippy::too_many_arguments)] to subscription and proxy crates (pre-existing upstream issue with Soroban contractimpl macro) - Remove obsolete contracts/src/ directory - All workspace tests pass, cargo fmt --check clean, clippy -D warnings clean --- contracts/Cargo.toml | 17 - contracts/proxy/src/lib.rs | 39 +- contracts/proxy/src/storage.rs | 15 +- ..._contract_call_charges_subscription.1.json | 796 ++ ...multiple_contract_interactions_work.1.json | 6430 ++++++++++------- ...s_actual_token_contract_for_charges.1.json | 796 ++ contracts/proxy/tests/upgrade_flow.rs | 42 +- contracts/src/lib.rs | 1209 ---- contracts/src/revenue.rs | 692 -- contracts/subscription/src/lib.rs | 270 +- contracts/subscription/src/revenue.rs | 387 + contracts/types/src/lib.rs | 12 + 12 files changed, 6293 insertions(+), 4412 deletions(-) delete mode 100644 contracts/src/lib.rs delete mode 100644 contracts/src/revenue.rs create mode 100644 contracts/subscription/src/revenue.rs diff --git a/contracts/Cargo.toml b/contracts/Cargo.toml index 95748d3..075fdd3 100644 --- a/contracts/Cargo.toml +++ b/contracts/Cargo.toml @@ -1,20 +1,3 @@ -[package] -name = "subtrackr-subscription" -version = "0.2.0" -edition = "2021" -authors = ["SubTrackr Team"] -description = "SubTrackr subscription implementation contract (Soroban)" - -[lib] -crate-type = ["cdylib", "rlib"] - -[dependencies] -soroban-sdk = "21.0.0" -subtrackr-types = { path = "../types" } - -[dev-dependencies] -soroban-sdk = { version = "21.0.0", features = ["testutils"] } -arbitrary = { version = "1.3", features = ["derive"] } [workspace] resolver = "2" members = [ diff --git a/contracts/proxy/src/lib.rs b/contracts/proxy/src/lib.rs index d0cfa16..12b1276 100644 --- a/contracts/proxy/src/lib.rs +++ b/contracts/proxy/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![allow(clippy::too_many_arguments)] mod storage; @@ -13,11 +14,7 @@ fn current_proxy_address(env: &Env) -> Address { env.current_contract_address() } -fn invoke_impl>( - env: &Env, - func: &str, - args: Vec, -) -> T { +fn invoke_impl>(env: &Env, func: &str, args: Vec) -> T { let impl_addr = proxy_storage::implementation(env); env.invoke_contract(&impl_addr, &soroban_sdk::Symbol::new(env, func), args) } @@ -92,11 +89,7 @@ impl UpgradeableProxy { let target_version: u32 = env.invoke_contract( &implementation, &soroban_sdk::Symbol::new(&env, "get_version"), - soroban_sdk::vec![ - &env, - proxy_addr.into_val(&env), - storage.into_val(&env) - ], + soroban_sdk::vec![&env, proxy_addr.into_val(&env), storage.into_val(&env)], ); proxy_storage::set_version(&env, target_version); } @@ -133,13 +126,13 @@ impl UpgradeableProxy { // Basic interface validation: ensure new implementation supports expected interface. let proxy_addr = current_proxy_address(&env); let storage_addr = proxy_storage::storage_address(&env); - let args: Vec = soroban_sdk::vec![ - &env, - proxy_addr.into_val(&env), - storage_addr.into_val(&env) - ]; - let _target_version: u32 = - env.invoke_contract(&implementation, &soroban_sdk::Symbol::new(&env, "get_version"), args); + let args: Vec = + soroban_sdk::vec![&env, proxy_addr.into_val(&env), storage_addr.into_val(&env)]; + let _target_version: u32 = env.invoke_contract( + &implementation, + &soroban_sdk::Symbol::new(&env, "get_version"), + args, + ); proxy_storage::set_scheduled_upgrade( &env, @@ -194,7 +187,10 @@ impl UpgradeableProxy { ); let now = env.ledger().timestamp(); - assert!(now >= scheduled.execute_after, "Upgrade timelock not expired"); + assert!( + now >= scheduled.execute_after, + "Upgrade timelock not expired" + ); let proxy_addr = current_proxy_address(&env); let storage_addr = proxy_storage::storage_address(&env); @@ -471,12 +467,7 @@ impl UpgradeableProxy { ); } - pub fn pause_by_subscriber( - env: Env, - subscriber: Address, - subscription_id: u64, - duration: u64, - ) { + pub fn pause_by_subscriber(env: Env, subscriber: Address, subscription_id: u64, duration: u64) { let proxy_addr = current_proxy_address(&env); let storage_addr = proxy_storage::storage_address(&env); invoke_impl::<()>( diff --git a/contracts/proxy/src/storage.rs b/contracts/proxy/src/storage.rs index 0052dc1..2de5f2d 100644 --- a/contracts/proxy/src/storage.rs +++ b/contracts/proxy/src/storage.rs @@ -80,7 +80,9 @@ pub(crate) fn set_rollback_delay_secs(env: &Env, delay_secs: u64) { } pub(crate) fn scheduled_upgrade(env: &Env) -> Option { - env.storage().instance().get(&StorageKey::ProxyScheduledUpgrade) + env.storage() + .instance() + .get(&StorageKey::ProxyScheduledUpgrade) } pub(crate) fn set_scheduled_upgrade(env: &Env, upgrade: &ScheduledUpgrade) { @@ -90,7 +92,9 @@ pub(crate) fn set_scheduled_upgrade(env: &Env, upgrade: &ScheduledUpgrade) { } pub(crate) fn clear_scheduled_upgrade(env: &Env) { - env.storage().instance().remove(&StorageKey::ProxyScheduledUpgrade); + env.storage() + .instance() + .remove(&StorageKey::ProxyScheduledUpgrade); } pub(crate) fn previous_count(env: &Env) -> u32 { @@ -112,9 +116,10 @@ pub(crate) fn previous_top(env: &Env) -> Option
{ pub(crate) fn push_previous(env: &Env, implementation: &Address) { let count = previous_count(env); - env.storage() - .instance() - .set(&StorageKey::ProxyPreviousImplementation(count), implementation); + env.storage().instance().set( + &StorageKey::ProxyPreviousImplementation(count), + implementation, + ); env.storage() .instance() .set(&StorageKey::ProxyPreviousImplementationCount, &(count + 1)); diff --git a/contracts/proxy/test_snapshots/integration_cross_contract_call_charges_subscription.1.json b/contracts/proxy/test_snapshots/integration_cross_contract_call_charges_subscription.1.json index b48de0a..1884720 100644 --- a/contracts/proxy/test_snapshots/integration_cross_contract_call_charges_subscription.1.json +++ b/contracts/proxy/test_snapshots/integration_cross_contract_call_charges_subscription.1.json @@ -630,6 +630,264 @@ 4095 ] ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "RevenueDeferredBalance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "RevenueDeferredBalance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent", + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "RevenueMerchantSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "RevenueMerchantSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent", + "val": { + "vec": [ + { + "u64": 1 + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "RevenueRecognisedBalance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "RevenueRecognisedBalance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent", + "val": { + "i128": { + "hi": 0, + "lo": 0 + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "RevenueSchedule" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "RevenueSchedule" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 1705184020 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 1702592020 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], [ { "contract_data": { @@ -4250,6 +4508,544 @@ }, "failed_call": false }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_get" + } + ], + "data": { + "vec": [ + { + "symbol": "RevenueRecognitionRule" + }, + { + "u64": 1 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_get" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_set" + } + ], + "data": { + "vec": [ + { + "vec": [ + { + "symbol": "RevenueSchedule" + }, + { + "u64": 1 + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 1705184020 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 1702592020 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + ] + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_set" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_get" + } + ], + "data": { + "vec": [ + { + "symbol": "RevenueRecognisedBalance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_get" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_get" + } + ], + "data": { + "vec": [ + { + "symbol": "RevenueDeferredBalance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_get" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_set" + } + ], + "data": { + "vec": [ + { + "vec": [ + { + "symbol": "RevenueRecognisedBalance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + { + "i128": { + "hi": 0, + "lo": 0 + } + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_set" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_set" + } + ], + "data": { + "vec": [ + { + "vec": [ + { + "symbol": "RevenueDeferredBalance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_set" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_get" + } + ], + "data": { + "vec": [ + { + "symbol": "RevenueMerchantSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_get" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_set" + } + ], + "data": { + "vec": [ + { + "vec": [ + { + "symbol": "RevenueMerchantSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + { + "vec": [ + { + "u64": 1 + } + ] + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_set" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, { "event": { "ext": "v0", diff --git a/contracts/proxy/test_snapshots/integration_multiple_contract_interactions_work.1.json b/contracts/proxy/test_snapshots/integration_multiple_contract_interactions_work.1.json index 1a44bd2..4661613 100644 --- a/contracts/proxy/test_snapshots/integration_multiple_contract_interactions_work.1.json +++ b/contracts/proxy/test_snapshots/integration_multiple_contract_interactions_work.1.json @@ -1106,10 +1106,10 @@ "key": { "vec": [ { - "symbol": "Subscription" + "symbol": "RevenueDeferredBalance" }, { - "u64": 1 + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" } ] }, @@ -1126,131 +1126,19 @@ "key": { "vec": [ { - "symbol": "Subscription" + "symbol": "RevenueDeferredBalance" }, { - "u64": 1 + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" } ] }, "durability": "persistent", "val": { - "map": [ - { - "key": { - "symbol": "charge_count" - }, - "val": { - "u32": 1 - } - }, - { - "key": { - "symbol": "id" - }, - "val": { - "u64": 1 - } - }, - { - "key": { - "symbol": "last_charged_at" - }, - "val": { - "u64": 1702592030 - } - }, - { - "key": { - "symbol": "next_charge_at" - }, - "val": { - "u64": 1705184030 - } - }, - { - "key": { - "symbol": "pause_duration" - }, - "val": { - "u64": 0 - } - }, - { - "key": { - "symbol": "paused_at" - }, - "val": { - "u64": 0 - } - }, - { - "key": { - "symbol": "plan_id" - }, - "val": { - "u64": 1 - } - }, - { - "key": { - "symbol": "refund_requested_amount" - }, - "val": { - "i128": { - "hi": 0, - "lo": 0 - } - } - }, - { - "key": { - "symbol": "started_at" - }, - "val": { - "u64": 1700000000 - } - }, - { - "key": { - "symbol": "status" - }, - "val": { - "vec": [ - { - "symbol": "Active" - } - ] - } - }, - { - "key": { - "symbol": "subscriber" - }, - "val": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" - } - }, - { - "key": { - "symbol": "total_gas_spent" - }, - "val": { - "u64": 100000 - } - }, - { - "key": { - "symbol": "total_paid" - }, - "val": { - "i128": { - "hi": 0, - "lo": 500 - } - } - } - ] + "i128": { + "hi": 0, + "lo": 500 + } } } }, @@ -1266,10 +1154,10 @@ "key": { "vec": [ { - "symbol": "Subscription" + "symbol": "RevenueDeferredBalance" }, { - "u64": 2 + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" } ] }, @@ -1286,131 +1174,19 @@ "key": { "vec": [ { - "symbol": "Subscription" + "symbol": "RevenueDeferredBalance" }, { - "u64": 2 + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" } ] }, "durability": "persistent", "val": { - "map": [ - { - "key": { - "symbol": "charge_count" - }, - "val": { - "u32": 1 - } - }, - { - "key": { - "symbol": "id" - }, - "val": { - "u64": 2 - } - }, - { - "key": { - "symbol": "last_charged_at" - }, - "val": { - "u64": 1702592030 - } - }, - { - "key": { - "symbol": "next_charge_at" - }, - "val": { - "u64": 1705184030 - } - }, - { - "key": { - "symbol": "pause_duration" - }, - "val": { - "u64": 0 - } - }, - { - "key": { - "symbol": "paused_at" - }, - "val": { - "u64": 0 - } - }, - { - "key": { - "symbol": "plan_id" - }, - "val": { - "u64": 2 - } - }, - { - "key": { - "symbol": "refund_requested_amount" - }, - "val": { - "i128": { - "hi": 0, - "lo": 0 - } - } - }, - { - "key": { - "symbol": "started_at" - }, - "val": { - "u64": 1700000000 - } - }, - { - "key": { - "symbol": "status" - }, - "val": { - "vec": [ - { - "symbol": "Active" - } - ] - } - }, - { - "key": { - "symbol": "subscriber" - }, - "val": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" - } - }, - { - "key": { - "symbol": "total_gas_spent" - }, - "val": { - "u64": 100000 - } - }, - { - "key": { - "symbol": "total_paid" - }, - "val": { - "i128": { - "hi": 0, - "lo": 900 - } - } - } - ] + "i128": { + "hi": 0, + "lo": 900 + } } } }, @@ -1426,13 +1202,10 @@ "key": { "vec": [ { - "symbol": "UserPlanIndex" - }, - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + "symbol": "RevenueMerchantSubscriptions" }, { - "u64": 1 + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" } ] }, @@ -1449,19 +1222,20 @@ "key": { "vec": [ { - "symbol": "UserPlanIndex" - }, - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + "symbol": "RevenueMerchantSubscriptions" }, { - "u64": 1 + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" } ] }, "durability": "persistent", "val": { - "u64": 1 + "vec": [ + { + "u64": 1 + } + ] } } }, @@ -1477,13 +1251,10 @@ "key": { "vec": [ { - "symbol": "UserPlanIndex" - }, - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + "symbol": "RevenueMerchantSubscriptions" }, { - "u64": 2 + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" } ] }, @@ -1500,19 +1271,20 @@ "key": { "vec": [ { - "symbol": "UserPlanIndex" - }, - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + "symbol": "RevenueMerchantSubscriptions" }, { - "u64": 2 + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" } ] }, "durability": "persistent", "val": { - "u64": 2 + "vec": [ + { + "u64": 2 + } + ] } } }, @@ -1528,10 +1300,10 @@ "key": { "vec": [ { - "symbol": "UserSubscriptions" + "symbol": "RevenueRecognisedBalance" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" } ] }, @@ -1548,23 +1320,19 @@ "key": { "vec": [ { - "symbol": "UserSubscriptions" + "symbol": "RevenueRecognisedBalance" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" } ] }, "durability": "persistent", "val": { - "vec": [ - { - "u64": 1 - }, - { - "u64": 2 - } - ] + "i128": { + "hi": 0, + "lo": 0 + } } } }, @@ -1577,7 +1345,16 @@ { "contract_data": { "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", - "key": "ledger_key_contract_instance", + "key": { + "vec": [ + { + "symbol": "RevenueRecognisedBalance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + } + ] + }, "durability": "persistent" } }, @@ -1588,63 +1365,21 @@ "contract_data": { "ext": "v0", "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", - "key": "ledger_key_contract_instance", + "key": { + "vec": [ + { + "symbol": "RevenueRecognisedBalance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + } + ] + }, "durability": "persistent", "val": { - "contract_instance": { - "executable": { - "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" - }, - "storage": [ - { - "key": { - "vec": [ - { - "symbol": "Admin" - } - ] - }, - "val": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" - } - }, - { - "key": { - "vec": [ - { - "symbol": "PlanCount" - } - ] - }, - "val": { - "u64": 2 - } - }, - { - "key": { - "vec": [ - { - "symbol": "ProxyImplementation" - } - ] - }, - "val": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" - } - }, - { - "key": { - "vec": [ - { - "symbol": "SubscriptionCount" - } - ] - }, - "val": { - "u64": 2 - } - } - ] + "i128": { + "hi": 0, + "lo": 0 } } } @@ -1657,8 +1392,17 @@ [ { "contract_data": { - "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4", - "key": "ledger_key_contract_instance", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "RevenueSchedule" + }, + { + "u64": 1 + } + ] + }, "durability": "persistent" } }, @@ -1668,16 +1412,88 @@ "data": { "contract_data": { "ext": "v0", - "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4", - "key": "ledger_key_contract_instance", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "RevenueSchedule" + }, + { + "u64": 1 + } + ] + }, "durability": "persistent", "val": { - "contract_instance": { - "executable": { - "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 1705184030 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 1702592030 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + ] + } + ] + } }, - "storage": null - } + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + ] } } }, @@ -1689,8 +1505,17 @@ [ { "contract_data": { - "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM", - "key": "ledger_key_contract_instance", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "RevenueSchedule" + }, + { + "u64": 2 + } + ] + }, "durability": "persistent" } }, @@ -1700,101 +1525,88 @@ "data": { "contract_data": { "ext": "v0", - "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM", - "key": "ledger_key_contract_instance", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "RevenueSchedule" + }, + { + "u64": 2 + } + ] + }, "durability": "persistent", "val": { - "contract_instance": { - "executable": { - "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" - }, - "storage": [ - { - "key": { - "vec": [ - { - "symbol": "ProxyImplementation" - } - ] - }, - "val": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" - } - }, - { - "key": { - "vec": [ - { - "symbol": "ProxyPreviousImplementationCount" - } - ] - }, - "val": { - "u32": 0 - } + "map": [ + { + "key": { + "symbol": "entries" }, - { - "key": { - "vec": [ - { - "symbol": "ProxyRollbackDelaySecs" - } - ] - }, - "val": { - "u64": 0 - } - }, - { - "key": { - "vec": [ - { - "symbol": "ProxyStorage" - } - ] - }, - "val": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" - } - }, - { - "key": { - "vec": [ - { - "symbol": "ProxyUpgradeDelaySecs" - } - ] - }, - "val": { - "u64": 0 - } + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 1705184030 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 1702592030 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 900 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" }, - { - "key": { - "vec": [ - { - "symbol": "ProxyUpgradeHistoryCount" - } - ] - }, - "val": { - "u32": 0 - } + "val": { + "u64": 2 + } + }, + { + "key": { + "symbol": "total_amount" }, - { - "key": { - "vec": [ - { - "symbol": "ProxyVersion" - } - ] - }, - "val": { - "u32": 2 + "val": { + "i128": { + "hi": 0, + "lo": 900 } } - ] - } + } + ] } } }, @@ -1806,80 +1618,14 @@ [ { "contract_data": { - "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON", - "key": { - "ledger_key_nonce": { - "nonce": 6277191135259896685 - } - }, - "durability": "temporary" - } - }, - [ - { - "last_modified_ledger_seq": 0, - "data": { - "contract_data": { - "ext": "v0", - "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON", - "key": { - "ledger_key_nonce": { - "nonce": 6277191135259896685 - } - }, - "durability": "temporary", - "val": "void" - } - }, - "ext": "v0" - }, - 6311999 - ] - ], - [ - { - "contract_data": { - "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAX5", - "key": { - "ledger_key_nonce": { - "nonce": 8370022561469687789 - } - }, - "durability": "temporary" - } - }, - [ - { - "last_modified_ledger_seq": 0, - "data": { - "contract_data": { - "ext": "v0", - "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAX5", - "key": { - "ledger_key_nonce": { - "nonce": 8370022561469687789 - } - }, - "durability": "temporary", - "val": "void" - } - }, - "ext": "v0" - }, - 6311999 - ] - ], - [ - { - "contract_data": { - "contract": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", "key": { "vec": [ { - "symbol": "Balance" + "symbol": "Subscription" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + "u64": 1 } ] }, @@ -1892,14 +1638,14 @@ "data": { "contract_data": { "ext": "v0", - "contract": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", "key": { "vec": [ { - "symbol": "Balance" + "symbol": "Subscription" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + "u64": 1 } ] }, @@ -1908,287 +1654,116 @@ "map": [ { "key": { - "symbol": "amount" + "symbol": "charge_count" }, "val": { - "i128": { - "hi": 0, - "lo": 69100 - } + "u32": 1 } }, { "key": { - "symbol": "authorized" + "symbol": "id" }, "val": { - "bool": true + "u64": 1 } }, { "key": { - "symbol": "clawback" + "symbol": "last_charged_at" }, "val": { - "bool": false + "u64": 1702592030 } - } - ] - } - } - }, - "ext": "v0" - }, - 518400 - ] - ], - [ - { - "contract_data": { - "contract": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45", - "key": { - "vec": [ - { - "symbol": "Balance" - }, - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" - } - ] - }, - "durability": "persistent" - } - }, - [ - { - "last_modified_ledger_seq": 0, - "data": { - "contract_data": { - "ext": "v0", - "contract": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45", - "key": { - "vec": [ + }, { - "symbol": "Balance" + "key": { + "symbol": "next_charge_at" + }, + "val": { + "u64": 1705184030 + } }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" - } - ] - }, - "durability": "persistent", - "val": { - "map": [ + "key": { + "symbol": "pause_duration" + }, + "val": { + "u64": 0 + } + }, { "key": { - "symbol": "amount" + "symbol": "paused_at" }, "val": { - "i128": { - "hi": 0, - "lo": 900 - } + "u64": 0 } }, { "key": { - "symbol": "authorized" + "symbol": "plan_id" }, "val": { - "bool": true + "u64": 1 } }, { "key": { - "symbol": "clawback" + "symbol": "refund_requested_amount" }, "val": { - "bool": false + "i128": { + "hi": 0, + "lo": 0 + } } - } - ] - } - } - }, - "ext": "v0" - }, - 518400 - ] - ], - [ - { - "contract_data": { - "contract": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45", - "key": "ledger_key_contract_instance", - "durability": "persistent" - } - }, - [ - { - "last_modified_ledger_seq": 0, - "data": { - "contract_data": { - "ext": "v0", - "contract": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45", - "key": "ledger_key_contract_instance", - "durability": "persistent", - "val": { - "contract_instance": { - "executable": "stellar_asset", - "storage": [ - { - "key": { - "symbol": "METADATA" - }, - "val": { - "map": [ - { - "key": { - "symbol": "decimal" - }, - "val": { - "u32": 7 - } - }, - { - "key": { - "symbol": "name" - }, - "val": { - "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWM2U" - } - }, - { - "key": { - "symbol": "symbol" - }, - "val": { - "string": "aaa" - } - } - ] - } - }, - { - "key": { - "vec": [ - { - "symbol": "Admin" - } - ] - }, - "val": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAX5" - } + }, + { + "key": { + "symbol": "started_at" }, - { - "key": { - "vec": [ - { - "symbol": "AssetInfo" - } - ] - }, - "val": { - "vec": [ - { - "symbol": "AlphaNum4" - }, - { - "map": [ - { - "key": { - "symbol": "asset_code" - }, - "val": { - "string": "aaa\\0" - } - }, - { - "key": { - "symbol": "issuer" - }, - "val": { - "bytes": "000000000000000000000000000000000000000000000000000000000000000b" - } - } - ] - } - ] - } + "val": { + "u64": 1700000000 } - ] - } - } - } - }, - "ext": "v0" - }, - 120960 - ] - ], - [ - { - "contract_data": { - "contract": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23", - "key": { - "vec": [ - { - "symbol": "Balance" - }, - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" - } - ] - }, - "durability": "persistent" - } - }, - [ - { - "last_modified_ledger_seq": 0, - "data": { - "contract_data": { - "ext": "v0", - "contract": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23", - "key": { - "vec": [ - { - "symbol": "Balance" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" - } - ] - }, - "durability": "persistent", - "val": { - "map": [ + "key": { + "symbol": "status" + }, + "val": { + "vec": [ + { + "symbol": "Active" + } + ] + } + }, { "key": { - "symbol": "amount" + "symbol": "subscriber" }, "val": { - "i128": { - "hi": 0, - "lo": 500 - } + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" } }, { "key": { - "symbol": "authorized" + "symbol": "total_gas_spent" }, "val": { - "bool": true + "u64": 100000 } }, { "key": { - "symbol": "clawback" + "symbol": "total_paid" }, "val": { - "bool": false + "i128": { + "hi": 0, + "lo": 500 + } } } ] @@ -2197,20 +1772,20 @@ }, "ext": "v0" }, - 518400 + 4095 ] ], [ { "contract_data": { - "contract": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", "key": { "vec": [ { - "symbol": "Balance" + "symbol": "Subscription" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + "u64": 2 } ] }, @@ -2223,14 +1798,14 @@ "data": { "contract_data": { "ext": "v0", - "contract": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", "key": { "vec": [ { - "symbol": "Balance" + "symbol": "Subscription" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + "u64": 2 } ] }, @@ -2239,176 +1814,1499 @@ "map": [ { "key": { - "symbol": "amount" + "symbol": "charge_count" }, "val": { - "i128": { - "hi": 0, - "lo": 49500 - } + "u32": 1 } }, { "key": { - "symbol": "authorized" + "symbol": "id" }, "val": { - "bool": true + "u64": 2 } }, { "key": { - "symbol": "clawback" + "symbol": "last_charged_at" }, "val": { - "bool": false + "u64": 1702592030 } - } - ] - } - } - }, - "ext": "v0" - }, - 518400 - ] - ], - [ - { - "contract_data": { - "contract": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23", - "key": "ledger_key_contract_instance", - "durability": "persistent" - } - }, - [ - { - "last_modified_ledger_seq": 0, - "data": { - "contract_data": { - "ext": "v0", - "contract": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23", - "key": "ledger_key_contract_instance", - "durability": "persistent", - "val": { - "contract_instance": { - "executable": "stellar_asset", - "storage": [ - { - "key": { - "symbol": "METADATA" - }, - "val": { - "map": [ - { - "key": { - "symbol": "decimal" - }, - "val": { - "u32": 7 - } - }, - { - "key": { - "symbol": "name" - }, - "val": { - "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUDE" - } - }, - { - "key": { - "symbol": "symbol" - }, - "val": { - "string": "aaa" - } - } - ] - } + }, + { + "key": { + "symbol": "next_charge_at" }, - { - "key": { - "vec": [ - { - "symbol": "Admin" - } - ] - }, - "val": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + "val": { + "u64": 1705184030 + } + }, + { + "key": { + "symbol": "pause_duration" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "paused_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "plan_id" + }, + "val": { + "u64": 2 + } + }, + { + "key": { + "symbol": "refund_requested_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 0 } + } + }, + { + "key": { + "symbol": "started_at" }, - { - "key": { - "vec": [ - { - "symbol": "AssetInfo" - } - ] - }, - "val": { - "vec": [ - { - "symbol": "AlphaNum4" - }, - { - "map": [ - { - "key": { - "symbol": "asset_code" - }, - "val": { - "string": "aaa\\0" - } - }, - { - "key": { - "symbol": "issuer" - }, - "val": { - "bytes": "0000000000000000000000000000000000000000000000000000000000000008" - } - } - ] - } - ] + "val": { + "u64": 1700000000 + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "vec": [ + { + "symbol": "Active" + } + ] + } + }, + { + "key": { + "symbol": "subscriber" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 100000 + } + }, + { + "key": { + "symbol": "total_paid" + }, + "val": { + "i128": { + "hi": 0, + "lo": 900 } } - ] + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "UserPlanIndex" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "UserPlanIndex" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent", + "val": { + "u64": 1 + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "UserPlanIndex" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "u64": 2 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "UserPlanIndex" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "u64": 2 + } + ] + }, + "durability": "persistent", + "val": { + "u64": 2 + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "UserSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "UserSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent", + "val": { + "vec": [ + { + "u64": 1 + }, + { + "u64": 2 + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Admin" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + }, + { + "key": { + "vec": [ + { + "symbol": "PlanCount" + } + ] + }, + "val": { + "u64": 2 + } + }, + { + "key": { + "vec": [ + { + "symbol": "ProxyImplementation" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + } + }, + { + "key": { + "vec": [ + { + "symbol": "SubscriptionCount" + } + ] + }, + "val": { + "u64": 2 + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "ProxyImplementation" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + } + }, + { + "key": { + "vec": [ + { + "symbol": "ProxyPreviousImplementationCount" + } + ] + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "vec": [ + { + "symbol": "ProxyRollbackDelaySecs" + } + ] + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "vec": [ + { + "symbol": "ProxyStorage" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + }, + { + "key": { + "vec": [ + { + "symbol": "ProxyUpgradeDelaySecs" + } + ] + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "vec": [ + { + "symbol": "ProxyUpgradeHistoryCount" + } + ] + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "vec": [ + { + "symbol": "ProxyVersion" + } + ] + }, + "val": { + "u32": 2 + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON", + "key": { + "ledger_key_nonce": { + "nonce": 6277191135259896685 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON", + "key": { + "ledger_key_nonce": { + "nonce": 6277191135259896685 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAX5", + "key": { + "ledger_key_nonce": { + "nonce": 8370022561469687789 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAX5", + "key": { + "ledger_key_nonce": { + "nonce": 8370022561469687789 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45", + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45", + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 69100 + } + } + }, + { + "key": { + "symbol": "authorized" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "clawback" + }, + "val": { + "bool": false + } + } + ] + } + } + }, + "ext": "v0" + }, + 518400 + ] + ], + [ + { + "contract_data": { + "contract": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45", + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45", + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 900 + } + } + }, + { + "key": { + "symbol": "authorized" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "clawback" + }, + "val": { + "bool": false + } + } + ] + } + } + }, + "ext": "v0" + }, + 518400 + ] + ], + [ + { + "contract_data": { + "contract": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": "stellar_asset", + "storage": [ + { + "key": { + "symbol": "METADATA" + }, + "val": { + "map": [ + { + "key": { + "symbol": "decimal" + }, + "val": { + "u32": 7 + } + }, + { + "key": { + "symbol": "name" + }, + "val": { + "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWM2U" + } + }, + { + "key": { + "symbol": "symbol" + }, + "val": { + "string": "aaa" + } + } + ] + } + }, + { + "key": { + "vec": [ + { + "symbol": "Admin" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAX5" + } + }, + { + "key": { + "vec": [ + { + "symbol": "AssetInfo" + } + ] + }, + "val": { + "vec": [ + { + "symbol": "AlphaNum4" + }, + { + "map": [ + { + "key": { + "symbol": "asset_code" + }, + "val": { + "string": "aaa\\0" + } + }, + { + "key": { + "symbol": "issuer" + }, + "val": { + "bytes": "000000000000000000000000000000000000000000000000000000000000000b" + } + } + ] + } + ] + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 120960 + ] + ], + [ + { + "contract_data": { + "contract": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23", + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23", + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + }, + { + "key": { + "symbol": "authorized" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "clawback" + }, + "val": { + "bool": false + } + } + ] + } + } + }, + "ext": "v0" + }, + 518400 + ] + ], + [ + { + "contract_data": { + "contract": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23", + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23", + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 49500 + } + } + }, + { + "key": { + "symbol": "authorized" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "clawback" + }, + "val": { + "bool": false + } + } + ] + } + } + }, + "ext": "v0" + }, + 518400 + ] + ], + [ + { + "contract_data": { + "contract": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": "stellar_asset", + "storage": [ + { + "key": { + "symbol": "METADATA" + }, + "val": { + "map": [ + { + "key": { + "symbol": "decimal" + }, + "val": { + "u32": 7 + } + }, + { + "key": { + "symbol": "name" + }, + "val": { + "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUDE" + } + }, + { + "key": { + "symbol": "symbol" + }, + "val": { + "string": "aaa" + } + } + ] + } + }, + { + "key": { + "vec": [ + { + "symbol": "Admin" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + }, + { + "key": { + "vec": [ + { + "symbol": "AssetInfo" + } + ] + }, + "val": { + "vec": [ + { + "symbol": "AlphaNum4" + }, + { + "map": [ + { + "key": { + "symbol": "asset_code" + }, + "val": { + "string": "aaa\\0" + } + }, + { + "key": { + "symbol": "issuer" + }, + "val": { + "bytes": "0000000000000000000000000000000000000000000000000000000000000008" + } + } + ] + } + ] + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 120960 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000007" + }, + { + "symbol": "initialize" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + }, + { + "u64": 0 + }, + { + "u64": 0 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000007", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "initialize" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "initialize" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000007", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000006" + }, + { + "symbol": "get_version" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "get_version" + } + ], + "data": { + "u32": 2 + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000007", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "initialize" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "f65bd4d892e052cedba1fd62974e564ffa9226ca720624f331c36770bea46c44" + }, + { + "symbol": "init_asset" + } + ], + "data": { + "bytes": "0000000161616100000000000000000000000000000000000000000000000000000000000000000000000008" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "f65bd4d892e052cedba1fd62974e564ffa9226ca720624f331c36770bea46c44", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "init_asset" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "f65bd4d892e052cedba1fd62974e564ffa9226ca720624f331c36770bea46c44" + }, + { + "symbol": "set_admin" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "f65bd4d892e052cedba1fd62974e564ffa9226ca720624f331c36770bea46c44", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "set_admin" + }, + { + "address": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUDE" + }, + { + "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUDE" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "f65bd4d892e052cedba1fd62974e564ffa9226ca720624f331c36770bea46c44", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "set_admin" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "f65bd4d892e052cedba1fd62974e564ffa9226ca720624f331c36770bea46c44" + }, + { + "symbol": "mint" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": { + "hi": 0, + "lo": 50000 } } - } - }, - "ext": "v0" - }, - 120960 - ] - ], - [ - { - "contract_code": { - "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ] + } } - }, - [ - { - "last_modified_ledger_seq": 0, + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "f65bd4d892e052cedba1fd62974e564ffa9226ca720624f331c36770bea46c44", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "mint" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUDE" + } + ], "data": { - "contract_code": { - "ext": "v0", - "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - "code": "" + "i128": { + "hi": 0, + "lo": 50000 } - }, - "ext": "v0" - }, - 4095 - ] - ] - ] - }, - "events": [ + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "f65bd4d892e052cedba1fd62974e564ffa9226ca720624f331c36770bea46c44", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "mint" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, { "event": { "ext": "v0", @@ -2424,25 +3322,117 @@ "bytes": "0000000000000000000000000000000000000000000000000000000000000007" }, { - "symbol": "initialize" + "symbol": "create_plan" } ], "data": { "vec": [ { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "string": "Integration Plan" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + }, + { + "address": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23" + }, + { + "vec": [ + { + "symbol": "Monthly" + } + ] + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000007", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000006" + }, + { + "symbol": "create_plan" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM" }, { "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" }, { - "u64": 0 + "string": "Integration Plan" }, { - "u64": 0 + "i128": { + "hi": 0, + "lo": 500 + } + }, + { + "address": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23" + }, + { + "vec": [ + { + "symbol": "Monthly" + } + ] + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "instance_get" + } + ], + "data": { + "vec": [ + { + "symbol": "Admin" } ] } @@ -2454,7 +3444,30 @@ { "event": { "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000007", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "instance_get" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", "type_": "diagnostic", "body": { "v0": { @@ -2466,16 +3479,16 @@ "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "symbol": "initialize" + "symbol": "instance_get" } ], "data": { "vec": [ { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + "symbol": "RateLimit" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + "string": "create_plan" } ] } @@ -2496,7 +3509,7 @@ "symbol": "fn_return" }, { - "symbol": "initialize" + "symbol": "instance_get" } ], "data": "void" @@ -2508,7 +3521,7 @@ { "event": { "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000007", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", "type_": "diagnostic", "body": { "v0": { @@ -2517,19 +3530,16 @@ "symbol": "fn_call" }, { - "bytes": "0000000000000000000000000000000000000000000000000000000000000006" + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "symbol": "get_version" + "symbol": "instance_get" } ], "data": { "vec": [ { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM" - }, - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + "symbol": "PlanCount" } ] } @@ -2541,7 +3551,7 @@ { "event": { "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", "type_": "diagnostic", "body": { "v0": { @@ -2550,11 +3560,11 @@ "symbol": "fn_return" }, { - "symbol": "get_version" + "symbol": "instance_get" } ], "data": { - "u32": 2 + "u64": 0 } } } @@ -2564,19 +3574,118 @@ { "event": { "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000007", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", "type_": "diagnostic", "body": { "v0": { "topics": [ { - "symbol": "fn_return" + "symbol": "fn_call" }, { - "symbol": "initialize" + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_set" } ], - "data": "void" + "data": { + "vec": [ + { + "vec": [ + { + "symbol": "Plan" + }, + { + "u64": 1 + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "active" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": 1700000000 + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "interval" + }, + "val": { + "vec": [ + { + "symbol": "Monthly" + } + ] + } + }, + { + "key": { + "symbol": "merchant" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "name" + }, + "val": { + "string": "Integration Plan" + } + }, + { + "key": { + "symbol": "price" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + }, + { + "key": { + "symbol": "subscriber_count" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23" + } + } + ] + } + ] + } } } }, @@ -2585,24 +3694,19 @@ { "event": { "ext": "v0", - "contract_id": null, + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", "type_": "diagnostic", "body": { "v0": { "topics": [ { - "symbol": "fn_call" - }, - { - "bytes": "f65bd4d892e052cedba1fd62974e564ffa9226ca720624f331c36770bea46c44" + "symbol": "fn_return" }, { - "symbol": "init_asset" + "symbol": "persistent_set" } ], - "data": { - "bytes": "0000000161616100000000000000000000000000000000000000000000000000000000000000000000000008" - } + "data": "void" } } }, @@ -2611,19 +3715,35 @@ { "event": { "ext": "v0", - "contract_id": "f65bd4d892e052cedba1fd62974e564ffa9226ca720624f331c36770bea46c44", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", "type_": "diagnostic", "body": { "v0": { "topics": [ { - "symbol": "fn_return" + "symbol": "fn_call" }, { - "symbol": "init_asset" + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "instance_set" } ], - "data": "void" + "data": { + "vec": [ + { + "vec": [ + { + "symbol": "PlanCount" + } + ] + }, + { + "u64": 1 + } + ] + } } } }, @@ -2632,24 +3752,19 @@ { "event": { "ext": "v0", - "contract_id": null, + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", "type_": "diagnostic", "body": { "v0": { "topics": [ { - "symbol": "fn_call" - }, - { - "bytes": "f65bd4d892e052cedba1fd62974e564ffa9226ca720624f331c36770bea46c44" + "symbol": "fn_return" }, { - "symbol": "set_admin" + "symbol": "instance_set" } ], - "data": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" - } + "data": "void" } } }, @@ -2658,23 +3773,30 @@ { "event": { "ext": "v0", - "contract_id": "f65bd4d892e052cedba1fd62974e564ffa9226ca720624f331c36770bea46c44", - "type_": "contract", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", "body": { "v0": { "topics": [ { - "symbol": "set_admin" + "symbol": "fn_call" }, { - "address": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUDE" + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUDE" + "symbol": "persistent_get" } ], "data": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + "vec": [ + { + "symbol": "MerchantPlans" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] } } } @@ -2684,7 +3806,7 @@ { "event": { "ext": "v0", - "contract_id": "f65bd4d892e052cedba1fd62974e564ffa9226ca720624f331c36770bea46c44", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", "type_": "diagnostic", "body": { "v0": { @@ -2693,7 +3815,7 @@ "symbol": "fn_return" }, { - "symbol": "set_admin" + "symbol": "persistent_get" } ], "data": "void" @@ -2705,7 +3827,7 @@ { "event": { "ext": "v0", - "contract_id": null, + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", "type_": "diagnostic", "body": { "v0": { @@ -2714,22 +3836,30 @@ "symbol": "fn_call" }, { - "bytes": "f65bd4d892e052cedba1fd62974e564ffa9226ca720624f331c36770bea46c44" + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "symbol": "mint" + "symbol": "persistent_set" } ], "data": { "vec": [ { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + "vec": [ + { + "symbol": "MerchantPlans" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] }, { - "i128": { - "hi": 0, - "lo": 50000 - } + "vec": [ + { + "u64": 1 + } + ] } ] } @@ -2741,29 +3871,41 @@ { "event": { "ext": "v0", - "contract_id": "f65bd4d892e052cedba1fd62974e564ffa9226ca720624f331c36770bea46c44", - "type_": "contract", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_set" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", "body": { "v0": { "topics": [ { - "symbol": "mint" - }, - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" - }, - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + "symbol": "fn_return" }, { - "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUDE" + "symbol": "create_plan" } ], "data": { - "i128": { - "hi": 0, - "lo": 50000 - } + "u64": 1 } } } @@ -2773,7 +3915,7 @@ { "event": { "ext": "v0", - "contract_id": "f65bd4d892e052cedba1fd62974e564ffa9226ca720624f331c36770bea46c44", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000007", "type_": "diagnostic", "body": { "v0": { @@ -2782,10 +3924,12 @@ "symbol": "fn_return" }, { - "symbol": "mint" + "symbol": "create_plan" } ], - "data": "void" + "data": { + "u64": 1 + } } } }, @@ -2806,32 +3950,16 @@ "bytes": "0000000000000000000000000000000000000000000000000000000000000007" }, { - "symbol": "create_plan" + "symbol": "subscribe" } ], "data": { "vec": [ { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" - }, - { - "string": "Integration Plan" - }, - { - "i128": { - "hi": 0, - "lo": 500 - } - }, - { - "address": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" }, { - "vec": [ - { - "symbol": "Monthly" - } - ] + "u64": 1 } ] } @@ -2855,7 +3983,7 @@ "bytes": "0000000000000000000000000000000000000000000000000000000000000006" }, { - "symbol": "create_plan" + "symbol": "subscribe" } ], "data": { @@ -2867,26 +3995,10 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" - }, - { - "string": "Integration Plan" - }, - { - "i128": { - "hi": 0, - "lo": 500 - } - }, - { - "address": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" }, { - "vec": [ - { - "symbol": "Monthly" - } - ] + "u64": 1 } ] } @@ -2972,7 +4084,7 @@ "symbol": "RateLimit" }, { - "string": "create_plan" + "string": "subscribe" } ] } @@ -3017,13 +4129,16 @@ "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "symbol": "instance_get" + "symbol": "persistent_get" } ], "data": { "vec": [ { - "symbol": "PlanCount" + "symbol": "Plan" + }, + { + "u64": 1 } ] } @@ -3044,129 +4159,89 @@ "symbol": "fn_return" }, { - "symbol": "instance_get" - } - ], - "data": { - "u64": 0 - } - } - } - }, - "failed_call": false - }, - { - "event": { - "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", - "type_": "diagnostic", - "body": { - "v0": { - "topics": [ - { - "symbol": "fn_call" - }, - { - "bytes": "0000000000000000000000000000000000000000000000000000000000000005" - }, - { - "symbol": "persistent_set" + "symbol": "persistent_get" } ], "data": { - "vec": [ + "map": [ { - "vec": [ - { - "symbol": "Plan" - }, - { - "u64": 1 - } - ] + "key": { + "symbol": "active" + }, + "val": { + "bool": true + } }, { - "map": [ - { - "key": { - "symbol": "active" - }, - "val": { - "bool": true - } - }, - { - "key": { - "symbol": "created_at" - }, - "val": { - "u64": 1700000000 - } - }, - { - "key": { - "symbol": "id" - }, - "val": { - "u64": 1 - } - }, - { - "key": { - "symbol": "interval" - }, - "val": { - "vec": [ - { - "symbol": "Monthly" - } - ] - } - }, - { - "key": { - "symbol": "merchant" - }, - "val": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" - } - }, - { - "key": { - "symbol": "name" - }, - "val": { - "string": "Integration Plan" - } - }, - { - "key": { - "symbol": "price" - }, - "val": { - "i128": { - "hi": 0, - "lo": 500 - } - } - }, - { - "key": { - "symbol": "subscriber_count" - }, - "val": { - "u32": 0 - } - }, - { - "key": { - "symbol": "token" - }, - "val": { - "address": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23" + "key": { + "symbol": "created_at" + }, + "val": { + "u64": 1700000000 + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "interval" + }, + "val": { + "vec": [ + { + "symbol": "Monthly" } + ] + } + }, + { + "key": { + "symbol": "merchant" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "name" + }, + "val": { + "string": "Integration Plan" + } + }, + { + "key": { + "symbol": "price" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 } - ] + } + }, + { + "key": { + "symbol": "subscriber_count" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23" + } } ] } @@ -3175,27 +4250,6 @@ }, "failed_call": false }, - { - "event": { - "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", - "type_": "diagnostic", - "body": { - "v0": { - "topics": [ - { - "symbol": "fn_return" - }, - { - "symbol": "persistent_set" - } - ], - "data": "void" - } - } - }, - "failed_call": false - }, { "event": { "ext": "v0", @@ -3211,17 +4265,16 @@ "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "symbol": "instance_set" + "symbol": "persistent_get" } ], "data": { "vec": [ { - "vec": [ - { - "symbol": "PlanCount" - } - ] + "symbol": "UserPlanIndex" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" }, { "u64": 1 @@ -3245,7 +4298,7 @@ "symbol": "fn_return" }, { - "symbol": "instance_set" + "symbol": "persistent_get" } ], "data": "void" @@ -3269,16 +4322,13 @@ "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "symbol": "persistent_get" + "symbol": "instance_get" } ], "data": { "vec": [ { - "symbol": "MerchantPlans" - }, - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + "symbol": "SubscriptionCount" } ] } @@ -3299,10 +4349,12 @@ "symbol": "fn_return" }, { - "symbol": "persistent_get" + "symbol": "instance_get" } ], - "data": "void" + "data": { + "u64": 0 + } } } }, @@ -3331,17 +4383,128 @@ { "vec": [ { - "symbol": "MerchantPlans" + "symbol": "Subscription" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + "u64": 1 } ] }, { - "vec": [ + "map": [ { - "u64": 1 + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "last_charged_at" + }, + "val": { + "u64": 1700000000 + } + }, + { + "key": { + "symbol": "next_charge_at" + }, + "val": { + "u64": 1702592000 + } + }, + { + "key": { + "symbol": "pause_duration" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "paused_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "plan_id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "refund_requested_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 0 + } + } + }, + { + "key": { + "symbol": "started_at" + }, + "val": { + "u64": 1700000000 + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "vec": [ + { + "symbol": "Active" + } + ] + } + }, + { + "key": { + "symbol": "subscriber" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "total_paid" + }, + "val": { + "i128": { + "hi": 0, + "lo": 0 + } + } } ] } @@ -3382,37 +4545,28 @@ "v0": { "topics": [ { - "symbol": "fn_return" + "symbol": "fn_call" }, { - "symbol": "create_plan" - } - ], - "data": { - "u64": 1 - } - } - } - }, - "failed_call": false - }, - { - "event": { - "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000007", - "type_": "diagnostic", - "body": { - "v0": { - "topics": [ - { - "symbol": "fn_return" + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "symbol": "create_plan" + "symbol": "instance_set" } ], "data": { - "u64": 1 + "vec": [ + { + "vec": [ + { + "symbol": "SubscriptionCount" + } + ] + }, + { + "u64": 1 + } + ] } } } @@ -3422,31 +4576,19 @@ { "event": { "ext": "v0", - "contract_id": null, + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", "type_": "diagnostic", "body": { "v0": { "topics": [ { - "symbol": "fn_call" - }, - { - "bytes": "0000000000000000000000000000000000000000000000000000000000000007" + "symbol": "fn_return" }, { - "symbol": "subscribe" + "symbol": "instance_set" } ], - "data": { - "vec": [ - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" - }, - { - "u64": 1 - } - ] - } + "data": "void" } } }, @@ -3455,7 +4597,7 @@ { "event": { "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000007", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", "type_": "diagnostic", "body": { "v0": { @@ -3464,25 +4606,19 @@ "symbol": "fn_call" }, { - "bytes": "0000000000000000000000000000000000000000000000000000000000000006" + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "symbol": "subscribe" + "symbol": "persistent_get" } ], "data": { "vec": [ { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM" - }, - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" - }, - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + "symbol": "UserSubscriptions" }, { - "u64": 1 + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" } ] } @@ -3491,6 +4627,27 @@ }, "failed_call": false }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_get" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, { "event": { "ext": "v0", @@ -3506,13 +4663,27 @@ "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "symbol": "instance_get" + "symbol": "persistent_set" } ], "data": { "vec": [ { - "symbol": "Admin" + "vec": [ + { + "symbol": "UserSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + { + "vec": [ + { + "u64": 1 + } + ] } ] } @@ -3533,12 +4704,10 @@ "symbol": "fn_return" }, { - "symbol": "instance_get" + "symbol": "persistent_set" } ], - "data": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" - } + "data": "void" } } }, @@ -3559,16 +4728,26 @@ "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "symbol": "instance_get" + "symbol": "persistent_set" } ], "data": { "vec": [ { - "symbol": "RateLimit" + "vec": [ + { + "symbol": "UserPlanIndex" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "u64": 1 + } + ] }, { - "string": "subscribe" + "u64": 1 } ] } @@ -3589,7 +4768,7 @@ "symbol": "fn_return" }, { - "symbol": "instance_get" + "symbol": "persistent_set" } ], "data": "void" @@ -3613,16 +4792,103 @@ "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "symbol": "persistent_get" + "symbol": "persistent_set" } ], "data": { "vec": [ { - "symbol": "Plan" + "vec": [ + { + "symbol": "Plan" + }, + { + "u64": 1 + } + ] }, { - "u64": 1 + "map": [ + { + "key": { + "symbol": "active" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": 1700000000 + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "interval" + }, + "val": { + "vec": [ + { + "symbol": "Monthly" + } + ] + } + }, + { + "key": { + "symbol": "merchant" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "name" + }, + "val": { + "string": "Integration Plan" + } + }, + { + "key": { + "symbol": "price" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + }, + { + "key": { + "symbol": "subscriber_count" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23" + } + } + ] } ] } @@ -3643,91 +4909,32 @@ "symbol": "fn_return" }, { - "symbol": "persistent_get" + "symbol": "persistent_set" } ], - "data": { - "map": [ - { - "key": { - "symbol": "active" - }, - "val": { - "bool": true - } - }, - { - "key": { - "symbol": "created_at" - }, - "val": { - "u64": 1700000000 - } - }, - { - "key": { - "symbol": "id" - }, - "val": { - "u64": 1 - } - }, - { - "key": { - "symbol": "interval" - }, - "val": { - "vec": [ - { - "symbol": "Monthly" - } - ] - } - }, - { - "key": { - "symbol": "merchant" - }, - "val": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" - } - }, - { - "key": { - "symbol": "name" - }, - "val": { - "string": "Integration Plan" - } - }, - { - "key": { - "symbol": "price" - }, - "val": { - "i128": { - "hi": 0, - "lo": 500 - } - } - }, - { - "key": { - "symbol": "subscriber_count" - }, - "val": { - "u32": 0 - } - }, - { - "key": { - "symbol": "token" - }, - "val": { - "address": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23" - } - } - ] + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "subscribe" + } + ], + "data": { + "u64": 1 } } } @@ -3737,7 +4944,30 @@ { "event": { "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000007", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "subscribe" + } + ], + "data": { + "u64": 1 + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, "type_": "diagnostic", "body": { "v0": { @@ -3746,24 +4976,14 @@ "symbol": "fn_call" }, { - "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + "bytes": "ab09d2084c7dae35a44766e8b341392f390d98ee43b1612a002664c058fae9a6" }, { - "symbol": "persistent_get" + "symbol": "init_asset" } ], "data": { - "vec": [ - { - "symbol": "UserPlanIndex" - }, - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" - }, - { - "u64": 1 - } - ] + "bytes": "000000016161610000000000000000000000000000000000000000000000000000000000000000000000000b" } } } @@ -3773,7 +4993,7 @@ { "event": { "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "contract_id": "ab09d2084c7dae35a44766e8b341392f390d98ee43b1612a002664c058fae9a6", "type_": "diagnostic", "body": { "v0": { @@ -3782,7 +5002,7 @@ "symbol": "fn_return" }, { - "symbol": "persistent_get" + "symbol": "init_asset" } ], "data": "void" @@ -3794,7 +5014,7 @@ { "event": { "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "contract_id": null, "type_": "diagnostic", "body": { "v0": { @@ -3803,18 +5023,14 @@ "symbol": "fn_call" }, { - "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + "bytes": "ab09d2084c7dae35a44766e8b341392f390d98ee43b1612a002664c058fae9a6" }, { - "symbol": "instance_get" + "symbol": "set_admin" } ], "data": { - "vec": [ - { - "symbol": "SubscriptionCount" - } - ] + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAX5" } } } @@ -3824,20 +5040,23 @@ { "event": { "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", - "type_": "diagnostic", + "contract_id": "ab09d2084c7dae35a44766e8b341392f390d98ee43b1612a002664c058fae9a6", + "type_": "contract", "body": { "v0": { "topics": [ { - "symbol": "fn_return" + "symbol": "set_admin" }, { - "symbol": "instance_get" + "address": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWM2U" + }, + { + "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWM2U" } ], "data": { - "u64": 0 + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAX5" } } } @@ -3847,7 +5066,28 @@ { "event": { "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "contract_id": "ab09d2084c7dae35a44766e8b341392f390d98ee43b1612a002664c058fae9a6", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "set_admin" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, "type_": "diagnostic", "body": { "v0": { @@ -3856,141 +5096,22 @@ "symbol": "fn_call" }, { - "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + "bytes": "ab09d2084c7dae35a44766e8b341392f390d98ee43b1612a002664c058fae9a6" }, { - "symbol": "persistent_set" + "symbol": "mint" } ], "data": { "vec": [ { - "vec": [ - { - "symbol": "Subscription" - }, - { - "u64": 1 - } - ] + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" }, { - "map": [ - { - "key": { - "symbol": "charge_count" - }, - "val": { - "u32": 0 - } - }, - { - "key": { - "symbol": "id" - }, - "val": { - "u64": 1 - } - }, - { - "key": { - "symbol": "last_charged_at" - }, - "val": { - "u64": 1700000000 - } - }, - { - "key": { - "symbol": "next_charge_at" - }, - "val": { - "u64": 1702592000 - } - }, - { - "key": { - "symbol": "pause_duration" - }, - "val": { - "u64": 0 - } - }, - { - "key": { - "symbol": "paused_at" - }, - "val": { - "u64": 0 - } - }, - { - "key": { - "symbol": "plan_id" - }, - "val": { - "u64": 1 - } - }, - { - "key": { - "symbol": "refund_requested_amount" - }, - "val": { - "i128": { - "hi": 0, - "lo": 0 - } - } - }, - { - "key": { - "symbol": "started_at" - }, - "val": { - "u64": 1700000000 - } - }, - { - "key": { - "symbol": "status" - }, - "val": { - "vec": [ - { - "symbol": "Active" - } - ] - } - }, - { - "key": { - "symbol": "subscriber" - }, - "val": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" - } - }, - { - "key": { - "symbol": "total_gas_spent" - }, - "val": { - "u64": 0 - } - }, - { - "key": { - "symbol": "total_paid" - }, - "val": { - "i128": { - "hi": 0, - "lo": 0 - } - } - } - ] + "i128": { + "hi": 0, + "lo": 70000 + } } ] } @@ -4002,7 +5123,39 @@ { "event": { "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "contract_id": "ab09d2084c7dae35a44766e8b341392f390d98ee43b1612a002664c058fae9a6", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "mint" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAX5" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWM2U" + } + ], + "data": { + "i128": { + "hi": 0, + "lo": 70000 + } + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "ab09d2084c7dae35a44766e8b341392f390d98ee43b1612a002664c058fae9a6", "type_": "diagnostic", "body": { "v0": { @@ -4011,7 +5164,7 @@ "symbol": "fn_return" }, { - "symbol": "persistent_set" + "symbol": "mint" } ], "data": "void" @@ -4023,7 +5176,7 @@ { "event": { "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "contract_id": null, "type_": "diagnostic", "body": { "v0": { @@ -4032,23 +5185,35 @@ "symbol": "fn_call" }, { - "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + "bytes": "0000000000000000000000000000000000000000000000000000000000000007" }, { - "symbol": "instance_set" + "symbol": "create_plan" } ], "data": { "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + }, + { + "string": "Premium Plan" + }, + { + "i128": { + "hi": 0, + "lo": 900 + } + }, + { + "address": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45" + }, { "vec": [ { - "symbol": "SubscriptionCount" + "symbol": "Monthly" } ] - }, - { - "u64": 1 } ] } @@ -4060,19 +5225,53 @@ { "event": { "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000007", "type_": "diagnostic", "body": { "v0": { "topics": [ { - "symbol": "fn_return" + "symbol": "fn_call" }, { - "symbol": "instance_set" + "bytes": "0000000000000000000000000000000000000000000000000000000000000006" + }, + { + "symbol": "create_plan" } ], - "data": "void" + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + }, + { + "string": "Premium Plan" + }, + { + "i128": { + "hi": 0, + "lo": 900 + } + }, + { + "address": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45" + }, + { + "vec": [ + { + "symbol": "Monthly" + } + ] + } + ] + } } } }, @@ -4093,16 +5292,13 @@ "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "symbol": "persistent_get" + "symbol": "instance_get" } ], "data": { "vec": [ { - "symbol": "UserSubscriptions" - }, - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + "symbol": "Admin" } ] } @@ -4123,10 +5319,12 @@ "symbol": "fn_return" }, { - "symbol": "persistent_get" + "symbol": "instance_get" } ], - "data": "void" + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } } } }, @@ -4147,27 +5345,16 @@ "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "symbol": "persistent_set" + "symbol": "instance_get" } ], "data": { "vec": [ { - "vec": [ - { - "symbol": "UserSubscriptions" - }, - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" - } - ] + "symbol": "RateLimit" }, { - "vec": [ - { - "u64": 1 - } - ] + "string": "create_plan" } ] } @@ -4188,7 +5375,7 @@ "symbol": "fn_return" }, { - "symbol": "persistent_set" + "symbol": "instance_get" } ], "data": "void" @@ -4212,26 +5399,13 @@ "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "symbol": "persistent_set" + "symbol": "instance_get" } ], "data": { "vec": [ { - "vec": [ - { - "symbol": "UserPlanIndex" - }, - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" - }, - { - "u64": 1 - } - ] - }, - { - "u64": 1 + "symbol": "PlanCount" } ] } @@ -4252,10 +5426,12 @@ "symbol": "fn_return" }, { - "symbol": "persistent_set" + "symbol": "instance_get" } ], - "data": "void" + "data": { + "u64": 1 + } } } }, @@ -4287,7 +5463,7 @@ "symbol": "Plan" }, { - "u64": 1 + "u64": 2 } ] }, @@ -4314,7 +5490,7 @@ "symbol": "id" }, "val": { - "u64": 1 + "u64": 2 } }, { @@ -4334,7 +5510,7 @@ "symbol": "merchant" }, "val": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" } }, { @@ -4342,7 +5518,7 @@ "symbol": "name" }, "val": { - "string": "Integration Plan" + "string": "Premium Plan" } }, { @@ -4352,7 +5528,7 @@ "val": { "i128": { "hi": 0, - "lo": 500 + "lo": 900 } } }, @@ -4361,7 +5537,7 @@ "symbol": "subscriber_count" }, "val": { - "u32": 1 + "u32": 0 } }, { @@ -4369,7 +5545,7 @@ "symbol": "token" }, "val": { - "address": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23" + "address": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45" } } ] @@ -4411,14 +5587,28 @@ "v0": { "topics": [ { - "symbol": "fn_return" + "symbol": "fn_call" }, { - "symbol": "subscribe" + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "instance_set" } ], "data": { - "u64": 1 + "vec": [ + { + "vec": [ + { + "symbol": "PlanCount" + } + ] + }, + { + "u64": 2 + } + ] } } } @@ -4428,7 +5618,7 @@ { "event": { "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000007", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", "type_": "diagnostic", "body": { "v0": { @@ -4437,11 +5627,42 @@ "symbol": "fn_return" }, { - "symbol": "subscribe" + "symbol": "instance_set" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_get" } ], "data": { - "u64": 1 + "vec": [ + { + "symbol": "MerchantPlans" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + } + ] } } } @@ -4451,7 +5672,28 @@ { "event": { "ext": "v0", - "contract_id": null, + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_get" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", "type_": "diagnostic", "body": { "v0": { @@ -4460,14 +5702,32 @@ "symbol": "fn_call" }, { - "bytes": "ab09d2084c7dae35a44766e8b341392f390d98ee43b1612a002664c058fae9a6" + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "symbol": "init_asset" + "symbol": "persistent_set" } ], "data": { - "bytes": "000000016161610000000000000000000000000000000000000000000000000000000000000000000000000b" + "vec": [ + { + "vec": [ + { + "symbol": "MerchantPlans" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + } + ] + }, + { + "vec": [ + { + "u64": 2 + } + ] + } + ] } } } @@ -4477,7 +5737,7 @@ { "event": { "ext": "v0", - "contract_id": "ab09d2084c7dae35a44766e8b341392f390d98ee43b1612a002664c058fae9a6", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", "type_": "diagnostic", "body": { "v0": { @@ -4486,7 +5746,7 @@ "symbol": "fn_return" }, { - "symbol": "init_asset" + "symbol": "persistent_set" } ], "data": "void" @@ -4498,23 +5758,20 @@ { "event": { "ext": "v0", - "contract_id": null, + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", "type_": "diagnostic", "body": { "v0": { "topics": [ { - "symbol": "fn_call" - }, - { - "bytes": "ab09d2084c7dae35a44766e8b341392f390d98ee43b1612a002664c058fae9a6" + "symbol": "fn_return" }, { - "symbol": "set_admin" + "symbol": "create_plan" } ], "data": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAX5" + "u64": 2 } } } @@ -4524,23 +5781,20 @@ { "event": { "ext": "v0", - "contract_id": "ab09d2084c7dae35a44766e8b341392f390d98ee43b1612a002664c058fae9a6", - "type_": "contract", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000007", + "type_": "diagnostic", "body": { "v0": { "topics": [ { - "symbol": "set_admin" - }, - { - "address": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWM2U" + "symbol": "fn_return" }, { - "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWM2U" + "symbol": "create_plan" } ], "data": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAX5" + "u64": 2 } } } @@ -4550,19 +5804,31 @@ { "event": { "ext": "v0", - "contract_id": "ab09d2084c7dae35a44766e8b341392f390d98ee43b1612a002664c058fae9a6", + "contract_id": null, "type_": "diagnostic", "body": { "v0": { "topics": [ { - "symbol": "fn_return" + "symbol": "fn_call" }, { - "symbol": "set_admin" + "bytes": "0000000000000000000000000000000000000000000000000000000000000007" + }, + { + "symbol": "subscribe" } ], - "data": "void" + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "u64": 2 + } + ] + } } } }, @@ -4571,7 +5837,7 @@ { "event": { "ext": "v0", - "contract_id": null, + "contract_id": "0000000000000000000000000000000000000000000000000000000000000007", "type_": "diagnostic", "body": { "v0": { @@ -4580,22 +5846,25 @@ "symbol": "fn_call" }, { - "bytes": "ab09d2084c7dae35a44766e8b341392f390d98ee43b1612a002664c058fae9a6" + "bytes": "0000000000000000000000000000000000000000000000000000000000000006" }, { - "symbol": "mint" + "symbol": "subscribe" } ], "data": { "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + }, { "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" }, { - "i128": { - "hi": 0, - "lo": 70000 - } + "u64": 2 } ] } @@ -4607,29 +5876,27 @@ { "event": { "ext": "v0", - "contract_id": "ab09d2084c7dae35a44766e8b341392f390d98ee43b1612a002664c058fae9a6", - "type_": "contract", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", "body": { "v0": { "topics": [ { - "symbol": "mint" - }, - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAX5" + "symbol": "fn_call" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWM2U" + "symbol": "instance_get" } ], "data": { - "i128": { - "hi": 0, - "lo": 70000 - } + "vec": [ + { + "symbol": "Admin" + } + ] } } } @@ -4639,7 +5906,7 @@ { "event": { "ext": "v0", - "contract_id": "ab09d2084c7dae35a44766e8b341392f390d98ee43b1612a002664c058fae9a6", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", "type_": "diagnostic", "body": { "v0": { @@ -4648,10 +5915,12 @@ "symbol": "fn_return" }, { - "symbol": "mint" + "symbol": "instance_get" } ], - "data": "void" + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } } } }, @@ -4660,7 +5929,7 @@ { "event": { "ext": "v0", - "contract_id": null, + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", "type_": "diagnostic", "body": { "v0": { @@ -4669,35 +5938,19 @@ "symbol": "fn_call" }, { - "bytes": "0000000000000000000000000000000000000000000000000000000000000007" + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "symbol": "create_plan" + "symbol": "instance_get" } ], "data": { "vec": [ { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" - }, - { - "string": "Premium Plan" - }, - { - "i128": { - "hi": 0, - "lo": 900 - } - }, - { - "address": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45" + "symbol": "RateLimit" }, { - "vec": [ - { - "symbol": "Monthly" - } - ] + "string": "subscribe" } ] } @@ -4709,7 +5962,28 @@ { "event": { "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000007", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "instance_get" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", "type_": "diagnostic", "body": { "v0": { @@ -4718,41 +5992,19 @@ "symbol": "fn_call" }, { - "bytes": "0000000000000000000000000000000000000000000000000000000000000006" + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "symbol": "create_plan" + "symbol": "persistent_get" } ], "data": { "vec": [ { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM" - }, - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" - }, - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" - }, - { - "string": "Premium Plan" - }, - { - "i128": { - "hi": 0, - "lo": 900 - } - }, - { - "address": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45" + "symbol": "Plan" }, { - "vec": [ - { - "symbol": "Monthly" - } - ] + "u64": 2 } ] } @@ -4764,25 +6016,98 @@ { "event": { "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", "type_": "diagnostic", "body": { "v0": { "topics": [ { - "symbol": "fn_call" - }, - { - "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + "symbol": "fn_return" }, { - "symbol": "instance_get" + "symbol": "persistent_get" } ], "data": { - "vec": [ + "map": [ + { + "key": { + "symbol": "active" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": 1700000000 + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "u64": 2 + } + }, + { + "key": { + "symbol": "interval" + }, + "val": { + "vec": [ + { + "symbol": "Monthly" + } + ] + } + }, + { + "key": { + "symbol": "merchant" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + } + }, + { + "key": { + "symbol": "name" + }, + "val": { + "string": "Premium Plan" + } + }, + { + "key": { + "symbol": "price" + }, + "val": { + "i128": { + "hi": 0, + "lo": 900 + } + } + }, + { + "key": { + "symbol": "subscriber_count" + }, + "val": { + "u32": 0 + } + }, { - "symbol": "Admin" + "key": { + "symbol": "token" + }, + "val": { + "address": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45" + } } ] } @@ -4791,29 +6116,6 @@ }, "failed_call": false }, - { - "event": { - "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", - "type_": "diagnostic", - "body": { - "v0": { - "topics": [ - { - "symbol": "fn_return" - }, - { - "symbol": "instance_get" - } - ], - "data": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" - } - } - } - }, - "failed_call": false - }, { "event": { "ext": "v0", @@ -4829,16 +6131,19 @@ "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "symbol": "instance_get" + "symbol": "persistent_get" } ], "data": { "vec": [ { - "symbol": "RateLimit" + "symbol": "UserPlanIndex" }, { - "string": "create_plan" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "u64": 2 } ] } @@ -4859,7 +6164,7 @@ "symbol": "fn_return" }, { - "symbol": "instance_get" + "symbol": "persistent_get" } ], "data": "void" @@ -4889,7 +6194,7 @@ "data": { "vec": [ { - "symbol": "PlanCount" + "symbol": "SubscriptionCount" } ] } @@ -4944,7 +6249,7 @@ { "vec": [ { - "symbol": "Plan" + "symbol": "Subscription" }, { "u64": 2 @@ -4955,15 +6260,23 @@ "map": [ { "key": { - "symbol": "active" + "symbol": "charge_count" }, "val": { - "bool": true + "u32": 0 } }, { "key": { - "symbol": "created_at" + "symbol": "id" + }, + "val": { + "u64": 2 + } + }, + { + "key": { + "symbol": "last_charged_at" }, "val": { "u64": 1700000000 @@ -4971,65 +6284,92 @@ }, { "key": { - "symbol": "id" + "symbol": "next_charge_at" }, "val": { - "u64": 2 + "u64": 1702592000 } }, { "key": { - "symbol": "interval" + "symbol": "pause_duration" }, "val": { - "vec": [ - { - "symbol": "Monthly" - } - ] + "u64": 0 } }, { "key": { - "symbol": "merchant" + "symbol": "paused_at" }, "val": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + "u64": 0 } }, { "key": { - "symbol": "name" + "symbol": "plan_id" }, "val": { - "string": "Premium Plan" + "u64": 2 } }, { "key": { - "symbol": "price" + "symbol": "refund_requested_amount" }, "val": { "i128": { "hi": 0, - "lo": 900 + "lo": 0 } } }, { "key": { - "symbol": "subscriber_count" + "symbol": "started_at" }, "val": { - "u32": 0 + "u64": 1700000000 } }, { "key": { - "symbol": "token" + "symbol": "status" }, "val": { - "address": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45" + "vec": [ + { + "symbol": "Active" + } + ] + } + }, + { + "key": { + "symbol": "subscriber" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "total_paid" + }, + "val": { + "i128": { + "hi": 0, + "lo": 0 + } } } ] @@ -5085,7 +6425,7 @@ { "vec": [ { - "symbol": "PlanCount" + "symbol": "SubscriptionCount" } ] }, @@ -5141,10 +6481,148 @@ "data": { "vec": [ { - "symbol": "MerchantPlans" + "symbol": "UserSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_get" + } + ], + "data": { + "vec": [ + { + "u64": 1 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_set" + } + ], + "data": { + "vec": [ + { + "vec": [ + { + "symbol": "UserSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + { + "vec": [ + { + "u64": 1 + }, + { + "u64": 2 + } + ] + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_set" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_set" + } + ], + "data": { + "vec": [ + { + "vec": [ + { + "symbol": "UserPlanIndex" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "u64": 2 + } + ] }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + "u64": 2 } ] } @@ -5165,7 +6643,7 @@ "symbol": "fn_return" }, { - "symbol": "persistent_get" + "symbol": "persistent_set" } ], "data": "void" @@ -5197,17 +6675,93 @@ { "vec": [ { - "symbol": "MerchantPlans" + "symbol": "Plan" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + "u64": 2 } ] }, { - "vec": [ + "map": [ { - "u64": 2 + "key": { + "symbol": "active" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": 1700000000 + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "u64": 2 + } + }, + { + "key": { + "symbol": "interval" + }, + "val": { + "vec": [ + { + "symbol": "Monthly" + } + ] + } + }, + { + "key": { + "symbol": "merchant" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + } + }, + { + "key": { + "symbol": "name" + }, + "val": { + "string": "Premium Plan" + } + }, + { + "key": { + "symbol": "price" + }, + "val": { + "i128": { + "hi": 0, + "lo": 900 + } + } + }, + { + "key": { + "symbol": "subscriber_count" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "token" + }, + "val": { + "address": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45" + } } ] } @@ -5251,7 +6805,7 @@ "symbol": "fn_return" }, { - "symbol": "create_plan" + "symbol": "subscribe" } ], "data": { @@ -5274,7 +6828,7 @@ "symbol": "fn_return" }, { - "symbol": "create_plan" + "symbol": "subscribe" } ], "data": { @@ -5300,16 +6854,45 @@ "bytes": "0000000000000000000000000000000000000000000000000000000000000007" }, { - "symbol": "subscribe" + "symbol": "charge_subscription" + } + ], + "data": { + "u64": 1 + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000007", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000006" + }, + { + "symbol": "charge_subscription" } ], "data": { "vec": [ { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM" }, { - "u64": 2 + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + }, + { + "u64": 1 } ] } @@ -5321,7 +6904,7 @@ { "event": { "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000007", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", "type_": "diagnostic", "body": { "v0": { @@ -5330,25 +6913,157 @@ "symbol": "fn_call" }, { - "bytes": "0000000000000000000000000000000000000000000000000000000000000006" + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "symbol": "subscribe" + "symbol": "persistent_get" } ], "data": { "vec": [ { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM" + "symbol": "Subscription" + }, + { + "u64": 1 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_get" + } + ], + "data": { + "map": [ + { + "key": { + "symbol": "charge_count" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "last_charged_at" + }, + "val": { + "u64": 1700000000 + } + }, + { + "key": { + "symbol": "next_charge_at" + }, + "val": { + "u64": 1702592000 + } + }, + { + "key": { + "symbol": "pause_duration" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "paused_at" + }, + "val": { + "u64": 0 + } + }, + { + "key": { + "symbol": "plan_id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "refund_requested_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 0 + } + } + }, + { + "key": { + "symbol": "started_at" + }, + "val": { + "u64": 1700000000 + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "vec": [ + { + "symbol": "Active" + } + ] + } }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + "key": { + "symbol": "subscriber" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 0 + } }, { - "u64": 2 + "key": { + "symbol": "total_paid" + }, + "val": { + "i128": { + "hi": 0, + "lo": 0 + } + } } ] } @@ -5434,7 +7149,7 @@ "symbol": "RateLimit" }, { - "string": "subscribe" + "string": "charge_subscription" } ] } @@ -5488,7 +7203,7 @@ "symbol": "Plan" }, { - "u64": 2 + "u64": 1 } ] } @@ -5535,7 +7250,7 @@ "symbol": "id" }, "val": { - "u64": 2 + "u64": 1 } }, { @@ -5555,7 +7270,7 @@ "symbol": "merchant" }, "val": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" } }, { @@ -5563,7 +7278,7 @@ "symbol": "name" }, "val": { - "string": "Premium Plan" + "string": "Integration Plan" } }, { @@ -5573,7 +7288,7 @@ "val": { "i128": { "hi": 0, - "lo": 900 + "lo": 500 } } }, @@ -5582,7 +7297,7 @@ "symbol": "subscriber_count" }, "val": { - "u32": 0 + "u32": 1 } }, { @@ -5590,7 +7305,7 @@ "symbol": "token" }, "val": { - "address": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45" + "address": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23" } } ] @@ -5612,22 +7327,25 @@ "symbol": "fn_call" }, { - "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + "bytes": "f65bd4d892e052cedba1fd62974e564ffa9226ca720624f331c36770bea46c44" }, { - "symbol": "persistent_get" + "symbol": "transfer" } ], "data": { "vec": [ { - "symbol": "UserPlanIndex" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" }, { - "u64": 2 + "i128": { + "hi": 0, + "lo": 500 + } } ] } @@ -5639,48 +7357,29 @@ { "event": { "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", - "type_": "diagnostic", + "contract_id": "f65bd4d892e052cedba1fd62974e564ffa9226ca720624f331c36770bea46c44", + "type_": "contract", "body": { "v0": { "topics": [ { - "symbol": "fn_return" + "symbol": "transfer" }, { - "symbol": "persistent_get" - } - ], - "data": "void" - } - } - }, - "failed_call": false - }, - { - "event": { - "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", - "type_": "diagnostic", - "body": { - "v0": { - "topics": [ - { - "symbol": "fn_call" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" }, { - "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" }, { - "symbol": "instance_get" + "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUDE" } ], "data": { - "vec": [ - { - "symbol": "SubscriptionCount" - } - ] + "i128": { + "hi": 0, + "lo": 500 + } } } } @@ -5690,7 +7389,7 @@ { "event": { "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "contract_id": "f65bd4d892e052cedba1fd62974e564ffa9226ca720624f331c36770bea46c44", "type_": "diagnostic", "body": { "v0": { @@ -5699,12 +7398,10 @@ "symbol": "fn_return" }, { - "symbol": "instance_get" + "symbol": "transfer" } ], - "data": { - "u64": 1 - } + "data": "void" } } }, @@ -5736,7 +7433,7 @@ "symbol": "Subscription" }, { - "u64": 2 + "u64": 1 } ] }, @@ -5747,7 +7444,7 @@ "symbol": "charge_count" }, "val": { - "u32": 0 + "u32": 1 } }, { @@ -5755,7 +7452,7 @@ "symbol": "id" }, "val": { - "u64": 2 + "u64": 1 } }, { @@ -5763,7 +7460,7 @@ "symbol": "last_charged_at" }, "val": { - "u64": 1700000000 + "u64": 1702592030 } }, { @@ -5771,7 +7468,7 @@ "symbol": "next_charge_at" }, "val": { - "u64": 1702592000 + "u64": 1705184030 } }, { @@ -5795,64 +7492,247 @@ "symbol": "plan_id" }, "val": { - "u64": 2 + "u64": 1 + } + }, + { + "key": { + "symbol": "refund_requested_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 0 + } + } + }, + { + "key": { + "symbol": "started_at" + }, + "val": { + "u64": 1700000000 + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "vec": [ + { + "symbol": "Active" + } + ] + } + }, + { + "key": { + "symbol": "subscriber" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + }, + { + "key": { + "symbol": "total_gas_spent" + }, + "val": { + "u64": 100000 } }, { - "key": { - "symbol": "refund_requested_amount" - }, - "val": { - "i128": { - "hi": 0, - "lo": 0 - } - } + "key": { + "symbol": "total_paid" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + ] + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_set" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_get" + } + ], + "data": { + "vec": [ + { + "symbol": "RevenueRecognitionRule" + }, + { + "u64": 1 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_get" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_set" + } + ], + "data": { + "vec": [ + { + "vec": [ + { + "symbol": "RevenueSchedule" }, { - "key": { - "symbol": "started_at" - }, - "val": { - "u64": 1700000000 - } - }, + "u64": 1 + } + ] + }, + { + "map": [ { "key": { - "symbol": "status" + "symbol": "entries" }, "val": { "vec": [ { - "symbol": "Active" + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 1705184030 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 1702592030 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + ] } ] } }, { "key": { - "symbol": "subscriber" - }, - "val": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" - } - }, - { - "key": { - "symbol": "total_gas_spent" + "symbol": "subscription_id" }, "val": { - "u64": 0 + "u64": 1 } }, { "key": { - "symbol": "total_paid" + "symbol": "total_amount" }, "val": { "i128": { "hi": 0, - "lo": 0 + "lo": 500 } } } @@ -5901,20 +7781,16 @@ "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "symbol": "instance_set" + "symbol": "persistent_get" } ], "data": { "vec": [ { - "vec": [ - { - "symbol": "SubscriptionCount" - } - ] + "symbol": "RevenueRecognisedBalance" }, { - "u64": 2 + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" } ] } @@ -5935,7 +7811,7 @@ "symbol": "fn_return" }, { - "symbol": "instance_set" + "symbol": "persistent_get" } ], "data": "void" @@ -5965,10 +7841,10 @@ "data": { "vec": [ { - "symbol": "UserSubscriptions" + "symbol": "RevenueDeferredBalance" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" } ] } @@ -5992,13 +7868,7 @@ "symbol": "persistent_get" } ], - "data": { - "vec": [ - { - "u64": 1 - } - ] - } + "data": "void" } } }, @@ -6027,22 +7897,18 @@ { "vec": [ { - "symbol": "UserSubscriptions" + "symbol": "RevenueRecognisedBalance" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" } ] }, { - "vec": [ - { - "u64": 1 - }, - { - "u64": 2 - } - ] + "i128": { + "hi": 0, + "lo": 0 + } } ] } @@ -6095,18 +7961,18 @@ { "vec": [ { - "symbol": "UserPlanIndex" - }, - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + "symbol": "RevenueDeferredBalance" }, { - "u64": 2 + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" } ] }, { - "u64": 2 + "i128": { + "hi": 0, + "lo": 500 + } } ] } @@ -6136,6 +8002,60 @@ }, "failed_call": false }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_get" + } + ], + "data": { + "vec": [ + { + "symbol": "RevenueMerchantSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_get" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, { "event": { "ext": "v0", @@ -6159,93 +8079,17 @@ { "vec": [ { - "symbol": "Plan" + "symbol": "RevenueMerchantSubscriptions" }, { - "u64": 2 + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" } - ] - }, - { - "map": [ - { - "key": { - "symbol": "active" - }, - "val": { - "bool": true - } - }, - { - "key": { - "symbol": "created_at" - }, - "val": { - "u64": 1700000000 - } - }, - { - "key": { - "symbol": "id" - }, - "val": { - "u64": 2 - } - }, - { - "key": { - "symbol": "interval" - }, - "val": { - "vec": [ - { - "symbol": "Monthly" - } - ] - } - }, - { - "key": { - "symbol": "merchant" - }, - "val": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" - } - }, - { - "key": { - "symbol": "name" - }, - "val": { - "string": "Premium Plan" - } - }, - { - "key": { - "symbol": "price" - }, - "val": { - "i128": { - "hi": 0, - "lo": 900 - } - } - }, - { - "key": { - "symbol": "subscriber_count" - }, - "val": { - "u32": 1 - } - }, - { - "key": { - "symbol": "token" - }, - "val": { - "address": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45" - } + ] + }, + { + "vec": [ + { + "u64": 1 } ] } @@ -6281,25 +8125,62 @@ "event": { "ext": "v0", "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", - "type_": "diagnostic", + "type_": "contract", "body": { "v0": { "topics": [ { - "symbol": "fn_return" + "string": "subscription_charged" }, { - "symbol": "subscribe" + "u64": 1 } ], "data": { - "u64": 2 + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + }, + { + "u64": 100000 + }, + { + "u64": 1702592030 + } + ] } } } }, "failed_call": false }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "charge_subscription" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, { "event": { "ext": "v0", @@ -6312,12 +8193,10 @@ "symbol": "fn_return" }, { - "symbol": "subscribe" + "symbol": "charge_subscription" } ], - "data": { - "u64": 2 - } + "data": "void" } } }, @@ -6342,7 +8221,7 @@ } ], "data": { - "u64": 1 + "u64": 2 } } } @@ -6376,7 +8255,7 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" }, { - "u64": 1 + "u64": 2 } ] } @@ -6409,7 +8288,7 @@ "symbol": "Subscription" }, { - "u64": 1 + "u64": 2 } ] } @@ -6448,7 +8327,7 @@ "symbol": "id" }, "val": { - "u64": 1 + "u64": 2 } }, { @@ -6488,7 +8367,7 @@ "symbol": "plan_id" }, "val": { - "u64": 1 + "u64": 2 } }, { @@ -6687,7 +8566,7 @@ "symbol": "Plan" }, { - "u64": 1 + "u64": 2 } ] } @@ -6734,7 +8613,7 @@ "symbol": "id" }, "val": { - "u64": 1 + "u64": 2 } }, { @@ -6754,7 +8633,7 @@ "symbol": "merchant" }, "val": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" } }, { @@ -6762,7 +8641,7 @@ "symbol": "name" }, "val": { - "string": "Integration Plan" + "string": "Premium Plan" } }, { @@ -6772,7 +8651,7 @@ "val": { "i128": { "hi": 0, - "lo": 500 + "lo": 900 } } }, @@ -6789,7 +8668,7 @@ "symbol": "token" }, "val": { - "address": "CD3FXVGYSLQFFTW3UH6WFF2OKZH7VERGZJZAMJHTGHBWO4F6URWEJL23" + "address": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45" } } ] @@ -6811,7 +8690,7 @@ "symbol": "fn_call" }, { - "bytes": "f65bd4d892e052cedba1fd62974e564ffa9226ca720624f331c36770bea46c44" + "bytes": "ab09d2084c7dae35a44766e8b341392f390d98ee43b1612a002664c058fae9a6" }, { "symbol": "transfer" @@ -6823,12 +8702,12 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" }, { "i128": { "hi": 0, - "lo": 500 + "lo": 900 } } ] @@ -6841,7 +8720,7 @@ { "event": { "ext": "v0", - "contract_id": "f65bd4d892e052cedba1fd62974e564ffa9226ca720624f331c36770bea46c44", + "contract_id": "ab09d2084c7dae35a44766e8b341392f390d98ee43b1612a002664c058fae9a6", "type_": "contract", "body": { "v0": { @@ -6853,16 +8732,16 @@ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" }, { - "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUDE" + "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWM2U" } ], "data": { "i128": { "hi": 0, - "lo": 500 + "lo": 900 } } } @@ -6873,7 +8752,7 @@ { "event": { "ext": "v0", - "contract_id": "f65bd4d892e052cedba1fd62974e564ffa9226ca720624f331c36770bea46c44", + "contract_id": "ab09d2084c7dae35a44766e8b341392f390d98ee43b1612a002664c058fae9a6", "type_": "diagnostic", "body": { "v0": { @@ -6917,7 +8796,7 @@ "symbol": "Subscription" }, { - "u64": 1 + "u64": 2 } ] }, @@ -6936,7 +8815,7 @@ "symbol": "id" }, "val": { - "u64": 1 + "u64": 2 } }, { @@ -6976,7 +8855,7 @@ "symbol": "plan_id" }, "val": { - "u64": 1 + "u64": 2 } }, { @@ -7033,7 +8912,7 @@ "val": { "i128": { "hi": 0, - "lo": 500 + "lo": 900 } } } @@ -7052,159 +8931,16 @@ "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", "type_": "diagnostic", "body": { - "v0": { - "topics": [ - { - "symbol": "fn_return" - }, - { - "symbol": "persistent_set" - } - ], - "data": "void" - } - } - }, - "failed_call": false - }, - { - "event": { - "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", - "type_": "contract", - "body": { - "v0": { - "topics": [ - { - "string": "subscription_charged" - }, - { - "u64": 1 - } - ], - "data": { - "vec": [ - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" - }, - { - "i128": { - "hi": 0, - "lo": 500 - } - }, - { - "u64": 100000 - }, - { - "u64": 1702592030 - } - ] - } - } - } - }, - "failed_call": false - }, - { - "event": { - "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", - "type_": "diagnostic", - "body": { - "v0": { - "topics": [ - { - "symbol": "fn_return" - }, - { - "symbol": "charge_subscription" - } - ], - "data": "void" - } - } - }, - "failed_call": false - }, - { - "event": { - "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000007", - "type_": "diagnostic", - "body": { - "v0": { - "topics": [ - { - "symbol": "fn_return" - }, - { - "symbol": "charge_subscription" - } - ], - "data": "void" - } - } - }, - "failed_call": false - }, - { - "event": { - "ext": "v0", - "contract_id": null, - "type_": "diagnostic", - "body": { - "v0": { - "topics": [ - { - "symbol": "fn_call" - }, - { - "bytes": "0000000000000000000000000000000000000000000000000000000000000007" - }, - { - "symbol": "charge_subscription" - } - ], - "data": { - "u64": 2 - } - } - } - }, - "failed_call": false - }, - { - "event": { - "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000007", - "type_": "diagnostic", - "body": { - "v0": { - "topics": [ - { - "symbol": "fn_call" - }, - { - "bytes": "0000000000000000000000000000000000000000000000000000000000000006" - }, - { - "symbol": "charge_subscription" - } - ], - "data": { - "vec": [ - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOLZM" - }, - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" - }, - { - "u64": 2 - } - ] - } + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_set" + } + ], + "data": "void" } } }, @@ -7231,7 +8967,7 @@ "data": { "vec": [ { - "symbol": "Subscription" + "symbol": "RevenueRecognitionRule" }, { "u64": 2 @@ -7258,124 +8994,7 @@ "symbol": "persistent_get" } ], - "data": { - "map": [ - { - "key": { - "symbol": "charge_count" - }, - "val": { - "u32": 0 - } - }, - { - "key": { - "symbol": "id" - }, - "val": { - "u64": 2 - } - }, - { - "key": { - "symbol": "last_charged_at" - }, - "val": { - "u64": 1700000000 - } - }, - { - "key": { - "symbol": "next_charge_at" - }, - "val": { - "u64": 1702592000 - } - }, - { - "key": { - "symbol": "pause_duration" - }, - "val": { - "u64": 0 - } - }, - { - "key": { - "symbol": "paused_at" - }, - "val": { - "u64": 0 - } - }, - { - "key": { - "symbol": "plan_id" - }, - "val": { - "u64": 2 - } - }, - { - "key": { - "symbol": "refund_requested_amount" - }, - "val": { - "i128": { - "hi": 0, - "lo": 0 - } - } - }, - { - "key": { - "symbol": "started_at" - }, - "val": { - "u64": 1700000000 - } - }, - { - "key": { - "symbol": "status" - }, - "val": { - "vec": [ - { - "symbol": "Active" - } - ] - } - }, - { - "key": { - "symbol": "subscriber" - }, - "val": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" - } - }, - { - "key": { - "symbol": "total_gas_spent" - }, - "val": { - "u64": 0 - } - }, - { - "key": { - "symbol": "total_paid" - }, - "val": { - "i128": { - "hi": 0, - "lo": 0 - } - } - } - ] - } + "data": "void" } } }, @@ -7396,13 +9015,91 @@ "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "symbol": "instance_get" + "symbol": "persistent_set" } ], "data": { "vec": [ { - "symbol": "Admin" + "vec": [ + { + "symbol": "RevenueSchedule" + }, + { + "u64": 2 + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 1705184030 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 1702592030 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 900 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 2 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 900 + } + } + } + ] } ] } @@ -7423,12 +9120,10 @@ "symbol": "fn_return" }, { - "symbol": "instance_get" + "symbol": "persistent_set" } ], - "data": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" - } + "data": "void" } } }, @@ -7449,16 +9144,16 @@ "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "symbol": "instance_get" + "symbol": "persistent_get" } ], "data": { "vec": [ { - "symbol": "RateLimit" + "symbol": "RevenueRecognisedBalance" }, { - "string": "charge_subscription" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" } ] } @@ -7479,7 +9174,7 @@ "symbol": "fn_return" }, { - "symbol": "instance_get" + "symbol": "persistent_get" } ], "data": "void" @@ -7509,10 +9204,10 @@ "data": { "vec": [ { - "symbol": "Plan" + "symbol": "RevenueDeferredBalance" }, { - "u64": 2 + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" } ] } @@ -7534,87 +9229,48 @@ }, { "symbol": "persistent_get" - } - ], - "data": { - "map": [ - { - "key": { - "symbol": "active" - }, - "val": { - "bool": true - } - }, - { - "key": { - "symbol": "created_at" - }, - "val": { - "u64": 1700000000 - } - }, - { - "key": { - "symbol": "id" - }, - "val": { - "u64": 2 - } - }, - { - "key": { - "symbol": "interval" - }, - "val": { - "vec": [ - { - "symbol": "Monthly" - } - ] - } - }, - { - "key": { - "symbol": "merchant" - }, - "val": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" - } - }, - { - "key": { - "symbol": "name" - }, - "val": { - "string": "Premium Plan" - } - }, + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_set" + } + ], + "data": { + "vec": [ { - "key": { - "symbol": "price" - }, - "val": { - "i128": { - "hi": 0, - "lo": 900 + "vec": [ + { + "symbol": "RevenueRecognisedBalance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" } - } - }, - { - "key": { - "symbol": "subscriber_count" - }, - "val": { - "u32": 1 - } + ] }, { - "key": { - "symbol": "token" - }, - "val": { - "address": "CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45" + "i128": { + "hi": 0, + "lo": 0 } } ] @@ -7624,6 +9280,27 @@ }, "failed_call": false }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_set" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, { "event": { "ext": "v0", @@ -7636,19 +9313,23 @@ "symbol": "fn_call" }, { - "bytes": "ab09d2084c7dae35a44766e8b341392f390d98ee43b1612a002664c058fae9a6" + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "symbol": "transfer" + "symbol": "persistent_set" } ], "data": { "vec": [ { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" - }, - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + "vec": [ + { + "symbol": "RevenueDeferredBalance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + } + ] }, { "i128": { @@ -7666,29 +9347,51 @@ { "event": { "ext": "v0", - "contract_id": "ab09d2084c7dae35a44766e8b341392f390d98ee43b1612a002664c058fae9a6", - "type_": "contract", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", "body": { "v0": { "topics": [ { - "symbol": "transfer" + "symbol": "fn_return" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + "symbol": "persistent_set" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" }, { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" }, { - "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWM2U" + "symbol": "persistent_get" } ], "data": { - "i128": { - "hi": 0, - "lo": 900 - } + "vec": [ + { + "symbol": "RevenueMerchantSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" + } + ] } } } @@ -7698,7 +9401,7 @@ { "event": { "ext": "v0", - "contract_id": "ab09d2084c7dae35a44766e8b341392f390d98ee43b1612a002664c058fae9a6", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", "type_": "diagnostic", "body": { "v0": { @@ -7707,7 +9410,7 @@ "symbol": "fn_return" }, { - "symbol": "transfer" + "symbol": "persistent_get" } ], "data": "void" @@ -7739,128 +9442,17 @@ { "vec": [ { - "symbol": "Subscription" + "symbol": "RevenueMerchantSubscriptions" }, { - "u64": 2 + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON" } ] }, { - "map": [ - { - "key": { - "symbol": "charge_count" - }, - "val": { - "u32": 1 - } - }, - { - "key": { - "symbol": "id" - }, - "val": { - "u64": 2 - } - }, - { - "key": { - "symbol": "last_charged_at" - }, - "val": { - "u64": 1702592030 - } - }, - { - "key": { - "symbol": "next_charge_at" - }, - "val": { - "u64": 1705184030 - } - }, - { - "key": { - "symbol": "pause_duration" - }, - "val": { - "u64": 0 - } - }, - { - "key": { - "symbol": "paused_at" - }, - "val": { - "u64": 0 - } - }, - { - "key": { - "symbol": "plan_id" - }, - "val": { - "u64": 2 - } - }, - { - "key": { - "symbol": "refund_requested_amount" - }, - "val": { - "i128": { - "hi": 0, - "lo": 0 - } - } - }, - { - "key": { - "symbol": "started_at" - }, - "val": { - "u64": 1700000000 - } - }, - { - "key": { - "symbol": "status" - }, - "val": { - "vec": [ - { - "symbol": "Active" - } - ] - } - }, - { - "key": { - "symbol": "subscriber" - }, - "val": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" - } - }, - { - "key": { - "symbol": "total_gas_spent" - }, - "val": { - "u64": 100000 - } - }, + "vec": [ { - "key": { - "symbol": "total_paid" - }, - "val": { - "i128": { - "hi": 0, - "lo": 900 - } - } + "u64": 2 } ] } diff --git a/contracts/proxy/test_snapshots/integration_uses_actual_token_contract_for_charges.1.json b/contracts/proxy/test_snapshots/integration_uses_actual_token_contract_for_charges.1.json index 062f218..7568a61 100644 --- a/contracts/proxy/test_snapshots/integration_uses_actual_token_contract_for_charges.1.json +++ b/contracts/proxy/test_snapshots/integration_uses_actual_token_contract_for_charges.1.json @@ -634,6 +634,264 @@ 4095 ] ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "RevenueDeferredBalance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "RevenueDeferredBalance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent", + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "RevenueMerchantSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "RevenueMerchantSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent", + "val": { + "vec": [ + { + "u64": 1 + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "RevenueRecognisedBalance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "RevenueRecognisedBalance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent", + "val": { + "i128": { + "hi": 0, + "lo": 0 + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "RevenueSchedule" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "vec": [ + { + "symbol": "RevenueSchedule" + }, + { + "u64": 1 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 1705184010 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 1702592010 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], [ { "contract_data": { @@ -4293,6 +4551,544 @@ }, "failed_call": false }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_get" + } + ], + "data": { + "vec": [ + { + "symbol": "RevenueRecognitionRule" + }, + { + "u64": 1 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_get" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_set" + } + ], + "data": { + "vec": [ + { + "vec": [ + { + "symbol": "RevenueSchedule" + }, + { + "u64": 1 + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "entries" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "is_recognised" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "period_end" + }, + "val": { + "u64": 1705184010 + } + }, + { + "key": { + "symbol": "period_start" + }, + "val": { + "u64": 1702592010 + } + }, + { + "key": { + "symbol": "recognised_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "subscription_id" + }, + "val": { + "u64": 1 + } + }, + { + "key": { + "symbol": "total_amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 500 + } + } + } + ] + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_set" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_get" + } + ], + "data": { + "vec": [ + { + "symbol": "RevenueRecognisedBalance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_get" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_get" + } + ], + "data": { + "vec": [ + { + "symbol": "RevenueDeferredBalance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_get" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_set" + } + ], + "data": { + "vec": [ + { + "vec": [ + { + "symbol": "RevenueRecognisedBalance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + { + "i128": { + "hi": 0, + "lo": 0 + } + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_set" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_set" + } + ], + "data": { + "vec": [ + { + "vec": [ + { + "symbol": "RevenueDeferredBalance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + { + "i128": { + "hi": 0, + "lo": 500 + } + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_set" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_get" + } + ], + "data": { + "vec": [ + { + "symbol": "RevenueMerchantSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_get" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000006", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000005" + }, + { + "symbol": "persistent_set" + } + ], + "data": { + "vec": [ + { + "vec": [ + { + "symbol": "RevenueMerchantSubscriptions" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + { + "vec": [ + { + "u64": 1 + } + ] + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000005", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "persistent_set" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, { "event": { "ext": "v0", diff --git a/contracts/proxy/tests/upgrade_flow.rs b/contracts/proxy/tests/upgrade_flow.rs index 8ebdc5b..cb48551 100644 --- a/contracts/proxy/tests/upgrade_flow.rs +++ b/contracts/proxy/tests/upgrade_flow.rs @@ -1,3 +1,4 @@ +#![allow(clippy::too_many_arguments)] use soroban_sdk::{ contract, contractimpl, testutils::{Address as _, Ledger}, @@ -18,15 +19,27 @@ fn storage_instance_get>( key: StorageKey, ) -> Option { let args: Vec = soroban_sdk::vec![env, key.into_val(env)]; - let val_opt: Option = - env.invoke_contract(storage, &soroban_sdk::Symbol::new(env, "instance_get"), args); + let val_opt: Option = env.invoke_contract( + storage, + &soroban_sdk::Symbol::new(env, "instance_get"), + args, + ); val_opt.map(|val| V::try_from_val(env, &val).unwrap()) } -fn storage_instance_set>(env: &Env, storage: &Address, key: StorageKey, value: V) { +fn storage_instance_set>( + env: &Env, + storage: &Address, + key: StorageKey, + value: V, +) { let val: Val = value.into_val(env); let args: Vec = soroban_sdk::vec![env, key.into_val(env), val]; - env.invoke_contract::<()>(storage, &soroban_sdk::Symbol::new(env, "instance_set"), args); + env.invoke_contract::<()>( + storage, + &soroban_sdk::Symbol::new(env, "instance_set"), + args, + ); } fn storage_persistent_get>( @@ -35,12 +48,20 @@ fn storage_persistent_get>( key: StorageKey, ) -> Option { let args: Vec = soroban_sdk::vec![env, key.into_val(env)]; - let val_opt: Option = - env.invoke_contract(storage, &soroban_sdk::Symbol::new(env, "persistent_get"), args); + let val_opt: Option = env.invoke_contract( + storage, + &soroban_sdk::Symbol::new(env, "persistent_get"), + args, + ); val_opt.map(|val| V::try_from_val(env, &val).unwrap()) } -fn storage_persistent_set>(env: &Env, storage: &Address, key: StorageKey, value: V) { +fn storage_persistent_set>( + env: &Env, + storage: &Address, + key: StorageKey, + value: V, +) { let val: Val = value.into_val(env); let args: Vec = soroban_sdk::vec![env, key.into_val(env), val]; env.invoke_contract::<()>( @@ -134,8 +155,8 @@ mod v1_impl { .expect("Plan not found"); assert!(plan.active, "Plan is not active"); - let mut sub_count: u64 = storage_instance_get(&env, &storage, StorageKey::SubscriptionCount) - .unwrap_or(0); + let mut sub_count: u64 = + storage_instance_get(&env, &storage, StorageKey::SubscriptionCount).unwrap_or(0); sub_count += 1; let now = env.ledger().timestamp(); @@ -163,7 +184,8 @@ mod v1_impl { pub fn get_plan(env: Env, proxy: Address, storage: Address, plan_id: u64) -> Plan { proxy.require_auth(); - storage_persistent_get(&env, &storage, StorageKey::Plan(plan_id)).expect("Plan not found") + storage_persistent_get(&env, &storage, StorageKey::Plan(plan_id)) + .expect("Plan not found") } pub fn get_subscription( diff --git a/contracts/src/lib.rs b/contracts/src/lib.rs deleted file mode 100644 index d463c31..0000000 --- a/contracts/src/lib.rs +++ /dev/null @@ -1,1209 +0,0 @@ -#![no_std] - -pub mod revenue; - -use soroban_sdk::{contract, contractimpl, contracttype, token, Address, Env, String, Vec}; - -/// Billing interval in seconds -#[contracttype] -#[derive(Clone, Debug, PartialEq)] -pub enum Interval { - Weekly, // 604800s - Monthly, // 2592000s (30 days) - Quarterly, // 7776000s (90 days) - Yearly, // 31536000s (365 days) -} - -const MAX_PAUSE_DURATION: u64 = 2_592_000; // 30 days - -impl Interval { - pub fn seconds(&self) -> u64 { - match self { - Interval::Weekly => 604_800, - Interval::Monthly => 2_592_000, - Interval::Quarterly => 7_776_000, - Interval::Yearly => 31_536_000, - } - } -} - -#[contracttype] -#[derive(Clone, Debug, PartialEq)] -pub enum SubscriptionStatus { - Active, - Paused, - Cancelled, - PastDue, -} - -/// A subscription plan created by a merchant -#[contracttype] -#[derive(Clone, Debug)] -pub struct Plan { - pub id: u64, - pub merchant: Address, - pub name: String, - pub price: i128, // price per interval in stroops (XLM smallest unit) - pub token: Address, // token address (native XLM or Stellar asset) - pub interval: Interval, - pub active: bool, - pub subscriber_count: u32, - pub created_at: u64, -} - -/// A user's subscription to a plan -#[contracttype] -#[derive(Clone, Debug)] -pub struct Subscription { - pub id: u64, - pub plan_id: u64, - pub subscriber: Address, - pub status: SubscriptionStatus, - pub started_at: u64, - pub last_charged_at: u64, - pub next_charge_at: u64, - pub total_paid: i128, - pub total_gas_spent: u64, - pub charge_count: u32, - pub paused_at: u64, - pub pause_duration: u64, - pub refund_requested_amount: i128, -} - -#[contracttype] -pub enum DataKey { - Plan(u64), - PlanCount, - Subscription(u64), - SubscriptionCount, - UserSubscriptions(Address), - MerchantPlans(Address), - Admin, - /// Minimum seconds between calls for a given function (by name) - RateLimit(String), - /// Last timestamp (seconds) a caller invoked a function (by function name) - LastCall(Address, String), - /// Pending transfer request: subscription_id -> pending recipient - PendingTransfer(u64), -} - -#[contract] -pub struct SubTrackrContract; - -#[contractimpl] -impl SubTrackrContract { - /// Initialize the contract - pub fn initialize(env: Env, admin: Address) { - admin.require_auth(); - env.storage().instance().set(&DataKey::Admin, &admin); - env.storage().instance().set(&DataKey::PlanCount, &0u64); - env.storage() - .instance() - .set(&DataKey::SubscriptionCount, &0u64); - } - - // ── Rate Limiting Admin ── - - /// Set minimum seconds between calls for a function name (admin only). - pub fn set_rate_limit(env: Env, function: String, min_interval_secs: u64) { - let admin = Self::get_admin(&env); - admin.require_auth(); - env.storage() - .instance() - .set(&DataKey::RateLimit(function), &min_interval_secs); - } - - /// Remove rate limit for a function name (admin only). - pub fn remove_rate_limit(env: Env, function: String) { - let admin = Self::get_admin(&env); - admin.require_auth(); - env.storage() - .instance() - .remove(&DataKey::RateLimit(function)); - } - - // ── Plan Management ── - - /// Merchant creates a subscription plan - pub fn create_plan( - env: Env, - merchant: Address, - name: String, - price: i128, - token: Address, - interval: Interval, - ) -> u64 { - // Admin override: admin bypasses rate limits; otherwise enforce for caller - if merchant != Self::get_admin(&env) { - Self::enforce_rate_limit(&env, &merchant, "create_plan"); - } - merchant.require_auth(); - assert!(price > 0, "Price must be positive"); - - let mut count: u64 = env - .storage() - .instance() - .get(&DataKey::PlanCount) - .unwrap_or(0); - count += 1; - - let plan = Plan { - id: count, - merchant: merchant.clone(), - name, - price, - token, - interval, - active: true, - subscriber_count: 0, - created_at: env.ledger().timestamp(), - }; - - env.storage().persistent().set(&DataKey::Plan(count), &plan); - env.storage().instance().set(&DataKey::PlanCount, &count); - - // Track merchant's plans - let mut merchant_plans: Vec = env - .storage() - .persistent() - .get(&DataKey::MerchantPlans(merchant.clone())) - .unwrap_or(Vec::new(&env)); - merchant_plans.push_back(count); - env.storage() - .persistent() - .set(&DataKey::MerchantPlans(merchant), &merchant_plans); - - count - } - - /// Merchant deactivates a plan (no new subscribers, existing ones continue) - pub fn deactivate_plan(env: Env, merchant: Address, plan_id: u64) { - if merchant != Self::get_admin(&env) { - Self::enforce_rate_limit(&env, &merchant, "deactivate_plan"); - } - merchant.require_auth(); - - let mut plan: Plan = env - .storage() - .persistent() - .get(&DataKey::Plan(plan_id)) - .expect("Plan not found"); - - assert!(plan.merchant == merchant, "Only plan owner can deactivate"); - plan.active = false; - - env.storage() - .persistent() - .set(&DataKey::Plan(plan_id), &plan); - } - - // ── Subscription Management ── - - /// User subscribes to a plan - pub fn subscribe(env: Env, subscriber: Address, plan_id: u64) -> u64 { - if subscriber != Self::get_admin(&env) { - Self::enforce_rate_limit(&env, &subscriber, "subscribe"); - } - subscriber.require_auth(); - - let mut plan: Plan = env - .storage() - .persistent() - .get(&DataKey::Plan(plan_id)) - .expect("Plan not found"); - assert!(plan.active, "Plan is not active"); - assert!( - plan.merchant != subscriber, - "Merchant cannot self-subscribe" - ); - - let user_subs: Vec = env - .storage() - .persistent() - .get(&DataKey::UserSubscriptions(subscriber.clone())) - .unwrap_or(Vec::new(&env)); - for sub_id in user_subs.iter() { - let existing_sub: Subscription = env - .storage() - .persistent() - .get(&DataKey::Subscription(sub_id)) - .expect("Subscription not found"); - if existing_sub.plan_id == plan_id - && existing_sub.status != SubscriptionStatus::Cancelled - { - panic!("Already subscribed to this plan"); - } - } - - let mut sub_count: u64 = env - .storage() - .instance() - .get(&DataKey::SubscriptionCount) - .unwrap_or(0); - sub_count += 1; - - let now = env.ledger().timestamp(); - - let subscription = Subscription { - id: sub_count, - plan_id, - subscriber: subscriber.clone(), - status: SubscriptionStatus::Active, - started_at: now, - last_charged_at: now, - next_charge_at: now + plan.interval.seconds(), - total_paid: 0, - total_gas_spent: 0, - charge_count: 0, - paused_at: 0, - pause_duration: 0, - refund_requested_amount: 0, - }; - - env.storage() - .persistent() - .set(&DataKey::Subscription(sub_count), &subscription); - env.storage() - .instance() - .set(&DataKey::SubscriptionCount, &sub_count); - - // Track user's subscriptions - let mut user_subs: Vec = env - .storage() - .persistent() - .get(&DataKey::UserSubscriptions(subscriber.clone())) - .unwrap_or(Vec::new(&env)); - user_subs.push_back(sub_count); - env.storage() - .persistent() - .set(&DataKey::UserSubscriptions(subscriber), &user_subs); - - // Increment plan subscriber count - plan.subscriber_count += 1; - env.storage() - .persistent() - .set(&DataKey::Plan(plan_id), &plan); - - sub_count - } - - /// User cancels their subscription - pub fn cancel_subscription(env: Env, subscriber: Address, subscription_id: u64) { - if subscriber != Self::get_admin(&env) { - Self::enforce_rate_limit(&env, &subscriber, "cancel_subscription"); - } - subscriber.require_auth(); - - let mut sub: Subscription = env - .storage() - .persistent() - .get(&DataKey::Subscription(subscription_id)) - .expect("Subscription not found"); - - assert!(sub.subscriber == subscriber, "Only subscriber can cancel"); - assert!( - sub.status == SubscriptionStatus::Active || sub.status == SubscriptionStatus::Paused, - "Subscription not active" - ); - - sub.status = SubscriptionStatus::Cancelled; - - env.storage() - .persistent() - .set(&DataKey::Subscription(subscription_id), &sub); - - // Decrement plan subscriber count - let mut plan: Plan = env - .storage() - .persistent() - .get(&DataKey::Plan(sub.plan_id)) - .expect("Plan not found"); - if plan.subscriber_count > 0 { - plan.subscriber_count -= 1; - } - env.storage() - .persistent() - .set(&DataKey::Plan(sub.plan_id), &plan); - } - - /// User pauses their subscription - pub fn pause_subscription(env: Env, subscriber: Address, subscription_id: u64) { - if subscriber != Self::get_admin(&env) { - Self::enforce_rate_limit(&env, &subscriber, "pause_subscription"); - } - Self::pause_by_subscriber(env, subscriber, subscription_id, MAX_PAUSE_DURATION); - } - - /// User pauses their subscription with a specific duration - pub fn pause_by_subscriber(env: Env, subscriber: Address, subscription_id: u64, duration: u64) { - if subscriber != Self::get_admin(&env) { - Self::enforce_rate_limit(&env, &subscriber, "pause_by_subscriber"); - } - subscriber.require_auth(); - - let mut sub: Subscription = env - .storage() - .persistent() - .get(&DataKey::Subscription(subscription_id)) - .expect("Subscription not found"); - - assert!(sub.subscriber == subscriber, "Only subscriber can pause"); - assert!( - sub.status == SubscriptionStatus::Active, - "Only active subscriptions can be paused" - ); - assert!( - duration <= MAX_PAUSE_DURATION, - "Pause duration exceeds limit" - ); - - sub.status = SubscriptionStatus::Paused; - sub.paused_at = env.ledger().timestamp(); - sub.pause_duration = duration; - - env.storage() - .persistent() - .set(&DataKey::Subscription(subscription_id), &sub); - - // Publish event - env.events().publish( - (String::from_str(&env, "subscription_paused"), subscriber), - (subscription_id, sub.paused_at, duration), - ); - } - - /// User resumes a paused subscription - pub fn resume_subscription(env: Env, subscriber: Address, subscription_id: u64) { - if subscriber != Self::get_admin(&env) { - Self::enforce_rate_limit(&env, &subscriber, "resume_subscription"); - } - subscriber.require_auth(); - - let mut sub: Subscription = env - .storage() - .persistent() - .get(&DataKey::Subscription(subscription_id)) - .expect("Subscription not found"); - - assert!(sub.subscriber == subscriber, "Only subscriber can resume"); - assert!( - sub.status == SubscriptionStatus::Paused - || Self::check_and_resume_internal(&env, &mut sub), - "Only paused subscriptions can be resumed" - ); - - let now = env.ledger().timestamp(); - let plan: Plan = env - .storage() - .persistent() - .get(&DataKey::Plan(sub.plan_id)) - .expect("Plan not found"); - - sub.status = SubscriptionStatus::Active; - sub.next_charge_at = now + plan.interval.seconds(); - sub.paused_at = 0; - sub.pause_duration = 0; - - env.storage() - .persistent() - .set(&DataKey::Subscription(subscription_id), &sub); - - // Publish event - env.events().publish( - (String::from_str(&env, "subscription_resumed"), subscriber), - subscription_id, - ); - } - - // ── Payment Processing ── - - /// Process a due payment for a subscription (callable by anyone — typically a cron/bot) - pub fn charge_subscription(env: Env, subscription_id: u64) { - let mut sub: Subscription = env - .storage() - .persistent() - .get(&DataKey::Subscription(subscription_id)) - .expect("Subscription not found"); - - if sub.subscriber != Self::get_admin(&env) { - // Rate limit by the subscriber address (payer) to avoid spamming their own subscription - Self::enforce_rate_limit(&env, &sub.subscriber, "charge_subscription"); - } - - sub.subscriber.require_auth(); - - // Handle auto-resume if needed - if Self::check_and_resume_internal(&env, &mut sub) { - env.storage() - .persistent() - .set(&DataKey::Subscription(subscription_id), &sub); - } - - assert!( - sub.status == SubscriptionStatus::Active, - "Subscription not active" - ); - - let now = env.ledger().timestamp(); - assert!(now >= sub.next_charge_at, "Payment not yet due"); - - let plan: Plan = env - .storage() - .persistent() - .get(&DataKey::Plan(sub.plan_id)) - .expect("Plan not found"); - - // Execute actual token transfer from subscriber to merchant - token::Client::new(&env, &plan.token).transfer( - &sub.subscriber, - &plan.merchant, - &plan.price, - ); - - sub.last_charged_at = now; - sub.next_charge_at = now + plan.interval.seconds(); - sub.total_paid += plan.price; - sub.total_gas_spent += 100_000; // Simulated gas cost (0.01 XLM) - sub.charge_count += 1; - - env.storage() - .persistent() - .set(&DataKey::Subscription(subscription_id), &sub); - - // Generate revenue recognition schedule for this charge. - revenue::generate_revenue_schedule( - &env, - subscription_id, - sub.plan_id, - plan.price, - now, - plan.interval.seconds(), - ); - // All newly charged revenue starts as deferred. - revenue::update_merchant_revenue_balances(&env, &plan.merchant, 0, plan.price); - // Track subscription under merchant for analytics. - revenue::track_merchant_subscription(&env, &plan.merchant, subscription_id); - - // Publish event - env.events().publish( - ( - String::from_str(&env, "subscription_charged"), - subscription_id, - ), - (sub.subscriber.clone(), plan.price, 100_000u64, now), - ); - } - - /// Request a refund for a subscription (can only be called by the subscriber) - pub fn request_refund(env: Env, subscription_id: u64, amount: i128) { - let mut sub: Subscription = env - .storage() - .persistent() - .get(&DataKey::Subscription(subscription_id)) - .expect("Subscription not found"); - - if sub.subscriber != Self::get_admin(&env) { - Self::enforce_rate_limit(&env, &sub.subscriber, "request_refund"); - } - - sub.subscriber.require_auth(); - - assert!(amount > 0, "Refund amount must be positive"); - assert!( - amount <= sub.total_paid, - "Refund amount cannot exceed total paid" - ); - - sub.refund_requested_amount = amount; - - env.storage() - .persistent() - .set(&DataKey::Subscription(subscription_id), &sub); - - // Publish event - env.events().publish( - (String::from_str(&env, "refund_requested"), subscription_id), - (sub.subscriber.clone(), amount), - ); - } - - /// Approve a refund (can only be called by the admin) - pub fn approve_refund(env: Env, subscription_id: u64) { - // Admin may be high-frequency; still allow limits if configured, but admin is exempt - let mut sub: Subscription = env - .storage() - .persistent() - .get(&DataKey::Subscription(subscription_id)) - .expect("Subscription not found"); - - let admin: Address = env - .storage() - .instance() - .get(&DataKey::Admin) - .expect("Admin not set"); - // Do not enforce for admin - admin.require_auth(); - - let amount = sub.refund_requested_amount; - assert!(amount > 0, "No pending refund request"); - - let _plan: Plan = env - .storage() - .persistent() - .get(&DataKey::Plan(sub.plan_id)) - .expect("Plan not found"); - - // TODO: Execute actual token transfer from merchant back to subscriber - // token::Client::new(&env, &plan.token).transfer( - // &plan.merchant, &sub.subscriber, &amount - // ); - - sub.total_paid -= amount; - sub.refund_requested_amount = 0; - - env.storage() - .persistent() - .set(&DataKey::Subscription(subscription_id), &sub); - - // Publish event - env.events().publish( - (String::from_str(&env, "refund_approved"), subscription_id), - (sub.subscriber.clone(), amount), - ); - } - - /// Reject a refund (can only be called by the admin) - pub fn reject_refund(env: Env, subscription_id: u64) { - // Admin exempt - let mut sub: Subscription = env - .storage() - .persistent() - .get(&DataKey::Subscription(subscription_id)) - .expect("Subscription not found"); - - let admin: Address = env - .storage() - .instance() - .get(&DataKey::Admin) - .expect("Admin not set"); - admin.require_auth(); - - assert!(sub.refund_requested_amount > 0, "No pending refund request"); - - sub.refund_requested_amount = 0; - - env.storage() - .persistent() - .set(&DataKey::Subscription(subscription_id), &sub); - - // Publish event - env.events().publish( - (String::from_str(&env, "refund_rejected"), subscription_id), - sub.subscriber.clone(), - ); - } - - // ── Subscription Transfer ── - - /// Current subscriber requests to transfer a subscription to `recipient`. - /// Requires subscriber auth. Records a pending transfer for later acceptance. - pub fn request_transfer(env: Env, subscription_id: u64, recipient: Address) { - let mut sub: Subscription = env - .storage() - .persistent() - .get(&DataKey::Subscription(subscription_id)) - .expect("Subscription not found"); - - // Rate limit by current subscriber - if sub.subscriber != Self::get_admin(&env) { - Self::enforce_rate_limit(&env, &sub.subscriber, "request_transfer"); - } - - sub.subscriber.require_auth(); - assert!( - sub.status != SubscriptionStatus::Cancelled, - "Subscription is cancelled" - ); - assert!(sub.subscriber != recipient, "Cannot transfer to self"); - - env.storage() - .instance() - .set(&DataKey::PendingTransfer(subscription_id), &recipient); - - env.events().publish( - ( - String::from_str(&env, "transfer_requested"), - subscription_id, - ), - (sub.subscriber.clone(), recipient.clone()), - ); - } - - /// Recipient accepts a pending transfer. - /// Requires recipient auth. Moves subscription ownership and updates indices. - pub fn accept_transfer(env: Env, subscription_id: u64, recipient: Address) { - // Require recipient auth and rate-limit by recipient - if recipient != Self::get_admin(&env) { - Self::enforce_rate_limit(&env, &recipient, "accept_transfer"); - } - recipient.require_auth(); - - let mut sub: Subscription = env - .storage() - .persistent() - .get(&DataKey::Subscription(subscription_id)) - .expect("Subscription not found"); - - // Verify pending transfer exists and matches recipient - let pending_recipient: Address = env - .storage() - .instance() - .get(&DataKey::PendingTransfer(subscription_id)) - .expect("No pending transfer for this subscription"); - assert!( - pending_recipient == recipient, - "Transfer recipient mismatch" - ); - - // Update user subscription indices: remove from old, add to new - let mut old_user_subs: Vec = env - .storage() - .persistent() - .get(&DataKey::UserSubscriptions(sub.subscriber.clone())) - .unwrap_or(Vec::new(&env)); - // Remove subscription_id from old list - let mut new_list: Vec = Vec::new(&env); - for id in old_user_subs.iter() { - if id != subscription_id { - new_list.push_back(id); - } - } - env.storage().persistent().set( - &DataKey::UserSubscriptions(sub.subscriber.clone()), - &new_list, - ); - - // Add to new recipient list - let mut rec_user_subs: Vec = env - .storage() - .persistent() - .get(&DataKey::UserSubscriptions(recipient.clone())) - .unwrap_or(Vec::new(&env)); - rec_user_subs.push_back(subscription_id); - env.storage().persistent().set( - &DataKey::UserSubscriptions(recipient.clone()), - &rec_user_subs, - ); - - // Move ownership - let old = sub.subscriber.clone(); - sub.subscriber = recipient.clone(); - env.storage() - .persistent() - .set(&DataKey::Subscription(subscription_id), &sub); - - // Clear pending transfer - env.storage() - .instance() - .remove(&DataKey::PendingTransfer(subscription_id)); - - env.events().publish( - (String::from_str(&env, "transfer_accepted"), subscription_id), - (old, recipient), - ); - } - - // ── Revenue Recognition API ── - - /// Set a revenue recognition rule for a plan (merchant only). - pub fn set_revenue_rule( - env: Env, - merchant: Address, - plan_id: u64, - method: revenue::RecognitionMethod, - recognition_period: u64, - ) { - merchant.require_auth(); - let plan: Plan = env - .storage() - .persistent() - .get(&DataKey::Plan(plan_id)) - .expect("Plan not found"); - assert!(plan.merchant == merchant, "Only plan owner can set revenue rule"); - revenue::set_recognition_rule( - &env, - revenue::RevenueRecognitionRule { plan_id, method, recognition_period }, - ); - } - - /// Compute a recognition snapshot for a subscription as of the current ledger time. - pub fn recognize_revenue(env: Env, subscription_id: u64) -> revenue::Recognition { - let sub: Subscription = env - .storage() - .persistent() - .get(&DataKey::Subscription(subscription_id)) - .expect("Subscription not found"); - let plan: Plan = env - .storage() - .persistent() - .get(&DataKey::Plan(sub.plan_id)) - .expect("Plan not found"); - let now = env.ledger().timestamp(); - revenue::recognize_revenue(&env, subscription_id, plan.merchant, now) - } - - /// Return the cumulative deferred revenue balance for a merchant. - pub fn get_deferred_revenue(env: Env, merchant_id: Address) -> i128 { - revenue::get_deferred_revenue(&env, merchant_id) - } - - /// Return the revenue schedule for a subscription (None if not yet generated). - pub fn get_revenue_schedule(env: Env, subscription_id: u64) -> Option { - revenue::get_revenue_schedule(&env, subscription_id) - } - - // ── Queries ── - - /// Get plan details - pub fn get_plan(env: Env, plan_id: u64) -> Plan { - env.storage() - .persistent() - .get(&DataKey::Plan(plan_id)) - .expect("Plan not found") - } - - /// Get subscription details - pub fn get_subscription(env: Env, subscription_id: u64) -> Subscription { - let mut sub: Subscription = env - .storage() - .persistent() - .get(&DataKey::Subscription(subscription_id)) - .expect("Subscription not found"); - - Self::check_and_resume_internal(&env, &mut sub); - sub - } - - /// Get all subscription IDs for a user - pub fn get_user_subscriptions(env: Env, subscriber: Address) -> Vec { - env.storage() - .persistent() - .get(&DataKey::UserSubscriptions(subscriber)) - .unwrap_or(Vec::new(&env)) - } - - /// Get all plan IDs for a merchant - pub fn get_merchant_plans(env: Env, merchant: Address) -> Vec { - env.storage() - .persistent() - .get(&DataKey::MerchantPlans(merchant)) - .unwrap_or(Vec::new(&env)) - } - - /// Get total plan count - pub fn get_plan_count(env: Env) -> u64 { - env.storage() - .instance() - .get(&DataKey::PlanCount) - .unwrap_or(0) - } - - /// Get total subscription count - pub fn get_subscription_count(env: Env) -> u64 { - env.storage() - .instance() - .get(&DataKey::SubscriptionCount) - .unwrap_or(0) - } - - // ── Internal Helpers ── - - fn get_admin(env: &Env) -> Address { - env.storage() - .instance() - .get(&DataKey::Admin) - .expect("Admin not set") - } - - /// If a rate limit is configured for `function_name`, enforce min interval per-caller. - fn enforce_rate_limit(env: &Env, caller: &Address, function_name: &str) { - // Read min interval; if none, return - let fname = String::from_str(env, function_name); - let min_interval: Option = env - .storage() - .instance() - .get(&DataKey::RateLimit(fname.clone())); - if min_interval.is_none() { - return; - } - let min_secs = min_interval.unwrap(); - if min_secs == 0 { - return; - } - - let now = env.ledger().timestamp(); - let last_opt: Option = env - .storage() - .instance() - .get(&DataKey::LastCall(caller.clone(), fname.clone())); - - if let Some(last) = last_opt { - if now < last + min_secs { - // Emit violation event, then revert with clear message - env.events().publish( - ( - String::from_str(env, "rate_limit_violation"), - caller.clone(), - ), - (fname.clone(), last, now, min_secs), - ); - panic!("Rate limited: please wait before calling this function again"); - } - } - - // Record last call timestamp - env.storage() - .instance() - .set(&DataKey::LastCall(caller.clone(), fname), &now); - } - - fn check_and_resume_internal(env: &Env, sub: &mut Subscription) -> bool { - if sub.status == SubscriptionStatus::Paused { - let now = env.ledger().timestamp(); - if now >= sub.paused_at + sub.pause_duration { - sub.status = SubscriptionStatus::Active; - sub.paused_at = 0; - sub.pause_duration = 0; - return true; - } - } - false - } -} - -#[cfg(test)] -mod test { - use super::*; - use soroban_sdk::testutils::{Address as _, Ledger}; - use soroban_sdk::Env; - - #[contract] - pub struct MockToken; - - #[contractimpl] - impl MockToken { - pub fn transfer(_env: Env, _from: Address, _to: Address, _amount: i128) {} - } - - fn setup( - env: &Env, - ) -> ( - SubTrackrContractClient<'_>, - Address, - Address, - Address, - Address, - ) { - let contract_id = env.register_contract(None, SubTrackrContract); - let client = SubTrackrContractClient::new(env, &contract_id); - - let admin = Address::generate(env); - let merchant = Address::generate(env); - let subscriber = Address::generate(env); - let token = env.register_contract(None, MockToken); - - env.mock_all_auths(); - client.initialize(&admin); - client.create_plan( - &merchant, - &String::from_str(env, "Basic"), - &500_i128, - &token, - &Interval::Monthly, - ); - - (client, admin, merchant, subscriber, token) - } - - #[test] - fn test_create_plan_and_subscribe() { - let env = Env::default(); - let contract_id = env.register_contract(None, SubTrackrContract); - let client = SubTrackrContractClient::new(&env, &contract_id); - - let admin = Address::generate(&env); - let merchant = Address::generate(&env); - let subscriber = Address::generate(&env); - let token = Address::generate(&env); - - env.mock_all_auths(); - - client.initialize(&admin); - - let plan_id = client.create_plan( - &merchant, - &String::from_str(&env, "Pro Plan"), - &1000_i128, - &token, - &Interval::Monthly, - ); - - assert_eq!(plan_id, 1); - assert_eq!(client.get_plan_count(), 1); - - let plan = client.get_plan(&1); - assert_eq!(plan.price, 1000); - assert!(plan.active); - - let sub_id = client.subscribe(&subscriber, &1); - assert_eq!(sub_id, 1); - - let sub = client.get_subscription(&1); - assert_eq!(sub.status, SubscriptionStatus::Active); - assert_eq!(sub.plan_id, 1); - - let user_subs = client.get_user_subscriptions(&subscriber); - assert_eq!(user_subs.len(), 1); - } - - #[test] - fn test_cancel_subscription() { - let env = Env::default(); - let (client, _admin, _merchant, subscriber, _token) = setup(&env); - client.subscribe(&subscriber, &1); - - client.cancel_subscription(&subscriber, &1); - - let sub = client.get_subscription(&1); - assert_eq!(sub.status, SubscriptionStatus::Cancelled); - - let plan = client.get_plan(&1); - assert_eq!(plan.subscriber_count, 0); - } - - #[test] - #[should_panic(expected = "Payment not yet due")] - fn test_charge_subscription_not_due() { - let env = Env::default(); - let (client, _admin, _merchant, subscriber, _token) = setup(&env); - client.subscribe(&subscriber, &1); - - client.charge_subscription(&1); - } - - #[test] - #[should_panic(expected = "Already subscribed to this plan")] - fn test_double_subscribe() { - let env = Env::default(); - let (client, _admin, _merchant, subscriber, _token) = setup(&env); - client.subscribe(&subscriber, &1); - - client.subscribe(&subscriber, &1); - } - - #[test] - fn test_plan_deactivation_existing_subscribers_unaffected() { - let env = Env::default(); - let (client, _admin, merchant, subscriber, _token) = setup(&env); - let sub_id = client.subscribe(&subscriber, &1); - - client.deactivate_plan(&merchant, &1); - let plan = client.get_plan(&1); - assert!(!plan.active); - - // Existing subscriber can still keep/operate subscription lifecycle. - client.pause_subscription(&subscriber, &sub_id); - let paused = client.get_subscription(&sub_id); - assert_eq!(paused.status, SubscriptionStatus::Paused); - } - - #[test] - #[should_panic(expected = "Subscription not active")] - fn test_charge_paused_subscription() { - let env = Env::default(); - let (client, _admin, _merchant, subscriber, _token) = setup(&env); - client.subscribe(&subscriber, &1); - client.pause_subscription(&subscriber, &1); - - client.charge_subscription(&1); - } - - #[test] - #[should_panic(expected = "Merchant cannot self-subscribe")] - fn test_merchant_cannot_subscribe() { - let env = Env::default(); - let (client, _admin, merchant, _subscriber, _token) = setup(&env); - - client.subscribe(&merchant, &1); - } - - #[test] - #[should_panic(expected = "Only subscriber can cancel")] - fn test_non_subscriber_cannot_cancel() { - let env = Env::default(); - let (client, _admin, _merchant, subscriber, _token) = setup(&env); - let attacker = Address::generate(&env); - client.subscribe(&subscriber, &1); - - client.cancel_subscription(&attacker, &1); - } - - #[test] - fn test_pause_and_resume() { - let env = Env::default(); - let (client, _admin, _merchant, subscriber, _token) = setup(&env); - let sub_id = client.subscribe(&subscriber, &1); - let initial = client.get_subscription(&sub_id); - - client.pause_subscription(&subscriber, &sub_id); - let paused = client.get_subscription(&sub_id); - assert_eq!(paused.status, SubscriptionStatus::Paused); - - env.ledger().with_mut(|li| { - li.timestamp += 86_400; - }); - - client.resume_subscription(&subscriber, &sub_id); - let resumed = client.get_subscription(&sub_id); - assert_eq!(resumed.status, SubscriptionStatus::Active); - assert_eq!( - resumed.next_charge_at, - env.ledger().timestamp() + Interval::Monthly.seconds() - ); - assert!(resumed.next_charge_at > initial.next_charge_at); - } - - #[test] - #[should_panic(expected = "Pause duration exceeds limit")] - fn test_pause_by_subscriber_limit_enforced() { - let env = Env::default(); - let (client, _admin, _merchant, subscriber, _token) = setup(&env); - let sub_id = client.subscribe(&subscriber, &1); - - // Max is 30 days (2,592_000s). Try 31 days. - client.pause_by_subscriber(&subscriber, &sub_id, &2_678_400); - } - - #[test] - fn test_auto_resume() { - let env = Env::default(); - let (client, _admin, _merchant, subscriber, _token) = setup(&env); - let sub_id = client.subscribe(&subscriber, &1); - - // Pause for 1 day (86,400s) - client.pause_by_subscriber(&subscriber, &sub_id, &86_400); - let paused = client.get_subscription(&sub_id); - assert_eq!(paused.status, SubscriptionStatus::Paused); - - // Fast forward 2 days (172,800s) - env.ledger().with_mut(|li| { - li.timestamp += 172_800; - }); - - // get_subscription should now return Active due to auto-resume - let resumed = client.get_subscription(&sub_id); - assert_eq!(resumed.status, SubscriptionStatus::Active); - assert_eq!(resumed.paused_at, 0); - assert_eq!(resumed.pause_duration, 0); - - // charge_subscription should also work now - // But we need to make sure next_charge_at is reached - env.ledger().with_mut(|li| { - li.timestamp += Interval::Monthly.seconds(); - }); - client.charge_subscription(&sub_id); - - let charged = client.get_subscription(&sub_id); - assert_eq!(charged.total_paid, 500); - } - - #[test] - #[should_panic(expected = "Rate limited")] - fn test_rate_limit_enforced_for_subscribe() { - let env = Env::default(); - let (client, _admin, _merchant, subscriber, _token) = setup(&env); - // Configure 100s min interval for subscribe - client.set_rate_limit(&String::from_str(&env, "subscribe"), &100u64); - - // First subscribe ok - let _ = client.subscribe(&subscriber, &1); - // Immediate second subscribe should be rate-limited - let _ = client.subscribe(&subscriber, &1); - } - - #[test] - fn test_admin_override_bypass() { - let env = Env::default(); - let (client, admin, merchant, _subscriber, _token) = setup(&env); - client.set_rate_limit(&String::from_str(&env, "create_plan"), &100u64); - - // Admin creates plans repeatedly without being rate-limited - let _ = client.create_plan( - &admin, - &String::from_str(&env, "Admin Plan A"), - &1_i128, - &merchant, // reuse address as mock token - &Interval::Monthly, - ); - let _ = client.create_plan( - &admin, - &String::from_str(&env, "Admin Plan B"), - &2_i128, - &merchant, - &Interval::Monthly, - ); - } - - #[test] - fn test_subscription_transfer_flow() { - let env = Env::default(); - let (client, _admin, _merchant, subscriber, _token) = setup(&env); - let recipient = Address::generate(&env); - - let sub_id = client.subscribe(&subscriber, &1); - - // Request transfer by current subscriber - client.request_transfer(&sub_id, &recipient); - - // Accept by recipient - client.accept_transfer(&sub_id, &recipient); - - // New owner is recipient - let sub = client.get_subscription(&sub_id); - assert_eq!(sub.subscriber, recipient); - - // Old owner list should not contain the subscription anymore - let old_list = client.get_user_subscriptions(&subscriber); - assert_eq!(old_list.len(), 0); - - // Recipient list should contain it - let new_list = client.get_user_subscriptions(&recipient); - assert_eq!(new_list.len(), 1); - assert_eq!(new_list.get_unchecked(0), sub_id); - } - - #[test] - fn test_refund_flow() { - let env = Env::default(); - let (client, _admin, _merchant, subscriber, _token) = setup(&env); - let sub_id = client.subscribe(&subscriber, &1); - - // Charge the subscription at month 1 - env.ledger().set_timestamp(86_400 * 31); - client.charge_subscription(&sub_id); - - let sub = client.get_subscription(&sub_id); - assert_eq!(sub.total_paid, 500); - - // Request refund - client.request_refund(&sub_id, &200); - let sub = client.get_subscription(&sub_id); - assert_eq!(sub.refund_requested_amount, 200); - - // Approve refund - client.approve_refund(&sub_id); - let sub = client.get_subscription(&sub_id); - assert_eq!(sub.total_paid, 300); - assert_eq!(sub.refund_requested_amount, 0); - } -} diff --git a/contracts/src/revenue.rs b/contracts/src/revenue.rs deleted file mode 100644 index 8dc2414..0000000 --- a/contracts/src/revenue.rs +++ /dev/null @@ -1,692 +0,0 @@ -use soroban_sdk::{contracttype, Address, Env, Vec}; - -// ── Types ───────────────────────────────────────────────────────────────────── - -/// How revenue is spread across a subscription period. -#[contracttype] -#[derive(Clone, Debug, PartialEq)] -pub enum RecognitionMethod { - /// Equal amount recognised each period (ASC 606 / IFRS 15 default). - StraightLine, - /// Recognised proportionally to actual usage reported by the merchant. - UsageBased, -} - -/// Configurable rule attached to a plan that governs how its revenue is recognised. -#[contracttype] -#[derive(Clone, Debug)] -pub struct RevenueRecognitionRule { - pub plan_id: u64, - pub method: RecognitionMethod, - /// Length of one recognition period in seconds (e.g. 2_592_000 = 30 days). - pub recognition_period: u64, -} - -/// A single entry in a revenue recognition schedule. -#[contracttype] -#[derive(Clone, Debug)] -pub struct RevenueScheduleEntry { - /// Unix timestamp when this slice of revenue starts being earned. - pub period_start: u64, - /// Unix timestamp when this slice of revenue finishes being earned. - pub period_end: u64, - /// Amount recognised (in stroops) during [period_start, period_end]. - pub recognised_amount: i128, - /// Whether this entry has been fully recognised (period_end has passed). - pub is_recognised: bool, -} - -/// Full recognition schedule for one subscription charge. -#[contracttype] -#[derive(Clone, Debug)] -pub struct RevenueSchedule { - pub subscription_id: u64, - pub total_amount: i128, - pub entries: Vec, -} - -/// Snapshot of recognised vs deferred revenue for a merchant. -#[contracttype] -#[derive(Clone, Debug)] -pub struct Recognition { - pub subscription_id: u64, - pub merchant: Address, - /// Revenue already earned (period elapsed). - pub recognised_revenue: i128, - /// Revenue received but not yet earned (future periods). - pub deferred_revenue: i128, - /// Timestamp of this snapshot. - pub as_of: u64, -} - -/// Per-period revenue analytics entry. -#[contracttype] -#[derive(Clone, Debug)] -pub struct PeriodRevenue { - pub period_start: u64, - pub period_end: u64, - pub recognised_amount: i128, - pub subscription_count: u32, -} - -// ── Storage keys ────────────────────────────────────────────────────────────── - -#[contracttype] -pub enum RevenueDataKey { - /// RevenueRecognitionRule keyed by plan_id. - RecognitionRule(u64), - /// RevenueSchedule keyed by subscription_id. - Schedule(u64), - /// Cumulative deferred revenue balance for a merchant. - DeferredRevenue(Address), - /// Cumulative recognised revenue for a merchant. - RecognisedRevenue(Address), - /// List of subscription IDs tracked for a merchant (for analytics). - MerchantSubscriptions(Address), -} - -// ── Core logic (pure functions, no Env dependency) ──────────────────────────── - -/// Build a straight-line schedule: split `total_amount` evenly across -/// `num_periods` consecutive periods of `period_secs` seconds each, -/// starting at `charge_time`. -/// -/// Any rounding remainder is added to the last entry. -pub fn build_straight_line_schedule( - env: &Env, - subscription_id: u64, - total_amount: i128, - charge_time: u64, - period_secs: u64, - num_periods: u32, -) -> RevenueSchedule { - assert!(num_periods > 0, "num_periods must be > 0"); - assert!(period_secs > 0, "period_secs must be > 0"); - assert!(total_amount >= 0, "total_amount must be non-negative"); - - let slice = total_amount / num_periods as i128; - let remainder = total_amount - slice * num_periods as i128; - - let mut entries: Vec = Vec::new(env); - for i in 0..num_periods { - let start = charge_time + (i as u64) * period_secs; - let end = start + period_secs; - let amount = if i == num_periods - 1 { slice + remainder } else { slice }; - entries.push_back(RevenueScheduleEntry { - period_start: start, - period_end: end, - recognised_amount: amount, - is_recognised: false, - }); - } - - RevenueSchedule { subscription_id, total_amount, entries } -} - -/// Build a usage-based schedule: a single entry covering the full interval. -/// The merchant reports actual usage later; until then the full amount is deferred. -pub fn build_usage_based_schedule( - env: &Env, - subscription_id: u64, - total_amount: i128, - charge_time: u64, - interval_secs: u64, -) -> RevenueSchedule { - let mut entries: Vec = Vec::new(env); - entries.push_back(RevenueScheduleEntry { - period_start: charge_time, - period_end: charge_time + interval_secs, - recognised_amount: total_amount, - is_recognised: false, - }); - RevenueSchedule { subscription_id, total_amount, entries } -} - -/// Walk a schedule and return (recognised, deferred) split as of `now`. -pub fn split_recognised_deferred( - schedule: &RevenueSchedule, - now: u64, -) -> (i128, i128) { - let mut recognised: i128 = 0; - let mut deferred: i128 = 0; - for entry in schedule.entries.iter() { - if now >= entry.period_end { - recognised += entry.recognised_amount; - } else if now >= entry.period_start { - // Partial recognition: pro-rate within the current period. - let elapsed = now - entry.period_start; - let duration = entry.period_end - entry.period_start; - let partial = entry.recognised_amount * elapsed as i128 / duration as i128; - recognised += partial; - deferred += entry.recognised_amount - partial; - } else { - deferred += entry.recognised_amount; - } - } - (recognised, deferred) -} - -// ── Contract-level helpers (require Env) ────────────────────────────────────── - -/// Persist a recognition rule for a plan. -pub fn set_recognition_rule(env: &Env, rule: RevenueRecognitionRule) { - env.storage() - .persistent() - .set(&RevenueDataKey::RecognitionRule(rule.plan_id), &rule); -} - -/// Retrieve the recognition rule for a plan (returns None if not configured). -pub fn get_recognition_rule(env: &Env, plan_id: u64) -> Option { - env.storage() - .persistent() - .get(&RevenueDataKey::RecognitionRule(plan_id)) -} - -/// Generate and persist a revenue schedule for a subscription charge. -/// `interval_secs` is the plan's billing interval. -pub fn generate_revenue_schedule( - env: &Env, - subscription_id: u64, - plan_id: u64, - total_amount: i128, - charge_time: u64, - interval_secs: u64, -) -> RevenueSchedule { - let rule = get_recognition_rule(env, plan_id); - - let schedule = match rule { - Some(r) => { - let num_periods = if r.recognition_period > 0 { - ((interval_secs + r.recognition_period - 1) / r.recognition_period) as u32 - } else { - 1 - }; - match r.method { - RecognitionMethod::StraightLine => build_straight_line_schedule( - env, - subscription_id, - total_amount, - charge_time, - r.recognition_period, - num_periods, - ), - RecognitionMethod::UsageBased => build_usage_based_schedule( - env, - subscription_id, - total_amount, - charge_time, - interval_secs, - ), - } - } - // Default: straight-line over the full interval as a single period. - None => build_straight_line_schedule( - env, - subscription_id, - total_amount, - charge_time, - interval_secs, - 1, - ), - }; - - env.storage() - .persistent() - .set(&RevenueDataKey::Schedule(subscription_id), &schedule); - - schedule -} - -/// Compute a Recognition snapshot for a subscription as of `now`. -/// Returns a zero-revenue snapshot if no schedule has been generated yet. -pub fn recognize_revenue(env: &Env, subscription_id: u64, merchant: Address, now: u64) -> Recognition { - let maybe_schedule: Option = env - .storage() - .persistent() - .get(&RevenueDataKey::Schedule(subscription_id)); - - match maybe_schedule { - None => Recognition { - subscription_id, - merchant, - recognised_revenue: 0, - deferred_revenue: 0, - as_of: now, - }, - Some(schedule) => { - let (recognised, deferred) = split_recognised_deferred(&schedule, now); - Recognition { - subscription_id, - merchant, - recognised_revenue: recognised, - deferred_revenue: deferred, - as_of: now, - } - } - } -} - -/// Return the cumulative deferred revenue balance for a merchant. -pub fn get_deferred_revenue(env: &Env, merchant_id: Address) -> i128 { - env.storage() - .persistent() - .get(&RevenueDataKey::DeferredRevenue(merchant_id)) - .unwrap_or(0i128) -} - -/// Return the revenue schedule for a subscription (None if not yet generated). -pub fn get_revenue_schedule(env: &Env, subscription_id: u64) -> Option { - env.storage() - .persistent() - .get(&RevenueDataKey::Schedule(subscription_id)) -} - -/// Update the merchant's cumulative recognised/deferred balances after a charge. -pub fn update_merchant_revenue_balances( - env: &Env, - merchant: &Address, - recognised_delta: i128, - deferred_delta: i128, -) { - let prev_recognised: i128 = env - .storage() - .persistent() - .get(&RevenueDataKey::RecognisedRevenue(merchant.clone())) - .unwrap_or(0i128); - let prev_deferred: i128 = env - .storage() - .persistent() - .get(&RevenueDataKey::DeferredRevenue(merchant.clone())) - .unwrap_or(0i128); - - env.storage().persistent().set( - &RevenueDataKey::RecognisedRevenue(merchant.clone()), - &(prev_recognised + recognised_delta), - ); - env.storage().persistent().set( - &RevenueDataKey::DeferredRevenue(merchant.clone()), - &(prev_deferred + deferred_delta), - ); -} - -/// Compute per-period revenue analytics for a merchant across all tracked subscriptions. -pub fn get_revenue_analytics_by_period( - env: &Env, - merchant: &Address, - period_secs: u64, - from: u64, - to: u64, -) -> Vec { - assert!(period_secs > 0, "period_secs must be > 0"); - assert!(to >= from, "to must be >= from"); - - let sub_ids: Vec = env - .storage() - .persistent() - .get(&RevenueDataKey::MerchantSubscriptions(merchant.clone())) - .unwrap_or(Vec::new(env)); - - // Build period buckets. - let num_buckets = ((to - from + period_secs - 1) / period_secs) as u32; - let mut buckets: Vec = Vec::new(env); - for i in 0..num_buckets { - let start = from + (i as u64) * period_secs; - let end = start + period_secs; - buckets.push_back(PeriodRevenue { - period_start: start, - period_end: end, - recognised_amount: 0, - subscription_count: 0, - }); - } - - // Accumulate schedule entries into buckets. - for sub_id in sub_ids.iter() { - let maybe_schedule: Option = env - .storage() - .persistent() - .get(&RevenueDataKey::Schedule(sub_id)); - if let Some(schedule) = maybe_schedule { - let mut contributed = false; - for entry in schedule.entries.iter() { - // Find which bucket this entry's period_start falls into. - if entry.period_start < from || entry.period_start >= to { - continue; - } - let bucket_idx = ((entry.period_start - from) / period_secs) as u32; - if bucket_idx < num_buckets { - let mut bucket = buckets.get_unchecked(bucket_idx); - bucket.recognised_amount += entry.recognised_amount; - if !contributed { - bucket.subscription_count += 1; - contributed = true; - } - buckets.set(bucket_idx, bucket); - } - } - } - } - - buckets -} - -/// Register a subscription under a merchant for analytics tracking. -/// Safe to call multiple times — deduplicates by subscription_id. -pub fn track_merchant_subscription(env: &Env, merchant: &Address, subscription_id: u64) { - let mut ids: Vec = env - .storage() - .persistent() - .get(&RevenueDataKey::MerchantSubscriptions(merchant.clone())) - .unwrap_or(Vec::new(env)); - // Deduplicate: only add if not already tracked. - for existing in ids.iter() { - if existing == subscription_id { - return; - } - } - ids.push_back(subscription_id); - env.storage() - .persistent() - .set(&RevenueDataKey::MerchantSubscriptions(merchant.clone()), &ids); -} - -// ── Tests ───────────────────────────────────────────────────────────────────── - -#[cfg(test)] -mod tests { - use super::*; - use soroban_sdk::{contract, contractimpl, Env}; - - fn make_env() -> Env { - Env::default() - } - - // ── straight-line schedule ──────────────────────────────────────────────── - - #[test] - fn test_straight_line_schedule_even_split() { - let env = make_env(); - // 1200 stroops over 4 periods of 100s each, starting at t=0. - let schedule = build_straight_line_schedule(&env, 1, 1200, 0, 100, 4); - assert_eq!(schedule.total_amount, 1200); - assert_eq!(schedule.entries.len(), 4); - for entry in schedule.entries.iter() { - assert_eq!(entry.recognised_amount, 300); - } - } - - #[test] - fn test_straight_line_schedule_remainder_in_last_entry() { - let env = make_env(); - // 1000 stroops over 3 periods → 333 + 333 + 334. - let schedule = build_straight_line_schedule(&env, 1, 1000, 0, 100, 3); - assert_eq!(schedule.entries.get_unchecked(0).recognised_amount, 333); - assert_eq!(schedule.entries.get_unchecked(1).recognised_amount, 333); - assert_eq!(schedule.entries.get_unchecked(2).recognised_amount, 334); - } - - #[test] - fn test_straight_line_single_period() { - let env = make_env(); - let schedule = build_straight_line_schedule(&env, 1, 500, 1000, 2_592_000, 1); - assert_eq!(schedule.entries.len(), 1); - assert_eq!(schedule.entries.get_unchecked(0).recognised_amount, 500); - assert_eq!(schedule.entries.get_unchecked(0).period_start, 1000); - assert_eq!(schedule.entries.get_unchecked(0).period_end, 1000 + 2_592_000); - } - - // ── usage-based schedule ────────────────────────────────────────────────── - - #[test] - fn test_usage_based_schedule_single_entry() { - let env = make_env(); - let schedule = build_usage_based_schedule(&env, 2, 800, 500, 2_592_000); - assert_eq!(schedule.entries.len(), 1); - let entry = schedule.entries.get_unchecked(0); - assert_eq!(entry.recognised_amount, 800); - assert_eq!(entry.period_start, 500); - assert_eq!(entry.period_end, 500 + 2_592_000); - assert!(!entry.is_recognised); - } - - // ── split_recognised_deferred ───────────────────────────────────────────── - - #[test] - fn test_split_all_deferred_before_period_start() { - let env = make_env(); - // Period starts at t=1000, ends at t=2000. Query at t=500. - let schedule = build_straight_line_schedule(&env, 1, 1000, 1000, 1000, 1); - let (rec, def) = split_recognised_deferred(&schedule, 500); - assert_eq!(rec, 0); - assert_eq!(def, 1000); - } - - #[test] - fn test_split_all_recognised_after_period_end() { - let env = make_env(); - let schedule = build_straight_line_schedule(&env, 1, 1000, 0, 1000, 1); - let (rec, def) = split_recognised_deferred(&schedule, 2000); - assert_eq!(rec, 1000); - assert_eq!(def, 0); - } - - #[test] - fn test_split_partial_recognition_midway() { - let env = make_env(); - // 1000 stroops, period 0..1000. Query at t=500 (50% elapsed). - let schedule = build_straight_line_schedule(&env, 1, 1000, 0, 1000, 1); - let (rec, def) = split_recognised_deferred(&schedule, 500); - assert_eq!(rec, 500); - assert_eq!(def, 500); - } - - #[test] - fn test_split_multi_period_partial() { - let env = make_env(); - // 1200 stroops, 4 periods of 100s each starting at t=0. - // Query at t=250 → first 2 periods fully recognised (300+300=600), - // third period 50% recognised (150), fourth fully deferred (300). - let schedule = build_straight_line_schedule(&env, 1, 1200, 0, 100, 4); - let (rec, def) = split_recognised_deferred(&schedule, 250); - assert_eq!(rec, 600 + 150); // 750 - assert_eq!(def, 150 + 300); // 450 - } - - // ── cancellation mid-period ─────────────────────────────────────────────── - - #[test] - fn test_cancellation_mid_period_partial_recognition() { - let env = make_env(); - // Subscription charged 1000 stroops for a 1000s period. - // Cancelled at t=300 (30% through). - let schedule = build_straight_line_schedule(&env, 1, 1000, 0, 1000, 1); - let (rec, def) = split_recognised_deferred(&schedule, 300); - assert_eq!(rec, 300); - assert_eq!(def, 700); - } - - // ── contract-level helpers ──────────────────────────────────────────────── - // Soroban storage is only accessible inside a contract context. - // We use a thin wrapper contract to host the storage calls. - - use soroban_sdk::testutils::Address as _; - - #[contract] - pub struct RevenueTestContract; - - #[contractimpl] - impl RevenueTestContract { - pub fn set_rule(env: Env, plan_id: u64, method: RecognitionMethod, period: u64) { - set_recognition_rule(&env, RevenueRecognitionRule { plan_id, method, recognition_period: period }); - } - pub fn get_rule(env: Env, plan_id: u64) -> Option { - get_recognition_rule(&env, plan_id) - } - pub fn gen_schedule(env: Env, sub_id: u64, plan_id: u64, amount: i128, charge: u64, interval: u64) -> RevenueSchedule { - generate_revenue_schedule(&env, sub_id, plan_id, amount, charge, interval) - } - pub fn recognize(env: Env, sub_id: u64, merchant: Address, now: u64) -> Recognition { - recognize_revenue(&env, sub_id, merchant, now) - } - pub fn get_deferred(env: Env, merchant: Address) -> i128 { - get_deferred_revenue(&env, merchant) - } - pub fn update_balances(env: Env, merchant: Address, rec_delta: i128, def_delta: i128) { - update_merchant_revenue_balances(&env, &merchant, rec_delta, def_delta); - } - pub fn analytics(env: Env, merchant: Address, period: u64, from: u64, to: u64) -> soroban_sdk::Vec { - get_revenue_analytics_by_period(&env, &merchant, period, from, to) - } - pub fn track(env: Env, merchant: Address, sub_id: u64) { - track_merchant_subscription(&env, &merchant, sub_id); - } - pub fn get_schedule(env: Env, sub_id: u64) -> Option { - get_revenue_schedule(&env, sub_id) - } - } - - fn setup_revenue_client(env: &Env) -> RevenueTestContractClient<'_> { - let id = env.register_contract(None, RevenueTestContract); - RevenueTestContractClient::new(env, &id) - } - - #[test] - fn test_set_and_get_recognition_rule() { - let env = make_env(); - env.mock_all_auths(); - let client = setup_revenue_client(&env); - client.set_rule(&42u64, &RecognitionMethod::StraightLine, &86_400u64); - let fetched = client.get_rule(&42u64).expect("rule should exist"); - assert_eq!(fetched.plan_id, 42); - assert_eq!(fetched.recognition_period, 86_400); - } - - #[test] - fn test_get_recognition_rule_missing_returns_none() { - let env = make_env(); - let client = setup_revenue_client(&env); - assert!(client.get_rule(&999u64).is_none()); - } - - #[test] - fn test_generate_revenue_schedule_straight_line_rule() { - let env = make_env(); - env.mock_all_auths(); - let client = setup_revenue_client(&env); - client.set_rule(&1u64, &RecognitionMethod::StraightLine, &2_592_000u64); - let schedule = client.gen_schedule(&1u64, &1u64, &500i128, &0u64, &2_592_000u64); - assert_eq!(schedule.entries.len(), 1); - assert_eq!(schedule.entries.get_unchecked(0).recognised_amount, 500); - } - - #[test] - fn test_generate_revenue_schedule_usage_based_rule() { - let env = make_env(); - env.mock_all_auths(); - let client = setup_revenue_client(&env); - client.set_rule(&2u64, &RecognitionMethod::UsageBased, &2_592_000u64); - let schedule = client.gen_schedule(&1u64, &2u64, &800i128, &0u64, &2_592_000u64); - assert_eq!(schedule.entries.len(), 1); - assert_eq!(schedule.entries.get_unchecked(0).recognised_amount, 800); - } - - #[test] - fn test_generate_revenue_schedule_no_rule_defaults_to_single_period() { - let env = make_env(); - let client = setup_revenue_client(&env); - let schedule = client.gen_schedule(&1u64, &99u64, &600i128, &0u64, &2_592_000u64); - assert_eq!(schedule.entries.len(), 1); - assert_eq!(schedule.entries.get_unchecked(0).recognised_amount, 600); - } - - #[test] - fn test_recognize_revenue_snapshot() { - let env = make_env(); - let client = setup_revenue_client(&env); - let merchant = Address::generate(&env); - client.gen_schedule(&1u64, &99u64, &1000i128, &0u64, &1000u64); - let rec = client.recognize(&1u64, &merchant, &600u64); - assert_eq!(rec.recognised_revenue, 600); - assert_eq!(rec.deferred_revenue, 400); - assert_eq!(rec.as_of, 600); - } - - #[test] - fn test_deferred_revenue_balance_accumulates() { - let env = make_env(); - let client = setup_revenue_client(&env); - let merchant = Address::generate(&env); - client.update_balances(&merchant, &0i128, &500i128); - client.update_balances(&merchant, &0i128, &300i128); - assert_eq!(client.get_deferred(&merchant), 800); - } - - #[test] - fn test_deferred_revenue_balance_zero_for_unknown_merchant() { - let env = make_env(); - let client = setup_revenue_client(&env); - let merchant = Address::generate(&env); - assert_eq!(client.get_deferred(&merchant), 0); - } - - #[test] - fn test_revenue_analytics_by_period() { - let env = make_env(); - let client = setup_revenue_client(&env); - let merchant = Address::generate(&env); - client.gen_schedule(&1u64, &99u64, &1000i128, &0u64, &100u64); - client.gen_schedule(&2u64, &99u64, &2000i128, &0u64, &100u64); - client.track(&merchant, &1u64); - client.track(&merchant, &2u64); - let analytics = client.analytics(&merchant, &100u64, &0u64, &200u64); - assert_eq!(analytics.len(), 2); - let bucket0 = analytics.get_unchecked(0); - assert_eq!(bucket0.recognised_amount, 3000); - assert_eq!(bucket0.subscription_count, 2); - } - - #[test] - fn test_track_merchant_subscription_accumulates() { - let env = make_env(); - let client = setup_revenue_client(&env); - let merchant = Address::generate(&env); - client.track(&merchant, &10u64); - client.track(&merchant, &20u64); - // Verify via analytics: two subscriptions tracked. - // (We can't read storage directly outside a contract context.) - // Instead, generate schedules and check analytics count. - client.gen_schedule(&10u64, &99u64, &100i128, &0u64, &100u64); - client.gen_schedule(&20u64, &99u64, &200i128, &0u64, &100u64); - let analytics = client.analytics(&merchant, &100u64, &0u64, &100u64); - assert_eq!(analytics.get_unchecked(0).subscription_count, 2); - } - - // ── multi-element arrangement ───────────────────────────────────────────── - - #[test] - fn test_multi_element_arrangement_deferred_balances() { - let env = make_env(); - let client = setup_revenue_client(&env); - let merchant = Address::generate(&env); - client.update_balances(&merchant, &0i128, &500i128); - client.update_balances(&merchant, &0i128, &300i128); - client.update_balances(&merchant, &0i128, &200i128); - assert_eq!(client.get_deferred(&merchant), 1000); - // Recognise 500. - client.update_balances(&merchant, &500i128, &-500i128); - assert_eq!(client.get_deferred(&merchant), 500); - } - - // ── contract modification: plan rule update ─────────────────────────────── - - #[test] - fn test_recognition_rule_can_be_updated() { - let env = make_env(); - env.mock_all_auths(); - let client = setup_revenue_client(&env); - client.set_rule(&5u64, &RecognitionMethod::StraightLine, &86_400u64); - client.set_rule(&5u64, &RecognitionMethod::UsageBased, &86_400u64); - let rule = client.get_rule(&5u64).unwrap(); - assert_eq!(rule.method, RecognitionMethod::UsageBased); - } -} diff --git a/contracts/subscription/src/lib.rs b/contracts/subscription/src/lib.rs index 5d4ba7d..33369fa 100644 --- a/contracts/subscription/src/lib.rs +++ b/contracts/subscription/src/lib.rs @@ -1,4 +1,7 @@ #![no_std] +#![allow(clippy::too_many_arguments)] + +pub mod revenue; use soroban_sdk::{token, Address, Env, IntoVal, String, TryFromVal, Val, Vec}; use subtrackr_types::{Interval, Plan, StorageKey, Subscription, SubscriptionStatus}; @@ -22,7 +25,12 @@ fn storage_instance_get>( val_opt.map(|val| V::try_from_val(env, &val).unwrap()) } -fn storage_instance_set>(env: &Env, storage: &Address, key: StorageKey, value: V) { +fn storage_instance_set>( + env: &Env, + storage: &Address, + key: StorageKey, + value: V, +) { let val: Val = value.into_val(env); let args: Vec = soroban_sdk::vec![env, key.into_val(env), val]; env.invoke_contract::<()>( @@ -55,7 +63,12 @@ fn storage_persistent_get>( val_opt.map(|val| V::try_from_val(env, &val).unwrap()) } -fn storage_persistent_set>(env: &Env, storage: &Address, key: StorageKey, value: V) { +fn storage_persistent_set>( + env: &Env, + storage: &Address, + key: StorageKey, + value: V, +) { let val: Val = value.into_val(env); let args: Vec = soroban_sdk::vec![env, key.into_val(env), val]; env.invoke_contract::<()>( @@ -100,7 +113,10 @@ fn enforce_rate_limit(env: &Env, storage: &Address, caller: &Address, function_n if let Some(last) = last_opt { if now < last + min_secs { env.events().publish( - (String::from_str(env, "rate_limit_violation"), caller.clone()), + ( + String::from_str(env, "rate_limit_violation"), + caller.clone(), + ), (fname.clone(), last, now, min_secs), ); panic!("Rate limited: please wait before calling this function again"); @@ -144,11 +160,24 @@ fn set_user_plan_index( } fn remove_user_plan_index(env: &Env, storage: &Address, subscriber: &Address, plan_id: u64) { - storage_persistent_remove(env, storage, StorageKey::UserPlanIndex(subscriber.clone(), plan_id)); + storage_persistent_remove( + env, + storage, + StorageKey::UserPlanIndex(subscriber.clone(), plan_id), + ); } -fn get_user_plan_index(env: &Env, storage: &Address, subscriber: &Address, plan_id: u64) -> Option { - storage_persistent_get(env, storage, StorageKey::UserPlanIndex(subscriber.clone(), plan_id)) +fn get_user_plan_index( + env: &Env, + storage: &Address, + subscriber: &Address, + plan_id: u64, +) -> Option { + storage_persistent_get( + env, + storage, + StorageKey::UserPlanIndex(subscriber.clone(), plan_id), + ) } // ───────────────────────────────────────────────────────────────────────────── @@ -194,8 +223,8 @@ impl SubTrackrSubscription { assert!(from_version < STORAGE_VERSION, "Unsupported migration path"); if from_version == 1 { - let sub_count: u64 = storage_instance_get(&env, &storage, StorageKey::SubscriptionCount) - .unwrap_or(0); + let sub_count: u64 = + storage_instance_get(&env, &storage, StorageKey::SubscriptionCount).unwrap_or(0); let mut i: u64 = 1; while i <= sub_count { let sub_opt: Option = @@ -270,7 +299,8 @@ impl SubTrackrSubscription { merchant.require_auth(); assert!(price > 0, "Price must be positive"); - let mut count: u64 = storage_instance_get(&env, &storage, StorageKey::PlanCount).unwrap_or(0); + let mut count: u64 = + storage_instance_get(&env, &storage, StorageKey::PlanCount).unwrap_or(0); count += 1; let plan = Plan { @@ -288,12 +318,9 @@ impl SubTrackrSubscription { storage_persistent_set(&env, &storage, StorageKey::Plan(count), plan.clone()); storage_instance_set(&env, &storage, StorageKey::PlanCount, count); - let mut merchant_plans: Vec = storage_persistent_get( - &env, - &storage, - StorageKey::MerchantPlans(merchant.clone()), - ) - .unwrap_or(Vec::new(&env)); + let mut merchant_plans: Vec = + storage_persistent_get(&env, &storage, StorageKey::MerchantPlans(merchant.clone())) + .unwrap_or(Vec::new(&env)); merchant_plans.push_back(count); storage_persistent_set( &env, @@ -305,7 +332,13 @@ impl SubTrackrSubscription { count } - pub fn deactivate_plan(env: Env, proxy: Address, storage: Address, merchant: Address, plan_id: u64) { + pub fn deactivate_plan( + env: Env, + proxy: Address, + storage: Address, + merchant: Address, + plan_id: u64, + ) { proxy.require_auth(); if merchant != get_admin(&env, &storage) { enforce_rate_limit(&env, &storage, &merchant, "deactivate_plan"); @@ -323,7 +356,13 @@ impl SubTrackrSubscription { // ── Subscription Management ── - pub fn subscribe(env: Env, proxy: Address, storage: Address, subscriber: Address, plan_id: u64) -> u64 { + pub fn subscribe( + env: Env, + proxy: Address, + storage: Address, + subscriber: Address, + plan_id: u64, + ) -> u64 { proxy.require_auth(); if subscriber != get_admin(&env, &storage) { enforce_rate_limit(&env, &storage, &subscriber, "subscribe"); @@ -369,7 +408,12 @@ impl SubTrackrSubscription { refund_requested_amount: 0, }; - storage_persistent_set(&env, &storage, StorageKey::Subscription(sub_count), subscription); + storage_persistent_set( + &env, + &storage, + StorageKey::Subscription(sub_count), + subscription, + ); storage_instance_set(&env, &storage, StorageKey::SubscriptionCount, sub_count); let mut user_subs: Vec = storage_persistent_get( @@ -395,7 +439,13 @@ impl SubTrackrSubscription { sub_count } - pub fn cancel_subscription(env: Env, proxy: Address, storage: Address, subscriber: Address, subscription_id: u64) { + pub fn cancel_subscription( + env: Env, + proxy: Address, + storage: Address, + subscriber: Address, + subscription_id: u64, + ) { proxy.require_auth(); if subscriber != get_admin(&env, &storage) { enforce_rate_limit(&env, &storage, &subscriber, "cancel_subscription"); @@ -413,7 +463,12 @@ impl SubTrackrSubscription { ); sub.status = SubscriptionStatus::Cancelled; - storage_persistent_set(&env, &storage, StorageKey::Subscription(subscription_id), sub.clone()); + storage_persistent_set( + &env, + &storage, + StorageKey::Subscription(subscription_id), + sub.clone(), + ); // Remove index remove_user_plan_index(&env, &storage, &subscriber, sub.plan_id); @@ -479,7 +534,12 @@ impl SubTrackrSubscription { sub.paused_at = env.ledger().timestamp(); sub.pause_duration = duration; - storage_persistent_set(&env, &storage, StorageKey::Subscription(subscription_id), sub.clone()); + storage_persistent_set( + &env, + &storage, + StorageKey::Subscription(subscription_id), + sub.clone(), + ); env.events().publish( (String::from_str(&env, "subscription_paused"), subscriber), @@ -506,8 +566,7 @@ impl SubTrackrSubscription { assert!(sub.subscriber == subscriber, "Only subscriber can resume"); assert!( - sub.status == SubscriptionStatus::Paused - || check_and_resume_internal(&env, &mut sub), + sub.status == SubscriptionStatus::Paused || check_and_resume_internal(&env, &mut sub), "Only paused subscriptions can be resumed" ); @@ -520,7 +579,12 @@ impl SubTrackrSubscription { sub.paused_at = 0; sub.pause_duration = 0; - storage_persistent_set(&env, &storage, StorageKey::Subscription(subscription_id), sub); + storage_persistent_set( + &env, + &storage, + StorageKey::Subscription(subscription_id), + sub, + ); env.events().publish( (String::from_str(&env, "subscription_resumed"), subscriber), @@ -562,7 +626,11 @@ impl SubTrackrSubscription { let plan: Plan = storage_persistent_get(&env, &storage, StorageKey::Plan(sub.plan_id)) .expect("Plan not found"); - token::Client::new(&env, &plan.token).transfer(&sub.subscriber, &plan.merchant, &plan.price); + token::Client::new(&env, &plan.token).transfer( + &sub.subscriber, + &plan.merchant, + &plan.price, + ); sub.last_charged_at = now; sub.next_charge_at = now + plan.interval.seconds(); @@ -570,10 +638,31 @@ impl SubTrackrSubscription { sub.total_gas_spent += 100_000; sub.charge_count += 1; - storage_persistent_set(&env, &storage, StorageKey::Subscription(subscription_id), sub.clone()); + storage_persistent_set( + &env, + &storage, + StorageKey::Subscription(subscription_id), + sub.clone(), + ); + + // Generate revenue recognition schedule and defer the full charge amount. + revenue::generate_revenue_schedule( + &env, + &storage, + subscription_id, + sub.plan_id, + plan.price, + now, + plan.interval.seconds(), + ); + revenue::update_merchant_revenue_balances(&env, &storage, &plan.merchant, 0, plan.price); + revenue::track_merchant_subscription(&env, &storage, &plan.merchant, subscription_id); env.events().publish( - (String::from_str(&env, "subscription_charged"), subscription_id), + ( + String::from_str(&env, "subscription_charged"), + subscription_id, + ), (sub.subscriber.clone(), plan.price, 100_000u64, now), ); } @@ -603,7 +692,12 @@ impl SubTrackrSubscription { ); sub.refund_requested_amount = amount; - storage_persistent_set(&env, &storage, StorageKey::Subscription(subscription_id), sub.clone()); + storage_persistent_set( + &env, + &storage, + StorageKey::Subscription(subscription_id), + sub.clone(), + ); env.events().publish( (String::from_str(&env, "refund_requested"), subscription_id), @@ -629,7 +723,12 @@ impl SubTrackrSubscription { sub.total_paid -= amount; sub.refund_requested_amount = 0; - storage_persistent_set(&env, &storage, StorageKey::Subscription(subscription_id), sub.clone()); + storage_persistent_set( + &env, + &storage, + StorageKey::Subscription(subscription_id), + sub.clone(), + ); env.events().publish( (String::from_str(&env, "refund_approved"), subscription_id), @@ -649,7 +748,12 @@ impl SubTrackrSubscription { assert!(sub.refund_requested_amount > 0, "No pending refund request"); sub.refund_requested_amount = 0; - storage_persistent_set(&env, &storage, StorageKey::Subscription(subscription_id), sub.clone()); + storage_persistent_set( + &env, + &storage, + StorageKey::Subscription(subscription_id), + sub.clone(), + ); env.events().publish( (String::from_str(&env, "refund_rejected"), subscription_id), @@ -690,7 +794,10 @@ impl SubTrackrSubscription { ); env.events().publish( - (String::from_str(&env, "transfer_requested"), subscription_id), + ( + String::from_str(&env, "transfer_requested"), + subscription_id, + ), (sub.subscriber.clone(), recipient), ); } @@ -759,7 +866,12 @@ impl SubTrackrSubscription { let old = sub.subscriber.clone(); sub.subscriber = recipient.clone(); - storage_persistent_set(&env, &storage, StorageKey::Subscription(subscription_id), sub); + storage_persistent_set( + &env, + &storage, + StorageKey::Subscription(subscription_id), + sub, + ); storage_instance_remove(&env, &storage, StorageKey::PendingTransfer(subscription_id)); @@ -776,7 +888,12 @@ impl SubTrackrSubscription { storage_persistent_get(&env, &storage, StorageKey::Plan(plan_id)).expect("Plan not found") } - pub fn get_subscription(env: Env, proxy: Address, storage: Address, subscription_id: u64) -> Subscription { + pub fn get_subscription( + env: Env, + proxy: Address, + storage: Address, + subscription_id: u64, + ) -> Subscription { proxy.require_auth(); let mut sub: Subscription = storage_persistent_get(&env, &storage, StorageKey::Subscription(subscription_id)) @@ -786,15 +903,26 @@ impl SubTrackrSubscription { sub } - pub fn get_user_subscriptions(env: Env, proxy: Address, storage: Address, subscriber: Address) -> Vec { + pub fn get_user_subscriptions( + env: Env, + proxy: Address, + storage: Address, + subscriber: Address, + ) -> Vec { proxy.require_auth(); storage_persistent_get(&env, &storage, StorageKey::UserSubscriptions(subscriber)) .unwrap_or(Vec::new(&env)) } - pub fn get_merchant_plans(env: Env, proxy: Address, storage: Address, merchant: Address) -> Vec { + pub fn get_merchant_plans( + env: Env, + proxy: Address, + storage: Address, + merchant: Address, + ) -> Vec { proxy.require_auth(); - storage_persistent_get(&env, &storage, StorageKey::MerchantPlans(merchant)).unwrap_or(Vec::new(&env)) + storage_persistent_get(&env, &storage, StorageKey::MerchantPlans(merchant)) + .unwrap_or(Vec::new(&env)) } pub fn get_plan_count(env: Env, proxy: Address, storage: Address) -> u64 { @@ -806,4 +934,74 @@ impl SubTrackrSubscription { proxy.require_auth(); storage_instance_get(&env, &storage, StorageKey::SubscriptionCount).unwrap_or(0) } + + // ── Revenue Recognition API ── + + /// Set a revenue recognition rule for a plan (merchant only). + pub fn set_revenue_rule( + env: Env, + proxy: Address, + storage: Address, + merchant: Address, + plan_id: u64, + method: revenue::RecognitionMethod, + recognition_period: u64, + ) { + proxy.require_auth(); + merchant.require_auth(); + let plan: Plan = storage_persistent_get(&env, &storage, StorageKey::Plan(plan_id)) + .expect("Plan not found"); + assert!( + plan.merchant == merchant, + "Only plan owner can set revenue rule" + ); + revenue::set_recognition_rule( + &env, + &storage, + revenue::RevenueRecognitionRule { + plan_id, + method, + recognition_period, + }, + ); + } + + /// Compute a recognition snapshot for a subscription as of the current ledger time. + pub fn recognize_revenue( + env: Env, + proxy: Address, + storage: Address, + subscription_id: u64, + ) -> revenue::Recognition { + proxy.require_auth(); + let sub: Subscription = + storage_persistent_get(&env, &storage, StorageKey::Subscription(subscription_id)) + .expect("Subscription not found"); + let plan: Plan = storage_persistent_get(&env, &storage, StorageKey::Plan(sub.plan_id)) + .expect("Plan not found"); + let now = env.ledger().timestamp(); + revenue::recognize_revenue(&env, &storage, subscription_id, plan.merchant, now) + } + + /// Return the cumulative deferred revenue balance for a merchant. + pub fn get_deferred_revenue( + env: Env, + proxy: Address, + storage: Address, + merchant_id: Address, + ) -> i128 { + proxy.require_auth(); + revenue::get_deferred_revenue(&env, &storage, &merchant_id) + } + + /// Return the revenue schedule for a subscription (None if not yet generated). + pub fn get_revenue_schedule( + env: Env, + proxy: Address, + storage: Address, + subscription_id: u64, + ) -> Option { + proxy.require_auth(); + revenue::get_revenue_schedule(&env, &storage, subscription_id) + } } diff --git a/contracts/subscription/src/revenue.rs b/contracts/subscription/src/revenue.rs new file mode 100644 index 0000000..c33c884 --- /dev/null +++ b/contracts/subscription/src/revenue.rs @@ -0,0 +1,387 @@ +/// Revenue recognition module for SubTrackr subscriptions. +/// +/// Implements ASC 606 / IFRS 15 compliant revenue recognition: +/// - Straight-line: revenue spread evenly across the billing period. +/// - Usage-based: revenue deferred until actual usage is reported. +/// +/// All storage is delegated to the shared storage contract via the +/// `storage_persistent_*` helpers defined in the parent module. +use soroban_sdk::{contracttype, Address, Env, Vec}; +use subtrackr_types::StorageKey; + +use crate::{storage_persistent_get, storage_persistent_set}; + +// ── Types ───────────────────────────────────────────────────────────────────── + +#[contracttype] +#[derive(Clone, Debug, PartialEq)] +pub enum RecognitionMethod { + StraightLine, + UsageBased, +} + +#[contracttype] +#[derive(Clone, Debug)] +pub struct RevenueRecognitionRule { + pub plan_id: u64, + pub method: RecognitionMethod, + /// Length of one recognition period in seconds. + pub recognition_period: u64, +} + +#[contracttype] +#[derive(Clone, Debug)] +pub struct RevenueScheduleEntry { + pub period_start: u64, + pub period_end: u64, + pub recognised_amount: i128, + pub is_recognised: bool, +} + +#[contracttype] +#[derive(Clone, Debug)] +pub struct RevenueSchedule { + pub subscription_id: u64, + pub total_amount: i128, + pub entries: Vec, +} + +#[contracttype] +#[derive(Clone, Debug)] +pub struct Recognition { + pub subscription_id: u64, + pub merchant: Address, + pub recognised_revenue: i128, + pub deferred_revenue: i128, + pub as_of: u64, +} + +#[contracttype] +#[derive(Clone, Debug)] +pub struct PeriodRevenue { + pub period_start: u64, + pub period_end: u64, + pub recognised_amount: i128, + pub subscription_count: u32, +} + +// ── Pure schedule builders ──────────────────────────────────────────────────── + +pub fn build_straight_line_schedule( + env: &Env, + subscription_id: u64, + total_amount: i128, + charge_time: u64, + period_secs: u64, + num_periods: u32, +) -> RevenueSchedule { + assert!(num_periods > 0, "num_periods must be > 0"); + assert!(period_secs > 0, "period_secs must be > 0"); + assert!(total_amount >= 0, "total_amount must be non-negative"); + + let slice = total_amount / num_periods as i128; + let remainder = total_amount - slice * num_periods as i128; + + let mut entries: Vec = Vec::new(env); + for i in 0..num_periods { + let start = charge_time + (i as u64) * period_secs; + let end = start + period_secs; + let amount = if i == num_periods - 1 { + slice + remainder + } else { + slice + }; + entries.push_back(RevenueScheduleEntry { + period_start: start, + period_end: end, + recognised_amount: amount, + is_recognised: false, + }); + } + RevenueSchedule { + subscription_id, + total_amount, + entries, + } +} + +pub fn build_usage_based_schedule( + env: &Env, + subscription_id: u64, + total_amount: i128, + charge_time: u64, + interval_secs: u64, +) -> RevenueSchedule { + let mut entries: Vec = Vec::new(env); + entries.push_back(RevenueScheduleEntry { + period_start: charge_time, + period_end: charge_time + interval_secs, + recognised_amount: total_amount, + is_recognised: false, + }); + RevenueSchedule { + subscription_id, + total_amount, + entries, + } +} + +/// Pro-rate a schedule as of `now` → (recognised, deferred). +pub fn split_recognised_deferred(schedule: &RevenueSchedule, now: u64) -> (i128, i128) { + let mut recognised: i128 = 0; + let mut deferred: i128 = 0; + for entry in schedule.entries.iter() { + if now >= entry.period_end { + recognised += entry.recognised_amount; + } else if now >= entry.period_start { + let elapsed = now - entry.period_start; + let duration = entry.period_end - entry.period_start; + let partial = entry.recognised_amount * elapsed as i128 / duration as i128; + recognised += partial; + deferred += entry.recognised_amount - partial; + } else { + deferred += entry.recognised_amount; + } + } + (recognised, deferred) +} + +// ── Storage helpers ─────────────────────────────────────────────────────────── + +pub fn set_recognition_rule(env: &Env, storage: &Address, rule: RevenueRecognitionRule) { + storage_persistent_set( + env, + storage, + StorageKey::RevenueRecognitionRule(rule.plan_id), + rule, + ); +} + +pub fn get_recognition_rule( + env: &Env, + storage: &Address, + plan_id: u64, +) -> Option { + storage_persistent_get(env, storage, StorageKey::RevenueRecognitionRule(plan_id)) +} + +pub fn get_revenue_schedule( + env: &Env, + storage: &Address, + subscription_id: u64, +) -> Option { + storage_persistent_get(env, storage, StorageKey::RevenueSchedule(subscription_id)) +} + +pub fn get_deferred_revenue(env: &Env, storage: &Address, merchant: &Address) -> i128 { + storage_persistent_get( + env, + storage, + StorageKey::RevenueDeferredBalance(merchant.clone()), + ) + .unwrap_or(0i128) +} + +/// Generate and persist a revenue schedule for a charge. +pub fn generate_revenue_schedule( + env: &Env, + storage: &Address, + subscription_id: u64, + plan_id: u64, + total_amount: i128, + charge_time: u64, + interval_secs: u64, +) -> RevenueSchedule { + let rule = get_recognition_rule(env, storage, plan_id); + + let schedule = match rule { + Some(r) => { + let num_periods = if r.recognition_period > 0 { + interval_secs.div_ceil(r.recognition_period) as u32 + } else { + 1 + }; + match r.method { + RecognitionMethod::StraightLine => build_straight_line_schedule( + env, + subscription_id, + total_amount, + charge_time, + r.recognition_period, + num_periods, + ), + RecognitionMethod::UsageBased => build_usage_based_schedule( + env, + subscription_id, + total_amount, + charge_time, + interval_secs, + ), + } + } + None => build_straight_line_schedule( + env, + subscription_id, + total_amount, + charge_time, + interval_secs, + 1, + ), + }; + + storage_persistent_set( + env, + storage, + StorageKey::RevenueSchedule(subscription_id), + schedule.clone(), + ); + schedule +} + +/// Compute a Recognition snapshot as of `now`. +pub fn recognize_revenue( + env: &Env, + storage: &Address, + subscription_id: u64, + merchant: Address, + now: u64, +) -> Recognition { + match get_revenue_schedule(env, storage, subscription_id) { + None => Recognition { + subscription_id, + merchant, + recognised_revenue: 0, + deferred_revenue: 0, + as_of: now, + }, + Some(schedule) => { + let (recognised, deferred) = split_recognised_deferred(&schedule, now); + Recognition { + subscription_id, + merchant, + recognised_revenue: recognised, + deferred_revenue: deferred, + as_of: now, + } + } + } +} + +/// Update merchant cumulative balances after a charge (all revenue starts deferred). +pub fn update_merchant_revenue_balances( + env: &Env, + storage: &Address, + merchant: &Address, + recognised_delta: i128, + deferred_delta: i128, +) { + let prev_rec: i128 = storage_persistent_get( + env, + storage, + StorageKey::RevenueRecognisedBalance(merchant.clone()), + ) + .unwrap_or(0i128); + let prev_def: i128 = storage_persistent_get( + env, + storage, + StorageKey::RevenueDeferredBalance(merchant.clone()), + ) + .unwrap_or(0i128); + + storage_persistent_set( + env, + storage, + StorageKey::RevenueRecognisedBalance(merchant.clone()), + prev_rec + recognised_delta, + ); + storage_persistent_set( + env, + storage, + StorageKey::RevenueDeferredBalance(merchant.clone()), + prev_def + deferred_delta, + ); +} + +/// Register a subscription under a merchant for analytics (deduplicates). +pub fn track_merchant_subscription( + env: &Env, + storage: &Address, + merchant: &Address, + subscription_id: u64, +) { + let mut ids: Vec = storage_persistent_get( + env, + storage, + StorageKey::RevenueMerchantSubscriptions(merchant.clone()), + ) + .unwrap_or(Vec::new(env)); + for existing in ids.iter() { + if existing == subscription_id { + return; + } + } + ids.push_back(subscription_id); + storage_persistent_set( + env, + storage, + StorageKey::RevenueMerchantSubscriptions(merchant.clone()), + ids, + ); +} + +/// Compute per-period revenue analytics for a merchant. +pub fn get_revenue_analytics_by_period( + env: &Env, + storage: &Address, + merchant: &Address, + period_secs: u64, + from: u64, + to: u64, +) -> Vec { + assert!(period_secs > 0, "period_secs must be > 0"); + assert!(to >= from, "to must be >= from"); + + let sub_ids: Vec = storage_persistent_get( + env, + storage, + StorageKey::RevenueMerchantSubscriptions(merchant.clone()), + ) + .unwrap_or(Vec::new(env)); + + let num_buckets = (to - from).div_ceil(period_secs) as u32; + let mut buckets: Vec = Vec::new(env); + for i in 0..num_buckets { + let start = from + (i as u64) * period_secs; + buckets.push_back(PeriodRevenue { + period_start: start, + period_end: start + period_secs, + recognised_amount: 0, + subscription_count: 0, + }); + } + + for sub_id in sub_ids.iter() { + let maybe: Option = + storage_persistent_get(env, storage, StorageKey::RevenueSchedule(sub_id)); + if let Some(schedule) = maybe { + let mut contributed = false; + for entry in schedule.entries.iter() { + if entry.period_start < from || entry.period_start >= to { + continue; + } + let idx = ((entry.period_start - from) / period_secs) as u32; + if idx < num_buckets { + let mut bucket = buckets.get_unchecked(idx); + bucket.recognised_amount += entry.recognised_amount; + if !contributed { + bucket.subscription_count += 1; + contributed = true; + } + buckets.set(idx, bucket); + } + } + } + } + + buckets +} diff --git a/contracts/types/src/lib.rs b/contracts/types/src/lib.rs index f93c984..2867f6e 100644 --- a/contracts/types/src/lib.rs +++ b/contracts/types/src/lib.rs @@ -134,4 +134,16 @@ pub enum StorageKey { /// Proxy pointer to the state storage contract. ProxyStorage, + + // ── Revenue recognition (added with revenue module) ── + /// RevenueRecognitionRule keyed by plan_id. + RevenueRecognitionRule(u64), + /// RevenueSchedule keyed by subscription_id. + RevenueSchedule(u64), + /// Cumulative deferred revenue balance for a merchant. + RevenueDeferredBalance(Address), + /// Cumulative recognised revenue balance for a merchant. + RevenueRecognisedBalance(Address), + /// List of subscription IDs tracked for a merchant (for analytics). + RevenueMerchantSubscriptions(Address), }