Adaptive TTL Extension for Program-Escrow
Overview
The Program Escrow contract manages the lifecycle of hackathon and program prize pools. A core part of storage management in Soroban is TTL (Time-To-Live) extension, which prevents active ledger entries from being archived and evicted from the state.
Previously, ProgramData and PayoutRecord entries received a uniform TTL extension, regardless of how often a program was accessed. This meant a frequently-active program paid the same relative TTL-extension overhead as a rarely-touched program, which wasn't efficient for long-running but inactive programs approaching archival.
This document outlines the Access-Frequency-Adaptive TTL Extension Policy introduced to optimize storage costs.
Mechanism
Lightweight Tracking
To adapt TTL based on activity, the contract tracks a lightweight hotness signal for each program:
- Signal Key:
DataKey::ProgramAccessSignal(program_id) - Signal Format: A simple
u32counter representing the total number of accesses/writes for a program. - Bounds: The counter is capped at
100(TTL_MAX_ACCESS_COUNT). This prevents unbounded storage value growth and caps the maximum TTL possible.
The signal increments whenever critical program state changes, specifically during:
- Payout distributions (
store_program_dataandappend_recipient_index). - Program locking and general state updates (
store_program_data).
Adaptive Extension Policy
When a write occurs, the contract determines the new TTL to set for the program's instance and persistent storage keys based on its access count. The policy scales the extension linearly between a safe minimum and maximum bound:
- Cold Programs (Access Count = 0): Gets the base TTL extension of
518,400ledgers (~30 days). This is the cheapest extension. - Hot Programs (Access Count = 100): Gets the maximum TTL extension of
3,110,400ledgers (~180 days).
For programs in between, the formula is:
extra_ttl = (MAX_TTL - MIN_TTL) * access_count / MAX_ACCESS_COUNT
ttl_to_set = MIN_TTL + extra_ttl
Affected Data Keys
- Instance Storage: Since
ProgramDatais stored in the contract's instance storage, the adaptive TTL policy extends the entire instance's TTL. - Persistent Storage: For
PayoutRecordentries (stored viaRecipientPayoutIndexkeys) and theProgramAccessSignalitself, the contract extends the TTL for the specific persistent key.
Reasoning for Operators
Operators can reason about worst-case archival timing easily:
- If a program becomes completely inactive, its last state change will leave it with a TTL proportional to its historical activity.
- Extremely hot programs will survive untouched for up to 180 days before falling to archival.
- Extremely cold/unsuccessful programs will expire much faster (within 30 days of their last action), saving network state rent.
Security and Efficiency Assumptions
- The
access_countis tracked using standard safe math. Because it is explicitly capped at100, overflow is structurally impossible, preserving security. - The tracking adds minimal overhead (one
u32read/write). - The linear scaling requires simple integer arithmetic without expensive loops or arrays, making the gas profile cheap and deterministic.