Skip to content

Commit 9608cc4

Browse files
committed
Implemented Storage TTL Extension
1 parent 66069cf commit 9608cc4

10 files changed

Lines changed: 94 additions & 30 deletions

craft-nexus-contract/src/lib.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,12 +206,16 @@ impl EscrowContract {
206206
};
207207

208208
env.storage().persistent().set(&PLATFORM_FEE, &config);
209+
env.storage().persistent().extend_ttl(&PLATFORM_FEE, 1000, 518400);
209210
env.storage().persistent().set(&PLATFORM_WALLET, &platform_wallet);
211+
env.storage().persistent().extend_ttl(&PLATFORM_WALLET, 1000, 518400);
210212
env.storage().persistent().set(&ADMIN, &admin);
213+
env.storage().persistent().extend_ttl(&ADMIN, 1000, 518400);
211214

212215
// Initialize total fees to 0
213216
let zero: i128 = 0;
214217
env.storage().persistent().set(&TOTAL_FEES, &zero);
218+
env.storage().persistent().extend_ttl(&TOTAL_FEES, 1000, 518400);
215219
}
216220
/// Create a new escrow for an order
217221
///
@@ -288,18 +292,21 @@ impl EscrowContract {
288292
env.storage()
289293
.persistent()
290294
.set(&(ESCROW, order_id), &escrow);
295+
env.storage().persistent().extend_ttl(&(ESCROW, order_id), 1000, 518400);
291296

292297
// Update buyer's escrow list for indexing
293298
let buyer_key = DataKey::BuyerEscrows(buyer.clone());
294299
let mut buyer_escrows: soroban_sdk::Vec<u64> = env.storage().persistent().get(&buyer_key).unwrap_or(soroban_sdk::Vec::new(&env));
295300
buyer_escrows.push_back(order_id as u64);
296301
env.storage().persistent().set(&buyer_key, &buyer_escrows);
302+
env.storage().persistent().extend_ttl(&buyer_key, 1000, 518400);
297303

298304
// Update seller's escrow list for indexing
299305
let seller_key = DataKey::SellerEscrows(seller.clone());
300306
let mut seller_escrows: soroban_sdk::Vec<u64> = env.storage().persistent().get(&seller_key).unwrap_or(soroban_sdk::Vec::new(&env));
301307
seller_escrows.push_back(order_id as u64);
302308
env.storage().persistent().set(&seller_key, &seller_escrows);
309+
env.storage().persistent().extend_ttl(&seller_key, 1000, 518400);
303310

304311
// Transfer funds from buyer to contract
305312
let client = token::Client::new(&env, &token);
@@ -330,6 +337,9 @@ impl EscrowContract {
330337
let key = DataKey::BuyerEscrows(buyer);
331338
let escrow_ids: soroban_sdk::Vec<u64> = env.storage().persistent().get(&key)
332339
.unwrap_or(soroban_sdk::Vec::new(&env));
340+
if env.storage().persistent().has(&key) {
341+
env.storage().persistent().extend_ttl(&key, 1000, 518400);
342+
}
333343

334344
let start = page * limit;
335345
let len = escrow_ids.len();
@@ -352,6 +362,9 @@ impl EscrowContract {
352362
let key = DataKey::SellerEscrows(seller);
353363
let escrow_ids: soroban_sdk::Vec<u64> = env.storage().persistent().get(&key)
354364
.unwrap_or(soroban_sdk::Vec::new(&env));
365+
if env.storage().persistent().has(&key) {
366+
env.storage().persistent().extend_ttl(&key, 1000, 518400);
367+
}
355368

356369
let start = page * limit;
357370
let len = escrow_ids.len();
@@ -370,6 +383,7 @@ impl EscrowContract {
370383
.persistent()
371384
.get(&PLATFORM_FEE);
372385
if !(config.is_some()) { env.panic_with_error(Error::PlatformNotInitialized); }
386+
env.storage().persistent().extend_ttl(&PLATFORM_FEE, 1000, 518400);
373387
config.unwrap()
374388
}
375389

@@ -388,6 +402,7 @@ impl EscrowContract {
388402
.persistent()
389403
.get(&(ESCROW, order_id));
390404
if !(escrow_opt.is_some()) { env.panic_with_error(Error::EscrowNotFound); }
405+
env.storage().persistent().extend_ttl(&(ESCROW, order_id), 1000, 518400);
391406
let mut escrow: Escrow = escrow_opt.unwrap();
392407

393408
// Only buyer can release funds
@@ -449,6 +464,7 @@ impl EscrowContract {
449464
.persistent()
450465
.get(&(ESCROW, order_id));
451466
if !(escrow_opt.is_some()) { env.panic_with_error(Error::EscrowNotFound); }
467+
env.storage().persistent().extend_ttl(&(ESCROW, order_id), 1000, 518400);
452468
let mut escrow: Escrow = escrow_opt.unwrap();
453469

454470
if !(escrow.status == EscrowStatus::Pending) { env.panic_with_error(Error::InvalidEscrowState); }
@@ -488,6 +504,7 @@ impl EscrowContract {
488504
.unwrap_or(0);
489505
total_fees += fee_amount;
490506
env.storage().persistent().set(&TOTAL_FEES, &total_fees);
507+
env.storage().persistent().extend_ttl(&TOTAL_FEES, 1000, 518400);
491508
}
492509

493510
// Transfer remaining funds to seller
@@ -515,6 +532,7 @@ impl EscrowContract {
515532
.persistent()
516533
.get(&(ESCROW, order_id));
517534
if !(escrow_opt.is_some()) { env.panic_with_error(Error::EscrowNotFound); }
535+
env.storage().persistent().extend_ttl(&(ESCROW, order_id), 1000, 518400);
518536
let mut escrow: Escrow = escrow_opt.unwrap();
519537

520538
// Only buyer or platform can refund
@@ -553,6 +571,7 @@ impl EscrowContract {
553571
.persistent()
554572
.get(&(ESCROW, order_id));
555573
if !(escrow_opt.is_some()) { env.panic_with_error(Error::EscrowNotFound); }
574+
env.storage().persistent().extend_ttl(&(ESCROW, order_id), 1000, 518400);
556575
escrow_opt.unwrap()
557576
}
558577

@@ -575,6 +594,7 @@ impl EscrowContract {
575594
.persistent()
576595
.get(&(ESCROW, order_id));
577596
if !(escrow_opt.is_some()) { env.panic_with_error(Error::EscrowNotFound); }
597+
env.storage().persistent().extend_ttl(&(ESCROW, order_id), 1000, 518400);
578598
let escrow: Escrow = escrow_opt.unwrap();
579599

580600
if escrow.status != EscrowStatus::Pending {
@@ -600,6 +620,7 @@ impl EscrowContract {
600620
.persistent()
601621
.get(&(ESCROW, order_id));
602622
if !(escrow_opt.is_some()) { env.panic_with_error(Error::EscrowNotFound); }
623+
env.storage().persistent().extend_ttl(&(ESCROW, order_id), 1000, 518400);
603624
let mut escrow: Escrow = escrow_opt.unwrap();
604625

605626
let config = Self::get_platform_config(&env);
@@ -641,6 +662,7 @@ impl EscrowContract {
641662
};
642663

643664
env.storage().persistent().set(&PLATFORM_FEE, &new_config);
665+
env.storage().persistent().extend_ttl(&PLATFORM_FEE, 1000, 518400);
644666
}
645667

646668
/// Update platform wallet address (admin only)
@@ -658,6 +680,7 @@ impl EscrowContract {
658680
};
659681

660682
env.storage().persistent().set(&PLATFORM_FEE, &new_config);
683+
env.storage().persistent().extend_ttl(&PLATFORM_FEE, 1000, 518400);
661684
}
662685

663686
/// Get current platform fee percentage
@@ -674,10 +697,14 @@ impl EscrowContract {
674697

675698
/// Get total fees collected by platform
676699
pub fn get_total_fees_collected(env: Env) -> i128 {
677-
env.storage()
700+
let fees = env.storage()
678701
.persistent()
679702
.get(&TOTAL_FEES)
680-
.unwrap_or(0)
703+
.unwrap_or(0);
704+
if env.storage().persistent().has(&TOTAL_FEES) {
705+
env.storage().persistent().extend_ttl(&TOTAL_FEES, 1000, 518400);
706+
}
707+
fees
681708
}
682709

683710
/// Calculate the fee for a given amount (for display purposes)

craft-nexus-contract/src/onboarding.rs

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ impl OnboardingContract {
108108
env.storage()
109109
.persistent()
110110
.set(&DataKey::Config, &config);
111+
env.storage().persistent().extend_ttl(&DataKey::Config, 1000, 518400);
111112

112113
let admin_username = String::from_str(&env, "admin");
113114
let normalized = normalize_username(&env, &admin_username);
@@ -124,11 +125,13 @@ impl OnboardingContract {
124125
env.storage()
125126
.persistent()
126127
.set(&DataKey::UserProfile(admin.clone()), &admin_profile);
128+
env.storage().persistent().extend_ttl(&DataKey::UserProfile(admin.clone()), 1000, 518400);
127129

128130
// Reserve the "admin" username
129131
env.storage()
130132
.persistent()
131-
.set(&DataKey::Username(normalized), &admin);
133+
.set(&DataKey::Username(normalized.clone()), &admin);
134+
env.storage().persistent().extend_ttl(&DataKey::Username(normalized), 1000, 518400);
132135

133136
config
134137
}
@@ -164,6 +167,7 @@ impl OnboardingContract {
164167
.persistent()
165168
.get(&DataKey::Config)
166169
.expect("Contract not initialized");
170+
env.storage().persistent().extend_ttl(&DataKey::Config, 1000, 518400);
167171

168172
// Normalize the username (lowercase + trim whitespace)
169173
let normalized = normalize_username(&env, &username);
@@ -183,6 +187,9 @@ impl OnboardingContract {
183187
let existing: Option<UserProfile> = env.storage()
184188
.persistent()
185189
.get(&DataKey::UserProfile(user.clone()));
190+
if existing.is_some() {
191+
env.storage().persistent().extend_ttl(&DataKey::UserProfile(user.clone()), 1000, 518400);
192+
}
186193

187194
assert!(existing.is_none(), "User already onboarded");
188195

@@ -207,11 +214,13 @@ impl OnboardingContract {
207214
env.storage()
208215
.persistent()
209216
.set(&DataKey::UserProfile(user.clone()), &profile);
217+
env.storage().persistent().extend_ttl(&DataKey::UserProfile(user.clone()), 1000, 518400);
210218

211219
// Store username → address mapping for uniqueness enforcement
212220
env.storage()
213221
.persistent()
214-
.set(&DataKey::Username(normalized), &user);
222+
.set(&DataKey::Username(normalized.clone()), &user);
223+
env.storage().persistent().extend_ttl(&DataKey::Username(normalized), 1000, 518400);
215224

216225
// Emit event
217226
env.events()
@@ -228,10 +237,12 @@ impl OnboardingContract {
228237
/// # Returns
229238
/// UserProfile if user exists, reverts otherwise
230239
pub fn get_user(env: Env, user: Address) -> UserProfile {
231-
env.storage()
240+
let profile: UserProfile = env.storage()
232241
.persistent()
233-
.get(&DataKey::UserProfile(user))
234-
.expect("User not found")
242+
.get(&DataKey::UserProfile(user.clone()))
243+
.expect("User not found");
244+
env.storage().persistent().extend_ttl(&DataKey::UserProfile(user), 1000, 518400);
245+
profile
235246
}
236247

237248
/// Get user profile by username (case-insensitive)
@@ -246,13 +257,17 @@ impl OnboardingContract {
246257

247258
let owner: Address = env.storage()
248259
.persistent()
249-
.get(&DataKey::Username(normalized))
260+
.get(&DataKey::Username(normalized.clone()))
250261
.expect("Username not found");
262+
env.storage().persistent().extend_ttl(&DataKey::Username(normalized), 1000, 518400);
251263

252-
env.storage()
264+
let profile: UserProfile = env.storage()
253265
.persistent()
254-
.get(&DataKey::UserProfile(owner))
255-
.expect("User not found")
266+
.get(&DataKey::UserProfile(owner.clone()))
267+
.expect("User not found");
268+
env.storage().persistent().extend_ttl(&DataKey::UserProfile(owner), 1000, 518400);
269+
270+
profile
256271
}
257272

258273
/// Check if a username is already taken (case-insensitive)
@@ -264,9 +279,13 @@ impl OnboardingContract {
264279
/// true if username is taken, false if available
265280
pub fn is_username_taken(env: Env, username: String) -> bool {
266281
let normalized = normalize_username(&env, &username);
267-
env.storage()
282+
let has = env.storage()
268283
.persistent()
269-
.has(&DataKey::Username(normalized))
284+
.has(&DataKey::Username(normalized.clone()));
285+
if has {
286+
env.storage().persistent().extend_ttl(&DataKey::Username(normalized), 1000, 518400);
287+
}
288+
has
270289
}
271290

272291
/// Check if user is onboarded
@@ -277,10 +296,14 @@ impl OnboardingContract {
277296
/// # Returns
278297
/// true if user has onboarded, false otherwise
279298
pub fn is_onboarded(env: Env, user: Address) -> bool {
280-
env.storage()
299+
let has = env.storage()
281300
.persistent()
282-
.get::<DataKey, UserProfile>(&DataKey::UserProfile(user))
283-
.is_some()
301+
.get::<DataKey, UserProfile>(&DataKey::UserProfile(user.clone()))
302+
.is_some();
303+
if has {
304+
env.storage().persistent().extend_ttl(&DataKey::UserProfile(user), 1000, 518400);
305+
}
306+
has
284307
}
285308

286309
/// Get user's role
@@ -293,9 +316,12 @@ impl OnboardingContract {
293316
pub fn get_user_role(env: Env, user: Address) -> UserRole {
294317
match env.storage()
295318
.persistent()
296-
.get::<DataKey, UserProfile>(&DataKey::UserProfile(user))
319+
.get::<DataKey, UserProfile>(&DataKey::UserProfile(user.clone()))
297320
{
298-
Some(profile) => profile.role,
321+
Some(profile) => {
322+
env.storage().persistent().extend_ttl(&DataKey::UserProfile(user), 1000, 518400);
323+
profile.role
324+
},
299325
None => UserRole::None,
300326
}
301327
}
@@ -319,6 +345,7 @@ impl OnboardingContract {
319345
.persistent()
320346
.get(&DataKey::Config)
321347
.expect("Contract not initialized");
348+
env.storage().persistent().extend_ttl(&DataKey::Config, 1000, 518400);
322349

323350
// Only admin can update roles
324351
config.platform_admin.require_auth();
@@ -328,6 +355,7 @@ impl OnboardingContract {
328355
.persistent()
329356
.get(&DataKey::UserProfile(user.clone()))
330357
.expect("User not found");
358+
env.storage().persistent().extend_ttl(&DataKey::UserProfile(user.clone()), 1000, 518400);
331359

332360
// Update role
333361
let _old_role = profile.role;
@@ -337,6 +365,7 @@ impl OnboardingContract {
337365
env.storage()
338366
.persistent()
339367
.set(&DataKey::UserProfile(user.clone()), &profile);
368+
env.storage().persistent().extend_ttl(&DataKey::UserProfile(user.clone()), 1000, 518400);
340369

341370
// Emit event
342371
env.events()
@@ -359,6 +388,7 @@ impl OnboardingContract {
359388
.persistent()
360389
.get(&DataKey::Config)
361390
.expect("Contract not initialized");
391+
env.storage().persistent().extend_ttl(&DataKey::Config, 1000, 518400);
362392

363393
// Only admin can verify users
364394
config.platform_admin.require_auth();
@@ -368,6 +398,7 @@ impl OnboardingContract {
368398
.persistent()
369399
.get(&DataKey::UserProfile(user.clone()))
370400
.expect("User not found");
401+
env.storage().persistent().extend_ttl(&DataKey::UserProfile(user.clone()), 1000, 518400);
371402

372403
// Set verified
373404
profile.is_verified = true;
@@ -376,6 +407,7 @@ impl OnboardingContract {
376407
env.storage()
377408
.persistent()
378409
.set(&DataKey::UserProfile(user.clone()), &profile);
410+
env.storage().persistent().extend_ttl(&DataKey::UserProfile(user.clone()), 1000, 518400);
379411

380412
// Emit event
381413
env.events()
@@ -389,10 +421,12 @@ impl OnboardingContract {
389421
/// # Returns
390422
/// OnboardingConfig struct
391423
pub fn get_config(env: Env) -> OnboardingConfig {
392-
env.storage()
424+
let config: OnboardingConfig = env.storage()
393425
.persistent()
394426
.get(&DataKey::Config)
395-
.expect("Contract not initialized")
427+
.expect("Contract not initialized");
428+
env.storage().persistent().extend_ttl(&DataKey::Config, 1000, 518400);
429+
config
396430
}
397431

398432
/// Check if user has specific role
@@ -417,9 +451,12 @@ impl OnboardingContract {
417451
pub fn is_verified(env: Env, user: Address) -> bool {
418452
match env.storage()
419453
.persistent()
420-
.get::<DataKey, UserProfile>(&DataKey::UserProfile(user))
454+
.get::<DataKey, UserProfile>(&DataKey::UserProfile(user.clone()))
421455
{
422-
Some(profile) => profile.is_verified,
456+
Some(profile) => {
457+
env.storage().persistent().extend_ttl(&DataKey::UserProfile(user), 1000, 518400);
458+
profile.is_verified
459+
},
423460
None => false,
424461
}
425462
}

craft-nexus-contract/test_snapshots/onboarding/onboarding_test/test_get_user_by_username_not_found.1.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@
450450
"data": {
451451
"vec": [
452452
{
453-
"string": "caught panic 'Username not found' from contract function 'Symbol(obj#55)'"
453+
"string": "caught panic 'Username not found' from contract function 'Symbol(obj#63)'"
454454
},
455455
{
456456
"string": "nonexistent"

craft-nexus-contract/test_snapshots/onboarding/onboarding_test/test_onboard_duplicate_user.1.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,7 @@
769769
"data": {
770770
"vec": [
771771
{
772-
"string": "caught panic 'User already onboarded' from contract function 'Symbol(obj#113)'"
772+
"string": "caught panic 'User already onboarded' from contract function 'Symbol(obj#129)'"
773773
},
774774
{
775775
"address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M"

0 commit comments

Comments
 (0)