From e0087963cc654b28a16662a10c0f76e6570c0b84 Mon Sep 17 00:00:00 2001 From: Copilot Date: Thu, 30 Jul 2026 10:15:18 +0100 Subject: [PATCH 1/3] fix: add extend_ttl to persistent storage operations across all contracts --- contracts/event/src/storage.rs | 70 ++++++++++++++++++++----- contracts/factory/src/storage.rs | 75 ++++++++++++++++++++------- contracts/payments/src/storage.rs | 85 ++++++++++++++++++++++++------- contracts/ticket/src/lib.rs | 15 ++++-- contracts/ticket/src/storage.rs | 69 ++++++++++++++++++++----- 5 files changed, 247 insertions(+), 67 deletions(-) diff --git a/contracts/event/src/storage.rs b/contracts/event/src/storage.rs index 601dd30..b700e62 100644 --- a/contracts/event/src/storage.rs +++ b/contracts/event/src/storage.rs @@ -44,10 +44,15 @@ pub fn event_exists(env: &Env, event_id: &Symbol) -> bool { .has(&DataKey::Event(event_id.clone())) } pub fn get_event(env: &Env, event_id: &Symbol) -> Result { + let key = DataKey::Event(event_id.clone()); + let event = env.storage() + .persistent() + .get(&key) + .ok_or(EventError::EventNotFound)?; env.storage() .persistent() - .get(&DataKey::Event(event_id.clone())) - .ok_or(EventError::EventNotFound) + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + Ok(event) } pub fn save_event(env: &Env, event_id: &Symbol, event: &Event) { let key = DataKey::Event(event_id.clone()); @@ -145,36 +150,57 @@ pub fn set_admin(env: &Env, admin: &Address) { } pub fn get_admin(env: &Env) -> Result { + let key = DataKey::Admin; + let admin = env.storage() + .persistent() + .get(&key) + .ok_or(EventError::ContractLinksNotConfigured)?; env.storage() .persistent() - .get(&DataKey::Admin) - .ok_or(EventError::ContractLinksNotConfigured) + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + Ok(admin) } pub fn set_ticket_contract(env: &Env, ticket_contract: &Address) { env.storage() .persistent() .set(&DataKey::TicketContract, ticket_contract); + env.storage() + .persistent() + .extend_ttl(&DataKey::TicketContract, TTL_THRESHOLD, TTL_BUMP); } pub fn set_payments_contract(env: &Env, payments_contract: &Address) { env.storage() .persistent() .set(&DataKey::PaymentsContract, payments_contract); + env.storage() + .persistent() + .extend_ttl(&DataKey::PaymentsContract, TTL_THRESHOLD, TTL_BUMP); } pub fn get_ticket_contract(env: &Env) -> Result { + let key = DataKey::TicketContract; + let address = env.storage() + .persistent() + .get(&key) + .ok_or(EventError::ContractLinksNotConfigured)?; env.storage() .persistent() - .get(&DataKey::TicketContract) - .ok_or(EventError::ContractLinksNotConfigured) + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + Ok(address) } pub fn get_payments_contract(env: &Env) -> Result { + let key = DataKey::PaymentsContract; + let address = env.storage() + .persistent() + .get(&key) + .ok_or(EventError::ContractLinksNotConfigured)?; env.storage() .persistent() - .get(&DataKey::PaymentsContract) - .ok_or(EventError::ContractLinksNotConfigured) + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + Ok(address) } pub fn has_linked_contracts(env: &Env) -> bool { @@ -201,10 +227,14 @@ pub fn get_reservation( attendee: &Address, ) -> Result { let key = DataKey::Reservation(event_id.clone(), attendee.clone()); - env.storage() + let reservation = env.storage() .persistent() .get(&key) - .ok_or(EventError::ReservationNotFound) + .ok_or(EventError::ReservationNotFound)?; + env.storage() + .persistent() + .extend_ttl(&key, 60 * 60, 60 * 60 * 2); + Ok(reservation) } pub fn remove_reservation(env: &Env, event_id: &Symbol, attendee: &Address) { @@ -242,10 +272,15 @@ pub fn set_event_privacy(env: &Env, event_id: &Symbol, level: &PrivacyLevel) { } pub fn get_event_privacy(env: &Env, event_id: &Symbol) -> PrivacyLevel { + let key = DataKey::EventPrivacy(event_id.clone()); + let privacy = env.storage() + .persistent() + .get(&key) + .unwrap_or(PrivacyLevel::Standard); env.storage() .persistent() - .get(&DataKey::EventPrivacy(event_id.clone())) - .unwrap_or(PrivacyLevel::Standard) + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + privacy } pub fn has_reservation(env: &Env, event_id: &Symbol, attendee: &Address) -> bool { @@ -260,9 +295,16 @@ pub fn set_postponement(env: &Env, event_id: &Symbol, info: &PostponementInfo) { .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); } pub fn get_postponement(env: &Env, event_id: &Symbol) -> Option { - env.storage() + let key = DataKey::Postponement(event_id.clone()); + let info = env.storage() .persistent() - .get(&DataKey::Postponement(event_id.clone())) + .get(&key); + if info.is_some() { + env.storage() + .persistent() + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + } + info } pub fn remove_postponement(env: &Env, event_id: &Symbol) { env.storage() diff --git a/contracts/factory/src/storage.rs b/contracts/factory/src/storage.rs index d3c6f96..4d679ad 100644 --- a/contracts/factory/src/storage.rs +++ b/contracts/factory/src/storage.rs @@ -27,10 +27,15 @@ pub fn is_initialized(env: &Env) -> bool { } pub fn get_admin(env: &Env) -> Result { + let key = DataKey::Admin; + let admin = env.storage() + .persistent() + .get(&key) + .ok_or(FactoryError::NotInitialized)?; env.storage() .persistent() - .get(&DataKey::Admin) - .ok_or(FactoryError::NotInitialized) + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + Ok(admin) } pub fn set_admin(env: &Env, admin: &Address) { @@ -41,10 +46,15 @@ pub fn set_admin(env: &Env, admin: &Address) { } pub fn get_event_wasm_hash(env: &Env) -> Result, FactoryError> { + let key = DataKey::EventWasm; + let hash = env.storage() + .persistent() + .get(&key) + .ok_or(FactoryError::NotInitialized)?; env.storage() .persistent() - .get(&DataKey::EventWasm) - .ok_or(FactoryError::NotInitialized) + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + Ok(hash) } pub fn set_event_wasm_hash(env: &Env, hash: &BytesN<32>) { @@ -55,10 +65,15 @@ pub fn set_event_wasm_hash(env: &Env, hash: &BytesN<32>) { } pub fn get_ticket_contract(env: &Env) -> Result { + let key = DataKey::TicketContract; + let address = env.storage() + .persistent() + .get(&key) + .ok_or(FactoryError::NotInitialized)?; env.storage() .persistent() - .get(&DataKey::TicketContract) - .ok_or(FactoryError::NotInitialized) + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + Ok(address) } pub fn set_ticket_contract(env: &Env, address: &Address) { @@ -71,10 +86,15 @@ pub fn set_ticket_contract(env: &Env, address: &Address) { } pub fn get_payments_contract(env: &Env) -> Result { + let key = DataKey::PaymentsContract; + let address = env.storage() + .persistent() + .get(&key) + .ok_or(FactoryError::NotInitialized)?; env.storage() .persistent() - .get(&DataKey::PaymentsContract) - .ok_or(FactoryError::NotInitialized) + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + Ok(address) } pub fn set_payments_contract(env: &Env, address: &Address) { @@ -120,24 +140,43 @@ pub fn save_deployed_event(env: &Env, event: &DeployedEvent) -> Result<(), Facto } pub fn get_deployed_event(env: &Env, event_id: &Symbol) -> Result { + let key = DataKey::DeployedEvent(event_id.clone()); + let event = env.storage() + .persistent() + .get(&key) + .ok_or(FactoryError::EventNotFoundInRegistry)?; env.storage() .persistent() - .get(&DataKey::DeployedEvent(event_id.clone())) - .ok_or(FactoryError::EventNotFoundInRegistry) + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + Ok(event) } pub fn get_all_event_ids(env: &Env) -> Vec { - env.storage() - .persistent() - .get(&DataKey::AllEvents) - .unwrap_or_else(|| Vec::new(env)) + let key = DataKey::AllEvents; + let events = env.storage() + .persistent() + .get(&key) + .unwrap_or_else(|| Vec::new(env)); + if !events.is_empty() { + env.storage() + .persistent() + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + } + events } pub fn get_organizer_events(env: &Env, organizer: &Address) -> Vec { - env.storage() - .persistent() - .get(&DataKey::OrganizerEvents(organizer.clone())) - .unwrap_or_else(|| Vec::new(env)) + let key = DataKey::OrganizerEvents(organizer.clone()); + let events = env.storage() + .persistent() + .get(&key) + .unwrap_or_else(|| Vec::new(env)); + if !events.is_empty() { + env.storage() + .persistent() + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + } + events } pub fn get_contract_version(env: &Env) -> u32 { env.storage() diff --git a/contracts/payments/src/storage.rs b/contracts/payments/src/storage.rs index bdd1516..6cbc27e 100644 --- a/contracts/payments/src/storage.rs +++ b/contracts/payments/src/storage.rs @@ -123,9 +123,16 @@ pub fn set_event_status(env: &Env, event_id: &Symbol, status: &EventStatus) { } pub fn get_event_status(env: &Env, event_id: &Symbol) -> Option { - env.storage() + let key = DataKey::EventStatus(event_id.clone()); + let status = env.storage() .persistent() - .get(&DataKey::EventStatus(event_id.clone())) + .get(&key); + if status.is_some() { + env.storage() + .persistent() + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + } + status } pub fn set_postpone_deadline(env: &Env, event_id: &Symbol, deadline_ledger: u32) { let key = DataKey::PostponeDeadline(event_id.clone()); @@ -148,10 +155,15 @@ pub fn remove_postpone_deadline(env: &Env, event_id: &Symbol) { .remove(&DataKey::PostponeDeadline(event_id.clone())); } pub fn get_admin(env: &Env) -> Result { + let key = DataKey::Admin; + let admin = env.storage() + .persistent() + .get(&key) + .ok_or(PaymentError::NotInitialized)?; env.storage() .persistent() - .get(&DataKey::Admin) - .ok_or(PaymentError::NotInitialized) + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + Ok(admin) } pub fn set_admin(env: &Env, admin: &soroban_sdk::Address) { @@ -177,10 +189,15 @@ pub fn set_paused(env: &Env, paused: bool) { .extend_ttl(&DataKey::Paused, TTL_THRESHOLD, TTL_BUMP); } pub fn get_accepted_token(env: &Env) -> Result { + let key = DataKey::AcceptedToken; + let token = env.storage() + .persistent() + .get(&key) + .ok_or(PaymentError::NotInitialized)?; env.storage() .persistent() - .get(&DataKey::AcceptedToken) - .ok_or(PaymentError::NotInitialized) + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + Ok(token) } pub fn set_accepted_token(env: &Env, token: &soroban_sdk::Address) { env.storage() @@ -194,10 +211,15 @@ pub fn set_accepted_token(env: &Env, token: &soroban_sdk::Address) { } pub fn get_event_contract(env: &Env) -> Result { + let key = DataKey::EventContract; + let contract = env.storage() + .persistent() + .get(&key) + .ok_or(PaymentError::NotInitialized)?; env.storage() .persistent() - .get(&DataKey::EventContract) - .ok_or(PaymentError::NotInitialized) + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + Ok(contract) } pub fn set_event_contract(env: &Env, event_contract: &soroban_sdk::Address) { @@ -212,10 +234,15 @@ pub fn set_event_contract(env: &Env, event_contract: &soroban_sdk::Address) { } pub fn get_ticket_contract(env: &Env) -> Result { + let key = DataKey::TicketContract; + let contract = env.storage() + .persistent() + .get(&key) + .ok_or(PaymentError::NotInitialized)?; env.storage() .persistent() - .get(&DataKey::TicketContract) - .ok_or(PaymentError::NotInitialized) + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + Ok(contract) } pub fn set_ticket_contract(env: &Env, ticket_contract: &soroban_sdk::Address) { @@ -248,9 +275,16 @@ pub fn set_event_privacy(env: &Env, event_id: &Symbol, privacy: &EventPrivacyCon } pub fn get_event_config(env: &Env, event_id: &Symbol) -> Option { - env.storage() + let key = DataKey::EventConfig(event_id.clone()); + let config = env.storage() .persistent() - .get(&DataKey::EventConfig(event_id.clone())) + .get(&key); + if config.is_some() { + env.storage() + .persistent() + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + } + config } pub fn set_event_config(env: &Env, event_id: &Symbol, config: &EventConfig) { @@ -323,10 +357,15 @@ pub fn save_payment(env: &Env, payment: &PaymentRecord) -> Result<(), PaymentErr Ok(()) } pub fn get_payment(env: &Env, payment_id: u64) -> Result { + let key = DataKey::Payment(payment_id); + let payment = env.storage() + .persistent() + .get(&key) + .ok_or(PaymentError::PaymentNotFound)?; env.storage() .persistent() - .get(&DataKey::Payment(payment_id)) - .ok_or(PaymentError::PaymentNotFound) + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + Ok(payment) } pub fn save_ticket(env: &Env, ticket: &Ticket) -> Result<(), PaymentError> { let key = DataKey::Ticket(ticket.ticket_id); @@ -340,10 +379,15 @@ pub fn save_ticket(env: &Env, ticket: &Ticket) -> Result<(), PaymentError> { Ok(()) } pub fn get_ticket(env: &Env, ticket_id: u64) -> Result { + let key = DataKey::Ticket(ticket_id); + let ticket = env.storage() + .persistent() + .get(&key) + .ok_or(PaymentError::TicketNotFound)?; env.storage() .persistent() - .get(&DataKey::Ticket(ticket_id)) - .ok_or(PaymentError::TicketNotFound) + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + Ok(ticket) } /// Map-based: Add an owner-ticket relationship pub fn add_owner_ticket_map(env: &Env, owner: &Address, ticket_id: u64) { @@ -676,10 +720,15 @@ pub fn set_platform_fee_bps(env: &Env, bps: u32) { ); } pub fn get_platform_wallet(env: &Env) -> Result { + let key = DataKey::PlatformWallet; + let wallet = env.storage() + .persistent() + .get(&key) + .ok_or(PaymentError::NotInitialized)?; env.storage() .persistent() - .get(&DataKey::PlatformWallet) - .ok_or(PaymentError::NotInitialized) + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + Ok(wallet) } pub fn set_platform_wallet(env: &Env, wallet: &Address) { env.storage() diff --git a/contracts/ticket/src/lib.rs b/contracts/ticket/src/lib.rs index db1615f..11e33da 100644 --- a/contracts/ticket/src/lib.rs +++ b/contracts/ticket/src/lib.rs @@ -374,14 +374,23 @@ impl TicketContract { } fn read_next_ticket_id(env: &Env) -> u64 { + let key = DataKey::NextTicketId; + let id: u64 = env.storage() + .persistent() + .get(&key) + .unwrap_or(1); env.storage() .persistent() - .get(&DataKey::NextTicketId) - .unwrap_or(1) + .extend_ttl(&key, storage::TTL_THRESHOLD, storage::TTL_BUMP); + id } fn write_next_ticket_id(env: &Env, next_id: u64) { + let key = DataKey::NextTicketId; + env.storage() + .persistent() + .set(&key, &next_id); env.storage() .persistent() - .set(&DataKey::NextTicketId, &next_id); + .extend_ttl(&key, storage::TTL_THRESHOLD, storage::TTL_BUMP); } diff --git a/contracts/ticket/src/storage.rs b/contracts/ticket/src/storage.rs index f7f2523..3693c29 100644 --- a/contracts/ticket/src/storage.rs +++ b/contracts/ticket/src/storage.rs @@ -3,8 +3,8 @@ use soroban_sdk::{contracttype, Address, BytesN, Env, Symbol, Vec}; use crate::errors::TicketError; use crate::types::Ticket; -const TTL_THRESHOLD: u32 = 60 * 60 * 24 * 30; -const TTL_BUMP: u32 = 60 * 60 * 24 * 30 * 2; +pub const TTL_THRESHOLD: u32 = 60 * 60 * 24 * 30; +pub const TTL_BUMP: u32 = 60 * 60 * 24 * 30 * 2; #[allow(dead_code)] const CURRENT_VERSION: u32 = 1; @@ -31,16 +31,25 @@ pub enum DataKey { } pub fn get_ticket(env: &Env, ticket_id: u64) -> Result { + let key = DataKey::Ticket(ticket_id); + let ticket = env.storage() + .persistent() + .get(&key) + .ok_or(TicketError::TicketNotFound)?; env.storage() .persistent() - .get(&DataKey::Ticket(ticket_id)) - .ok_or(TicketError::TicketNotFound) + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + Ok(ticket) } pub fn update_ticket(env: &Env, ticket: &Ticket) { + let key = DataKey::Ticket(ticket.ticket_id); + env.storage() + .persistent() + .set(&key, ticket); env.storage() .persistent() - .set(&DataKey::Ticket(ticket.ticket_id), ticket); + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); } /// Add an owner-ticket relationship (map-based) @@ -188,6 +197,9 @@ pub fn get_tickets_by_owner(env: &Env, owner: Address) -> Vec { for i in 0..count { let idx_key = DataKey::OwnerTicketIndex(owner.clone(), i); if let Some(ticket_id) = env.storage().persistent().get(&idx_key) { + env.storage() + .persistent() + .extend_ttl(&idx_key, TTL_THRESHOLD, TTL_BUMP); tickets.push_back(ticket_id); } } @@ -203,6 +215,9 @@ pub fn get_tickets_by_event(env: &Env, event_id: Symbol) -> Vec { for i in 0..count { let idx_key = DataKey::EventTicketIndex(event_id.clone(), i); if let Some(ticket_id) = env.storage().persistent().get(&idx_key) { + env.storage() + .persistent() + .extend_ttl(&idx_key, TTL_THRESHOLD, TTL_BUMP); tickets.push_back(ticket_id); } } @@ -233,15 +248,26 @@ pub fn verify_version(env: &Env) -> Result<(), TicketError> { } pub fn get_recovery_key(env: &Env, ticket_id: u64) -> Option> { - env.storage() + let key = DataKey::RecoveryKey(ticket_id); + let value = env.storage() .persistent() - .get(&DataKey::RecoveryKey(ticket_id)) + .get(&key); + if value.is_some() { + env.storage() + .persistent() + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + } + value } pub fn set_recovery_key(env: &Env, ticket_id: u64, public_key: &BytesN<32>) { + let key = DataKey::RecoveryKey(ticket_id); + env.storage() + .persistent() + .set(&key, public_key); env.storage() .persistent() - .set(&DataKey::RecoveryKey(ticket_id), public_key); + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); } pub fn remove_recovery_key(env: &Env, ticket_id: u64) { @@ -251,10 +277,15 @@ pub fn remove_recovery_key(env: &Env, ticket_id: u64) { } pub fn get_payments_contract(env: &Env) -> Result { + let key = DataKey::PaymentsContract; + let address = env.storage() + .persistent() + .get(&key) + .ok_or(TicketError::Unauthorized)?; env.storage() .persistent() - .get(&DataKey::PaymentsContract) - .ok_or(TicketError::Unauthorized) // or specific error if available + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + Ok(address) } pub fn set_payments_contract(env: &Env, payments_contract: &Address) { @@ -267,10 +298,15 @@ pub fn set_payments_contract(env: &Env, payments_contract: &Address) { } pub fn get_event_contract(env: &Env) -> Result { + let key = DataKey::EventContract; + let address = env.storage() + .persistent() + .get(&key) + .ok_or(TicketError::Unauthorized)?; env.storage() .persistent() - .get(&DataKey::EventContract) - .ok_or(TicketError::Unauthorized) + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + Ok(address) } pub fn set_event_contract(env: &Env, event_contract: &Address) { @@ -283,10 +319,15 @@ pub fn set_event_contract(env: &Env, event_contract: &Address) { } pub fn get_admin(env: &Env) -> Result { + let key = DataKey::Admin; + let address = env.storage() + .persistent() + .get(&key) + .ok_or(TicketError::Unauthorized)?; env.storage() .persistent() - .get(&DataKey::Admin) - .ok_or(TicketError::Unauthorized) + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + Ok(address) } pub fn set_admin(env: &Env, admin: &Address) { From 7cb91e5e5cbf7ebd0d389fa1d757967991aaee99 Mon Sep 17 00:00:00 2001 From: Abdulsalam Muhammed Date: Tue, 4 Aug 2026 08:34:35 +0000 Subject: [PATCH 2/3] fix: use ledger-based TTL constants and harden index count handling - Replace seconds-style TTL constants with ledger counts (518,400 threshold / 1,036,800 bump) that stay within the 3,110,400-ledger network maximum; previously TTL_BUMP (5,184,000) exceeded the max and would panic on mainnet. - Route all inline extend_ttl literals through the shared constants in event, factory, payments, and ticket storage. - get_event_privacy / read_next_ticket_id only extend TTL when the key exists (extend_ttl on a missing key panics with Error(Storage, MissingValue)). - get_event_tickets_count / get_owner_tickets_count read Option and refresh TTL only when a count exists. - remove_owner_ticket defensively cleans up its index slot when the count entry is missing so later adds cannot collide with stale entries. --- contracts/event/src/storage.rs | 63 +++++++++++-------- contracts/factory/src/storage.rs | 28 ++++++--- contracts/payments/src/storage.rs | 100 ++++++++++++++---------------- contracts/ticket/src/lib.rs | 21 +++---- contracts/ticket/src/storage.rs | 53 +++++++++++----- 5 files changed, 146 insertions(+), 119 deletions(-) diff --git a/contracts/event/src/storage.rs b/contracts/event/src/storage.rs index b700e62..b2309e1 100644 --- a/contracts/event/src/storage.rs +++ b/contracts/event/src/storage.rs @@ -6,8 +6,15 @@ use crate::types::{ use soroban_sdk::{contracttype, Address, BytesN, Env, Symbol, Vec}; const CURRENT_VERSION: u32 = 1; -const TTL_THRESHOLD: u32 = 60 * 60 * 24 * 30; -const TTL_BUMP: u32 = 60 * 60 * 24 * 30 * 2; +/// TTL refresh threshold in ledgers (~30 days at 5s/ledger). +const TTL_THRESHOLD: u32 = 518_400; +/// TTL extension target in ledgers (~60 days at 5s/ledger), well within the +/// network maximum of 3,110,400 ledgers. +const TTL_BUMP: u32 = 1_036_800; +/// Reservations expire after 15 minutes; keep their entries on a ~1h/2h +/// ledger-based schedule so they outlive the reservation window. +const RESERVATION_TTL_THRESHOLD: u32 = 720; +const RESERVATION_TTL_BUMP: u32 = 1_440; #[contracttype] pub enum DataKey { @@ -45,7 +52,8 @@ pub fn event_exists(env: &Env, event_id: &Symbol) -> bool { } pub fn get_event(env: &Env, event_id: &Symbol) -> Result { let key = DataKey::Event(event_id.clone()); - let event = env.storage() + let event = env + .storage() .persistent() .get(&key) .ok_or(EventError::EventNotFound)?; @@ -59,7 +67,7 @@ pub fn save_event(env: &Env, event_id: &Symbol, event: &Event) { env.storage().persistent().set(&key, event); env.storage() .persistent() - .extend_ttl(&key, 60 * 60 * 24 * 30, 60 * 60 * 24 * 30 * 2); + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); } pub fn update_event(env: &Env, event_id: &Symbol, event: &Event) -> Result<(), EventError> { if !event_exists(env, event_id) { @@ -142,16 +150,15 @@ pub fn get_attendees_paginated( pub fn set_admin(env: &Env, admin: &Address) { env.storage().persistent().set(&DataKey::Admin, admin); - env.storage().persistent().extend_ttl( - &DataKey::Admin, - 60 * 60 * 24 * 30, - 60 * 60 * 24 * 30 * 2, - ); + env.storage() + .persistent() + .extend_ttl(&DataKey::Admin, TTL_THRESHOLD, TTL_BUMP); } pub fn get_admin(env: &Env) -> Result { let key = DataKey::Admin; - let admin = env.storage() + let admin = env + .storage() .persistent() .get(&key) .ok_or(EventError::ContractLinksNotConfigured)?; @@ -181,7 +188,8 @@ pub fn set_payments_contract(env: &Env, payments_contract: &Address) { pub fn get_ticket_contract(env: &Env) -> Result { let key = DataKey::TicketContract; - let address = env.storage() + let address = env + .storage() .persistent() .get(&key) .ok_or(EventError::ContractLinksNotConfigured)?; @@ -193,7 +201,8 @@ pub fn get_ticket_contract(env: &Env) -> Result { pub fn get_payments_contract(env: &Env) -> Result { let key = DataKey::PaymentsContract; - let address = env.storage() + let address = env + .storage() .persistent() .get(&key) .ok_or(EventError::ContractLinksNotConfigured)?; @@ -218,7 +227,7 @@ pub fn save_reservation( env.storage().persistent().set(&key, reservation); env.storage() .persistent() - .extend_ttl(&key, 60 * 60, 60 * 60 * 2); + .extend_ttl(&key, RESERVATION_TTL_THRESHOLD, RESERVATION_TTL_BUMP); } pub fn get_reservation( @@ -227,13 +236,14 @@ pub fn get_reservation( attendee: &Address, ) -> Result { let key = DataKey::Reservation(event_id.clone(), attendee.clone()); - let reservation = env.storage() + let reservation = env + .storage() .persistent() .get(&key) .ok_or(EventError::ReservationNotFound)?; env.storage() .persistent() - .extend_ttl(&key, 60 * 60, 60 * 60 * 2); + .extend_ttl(&key, RESERVATION_TTL_THRESHOLD, RESERVATION_TTL_BUMP); Ok(reservation) } @@ -268,19 +278,20 @@ pub fn set_event_privacy(env: &Env, event_id: &Symbol, level: &PrivacyLevel) { env.storage().persistent().set(&key, level); env.storage() .persistent() - .extend_ttl(&key, 60 * 60 * 24 * 30, 60 * 60 * 24 * 30 * 2); + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); } pub fn get_event_privacy(env: &Env, event_id: &Symbol) -> PrivacyLevel { let key = DataKey::EventPrivacy(event_id.clone()); - let privacy = env.storage() - .persistent() - .get(&key) - .unwrap_or(PrivacyLevel::Standard); - env.storage() - .persistent() - .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); - privacy + let privacy: Option = env.storage().persistent().get(&key); + // Only extend the TTL when the key already exists: extend_ttl on a missing + // key panics with Error(Storage, MissingValue). + if privacy.is_some() { + env.storage() + .persistent() + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + } + privacy.unwrap_or(PrivacyLevel::Standard) } pub fn has_reservation(env: &Env, event_id: &Symbol, attendee: &Address) -> bool { @@ -296,9 +307,7 @@ pub fn set_postponement(env: &Env, event_id: &Symbol, info: &PostponementInfo) { } pub fn get_postponement(env: &Env, event_id: &Symbol) -> Option { let key = DataKey::Postponement(event_id.clone()); - let info = env.storage() - .persistent() - .get(&key); + let info = env.storage().persistent().get(&key); if info.is_some() { env.storage() .persistent() diff --git a/contracts/factory/src/storage.rs b/contracts/factory/src/storage.rs index 4d679ad..580386f 100644 --- a/contracts/factory/src/storage.rs +++ b/contracts/factory/src/storage.rs @@ -2,8 +2,11 @@ use crate::errors::FactoryError; use crate::types::DeployedEvent; use soroban_sdk::{contracttype, Address, BytesN, Env, Symbol, Vec}; -const TTL_THRESHOLD: u32 = 60 * 60 * 24 * 30; -const TTL_BUMP: u32 = 60 * 60 * 24 * 30 * 2; +/// TTL refresh threshold in ledgers (~30 days at 5s/ledger). +const TTL_THRESHOLD: u32 = 518_400; +/// TTL extension target in ledgers (~60 days at 5s/ledger), well within the +/// network maximum of 3,110,400 ledgers. +const TTL_BUMP: u32 = 1_036_800; const CURRENT_VERSION: u32 = 1; #[contracttype] @@ -28,7 +31,8 @@ pub fn is_initialized(env: &Env) -> bool { pub fn get_admin(env: &Env) -> Result { let key = DataKey::Admin; - let admin = env.storage() + let admin = env + .storage() .persistent() .get(&key) .ok_or(FactoryError::NotInitialized)?; @@ -47,7 +51,8 @@ pub fn set_admin(env: &Env, admin: &Address) { pub fn get_event_wasm_hash(env: &Env) -> Result, FactoryError> { let key = DataKey::EventWasm; - let hash = env.storage() + let hash = env + .storage() .persistent() .get(&key) .ok_or(FactoryError::NotInitialized)?; @@ -66,7 +71,8 @@ pub fn set_event_wasm_hash(env: &Env, hash: &BytesN<32>) { pub fn get_ticket_contract(env: &Env) -> Result { let key = DataKey::TicketContract; - let address = env.storage() + let address = env + .storage() .persistent() .get(&key) .ok_or(FactoryError::NotInitialized)?; @@ -87,7 +93,8 @@ pub fn set_ticket_contract(env: &Env, address: &Address) { pub fn get_payments_contract(env: &Env) -> Result { let key = DataKey::PaymentsContract; - let address = env.storage() + let address = env + .storage() .persistent() .get(&key) .ok_or(FactoryError::NotInitialized)?; @@ -141,7 +148,8 @@ pub fn save_deployed_event(env: &Env, event: &DeployedEvent) -> Result<(), Facto pub fn get_deployed_event(env: &Env, event_id: &Symbol) -> Result { let key = DataKey::DeployedEvent(event_id.clone()); - let event = env.storage() + let event = env + .storage() .persistent() .get(&key) .ok_or(FactoryError::EventNotFoundInRegistry)?; @@ -153,7 +161,8 @@ pub fn get_deployed_event(env: &Env, event_id: &Symbol) -> Result Vec { let key = DataKey::AllEvents; - let events = env.storage() + let events = env + .storage() .persistent() .get(&key) .unwrap_or_else(|| Vec::new(env)); @@ -167,7 +176,8 @@ pub fn get_all_event_ids(env: &Env) -> Vec { pub fn get_organizer_events(env: &Env, organizer: &Address) -> Vec { let key = DataKey::OrganizerEvents(organizer.clone()); - let events = env.storage() + let events = env + .storage() .persistent() .get(&key) .unwrap_or_else(|| Vec::new(env)); diff --git a/contracts/payments/src/storage.rs b/contracts/payments/src/storage.rs index 6cbc27e..938988e 100644 --- a/contracts/payments/src/storage.rs +++ b/contracts/payments/src/storage.rs @@ -4,8 +4,11 @@ use crate::types::{ }; use soroban_sdk::{contracttype, Address, BytesN, Env, Symbol, Vec}; -const TTL_THRESHOLD: u32 = 60 * 60 * 24 * 30; -const TTL_BUMP: u32 = 60 * 60 * 24 * 30 * 2; +/// TTL refresh threshold in ledgers (~30 days at 5s/ledger). +const TTL_THRESHOLD: u32 = 518_400; +/// TTL extension target in ledgers (~60 days at 5s/ledger), well within the +/// network maximum of 3,110,400 ledgers. +const TTL_BUMP: u32 = 1_036_800; const CURRENT_VERSION: u32 = 1; #[contracttype] @@ -124,9 +127,7 @@ pub fn set_event_status(env: &Env, event_id: &Symbol, status: &EventStatus) { pub fn get_event_status(env: &Env, event_id: &Symbol) -> Option { let key = DataKey::EventStatus(event_id.clone()); - let status = env.storage() - .persistent() - .get(&key); + let status = env.storage().persistent().get(&key); if status.is_some() { env.storage() .persistent() @@ -156,7 +157,8 @@ pub fn remove_postpone_deadline(env: &Env, event_id: &Symbol) { } pub fn get_admin(env: &Env) -> Result { let key = DataKey::Admin; - let admin = env.storage() + let admin = env + .storage() .persistent() .get(&key) .ok_or(PaymentError::NotInitialized)?; @@ -168,11 +170,9 @@ pub fn get_admin(env: &Env) -> Result { pub fn set_admin(env: &Env, admin: &soroban_sdk::Address) { env.storage().persistent().set(&DataKey::Admin, admin); - env.storage().persistent().extend_ttl( - &DataKey::Admin, - 60 * 60 * 24 * 30, - 60 * 60 * 24 * 30 * 2, - ); + env.storage() + .persistent() + .extend_ttl(&DataKey::Admin, TTL_THRESHOLD, TTL_BUMP); } pub fn is_paused(env: &Env) -> bool { @@ -190,7 +190,8 @@ pub fn set_paused(env: &Env, paused: bool) { } pub fn get_accepted_token(env: &Env) -> Result { let key = DataKey::AcceptedToken; - let token = env.storage() + let token = env + .storage() .persistent() .get(&key) .ok_or(PaymentError::NotInitialized)?; @@ -203,16 +204,15 @@ pub fn set_accepted_token(env: &Env, token: &soroban_sdk::Address) { env.storage() .persistent() .set(&DataKey::AcceptedToken, token); - env.storage().persistent().extend_ttl( - &DataKey::AcceptedToken, - 60 * 60 * 24 * 30, - 60 * 60 * 24 * 30 * 2, - ); + env.storage() + .persistent() + .extend_ttl(&DataKey::AcceptedToken, TTL_THRESHOLD, TTL_BUMP); } pub fn get_event_contract(env: &Env) -> Result { let key = DataKey::EventContract; - let contract = env.storage() + let contract = env + .storage() .persistent() .get(&key) .ok_or(PaymentError::NotInitialized)?; @@ -226,16 +226,15 @@ pub fn set_event_contract(env: &Env, event_contract: &soroban_sdk::Address) { env.storage() .persistent() .set(&DataKey::EventContract, event_contract); - env.storage().persistent().extend_ttl( - &DataKey::EventContract, - 60 * 60 * 24 * 30, - 60 * 60 * 24 * 30 * 2, - ); + env.storage() + .persistent() + .extend_ttl(&DataKey::EventContract, TTL_THRESHOLD, TTL_BUMP); } pub fn get_ticket_contract(env: &Env) -> Result { let key = DataKey::TicketContract; - let contract = env.storage() + let contract = env + .storage() .persistent() .get(&key) .ok_or(PaymentError::NotInitialized)?; @@ -249,11 +248,9 @@ pub fn set_ticket_contract(env: &Env, ticket_contract: &soroban_sdk::Address) { env.storage() .persistent() .set(&DataKey::TicketContract, ticket_contract); - env.storage().persistent().extend_ttl( - &DataKey::TicketContract, - 60 * 60 * 24 * 30, - 60 * 60 * 24 * 30 * 2, - ); + env.storage() + .persistent() + .extend_ttl(&DataKey::TicketContract, TTL_THRESHOLD, TTL_BUMP); } pub fn get_event_privacy(env: &Env, event_id: &Symbol) -> EventPrivacyConfig { @@ -276,9 +273,7 @@ pub fn set_event_privacy(env: &Env, event_id: &Symbol, privacy: &EventPrivacyCon pub fn get_event_config(env: &Env, event_id: &Symbol) -> Option { let key = DataKey::EventConfig(event_id.clone()); - let config = env.storage() - .persistent() - .get(&key); + let config = env.storage().persistent().get(&key); if config.is_some() { env.storage() .persistent() @@ -321,11 +316,9 @@ pub fn get_next_payment_id(env: &Env) -> u64 { env.storage() .persistent() .set(&DataKey::NextPaymentId, &next_id); - env.storage().persistent().extend_ttl( - &DataKey::NextPaymentId, - 60 * 60 * 24 * 30, - 60 * 60 * 24 * 30 * 2, - ); + env.storage() + .persistent() + .extend_ttl(&DataKey::NextPaymentId, TTL_THRESHOLD, TTL_BUMP); next_id } pub fn get_next_ticket_id(env: &Env) -> u64 { @@ -338,11 +331,9 @@ pub fn get_next_ticket_id(env: &Env) -> u64 { env.storage() .persistent() .set(&DataKey::NextTicketId, &next_id); - env.storage().persistent().extend_ttl( - &DataKey::NextTicketId, - 60 * 60 * 24 * 30, - 60 * 60 * 24 * 30 * 2, - ); + env.storage() + .persistent() + .extend_ttl(&DataKey::NextTicketId, TTL_THRESHOLD, TTL_BUMP); next_id } pub fn save_payment(env: &Env, payment: &PaymentRecord) -> Result<(), PaymentError> { @@ -358,7 +349,8 @@ pub fn save_payment(env: &Env, payment: &PaymentRecord) -> Result<(), PaymentErr } pub fn get_payment(env: &Env, payment_id: u64) -> Result { let key = DataKey::Payment(payment_id); - let payment = env.storage() + let payment = env + .storage() .persistent() .get(&key) .ok_or(PaymentError::PaymentNotFound)?; @@ -380,7 +372,8 @@ pub fn save_ticket(env: &Env, ticket: &Ticket) -> Result<(), PaymentError> { } pub fn get_ticket(env: &Env, ticket_id: u64) -> Result { let key = DataKey::Ticket(ticket_id); - let ticket = env.storage() + let ticket = env + .storage() .persistent() .get(&key) .ok_or(PaymentError::TicketNotFound)?; @@ -713,15 +706,14 @@ pub fn set_platform_fee_bps(env: &Env, bps: u32) { env.storage() .persistent() .set(&DataKey::PlatformFeeBps, &bps); - env.storage().persistent().extend_ttl( - &DataKey::PlatformFeeBps, - 60 * 60 * 24 * 30, - 60 * 60 * 24 * 30 * 2, - ); + env.storage() + .persistent() + .extend_ttl(&DataKey::PlatformFeeBps, TTL_THRESHOLD, TTL_BUMP); } pub fn get_platform_wallet(env: &Env) -> Result { let key = DataKey::PlatformWallet; - let wallet = env.storage() + let wallet = env + .storage() .persistent() .get(&key) .ok_or(PaymentError::NotInitialized)?; @@ -734,11 +726,9 @@ pub fn set_platform_wallet(env: &Env, wallet: &Address) { env.storage() .persistent() .set(&DataKey::PlatformWallet, wallet); - env.storage().persistent().extend_ttl( - &DataKey::PlatformWallet, - 60 * 60 * 24 * 30, - 60 * 60 * 24 * 30 * 2, - ); + env.storage() + .persistent() + .extend_ttl(&DataKey::PlatformWallet, TTL_THRESHOLD, TTL_BUMP); } pub fn get_platform_revenue(env: &Env, event_id: &Symbol) -> i128 { env.storage() diff --git a/contracts/ticket/src/lib.rs b/contracts/ticket/src/lib.rs index 11e33da..38cdcf8 100644 --- a/contracts/ticket/src/lib.rs +++ b/contracts/ticket/src/lib.rs @@ -375,21 +375,20 @@ impl TicketContract { fn read_next_ticket_id(env: &Env) -> u64 { let key = DataKey::NextTicketId; - let id: u64 = env.storage() - .persistent() - .get(&key) - .unwrap_or(1); - env.storage() - .persistent() - .extend_ttl(&key, storage::TTL_THRESHOLD, storage::TTL_BUMP); - id + let id: Option = env.storage().persistent().get(&key); + // Only extend the TTL when the key already exists: extend_ttl on a missing + // key panics with Error(Storage, MissingValue). + if id.is_some() { + env.storage() + .persistent() + .extend_ttl(&key, storage::TTL_THRESHOLD, storage::TTL_BUMP); + } + id.unwrap_or(1) } fn write_next_ticket_id(env: &Env, next_id: u64) { let key = DataKey::NextTicketId; - env.storage() - .persistent() - .set(&key, &next_id); + env.storage().persistent().set(&key, &next_id); env.storage() .persistent() .extend_ttl(&key, storage::TTL_THRESHOLD, storage::TTL_BUMP); diff --git a/contracts/ticket/src/storage.rs b/contracts/ticket/src/storage.rs index 3693c29..66f9b26 100644 --- a/contracts/ticket/src/storage.rs +++ b/contracts/ticket/src/storage.rs @@ -3,8 +3,11 @@ use soroban_sdk::{contracttype, Address, BytesN, Env, Symbol, Vec}; use crate::errors::TicketError; use crate::types::Ticket; -pub const TTL_THRESHOLD: u32 = 60 * 60 * 24 * 30; -pub const TTL_BUMP: u32 = 60 * 60 * 24 * 30 * 2; +/// TTL refresh threshold in ledgers (~30 days at 5s/ledger). +pub const TTL_THRESHOLD: u32 = 518_400; +/// TTL extension target in ledgers (~60 days at 5s/ledger), well within the +/// network maximum of 3,110,400 ledgers. +pub const TTL_BUMP: u32 = 1_036_800; #[allow(dead_code)] const CURRENT_VERSION: u32 = 1; @@ -32,7 +35,8 @@ pub enum DataKey { pub fn get_ticket(env: &Env, ticket_id: u64) -> Result { let key = DataKey::Ticket(ticket_id); - let ticket = env.storage() + let ticket = env + .storage() .persistent() .get(&key) .ok_or(TicketError::TicketNotFound)?; @@ -44,9 +48,7 @@ pub fn get_ticket(env: &Env, ticket_id: u64) -> Result { pub fn update_ticket(env: &Env, ticket: &Ticket) { let key = DataKey::Ticket(ticket.ticket_id); - env.storage() - .persistent() - .set(&key, ticket); + env.storage().persistent().set(&key, ticket); env.storage() .persistent() .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); @@ -102,6 +104,12 @@ pub fn remove_owner_ticket(env: &Env, owner: &Address, ticket_id: u64) { if let Some(idx) = stored_index { let count = get_owner_tickets_count(env, owner); if count == 0 { + // The count entry is missing/archived, so there is no valid index + // bound to reconcile. Defensively remove this slot so a later add + // cannot collide with a stale entry. + env.storage() + .persistent() + .remove(&DataKey::OwnerTicketIndex(owner.clone(), idx)); return; } @@ -180,13 +188,25 @@ pub fn has_event_ticket(env: &Env, event_id: &Symbol, ticket_id: u64) -> bool { /// Get the count of tickets for an event pub fn get_event_tickets_count(env: &Env, event_id: &Symbol) -> u64 { let key = DataKey::EventTicketsCount(event_id.clone()); - env.storage().persistent().get(&key).unwrap_or(0) + let count: Option = env.storage().persistent().get(&key); + if count.is_some() { + env.storage() + .persistent() + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + } + count.unwrap_or(0) } /// Get the count of tickets owned by an address pub fn get_owner_tickets_count(env: &Env, owner: &Address) -> u64 { let key = DataKey::OwnerTicketsCount(owner.clone()); - env.storage().persistent().get(&key).unwrap_or(0) + let count: Option = env.storage().persistent().get(&key); + if count.is_some() { + env.storage() + .persistent() + .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); + } + count.unwrap_or(0) } /// Get all tickets by owner using indexed storage @@ -249,9 +269,7 @@ pub fn verify_version(env: &Env) -> Result<(), TicketError> { pub fn get_recovery_key(env: &Env, ticket_id: u64) -> Option> { let key = DataKey::RecoveryKey(ticket_id); - let value = env.storage() - .persistent() - .get(&key); + let value = env.storage().persistent().get(&key); if value.is_some() { env.storage() .persistent() @@ -262,9 +280,7 @@ pub fn get_recovery_key(env: &Env, ticket_id: u64) -> Option> { pub fn set_recovery_key(env: &Env, ticket_id: u64, public_key: &BytesN<32>) { let key = DataKey::RecoveryKey(ticket_id); - env.storage() - .persistent() - .set(&key, public_key); + env.storage().persistent().set(&key, public_key); env.storage() .persistent() .extend_ttl(&key, TTL_THRESHOLD, TTL_BUMP); @@ -278,7 +294,8 @@ pub fn remove_recovery_key(env: &Env, ticket_id: u64) { pub fn get_payments_contract(env: &Env) -> Result { let key = DataKey::PaymentsContract; - let address = env.storage() + let address = env + .storage() .persistent() .get(&key) .ok_or(TicketError::Unauthorized)?; @@ -299,7 +316,8 @@ pub fn set_payments_contract(env: &Env, payments_contract: &Address) { pub fn get_event_contract(env: &Env) -> Result { let key = DataKey::EventContract; - let address = env.storage() + let address = env + .storage() .persistent() .get(&key) .ok_or(TicketError::Unauthorized)?; @@ -320,7 +338,8 @@ pub fn set_event_contract(env: &Env, event_contract: &Address) { pub fn get_admin(env: &Env) -> Result { let key = DataKey::Admin; - let address = env.storage() + let address = env + .storage() .persistent() .get(&key) .ok_or(TicketError::Unauthorized)?; From c95763966fa07af10b7c6b47b25cf5f8a3dbc37b Mon Sep 17 00:00:00 2001 From: Abdulsalam Muhammed Date: Tue, 4 Aug 2026 09:01:56 +0000 Subject: [PATCH 3/3] fix: extend TTL on ticket writes and cap nullifier/nonce TTLs at protocol max - mint_ticket, transfer_ticket, use_ticket now extend the Ticket key TTL immediately after writing it, so a fresh ticket is not left at the default low TTL until its first read (matches the PR's write-path intent). - Route ProcessedNonce/ProcessedNonceHash entries through 7/14-day ledger constants (120,960/241,920) instead of seconds-style literals. - mark_nullifier_spent previously used 365-day seconds-style literals (31,536,000/63,072,000) that exceed the 3,110,400-ledger network maximum and would panic on mainnet; cap it at 60-day threshold / protocol-maximum bump. --- contracts/payments/src/storage.rs | 13 ++++++++++--- contracts/ticket/src/lib.rs | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/contracts/payments/src/storage.rs b/contracts/payments/src/storage.rs index 938988e..ac350cb 100644 --- a/contracts/payments/src/storage.rs +++ b/contracts/payments/src/storage.rs @@ -10,6 +10,13 @@ const TTL_THRESHOLD: u32 = 518_400; /// network maximum of 3,110,400 ledgers. const TTL_BUMP: u32 = 1_036_800; const CURRENT_VERSION: u32 = 1; +/// Processed-nonce replay-protection entries: ~7-day/14-day ledger schedule. +const NONCE_TTL_THRESHOLD: u32 = 120_960; // ~7 days at 5s/ledger +const NONCE_TTL_BUMP: u32 = 241_920; // ~14 days at 5s/ledger +/// Spent-nullifier replay-protection entries: extend to the protocol maximum +/// (3,110,400 ledgers, ~180 days) since a replayed nullifier must never pass. +const NULLIFIER_TTL_THRESHOLD: u32 = 1_036_800; // ~60 days +const NULLIFIER_TTL_BUMP: u32 = 3_110_400; // protocol maximum TTL #[contracttype] #[derive(Clone)] @@ -791,7 +798,7 @@ pub fn set_nonce(env: &Env, address: &Address, nonce: u64) { env.storage().persistent().set(&key, &true); env.storage() .persistent() - .extend_ttl(&key, 60 * 60 * 24 * 7, 60 * 60 * 24 * 14); + .extend_ttl(&key, NONCE_TTL_THRESHOLD, NONCE_TTL_BUMP); } pub fn has_nonce_hash(env: &Env, hash: &BytesN<32>, nonce: u64) -> bool { @@ -806,7 +813,7 @@ pub fn set_nonce_hash(env: &Env, hash: &BytesN<32>, nonce: u64) { env.storage().persistent().set(&key, &true); env.storage() .persistent() - .extend_ttl(&key, 60 * 60 * 24 * 7, 60 * 60 * 24 * 14); + .extend_ttl(&key, NONCE_TTL_THRESHOLD, NONCE_TTL_BUMP); } pub fn has_nullifier(env: &Env, commitment: &BytesN<32>) -> bool { @@ -821,7 +828,7 @@ pub fn mark_nullifier_spent(env: &Env, commitment: &BytesN<32>) { env.storage().persistent().set(&key, &true); env.storage() .persistent() - .extend_ttl(&key, 60 * 60 * 24 * 365, 60 * 60 * 24 * 365 * 2); + .extend_ttl(&key, NULLIFIER_TTL_THRESHOLD, NULLIFIER_TTL_BUMP); } /// Get the current contract version from storage. diff --git a/contracts/ticket/src/lib.rs b/contracts/ticket/src/lib.rs index 38cdcf8..478d09e 100644 --- a/contracts/ticket/src/lib.rs +++ b/contracts/ticket/src/lib.rs @@ -50,6 +50,11 @@ impl TicketContract { env.storage() .persistent() .set(&DataKey::Ticket(ticket_id), &ticket); + env.storage().persistent().extend_ttl( + &DataKey::Ticket(ticket_id), + storage::TTL_THRESHOLD, + storage::TTL_BUMP, + ); // Use map-based indexing instead of vector storage storage::add_owner_ticket(&env, &owner, ticket_id); @@ -106,6 +111,11 @@ impl TicketContract { env.storage() .persistent() .set(&DataKey::Ticket(ticket_id), &ticket); + env.storage().persistent().extend_ttl( + &DataKey::Ticket(ticket_id), + storage::TTL_THRESHOLD, + storage::TTL_BUMP, + ); // Use map-based indexing: remove from old owner, add to new owner storage::remove_owner_ticket(&env, &from, ticket_id); @@ -153,6 +163,11 @@ impl TicketContract { env.storage() .persistent() .set(&DataKey::Ticket(ticket_id), &ticket); + env.storage().persistent().extend_ttl( + &DataKey::Ticket(ticket_id), + storage::TTL_THRESHOLD, + storage::TTL_BUMP, + ); events::emit_ticket_used( &env, ticket_id,