Skip to content

Commit

Permalink
feature package
Browse files Browse the repository at this point in the history
  • Loading branch information
DevRozaDev committed Sep 27, 2024
1 parent 293380d commit 9327da8
Show file tree
Hide file tree
Showing 33 changed files with 2,938 additions and 1,050 deletions.
368 changes: 186 additions & 182 deletions protocol/Cargo.lock

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions protocol/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@
"test": "npm run build:all && npx ts-mocha -p tsconfig.json tests/**/*.ts",
"test:init": "anchor test --skip-build tests/init.test.ts",
"test:init-lp-pool": "anchor test --skip-build tests/init-lp-pool.test.ts",
"test:mint": "anchor test --skip-build tests/mint.test.ts",
"test:mint-zero-tick-small": "anchor test --skip-build tests/mint-zero-tick-small.test.ts",
"test:mint-zero-tick-big": "anchor test --skip-build tests/mint-zero-tick-big.test.ts",
"test:mint-high-tick-small": "anchor test --skip-build tests/mint-high-tick-small.test.ts",
"test:mint-high-tick-big": "anchor test --skip-build tests/mint-high-tick-big.test.ts",
"test:burn": "anchor test --skip-build tests/burn.test.ts",
"test:math": "anchor test --skip-build tests/math.test.ts"
"test:math": "anchor test --skip-build tests/math.test.ts",
"test:multi-pool": "anchor test --skip-build tests/multi-pool.test.ts"
},
"keywords": [],
"author": "",
Expand Down
3 changes: 3 additions & 0 deletions protocol/programs/invariant/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ all = []

[dependencies]
decimal = { path = "decimal" }

solana-program = "=1.17.6"
anchor-lang = "0.29.0"
anchor-spl = "0.29.0"

integer-sqrt = "0.1.5"
uint = "0.9.1"
num-traits = "0.2.14"
Expand Down
2 changes: 1 addition & 1 deletion protocol/programs/invariant/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub enum ErrorCode {
InvalidTickLiquidity = 9, // 1779
#[msg("Disable empty position pokes")]
EmptyPositionPokes = 10, // 177a
#[msg("Invalid tick liquidity")]
#[msg("Invalid position liquidity")]
InvalidPositionLiquidity = 11, // 177b
#[msg("Invalid pool liquidity")]
InvalidPoolLiquidity = 12, // 177c
Expand Down
283 changes: 283 additions & 0 deletions protocol/programs/invariant/src/instructions/change_liquidity.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
use crate::interfaces::take_tokens::TakeTokens;
use crate::structs::pool::Pool;
use crate::structs::position::Position;
use crate::structs::tick::Tick;
use crate::ErrorCode::{self, *};
use crate::*;
use anchor_lang::prelude::*;
use anchor_spl::token;
use anchor_spl::token_2022;
use anchor_spl::token_interface::TokenInterface;
use anchor_spl::token_interface::{Mint, TokenAccount};
use decimals::*;
use interfaces::send_tokens::SendTokens;

#[derive(Accounts)]
#[instruction( index: u32)]
pub struct ChangeLiquidity<'info> {
#[account(seeds = [b"statev1".as_ref()], bump = state.load()?.bump)]
pub state: AccountLoader<'info, State>,
#[account(mut,
seeds = [b"positionv1",
owner.key().as_ref(),
&index.to_le_bytes()],
bump = position.load()?.bump,
constraint = position.load()?.pool == pool.key() @ InvalidPositionIndex
)]
pub position: AccountLoader<'info, Position>,
#[account(mut,
seeds = [b"poolv1", token_x.key().as_ref(), token_y.key().as_ref(), &pool.load()?.fee.v.to_le_bytes(), &pool.load()?.tick_spacing.to_le_bytes()],
bump = pool.load()?.bump
)]
pub pool: AccountLoader<'info, Pool>,
#[account(mut)]
pub payer: Signer<'info>,
pub owner: Signer<'info>,
#[account(mut,
seeds = [b"tickv1", pool.key().as_ref(), &position.load()?.lower_tick_index.to_le_bytes()],
bump = lower_tick.load()?.bump
)]
pub lower_tick: AccountLoader<'info, Tick>,
#[account(mut,
seeds = [b"tickv1", pool.key().as_ref(), &position.load()?.upper_tick_index.to_le_bytes()],
bump = upper_tick.load()?.bump
)]
pub upper_tick: AccountLoader<'info, Tick>,
#[account(constraint = token_x.key() == pool.load()?.token_x @ InvalidTokenAccount, mint::token_program = token_x_program)]
pub token_x: InterfaceAccount<'info, Mint>,
#[account(constraint = token_y.key() == pool.load()?.token_y @ InvalidTokenAccount, mint::token_program = token_y_program)]
pub token_y: InterfaceAccount<'info, Mint>,
#[account(mut,
constraint = account_x.mint == token_x.key() @ InvalidMint,
constraint = &account_x.owner == owner.key @ InvalidOwner,
token::token_program = token_x_program,
)]
pub account_x: Box<InterfaceAccount<'info, TokenAccount>>,
#[account(mut,
constraint = account_y.mint == token_y.key() @ InvalidMint,
constraint = &account_y.owner == owner.key @ InvalidOwner,
token::token_program = token_y_program,
)]
pub account_y: Box<InterfaceAccount<'info, TokenAccount>>,
#[account(mut,
constraint = reserve_x.mint == token_x.key() @ InvalidMint,
constraint = &reserve_x.owner == program_authority.key @ InvalidOwner,
constraint = reserve_x.key() == pool.load()?.token_x_reserve @ InvalidTokenAccount,
token::token_program = token_x_program,
)]
pub reserve_x: Box<InterfaceAccount<'info, TokenAccount>>,
#[account(mut,
constraint = reserve_y.mint == token_y.key() @ InvalidMint,
constraint = &reserve_y.owner == program_authority.key @ InvalidOwner,
constraint = reserve_y.key() == pool.load()?.token_y_reserve @ InvalidTokenAccount,
token::token_program = token_y_program,
)]
pub reserve_y: Box<InterfaceAccount<'info, TokenAccount>>,
#[account(constraint = &state.load()?.authority == program_authority.key @ InvalidAuthority)]
/// CHECK: ignore
pub program_authority: AccountInfo<'info>,
pub token_x_program: Interface<'info, TokenInterface>,
pub token_y_program: Interface<'info, TokenInterface>,
}

