Use this file to discover all available pages before exploring further.
The storage fund is a key innovation in Sui’s economic model, designed to make on-chain storage economically sustainable in the long term by creating a pay-for-what-you-use model.
The storage fund maintains a balance that represents the cost of storing all current on-chain objects:
/// Struct representing the storage fund, containing two `Balance`s:/// - `total_object_storage_rebates` has the invariant that it's the sum of `storage_rebate` of/// all objects currently stored on-chain./// - `non_refundable_balance` contains any remaining inflow of the storage fund that should not/// be taken out of the fund.public struct StorageFund has store { total_object_storage_rebates: Balance<SUI>, non_refundable_balance: Balance<SUI>,}
public(package) fun advance_epoch( self: &mut StorageFund, storage_charges: Balance<SUI>, storage_fund_reinvestment: Balance<SUI>, leftover_staking_rewards: Balance<SUI>, storage_rebate_amount: u64, non_refundable_storage_fee_amount: u64,): Balance<SUI> { // Both the reinvestment and leftover rewards are not to be refunded self.non_refundable_balance.join(storage_fund_reinvestment); self.non_refundable_balance.join(leftover_staking_rewards); // Storage charges go into total rebates pool self.total_object_storage_rebates.join(storage_charges); // Split out non-refundable portion of storage rebate let non_refundable_storage_fee = self .total_object_storage_rebates .split(non_refundable_storage_fee_amount); self.non_refundable_balance.join(non_refundable_storage_fee); // Return refundable portion to be burned let storage_rebate = self.total_object_storage_rebates.split(storage_rebate_amount); storage_rebate}
public fun total_object_storage_rebates(self: &StorageFund): u64 { self.total_object_storage_rebates.value()}public fun total_balance(self: &StorageFund): u64 { self.total_object_storage_rebates.value() + self.non_refundable_balance.value()}
// Create object (user pays storage)public fun create(ctx: &mut TxContext) { let obj = MyObject { id: object::new(ctx), data: vector[/* 1KB of data */], }; // User charged: 1024 bytes × storage_price // This amount held in storage fund as rebate transfer::transfer(obj, ctx.sender());}// Delete object (user gets rebate)public fun cleanup(obj: MyObject) { let MyObject { id, data: _ } = obj; id.delete(); // User receives: ~95% of original storage cost // Storage fund keeps: ~5% as non-refundable fee}
// Bad: Storing large data on-chainpublic struct Document has key { id: UID, content: vector<u8>, // 1 MB of data}// Good: Store reference to off-chain datapublic struct Document has key { id: UID, content_hash: vector<u8>, // 32 bytes storage_url: String, // URL to IPFS/Arweave}
Implement cleanup mechanisms
// Allow users to clean up old datapublic fun cleanup_old_records(registry: &mut Registry) { let cutoff_time = current_time() - 90_DAYS; // Remove old records registry.records.retain(|_, record| { record.timestamp > cutoff_time });}
Use dynamic fields wisely
// Optional data as dynamic fields// Only pay storage when actually usedif (has_metadata) { dynamic_field::add(&mut obj.id, b"metadata", metadata);}// Remove when no longer neededif (dynamic_field::exists_(&obj.id, b"metadata")) { let _: Metadata = dynamic_field::remove(&mut obj.id, b"metadata"); // Get storage rebate}