Design and Implementation

Smart Contract Design for Automated Restaking

Overview

The smart contract is the core component of the automated restaking mechanism. It manages user deposits, dynamically reallocates assets to maximize returns, and automates the restaking process. Designed using Rust and the Anchor framework, it ensures high efficiency, modularity, and security.


Key Features

1. Stake Management Module

Handles user interactions for depositing, withdrawing, and managing Liquid Staking Tokens (LSTs).

2. APY Optimization Module

Continuously monitors staking platforms and reallocates user funds to maximize Annual Percentage Yield (APY).

3. Gas Optimization Module

Batch transactions and optimize execution timing to reduce gas fees.


Detailed Structure

Data Structures

  1. Global State Stores protocol-wide configuration and staking pool data.

    #[account]
    pub struct GlobalState {
        pub admin: Pubkey,            // Admin authority
        pub staking_pools: Vec<Pool>, // List of staking pools
        pub oracle: Pubkey,           // Oracle for APY data
    }
    
    #[derive(AnchorSerialize, AnchorDeserialize, Clone)]
    pub struct Pool {
        pub pool_address: Pubkey,     // Address of the staking pool
        pub current_apy: u64,         // Current APY of the pool
        pub total_staked: u64,        // Total LSTs staked in this pool
    }
  2. User Account Tracks user-specific staking details.

    #[account]
    pub struct UserAccount {
        pub user: Pubkey,             // User wallet address
        pub staked_tokens: u64,       // Total staked tokens
        pub current_pool: Pubkey,     // Pool where the user’s tokens are staked
        pub reward_balance: u64,      // Accumulated rewards
    }

Functions

1. Initialize Protocol

Sets up the global state and configures the staking pools.

pub fn initialize(ctx: Context<Initialize>, oracle: Pubkey) -> Result<()> {
    let global_state = &mut ctx.accounts.global_state;
    global_state.admin = *ctx.accounts.admin.key;
    global_state.oracle = oracle;
    global_state.staking_pools = Vec::new();
    Ok(())
}

2. Add Staking Pool

Adds a new staking pool to the protocol.

pub fn add_pool(ctx: Context<AddPool>, pool_address: Pubkey, initial_apy: u64) -> Result<()> {
    let global_state = &mut ctx.accounts.global_state;
    global_state.staking_pools.push(Pool {
        pool_address,
        current_apy: initial_apy,
        total_staked: 0,
    });
    Ok(())
}

3. Deposit Tokens

Allows users to deposit LSTs into the protocol.

pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
    let user_account = &mut ctx.accounts.user_account;
    let global_state = &mut ctx.accounts.global_state;

    // Allocate to the pool with the highest APY
    let best_pool = global_state.staking_pools.iter()
        .max_by_key(|pool| pool.current_apy)
        .ok_or(ProgramError::Custom(1))?; // Error if no pool is available

    user_account.staked_tokens += amount;
    user_account.current_pool = best_pool.pool_address;
    best_pool.total_staked += amount;

    Ok(())
}

4. Optimize APY

Reallocates user funds to the pool with the highest APY.

pub fn optimize_apy(ctx: Context<OptimizeApy>) -> Result<()> {
    let user_account = &mut ctx.accounts.user_account;
    let global_state = &mut ctx.accounts.global_state;

    let current_pool = global_state.staking_pools.iter_mut()
        .find(|pool| pool.pool_address == user_account.current_pool)
        .ok_or(ProgramError::Custom(2))?;

    let best_pool = global_state.staking_pools.iter()
        .max_by_key(|pool| pool.current_apy)
        .ok_or(ProgramError::Custom(1))?;

    if best_pool.pool_address != current_pool.pool_address {
        // Reallocate funds
        current_pool.total_staked -= user_account.staked_tokens;
        best_pool.total_staked += user_account.staked_tokens;
        user_account.current_pool = best_pool.pool_address;
    }

    Ok(())
}

5. Withdraw Tokens

Allows users to withdraw their staked LSTs.

pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> Result<()> {
    let user_account = &mut ctx.accounts.user_account;
    let global_state = &mut ctx.accounts.global_state;

    require!(user_account.staked_tokens >= amount, CustomError::InsufficientFunds);

    let current_pool = global_state.staking_pools.iter_mut()
        .find(|pool| pool.pool_address == user_account.current_pool)
        .ok_or(ProgramError::Custom(2))?;

    user_account.staked_tokens -= amount;
    current_pool.total_staked -= amount;

    Ok(())
}

Pseudocode for APY Optimization Algorithm

fn optimize_apy(user_tokens: &mut UserTokens, pools: &[Pool]) {
    // Find the current pool where the user's tokens are staked
    let current_pool = pools.iter()
        .find(|pool| pool.address == user_tokens.current_pool)
        .expect("Current pool not found");

    // Find the pool with the highest APY
    let best_pool = pools.iter()
        .max_by_key(|pool| pool.apy)
        .expect("No pools available");

    // If the user is not already in the pool with the highest APY, reallocate tokens
    if best_pool.address != current_pool.address {
        // Update the total staked amount for the current and best pools
        current_pool.total_staked -= user_tokens.amount;
        best_pool.total_staked += user_tokens.amount;
        
        // Update the user's current pool to the best pool
        user_tokens.current_pool = best_pool.address;
    }
}

Security Features

  1. Oracle Validation: Ensure APY data is verified through trusted oracles.

  2. Reentrancy Protection: Use Anchor’s account locks to prevent multiple calls within a single transaction.

  3. Admin Controls: Restrict sensitive operations like adding pools or updating the oracle.

Summary

This smart contract design leverages the scalability of Solana to provide a seamless, automated restaking experience. By dynamically reallocating funds based on APY and optimizing gas usage, it ensures users receive the best possible returns.

Last updated