This document describes the storage layout optimizations applied to Nova Rewards smart contracts to stay within Soroban’s WASM size and storage budget limits.
This audit reviewed the nova-rewards, nova_token, vesting, referral, reward_pool, and admin_roles contracts. Existing keys are already mostly consolidated; the new DailyUsage struct in reward_pool further reduces per-wallet storage overhead.
Where multiple fields are always read/written together, they have been consolidated into single struct values to reduce storage overhead.
Using Bytes or BytesN instead of String for fixed-length identifiers reduces encoding overhead.
All persistent storage entries have appropriate TTL extensions to prevent ledger expiry.
nova-rewards)Storage Keys:
Admin (instance) - Administrator addressBalance(Address) (instance) - User point balancesMigratedVersion (instance) - Contract version for migrationsXlmToken (instance) - XLM SAC token contract addressRouter (instance) - DEX router contract addressOptimizations Applied:
Balance(Address) instead of separate mappingi128 to prevent overflow without additional storageWASM Size: Measured with stellar contract inspect --wasm
nova_token)Storage Keys:
Admin (instance) - Administrator addressBalance(Address) (persistent) - Token balances per addressAllowance(Address, Address) (persistent) - Spending allowancesOptimizations Applied:
TTL Extensions:
vesting)Storage Keys:
Admin (instance) - Administrator addressPoolBalance (instance) - Total pool balanceSchedule(Address, u32) (persistent) - Vesting schedules by beneficiary and IDNextId(Address) (instance) - Counter for schedule IDs per beneficiaryOptimizations Applied:
VestingSchedule struct consolidates all schedule fields (beneficiary, amounts, times, released)Schedule(Address, u32) enables efficient per-user schedule lookupsTTL Extensions:
admin_roles)Storage Keys:
Admins (instance) - Set of admin addressesMultisigThreshold (instance) - Required signature countPendingAdmin (instance) - Two-step transfer stateOptimizations Applied:
referral)Storage Keys:
Admin (instance) - Administrator addressUserReferrer(Address) (persistent) - Referrer for each userReferralCount(Address) (persistent) - Count of referrals per userRegistrationCounter (instance) - Global registration counterOptimizations Applied:
TTL Extensions:
reward_pool)Storage Keys:
Admin (instance) - Administrator addressBalance (instance) - Pool balanceDailyLimit (instance) - Global per-wallet daily withdraw limitDailyUsage(Address) (persistent) - Per-wallet daily usage and window startOptimizations Applied:
DailyUsage struct for each walletTTL Extensions:
DailyUsage entries: Extended on each withdraw/check to preserve rolling window stateextend_ttl() on read/write// Example: Extending TTL on persistent storage read
let balance: i128 = env
.storage()
.persistent()
.get(&DataKey::Balance(addr.clone()))
.unwrap_or(0);
// Extend TTL by 31 days (2,678,400 ledgers at 5s/ledger)
env.storage()
.persistent()
.extend_ttl(&DataKey::Balance(addr.clone()), 2_678_400, 2_678_400);
Run before and after optimization:
cd contracts
stellar contract build
stellar contract inspect --wasm target/wasm32v1-none/release/nova_rewards.wasm
stellar contract inspect --wasm target/wasm32v1-none/release/nova_token.wasm
stellar contract inspect --wasm target/wasm32v1-none/release/vesting.wasm