Nevix
  • Protocol Overview
  • Protocol Architecture
  • Mathematical Foundation
  • Protocol Flow
  • Staking Process
  • Technical Implementation
    • Solana Program
    • Account Structure
    • Client Integration
  • Socials
    • X
  • Telegram
  • Website
Powered by GitBook
On this page
  1. Technical Implementation

Solana Program

// Rust program example

#[program]
pub mod nevix_vault {

    use super::*;

    pub fn withdraw_nevix(ctx: Context<Withdraw>, index: usize, reward_only: bool) -> Result<()> {    
        // Safely access 'total_deposits' array and validate index
        let total_deposits = &ctx.accounts.user_interactions_counter.total_deposits;
        let amount = total_deposits.get(index).ok_or(ErrorCode::InvalidIndex)?;
        // Safely access 'stake_deposits' array
        let stake_deposits = &ctx.accounts.user_interactions_counter.stake_deposits;
        let stake_time = stake_deposits.get(index).ok_or(ErrorCode::InvalidIndex)?;
        let clock = Clock::get()?;
        let unix_timestamp: u64 = clock
            .unix_timestamp
            .try_into()
            .map_err(|_| ErrorCode::Overflow)?;
        let time_elapsed = unix_timestamp
            .checked_sub(*stake_time)
            .ok_or(ErrorCode::Overflow)?
            .checked_mul(MILLISECONDS_IN_SECOND)
            .ok_or(ErrorCode::Overflow)?;
        // Safely update user interaction counters
        let counters = &mut ctx.accounts.user_interactions_counter;
        if !reward_only {
            if let Some(total) = counters.total_deposits.get_mut(index) {
                *total = 0;
            } else {
                return Err(ErrorCode::InvalidIndex.into());
            }
    
            if let Some(time) = counters.time_deposits.get_mut(index) {
                *time = 0;
            } else {
                return Err(ErrorCode::InvalidIndex.into());
            }
    
            if let Some(stake) = counters.stake_deposits.get_mut(index) {
                *stake = 0;
            } else {
                return Err(ErrorCode::InvalidIndex.into());
            }
        } else {
            if let Some(stake) = counters.stake_deposits.get_mut(index) {
                *stake = unix_timestamp;
            } else {
                return Err(ErrorCode::InvalidIndex.into());
            }
        }
        // Reduce current stakers if applicable
        if let Some(first_deposit) = total_deposits.get(0) {
            if *first_deposit == 0 && iter_all_eq(total_deposits).is_some() {
                ctx.accounts.vault.current_stakers = ctx
                    .accounts
                    .vault
                    .current_stakers
                    .checked_sub(1)
                    .ok_or(ErrorCode::Overflow)?;
            }
        } 
        // Initialize withdrawal amount
        let mut withdraw_amount = *amount;    
        // Check if sufficient time has elapsed to calculate rewards
        let hours_elapsed: u64 = time_elapsed
            .checked_div(MILLISECONDS_IN_SECOND)
            .and_then(|ms| ms.checked_div(SECONDS_IN_MINUTE))
            .and_then(|min| min.checked_div(MINUTES_IN_HOUR))
            .ok_or(ErrorCode::Overflow)?;
        if hours_elapsed >= ctx.accounts.vault.base_hour.try_into().map_err(|_| ErrorCode::Overflow)? {
            // Safely access 'vault' fields and ensure no division by zero
            let start_pool_f64: f64 = ctx.accounts.vault.start_pool
                .try_into()
                .map_err(|_| ErrorCode::Overflow)?;
            if start_pool_f64 == 0.0 {
                return Err(ErrorCode::DivisionByZero.into());
            }
            let amount_f64: f64 = ctx.accounts.vault.amount
                .try_into()
                .map_err(|_| ErrorCode::Overflow)?;  
            let pool_percent = amount_f64 / start_pool_f64;
            let base_hour_adjuster: u64 = ctx.accounts
                .vault
                .base_hour
                .try_into()
                .map_err(|_| ErrorCode::Overflow)?;
            // Safely adjust held hours
            let held_hours = hours_elapsed.min(MAX_HELD_HOURS);
            let remainder = held_hours % base_hour_adjuster;
            let multi: u64 = if remainder == 0 {
                held_hours.checked_div(base_hour_adjuster).ok_or(ErrorCode::Overflow)?
            } else {
                (held_hours.checked_sub(remainder).ok_or(ErrorCode::Overflow)?)
                    .checked_div(base_hour_adjuster)
                    .ok_or(ErrorCode::Overflow)?
            };   
            let base_rate_f32: f32 = ctx.accounts.vault.base_rate
                .try_into()
                .map_err(|_| ErrorCode::Overflow)?;
            let final_multi: f32 = (multi.try_into().map_err(|_| ErrorCode::Overflow)? * base_rate_f32)
                .checked_mul(pool_percent as f32)
                .ok_or(ErrorCode::Overflow)?;
            // Safely calculate reward tokens as u64
            let reward_tokens: u64 = (amount_f64 * (final_multi / divisor))
                .floor()
                .try_into()
                .map_err(|_| ErrorCode::Overflow)?;
            ctx.accounts.vault.amount = ctx
                .accounts
                .vault
                .amount
                .checked_sub(reward_tokens)
                .ok_or(ErrorCode::Overflow)?;
            withdraw_amount = if reward_only {
                reward_tokens
            } else {
                withdraw_amount.checked_add(reward_tokens).ok_or(ErrorCode::Overflow)?
            };
        }   
        // Safely update vault staked amount if not reward-only
        if !reward_only {
            ctx.accounts.vault.amount_staked = ctx
                .accounts.vault.amount_staked
                .checked_sub(*amount)
                .ok_or(ErrorCode::Overflow)?;
        } else if withdraw_amount == *amount {
            withdraw_amount = 0;
        }
        // Safely transfer tokens from vault to withdrawer
        if withdraw_amount > 0 {
            let cpi_accounts = Transfer {
                from: ctx.accounts.vault_token_account.to_account_info(),
                to: ctx.accounts.withdrawer_token_account.to_account_info(),
                authority: ctx.accounts.vault_token_account.to_account_info(),
            };
            let cpi_program = ctx.accounts.token_program.to_account_info();
            transfer(CpiContext::new(cpi_program, cpi_accounts), withdraw_amount)?;
        }
        Ok(())
    }
}
PreviousStaking ProcessNextAccount Structure

Last updated 27 days ago