Canonical map of Soroban storage keys for raffle-factory and raffle-instance: tier (instance vs persistent), write pattern, archival risk, and operator TTL guidance.
Soroban deletes expired entries permanently. These contracts do not call
extend_ttlon-chain; operators must bump TTLs externally (Stellar CLI / cron). See also the TTL section in DEVELOPMENT.md.
| Tier | TTL model | Used when |
|---|---|---|
| Instance | One TTL for the whole contract instance entry | Hot operational flags and the live Raffle blob |
| Persistent | Per-key TTL | Registry indexes, tickets, fairness audit data, factory config |
| Temporary | Short-lived | Not used by either contract today |
Approximate ledger timing on public networks: ~5 seconds per ledger. Default persistent minimum TTL is on the order of ~120 days; plan bumps well before expiry.
Source of truth: DataKey enum.
| DataKey | Tier | Written | Consensus-critical? | Archive-safe? | Notes |
|---|---|---|---|---|---|
Initialized |
Persistent | Once (init_factory) |
Yes | No | Guards single init |
Admin |
Persistent | Once + on admin transfer accept | Yes | No | Loss = permanent lockout |
InstanceWasmHash |
Persistent | Init + timelocked upgrade ops | Yes | No | Required to deploy instances |
ProtocolFeeBP |
Persistent | Init + executed PendingOp |
Yes | No | Fee policy |
Treasury |
Persistent | Init + executed PendingOp |
Yes | No | Fee recipient |
Paused |
Instance | Updated on pause/unpause | Yes | No | Absent ⇒ not paused |
PendingAdmin |
Persistent | Set on propose; removed on accept/cancel | Yes (while pending) | No while pending | Two-step admin transfer |
PendingOp(u32) |
Persistent | Per proposed op; removed on execute/cancel | Yes while pending | After execute/cancel OK | Timelock payload (TIMELOCK_DELAY_SECONDS = 172800) |
OpCounter |
Persistent | Incremented per propose | Yes | No | Allocates op IDs |
RaffleById(u32) |
Persistent | On create_raffle; removed on tombstone |
Yes | No for live IDs | O(1) stable ID → address map |
NextRaffleId |
Persistent | Incremented on create | Yes | No | Next stable ID (never decremented) |
RaffleCount |
Persistent | Updated on create/tombstone | Operational | Prefer keep | Live (non-tombstoned) count |
TotalRafflesCreated |
Persistent | Incremented on create | Audit | Prefer keep | Cumulative creations |
CreatorRaffles(Address) |
Persistent | Appended on create | Index | Prefer keep | Per-creator list |
CategoryRaffles(String) |
Persistent | Appended when config has category | Index | Prefer keep | Category filter index (#439) |
UniqueParticipant(Address) |
Persistent | Once per address | Analytics | Soft | First-seen flag |
TotalUniqueParticipants |
Persistent | Incremented with unique | Analytics | Soft | Aggregate |
MinCreationDelay |
Persistent | Admin config | Yes if rate-limit on | Prefer keep | Creation rate limit |
LastCreationTime(Address) |
Persistent | Per successful create | Yes if rate-limit on | Soft after delay window | Per-creator timestamp |
WhitelistedPartner(Address) |
Persistent | Admin set | Yes if whitelist used | Prefer keep | Partner bypass / privileges |
TotalVolumePerAsset(Address) |
Persistent | Updated on volume record | Analytics | Soft | Cumulative volume |
Checkpoint(u32) |
Persistent | Every CHECKPOINT_INTERVAL (1000) raffles |
Audit | Soft historically | Periodic snapshot |
LatestCheckpointIndex |
Persistent | With checkpoint write | Audit | Prefer keep | Points at latest checkpoint |
RaffleInstancesCount |
Persistent | Test path only | Test | N/A | Address generation under cfg(test) |
- Write-once (effectively):
Initialized, firstAdmin/InstanceWasmHash/ProtocolFeeBP/Treasury(later changes go through timelock or transfer flows). - Per-raffle create:
RaffleById,NextRaffleId,RaffleCount,TotalRafflesCreated,CreatorRaffles, optionalCategoryRaffles, rate-limit + volume keys. - Per admin action:
Paused,PendingAdmin,PendingOp/OpCounter, partner/delay config.
| Keep forever (operator must bump) | Can expire only after lifecycle ends |
|---|---|
Admin, Initialized, InstanceWasmHash, Treasury, ProtocolFeeBP, NextRaffleId, live RaffleById(*) |
Historical Checkpoint(*) (audit degradation only), stale LastCreationTime, old tombstoned indexes |
Source of truth: DataKey enum (and admin-cancel flow keys noted below).
| DataKey | Tier | Written | Consensus-critical? | Archive-safe? | Notes |
|---|---|---|---|---|---|
Raffle |
Instance | Init; updated every lifecycle tx | Yes | No until fully claimed/cleaned | Full raffle state (status, winners, fees, …) |
Factory |
Instance | Once at init |
Yes | No while live | Parent factory address |
Admin |
Instance (primary); also cleared from persistent on cleanup | Init / admin update paths | Yes | No | Authorization |
Paused |
Instance | Pause / unpause | Yes | No | Instance circuit breaker |
ReentrancyGuard |
Instance | Set around guarded calls; removed after | Transient | Yes after call | Must not stick true across txs |
DrawingLock |
Instance | Set on draw start; cleared on complete/fallback/cancel | Yes during draw | Yes after clear | Single-owner draw guard |
RandomnessRequested |
Instance | External draw request | Yes while pending | Yes after resolve | Oracle pending flag |
RandomnessRequestLedger |
Instance | With request | Yes while pending | Yes after resolve | Timeout base (ORACLE_TIMEOUT_LEDGERS = 200) |
RandomnessRequestId |
Instance | With request (when ID allocated) | Yes while pending | Yes after resolve | Correlates oracle callback |
AccumulatedFees |
Instance | Ticket buys / fee withdraw | Yes | No while balance > 0 | Escrowed protocol fees |
FinishTime |
Instance | Lifecycle helper / cleanup target | Operational | After terminal state | Present in enum; extend with instance TTL |
RandomnessSeed |
Persistent (fairness metadata); readers may also check instance in older paths | On successful finalize | Audit / dispute | Prefer keep after finalize | FairnessMetadata / seed fingerprint for get_fairness_data |
Ticket(u32) |
Persistent | Per purchased ticket | Yes | No until refunds/claims done | Owner + purchase metadata |
TicketCount(Address) |
Persistent | Updated per buy | Yes | No until cleanup | Per-buyer counts |
OwnerTickets(Address) |
Persistent | Appended per buy | Yes | No until cleanup | O(1) owner → ticket IDs index |
TicketBuyers |
Persistent | Appended when buyer first appears | Yes | No until cleanup | Buyer enumeration for cleanup/refunds |
TicketRefunded(u32) |
Persistent | Once per refunded/claimed ticket | Yes | After full settlement | Idempotency marker |
CommitEntry(u32) |
Persistent | submit_commit (CommitReveal mode) |
Yes until draw | After finalize OK | Hash keyed by ticket ID (survives transfer) |
Admin cancellation scheduling (execute_admin_cancel / related flows) stores a unlock timestamp under instance storage as PendingAdminCancel (u64). Treat it as consensus-critical while a cancel is scheduled; remove after execution. If your checkout’s DataKey enum is mid-refactor, keep this key’s tier/lifetime aligned with those call sites.
| Pattern | Keys |
|---|---|
| Once at init | Factory, Admin, initial Raffle |
| Every ticket purchase | Ticket, TicketCount, OwnerTickets, TicketBuyers, Raffle, maybe AccumulatedFees |
| Commit-reveal | CommitEntry(ticket_id) once per commit |
| Draw / oracle | DrawingLock, RandomnessRequested, RandomnessRequestLedger, RandomnessRequestId |
| Finalize | Raffle (winners/status), RandomnessSeed |
| Claim / refund | TicketRefunded, Raffle.claimed_winners |
| Transient | ReentrancyGuard |
| Must not archive while… | Safer to drop only after… |
|---|---|
Raffle / instance TTL — Active, Drawing, Finalized with unclaimed prizes |
Claimed, Cancelled, or Failed and cleanup |
Any Ticket(*) / OwnerTickets / TicketCount — sales or refunds open |
Explicit cleanup / all refunds processed |
RandomnessSeed — disputes possible |
Policy retention window post-finalize |
| Oracle pending keys | provide_randomness or fallback completes |
| Contract | Extend instance by | Re-bump cadence | Priority persistent keys |
|---|---|---|---|
| Factory (long-lived) | ~1 year (6220800 ledgers) |
Every ~6 months | Admin, InstanceWasmHash, Treasury, ProtocolFeeBP, NextRaffleId, live RaffleById |
| Instance (per raffle) | ~6 months (3110400 ledgers) while Active/Drawing/Finalized |
Monthly for open raffles | All Ticket(*), TicketCount, OwnerTickets, CommitEntry, RandomnessSeed |
Example (factory instance TTL):
stellar contract extend \
--id <FACTORY_CONTRACT_ADDRESS> \
--ledgers-to-extend 6220800 \
--network <NETWORK> \
--source-account <OPERATOR_KEY>Persistent keys need --durability persistent and --key … (or batched tooling). Ticket keys are independent of the instance TTL — bumping only the instance is not enough for long-running sales.
- DEPLOYMENT.md — deploy then operate
- RANDOMNESS.md — how draw keys are used
- ARCHITECTURE.md — lifecycle states
- COMMIT_REVEAL.md —
CommitEntryprotocol