Skip to content

## [CONTRACT-22] Add subscription_count TTL Bump on Instance Storage #478

Description

@orunganiekan
  • Points: 200
  • Labels: contract, storage, ttl, reliability

Background

DataKey::ActiveCount is stored in instance storage, which in Soroban shares its TTL with the contract instance itself. However, DataKey::FeeCollector, DataKey::FeeBps, DataKey::GracePeriod, DataKey::WhitelistEnabled, DataKey::Admin, and DataKey::SchemaVersion are all also in instance storage and share the same TTL. The contract currently never explicitly calls extend_ttl on instance storage. On Soroban mainnet, instance storage TTL is bounded — if the contract goes dormant (no transactions) for long enough, all instance-level data expires. A contract that loses its Admin or Token key is effectively bricked: charge will fail on Token lookup, and admin functions will fail on Admin lookup.

Task

Add a bump_instance_ttl(env: &Env) private helper in lib.rs that calls env.storage().instance().extend_ttl(SUBSCRIPTION_TTL_LEDGERS / 2, SUBSCRIPTION_TTL_LEDGERS). Call bump_instance_ttl at the start of every state-mutating public function: subscribe, charge, pay_per_use, cancel, pause, resume, set_fee, set_grace_period, set_whitelist_enabled, add_merchant_to_whitelist, remove_merchant_from_whitelist, and batch_charge. This ensures any active use of the contract continuously refreshes its instance TTL without a separate keeper.

Key Files

  • contract/src/lib.rs — add fn bump_instance_ttl(env: &Env) and call it at the start of all write-path public functions
  • contract/src/test.rs — add a test that verifies env.storage().instance().get_ttl() is $\ge$ SUBSCRIPTION_TTL_LEDGERS / 2 after a subscribe call

Edge Cases

  • Read-only functions (get_protocol_stats, get_subscription, next_charge_at, etc.) must NOT call bump_instance_ttl — they should not write to storage
  • bump_instance_ttl uses threshold = SUBSCRIPTION_TTL_LEDGERS / 2 so it only extends when needed, avoiding unnecessary writes on every single call
  • initialize should call bump_instance_ttl since it is the first write and sets the initial TTL

Acceptance Criteria

  • bump_instance_ttl is called in all write-path functions listed above
  • No read-only function calls bump_instance_ttl
  • Test: env.storage().instance().get_ttl() >= SUBSCRIPTION_TTL_LEDGERS / 2 after subscribe
  • Test: initialize sets a non-zero instance TTL
  • cargo test passes with no regressions

Metadata

Metadata

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions