From 203e8ef4e0786470d2d0ef536a7e658153b4a882 Mon Sep 17 00:00:00 2001 From: amalnathsathyan Date: Wed, 25 Feb 2026 12:05:24 +0530 Subject: [PATCH 1/4] Modified Lending Vault to have token accounts for tokenX&tokenY --- .../src/instructions/close_position.rs | 2 +- .../instructions/initialize_lending_vault.rs | 49 +++++++++++-------- .../src/instructions/liquidate.rs | 2 +- .../src/instructions/open_position.rs | 2 +- .../metlev-engine/src/instructions/supply.rs | 2 +- .../src/instructions/withdraw.rs | 2 +- .../metlev-engine/src/state/lending_vault.rs | 3 +- 7 files changed, 35 insertions(+), 27 deletions(-) diff --git a/programs/metlev-engine/src/instructions/close_position.rs b/programs/metlev-engine/src/instructions/close_position.rs index f118f43..c16088d 100644 --- a/programs/metlev-engine/src/instructions/close_position.rs +++ b/programs/metlev-engine/src/instructions/close_position.rs @@ -25,7 +25,7 @@ pub struct ClosePosition<'info> { #[account( mut, seeds = [LendingVault::SEED_PREFIX], - bump = lending_vault.bump, + bump = lending_vault.vault_bump, )] pub lending_vault: Account<'info, LendingVault>, diff --git a/programs/metlev-engine/src/instructions/initialize_lending_vault.rs b/programs/metlev-engine/src/instructions/initialize_lending_vault.rs index 3d897ee..3f202fe 100644 --- a/programs/metlev-engine/src/instructions/initialize_lending_vault.rs +++ b/programs/metlev-engine/src/instructions/initialize_lending_vault.rs @@ -1,4 +1,5 @@ -use anchor_lang::{prelude::*, system_program::{Transfer, transfer}}; +use anchor_lang::prelude::*; +use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface}; use crate::state::LendingVault; use crate::state::Config; use crate::errors::ProtocolError; @@ -25,39 +26,45 @@ pub struct InitializeLendingVault<'info>{ pub lending_vault: Account<'info, LendingVault>, #[account( - mut, - seeds = [b"sol_vault", lending_vault.key().as_ref()], - bump, + init_if_needed, + payer = authority, + token::mint = mint_x, + token::authority = lending_vault, + token::token_program = token_program, + seeds = [b"token_x_vault"], + bump + )] + pub token_x_vault: InterfaceAccount<'info, TokenAccount>, + + #[account( + init_if_needed, + payer = authority, + token::mint = mint_y, + token::authority = lending_vault, + token::token_program = token_program, + seeds = [b"token_y_vault"], + bump )] - pub sol_vault: SystemAccount<'info>, + pub token_y_vault: InterfaceAccount<'info, TokenAccount>, + // mint_x = NATIVE_MINT for WSOL + pub mint_x: InterfaceAccount<'info, Mint>, + pub mint_y: InterfaceAccount<'info, Mint>, + pub token_program: Interface<'info, TokenInterface>, pub system_program: Program<'info, System>, } impl<'info> InitializeLendingVault<'info> { pub fn initialize_lending_vault(&mut self, bumps: &InitializeLendingVaultBumps) -> Result<()> { - let rent_exempt = Rent::get()?.minimum_balance( - self.sol_vault.to_account_info().data_len() - ); - let accounts = Transfer { - from: self.authority.to_account_info(), - to: self.sol_vault.to_account_info(), - }; - - let ctx = CpiContext::new( - self.system_program.to_account_info(), - accounts, - ); - transfer(ctx, rent_exempt)?; - self.lending_vault.set_inner(LendingVault { authority: self.authority.key(), total_supplied: 0, total_borrowed: 0, interest_rate_bps: 30, // Let's update that later to be dynamic last_update: Clock::get()?.unix_timestamp, - bump: bumps.lending_vault, - vault_bump: bumps.sol_vault, + vault_bump: bumps.lending_vault, + x_vault_bump:bumps.token_x_vault, + y_vault_bump:bumps.token_y_vault, }); Ok(()) } diff --git a/programs/metlev-engine/src/instructions/liquidate.rs b/programs/metlev-engine/src/instructions/liquidate.rs index 91316f1..252e966 100644 --- a/programs/metlev-engine/src/instructions/liquidate.rs +++ b/programs/metlev-engine/src/instructions/liquidate.rs @@ -24,7 +24,7 @@ pub struct Liquidate<'info> { #[account( mut, seeds = [LendingVault::SEED_PREFIX], - bump = lending_vault.bump, + bump = lending_vault.vault_bump, )] pub lending_vault: Account<'info, LendingVault>, diff --git a/programs/metlev-engine/src/instructions/open_position.rs b/programs/metlev-engine/src/instructions/open_position.rs index 7b00ee9..4dda473 100644 --- a/programs/metlev-engine/src/instructions/open_position.rs +++ b/programs/metlev-engine/src/instructions/open_position.rs @@ -25,7 +25,7 @@ pub struct OpenPosition<'info> { #[account( mut, seeds = [LendingVault::SEED_PREFIX], - bump = lending_vault.bump, + bump = lending_vault.vault_bump, )] pub lending_vault: Account<'info, LendingVault>, diff --git a/programs/metlev-engine/src/instructions/supply.rs b/programs/metlev-engine/src/instructions/supply.rs index 1ea33ee..78fccb2 100644 --- a/programs/metlev-engine/src/instructions/supply.rs +++ b/programs/metlev-engine/src/instructions/supply.rs @@ -17,7 +17,7 @@ pub struct Supply<'info> { #[account( mut, seeds = [LendingVault::SEED_PREFIX], - bump = lending_vault.bump + bump = lending_vault.vault_bump )] pub lending_vault: Account<'info, LendingVault>, diff --git a/programs/metlev-engine/src/instructions/withdraw.rs b/programs/metlev-engine/src/instructions/withdraw.rs index ad808ed..6c49ed3 100644 --- a/programs/metlev-engine/src/instructions/withdraw.rs +++ b/programs/metlev-engine/src/instructions/withdraw.rs @@ -20,7 +20,7 @@ pub struct Withdraw<'info> { #[account( mut, seeds = [LendingVault::SEED_PREFIX], - bump = lending_vault.bump, + bump = lending_vault.vault_bump, )] pub lending_vault: Account<'info, LendingVault>, diff --git a/programs/metlev-engine/src/state/lending_vault.rs b/programs/metlev-engine/src/state/lending_vault.rs index 427d242..71b0cd3 100644 --- a/programs/metlev-engine/src/state/lending_vault.rs +++ b/programs/metlev-engine/src/state/lending_vault.rs @@ -12,8 +12,9 @@ pub struct LendingVault { pub interest_rate_bps: u16, /// Last time interest was accrued pub last_update: i64, - pub bump: u8, pub vault_bump: u8, + pub x_vault_bump: u8, + pub y_vault_bump: u8, } impl LendingVault { From adb41a6a7c13da61b1575abec2cde327e1c9e448 Mon Sep 17 00:00:00 2001 From: amalnathsathyan Date: Wed, 25 Feb 2026 17:35:45 +0530 Subject: [PATCH 2/4] Added Support For Token_X and Token_Y for LendingVault, Changed supply_lp logic --- Cargo.lock | 88 +++++----- programs/metlev-engine/src/errors.rs | 3 + .../metlev-engine/src/instructions/mod.rs | 8 +- .../metlev-engine/src/instructions/supply.rs | 78 --------- .../src/instructions/supply_vault_lp.rs | 154 ++++++++++++++++++ .../src/instructions/withdraw.rs | 79 --------- .../metlev-engine/src/state/lending_vault.rs | 7 +- .../metlev-engine/src/state/lp_position.rs | 27 ++- programs/metlev-engine/src/utils/constants.rs | 4 + programs/metlev-engine/src/utils/mod.rs | 2 + 10 files changed, 236 insertions(+), 214 deletions(-) delete mode 100644 programs/metlev-engine/src/instructions/supply.rs create mode 100644 programs/metlev-engine/src/instructions/supply_vault_lp.rs delete mode 100644 programs/metlev-engine/src/instructions/withdraw.rs create mode 100644 programs/metlev-engine/src/utils/constants.rs diff --git a/Cargo.lock b/Cargo.lock index 5349229..0b07693 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -404,7 +404,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -471,7 +471,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -609,7 +609,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -944,7 +944,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -984,7 +984,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -1078,9 +1078,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -1096,9 +1096,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.41" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ "proc-macro2", ] @@ -1282,7 +1282,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -1487,7 +1487,7 @@ dependencies = [ "curve25519-dalek", "solana-define-syscall", "subtle", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -1561,7 +1561,7 @@ dependencies = [ "solana-pubkey", "solana-sdk-ids", "solana-system-interface", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -1872,7 +1872,7 @@ dependencies = [ "solana-sysvar", "solana-sysvar-id", "solana-vote-interface", - "thiserror 2.0.17", + "thiserror 2.0.18", "wasm-bindgen", ] @@ -1991,7 +1991,7 @@ dependencies = [ "bs58", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -2002,7 +2002,7 @@ checksum = "baa3120b6cdaa270f39444f5093a90a7b03d296d362878f7a6991d6de3bbe496" dependencies = [ "libsecp256k1", "solana-define-syscall", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -2277,7 +2277,7 @@ dependencies = [ "solana-signature", "solana-signer", "subtle", - "thiserror 2.0.17", + "thiserror 2.0.18", "wasm-bindgen", "zeroize", ] @@ -2295,7 +2295,7 @@ dependencies = [ "spl-associated-token-account-client", "spl-token", "spl-token-2022", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -2328,7 +2328,7 @@ checksum = "d9e8418ea6269dcfb01c712f0444d2c75542c04448b480e87de59d2865edc750" dependencies = [ "quote", "spl-discriminator-syn", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -2340,7 +2340,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.9", - "syn 2.0.106", + "syn 2.0.117", "thiserror 1.0.69", ] @@ -2398,7 +2398,7 @@ dependencies = [ "solana-program-option", "solana-pubkey", "solana-zk-sdk", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -2413,7 +2413,7 @@ dependencies = [ "solana-msg", "solana-program-error", "spl-program-error-derive", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -2425,7 +2425,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.9", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -2447,7 +2447,7 @@ dependencies = [ "spl-pod", "spl-program-error", "spl-type-length-value", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -2475,7 +2475,7 @@ dependencies = [ "solana-rent", "solana-sdk-ids", "solana-sysvar", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -2519,7 +2519,7 @@ dependencies = [ "spl-token-metadata-interface", "spl-transfer-hook-interface", "spl-type-length-value", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -2551,7 +2551,7 @@ dependencies = [ "solana-sdk-ids", "solana-zk-sdk", "spl-pod", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -2562,7 +2562,7 @@ checksum = "fa27b9174bea869a7ebf31e0be6890bce90b1a4288bc2bbf24bd413f80ae3fde" dependencies = [ "curve25519-dalek", "solana-zk-sdk", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -2581,7 +2581,7 @@ dependencies = [ "solana-pubkey", "spl-discriminator", "spl-pod", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -2602,7 +2602,7 @@ dependencies = [ "spl-discriminator", "spl-pod", "spl-type-length-value", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -2627,7 +2627,7 @@ dependencies = [ "spl-program-error", "spl-tlv-account-resolution", "spl-type-length-value", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -2645,7 +2645,7 @@ dependencies = [ "solana-program-error", "spl-discriminator", "spl-pod", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -2667,9 +2667,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.106" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -2687,11 +2687,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.18", ] [[package]] @@ -2702,18 +2702,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -2890,7 +2890,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", "wasm-bindgen-shared", ] @@ -2912,7 +2912,7 @@ checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2968,7 +2968,7 @@ checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -2988,5 +2988,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] diff --git a/programs/metlev-engine/src/errors.rs b/programs/metlev-engine/src/errors.rs index 1dd9abe..1f890f7 100644 --- a/programs/metlev-engine/src/errors.rs +++ b/programs/metlev-engine/src/errors.rs @@ -29,6 +29,9 @@ pub enum ProtocolError { #[msg("Lending vault has insufficient liquidity")] InsufficientLiquidity, + #[msg("Error in provided Pubkeys")] + PubkeyMismatch, + #[msg("Oracle price is stale or invalid")] OracleStale, diff --git a/programs/metlev-engine/src/instructions/mod.rs b/programs/metlev-engine/src/instructions/mod.rs index 0ffe92b..5909047 100644 --- a/programs/metlev-engine/src/instructions/mod.rs +++ b/programs/metlev-engine/src/instructions/mod.rs @@ -7,8 +7,8 @@ pub mod open_position; pub mod close_position; pub mod liquidate; pub mod update_config; -pub mod supply; -pub mod withdraw; +pub mod supply_vault_lp; +pub mod withdraw_vault_lp; pub use initialize::*; pub use initialize_lending_vault::*; @@ -19,5 +19,5 @@ pub use open_position::*; pub use close_position::*; pub use liquidate::*; pub use update_config::*; -pub use supply::*; -pub use withdraw::*; +pub use supply_vault_lp::*; +pub use withdraw_vault_lp::*; diff --git a/programs/metlev-engine/src/instructions/supply.rs b/programs/metlev-engine/src/instructions/supply.rs deleted file mode 100644 index 78fccb2..0000000 --- a/programs/metlev-engine/src/instructions/supply.rs +++ /dev/null @@ -1,78 +0,0 @@ -use anchor_lang::{ - prelude::*, - system_program::{ - transfer, - Transfer - } -}; -use crate::state::{LendingVault}; -use crate::state::LpPosition; -use crate::errors::ProtocolError; - -#[derive(Accounts)] -pub struct Supply<'info> { - #[account(mut)] - pub signer: Signer<'info>, - - #[account( - mut, - seeds = [LendingVault::SEED_PREFIX], - bump = lending_vault.vault_bump - )] - pub lending_vault: Account<'info, LendingVault>, - - #[account( - mut, - seeds = [b"sol_vault", lending_vault.key().as_ref()], - bump = lending_vault.vault_bump - )] - pub sol_vault: SystemAccount<'info>, - - #[account( - init_if_needed, - payer = signer, - space = LpPosition::DISCRIMINATOR.len() + LpPosition::INIT_SPACE, - seeds = [b"lp_position", signer.key().as_ref()], - bump, - )] - pub lp_position: Account<'info, LpPosition>, - - pub system_program: Program<'info, System>, -} - -impl<'info> Supply<'info> { - pub fn supply(&mut self, bumps: &SupplyBumps, amount: u64) -> Result<()> { - let current_time = Clock::get()?.unix_timestamp; - - if self.lp_position.lp == Pubkey::default() { - self.lp_position.lp = self.signer.key(); - self.lp_position.last_update = current_time; - self.lp_position.bump = bumps.lp_position; - } else { - self.lp_position.accrue_interest( - self.lending_vault.interest_rate_bps, - current_time, - ); - } - - self.lp_position.supplied_amount = self.lp_position.supplied_amount - .checked_add(amount) - .ok_or(ProtocolError::MathOverflow)?; - - self.lending_vault.total_supplied = self.lending_vault.total_supplied - .checked_add(amount) - .ok_or(ProtocolError::MathOverflow)?; - - let accounts = Transfer { - from: self.signer.to_account_info(), - to: self.sol_vault.to_account_info() - }; - - let ctx = CpiContext::new( - self.system_program.to_account_info(), - accounts, - ); - - transfer(ctx, amount) - } -} \ No newline at end of file diff --git a/programs/metlev-engine/src/instructions/supply_vault_lp.rs b/programs/metlev-engine/src/instructions/supply_vault_lp.rs new file mode 100644 index 0000000..ef1c9f4 --- /dev/null +++ b/programs/metlev-engine/src/instructions/supply_vault_lp.rs @@ -0,0 +1,154 @@ +use anchor_lang::prelude::*; +use anchor_lang::system_program::{Transfer, transfer}; +use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface, TransferChecked, transfer_checked, sync_native, SyncNative}; +use crate::state::{LendingVault}; +use crate::state::LpPosition; +use crate::errors::ProtocolError; +use crate::utils::constants::*; + +#[derive(Accounts)] +pub struct Supply<'info> { + #[account(mut)] + pub signer: Signer<'info>, + + #[account( + mut, + seeds = [LendingVault::SEED_PREFIX], + bump = lending_vault.vault_bump + )] + pub lending_vault: Account<'info, LendingVault>, + + #[account( + token::mint = mint_x, + token::authority = lending_vault, + token::token_program = token_program, + seeds = [b"token_x_vault"], + bump + )] + pub token_x_vault: InterfaceAccount<'info, TokenAccount>, + + #[account( + token::mint = mint_y, + token::authority = lending_vault, + token::token_program = token_program, + seeds = [b"token_y_vault"], + bump + )] + pub token_y_vault: InterfaceAccount<'info, TokenAccount>, + + #[account( + init_if_needed, + payer = signer, + space = LpPosition::DISCRIMINATOR.len() + LpPosition::INIT_SPACE, + seeds = [b"lp_position", signer.key().as_ref()], + bump, + )] + pub lp_position: Account<'info, LpPosition>, + // mint_x = NATIVE_MINT for WSOL + pub mint_x: InterfaceAccount<'info, Mint>, + pub mint_y: InterfaceAccount<'info, Mint>, + ///Check: SPL token interface will check this + pub user_x_ata: UncheckedAccount<'info>, + ///Check: SPL token interface will check this + pub user_y_ata: UncheckedAccount<'info>, + pub token_program: Interface<'info, TokenInterface>, + pub system_program: Program<'info, System>, +} + +impl<'info> Supply<'info> { + pub fn supply(&mut self, bumps: &SupplyBumps, amount_x: u64, amount_y:u64) -> Result<()> { + let current_time = Clock::get()?.unix_timestamp; + + require_neq!(self.mint_x.key(), self.mint_y.key(), ProtocolError::PubkeyMismatch); + require!(!(self.mint_x.key()==Pubkey::default() && self.mint_x.key()==Pubkey::default()), ProtocolError::PubkeyMismatch); + + if self.lp_position.lp == Pubkey::default() { + self.lp_position.lp = self.signer.key(); + self.lp_position.last_update = current_time; + self.lp_position.bump = bumps.lp_position; + } else { + self.lp_position.accrue_interest( + self.lending_vault.interest_rate_bps, + current_time, + ); + } + + self.lp_position.supplied_amount_x = self.lp_position.supplied_amount_x + .checked_add(amount_x) + .ok_or(ProtocolError::MathOverflow)?; + + self.lp_position.supplied_amount_y = self.lp_position.supplied_amount_y + .checked_add(amount_y) + .ok_or(ProtocolError::MathOverflow)?; + + self.lending_vault.total_supplied_x = self.lending_vault.total_supplied_x + .checked_add(amount_x) + .ok_or(ProtocolError::MathOverflow)?; + self.lending_vault.total_supplied_y = self.lending_vault.total_supplied_y + .checked_add(amount_y) + .ok_or(ProtocolError::MathOverflow)?; + + let is_native_x = self.mint_x.key() == NATIVE_MINT_ID; + let is_native_y = self.mint_y.key() == NATIVE_MINT_ID; + + // Transfer Logic for X + self.handle_transfer( + is_native_x, + amount_x, + &self.user_x_ata.to_account_info(), + &self.token_x_vault.to_account_info(), + &self.mint_x + )?; + + // Transfer Logic for Y + self.handle_transfer( + is_native_y, + amount_y, + &self.user_y_ata.to_account_info(), + &self.token_y_vault.to_account_info(), + &self.mint_y + )?; + + Ok(()) + } + + //Helper Function For Handling Native & SPL transfers + fn handle_transfer( + &self, + is_native:bool, + amount: u64, + user_token_info: &AccountInfo<'info>, + vault_info: &AccountInfo<'info>, + mint: &InterfaceAccount<'info, Mint> + ) -> Result<()> { + if amount == 0 { + return Ok(()) + }; + if is_native { + //Transfer Raw SOL to Vault token account + let native_transfer_accounts = Transfer { + from: self.signer.to_account_info(), + to: vault_info.clone() + }; + let native_transfer_ctx = CpiContext::new(self.system_program.to_account_info(), native_transfer_accounts); + transfer(native_transfer_ctx, amount)?; + // 2. Sync Native to wrap it instantly inside the Vault + let sync_native_ctx = CpiContext::new(self.token_program.to_account_info(), SyncNative { + account: vault_info.clone() + }); + sync_native(sync_native_ctx)?; + + } else { + let spl_transfer_accounts = TransferChecked { + from: user_token_info.clone(), + to: vault_info.clone(), + mint: mint.to_account_info(), + authority: self.signer.to_account_info() + }; + let spl_transfer_ctx = CpiContext::new(self.token_program.to_account_info(), spl_transfer_accounts); + transfer_checked(spl_transfer_ctx, amount, mint.decimals)?; + } + Ok(()) + } + +} diff --git a/programs/metlev-engine/src/instructions/withdraw.rs b/programs/metlev-engine/src/instructions/withdraw.rs deleted file mode 100644 index 6c49ed3..0000000 --- a/programs/metlev-engine/src/instructions/withdraw.rs +++ /dev/null @@ -1,79 +0,0 @@ -use anchor_lang::{prelude::*, system_program::{Transfer, transfer}}; - -use crate::state::{LpPosition, LendingVault}; -use crate::errors::ProtocolError; - -#[derive(Accounts)] -pub struct Withdraw<'info> { - #[account(mut)] - pub signer: Signer<'info>, - - #[account( - mut, - close = signer, - seeds = [LpPosition::SEED_PREFIX, signer.key().as_ref()], - bump = lp_position.bump, - constraint = lp_position.lp == signer.key() @ ProtocolError::InvalidOwner, - )] - pub lp_position: Account<'info, LpPosition>, - - #[account( - mut, - seeds = [LendingVault::SEED_PREFIX], - bump = lending_vault.vault_bump, - )] - pub lending_vault: Account<'info, LendingVault>, - - #[account( - mut, - seeds = [b"sol_vault", lending_vault.key().as_ref()], - bump = lending_vault.vault_bump, - )] - pub sol_vault: SystemAccount<'info>, - - pub system_program: Program<'info, System>, - -} - -impl<'info> Withdraw<'info> { - pub fn withdraw(&mut self) -> Result<()> { - - // TODO update this later for an algo based on the supply and demande dynamic APY - self.lp_position.accrue_interest( - self.lending_vault.interest_rate_bps, - - Clock::get()?.unix_timestamp - ); - let amount = self.lp_position.claimable(); - - - let rent_exempt = Rent::get()?.minimum_balance(0); - require!( - self.sol_vault.get_lamports() >= amount + rent_exempt, - ProtocolError::InsufficientLiquidity - ); - - self.lending_vault.total_supplied = self.lending_vault.total_supplied - .checked_sub(self.lp_position.supplied_amount) - .ok_or(ProtocolError::MathUnderflow)?; - - let accounts = Transfer{ - from: self.sol_vault.to_account_info(), - to: self.signer.to_account_info() - }; - let lending_vault_key = self.lending_vault.key(); - let signer_seeds: &[&[&[u8]]] = &[&[ - b"sol_vault", - lending_vault_key.as_ref(), - &[self.lending_vault.vault_bump], - ] - ]; - let ctx = CpiContext::new_with_signer( - self.system_program.to_account_info(), - accounts, - signer_seeds - ); - - transfer(ctx, amount) - } -} \ No newline at end of file diff --git a/programs/metlev-engine/src/state/lending_vault.rs b/programs/metlev-engine/src/state/lending_vault.rs index 71b0cd3..822fc51 100644 --- a/programs/metlev-engine/src/state/lending_vault.rs +++ b/programs/metlev-engine/src/state/lending_vault.rs @@ -6,9 +6,12 @@ use anchor_lang::prelude::*; pub struct LendingVault { /// Vault authority (program PDA) pub authority: Pubkey, - pub total_supplied: u64, - pub total_borrowed: u64, + pub total_supplied_x: u64, + pub total_supplied_y:u64, + pub total_borrowed_x: u64, + pub total_borrowed_y: u64, /// Simple interest rate (basis points per year, 500 = 5%) + /// MVP:: interest rate same for both X & Y pub interest_rate_bps: u16, /// Last time interest was accrued pub last_update: i64, diff --git a/programs/metlev-engine/src/state/lp_position.rs b/programs/metlev-engine/src/state/lp_position.rs index 8448b9f..b9580ce 100644 --- a/programs/metlev-engine/src/state/lp_position.rs +++ b/programs/metlev-engine/src/state/lp_position.rs @@ -6,11 +6,17 @@ pub struct LpPosition { /// LP provider wallet pub lp: Pubkey, - /// Total SOL supplied (in lamports) - pub supplied_amount: u64, + /// Total SOL/TOKEN_X supplied (in lamports) + pub supplied_amount_x: u64, + + // TOTAL TOKEN_Y + pub supplied_amount_y: u64, /// Accumulated interest earned so far (in lamports) - pub interest_earned: u64, + pub interest_earned_x: u64, + + /// Accumulated interest earned so far (in lamports) + pub interest_earned_y: u64, /// Last time interest was accrued (unix timestamp) pub last_update: i64, @@ -24,19 +30,26 @@ impl LpPosition { /// Accrue simple annual interest based on elapsed time and update state. /// interest = principal * rate_bps * elapsed_seconds / (365 * 24 * 3600 * 10000) + /// Interest Rate Same for Token_x and Token_Y pub fn accrue_interest(&mut self, interest_rate_bps: u16, current_time: i64) { let elapsed = (current_time - self.last_update).max(0) as u128; - let interest = (self.supplied_amount as u128) + let interest_x = (self.supplied_amount_x as u128) + .saturating_mul(interest_rate_bps as u128) + .saturating_mul(elapsed) + / (365u128 * 24 * 3600 * 10000); + + let interest_y = (self.supplied_amount_y as u128) .saturating_mul(interest_rate_bps as u128) .saturating_mul(elapsed) / (365u128 * 24 * 3600 * 10000); - self.interest_earned = self.interest_earned.saturating_add(interest as u64); + self.interest_earned_x = self.interest_earned_x.saturating_add(interest_x as u64); + self.interest_earned_y = self.interest_earned_y.saturating_add(interest_y as u64); self.last_update = current_time; } /// Total claimable amount (principal + accrued interest) - pub fn claimable(&self) -> u64 { - self.supplied_amount.saturating_add(self.interest_earned) + pub fn claimable(&self) -> (u64,u64) { + (self.supplied_amount_x.saturating_add(self.interest_earned_x),self.supplied_amount_y.saturating_add(self.interest_earned_y)) } } diff --git a/programs/metlev-engine/src/utils/constants.rs b/programs/metlev-engine/src/utils/constants.rs new file mode 100644 index 0000000..1ab590b --- /dev/null +++ b/programs/metlev-engine/src/utils/constants.rs @@ -0,0 +1,4 @@ +use anchor_lang::prelude::*; + + +pub const NATIVE_MINT_ID:Pubkey = pubkey!("So11111111111111111111111111111111111111112"); \ No newline at end of file diff --git a/programs/metlev-engine/src/utils/mod.rs b/programs/metlev-engine/src/utils/mod.rs index 5c53e78..bbd6a64 100644 --- a/programs/metlev-engine/src/utils/mod.rs +++ b/programs/metlev-engine/src/utils/mod.rs @@ -1,5 +1,7 @@ pub mod health; pub mod oracle; +pub mod constants; pub use health::*; pub use oracle::*; +pub use constants::*; From 40f577e155a08161446932b7e2b670aac7b0b104 Mon Sep 17 00:00:00 2001 From: amalnathsathyan Date: Wed, 25 Feb 2026 17:36:33 +0530 Subject: [PATCH 3/4] Test modification, Withdraw are pending --- .../src/instructions/withdraw_vault_lp.rs | 94 +++++++++++++++++++ tests/lending_vault.ts | 45 +++++++-- 2 files changed, 129 insertions(+), 10 deletions(-) create mode 100644 programs/metlev-engine/src/instructions/withdraw_vault_lp.rs diff --git a/programs/metlev-engine/src/instructions/withdraw_vault_lp.rs b/programs/metlev-engine/src/instructions/withdraw_vault_lp.rs new file mode 100644 index 0000000..d3c5fb3 --- /dev/null +++ b/programs/metlev-engine/src/instructions/withdraw_vault_lp.rs @@ -0,0 +1,94 @@ +use anchor_lang::{prelude::*, system_program::{Transfer, transfer}}; +use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface}; +use crate::state::{LpPosition, LendingVault}; +use crate::errors::ProtocolError; + +#[derive(Accounts)] +pub struct Withdraw<'info> { + #[account(mut)] + pub signer: Signer<'info>, + + #[account( + mut, + close = signer, + seeds = [LpPosition::SEED_PREFIX, signer.key().as_ref()], + bump = lp_position.bump, + constraint = lp_position.lp == signer.key() @ ProtocolError::InvalidOwner, + )] + pub lp_position: Account<'info, LpPosition>, + + #[account( + mut, + seeds = [LendingVault::SEED_PREFIX], + bump = lending_vault.vault_bump, + )] + pub lending_vault: Account<'info, LendingVault>, + + #[account( + token::mint = mint_x, + token::authority = lending_vault, + token::token_program = token_program, + seeds = [b"token_x_vault"], + bump + )] + pub token_x_vault: InterfaceAccount<'info, TokenAccount>, + + #[account( + token::mint = mint_y, + token::authority = lending_vault, + token::token_program = token_program, + seeds = [b"token_y_vault"], + bump + )] + pub token_y_vault: InterfaceAccount<'info, TokenAccount>, + + // mint_x = NATIVE_MINT for WSOL + pub mint_x: InterfaceAccount<'info, Mint>, + pub mint_y: InterfaceAccount<'info, Mint>, + pub token_program: Interface<'info, TokenInterface>, + pub system_program: Program<'info, System>, + +} + +impl<'info> Withdraw<'info> { + pub fn withdraw(&mut self) -> Result<()> { + + // TODO update this later for an algo based on the supply and demande dynamic APY + self.lp_position.accrue_interest( + self.lending_vault.interest_rate_bps, + + Clock::get()?.unix_timestamp + ); + let amount = self.lp_position.claimable(); + + + let rent_exempt = Rent::get()?.minimum_balance(0); + require!( + self.sol_vault.get_lamports() >= amount + rent_exempt, + ProtocolError::InsufficientLiquidity + ); + + self.lending_vault.total_supplied = self.lending_vault.total_supplied + .checked_sub(self.lp_position.supplied_amount) + .ok_or(ProtocolError::MathUnderflow)?; + + let accounts = Transfer{ + from: self.sol_vault.to_account_info(), + to: self.signer.to_account_info() + }; + let lending_vault_key = self.lending_vault.key(); + let signer_seeds: &[&[&[u8]]] = &[&[ + b"sol_vault", + lending_vault_key.as_ref(), + &[self.lending_vault.vault_bump], + ] + ]; + let ctx = CpiContext::new_with_signer( + self.system_program.to_account_info(), + accounts, + signer_seeds + ); + + transfer(ctx, amount) + } +} \ No newline at end of file diff --git a/tests/lending_vault.ts b/tests/lending_vault.ts index e3cad36..6012c4a 100644 --- a/tests/lending_vault.ts +++ b/tests/lending_vault.ts @@ -1,7 +1,14 @@ import * as anchor from "@coral-xyz/anchor"; import { Program } from "@coral-xyz/anchor"; import { MetlevEngine } from "../target/types/metlev_engine"; -import { PublicKey, Keypair, SystemProgram, LAMPORTS_PER_SOL } from "@solana/web3.js"; +import { PublicKey, Keypair, SystemProgram, LAMPORTS_PER_SOL, } from "@solana/web3.js"; +import { + TOKEN_PROGRAM_ID, + createMint, + createAccount, + mintTo, + getAccount, +} from "@solana/spl-token"; import { expect } from "chai"; describe("Lending Vault", () => { @@ -16,7 +23,12 @@ describe("Lending Vault", () => { let configPda: PublicKey; let lendingVaultPda: PublicKey; - let solVaultPda: PublicKey; + let tokenXVaultPda: PublicKey; + let tokenYVaultPda: PublicKey; + + const SOL_MINT = new PublicKey("So11111111111111111111111111111111111111112"); + let USDC_MINT: PublicKey; + before(async () => { [configPda] = PublicKey.findProgramAddressSync( @@ -29,11 +41,23 @@ describe("Lending Vault", () => { program.programId ); - [solVaultPda] = PublicKey.findProgramAddressSync( - [Buffer.from("sol_vault"), lendingVaultPda.toBuffer()], + [tokenXVaultPda] = PublicKey.findProgramAddressSync( + [Buffer.from("token_x_vault")], + program.programId + ); + [tokenYVaultPda] = PublicKey.findProgramAddressSync( + [Buffer.from("token_y_vault")], program.programId ); + USDC_MINT = await createMint( + provider.connection, + provider.wallet.payer, + authority, + null, + 6 + ); + for (const user of [lp, lp2]) { const sig = await provider.connection.requestAirdrop( user.publicKey, @@ -66,7 +90,11 @@ describe("Lending Vault", () => { authority, config: configPda, lendingVault: lendingVaultPda, - solVault: solVaultPda, + tokenXVault: tokenXVaultPda, + tokenYVault: tokenYVaultPda, + mintX: SOL_MINT, + mintY: USDC_MINT, + tokenProgram: TOKEN_PROGRAM_ID, systemProgram: SystemProgram.programId, }) .rpc(); @@ -75,7 +103,8 @@ describe("Lending Vault", () => { console.log("\n=== Setup Complete ==="); console.log("Lending Vault PDA:", lendingVaultPda.toBase58()); - console.log("SOL Vault PDA:", solVaultPda.toBase58()); + console.log("WSOL Vault PDA:", tokenXVaultPda.toBase58()); + console.log("USDC Vault PDA:", tokenYVaultPda.toBase58()); }); describe("Initialize Lending Vault", () => { @@ -86,11 +115,7 @@ describe("Lending Vault", () => { expect(vault.totalSupplied.toNumber()).to.equal(0); expect(vault.totalBorrowed.toNumber()).to.equal(0); - const solVaultBalance = await provider.connection.getBalance(solVaultPda); - expect(solVaultBalance).to.be.greaterThan(0); - console.log("Vault authority:", vault.authority.toBase58()); - console.log("SOL vault balance (rent):", solVaultBalance, "lamports"); }); }); From c2135d86e42ddeebf49976612120b23fe40c208c Mon Sep 17 00:00:00 2001 From: amalnathsathyan Date: Thu, 26 Feb 2026 11:47:55 +0530 Subject: [PATCH 4/4] Removed token_y, modified to more granular token_vault --- .../instructions/initialize_lending_vault.rs | 21 ++------ .../src/instructions/supply_vault_lp.rs | 51 ++++--------------- .../src/instructions/withdraw_vault_lp.rs | 31 +++++------ .../metlev-engine/src/state/lending_vault.rs | 23 +++++---- .../metlev-engine/src/state/lp_position.rs | 26 +++------- 5 files changed, 47 insertions(+), 105 deletions(-) diff --git a/programs/metlev-engine/src/instructions/initialize_lending_vault.rs b/programs/metlev-engine/src/instructions/initialize_lending_vault.rs index 3f202fe..81c5fb1 100644 --- a/programs/metlev-engine/src/instructions/initialize_lending_vault.rs +++ b/programs/metlev-engine/src/instructions/initialize_lending_vault.rs @@ -26,30 +26,18 @@ pub struct InitializeLendingVault<'info>{ pub lending_vault: Account<'info, LendingVault>, #[account( - init_if_needed, + init, payer = authority, token::mint = mint_x, token::authority = lending_vault, token::token_program = token_program, - seeds = [b"token_x_vault"], - bump - )] - pub token_x_vault: InterfaceAccount<'info, TokenAccount>, - - #[account( - init_if_needed, - payer = authority, - token::mint = mint_y, - token::authority = lending_vault, - token::token_program = token_program, - seeds = [b"token_y_vault"], + seeds = [b"token_vault"], bump )] - pub token_y_vault: InterfaceAccount<'info, TokenAccount>, + pub token_vault: InterfaceAccount<'info, TokenAccount>, // mint_x = NATIVE_MINT for WSOL pub mint_x: InterfaceAccount<'info, Mint>, - pub mint_y: InterfaceAccount<'info, Mint>, pub token_program: Interface<'info, TokenInterface>, pub system_program: Program<'info, System>, } @@ -63,8 +51,7 @@ impl<'info> InitializeLendingVault<'info> { interest_rate_bps: 30, // Let's update that later to be dynamic last_update: Clock::get()?.unix_timestamp, vault_bump: bumps.lending_vault, - x_vault_bump:bumps.token_x_vault, - y_vault_bump:bumps.token_y_vault, + token_vault_bump: bumps.token_vault, }); Ok(()) } diff --git a/programs/metlev-engine/src/instructions/supply_vault_lp.rs b/programs/metlev-engine/src/instructions/supply_vault_lp.rs index ef1c9f4..65121bf 100644 --- a/programs/metlev-engine/src/instructions/supply_vault_lp.rs +++ b/programs/metlev-engine/src/instructions/supply_vault_lp.rs @@ -19,22 +19,14 @@ pub struct Supply<'info> { pub lending_vault: Account<'info, LendingVault>, #[account( + mut, token::mint = mint_x, token::authority = lending_vault, token::token_program = token_program, seeds = [b"token_x_vault"], bump )] - pub token_x_vault: InterfaceAccount<'info, TokenAccount>, - - #[account( - token::mint = mint_y, - token::authority = lending_vault, - token::token_program = token_program, - seeds = [b"token_y_vault"], - bump - )] - pub token_y_vault: InterfaceAccount<'info, TokenAccount>, + pub token_vault: InterfaceAccount<'info, TokenAccount>, #[account( init_if_needed, @@ -46,21 +38,17 @@ pub struct Supply<'info> { pub lp_position: Account<'info, LpPosition>, // mint_x = NATIVE_MINT for WSOL pub mint_x: InterfaceAccount<'info, Mint>, - pub mint_y: InterfaceAccount<'info, Mint>, ///Check: SPL token interface will check this pub user_x_ata: UncheckedAccount<'info>, - ///Check: SPL token interface will check this - pub user_y_ata: UncheckedAccount<'info>, pub token_program: Interface<'info, TokenInterface>, pub system_program: Program<'info, System>, } impl<'info> Supply<'info> { - pub fn supply(&mut self, bumps: &SupplyBumps, amount_x: u64, amount_y:u64) -> Result<()> { + pub fn supply(&mut self, bumps: &SupplyBumps, amount: u64) -> Result<()> { let current_time = Clock::get()?.unix_timestamp; - require_neq!(self.mint_x.key(), self.mint_y.key(), ProtocolError::PubkeyMismatch); - require!(!(self.mint_x.key()==Pubkey::default() && self.mint_x.key()==Pubkey::default()), ProtocolError::PubkeyMismatch); + require_neq!(self.mint_x.key(), Pubkey::default(), ProtocolError::PubkeyMismatch); if self.lp_position.lp == Pubkey::default() { self.lp_position.lp = self.signer.key(); @@ -73,42 +61,25 @@ impl<'info> Supply<'info> { ); } - self.lp_position.supplied_amount_x = self.lp_position.supplied_amount_x - .checked_add(amount_x) - .ok_or(ProtocolError::MathOverflow)?; - - self.lp_position.supplied_amount_y = self.lp_position.supplied_amount_y - .checked_add(amount_y) + self.lp_position.supplied_amount = self.lp_position.supplied_amount + .checked_add(amount) .ok_or(ProtocolError::MathOverflow)?; - self.lending_vault.total_supplied_x = self.lending_vault.total_supplied_x + self.lending_vault.total_supplied = self.lending_vault.total_supplied .checked_add(amount_x) .ok_or(ProtocolError::MathOverflow)?; - self.lending_vault.total_supplied_y = self.lending_vault.total_supplied_y - .checked_add(amount_y) - .ok_or(ProtocolError::MathOverflow)?; - let is_native_x = self.mint_x.key() == NATIVE_MINT_ID; - let is_native_y = self.mint_y.key() == NATIVE_MINT_ID; + let is_native = self.mint_x.key() == NATIVE_MINT_ID; // Transfer Logic for X self.handle_transfer( - is_native_x, - amount_x, + is_native, + amount, &self.user_x_ata.to_account_info(), - &self.token_x_vault.to_account_info(), + &self.token_vault.to_account_info(), &self.mint_x )?; - // Transfer Logic for Y - self.handle_transfer( - is_native_y, - amount_y, - &self.user_y_ata.to_account_info(), - &self.token_y_vault.to_account_info(), - &self.mint_y - )?; - Ok(()) } diff --git a/programs/metlev-engine/src/instructions/withdraw_vault_lp.rs b/programs/metlev-engine/src/instructions/withdraw_vault_lp.rs index d3c5fb3..5971db9 100644 --- a/programs/metlev-engine/src/instructions/withdraw_vault_lp.rs +++ b/programs/metlev-engine/src/instructions/withdraw_vault_lp.rs @@ -25,26 +25,17 @@ pub struct Withdraw<'info> { pub lending_vault: Account<'info, LendingVault>, #[account( + mut, token::mint = mint_x, token::authority = lending_vault, token::token_program = token_program, - seeds = [b"token_x_vault"], + seeds = [b"token_vault"], bump )] - pub token_x_vault: InterfaceAccount<'info, TokenAccount>, - - #[account( - token::mint = mint_y, - token::authority = lending_vault, - token::token_program = token_program, - seeds = [b"token_y_vault"], - bump - )] - pub token_y_vault: InterfaceAccount<'info, TokenAccount>, + pub token_vault: InterfaceAccount<'info, TokenAccount>, // mint_x = NATIVE_MINT for WSOL pub mint_x: InterfaceAccount<'info, Mint>, - pub mint_y: InterfaceAccount<'info, Mint>, pub token_program: Interface<'info, TokenInterface>, pub system_program: Program<'info, System>, @@ -62,24 +53,26 @@ impl<'info> Withdraw<'info> { let amount = self.lp_position.claimable(); - let rent_exempt = Rent::get()?.minimum_balance(0); + // let rent_exempt = Rent::get()?.minimum_balance(0); + //not sure whether it is safer to treat the WSOL as a token account and use token_vault.amount here + // as we have used the sync_native to sync the balances require!( - self.sol_vault.get_lamports() >= amount + rent_exempt, + // self.sol_vault.get_lamports() >= amount + rent_exempt, + self.token_vault.amount >= amount, ProtocolError::InsufficientLiquidity - ); + ); self.lending_vault.total_supplied = self.lending_vault.total_supplied .checked_sub(self.lp_position.supplied_amount) .ok_or(ProtocolError::MathUnderflow)?; let accounts = Transfer{ - from: self.sol_vault.to_account_info(), + from: self.token_vault.to_account_info(), to: self.signer.to_account_info() }; - let lending_vault_key = self.lending_vault.key(); + let signer_seeds: &[&[&[u8]]] = &[&[ - b"sol_vault", - lending_vault_key.as_ref(), + b"token_vault", &[self.lending_vault.vault_bump], ] ]; diff --git a/programs/metlev-engine/src/state/lending_vault.rs b/programs/metlev-engine/src/state/lending_vault.rs index 822fc51..2b2a4bd 100644 --- a/programs/metlev-engine/src/state/lending_vault.rs +++ b/programs/metlev-engine/src/state/lending_vault.rs @@ -6,18 +6,14 @@ use anchor_lang::prelude::*; pub struct LendingVault { /// Vault authority (program PDA) pub authority: Pubkey, - pub total_supplied_x: u64, - pub total_supplied_y:u64, - pub total_borrowed_x: u64, - pub total_borrowed_y: u64, + pub total_supplied: u64, + pub total_borrowed: u64, /// Simple interest rate (basis points per year, 500 = 5%) - /// MVP:: interest rate same for both X & Y pub interest_rate_bps: u16, /// Last time interest was accrued pub last_update: i64, pub vault_bump: u8, - pub x_vault_bump: u8, - pub y_vault_bump: u8, + pub token_vault_bump: u8, } impl LendingVault { @@ -34,15 +30,22 @@ impl LendingVault { } pub fn borrow(&mut self, amount: u64) -> Result<()> { - require!(self.can_borrow(amount), crate::errors::ProtocolError::InsufficientLiquidity); - self.total_borrowed = self.total_borrowed.checked_add(amount) + require!( + self.can_borrow(amount), + crate::errors::ProtocolError::InsufficientLiquidity + ); + self.total_borrowed = self + .total_borrowed + .checked_add(amount) .ok_or(crate::errors::ProtocolError::MathOverflow)?; Ok(()) } /// Record debt repayment pub fn repay(&mut self, amount: u64) -> Result<()> { - self.total_borrowed = self.total_borrowed.checked_sub(amount) + self.total_borrowed = self + .total_borrowed + .checked_sub(amount) .ok_or(crate::errors::ProtocolError::MathOverflow)?; Ok(()) } diff --git a/programs/metlev-engine/src/state/lp_position.rs b/programs/metlev-engine/src/state/lp_position.rs index b9580ce..5172909 100644 --- a/programs/metlev-engine/src/state/lp_position.rs +++ b/programs/metlev-engine/src/state/lp_position.rs @@ -6,17 +6,11 @@ pub struct LpPosition { /// LP provider wallet pub lp: Pubkey, - /// Total SOL/TOKEN_X supplied (in lamports) - pub supplied_amount_x: u64, - - // TOTAL TOKEN_Y - pub supplied_amount_y: u64, + /// Total SOL/TOKEN supplied (in lamports) + pub supplied_amount: u64, /// Accumulated interest earned so far (in lamports) - pub interest_earned_x: u64, - - /// Accumulated interest earned so far (in lamports) - pub interest_earned_y: u64, + pub interest_earned: u64, /// Last time interest was accrued (unix timestamp) pub last_update: i64, @@ -33,23 +27,17 @@ impl LpPosition { /// Interest Rate Same for Token_x and Token_Y pub fn accrue_interest(&mut self, interest_rate_bps: u16, current_time: i64) { let elapsed = (current_time - self.last_update).max(0) as u128; - let interest_x = (self.supplied_amount_x as u128) - .saturating_mul(interest_rate_bps as u128) - .saturating_mul(elapsed) - / (365u128 * 24 * 3600 * 10000); - - let interest_y = (self.supplied_amount_y as u128) + let interest = (self.supplied_amount as u128) .saturating_mul(interest_rate_bps as u128) .saturating_mul(elapsed) / (365u128 * 24 * 3600 * 10000); - self.interest_earned_x = self.interest_earned_x.saturating_add(interest_x as u64); - self.interest_earned_y = self.interest_earned_y.saturating_add(interest_y as u64); + self.interest_earned = self.interest_earned.saturating_add(interest as u64); self.last_update = current_time; } /// Total claimable amount (principal + accrued interest) - pub fn claimable(&self) -> (u64,u64) { - (self.supplied_amount_x.saturating_add(self.interest_earned_x),self.supplied_amount_y.saturating_add(self.interest_earned_y)) + pub fn claimable(&self) -> u64 { + self.supplied_amount.saturating_add(self.interest_earned) } }