Post
Topic
Board Announcements (Altcoins)
Re: [EXPERIMENT] - Token Minimal Extractable Value : TMEV
by
elmuria
on 28/01/2025, 20:28:18 UTC
Here is an o1 first version :


Below is an example Solana smart contract (using the Anchor framework) that implements an immutable vault for a single specified SPL token. This contract shows how to:


  • Initialize a vault with a hardcoded max supply for one SPL token.
  • Deposit SOL into the vault.
  • Swap SPL tokens for SOL based on the formula: 
    Quote
    SOL received = ( (Total SOL in vault) * (SPL tokens sent by user) ) / (SPL token max supply - SPL tokens locked in vault)
  • Lock incoming SPL tokens, making them permanently non-withdrawable.
  • Track relevant vault data on-chain (total SOL, total locked SPL tokens, current token price).
  • Ensure no administrative or upgrade authority exists, achieving immutability.

All the code is heavily commented for clarity. You can compile and deploy it with Anchor, or modify it for a native Solana program if you prefer. This example has not been audited; it is meant as a reference starting point.



Program Overview

  • Vault State (PDA)
    • Holds the vault’s data:
      • locked_spl_tokens: How many SPL tokens have been sent to the vault and are locked.
      • bump: PDA “bump seed” for program-derived addresses.
      • token_mint: Address of the SPL token this vault supports.
      • vault_token_account: Token account address (PDA) where locked SPL tokens reside.
      • max_supply: The hardcoded max supply for the SPL token (used in exchange rate).
  • Instructions
    • initialize_vault
      • Creates the vault state account (PDA) and its associated token account to hold locked SPL tokens.
      • Hardcodes MAX_SUPPLY for the chosen token.
    • deposit_sol
      • Allows any user to deposit SOL into the vault.
      • SOL becomes part of the vault’s balance.
    • swap_spl_for_sol
      • Swaps user-specified SPL tokens for SOL using the specified formula.
      • The user sends SPL tokens to the vault’s token account.
      • The vault sends the user their SOL share.
  • Exchange Rate Formula
    Quote
    SOL received = ( (Vault SOL) * (SPL tokens sent) ) / (MAX_SUPPLY - locked_spl_tokens )
    After the swap, the user’s SPL tokens remain locked in the vault’s token account, and locked_spl_tokens is incremented accordingly.
  • No Admin / Immutability
    • The program includes no special authority or permission checks.
    • Once deployed (without upgradeable features) on Solana, this contract is effectively immutable.
  • On-Chain Transparency
    • Anyone can query the vault’s PDA to see:
      • locked_spl_tokens
      • Vault’s SOL balance
      • The current SPL token price, computed as:
        Quote
        (Vault’s total SOL) / (MAX_SUPPLY - locked_spl_tokens)



Example Code

Code:
... /// Initializes the vault for a specific SPL token.
/// 1) Creates and seeds the VaultAccount (PDA).
/// 2) Creates an associated token account (PDA) that will store locked SPL tokens.
/// 3) Hardcodes MAX_SUPPLY for the chosen token.
///
/// This must be called once per vault instance.
pub fn initialize_vault(ctx: Context<InitializeVault>) -> Result<()> {
    let vault_account = &mut ctx.accounts.vault_account;

    // Store the token mint in the vault data structure
    vault_account.token_mint = ctx.accounts.token_mint.key();

    // Record the bump seed that is used to derive the vault's PDA
    vault_account.bump = *ctx.bumps.get("vault_account").unwrap();

    // Initialize locked tokens to zero
    vault_account.locked_spl_tokens = 0;

    // Store the vault's token account (the locked SPL token account) in the vault state
    vault_account.vault_token_account = ctx.accounts.vault_token_account.key();

    // Store the hardcoded max supply
    vault_account.max_supply = MAX_SUPPLY;

    Ok(())
}