impl<'info> TakeTokens<'info> for ChangeLiquidity<'info> {
fn take_x(&self) -> CpiContext<'_, '_, '_, 'info, token::Transfer<'info>> {
CpiContext::new(
self.token_x_program.to_account_info(),
token::Transfer {
from: self.account_x.to_account_info(),
to: self.reserve_x.to_account_info(),
authority: self.owner.to_account_info().clone(),
},
)
}

fn take_y(&self) -> CpiContext<'_, '_, '_, 'info, token::Transfer<'info>> {
CpiContext::new(
self.token_y_program.to_account_info(),
token::Transfer {
from: self.account_y.to_account_info(),
to: self.reserve_y.to_account_info(),
authority: self.owner.to_account_info().clone(),
},
)
}

fn take_x_2022(&self) -> CpiContext<'_, '_, '_, 'info, token_2022::TransferChecked<'info>> {
CpiContext::new(
self.token_x_program.to_account_info(),
token_2022::TransferChecked {
mint: self.token_x.to_account_info(),
from: self.account_x.to_account_info(),
to: self.reserve_x.to_account_info(),
authority: self.owner.to_account_info().clone(),
},
)
}

fn take_y_2022(&self) -> CpiContext<'_, '_, '_, 'info, token_2022::TransferChecked<'info>> {
CpiContext::new(
self.token_y_program.to_account_info(),
token_2022::TransferChecked {
mint: self.token_y.to_account_info(),
from: self.account_y.to_account_info(),
to: self.reserve_y.to_account_info(),
authority: self.owner.to_account_info().clone(),
},
)
}
}

impl<'info> SendTokens<'info> for ChangeLiquidity<'info> {
fn send_x(&self) -> CpiContext<'_, '_, '_, 'info, token::Transfer<'info>> {
CpiContext::new(
self.token_x_program.to_account_info(),
token::Transfer {
from: self.reserve_x.to_account_info(),
to: self.account_x.to_account_info(),
authority: self.program_authority.clone(),
},
)
}

fn send_y(&self) -> CpiContext<'_, '_, '_, 'info, token::Transfer<'info>> {
CpiContext::new(
self.token_y_program.to_account_info(),
token::Transfer {
from: self.reserve_y.to_account_info(),
to: self.account_y.to_account_info(),
authority: self.program_authority.clone(),
},
)
}

fn send_x_2022(&self) -> CpiContext<'_, '_, '_, 'info, token_2022::TransferChecked<'info>> {
CpiContext::new(
self.token_x_program.to_account_info(),
token_2022::TransferChecked {
mint: self.token_x.to_account_info(),
from: self.reserve_x.to_account_info(),
to: self.account_x.to_account_info(),
authority: self.program_authority.clone(),
},
)
}

fn send_y_2022(&self) -> CpiContext<'_, '_, '_, 'info, token_2022::TransferChecked<'info>> {
CpiContext::new(
self.token_y_program.to_account_info(),
token_2022::TransferChecked {
mint: self.token_y.to_account_info(),
from: self.reserve_y.to_account_info(),
to: self.account_y.to_account_info(),
authority: self.program_authority.clone(),
},
)
}
}

