Storage Design Bug
File: lifebank-soroban/contracts/analytics/src/lib.rs (lines 113–122)
Problem
All five lifetime counters (TotalDonations, TotalRequests, TotalDeliveries, TotalPaymentsReleased, TotalVolume) are stored in instance() storage alongside the contract config:
env.storage().instance().set(&DataKey::TotalDonations, &0u64);
env.storage().instance().set(&DataKey::TotalRequests, &0u64);
env.storage().instance().set(&DataKey::TotalDeliveries, &0u64);
env.storage().instance().set(&DataKey::TotalPaymentsReleased, &0u64);
env.storage().instance().set(&DataKey::TotalVolume, &0i128);
Soroban's instance() storage is loaded into memory on every single contract invocation, regardless of whether the call needs these values. Its total size is also bounded — adding more keys to instance storage increases the per-call memory footprint for the lifetime of the contract.
Additionally, the periodic snapshot entries are stored in persistent() storage with no TTL extension — they will silently expire from the ledger after the default TTL window.
Impact
- Every analytics contract call pays the memory cost of loading all 5 counters plus config, even for a simple
get_current_snapshot() read
- Snapshots expire from persistent storage if not accessed within the TTL window — historical periods are permanently lost
- As the number of tracked metrics grows, instance storage bloat increases the cost of every transaction
Fix
Move lifetime counters to persistent() storage with individual keys, and extend TTL on snapshots when they are written:
// Write with TTL extension
env.storage().persistent().set(&DataKey::Snapshot(idx), &snap);
env.storage().persistent().extend_ttl(&DataKey::Snapshot(idx), MIN_TTL, MAX_TTL);
Storage Design Bug
File:
lifebank-soroban/contracts/analytics/src/lib.rs(lines 113–122)Problem
All five lifetime counters (
TotalDonations,TotalRequests,TotalDeliveries,TotalPaymentsReleased,TotalVolume) are stored ininstance()storage alongside the contract config:Soroban's
instance()storage is loaded into memory on every single contract invocation, regardless of whether the call needs these values. Its total size is also bounded — adding more keys to instance storage increases the per-call memory footprint for the lifetime of the contract.Additionally, the periodic snapshot entries are stored in
persistent()storage with no TTL extension — they will silently expire from the ledger after the default TTL window.Impact
get_current_snapshot()readFix
Move lifetime counters to
persistent()storage with individual keys, and extend TTL on snapshots when they are written: