From 7c0288d73172efcaffbabc154d76c7b7d51f10ee Mon Sep 17 00:00:00 2001 From: Greg Mitchell Date: Tue, 16 Jun 2026 17:57:37 +0000 Subject: [PATCH 1/2] serviceability: expose public fn to calculate access pass airdrop target --- .../src/processors/accesspass/mod.rs | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/smartcontract/programs/doublezero-serviceability/src/processors/accesspass/mod.rs b/smartcontract/programs/doublezero-serviceability/src/processors/accesspass/mod.rs index d281f8a96..25a22da6f 100644 --- a/smartcontract/programs/doublezero-serviceability/src/processors/accesspass/mod.rs +++ b/smartcontract/programs/doublezero-serviceability/src/processors/accesspass/mod.rs @@ -11,6 +11,23 @@ use solana_program::{ // `User` account size assumes a single publisher and subscriber pubkey registered (266 bytes each). pub const AIRDROP_USER_RENT_LAMPORTS_BYTES: usize = 266 * 3; // 266 bytes per User account x 3 accounts = 798 bytes +/// Computes the target lamport balance a `user_payer` must hold to cover rent for its `User` +/// accounts plus the configured per-user airdrop. `multiplier` scales the target for passes that +/// admit several users (e.g. `allow_multiple_ip` seat keypairs); pass `1` for single-user passes. +/// +/// Off-chain callers (e.g. the feed oracle) can obtain `Rent` over RPC — by fetching the rent +/// sysvar account, or via `getMinimumBalanceForRentExemption` — since `Rent::get()` is a syscall +/// only available inside a running program. +pub fn airdrop_user_target_lamports( + rent: &Rent, + user_airdrop_lamports: u64, + multiplier: u64, +) -> u64 { + rent.minimum_balance(AIRDROP_USER_RENT_LAMPORTS_BYTES) + .saturating_add(user_airdrop_lamports) + .saturating_mul(multiplier) +} + /// Tops up `user_payer` so it holds enough SOL to cover rent for its `User` accounts plus the /// configured per-user airdrop, allowing the user to connect immediately. `multiplier` scales the /// target for passes that admit several users (e.g. `allow_multiple_ip` seat keypairs); pass `1` @@ -24,12 +41,8 @@ pub fn airdrop_user_credits<'a>( user_airdrop_lamports: u64, multiplier: u64, ) -> ProgramResult { - let base_target = Rent::get()? - .minimum_balance(AIRDROP_USER_RENT_LAMPORTS_BYTES) - .saturating_add(user_airdrop_lamports); - let deposit = base_target - .saturating_mul(multiplier) - .saturating_sub(user_payer.lamports()); + let target = airdrop_user_target_lamports(&Rent::get()?, user_airdrop_lamports, multiplier); + let deposit = target.saturating_sub(user_payer.lamports()); if deposit == 0 { return Ok(()); @@ -46,3 +59,24 @@ pub fn airdrop_user_credits<'a>( &[], ) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_airdrop_user_target_lamports() { + let rent = Rent::default(); + let base = rent.minimum_balance(AIRDROP_USER_RENT_LAMPORTS_BYTES); + let airdrop = 40_000; + + assert_eq!( + airdrop_user_target_lamports(&rent, airdrop, 1), + base + airdrop + ); + assert_eq!( + airdrop_user_target_lamports(&rent, airdrop, 5), + (base + airdrop) * 5 + ); + } +} From 5e5edafff7e759eb17d0be63aa9c5079245e7645 Mon Sep 17 00:00:00 2001 From: Greg Mitchell Date: Tue, 16 Jun 2026 18:02:22 +0000 Subject: [PATCH 2/2] serviceability: name DEFAULT_USER_AIRDROP_LAMPORTS and use it at init --- .../src/processors/accesspass/mod.rs | 4 ++++ .../src/processors/globalstate/initialize.rs | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/smartcontract/programs/doublezero-serviceability/src/processors/accesspass/mod.rs b/smartcontract/programs/doublezero-serviceability/src/processors/accesspass/mod.rs index 25a22da6f..18ec796b7 100644 --- a/smartcontract/programs/doublezero-serviceability/src/processors/accesspass/mod.rs +++ b/smartcontract/programs/doublezero-serviceability/src/processors/accesspass/mod.rs @@ -11,6 +11,10 @@ use solana_program::{ // `User` account size assumes a single publisher and subscriber pubkey registered (266 bytes each). pub const AIRDROP_USER_RENT_LAMPORTS_BYTES: usize = 266 * 3; // 266 bytes per User account x 3 accounts = 798 bytes +/// Default per-user airdrop seeded into `GlobalState.user_airdrop_lamports` at initialization. +/// Admins can override it via the `SetAirdrop` instruction. +pub const DEFAULT_USER_AIRDROP_LAMPORTS: u64 = 40_000; + /// Computes the target lamport balance a `user_payer` must hold to cover rent for its `User` /// accounts plus the configured per-user airdrop. `multiplier` scales the target for passes that /// admit several users (e.g. `allow_multiple_ip` seat keypairs); pass `1` for single-user passes. diff --git a/smartcontract/programs/doublezero-serviceability/src/processors/globalstate/initialize.rs b/smartcontract/programs/doublezero-serviceability/src/processors/globalstate/initialize.rs index 5ffe9b73b..49f73beff 100644 --- a/smartcontract/programs/doublezero-serviceability/src/processors/globalstate/initialize.rs +++ b/smartcontract/programs/doublezero-serviceability/src/processors/globalstate/initialize.rs @@ -1,5 +1,6 @@ use crate::{ pda::*, + processors::accesspass::DEFAULT_USER_AIRDROP_LAMPORTS, programversion::ProgramVersion, seeds::{SEED_GLOBALSTATE, SEED_PREFIX, SEED_PROGRAM_CONFIG}, serializer::{try_acc_create, try_acc_write}, @@ -102,7 +103,7 @@ pub fn initialize_global_state(program_id: &Pubkey, accounts: &[AccountInfo]) -> activator_authority_pk: *payer_account.key, sentinel_authority_pk: *payer_account.key, contributor_airdrop_lamports: 1_000_000_000, - user_airdrop_lamports: 40_000, + user_airdrop_lamports: DEFAULT_USER_AIRDROP_LAMPORTS, health_oracle_pk: *payer_account.key, qa_allowlist: vec![*payer_account.key], feature_flags: 0,