impl<'info> ChangeLiquidity<'info> {
pub fn handler(
&self,
liquidity_delta: Liquidity,
add_liquidity: bool,
slippage_limit_lower: Price,
slippage_limit_upper: Price,
) -> Result<()> {
msg!("INVARIANT: CHANGE POSITION LIQUIDITY");

let mut position = self.position.load_mut()?;
let pool = &mut self.pool.load_mut()?;
let lower_tick = &mut self.lower_tick.load_mut()?;
let upper_tick = &mut self.upper_tick.load_mut()?;
let current_timestamp = get_current_timestamp();
let slot = get_current_slot();

let liquidity = position.liquidity;
require!(
add_liquidity || liquidity_delta != liquidity,
ErrorCode::PositionWithoutLiquidity
);

require!(liquidity_delta != Liquidity::new(0), ErrorCode::ZeroAmount);

// validate price
let sqrt_price = pool.sqrt_price;
require!(
sqrt_price >= slippage_limit_lower,
ErrorCode::PriceLimitReached
);
require!(
sqrt_price <= slippage_limit_upper,
ErrorCode::PriceLimitReached
);

position.seconds_per_liquidity_inside = calculate_seconds_per_liquidity_inside(
**lower_tick,
**upper_tick,
pool,
current_timestamp,
);
position.last_slot = slot;

let (amount_x, amount_y) = position.modify(
pool,
upper_tick,
lower_tick,
liquidity_delta,
add_liquidity,
current_timestamp,
)?;

require!(
amount_x != TokenAmount::new(0) || amount_y != TokenAmount::new(0),
ErrorCode::ZeroOutput
);

if add_liquidity {
match self.token_x_program.key() {
token_2022::ID => token_2022::transfer_checked(
self.take_x_2022(),
amount_x.0,
self.token_x.decimals,
)?,
token::ID => token::transfer(self.take_x(), amount_x.0)?,
_ => return Err(ErrorCode::InvalidTokenProgram.into()),
};
match self.token_y_program.key() {
token_2022::ID => token_2022::transfer_checked(
self.take_y_2022(),
amount_y.0,
self.token_y.decimals,
)?,
token::ID => token::transfer(self.take_y(), amount_y.0)?,
_ => return Err(ErrorCode::InvalidTokenProgram.into()),
};
} else {
let state = self.state.load()?;
let signer: &[&[&[u8]]] = get_signer!(state.nonce);

match self.token_x_program.key() {
token_2022::ID => token_2022::transfer_checked(
self.send_x_2022().with_signer(signer),
amount_x.0,
self.token_x.decimals,
)?,
token::ID => token::transfer(self.send_x().with_signer(signer), amount_x.0)?,
_ => return Err(ErrorCode::InvalidTokenProgram.into()),
};

match self.token_y_program.key() {
token_2022::ID => token_2022::transfer_checked(
self.send_y_2022().with_signer(signer),
amount_y.0,
self.token_y.decimals,
)?,
token::ID => token::transfer(self.send_y().with_signer(signer), amount_y.0)?,
_ => return Err(ErrorCode::InvalidTokenProgram.into()),
};
}

Ok(())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ impl<'info> CreatePosition<'info> {
bump: u8,
) -> Result<()> {
msg!("INVARIANT: CREATE POSITION");

let mut position = self.position.load_init()?;
let mut pool = &mut self.pool.load_mut()?;
let lower_tick = &mut self.lower_tick.load_mut()?;
Expand Down
2 changes: 2 additions & 0 deletions protocol/programs/invariant/src/instructions/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod change_fee_receiver;
pub mod change_liquidity;
pub mod change_protocol_fee;
pub mod claim_fee;
pub mod create_fee_tier;
Expand All @@ -16,6 +17,7 @@ pub mod update_seconds_per_liquidity;
pub mod withdraw_protocol_fee;

pub use change_fee_receiver::*;
pub use change_liquidity::*;
pub use change_protocol_fee::*;
pub use claim_fee::*;
pub use create_fee_tier::*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub struct RemovePosition<'info> {
)]
pub position_list: AccountLoader<'info, PositionList>,
#[account(mut,
close = owner,
close = payer,
seeds = [b"positionv1",
owner.key().as_ref(),
&(position_list.load()?.head - 1).to_le_bytes()],
Expand Down Expand Up @@ -62,6 +62,7 @@ pub struct RemovePosition<'info> {
)]
pub upper_tick: AccountLoader<'info, Tick>,
#[account(mut)]
pub payer: Signer<'info>,
pub owner: Signer<'info>,
#[account(constraint = token_x.key() == pool.load()?.token_x @ InvalidTokenAccount, mint::token_program = token_x_program)]
pub token_x: InterfaceAccount<'info, Mint>,
Expand Down Expand Up @@ -198,7 +199,7 @@ impl<'info> RemovePosition<'info> {
}
close(
self.lower_tick.to_account_info(),
self.owner.to_account_info(),
self.payer.to_account_info(),
)
.unwrap();

Expand All @@ -211,7 +212,7 @@ impl<'info> RemovePosition<'info> {
}
close(
self.upper_tick.to_account_info(),
self.owner.to_account_info(),
self.payer.to_account_info(),
)
.unwrap();

Expand Down
Loading

0 comments on commit 9327da8

Please sign in to comment.