/// Allows a user to deposit SOL into the vault.
/// The user must send lamports along with this instruction.
/// No upper or lower limit is enforced by the contract.
///
/// Important note: The system transfer is handled automatically
/// by Anchor if you specify `#[account(mut)]` for the payer and
/// have an associated CPI call to the system program.
pub fn deposit_sol(ctx: Context<DepositSol>, lamports: u64) -> Result<()> {
    // The user is transferring 'lamports' SOL to the vault's
    // program-owned account. Anchor will handle the actual
    // system_program::transfer CPI based on the context.

    // Safety checks: you can add checks to ensure 'lamports'
    // matches the lamports passed, or skip them if you want
    // fully trustless behavior. For example:
    // require!(lamports > 0, VaultError::InvalidLamportsAmount);
   
    // The "deposit" transaction is effectively letting the
    // vault's system account accumulate SOL. There's no need
    // to store an explicit "total_sol" in the vault state
    // because you can always check the vault's account
    // balance on-chain if needed.
   
    // If you want a reference or an event, you could emit it here:
    // emit!(DepositEvent { ... });

    Ok(())
}

/// Swaps the user’s SPL tokens for SOL from the vault.
///
/// The formula used is:
/// SOL received = ( (Vault SOL) * (SPL tokens in) )
///                / (max_supply - locked_spl_tokens)
///
/// Steps:
/// 1) Transfer the user's SPL tokens to the vault's token account.
/// 2) Calculate how much SOL to give the user.
/// 3) Transfer that SOL from the vault to the user.
/// 4) Update the vault state to record locked SPL tokens.
pub fn swap_spl_for_sol(
    ctx: Context<SwapSplForSol>,
    spl_amount_in: u64
) -> Result<()> {
    let vault_account = &mut ctx.accounts.vault_account;

    // ---------------------------------------
    // Step 1) Transfer SPL tokens from user to vault
    // ---------------------------------------
    // This CPI call moves 'spl_amount_in' from user's token account
    // to the vault's token account (which is locked).

    let cpi_ctx = CpiContext::new(
        ctx.accounts.token_program.to_account_info(),
        Transfer {
            from: ctx.accounts.user_token_account.to_account_info(),
            to: ctx.accounts.vault_token_account.to_account_info(),
            authority: ctx.accounts.user_authority.to_account_info(),
        },
    );

    token::transfer(cpi_ctx, spl_amount_in)?;

    // ---------------------------------------
    // Step 2) Calculate the SOL to send to user
    // ---------------------------------------
    // Retrieve the vault's current SOL balance. This is the
    // lamport balance of the vault_account's system account.
    //
    // Usually in Anchor, the `vault_account` is not the same as
    // a system-owned "vault for SOL" unless you used that approach.
    // Alternatively, you might use another PDA for storing SOL.
    //
    // For simplicity, assume here that the vault_account itself
    // is system-owned and holds the lamports. If not, you'd keep
    // track of the lamport balance in the state or fetch it from
    // a different account.
    let vault_sol_balance = ctx.accounts.vault_account_pda.to_account_info().lamports();

    // The formula:
    // sol_out = (vault_sol_balance * spl_amount_in)
    //           / (max_supply - locked_spl_tokens)
    let remaining_supply = vault_account
        .max_supply
        .checked_sub(vault_account.locked_spl_tokens)
        .ok_or(VaultError::MathOverflow)?;

    require!(
        remaining_supply >= spl_amount_in,
        VaultError::NotEnoughRemainingSupply
    );

    let sol_out = (vault_sol_balance as u128)
        .checked_mul(spl_amount_in as u128)
        .ok_or(VaultError::MathOverflow)?
        .checked_div(remaining_supply as u128)
        .ok_or(VaultError::MathOverflow)? as u64;

    // ---------------------------------------
    // Step 3) Transfer SOL from vault to user
    // ---------------------------------------
    // We do a system_program::transfer from the vault_account_pda to user_authority.
    //
    // Use the seeds of the vault_account to sign as the program-derived address
    // (because the vault's account is PD-owned, not user-owned).
    let vault_seeds = &[        b"vault_account",        vault_account.token_mint.as_ref(),        &[vault_account.bump],
    ];
    let signer = &[&vault_seeds[..]];

    // Build the transfer instruction
    let ix = anchor_lang::solana_program::system_instruction::transfer(
        &ctx.accounts.vault_account_pda.key(),
        &ctx.accounts.user_authority.key(),
        sol_out,
    );

    // Invoke the transfer
    anchor_lang::solana_program::program::invoke_signed(
        &ix,
        &[
            ctx.accounts.vault_account_pda.to_account_info(),
            ctx.accounts.user_authority.to_account_info(),
            ctx.accounts.system_program.to_account_info(),
        ],
        signer,
    )?;

    // ---------------------------------------
    // Step 4) Update locked SPL tokens
    // ---------------------------------------
    vault_account.locked_spl_tokens = vault_account
        .locked_spl_tokens
        .checked_add(spl_amount_in)
        .ok_or(VaultError::MathOverflow)?;

    Ok(())
}
 ...



