From 18527850d577c613b0c49d61b32023d6d777ea99 Mon Sep 17 00:00:00 2001 From: legend-esc Date: Mon, 29 Jun 2026 13:36:20 +0100 Subject: [PATCH] Add storage key XDR snapshot test (#331) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add storage_snapshot.rs test that serializes every StorageKey variant to XDR hex and compares against committed baseline - Create tests/snapshots/storage_keys.json baseline with all 91 key XDR values, sorted alphabetically - Collision check: no two keys produce identical XDR - Add CI step: cargo test -p split storage_snapshot - Add README Storage Key Registry section with policy docs - Fix pre-existing compilation bugs blocking the build: - arch_after symbol too long (10 > 9) → arch_af - Remove non-existent InvoiceExt2 fields - Remove duplicate code block in load_invoice - Close unclosed reveal_confidential_total function --- .github/workflows/test.yml | 3 + README.md | 15 ++ contracts/split/src/lib.rs | 13 +- contracts/split/src/storage_snapshot.rs | 191 ++++++++++++++++++++++++ tests/snapshots/storage_keys.json | 100 +++++++++++++ 5 files changed, 315 insertions(+), 7 deletions(-) create mode 100644 contracts/split/src/storage_snapshot.rs create mode 100644 tests/snapshots/storage_keys.json diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7ee154f..6a61cc7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,3 +31,6 @@ jobs: - name: Run tests run: cargo test --workspace + + - name: Storage key snapshot test + run: cargo test -p split storage_snapshot diff --git a/README.md b/README.md index 085b9e4..0b92f02 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,21 @@ Returns the full invoice struct. PLACEHOLDER — update after deployment ``` +## Storage Key Registry + +Every storage key the contract uses is serialised to XDR and compared against a committed baseline in `tests/snapshots/storage_keys.json`. The snapshot runs as `cargo test -p split storage_snapshot`. + +**Policy:** +- Adding, removing, or changing any storage key will intentionally fail the snapshot test. +- If the change is intentional (e.g. a new feature adds a key, or a migration renames an existing one), update the baseline file by running the test locally, copying the generated XDR into `tests/snapshots/storage_keys.json`, and **including a migration note in the PR description**. +- The test also asserts that **no two keys produce the same XDR** (collision-free). +- Each key entry in the snapshot is labelled by its Rust function name (e.g. `invoice_key`, `rep_key`). + +To update the baseline: +```bash +cargo test -p split storage_snapshot 2>&1 | grep -A 999 "EXPECTED (generated)" | tail -n +2 | head -n -1 > tests/snapshots/storage_keys.json +``` + ## Contributing via Drips Wave This project participates in the [Drips Wave Program](https://drips.network/wave) by the Stellar Development Foundation. Contributors can earn rewards by completing open issues. diff --git a/contracts/split/src/lib.rs b/contracts/split/src/lib.rs index 0bdc506..d78338f 100644 --- a/contracts/split/src/lib.rs +++ b/contracts/split/src/lib.rs @@ -31,6 +31,9 @@ mod test; #[cfg(test)] mod fuzz_tests; +#[cfg(test)] +mod storage_snapshot; + use soroban_sdk::{ String, contract, contractimpl, symbol_short, token, Address, Bytes, BytesN, Env, IntoVal, Map, Symbol, Val, Vec, @@ -127,7 +130,7 @@ fn counter_key() -> Symbol { symbol_short!("counter") } fn archive_after_ledgers_key() -> Symbol { - symbol_short!("arch_after") + symbol_short!("arch_af") } fn archive_marker_key(id: u64) -> (Symbol, u64) { (symbol_short!("archv"), id) @@ -568,8 +571,6 @@ fn archive_invoice_storage(env: &Env, id: u64, core: &InvoiceCore) { cross_chain_ref: None, require_kyc: false, arbiter: None, disputed: false, admin_frozen: false, auction_on_expiry: false, auction_end: 0, bids: Vec::new(env), min_payment: 0, min_funding_amount: 0, priorities: Vec::new(env), - substitute_recipient_approvals: Vec::new(env), - creation_timestamp: 0, min_payment_increment: 0, }); env.storage().instance().set(&invoice_key(id), core); @@ -726,10 +727,6 @@ fn load_invoice(env: &Env, id: u64) -> Invoice { target_usd_cents: None, }); - // Load compact representation if available - if let Some(compact) = env.storage().persistent().get::<_, CompactInvoice>(&invoice_compact_key(id)) - .or_else(|| env.storage().instance().get(&invoice_compact_key(id))) - { // Load compact representation if available, then overlay hot fields. let mut invoice = if let Some(compact) = env.storage().persistent().get::<_, CompactInvoice>(&invoice_compact_key(id)) { Invoice::from_compact(&compact, core, ext, ext2) @@ -6830,6 +6827,8 @@ impl SplitContract { let actor = env.current_contract_address(); Self::_release(&env, invoice_id, &mut invoice.clone(), &actor); } + } + // Issue #308: Per-payer claim_refund after deadline // ----------------------------------------------------------------------- diff --git a/contracts/split/src/storage_snapshot.rs b/contracts/split/src/storage_snapshot.rs new file mode 100644 index 0000000..51eb5af --- /dev/null +++ b/contracts/split/src/storage_snapshot.rs @@ -0,0 +1,191 @@ +#![cfg(test)] + +use super::*; +use soroban_sdk::{xdr::ToXdr, Env}; + +fn hex_xdr(env: &Env, val: impl ToXdr) -> String { + let bytes = val.to_xdr(env); + bytes.iter().map(|b| format!("{:02x}", b)).collect::>().concat() +} + +#[test] +fn storage_key_snapshot() { + let env = Env::default(); + // Deterministic address (all-zero G... strkey) — do not change. + let a = Address::from_str(&env, "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF"); + let s = symbol_short!("x"); + + let mut keys: Vec<(&str, String)> = Vec::new(); + + // ----------------------------------------------------------------------- + // Instance-tier keys (contract-level singletons, return Symbol) + // ----------------------------------------------------------------------- + + keys.push(("admin_key", hex_xdr(&env, admin_key()))); + keys.push(("admins_key", hex_xdr(&env, admins_key()))); + keys.push(("paused_key", hex_xdr(&env, paused_key()))); + keys.push(("paused_fns_key", hex_xdr(&env, paused_fns_key()))); + keys.push(("treasury_key", hex_xdr(&env, treasury_key()))); + keys.push(("usdc_token_key", hex_xdr(&env, usdc_token_key()))); + keys.push(("creation_fee_key", hex_xdr(&env, creation_fee_key()))); + keys.push(("platform_fee_bps_key", hex_xdr(&env, platform_fee_bps_key()))); + keys.push(("platform_fee_waiver_list_key", hex_xdr(&env, platform_fee_waiver_list_key()))); + keys.push(("creator_fee_waiver_key", hex_xdr(&env, creator_fee_waiver_key()))); + keys.push(("counter_key", hex_xdr(&env, counter_key()))); + keys.push(("global_payer_limit_key", hex_xdr(&env, global_payer_limit_key()))); + keys.push(("global_payer_window_key", hex_xdr(&env, global_payer_window_key()))); + keys.push(("stream_contract_key", hex_xdr(&env, stream_contract_key()))); + keys.push(("creator_whitelist_key", hex_xdr(&env, creator_whitelist_key()))); + keys.push(("compliance_key", hex_xdr(&env, compliance_key()))); + keys.push(("rate_limit_key", hex_xdr(&env, rate_limit_key()))); + keys.push(("rate_window_key", hex_xdr(&env, rate_window_key()))); + keys.push(("max_cancel_bps_key", hex_xdr(&env, max_cancel_bps_key()))); + keys.push(("receipt_factory_key", hex_xdr(&env, receipt_factory_key()))); + keys.push(("dashboard_contract_key", hex_xdr(&env, dashboard_contract_key()))); + keys.push(("nft_gate_key", hex_xdr(&env, nft_gate_key()))); + keys.push(("timelock_secs_key", hex_xdr(&env, timelock_secs_key()))); + keys.push(("timelock_action_counter_key", hex_xdr(&env, timelock_action_counter_key()))); + keys.push(("fee_tiers_key", hex_xdr(&env, fee_tiers_key()))); + keys.push(("pending_admin_key", hex_xdr(&env, pending_admin_key()))); + keys.push(("governance_contract_key", hex_xdr(&env, governance_contract_key()))); + keys.push(("factories_key", hex_xdr(&env, factories_key()))); + keys.push(("total_invoices_key", hex_xdr(&env, total_invoices_key()))); + keys.push(("total_volume_key", hex_xdr(&env, total_volume_key()))); + keys.push(("total_released_key", hex_xdr(&env, total_released_key()))); + keys.push(("total_refunded_key", hex_xdr(&env, total_refunded_key()))); + keys.push(("treasury_group_counter_key", hex_xdr(&env, treasury_group_counter_key()))); + keys.push(("circuit_breaker_key", hex_xdr(&env, circuit_breaker_key()))); + keys.push(("circuit_breaker_reason_key", hex_xdr(&env, circuit_breaker_reason_key()))); + keys.push(("archive_after_ledgers_key", hex_xdr(&env, archive_after_ledgers_key()))); + keys.push(("platform_vol_thresh_key", hex_xdr(&env, platform_vol_thresh_key()))); + keys.push(("platform_vol_mile_key", hex_xdr(&env, platform_vol_mile_key()))); + keys.push(("creator_vol_thresh_key", hex_xdr(&env, creator_vol_thresh_key()))); + keys.push(("kyc_contract_key", hex_xdr(&env, kyc_contract_key()))); + keys.push(("upgrade_proposal_key", hex_xdr(&env, upgrade_proposal_key()))); + + // ----------------------------------------------------------------------- + // Persistent-tier keys (per-entity) + // ----------------------------------------------------------------------- + + // (Symbol, u64) + keys.push(("invoice_key", hex_xdr(&env, invoice_key(1)))); + keys.push(("invoice_ext_key", hex_xdr(&env, invoice_ext_key(1)))); + keys.push(("invoice_ext2_key", hex_xdr(&env, invoice_ext2_key(1)))); + keys.push(("invoice_compact_key", hex_xdr(&env, invoice_compact_key(1)))); + keys.push(("invoice_hot_key", hex_xdr(&env, invoice_hot_key(1)))); + keys.push(("audit_log_key", hex_xdr(&env, audit_log_key(1)))); + keys.push(("archive_marker_key", hex_xdr(&env, archive_marker_key(1)))); + keys.push(("created_ledger_key", hex_xdr(&env, created_ledger_key(1)))); + keys.push(("subscription_params_key", hex_xdr(&env, subscription_params_key(1)))); + keys.push(("confidential_count_key", hex_xdr(&env, confidential_count_key(1)))); + keys.push(("ext_vote_key", hex_xdr(&env, ext_vote_key(1)))); + keys.push(("group_key", hex_xdr(&env, group_key(1)))); + keys.push(("invoice_group_key", hex_xdr(&env, invoice_group_key(1)))); + keys.push(("invoice_treasury_key", hex_xdr(&env, invoice_treasury_key(1)))); + keys.push(("group_treasury_key", hex_xdr(&env, group_treasury_key(1)))); + keys.push(("delegate_key", hex_xdr(&env, delegate_key(1)))); + keys.push(("payment_window_key", hex_xdr(&env, payment_window_key(1)))); + keys.push(("cert_key", hex_xdr(&env, cert_key(1)))); + keys.push(("timelock_action_key", hex_xdr(&env, timelock_action_key(1)))); + keys.push(("refunded_key", hex_xdr(&env, refunded_key(1)))); + + // (Symbol, Address) + keys.push(("pause_exempt_key", hex_xdr(&env, pause_exempt_key(&a)))); + keys.push(("global_vel_key", hex_xdr(&env, global_vel_key(&a)))); + keys.push(("rep_key", hex_xdr(&env, rep_key(&a)))); + keys.push(("credit_key", hex_xdr(&env, credit_key(&a)))); + keys.push(("referral_count_key", hex_xdr(&env, referral_count_key(&a)))); + keys.push(("recipient_invoice_ids_key", hex_xdr(&env, recipient_invoice_ids_key(&a)))); + keys.push(("delegate_pay_key", hex_xdr(&env, delegate_pay_key(&a)))); + keys.push(("rate_usage_key", hex_xdr(&env, rate_usage_key(&a)))); + keys.push(("invoice_count_key", hex_xdr(&env, invoice_count_key(&a)))); + keys.push(("cancel_count_key", hex_xdr(&env, cancel_count_key(&a)))); + keys.push(("creator_stats_count_key", hex_xdr(&env, creator_stats_count_key(&a)))); + keys.push(("creator_stats_volume_key", hex_xdr(&env, creator_stats_volume_key(&a)))); + keys.push(("creator_stats_released_key", hex_xdr(&env, creator_stats_released_key(&a)))); + keys.push(("creator_stats_refunded_key", hex_xdr(&env, creator_stats_refunded_key(&a)))); + keys.push(("creator_stats_payers_key", hex_xdr(&env, creator_stats_payers_key(&a)))); + keys.push(("creator_stats_avg_funding_key", hex_xdr(&env, creator_stats_avg_funding_key(&a)))); + keys.push(("creator_vol_mile_key", hex_xdr(&env, creator_vol_mile_key(&a)))); + keys.push(("creator_volume_cap_key", hex_xdr(&env, creator_volume_cap_key(&a)))); + keys.push(("creator_volume_used_key", hex_xdr(&env, creator_volume_used_key(&a)))); + + // (Symbol, u64, Address) + keys.push(("confidential_pay_key", hex_xdr(&env, confidential_pay_key(1, &a)))); + keys.push(("reminder_key", hex_xdr(&env, reminder_key(1, &a)))); + keys.push(("pending_payout_key", hex_xdr(&env, pending_payout_key(1, &a)))); + keys.push(("channel_key", hex_xdr(&env, channel_key(1, &a)))); + keys.push(("nonce_key", hex_xdr(&env, nonce_key(1, &a)))); + keys.push(("vel_key", hex_xdr(&env, vel_key(1, &a)))); + keys.push(("receipt_token_key", hex_xdr(&env, receipt_token_key(1, &a)))); + keys.push(("accum_key", hex_xdr(&env, accum_key(1, &a)))); + + // (Symbol, u64, Address) where Address is owned + keys.push(("payer_cooldown_key", hex_xdr(&env, payer_cooldown_key(1, a.clone())))); + + // (Symbol, u64, u64) + keys.push(("pay_shard_key", hex_xdr(&env, pay_shard_key(1, 1)))); + + // (Symbol, Address, Symbol) + keys.push(("template_key", hex_xdr(&env, template_key(&a, &s)))); + keys.push(("template_version_count_key", hex_xdr(&env, template_version_count_key(&a, &s)))); + + // (Symbol, Address, Symbol, u32) + keys.push(("template_version_key", hex_xdr(&env, template_version_key(&a, &s, 1)))); + + // Sort by key name for deterministic output + keys.sort_by(|a, b| a.0.cmp(b.0)); + + // ----------------------------------------------------------------------- + // Collision check + // ----------------------------------------------------------------------- + { + let mut seen = std::collections::HashSet::new(); + for (name, xdr) in &keys { + assert!( + seen.insert(xdr), + "Collision detected: '{}' has the same XDR as another key", + name, + ); + } + } + + // ----------------------------------------------------------------------- + // Build expected snapshot JSON + // ----------------------------------------------------------------------- + let mut lines: Vec = Vec::new(); + lines.push("{".to_string()); + lines.push(format!(" \"_comment\": \"Storage key XDR snapshot — see README Storage Key Registry section for policy.\",")); + lines.push(format!(" \"_keys_introduced\": \"Snapshot introduced with #331. Add your key name and XDR here.\",")); + lines.push(format!(" \"version\": \"1\",")); + lines.push(format!(" \"keys\": {{")); + for (i, (name, xdr)) in keys.iter().enumerate() { + let comma = if i == keys.len() - 1 { "" } else { "," }; + lines.push(format!(" \"{}\": \"{}\"{}", name, xdr, comma)); + } + lines.push(format!(" }}")); + lines.push(format!("}}")); + lines.push(String::new()); + let generated = lines.join("\n"); + + // ----------------------------------------------------------------------- + // Compare against committed baseline + // ----------------------------------------------------------------------- + let baseline = include_str!("../../../tests/snapshots/storage_keys.json"); + + if generated != baseline { + panic!( + "\n=== SNAPSHOT MISMATCH ===\n\ + Storage key XDR has changed from the committed baseline.\n\ + If this is intentional, update tests/snapshots/storage_keys.json\n\ + with the new output below, and include a migration note in your PR.\n\ + \n\ + === EXPECTED (generated) ===\n\ + {}\n\ + === ACTUAL (baseline) ===\n\ + {}\n\ + ============================", + generated, baseline, + ); + } +} diff --git a/tests/snapshots/storage_keys.json b/tests/snapshots/storage_keys.json new file mode 100644 index 0000000..10c232b --- /dev/null +++ b/tests/snapshots/storage_keys.json @@ -0,0 +1,100 @@ +{ + "_comment": "Storage key XDR snapshot — see README Storage Key Registry section for policy.", + "_keys_introduced": "Snapshot introduced with #331. Add your key name and XDR here.", + "version": "1", + "keys": { + "accum_key": "0000001000000001000000030000000f00000005616363756d0000000000000500000000000000010000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "admin_key": "0000000f0000000561646d696e000000", + "admins_key": "0000000f0000000661646d696e730000", + "archive_after_ledgers_key": "0000000f00000007617263685f616600", + "archive_marker_key": "0000001000000001000000020000000f000000056172636876000000000000050000000000000001", + "audit_log_key": "0000001000000001000000020000000f000000036c6f6700000000050000000000000001", + "cancel_count_key": "0000001000000001000000020000000f00000009636e6c5f636f756e740000000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "cert_key": "0000001000000001000000020000000f0000000463657274000000050000000000000001", + "channel_key": "0000001000000001000000030000000f000000046368616e0000000500000000000000010000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "circuit_breaker_key": "0000000f0000000763625f666c616700", + "circuit_breaker_reason_key": "0000000f0000000663625f72736e0000", + "compliance_key": "0000000f00000006636f6d706c790000", + "confidential_count_key": "0000001000000001000000020000000f00000008636f6e665f636e74000000050000000000000001", + "confidential_pay_key": "0000001000000001000000030000000f00000008636f6e665f7061790000000500000000000000010000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "counter_key": "0000000f00000007636f756e74657200", + "created_ledger_key": "0000001000000001000000020000000f0000000963725f6c6564676572000000000000050000000000000001", + "creation_fee_key": "0000000f000000076372745f66656500", + "creator_fee_waiver_key": "0000000f0000000563725f6677000000", + "creator_stats_avg_funding_key": "0000001000000001000000020000000f0000000763725f61766766000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "creator_stats_count_key": "0000001000000001000000020000000f0000000663725f636e7400000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "creator_stats_payers_key": "0000001000000001000000020000000f0000000663725f70797200000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "creator_stats_refunded_key": "0000001000000001000000020000000f0000000663725f72656600000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "creator_stats_released_key": "0000001000000001000000020000000f0000000663725f72656c00000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "creator_stats_volume_key": "0000001000000001000000020000000f0000000663725f766f6c00000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "creator_vol_mile_key": "0000001000000001000000020000000f0000000763725f765f6d73000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "creator_vol_thresh_key": "0000000f0000000763725f765f746800", + "creator_volume_cap_key": "0000001000000001000000020000000f0000000863725f765f6361700000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "creator_volume_used_key": "0000001000000001000000020000000f0000000863725f765f7573650000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "creator_whitelist_key": "0000000f000000066372745f776c0000", + "credit_key": "0000001000000001000000020000000f0000000663726564697400000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "dashboard_contract_key": "0000000f00000008646173685f637472", + "delegate_key": "0000001000000001000000020000000f0000000864656c6567617465000000050000000000000001", + "delegate_pay_key": "0000001000000001000000020000000f00000008646c67745f7061790000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "ext_vote_key": "0000001000000001000000020000000f000000086578745f766f7465000000050000000000000001", + "factories_key": "0000000f00000009666163746f72696573000000", + "fee_tiers_key": "0000000f000000076665655f74727300", + "global_payer_limit_key": "0000000f00000009675f76656c5f6c696d000000", + "global_payer_window_key": "0000000f00000009675f76656c5f77696e000000", + "global_vel_key": "0000001000000001000000020000000f00000005675f76656c0000000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "governance_contract_key": "0000000f00000007676f765f63747200", + "group_key": "0000001000000001000000020000000f0000000367727000000000050000000000000001", + "group_treasury_key": "0000001000000001000000020000000f000000066772705f74720000000000050000000000000001", + "invoice_compact_key": "0000001000000001000000020000000f00000007696e765f63707400000000050000000000000001", + "invoice_count_key": "0000001000000001000000020000000f00000009696e765f636f756e740000000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "invoice_ext2_key": "0000001000000001000000020000000f00000007696e765f65783200000000050000000000000001", + "invoice_ext_key": "0000001000000001000000020000000f00000007696e765f65787400000000050000000000000001", + "invoice_group_key": "0000001000000001000000020000000f00000006696e766772700000000000050000000000000001", + "invoice_hot_key": "0000001000000001000000020000000f00000007696e765f686f7400000000050000000000000001", + "invoice_key": "0000001000000001000000020000000f00000003696e7600000000050000000000000001", + "invoice_treasury_key": "0000001000000001000000020000000f00000006696e765f74720000000000050000000000000001", + "kyc_contract_key": "0000000f000000076b79635f63747200", + "max_cancel_bps_key": "0000000f000000096d785f636e6c5f6270000000", + "nft_gate_key": "0000000f000000076e66745f67746500", + "nonce_key": "0000001000000001000000030000000f000000056e6f6e63650000000000000500000000000000010000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "pause_exempt_key": "0000001000000001000000020000000f00000008705f6578656d70740000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "paused_fns_key": "0000000f0000000670735f666e730000", + "paused_key": "0000000f000000067061757365640000", + "pay_shard_key": "0000001000000001000000030000000f000000067061795f73680000000000050000000000000001000000050000000000000001", + "payer_cooldown_key": "0000001000000001000000030000000f000000067079725f636400000000000500000000000000010000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "payment_window_key": "0000001000000001000000020000000f000000077061795f77696e00000000050000000000000001", + "pending_admin_key": "0000000f0000000870656e645f61646d", + "pending_payout_key": "0000001000000001000000030000000f0000000870656e645f7061790000000500000000000000010000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "platform_fee_bps_key": "0000000f00000008706c61745f666565", + "platform_fee_waiver_list_key": "0000000f000000086665655f77767273", + "platform_vol_mile_key": "0000000f00000008706c745f765f6d73", + "platform_vol_thresh_key": "0000000f00000008706c745f765f7468", + "rate_limit_key": "0000000f00000008726174655f6c696d", + "rate_usage_key": "0000001000000001000000020000000f00000004726174650000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "rate_window_key": "0000000f00000008726174655f77696e", + "receipt_factory_key": "0000000f00000008726370745f666163", + "receipt_token_key": "0000001000000001000000030000000f00000004726370740000000500000000000000010000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "recipient_invoice_ids_key": "0000001000000001000000020000000f000000077265635f696e76000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "referral_count_key": "0000001000000001000000020000000f000000077265665f636e74000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "refunded_key": "0000001000000001000000020000000f00000008726566756e646564000000050000000000000001", + "reminder_key": "0000001000000001000000030000000f0000000372656d000000000500000000000000010000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "rep_key": "0000001000000001000000020000000f00000003726570000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", + "stream_contract_key": "0000000f000000087374726d5f637472", + "subscription_params_key": "0000001000000001000000020000000f0000000373756200000000050000000000000001", + "template_key": "0000001000000001000000030000000f00000004746d706c00000012000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0000000178000000", + "template_version_count_key": "0000001000000001000000030000000f00000007746d706c5f63740000000012000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0000000178000000", + "template_version_key": "0000001000000001000000040000000f00000006746d706c5f76000000000012000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f00000001780000000000000300000001", + "timelock_action_counter_key": "0000000f00000007746c5f636e747200", + "timelock_action_key": "0000001000000001000000020000000f00000006746c5f6163740000000000050000000000000001", + "timelock_secs_key": "0000000f00000007746c5f7365637300", + "total_invoices_key": "0000000f00000007746f745f696e7600", + "total_refunded_key": "0000000f00000007746f745f72656600", + "total_released_key": "0000000f00000007746f745f72656c00", + "total_volume_key": "0000000f00000007746f745f766f6c00", + "treasury_group_counter_key": "0000000f000000096772705f74725f636e000000", + "treasury_key": "0000000f000000087472656173757279", + "upgrade_proposal_key": "0000000f000000087570675f70726f70", + "usdc_token_key": "0000000f00000008757364635f746f6b", + "vel_key": "0000001000000001000000030000000f0000000376656c000000000500000000000000010000001200000000000000000000000000000000000000000000000000000000000000000000000000000000" + } +}