Detailed Explanations

  • 1. MAX_SUPPLY Constant
    • The vault uses a hardcoded constant MAX_SUPPLY to ensure the correct exchange rate is always used.
    • In production, you would recompile the contract for each unique SPL token to embed the correct max supply.
  • 2. No Admin Keys / Immutability
    • There is no authority field with special privileges.
    • Once deployed (without upgradeable features) on Solana, this contract is effectively immutable.
  • 3. How Deposits Work
    • A user calls deposit_sol with lamports to deposit.
    • The standard system program transfer moves SOL from the user’s account to the vault’s program-owned account (the vault PDA).
    • The vault accumulates SOL; it cannot be withdrawn except through the swap function (by giving up SPL tokens).
  • 4. How Swaps Work
    • User calls swap_spl_for_sol, providing:
      • The user’s SPL Token Account.
      • The vault’s token account (to lock tokens).
      • The vault’s system account (holding SOL).
    • The program:
      • Transfers the user’s SPL tokens to the vault’s token account.
      • Computes how much SOL to return to the user by the formula:
        Quote
        sol_out = (vault_sol_balance * spl_amount_in) / (max_supply - locked_spl_tokens)
      • Transfers that SOL to the user.
      • Updates locked_spl_tokens.
  • 5. Token Locking
    • Once SPL tokens are transferred to vault_token_account, they remain there permanently.
    • There is no mechanism to withdraw them, ensuring they stay locked indefinitely.
  • 6. On-Chain Data
    • locked_spl_tokens and max_supply are publicly visible in the VaultAccount.
    • The total SOL in the vault can be inspected by querying the system account (PDA).
    • The current SPL token price (in SOL) can be derived on the front end by:
      Quote
      price = vault_sol_balance / (max_supply - locked_spl_tokens)



Optional Future Feature: Using a Currency Basket

Instead of holding only SOL, the vault could hold a basket of assets (e.g., multiple SPL stablecoins like USDC, or even additional cryptocurrencies). The steps for extending this design to a basket:

  • 1. Additional PDA Accounts
    • For each asset in the basket, the vault would need an account (or token account) to hold that asset.
  • 2. Weighted Exchange Rate
    • The formula for determining how many SPL tokens to return (or how much asset to receive) would be more complex, taking into account the combined value of all assets.
    • You would track each asset’s total quantity, price feed (from an oracle, if needed), and then compute an overall “vault value.”
  • 3. Multi-Asset Swaps
    • The user could specify which asset (or mix of assets) they want in exchange for their SPL tokens, leading to a more flexible swap mechanism.
  • 4. Rebalancing
    • If you want a truly dynamic basket, you might allow automated rebalancing of assets, which requires additional logic and possibly a governance mechanism (contradicting the “no admin” rule unless governed by token-based votes or an on-chain Weighted Automatic Market Maker).

These enhancements go beyond the scope of a simple, single-token vault but illustrate how the concept can expand to more sophisticated use cases.



Final Notes

  • This example demonstrates the core vault mechanics: deposit, swap, lock tokens, and keep an on-chain record of locked tokens.
  • Real-world production code requires extensive testing and security audits.
  • The contract is immutable if deployed without upgradeable features (e.g., Anchor toml set with [programs.local] <program-name> = <your-id> in a non-upgradeable manner).
  • Always verify that your PDA derivations and signers are correct and that you handle Solana’s rent-exemption for all created accounts.

Use this reference as a starting point to build your own specialized vault program on Solana. Adjust the code, add features (like a currency basket), and carefully test before deploying to mainnet.