From 41ad77cc3a60bf3ea91e57bf1d96fae9e923adb6 Mon Sep 17 00:00:00 2001 From: DevRozaDev <158298065+DevRozaDev@users.noreply.github.com> Date: Mon, 19 Aug 2024 19:14:00 +0200 Subject: [PATCH 1/6] mint draft --- protocol/package.json | 3 +- .../protocol/src/contexts/init_pool.rs | 7 +- .../programs/protocol/src/contexts/mint.rs | 281 ++++++++++++ .../programs/protocol/src/contexts/mod.rs | 2 + .../protocol/src/contexts/token/mod.rs | 4 +- protocol/programs/protocol/src/errors.rs | 2 + protocol/programs/protocol/src/lib.rs | 15 +- .../programs/protocol/src/states/lp_pool.rs | 1 + protocol/programs/protocol/src/states/mod.rs | 1 + protocol/sdk/src/idl/protocol.ts | 426 +++++++++++++++--- protocol/sdk/src/protocol.ts | 112 ++++- protocol/sdk/src/types.ts | 25 + protocol/tests.sh | 9 +- protocol/tests/init-lp-pool.test.ts | 2 +- protocol/tests/invoke.test.ts | 2 +- protocol/tests/mint.test.ts | 188 ++++++++ 16 files changed, 975 insertions(+), 105 deletions(-) create mode 100644 protocol/programs/protocol/src/contexts/mint.rs create mode 100644 protocol/tests/mint.test.ts diff --git a/protocol/package.json b/protocol/package.json index 970b21d..cf10133 100644 --- a/protocol/package.json +++ b/protocol/package.json @@ -11,7 +11,8 @@ "test:init": "anchor test --skip-build tests/init.test.ts", "test:token": "anchor test --skip-build tests/token.test.ts", "test:invoke": "anchor test --skip-build tests/invoke.test.ts", - "test:init-lp-pool": "anchor test --skip-build tests/init-lp-pool.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" }, "keywords": [], "author": "", diff --git a/protocol/programs/protocol/src/contexts/init_pool.rs b/protocol/programs/protocol/src/contexts/init_pool.rs index 703a0af..c2801ae 100644 --- a/protocol/programs/protocol/src/contexts/init_pool.rs +++ b/protocol/programs/protocol/src/contexts/init_pool.rs @@ -1,4 +1,4 @@ -use crate::states::{DerivedAccountIdentifier, LpPool, LP_TOKEN_IDENT}; +use crate::states::{DerivedAccountIdentifier, LpPool, INVARIANT_POOL_IDENT, LP_TOKEN_IDENT}; use crate::ErrorCode::*; use anchor_lang::prelude::*; use anchor_spl::associated_token::AssociatedToken; @@ -39,7 +39,7 @@ pub struct InitPoolCtx<'info> { #[account(mut)] pub payer: Signer<'info>, #[account( - 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()], + seeds = [INVARIANT_POOL_IDENT, 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, seeds::program = invariant::ID )] @@ -70,7 +70,7 @@ pub struct InitPoolCtx<'info> { } impl InitPoolCtx<'_> { - pub fn process(&mut self, bump: u8) -> Result<()> { + pub fn process(&mut self, token_bump: u8, bump: u8) -> Result<()> { let token_x = self.token_x.key(); let token_y = self.token_y.key(); let lp_pool = &mut self.lp_pool.load_init()?; @@ -84,6 +84,7 @@ impl InitPoolCtx<'_> { token_y, tick_spacing: pool.tick_spacing, fee: crate::decimals::FixedPoint::new(pool.fee.v), + token_bump, bump, }; diff --git a/protocol/programs/protocol/src/contexts/mint.rs b/protocol/programs/protocol/src/contexts/mint.rs new file mode 100644 index 0000000..7fd9ee1 --- /dev/null +++ b/protocol/programs/protocol/src/contexts/mint.rs @@ -0,0 +1,281 @@ +use crate::decimals::{Liquidity, Price, TokenAmount}; +use crate::math::{calculate_amount_delta, get_max_tick}; +use crate::states::{ + DerivedAccountIdentifier, LpPool, State, INVARIANT_POOL_IDENT, LP_TOKEN_IDENT, +}; +use crate::{get_signer, ErrorCode::*}; +use anchor_lang::prelude::*; +use anchor_spl::associated_token::AssociatedToken; +use anchor_spl::{token::{self, TokenAccount}, token_2022}; +use anchor_spl::token_2022::{mint_to, MintTo, Token2022}; +use anchor_spl::token_interface::{Mint, TokenAccount as ITokenAccount, TokenInterface}; +use decimal::Factories; +use invariant::decimals::{Liquidity as InvLiquidity, Price as InvPrice}; +use invariant::{ + cpi::accounts::{CreatePosition, RemovePosition}, + program::Invariant, + structs::{Pool, Position, PositionList, State as InvariantState, Tick, Tickmap}, +}; + +#[macro_export] +macro_rules! try_from { + ($ty: ty, $acc: expr) => { + <$ty>::try_from(unsafe { core::mem::transmute::<_, &AccountInfo<'_>>($acc.as_ref()) }) + }; +} + +#[derive(Accounts)] +pub struct MintLpTokenCtx<'info> { + #[account(mut)] + pub owner: Signer<'info>, + #[account( + seeds = [State::IDENT], + bump = state.load()?.bump + )] + pub state: AccountLoader<'info, State>, + /// CHECK: cached from the state account + #[account(constraint = &state.load()?.program_authority == program_authority.key @ InvalidAuthority)] + pub program_authority: AccountInfo<'info>, + #[account(mut, + seeds = [LpPool::IDENT, token_x.key().as_ref(), token_y.key().as_ref(), &lp_pool.load()?.fee.v.to_le_bytes(), &lp_pool.load()?.tick_spacing.to_le_bytes()], + bump=lp_pool.load()?.bump, + )] + pub lp_pool: AccountLoader<'info, LpPool>, + #[account(mut, + seeds = [LP_TOKEN_IDENT, token_x.key().as_ref(), token_y.key().as_ref(), &lp_pool.load()?.fee.v.to_le_bytes(), &lp_pool.load()?.tick_spacing.to_le_bytes()], + bump=lp_pool.load()?.token_bump, + )] + pub token_lp: InterfaceAccount<'info, Mint>, + #[account(mut, + associated_token::mint = token_x, + associated_token::authority = program_authority)] + pub reserve_x: InterfaceAccount<'info, ITokenAccount>, + #[account(mut, + associated_token::mint = token_y, + associated_token::authority = program_authority)] + pub reserve_y: InterfaceAccount<'info, ITokenAccount>, + #[account(init_if_needed, + payer = owner, + associated_token::mint = token_lp, + associated_token::authority = owner + )] + pub account_lp: InterfaceAccount<'info, ITokenAccount>, + pub token_program: Program<'info, Token2022>, + pub associated_token_program: Program<'info, AssociatedToken>, + /// INVARIANT + pub inv_program: Program<'info, Invariant>, + pub inv_state: AccountLoader<'info, InvariantState>, + /// CHECK: invariant_program_authority is the authority of the Invariant program + pub inv_program_authority: AccountInfo<'info>, + // might not exist, explicit check in the handler + #[account(mut)] + pub position: AccountInfo<'info>, + // might not exist, check in Invariant CPI + #[account(mut)] + pub last_position: UncheckedAccount<'info>, + #[account(mut, + seeds = [INVARIANT_POOL_IDENT, token_x.key().as_ref(), token_y.key().as_ref(), &lp_pool.load()?.fee.v.to_le_bytes(), &lp_pool.load()?.tick_spacing.to_le_bytes()], + bump=pool.load()?.bump, + seeds::program = invariant::ID + )] + pub pool: AccountLoader<'info, Pool>, + #[account(mut)] + pub position_list: AccountLoader<'info, PositionList>, + // TODO: check if lowest tick possible + #[account(mut)] + pub lower_tick: AccountLoader<'info, Tick>, + // TODO: check if highest tick possible + #[account(mut)] + pub upper_tick: AccountLoader<'info, Tick>, + #[account(mut)] + pub tickmap: AccountLoader<'info, Tickmap>, + pub token_x: InterfaceAccount<'info, Mint>, + pub token_y: InterfaceAccount<'info, Mint>, + #[account(mut)] + pub account_x: InterfaceAccount<'info, ITokenAccount>, + #[account(mut)] + pub account_y: InterfaceAccount<'info, ITokenAccount>, + #[account(mut)] + pub inv_reserve_x: Box>, + #[account(mut)] + pub inv_reserve_y: Box>, + #[account(constraint = token_x_program.key() == token::ID || token_x_program.key() == token_2022::ID)] + pub token_x_program: Interface<'info, TokenInterface>, + #[account(constraint = token_y_program.key() == token::ID || token_y_program.key() == token_2022::ID)] + pub token_y_program: Interface<'info, TokenInterface>, + pub rent: Sysvar<'info, Rent>, + pub system_program: Program<'info, System>, +} + +impl<'info> MintLpTokenCtx<'info> { + pub fn mint_lp(&self) -> CpiContext<'_, '_, '_, 'info, MintTo<'info>> { + CpiContext::new( + self.token_program.to_account_info(), + MintTo { + mint: self.token_lp.to_account_info(), + to: self.account_lp.to_account_info(), + authority: self.program_authority.to_account_info(), + }, + ) + } + + pub fn deposit_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(), + }, + ) + } + pub fn deposit_x_2022(&self) -> CpiContext<'_, '_, '_, 'info, token_2022::TransferChecked<'info>> { + CpiContext::new( + self.token_x_program.to_account_info(), + token_2022::TransferChecked { + from: self.account_x.to_account_info(), + mint: self.token_x.to_account_info(), + to: self.reserve_x.to_account_info(), + authority: self.owner.to_account_info(), + }, + ) + } + + pub fn deposit_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(), + }, + ) + } + pub fn deposit_y_2022(&self) -> CpiContext<'_, '_, '_, 'info, token_2022::TransferChecked<'info>> { + CpiContext::new( + self.token_y_program.to_account_info(), + token_2022::TransferChecked { + from: self.account_y.to_account_info(), + mint: self.token_y.to_account_info(), + to: self.reserve_y.to_account_info(), + authority: self.owner.to_account_info(), + }, + ) + } + + pub fn remove_position(&self) -> CpiContext<'_, '_, '_, 'info, RemovePosition<'info>> { + CpiContext::new( + self.inv_program.to_account_info(), + RemovePosition { + state: self.inv_state.to_account_info(), + program_authority: self.inv_program_authority.to_account_info(), + owner: self.program_authority.to_account_info(), + removed_position: self.position.to_account_info(), + position_list: self.position_list.to_account_info(), + last_position: self.last_position.to_account_info(), + pool: self.pool.to_account_info(), + tickmap: self.tickmap.to_account_info(), + lower_tick: self.lower_tick.to_account_info(), + upper_tick: self.upper_tick.to_account_info(), + token_x: self.token_x.to_account_info(), + token_y: self.token_y.to_account_info(), + account_x: self.account_x.to_account_info(), + account_y: self.account_y.to_account_info(), + reserve_x: self.inv_reserve_x.to_account_info(), + reserve_y: self.inv_reserve_y.to_account_info(), + token_x_program: self.token_x_program.to_account_info(), + token_y_program: self.token_y_program.to_account_info(), + }, + ) + } + pub fn create_position(&self) -> CpiContext<'_, '_, '_, 'info, CreatePosition<'info>> { + CpiContext::new( + self.inv_program.to_account_info(), + CreatePosition { + state: self.inv_state.to_account_info(), + // the previous last position was moved to the previous' address + position: self.last_position.to_account_info(), + pool: self.pool.to_account_info(), + position_list: self.position_list.to_account_info(), + payer: self.owner.to_account_info(), + owner: self.program_authority.to_account_info(), + lower_tick: self.lower_tick.to_account_info(), + upper_tick: self.upper_tick.to_account_info(), + tickmap: self.tickmap.to_account_info(), + token_x: self.token_x.to_account_info(), + token_y: self.token_y.to_account_info(), + account_x: self.account_x.to_account_info(), + account_y: self.account_y.to_account_info(), + reserve_x: self.inv_reserve_x.to_account_info(), + reserve_y: self.inv_reserve_y.to_account_info(), + program_authority: self.inv_program_authority.to_account_info(), + token_x_program: self.token_x_program.to_account_info(), + token_y_program: self.token_y_program.to_account_info(), + rent: self.rent.to_account_info(), + system_program: self.system_program.to_account_info(), + }, + ) + } +// } + // impl MintLpTokenCtx<'_> { + pub fn process(&mut self, liquidity: Liquidity, index: u32) -> Result<()> { + let lp_pool = self.lp_pool.load()?; + let Pool { sqrt_price, current_tick_index, .. } = *self.pool.load()?; + let sqrt_price = Price::from_integer(sqrt_price.v); + let upper_tick_index = get_max_tick(lp_pool.tick_spacing); + let lower_tick_index = -upper_tick_index; + + let dummy_test_val_please_remove = liquidity.v; + + // let (required_x, required_y) = calculate_amount_delta(sqrt_price, liquidity, true, current_tick_index, lower_tick_index, upper_tick_index).unwrap(); + let (required_x, required_y) = (TokenAmount::from_integer(dummy_test_val_please_remove), TokenAmount::from_integer(dummy_test_val_please_remove)); + match self.token_x_program.key() { + token_2022::ID => token_2022::transfer_checked( + self.deposit_x_2022(), + required_x.0, + self.token_x.decimals, + )?, + token::ID => token::transfer(self.deposit_x(), required_x.0)?, + _ => return Err(InvalidTokenProgram.into()), + }; + + match self.token_y_program.key() { + token_2022::ID => token_2022::transfer_checked( + self.deposit_y_2022(), + required_y.0, + self.token_y.decimals, + )?, + token::ID => token::transfer(self.deposit_y(), required_y.0)?, + _ => return Err(InvalidTokenProgram.into()), + }; + + let init_liquidity = if lp_pool.invariant_position != Pubkey::default() { + // let position = try_from!(AccountLoader::, &self.position)?; + // let val = position.load()?.liquidity.v; + // Liquidity::from_integer(val) + Liquidity::from_integer(0) + } + else { + Liquidity::from_integer(0) + }; + + if lp_pool.invariant_position != Pubkey::default() + { + // TODO: move and track index inside of LpPool + // invariant::cpi::remove_position(self.remove_position(), index, lower_tick_index, upper_tick_index)?; + } + { + // invariant::cpi::create_position( + // self.create_position(), + // lower_tick_index, + // upper_tick_index, + // InvLiquidity::from_integer(init_liquidity.v + dummy_test_val_please_remove), + // InvPrice::from_integer(sqrt_price.v), + // InvPrice::from_integer(sqrt_price.v), + // )?; + } + + let signer: &[&[&[u8]]] = get_signer!(self.state.load()?.bump_authority); + mint_to(self.mint_lp().with_signer(signer), dummy_test_val_please_remove as u64) + } +} diff --git a/protocol/programs/protocol/src/contexts/mod.rs b/protocol/programs/protocol/src/contexts/mod.rs index ebd98f9..30e03f6 100644 --- a/protocol/programs/protocol/src/contexts/mod.rs +++ b/protocol/programs/protocol/src/contexts/mod.rs @@ -6,6 +6,7 @@ mod invoke_update_seconds_per_liquidity; mod reopen_position; mod test; mod token; +mod mint; pub use init::*; pub use init_pool::*; @@ -15,3 +16,4 @@ pub use invoke_update_seconds_per_liquidity::*; pub use reopen_position::*; pub use test::*; pub use token::*; +pub use mint::*; \ No newline at end of file diff --git a/protocol/programs/protocol/src/contexts/token/mod.rs b/protocol/programs/protocol/src/contexts/token/mod.rs index 6882d09..ef55c27 100644 --- a/protocol/programs/protocol/src/contexts/token/mod.rs +++ b/protocol/programs/protocol/src/contexts/token/mod.rs @@ -1,7 +1,7 @@ pub mod deposit; -pub mod mint; +// pub mod mint; pub mod withdraw; pub use self::deposit::*; -pub use self::mint::*; +// pub use self::mint::*; pub use self::withdraw::*; diff --git a/protocol/programs/protocol/src/errors.rs b/protocol/programs/protocol/src/errors.rs index 46770e4..27cc1cb 100644 --- a/protocol/programs/protocol/src/errors.rs +++ b/protocol/programs/protocol/src/errors.rs @@ -14,6 +14,8 @@ pub enum ErrorCode { InvalidOwner = 3, //0x12F (303) #[msg("Provided Invariant authority is different than expected")] InvalidInvariantAuthority = 4, //0x130 (304) + #[msg("Provided Token Program for Token is different than expected")] + InvalidTokenProgram = 5, //0x131 (305) } impl TryInto for u32 { diff --git a/protocol/programs/protocol/src/lib.rs b/protocol/programs/protocol/src/lib.rs index d0bf314..1c9718a 100644 --- a/protocol/programs/protocol/src/lib.rs +++ b/protocol/programs/protocol/src/lib.rs @@ -26,6 +26,8 @@ macro_rules! get_signer { #[program] pub mod protocol { + use decimals::Liquidity; + use super::*; pub fn init(ctx: Context, bump_authority: u8) -> Result<()> { @@ -37,9 +39,9 @@ pub mod protocol { ctx.accounts.process(state_bump) } - pub fn mint(ctx: Context, amount: u64) -> Result<()> { - ctx.accounts.process(amount) - } + // pub fn mint(ctx: Context, amount: u64) -> Result<()> { + // ctx.accounts.process(amount) + // } pub fn deposit(ctx: Context, amount: u64) -> Result<()> { ctx.accounts.process(amount) @@ -91,7 +93,12 @@ pub mod protocol { } pub fn init_lp_pool(ctx: Context) -> Result<()> { + let token_bump = ctx.bumps.lp_pool; let bump = ctx.bumps.lp_pool; - ctx.accounts.process(bump) + ctx.accounts.process(token_bump, bump) + } + + pub fn mint_lp_token(ctx: Context, liquidity: u128, index: u32) -> Result<()> { + ctx.accounts.process(Liquidity::from_integer(liquidity), index) } } diff --git a/protocol/programs/protocol/src/states/lp_pool.rs b/protocol/programs/protocol/src/states/lp_pool.rs index b6e379d..a7922d4 100644 --- a/protocol/programs/protocol/src/states/lp_pool.rs +++ b/protocol/programs/protocol/src/states/lp_pool.rs @@ -17,6 +17,7 @@ pub struct LpPool { pub token_y: Pubkey, pub tick_spacing: u16, pub fee: FixedPoint, + pub token_bump: u8, pub bump: u8, } diff --git a/protocol/programs/protocol/src/states/mod.rs b/protocol/programs/protocol/src/states/mod.rs index a25348c..e80f80a 100644 --- a/protocol/programs/protocol/src/states/mod.rs +++ b/protocol/programs/protocol/src/states/mod.rs @@ -4,6 +4,7 @@ mod state; pub use lp_pool::LpPool; pub use state::State; +pub const INVARIANT_POOL_IDENT: &'static [u8] = b"poolv1"; pub const LP_TOKEN_IDENT: &'static [u8] = b"lp_tokenv1"; pub trait DerivedAccountIdentifier { diff --git a/protocol/sdk/src/idl/protocol.ts b/protocol/sdk/src/idl/protocol.ts index d2d11be..749769f 100644 --- a/protocol/sdk/src/idl/protocol.ts +++ b/protocol/sdk/src/idl/protocol.ts @@ -69,42 +69,6 @@ export type Protocol = { } ] }, - { - "name": "mint", - "accounts": [ - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "programAuthority", - "isMut": false, - "isSigner": false - }, - { - "name": "tokenMint", - "isMut": true, - "isSigner": false - }, - { - "name": "to", - "isMut": true, - "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "amount", - "type": "u64" - } - ] - }, { "name": "deposit", "accounts": [ @@ -703,6 +667,174 @@ export type Protocol = { } ], "args": [] + }, + { + "name": "mintLpToken", + "accounts": [ + { + "name": "owner", + "isMut": true, + "isSigner": true + }, + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "programAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "lpPool", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenLp", + "isMut": true, + "isSigner": false + }, + { + "name": "reserveX", + "isMut": true, + "isSigner": false + }, + { + "name": "reserveY", + "isMut": true, + "isSigner": false + }, + { + "name": "accountLp", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "associatedTokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "invProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "INVARIANT" + ] + }, + { + "name": "invState", + "isMut": false, + "isSigner": false + }, + { + "name": "invProgramAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "position", + "isMut": true, + "isSigner": false + }, + { + "name": "lastPosition", + "isMut": true, + "isSigner": false + }, + { + "name": "pool", + "isMut": true, + "isSigner": false + }, + { + "name": "positionList", + "isMut": true, + "isSigner": false + }, + { + "name": "lowerTick", + "isMut": true, + "isSigner": false + }, + { + "name": "upperTick", + "isMut": true, + "isSigner": false + }, + { + "name": "tickmap", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenX", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenY", + "isMut": false, + "isSigner": false + }, + { + "name": "accountX", + "isMut": true, + "isSigner": false + }, + { + "name": "accountY", + "isMut": true, + "isSigner": false + }, + { + "name": "invReserveX", + "isMut": true, + "isSigner": false + }, + { + "name": "invReserveY", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenXProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenYProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "liquidity", + "type": "u128" + }, + { + "name": "index", + "type": "u32" + } + ] } ], "accounts": [ @@ -741,6 +873,10 @@ export type Protocol = { "defined": "FixedPoint" } }, + { + "name": "tokenBump", + "type": "u8" + }, { "name": "bump", "type": "u8" @@ -852,6 +988,11 @@ export type Protocol = { "code": 6004, "name": "InvalidInvariantAuthority", "msg": "Provided Invariant authority is different than expected" + }, + { + "code": 6005, + "name": "InvalidTokenProgram", + "msg": "Provided Token Program for Token is different than expected" } ] }; @@ -927,42 +1068,6 @@ export const IDL: Protocol = { } ] }, - { - "name": "mint", - "accounts": [ - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "programAuthority", - "isMut": false, - "isSigner": false - }, - { - "name": "tokenMint", - "isMut": true, - "isSigner": false - }, - { - "name": "to", - "isMut": true, - "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "amount", - "type": "u64" - } - ] - }, { "name": "deposit", "accounts": [ @@ -1561,6 +1666,174 @@ export const IDL: Protocol = { } ], "args": [] + }, + { + "name": "mintLpToken", + "accounts": [ + { + "name": "owner", + "isMut": true, + "isSigner": true + }, + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "programAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "lpPool", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenLp", + "isMut": true, + "isSigner": false + }, + { + "name": "reserveX", + "isMut": true, + "isSigner": false + }, + { + "name": "reserveY", + "isMut": true, + "isSigner": false + }, + { + "name": "accountLp", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "associatedTokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "invProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "INVARIANT" + ] + }, + { + "name": "invState", + "isMut": false, + "isSigner": false + }, + { + "name": "invProgramAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "position", + "isMut": true, + "isSigner": false + }, + { + "name": "lastPosition", + "isMut": true, + "isSigner": false + }, + { + "name": "pool", + "isMut": true, + "isSigner": false + }, + { + "name": "positionList", + "isMut": true, + "isSigner": false + }, + { + "name": "lowerTick", + "isMut": true, + "isSigner": false + }, + { + "name": "upperTick", + "isMut": true, + "isSigner": false + }, + { + "name": "tickmap", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenX", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenY", + "isMut": false, + "isSigner": false + }, + { + "name": "accountX", + "isMut": true, + "isSigner": false + }, + { + "name": "accountY", + "isMut": true, + "isSigner": false + }, + { + "name": "invReserveX", + "isMut": true, + "isSigner": false + }, + { + "name": "invReserveY", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenXProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenYProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "liquidity", + "type": "u128" + }, + { + "name": "index", + "type": "u32" + } + ] } ], "accounts": [ @@ -1599,6 +1872,10 @@ export const IDL: Protocol = { "defined": "FixedPoint" } }, + { + "name": "tokenBump", + "type": "u8" + }, { "name": "bump", "type": "u8" @@ -1710,6 +1987,11 @@ export const IDL: Protocol = { "code": 6004, "name": "InvalidInvariantAuthority", "msg": "Provided Invariant authority is different than expected" + }, + { + "code": 6005, + "name": "InvalidTokenProgram", + "msg": "Provided Token Program for Token is different than expected" } ] }; diff --git a/protocol/sdk/src/protocol.ts b/protocol/sdk/src/protocol.ts index cebeb97..82f011e 100644 --- a/protocol/sdk/src/protocol.ts +++ b/protocol/sdk/src/protocol.ts @@ -36,6 +36,7 @@ import { IInvokeCreatePosition, IInvokeUpdateSecondsPerLiquidity, IMint, + IMintLpToken, IReopenPosition, ITest, IWithdraw, @@ -44,6 +45,7 @@ import { import { getMarketAddress, getTokenProgramAddress, + Market, Pair, } from "@invariant-labs/sdk-eclipse"; @@ -138,6 +140,28 @@ export class Protocol { const ix = await this.initIx(signer); return await this.sendTx([ix], [signer]); } + // TODO: Make this the only init function + async initWithPositionlist( + signer: Keypair, + market: Market + ): Promise { + const ix = await this.initIx(signer); + + // TODO: Fix the function in SDK To accept different payer and owner + const { positionListAddress } = await market.getPositionListAddress( + this.programAuthority + ); + const positionListIx = market.program.instruction.createPositionList({ + accounts: { + positionList: positionListAddress, + owner: this.programAuthority, + signer: signer.publicKey, + rent: SYSVAR_RENT_PUBKEY, + systemProgram: SystemProgram.programId, + }, + }); + return await this.sendTx([ix, positionListIx], [signer]); + } async initIx(signer?: Keypair): Promise { const [, programAuthorityBump] = this.getProgramAuthorityAddressAndBump(); @@ -179,25 +203,21 @@ export class Protocol { return await this.sendTx([ix], [signer]); } - async mintIx({ - amount, - tokenMint, - ...accounts - }: IMint): Promise { + async mintIx({ amount, tokenMint, ...accounts }: IMint): Promise { const tokenProgram = await getTokenProgramAddress( this.connection, tokenMint ); - return await this.program.methods - .mint(amount) - .accounts({ - state: this.stateAddress, - programAuthority: this.programAuthority, - tokenMint, - tokenProgram, - ...accounts, - }) - .instruction(); + // return await this.program.methods + // .mint(amount) + // .accounts({ + // state: this.stateAddress, + // programAuthority: this.programAuthority, + // tokenMint, + // tokenProgram, + // ...accounts, + // }) + // .instruction(); } async deposit( @@ -362,8 +382,8 @@ export class Protocol { .instruction(); } - async initLpPool(accounts: IInitLpPool, signer: Keypair) { - const ix = await this.initLpPoolIx(accounts, signer); + async initLpPool(params: IInitLpPool, signer: Keypair) { + const ix = await this.initLpPoolIx(params, signer); return await this.sendTx([ix], [signer]); } @@ -409,4 +429,62 @@ export class Protocol { }) .instruction(); } + + async mintLpToken(params: IMintLpToken, signer: Keypair) { + const ix = await this.mintLpTokenIx(params, signer); + return await this.sendTx([ix], [signer]); + } + + async mintLpTokenIx( + { pair, liquidityDelta, index, ...accounts }: IMintLpToken, + signer?: Keypair + ) { + const owner = signer?.publicKey ?? this.wallet.publicKey; + + const [lpPool] = this.getLpPoolAddressAndBump(pair); + const [tokenLp] = this.getLpTokenAddressAndBump(pair); + const pool = + accounts.pool ?? + (await pair.getAddress(new PublicKey(getMarketAddress(this.network)))); + const reserveX = this.getReserveAddress(pair.tokenX); + const reserveY = this.getReserveAddress(pair.tokenY); + const tokenXProgram = + accounts.tokenXProgram ?? + (await getTokenProgramAddress(this.connection, pair.tokenX)); + const tokenYProgram = + accounts.tokenYProgram ?? + (await getTokenProgramAddress(this.connection, pair.tokenY)); + const accountLp = getAssociatedTokenAddressSync( + tokenLp, + owner, + undefined, + TOKEN_2022_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + + console.log(pair) + + return await this.program.methods + .mintLpToken(liquidityDelta, index) + .accounts({ + state: this.stateAddress, + programAuthority: this.programAuthority, + lpPool, + tokenLp, + accountLp, + owner, + pool, + tokenX: pair.tokenX, + tokenY: pair.tokenY, + reserveX, + reserveY, + tokenXProgram, + tokenYProgram, + tokenProgram: TOKEN_2022_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + systemProgram: SystemProgram.programId, + ...accounts, + }) + .instruction(); + } } diff --git a/protocol/sdk/src/types.ts b/protocol/sdk/src/types.ts index 5aa0278..cecc510 100644 --- a/protocol/sdk/src/types.ts +++ b/protocol/sdk/src/types.ts @@ -137,3 +137,28 @@ export interface IInitLpPool { tokenXProgram?: PublicKey; tokenYProgram?: PublicKey; } + +export interface IMintLpToken { + // data + pair: Pair; + // params + index: number; + liquidityDelta: BN; + // invariant accounts + invProgram: PublicKey; + invState: PublicKey; + pool?: PublicKey; + position: PublicKey; + lastPosition: PublicKey; + positionList: PublicKey; + lowerTick: PublicKey; + upperTick: PublicKey; + tickmap: PublicKey; + accountX: PublicKey; + accountY: PublicKey; + invReserveX: PublicKey; + invReserveY: PublicKey; + invProgramAuthority: PublicKey; + tokenXProgram?: PublicKey; + tokenYProgram?: PublicKey; +} \ No newline at end of file diff --git a/protocol/tests.sh b/protocol/tests.sh index dc8b617..de5f503 100755 --- a/protocol/tests.sh +++ b/protocol/tests.sh @@ -1,10 +1,11 @@ #!/bin/bash e2e_tests=( - "init" - "token" - "invoke" - "init-lp-pool" + # "init" + # "token" + # "invoke" + # "init-lp-pool" + "mint" ) # currenty, there are no unit tests diff --git a/protocol/tests/init-lp-pool.test.ts b/protocol/tests/init-lp-pool.test.ts index aecbe74..9060b62 100644 --- a/protocol/tests/init-lp-pool.test.ts +++ b/protocol/tests/init-lp-pool.test.ts @@ -12,7 +12,7 @@ import { Pair } from "@invariant-labs/sdk-eclipse"; import { fromFee } from "@invariant-labs/sdk-eclipse/lib/utils"; import { FeeTier, Market } from "@invariant-labs/sdk-eclipse/lib/market"; import { LpPoolStructure } from "../sdk/src/types"; -import { assert, expect } from "chai"; +import { assert } from "chai"; import { ASSOCIATED_TOKEN_PROGRAM_ID, getAccount, diff --git a/protocol/tests/invoke.test.ts b/protocol/tests/invoke.test.ts index d1627c2..c6a6f27 100644 --- a/protocol/tests/invoke.test.ts +++ b/protocol/tests/invoke.test.ts @@ -48,7 +48,7 @@ describe("invariant cpi", () => { ); protocol = await Protocol.build(Network.LOCAL, walletAnchor, connection); - protocol.init(owner); + await protocol.init(owner); market = await Market.build( Network.LOCAL, diff --git a/protocol/tests/mint.test.ts b/protocol/tests/mint.test.ts new file mode 100644 index 0000000..14a58b0 --- /dev/null +++ b/protocol/tests/mint.test.ts @@ -0,0 +1,188 @@ +import { AnchorProvider, BN } from "@coral-xyz/anchor"; +import { Network } from "../sdk/src/network"; +import { Protocol } from "../sdk/src/protocol"; +import { Keypair } from "@solana/web3.js"; +import { + createTokenMint, + initMarket, + INVARIANT_ADDRESS, + requestAirdrop, + sleep, +} from "./test-utils"; +import { assert } from "chai"; +import { getOrCreateAssociatedTokenAccount, mintTo } from "@solana/spl-token"; +import { Pair } from "@invariant-labs/sdk-eclipse"; +import { + fromFee, + getTokenProgramAddress, +} from "@invariant-labs/sdk-eclipse/lib/utils"; +import { + CreateTick, + FeeTier, + Market, +} from "@invariant-labs/sdk-eclipse/lib/market"; + +describe("mint lp token", () => { + const { wallet: walletAnchor, connection } = AnchorProvider.local(); + const owner = Keypair.generate(); + const wallet = Keypair.generate(); + const mintAuthority = Keypair.generate(); + + let protocol: Protocol; + let market: Market; + const feeTier: FeeTier = { + fee: fromFee(new BN(600)), + tickSpacing: 10, + }; + let pair: Pair; + const lowerTick = -10; + const upperTick = 10; + const initTick = 0; + + before(async () => { + let giveSOL = [owner.publicKey, mintAuthority.publicKey, wallet.publicKey]; + await Promise.all( + giveSOL.map((account) => requestAirdrop(connection, account, 1e14)) + ); + + market = await Market.build( + Network.LOCAL, + walletAnchor, + connection, + INVARIANT_ADDRESS + ); + + const [token0, token1] = await Promise.all([ + createTokenMint(connection, owner, mintAuthority.publicKey, 6), + createTokenMint(connection, owner, mintAuthority.publicKey, 6), + ]); + pair = new Pair(token0, token1, feeTier); + + await initMarket(market, [pair], owner, initTick); + + protocol = await Protocol.build(Network.LOCAL, walletAnchor, connection); + await protocol.initWithPositionlist(owner, market); + + const lowerTickVars: CreateTick = { + pair, + index: lowerTick, + payer: owner.publicKey, + }; + const upperTickVars: CreateTick = { + pair, + index: upperTick, + payer: owner.publicKey, + }; + await market.createTick(lowerTickVars, owner); + await market.createTick(upperTickVars, owner); + + const xOwnerAmount = 1e10; + const yOwnerAmount = 1e10; + + const userTokenXAccount = await getOrCreateAssociatedTokenAccount( + connection, + owner, + pair.tokenX, + owner.publicKey + ); + const userTokenYAccount = await getOrCreateAssociatedTokenAccount( + connection, + owner, + pair.tokenY, + owner.publicKey + ); + + await mintTo( + connection, + owner, + pair.tokenX, + userTokenXAccount.address, + mintAuthority, + xOwnerAmount + ); + await mintTo( + connection, + owner, + pair.tokenY, + userTokenYAccount.address, + mintAuthority, + yOwnerAmount + ); + }); + + it("test", async () => { + const userTokenXAccount = await getOrCreateAssociatedTokenAccount( + connection, + owner, + pair.tokenX, + owner.publicKey + ); + const userTokenYAccount = await getOrCreateAssociatedTokenAccount( + connection, + owner, + pair.tokenY, + owner.publicKey + ); + + // TODO: make it support cases where the id is different from 0 + const positionId = 0; + const lastPositionId = 0; + + const liquidityDelta = new BN(5000); + + const { address: stateAddress } = await market.getStateAddress(); + const poolAddress = await pair.getAddress(INVARIANT_ADDRESS); + const { positionListAddress: positionListAddress } = + await market.getPositionListAddress(protocol.programAuthority); + const { positionAddress } = await market.getPositionAddress( + protocol.programAuthority, + positionId + ); + const { positionAddress: lastPositionAddress } = + await market.getPositionAddress(protocol.programAuthority, lastPositionId); + const { tickAddress: lowerTickAddress } = await market.getTickAddress( + pair, + lowerTick + ); + const { tickAddress: upperTickAddress } = await market.getTickAddress( + pair, + upperTick + ); + const { tokenXReserve, tokenYReserve, tickmap } = await market.getPool( + pair + ); + + await protocol.initLpPool( + { + pair, + }, + owner + ); + + await protocol.mintLpToken( + { + liquidityDelta, + pair, + index: positionId, + invProgram: INVARIANT_ADDRESS, + invState: stateAddress, + position: positionAddress, + lastPosition: lastPositionAddress, + pool: poolAddress, + positionList: positionListAddress, + lowerTick: lowerTickAddress, + upperTick: upperTickAddress, + tickmap, + accountX: userTokenXAccount.address, + accountY: userTokenYAccount.address, + invReserveX: tokenXReserve, + invReserveY: tokenYReserve, + invProgramAuthority: market.programAuthority, + }, + owner + ); + + const position = await market.getPosition(protocol.programAuthority, positionId); + assert.ok(position); + }); +}); From 774467ec250fa73feffefec06f459eb4bd6b23c6 Mon Sep 17 00:00:00 2001 From: DevRozaDev <158298065+DevRozaDev@users.noreply.github.com> Date: Tue, 20 Aug 2024 13:15:43 +0200 Subject: [PATCH 2/6] updated mint draft, passes in current state --- protocol/build.sh | 2 +- .../programs/protocol/src/contexts/mint.rs | 124 ++++++++++++------ protocol/programs/protocol/src/lib.rs | 2 +- protocol/sdk/src/protocol.ts | 2 - protocol/tests.sh | 1 + protocol/tests/mint.test.ts | 75 +++++++---- 6 files changed, 134 insertions(+), 72 deletions(-) diff --git a/protocol/build.sh b/protocol/build.sh index a7e45b8..40f76a7 100755 --- a/protocol/build.sh +++ b/protocol/build.sh @@ -1,4 +1,4 @@ - +set -e # Define the keypair JSON content protocol='[125,25,103,8,46,252,74,11,10,231,221,13,113,82,123,17,118,205,218,140,247,37,159,150,140,109,50,158,185,90,57,107,244,112,134,82,19,192,92,70,188,247,227,156,239,127,119,76,193,85,143,146,12,43,48,189,79,193,48,21,49,108,226,209]' diff --git a/protocol/programs/protocol/src/contexts/mint.rs b/protocol/programs/protocol/src/contexts/mint.rs index 7fd9ee1..f8f1fbe 100644 --- a/protocol/programs/protocol/src/contexts/mint.rs +++ b/protocol/programs/protocol/src/contexts/mint.rs @@ -9,7 +9,7 @@ use anchor_spl::associated_token::AssociatedToken; use anchor_spl::{token::{self, TokenAccount}, token_2022}; use anchor_spl::token_2022::{mint_to, MintTo, Token2022}; use anchor_spl::token_interface::{Mint, TokenAccount as ITokenAccount, TokenInterface}; -use decimal::Factories; +use decimal::{Decimal, Factories}; use invariant::decimals::{Liquidity as InvLiquidity, Price as InvPrice}; use invariant::{ cpi::accounts::{CreatePosition, RemovePosition}, @@ -42,72 +42,87 @@ pub struct MintLpTokenCtx<'info> { )] pub lp_pool: AccountLoader<'info, LpPool>, #[account(mut, + // validated in the handler! seeds = [LP_TOKEN_IDENT, token_x.key().as_ref(), token_y.key().as_ref(), &lp_pool.load()?.fee.v.to_le_bytes(), &lp_pool.load()?.tick_spacing.to_le_bytes()], bump=lp_pool.load()?.token_bump, )] - pub token_lp: InterfaceAccount<'info, Mint>, + pub token_lp: Box>, #[account(mut, associated_token::mint = token_x, - associated_token::authority = program_authority)] - pub reserve_x: InterfaceAccount<'info, ITokenAccount>, + associated_token::authority = program_authority, + associated_token::token_program = token_x_program, + )] + pub reserve_x: Box>, #[account(mut, associated_token::mint = token_y, - associated_token::authority = program_authority)] - pub reserve_y: InterfaceAccount<'info, ITokenAccount>, - #[account(init_if_needed, - payer = owner, + associated_token::authority = program_authority, + associated_token::token_program = token_y_program, + )] + pub reserve_y: Box>, + #[account(mut, associated_token::mint = token_lp, - associated_token::authority = owner + associated_token::authority = owner, + associated_token::token_program = token_program, )] - pub account_lp: InterfaceAccount<'info, ITokenAccount>, + pub account_lp: Box>, pub token_program: Program<'info, Token2022>, pub associated_token_program: Program<'info, AssociatedToken>, /// INVARIANT - pub inv_program: Program<'info, Invariant>, - pub inv_state: AccountLoader<'info, InvariantState>, + /// CHECK: passed to Invariant + pub inv_program: UncheckedAccount<'info>, + /// CHECK: passed to Invariant + pub inv_state: UncheckedAccount<'info>, /// CHECK: invariant_program_authority is the authority of the Invariant program - pub inv_program_authority: AccountInfo<'info>, - // might not exist, explicit check in the handler + pub inv_program_authority: UncheckedAccount<'info>, + /// CHECK: might not exist, explicit check in the handler #[account(mut)] pub position: AccountInfo<'info>, - // might not exist, check in Invariant CPI + /// CHECK: might not exist, check in Invariant CPI #[account(mut)] pub last_position: UncheckedAccount<'info>, #[account(mut, + // validated in the handler! seeds = [INVARIANT_POOL_IDENT, token_x.key().as_ref(), token_y.key().as_ref(), &lp_pool.load()?.fee.v.to_le_bytes(), &lp_pool.load()?.tick_spacing.to_le_bytes()], bump=pool.load()?.bump, seeds::program = invariant::ID + // OR + // constraint = pool.load()?.token_x == token_x.key() && pool.load()?.token_y == token_y.key(), + // constraint = pool.load()?.tick_spacing == lp_pool.load()?.tick_spacing, + // constraint = pool.load()?.fee.v == lp_pool.load()?.fee.v )] pub pool: AccountLoader<'info, Pool>, + /// CHECK: passed to Invariant #[account(mut)] - pub position_list: AccountLoader<'info, PositionList>, - // TODO: check if lowest tick possible + pub position_list: UncheckedAccount<'info>, + /// CHECK: passed to Invariant #[account(mut)] - pub lower_tick: AccountLoader<'info, Tick>, - // TODO: check if highest tick possible + pub lower_tick: UncheckedAccount<'info>, + /// CHECK: passed to Invariant #[account(mut)] - pub upper_tick: AccountLoader<'info, Tick>, + pub upper_tick: UncheckedAccount<'info>, + /// CHECK: passed to Invariant #[account(mut)] - pub tickmap: AccountLoader<'info, Tickmap>, - pub token_x: InterfaceAccount<'info, Mint>, - pub token_y: InterfaceAccount<'info, Mint>, + pub tickmap: UncheckedAccount<'info>, + pub token_x: Box>, + pub token_y: Box>, #[account(mut)] - pub account_x: InterfaceAccount<'info, ITokenAccount>, + pub account_x: Box>, #[account(mut)] - pub account_y: InterfaceAccount<'info, ITokenAccount>, + pub account_y: Box>, #[account(mut)] pub inv_reserve_x: Box>, #[account(mut)] pub inv_reserve_y: Box>, - #[account(constraint = token_x_program.key() == token::ID || token_x_program.key() == token_2022::ID)] pub token_x_program: Interface<'info, TokenInterface>, - #[account(constraint = token_y_program.key() == token::ID || token_y_program.key() == token_2022::ID)] pub token_y_program: Interface<'info, TokenInterface>, - pub rent: Sysvar<'info, Rent>, - pub system_program: Program<'info, System>, + /// CHECK: passed to Invariant + pub rent: UncheckedAccount<'info>, + /// CHECK: no inits here, passed to Invariant + pub system_program: UncheckedAccount<'info>, } impl<'info> MintLpTokenCtx<'info> { + pub fn mint_lp(&self) -> CpiContext<'_, '_, '_, 'info, MintTo<'info>> { CpiContext::new( self.token_program.to_account_info(), @@ -129,6 +144,7 @@ impl<'info> MintLpTokenCtx<'info> { }, ) } + pub fn deposit_x_2022(&self) -> CpiContext<'_, '_, '_, 'info, token_2022::TransferChecked<'info>> { CpiContext::new( self.token_x_program.to_account_info(), @@ -151,6 +167,7 @@ impl<'info> MintLpTokenCtx<'info> { }, ) } + pub fn deposit_y_2022(&self) -> CpiContext<'_, '_, '_, 'info, token_2022::TransferChecked<'info>> { CpiContext::new( self.token_y_program.to_account_info(), @@ -188,6 +205,7 @@ impl<'info> MintLpTokenCtx<'info> { }, ) } + pub fn create_position(&self) -> CpiContext<'_, '_, '_, 'info, CreatePosition<'info>> { CpiContext::new( self.inv_program.to_account_info(), @@ -216,19 +234,43 @@ impl<'info> MintLpTokenCtx<'info> { }, ) } -// } - // impl MintLpTokenCtx<'_> { + + pub fn validate_pool(&self) -> Result<()> { + let lp_pool = &self.lp_pool.load()?; + let pool = &self.pool.load()?; + require_keys_eq!(pool.token_x, self.token_x.key()); + require_keys_eq!(pool.token_y, self.token_y.key()); + require_eq!(pool.fee.v, lp_pool.fee.v); + require_eq!(pool.tick_spacing, lp_pool.tick_spacing); + Ok(()) + } + + pub fn validate_token_lp(&self) -> Result<()> { + let lp_pool = &self.lp_pool.load()?; + let token_x = self.token_x.key(); + let token_y = self.token_y.key(); + let seeds = [LP_TOKEN_IDENT, token_x.as_ref(), token_y.as_ref(), &lp_pool.fee.v.to_le_bytes(), &lp_pool.tick_spacing.to_le_bytes()]; + let (pubkey, token_bump) = Pubkey::find_program_address(&seeds, &crate::ID); + require_keys_eq!(pubkey, self.token_lp.key()); + require_eq!(token_bump, lp_pool.token_bump); + Ok(()) + } + pub fn process(&mut self, liquidity: Liquidity, index: u32) -> Result<()> { - let lp_pool = self.lp_pool.load()?; - let Pool { sqrt_price, current_tick_index, .. } = *self.pool.load()?; - let sqrt_price = Price::from_integer(sqrt_price.v); + self.validate_pool()?; + self.validate_token_lp()?; + + let lp_pool = &self.lp_pool.load()?; + // let Pool { sqrt_price, current_tick_index, .. } = &self.pool.load()?; + + let sqrt_price = Price::new(5); let upper_tick_index = get_max_tick(lp_pool.tick_spacing); let lower_tick_index = -upper_tick_index; let dummy_test_val_please_remove = liquidity.v; // let (required_x, required_y) = calculate_amount_delta(sqrt_price, liquidity, true, current_tick_index, lower_tick_index, upper_tick_index).unwrap(); - let (required_x, required_y) = (TokenAmount::from_integer(dummy_test_val_please_remove), TokenAmount::from_integer(dummy_test_val_please_remove)); + let (required_x, required_y) = (TokenAmount::new(dummy_test_val_please_remove as u64), TokenAmount::new(dummy_test_val_please_remove as u64)); match self.token_x_program.key() { token_2022::ID => token_2022::transfer_checked( self.deposit_x_2022(), @@ -252,11 +294,11 @@ impl<'info> MintLpTokenCtx<'info> { let init_liquidity = if lp_pool.invariant_position != Pubkey::default() { // let position = try_from!(AccountLoader::, &self.position)?; // let val = position.load()?.liquidity.v; - // Liquidity::from_integer(val) - Liquidity::from_integer(0) + // Liquidity::new(val) + Liquidity::new(0) } else { - Liquidity::from_integer(0) + Liquidity::new(0) }; if lp_pool.invariant_position != Pubkey::default() @@ -269,9 +311,9 @@ impl<'info> MintLpTokenCtx<'info> { // self.create_position(), // lower_tick_index, // upper_tick_index, - // InvLiquidity::from_integer(init_liquidity.v + dummy_test_val_please_remove), - // InvPrice::from_integer(sqrt_price.v), - // InvPrice::from_integer(sqrt_price.v), + // InvLiquidity::new(init_liquidity.v + dummy_test_val_please_remove), + // InvPrice::new(sqrt_price.v), + // InvPrice::new(sqrt_price.v), // )?; } diff --git a/protocol/programs/protocol/src/lib.rs b/protocol/programs/protocol/src/lib.rs index 1c9718a..5fa6cda 100644 --- a/protocol/programs/protocol/src/lib.rs +++ b/protocol/programs/protocol/src/lib.rs @@ -93,7 +93,7 @@ pub mod protocol { } pub fn init_lp_pool(ctx: Context) -> Result<()> { - let token_bump = ctx.bumps.lp_pool; + let token_bump = ctx.bumps.token_lp; let bump = ctx.bumps.lp_pool; ctx.accounts.process(token_bump, bump) } diff --git a/protocol/sdk/src/protocol.ts b/protocol/sdk/src/protocol.ts index 82f011e..82e1cf2 100644 --- a/protocol/sdk/src/protocol.ts +++ b/protocol/sdk/src/protocol.ts @@ -462,8 +462,6 @@ export class Protocol { ASSOCIATED_TOKEN_PROGRAM_ID ); - console.log(pair) - return await this.program.methods .mintLpToken(liquidityDelta, index) .accounts({ diff --git a/protocol/tests.sh b/protocol/tests.sh index de5f503..1e128c2 100755 --- a/protocol/tests.sh +++ b/protocol/tests.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -e e2e_tests=( # "init" diff --git a/protocol/tests/mint.test.ts b/protocol/tests/mint.test.ts index 14a58b0..3e8526d 100644 --- a/protocol/tests/mint.test.ts +++ b/protocol/tests/mint.test.ts @@ -1,7 +1,7 @@ import { AnchorProvider, BN } from "@coral-xyz/anchor"; import { Network } from "../sdk/src/network"; import { Protocol } from "../sdk/src/protocol"; -import { Keypair } from "@solana/web3.js"; +import { Keypair, SendTransactionError } from "@solana/web3.js"; import { createTokenMint, initMarket, @@ -10,7 +10,7 @@ import { sleep, } from "./test-utils"; import { assert } from "chai"; -import { getOrCreateAssociatedTokenAccount, mintTo } from "@solana/spl-token"; +import { ASSOCIATED_TOKEN_PROGRAM_ID, getOrCreateAssociatedTokenAccount, mintTo, TOKEN_2022_PROGRAM_ID } from "@solana/spl-token"; import { Pair } from "@invariant-labs/sdk-eclipse"; import { fromFee, @@ -158,31 +158,52 @@ describe("mint lp token", () => { }, owner ); - - await protocol.mintLpToken( - { - liquidityDelta, - pair, - index: positionId, - invProgram: INVARIANT_ADDRESS, - invState: stateAddress, - position: positionAddress, - lastPosition: lastPositionAddress, - pool: poolAddress, - positionList: positionListAddress, - lowerTick: lowerTickAddress, - upperTick: upperTickAddress, - tickmap, - accountX: userTokenXAccount.address, - accountY: userTokenYAccount.address, - invReserveX: tokenXReserve, - invReserveY: tokenYReserve, - invProgramAuthority: market.programAuthority, - }, - owner - ); - const position = await market.getPosition(protocol.programAuthority, positionId); - assert.ok(position); + const [tokenLp] = protocol.getLpTokenAddressAndBump(pair); + const accountLp = await getOrCreateAssociatedTokenAccount( + connection, + owner, + tokenLp, + owner.publicKey, + undefined, + undefined, + undefined, + TOKEN_2022_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + + + try { + await protocol.mintLpToken( + { + liquidityDelta, + pair, + index: positionId, + invProgram: INVARIANT_ADDRESS, + invState: stateAddress, + position: positionAddress, + lastPosition: lastPositionAddress, + pool: poolAddress, + positionList: positionListAddress, + lowerTick: lowerTickAddress, + upperTick: upperTickAddress, + tickmap, + accountX: userTokenXAccount.address, + accountY: userTokenYAccount.address, + invReserveX: tokenXReserve, + invReserveY: tokenYReserve, + invProgramAuthority: market.programAuthority, + }, + owner + ); + } catch (error) { + if (error instanceof SendTransactionError) { + console.log(await error.getLogs(connection)); + } + } + + + // const position = await market.getPosition(protocol.programAuthority, positionId); + // assert.ok(position); }); }); From 8f1d2b01c585aeb0bb418da487a463230342e502 Mon Sep 17 00:00:00 2001 From: DevRozaDev <158298065+DevRozaDev@users.noreply.github.com> Date: Tue, 20 Aug 2024 19:27:46 +0200 Subject: [PATCH 3/6] updated mint draft, support minting for a single LpPool... once --- .../programs/protocol/src/contexts/mint.rs | 196 ++++++++---- .../programs/protocol/src/contexts/mod.rs | 4 +- protocol/programs/protocol/src/errors.rs | 2 + protocol/programs/protocol/src/lib.rs | 3 +- protocol/programs/protocol/src/math.rs | 282 +++++++++--------- protocol/sdk/src/idl/protocol.ts | 14 +- protocol/sdk/src/protocol.ts | 3 +- protocol/tests.sh | 6 +- protocol/tests/init-lp-pool.test.ts | 35 +-- protocol/tests/mint.test.ts | 118 +++++--- 10 files changed, 398 insertions(+), 265 deletions(-) diff --git a/protocol/programs/protocol/src/contexts/mint.rs b/protocol/programs/protocol/src/contexts/mint.rs index f8f1fbe..cc8fa2c 100644 --- a/protocol/programs/protocol/src/contexts/mint.rs +++ b/protocol/programs/protocol/src/contexts/mint.rs @@ -1,20 +1,20 @@ use crate::decimals::{Liquidity, Price, TokenAmount}; -use crate::math::{calculate_amount_delta, get_max_tick}; -use crate::states::{ - DerivedAccountIdentifier, LpPool, State, INVARIANT_POOL_IDENT, LP_TOKEN_IDENT, -}; +use crate::math::{calculate_amount_delta, compute_lp_share_change, get_max_tick, get_min_tick}; +use crate::states::{DerivedAccountIdentifier, LpPool, State, LP_TOKEN_IDENT}; use crate::{get_signer, ErrorCode::*}; use anchor_lang::prelude::*; use anchor_spl::associated_token::AssociatedToken; -use anchor_spl::{token::{self, TokenAccount}, token_2022}; use anchor_spl::token_2022::{mint_to, MintTo, Token2022}; use anchor_spl::token_interface::{Mint, TokenAccount as ITokenAccount, TokenInterface}; -use decimal::{Decimal, Factories}; +use anchor_spl::{ + token::{self}, + token_2022, +}; +use decimal::{BetweenDecimals, Decimal}; use invariant::decimals::{Liquidity as InvLiquidity, Price as InvPrice}; use invariant::{ cpi::accounts::{CreatePosition, RemovePosition}, - program::Invariant, - structs::{Pool, Position, PositionList, State as InvariantState, Tick, Tickmap}, + structs::{Pool, Position}, }; #[macro_export] @@ -24,6 +24,9 @@ macro_rules! try_from { }; } +const ADD: bool = true; +const SUBTRACT: bool = false; + #[derive(Accounts)] pub struct MintLpTokenCtx<'info> { #[account(mut)] @@ -34,6 +37,7 @@ pub struct MintLpTokenCtx<'info> { )] pub state: AccountLoader<'info, State>, /// CHECK: cached from the state account + #[account(mut)] #[account(constraint = &state.load()?.program_authority == program_authority.key @ InvalidAuthority)] pub program_authority: AccountInfo<'info>, #[account(mut, @@ -43,8 +47,8 @@ pub struct MintLpTokenCtx<'info> { pub lp_pool: AccountLoader<'info, LpPool>, #[account(mut, // validated in the handler! - seeds = [LP_TOKEN_IDENT, token_x.key().as_ref(), token_y.key().as_ref(), &lp_pool.load()?.fee.v.to_le_bytes(), &lp_pool.load()?.tick_spacing.to_le_bytes()], - bump=lp_pool.load()?.token_bump, + // seeds = [LP_TOKEN_IDENT, token_x.key().as_ref(), token_y.key().as_ref(), &lp_pool.load()?.fee.v.to_le_bytes(), &lp_pool.load()?.tick_spacing.to_le_bytes()], + // bump=lp_pool.load()?.token_bump, )] pub token_lp: Box>, #[account(mut, @@ -60,7 +64,7 @@ pub struct MintLpTokenCtx<'info> { )] pub reserve_y: Box>, #[account(mut, - associated_token::mint = token_lp, + associated_token::mint = token_lp, associated_token::authority = owner, associated_token::token_program = token_program, )] @@ -82,9 +86,9 @@ pub struct MintLpTokenCtx<'info> { pub last_position: UncheckedAccount<'info>, #[account(mut, // validated in the handler! - seeds = [INVARIANT_POOL_IDENT, token_x.key().as_ref(), token_y.key().as_ref(), &lp_pool.load()?.fee.v.to_le_bytes(), &lp_pool.load()?.tick_spacing.to_le_bytes()], - bump=pool.load()?.bump, - seeds::program = invariant::ID + // seeds = [INVARIANT_POOL_IDENT, token_x.key().as_ref(), token_y.key().as_ref(), &lp_pool.load()?.fee.v.to_le_bytes(), &lp_pool.load()?.tick_spacing.to_le_bytes()], + // bump=pool.load()?.bump, + // seeds::program = invariant::ID // OR // constraint = pool.load()?.token_x == token_x.key() && pool.load()?.token_y == token_y.key(), // constraint = pool.load()?.tick_spacing == lp_pool.load()?.tick_spacing, @@ -122,7 +126,6 @@ pub struct MintLpTokenCtx<'info> { } impl<'info> MintLpTokenCtx<'info> { - pub fn mint_lp(&self) -> CpiContext<'_, '_, '_, 'info, MintTo<'info>> { CpiContext::new( self.token_program.to_account_info(), @@ -145,7 +148,9 @@ impl<'info> MintLpTokenCtx<'info> { ) } - pub fn deposit_x_2022(&self) -> CpiContext<'_, '_, '_, 'info, token_2022::TransferChecked<'info>> { + pub fn deposit_x_2022( + &self, + ) -> CpiContext<'_, '_, '_, 'info, token_2022::TransferChecked<'info>> { CpiContext::new( self.token_x_program.to_account_info(), token_2022::TransferChecked { @@ -168,7 +173,9 @@ impl<'info> MintLpTokenCtx<'info> { ) } - pub fn deposit_y_2022(&self) -> CpiContext<'_, '_, '_, 'info, token_2022::TransferChecked<'info>> { + pub fn deposit_y_2022( + &self, + ) -> CpiContext<'_, '_, '_, 'info, token_2022::TransferChecked<'info>> { CpiContext::new( self.token_y_program.to_account_info(), token_2022::TransferChecked { @@ -196,8 +203,8 @@ impl<'info> MintLpTokenCtx<'info> { upper_tick: self.upper_tick.to_account_info(), token_x: self.token_x.to_account_info(), token_y: self.token_y.to_account_info(), - account_x: self.account_x.to_account_info(), - account_y: self.account_y.to_account_info(), + account_x: self.reserve_x.to_account_info(), + account_y: self.reserve_y.to_account_info(), reserve_x: self.inv_reserve_x.to_account_info(), reserve_y: self.inv_reserve_y.to_account_info(), token_x_program: self.token_x_program.to_account_info(), @@ -222,8 +229,8 @@ impl<'info> MintLpTokenCtx<'info> { tickmap: self.tickmap.to_account_info(), token_x: self.token_x.to_account_info(), token_y: self.token_y.to_account_info(), - account_x: self.account_x.to_account_info(), - account_y: self.account_y.to_account_info(), + account_x: self.reserve_x.to_account_info(), + account_y: self.reserve_y.to_account_info(), reserve_x: self.inv_reserve_x.to_account_info(), reserve_y: self.inv_reserve_y.to_account_info(), program_authority: self.inv_program_authority.to_account_info(), @@ -249,75 +256,146 @@ impl<'info> MintLpTokenCtx<'info> { let lp_pool = &self.lp_pool.load()?; let token_x = self.token_x.key(); let token_y = self.token_y.key(); - let seeds = [LP_TOKEN_IDENT, token_x.as_ref(), token_y.as_ref(), &lp_pool.fee.v.to_le_bytes(), &lp_pool.tick_spacing.to_le_bytes()]; + let seeds = [ + LP_TOKEN_IDENT, + token_x.as_ref(), + token_y.as_ref(), + &lp_pool.fee.v.to_le_bytes(), + &lp_pool.tick_spacing.to_le_bytes(), + ]; let (pubkey, token_bump) = Pubkey::find_program_address(&seeds, &crate::ID); require_keys_eq!(pubkey, self.token_lp.key()); require_eq!(token_bump, lp_pool.token_bump); Ok(()) } + pub fn validate_position(&self) -> Result<()> { + let lp_pool = &self.lp_pool.load()?; + if lp_pool.invariant_position != Pubkey::default() { + let upper_tick_index = get_max_tick(lp_pool.tick_spacing); + let lower_tick_index = -upper_tick_index; + let position = try_from!(AccountLoader::, &self.position)?; + require_eq!(position.load()?.upper_tick_index, upper_tick_index); + require_eq!(position.load()?.lower_tick_index, lower_tick_index); + } + Ok(()) + } + pub fn process(&mut self, liquidity: Liquidity, index: u32) -> Result<()> { self.validate_pool()?; self.validate_token_lp()?; + self.validate_position()?; - let lp_pool = &self.lp_pool.load()?; - // let Pool { sqrt_price, current_tick_index, .. } = &self.pool.load()?; - - let sqrt_price = Price::new(5); + let lp_pool = &mut self.lp_pool.load_mut()?; + let Pool { + sqrt_price, + current_tick_index, + .. + } = *self.pool.load()?; + let current_sqrt_price = Price::new(sqrt_price.v); let upper_tick_index = get_max_tick(lp_pool.tick_spacing); - let lower_tick_index = -upper_tick_index; + let lower_tick_index = get_min_tick(lp_pool.tick_spacing); + let tick_spacing = lp_pool.tick_spacing; + msg!("tick_spacing: {}", tick_spacing); + msg!("upper_tick_index: {}", upper_tick_index); + msg!("lower_tick_index: {}", lower_tick_index); + + let (unclaimed_fee_x, unclaimed_fee_y, (position_x, position_y)) = + if lp_pool.invariant_position != Pubkey::default() { + let position = *try_from!(AccountLoader::, &self.position)?.load()?; + let liquidity = Liquidity::new(position.liquidity.v); + let tokens_owed_x = TokenAmount::from_decimal(position.tokens_owed_x); + let tokens_owed_y = TokenAmount::from_decimal(position.tokens_owed_y); + let position_tokens = calculate_amount_delta( + current_sqrt_price, + liquidity, + SUBTRACT, + current_tick_index, + lower_tick_index, + upper_tick_index, + ) + .unwrap(); + (tokens_owed_x, tokens_owed_y, position_tokens) + } else { + ( + TokenAmount::new(0), + TokenAmount::new(0), + (TokenAmount::new(0), TokenAmount::new(0)), + ) + }; - let dummy_test_val_please_remove = liquidity.v; + let shares = compute_lp_share_change( + ADD, + TokenAmount::new(self.token_lp.supply), + liquidity, + TokenAmount::new(lp_pool.leftover_x) + position_x + unclaimed_fee_x, + TokenAmount::new(lp_pool.leftover_y) + position_y + unclaimed_fee_y, + lp_pool.tick_spacing, + current_tick_index, + current_sqrt_price, + ) + .unwrap(); + + msg!("shares: {:?}", shares); + + let (deposited_x, deposited_y) = shares.transferred_amounts; + let (leftover_x, leftover_y) = shares.leftover_amounts; + lp_pool.leftover_x = leftover_x.0; + lp_pool.leftover_y = leftover_y.0; - // let (required_x, required_y) = calculate_amount_delta(sqrt_price, liquidity, true, current_tick_index, lower_tick_index, upper_tick_index).unwrap(); - let (required_x, required_y) = (TokenAmount::new(dummy_test_val_please_remove as u64), TokenAmount::new(dummy_test_val_please_remove as u64)); match self.token_x_program.key() { token_2022::ID => token_2022::transfer_checked( self.deposit_x_2022(), - required_x.0, + deposited_x.0, self.token_x.decimals, )?, - token::ID => token::transfer(self.deposit_x(), required_x.0)?, + token::ID => token::transfer(self.deposit_x(), deposited_x.0)?, _ => return Err(InvalidTokenProgram.into()), }; match self.token_y_program.key() { token_2022::ID => token_2022::transfer_checked( self.deposit_y_2022(), - required_y.0, + deposited_y.0, self.token_y.decimals, )?, - token::ID => token::transfer(self.deposit_y(), required_y.0)?, + token::ID => token::transfer(self.deposit_y(), deposited_y.0)?, _ => return Err(InvalidTokenProgram.into()), }; - - let init_liquidity = if lp_pool.invariant_position != Pubkey::default() { - // let position = try_from!(AccountLoader::, &self.position)?; - // let val = position.load()?.liquidity.v; - // Liquidity::new(val) - Liquidity::new(0) - } - else { - Liquidity::new(0) - }; - - if lp_pool.invariant_position != Pubkey::default() - { + + // close and reopen position with new amount + let signer: &[&[&[u8]]] = get_signer!(self.state.load()?.bump_authority); + if lp_pool.invariant_position != Pubkey::default() { // TODO: move and track index inside of LpPool - // invariant::cpi::remove_position(self.remove_position(), index, lower_tick_index, upper_tick_index)?; + invariant::cpi::remove_position( + self.remove_position().with_signer(signer), + index, + lower_tick_index, + upper_tick_index, + )?; } { - // invariant::cpi::create_position( - // self.create_position(), - // lower_tick_index, - // upper_tick_index, - // InvLiquidity::new(init_liquidity.v + dummy_test_val_please_remove), - // InvPrice::new(sqrt_price.v), - // InvPrice::new(sqrt_price.v), - // )?; - } + let new_liquidity = shares.positions_details.liquidity.v; + msg!("new_liquidity: {}", new_liquidity); + msg!("reserve x: {}", self.reserve_x.amount); + msg!("reserve y: {}", self.reserve_y.amount); - let signer: &[&[&[u8]]] = get_signer!(self.state.load()?.bump_authority); - mint_to(self.mint_lp().with_signer(signer), dummy_test_val_please_remove as u64) + invariant::cpi::create_position( + self.create_position().with_signer(signer), + lower_tick_index, + upper_tick_index, + InvLiquidity::new(new_liquidity), + InvPrice::new(sqrt_price.v), + InvPrice::new(sqrt_price.v), + )?; + + // TODO: adjust to support multiple pools + lp_pool.invariant_position = self.position.key(); + } + // mint LP tokens for user + mint_to( + self.mint_lp().with_signer(signer), + shares.lp_token_change.unwrap_or_default().0, + ) } } diff --git a/protocol/programs/protocol/src/contexts/mod.rs b/protocol/programs/protocol/src/contexts/mod.rs index 30e03f6..b466eb7 100644 --- a/protocol/programs/protocol/src/contexts/mod.rs +++ b/protocol/programs/protocol/src/contexts/mod.rs @@ -3,17 +3,17 @@ mod init_pool; mod invoke_close_position; mod invoke_create_position; mod invoke_update_seconds_per_liquidity; +mod mint; mod reopen_position; mod test; mod token; -mod mint; pub use init::*; pub use init_pool::*; pub use invoke_close_position::*; pub use invoke_create_position::*; pub use invoke_update_seconds_per_liquidity::*; +pub use mint::*; pub use reopen_position::*; pub use test::*; pub use token::*; -pub use mint::*; \ No newline at end of file diff --git a/protocol/programs/protocol/src/errors.rs b/protocol/programs/protocol/src/errors.rs index 27cc1cb..463ffa6 100644 --- a/protocol/programs/protocol/src/errors.rs +++ b/protocol/programs/protocol/src/errors.rs @@ -16,6 +16,8 @@ pub enum ErrorCode { InvalidInvariantAuthority = 4, //0x130 (304) #[msg("Provided Token Program for Token is different than expected")] InvalidTokenProgram = 5, //0x131 (305) + #[msg("Error originated from compute_lp_share_change")] + InvalidShares = 6, //0x132 (306) } impl TryInto for u32 { diff --git a/protocol/programs/protocol/src/lib.rs b/protocol/programs/protocol/src/lib.rs index 5fa6cda..8fb5a54 100644 --- a/protocol/programs/protocol/src/lib.rs +++ b/protocol/programs/protocol/src/lib.rs @@ -99,6 +99,7 @@ pub mod protocol { } pub fn mint_lp_token(ctx: Context, liquidity: u128, index: u32) -> Result<()> { - ctx.accounts.process(Liquidity::from_integer(liquidity), index) + ctx.accounts + .process(Liquidity::from_integer(liquidity), index) } } diff --git a/protocol/programs/protocol/src/math.rs b/protocol/programs/protocol/src/math.rs index 0893739..965e07d 100644 --- a/protocol/programs/protocol/src/math.rs +++ b/protocol/programs/protocol/src/math.rs @@ -35,10 +35,10 @@ pub struct PositionDetails { #[derive(Debug, Clone)] pub struct LiquidityChangeResult { - positions_details: PositionDetails, - transferred_amounts: (TokenAmount, TokenAmount), - lp_token_change: Option, - leftover_amounts: (TokenAmount, TokenAmount), + pub positions_details: PositionDetails, + pub transferred_amounts: (TokenAmount, TokenAmount), + pub lp_token_change: Option, + pub leftover_amounts: (TokenAmount, TokenAmount), } // converts ticks to price with reduced precision @@ -508,21 +508,28 @@ pub fn calculate_amount_delta( } pub fn get_max_tick(tick_spacing: u16) -> i32 { + let limit_by_price = MAX_TICK + .checked_sub(MAX_TICK.checked_rem_euclid(tick_spacing.into()).unwrap()) + .unwrap(); + let limit_by_space = TICK_LIMIT .checked_sub(1) .unwrap() .checked_mul(tick_spacing.into()) .unwrap(); - limit_by_space.min(MAX_TICK) + limit_by_space.min(limit_by_price) } pub fn get_min_tick(tick_spacing: u16) -> i32 { + let limit_by_price = (-MAX_TICK) + .checked_add(MAX_TICK % tick_spacing as i32) + .unwrap(); let limit_by_space = (-TICK_LIMIT) .checked_add(1) .unwrap() .checked_mul(tick_spacing.into()) .unwrap(); - limit_by_space.max(-MAX_TICK) + limit_by_space.max(limit_by_price) } pub fn liquidity_to_lp_token_amount( @@ -596,6 +603,138 @@ pub fn lp_token_amount_to_liquidity( Ok(amount) } + +pub fn compute_max_liquidity_position( + x_before: TokenAmount, + y_before: TokenAmount, + min_tick: i32, + max_tick: i32, + current_tick_index: i32, + current_sqrt_price: Price, +) -> TrackableResult<(TokenAmount, TokenAmount, Liquidity)> { + let current_liquidity = get_max_liquidity( + x_before, + y_before, + min_tick, + max_tick, + current_sqrt_price, + true, + )?; + + let old_position_amounts = calculate_amount_delta( + current_sqrt_price, + current_liquidity.l, + true, + current_tick_index, + min_tick, + max_tick, + ) + .map_err(|_| err!("Failed to calculate old lp share token cost"))?; + return Ok(( + old_position_amounts.0, + old_position_amounts.1, + current_liquidity.l, + )); +} + +pub fn compute_lp_share_change( + provide_liquidity: bool, + lp_token_supply: TokenAmount, + liquidity_delta: Liquidity, + x_before: TokenAmount, //fee + reserve + amount + y_before: TokenAmount, //fee + reserve + amount + tick_spacing: u16, + current_tick_index: i32, + current_sqrt_price: Price, +) -> TrackableResult { + let max_tick = get_max_tick(tick_spacing); + let min_tick = get_min_tick(tick_spacing); + let (old_x, old_y, current_liquidity) = compute_max_liquidity_position( + x_before, + y_before, + min_tick, + max_tick, + current_tick_index, + current_sqrt_price, + )?; + + // since the second position doesn't have an error, + // the only way for leftovers to appear is from creating the initial position with fee + let leftover_x = x_before - old_x; + let leftover_y = y_before - old_y; + + if liquidity_delta.v == 0 { + return Ok(LiquidityChangeResult { + positions_details: PositionDetails { + lower_tick: min_tick, + upper_tick: max_tick, + liquidity: current_liquidity, + }, + lp_token_change: None, + transferred_amounts: (TokenAmount::new(0), TokenAmount::new(0)), + leftover_amounts: (leftover_x, leftover_y), + }); + } + + let new_liquidity = if provide_liquidity { + current_liquidity + .checked_add(liquidity_delta) + .map_err(|_| err!(TrackableError::ADD))? + } else { + current_liquidity + .checked_sub(liquidity_delta) + .map_err(|_| err!(TrackableError::SUB))? + }; + + let new_position_amounts = calculate_amount_delta( + current_sqrt_price, + new_liquidity, + true, + current_tick_index, + min_tick, + max_tick, + ) + .map_err(|_| err!("Failed to calculate new lp share token cost"))?; + + let (transferred_x, transferred_y) = if provide_liquidity { + ( + new_position_amounts.0 - old_x, + new_position_amounts.1 - old_y, + ) + } else { + ( + old_x - new_position_amounts.0, + old_y - new_position_amounts.1, + ) + }; + + if transferred_x == TokenAmount::new(0) && transferred_y == TokenAmount::new(0) { + Err(err!("Liquidity delta too small to create a deposit"))? + } + + let lp_token_change = liquidity_to_lp_token_amount( + lp_token_supply, + current_liquidity, + liquidity_delta, + !provide_liquidity, + )?; + + if lp_token_change == TokenAmount::new(0) { + Err(err!("Liquidity delta too small to change LpToken amount"))? + } + + Ok(LiquidityChangeResult { + positions_details: PositionDetails { + lower_tick: min_tick, + upper_tick: max_tick, + liquidity: new_liquidity, + }, + lp_token_change: Some(lp_token_change), + transferred_amounts: (transferred_x, transferred_y), + leftover_amounts: (leftover_x, leftover_y), + }) +} + #[cfg(test)] mod test { use super::*; @@ -655,137 +794,6 @@ mod test { assert_eq!(new_lp_tokens_minted, lp_tokens_minted); } - pub fn compute_max_liquidity_position( - x_before: TokenAmount, - y_before: TokenAmount, - min_tick: i32, - max_tick: i32, - current_tick_index: i32, - current_sqrt_price: Price, - ) -> TrackableResult<(TokenAmount, TokenAmount, Liquidity)> { - let current_liquidity = get_max_liquidity( - x_before, - y_before, - min_tick, - max_tick, - current_sqrt_price, - true, - )?; - - let old_position_amounts = calculate_amount_delta( - current_sqrt_price, - current_liquidity.l, - true, - current_tick_index, - min_tick, - max_tick, - ) - .map_err(|_| err!("Failed to calculate old lp share token cost"))?; - return Ok(( - old_position_amounts.0, - old_position_amounts.1, - current_liquidity.l, - )); - } - - fn compute_lp_share_change( - provide_liquidity: bool, - lp_token_supply: TokenAmount, - liquidity_delta: Liquidity, - x_before: TokenAmount, //fee + reserve + amount - y_before: TokenAmount, //fee + reserve + amount - tick_spacing: u16, - current_tick_index: i32, - current_sqrt_price: Price, - ) -> TrackableResult { - let max_tick = get_max_tick(tick_spacing); - let min_tick = -max_tick; - let (old_x, old_y, current_liquidity) = compute_max_liquidity_position( - x_before, - y_before, - min_tick, - max_tick, - current_tick_index, - current_sqrt_price, - )?; - - // since the second position doesn't have an error, - // the only way for leftovers to appear is from creating the initial position with fee - let leftover_x = x_before - old_x; - let leftover_y = y_before - old_y; - - if liquidity_delta.v == 0 { - return Ok(LiquidityChangeResult { - positions_details: PositionDetails { - lower_tick: min_tick, - upper_tick: max_tick, - liquidity: current_liquidity, - }, - lp_token_change: None, - transferred_amounts: (TokenAmount::new(0), TokenAmount::new(0)), - leftover_amounts: (leftover_x, leftover_y), - }); - } - - let new_liquidity = if provide_liquidity { - current_liquidity - .checked_add(liquidity_delta) - .map_err(|_| err!(TrackableError::ADD))? - } else { - current_liquidity - .checked_sub(liquidity_delta) - .map_err(|_| err!(TrackableError::SUB))? - }; - - let new_position_amounts = calculate_amount_delta( - current_sqrt_price, - new_liquidity, - true, - current_tick_index, - min_tick, - max_tick, - ) - .map_err(|_| err!("Failed to calculate new lp share token cost"))?; - - let (transferred_x, transferred_y) = if provide_liquidity { - ( - new_position_amounts.0 - old_x, - new_position_amounts.1 - old_y, - ) - } else { - ( - old_x - new_position_amounts.0, - old_y - new_position_amounts.1, - ) - }; - - if transferred_x == TokenAmount::new(0) && transferred_y == TokenAmount::new(0) { - Err(err!("Liquidity delta too small to create a deposit"))? - } - - let lp_token_change = liquidity_to_lp_token_amount( - lp_token_supply, - current_liquidity, - liquidity_delta, - !provide_liquidity, - )?; - - if lp_token_change == TokenAmount::new(0) { - Err(err!("Liquidity delta too small to change LpToken amount"))? - } - - Ok(LiquidityChangeResult { - positions_details: PositionDetails { - lower_tick: min_tick, - upper_tick: max_tick, - liquidity: new_liquidity, - }, - lp_token_change: Some(lp_token_change), - transferred_amounts: (transferred_x, transferred_y), - leftover_amounts: (leftover_x, leftover_y), - }) - } - #[test] fn test_compute_lp_share_change() { { diff --git a/protocol/sdk/src/idl/protocol.ts b/protocol/sdk/src/idl/protocol.ts index 749769f..bf0a6d9 100644 --- a/protocol/sdk/src/idl/protocol.ts +++ b/protocol/sdk/src/idl/protocol.ts @@ -683,7 +683,7 @@ export type Protocol = { }, { "name": "programAuthority", - "isMut": false, + "isMut": true, "isSigner": false }, { @@ -993,6 +993,11 @@ export type Protocol = { "code": 6005, "name": "InvalidTokenProgram", "msg": "Provided Token Program for Token is different than expected" + }, + { + "code": 6006, + "name": "InvalidShares", + "msg": "Error originated from compute_lp_share_change" } ] }; @@ -1682,7 +1687,7 @@ export const IDL: Protocol = { }, { "name": "programAuthority", - "isMut": false, + "isMut": true, "isSigner": false }, { @@ -1992,6 +1997,11 @@ export const IDL: Protocol = { "code": 6005, "name": "InvalidTokenProgram", "msg": "Provided Token Program for Token is different than expected" + }, + { + "code": 6006, + "name": "InvalidShares", + "msg": "Error originated from compute_lp_share_change" } ] }; diff --git a/protocol/sdk/src/protocol.ts b/protocol/sdk/src/protocol.ts index 82e1cf2..ab45f5c 100644 --- a/protocol/sdk/src/protocol.ts +++ b/protocol/sdk/src/protocol.ts @@ -431,8 +431,9 @@ export class Protocol { } async mintLpToken(params: IMintLpToken, signer: Keypair) { + const setCuIx = computeUnitsInstruction(1_400_000); const ix = await this.mintLpTokenIx(params, signer); - return await this.sendTx([ix], [signer]); + return await this.sendTx([setCuIx, ix], [signer]); } async mintLpTokenIx( diff --git a/protocol/tests.sh b/protocol/tests.sh index 1e128c2..0abb480 100755 --- a/protocol/tests.sh +++ b/protocol/tests.sh @@ -2,10 +2,10 @@ set -e e2e_tests=( - # "init" + "init" # "token" - # "invoke" - # "init-lp-pool" + "invoke" + "init-lp-pool" "mint" ) diff --git a/protocol/tests/init-lp-pool.test.ts b/protocol/tests/init-lp-pool.test.ts index 9060b62..7e92642 100644 --- a/protocol/tests/init-lp-pool.test.ts +++ b/protocol/tests/init-lp-pool.test.ts @@ -104,23 +104,24 @@ describe("init lp-pool", () => { assert.equal(lpTokenAccountInfo.amount, 0n); } - await protocol.mint( - { - tokenMint: lpToken, - to: lpTokenAccount.address, - amount: mintAmount, - }, - owner - ); + // test mint entrypoint does not support our new PDA format + // await protocol.mint( + // { + // tokenMint: lpToken, + // to: lpTokenAccount.address, + // amount: mintAmount, + // }, + // owner + // ); - { - const lpTokenAccountInfo = await getAccount( - connection, - lpTokenAccount.address, - undefined, - TOKEN_2022_PROGRAM_ID - ); - assert.equal(lpTokenAccountInfo.amount, mintAmount); - } + // { + // const lpTokenAccountInfo = await getAccount( + // connection, + // lpTokenAccount.address, + // undefined, + // TOKEN_2022_PROGRAM_ID + // ); + // assert.equal(lpTokenAccountInfo.amount, mintAmount); + // } }); }); diff --git a/protocol/tests/mint.test.ts b/protocol/tests/mint.test.ts index 3e8526d..6e5be59 100644 --- a/protocol/tests/mint.test.ts +++ b/protocol/tests/mint.test.ts @@ -1,20 +1,26 @@ import { AnchorProvider, BN } from "@coral-xyz/anchor"; import { Network } from "../sdk/src/network"; import { Protocol } from "../sdk/src/protocol"; -import { Keypair, SendTransactionError } from "@solana/web3.js"; +import { Keypair } from "@solana/web3.js"; import { createTokenMint, initMarket, INVARIANT_ADDRESS, requestAirdrop, - sleep, } from "./test-utils"; import { assert } from "chai"; -import { ASSOCIATED_TOKEN_PROGRAM_ID, getOrCreateAssociatedTokenAccount, mintTo, TOKEN_2022_PROGRAM_ID } from "@solana/spl-token"; +import { + ASSOCIATED_TOKEN_PROGRAM_ID, + createAssociatedTokenAccount, + getOrCreateAssociatedTokenAccount, + mintTo, + TOKEN_2022_PROGRAM_ID, +} from "@solana/spl-token"; import { Pair } from "@invariant-labs/sdk-eclipse"; import { fromFee, - getTokenProgramAddress, + getMaxTick, + getMinTick, } from "@invariant-labs/sdk-eclipse/lib/utils"; import { CreateTick, @@ -35,8 +41,8 @@ describe("mint lp token", () => { tickSpacing: 10, }; let pair: Pair; - const lowerTick = -10; - const upperTick = 10; + const lowerTick = getMinTick(feeTier.tickSpacing ? feeTier.tickSpacing : 0); + const upperTick = getMaxTick(feeTier.tickSpacing ? feeTier.tickSpacing : 0); const initTick = 0; before(async () => { @@ -124,7 +130,7 @@ describe("mint lp token", () => { owner.publicKey ); - // TODO: make it support cases where the id is different from 0 + // TODO: make it support cases where the id is different from 0 -> multiple positions for user aka multiple LP pools const positionId = 0; const lastPositionId = 0; @@ -139,7 +145,10 @@ describe("mint lp token", () => { positionId ); const { positionAddress: lastPositionAddress } = - await market.getPositionAddress(protocol.programAuthority, lastPositionId); + await market.getPositionAddress( + protocol.programAuthority, + lastPositionId + ); const { tickAddress: lowerTickAddress } = await market.getTickAddress( pair, lowerTick @@ -160,50 +169,73 @@ describe("mint lp token", () => { ); const [tokenLp] = protocol.getLpTokenAddressAndBump(pair); - const accountLp = await getOrCreateAssociatedTokenAccount( + // getorCreate has the advantage since you can call it several times with no downsides + const accountLp = await createAssociatedTokenAccount( connection, owner, tokenLp, owner.publicKey, undefined, - undefined, - undefined, TOKEN_2022_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID ); - - - try { - await protocol.mintLpToken( - { - liquidityDelta, - pair, - index: positionId, - invProgram: INVARIANT_ADDRESS, - invState: stateAddress, - position: positionAddress, - lastPosition: lastPositionAddress, - pool: poolAddress, - positionList: positionListAddress, - lowerTick: lowerTickAddress, - upperTick: upperTickAddress, - tickmap, - accountX: userTokenXAccount.address, - accountY: userTokenYAccount.address, - invReserveX: tokenXReserve, - invReserveY: tokenYReserve, - invProgramAuthority: market.programAuthority, - }, - owner - ); - } catch (error) { - if (error instanceof SendTransactionError) { - console.log(await error.getLogs(connection)); - } - } + await protocol.mintLpToken( + { + liquidityDelta, + pair, + index: positionId, + invProgram: INVARIANT_ADDRESS, + invState: stateAddress, + position: positionAddress, + lastPosition: lastPositionAddress, + pool: poolAddress, + positionList: positionListAddress, + lowerTick: lowerTickAddress, + upperTick: upperTickAddress, + tickmap, + accountX: userTokenXAccount.address, + accountY: userTokenYAccount.address, + invReserveX: tokenXReserve, + invReserveY: tokenYReserve, + invProgramAuthority: market.programAuthority, + }, + owner + ); - // const position = await market.getPosition(protocol.programAuthority, positionId); - // assert.ok(position); + // const { positionAddress: lastPositionAddress2 } = + // await market.getPositionAddress(protocol.programAuthority, 1); + + // console.log("mint 2") + + // await protocol.mintLpToken( + // { + // liquidityDelta, + // pair, + // index: positionId, + // invProgram: INVARIANT_ADDRESS, + // invState: stateAddress, + // position: positionAddress, + // lastPosition: lastPositionAddress, + // pool: poolAddress, + // positionList: positionListAddress, + // lowerTick: lowerTickAddress, + // upperTick: upperTickAddress, + // tickmap, + // accountX: userTokenXAccount.address, + // accountY: userTokenYAccount.address, + // invReserveX: tokenXReserve, + // invReserveY: tokenYReserve, + // invProgramAuthority: market.programAuthority, + // }, + // owner + // ); + + const position = await market.getPosition( + protocol.programAuthority, + positionId + ); + console.log(position); + assert.ok(position); }); }); From fad8081d347220467b0e8faef72b2ffee258e0ab Mon Sep 17 00:00:00 2001 From: DevRozaDev <158298065+DevRozaDev@users.noreply.github.com> Date: Wed, 21 Aug 2024 01:16:51 +0200 Subject: [PATCH 4/6] support minting for a single LpPool multiple times --- protocol/programs/invariant/src/util.rs | 14 +-- .../programs/protocol/src/contexts/mint.rs | 35 +++++- protocol/tests/mint.test.ts | 101 +++++++++++++----- 3 files changed, 111 insertions(+), 39 deletions(-) diff --git a/protocol/programs/invariant/src/util.rs b/protocol/programs/invariant/src/util.rs index 0d97d17..92ed914 100644 --- a/protocol/programs/invariant/src/util.rs +++ b/protocol/programs/invariant/src/util.rs @@ -1,8 +1,6 @@ -use anchor_lang::__private::CLOSED_ACCOUNT_DISCRIMINATOR; -use anchor_lang::error::ErrorCode::AccountDidNotDeserialize; +use anchor_lang::solana_program::system_program; use std::cell::RefMut; use std::convert::TryInto; -use std::io::Write; use crate::math::calculate_price_sqrt; use crate::structs::pool::Pool; @@ -131,14 +129,8 @@ pub fn close<'info>(info: AccountInfo<'info>, sol_destination: AccountInfo<'info dest_starting_lamports.checked_add(info.lamports()).unwrap(); **info.lamports.borrow_mut() = 0; - // Mark the account discriminator as closed. - let mut data = info.try_borrow_mut_data()?; - let dst: &mut [u8] = &mut data; - let mut cursor = std::io::Cursor::new(dst); - cursor - .write_all(&CLOSED_ACCOUNT_DISCRIMINATOR) - .map_err(|_| AccountDidNotDeserialize)?; - Ok(()) + info.assign(&system_program::ID); + info.realloc(0, false).map_err(Into::into) } #[cfg(test)] diff --git a/protocol/programs/protocol/src/contexts/mint.rs b/protocol/programs/protocol/src/contexts/mint.rs index cc8fa2c..aec0506 100644 --- a/protocol/programs/protocol/src/contexts/mint.rs +++ b/protocol/programs/protocol/src/contexts/mint.rs @@ -13,7 +13,7 @@ use anchor_spl::{ use decimal::{BetweenDecimals, Decimal}; use invariant::decimals::{Liquidity as InvLiquidity, Price as InvPrice}; use invariant::{ - cpi::accounts::{CreatePosition, RemovePosition}, + cpi::accounts::{CreatePosition, CreateTick, RemovePosition}, structs::{Pool, Position}, }; @@ -242,6 +242,28 @@ impl<'info> MintLpTokenCtx<'info> { ) } + pub fn create_tick( + &self, + account: AccountInfo<'info>, + ) -> CpiContext<'_, '_, '_, 'info, CreateTick<'info>> { + CpiContext::new( + self.inv_program.to_account_info(), + CreateTick { + tick: account, + pool: self.pool.to_account_info(), + tickmap: self.tickmap.to_account_info(), + + payer: self.owner.to_account_info(), + token_x: self.token_x.to_account_info(), + token_y: self.token_y.to_account_info(), + token_x_program: self.token_x_program.to_account_info(), + token_y_program: self.token_y_program.to_account_info(), + rent: self.rent.to_account_info(), + system_program: self.system_program.to_account_info(), + }, + ) + } + pub fn validate_pool(&self) -> Result<()> { let lp_pool = &self.lp_pool.load()?; let pool = &self.pool.load()?; @@ -373,7 +395,18 @@ impl<'info> MintLpTokenCtx<'info> { lower_tick_index, upper_tick_index, )?; + + invariant::cpi::create_tick( + self.create_tick(self.lower_tick.to_account_info()), + lower_tick_index, + )?; + + invariant::cpi::create_tick( + self.create_tick(self.upper_tick.to_account_info()), + upper_tick_index, + )?; } + { let new_liquidity = shares.positions_details.liquidity.v; msg!("new_liquidity: {}", new_liquidity); diff --git a/protocol/tests/mint.test.ts b/protocol/tests/mint.test.ts index 6e5be59..b8034b4 100644 --- a/protocol/tests/mint.test.ts +++ b/protocol/tests/mint.test.ts @@ -203,33 +203,80 @@ describe("mint lp token", () => { owner ); - // const { positionAddress: lastPositionAddress2 } = - // await market.getPositionAddress(protocol.programAuthority, 1); - - // console.log("mint 2") - - // await protocol.mintLpToken( - // { - // liquidityDelta, - // pair, - // index: positionId, - // invProgram: INVARIANT_ADDRESS, - // invState: stateAddress, - // position: positionAddress, - // lastPosition: lastPositionAddress, - // pool: poolAddress, - // positionList: positionListAddress, - // lowerTick: lowerTickAddress, - // upperTick: upperTickAddress, - // tickmap, - // accountX: userTokenXAccount.address, - // accountY: userTokenYAccount.address, - // invReserveX: tokenXReserve, - // invReserveY: tokenYReserve, - // invProgramAuthority: market.programAuthority, - // }, - // owner - // ); + console.log("mint 2"); + + await protocol.mintLpToken( + { + liquidityDelta, + pair, + index: positionId, + invProgram: INVARIANT_ADDRESS, + invState: stateAddress, + position: positionAddress, + lastPosition: lastPositionAddress, + pool: poolAddress, + positionList: positionListAddress, + lowerTick: lowerTickAddress, + upperTick: upperTickAddress, + tickmap, + accountX: userTokenXAccount.address, + accountY: userTokenYAccount.address, + invReserveX: tokenXReserve, + invReserveY: tokenYReserve, + invProgramAuthority: market.programAuthority, + }, + owner + ); + + console.log("mint 3"); + + await protocol.mintLpToken( + { + liquidityDelta, + pair, + index: positionId, + invProgram: INVARIANT_ADDRESS, + invState: stateAddress, + position: positionAddress, + lastPosition: lastPositionAddress, + pool: poolAddress, + positionList: positionListAddress, + lowerTick: lowerTickAddress, + upperTick: upperTickAddress, + tickmap, + accountX: userTokenXAccount.address, + accountY: userTokenYAccount.address, + invReserveX: tokenXReserve, + invReserveY: tokenYReserve, + invProgramAuthority: market.programAuthority, + }, + owner + ); + + console.log("mint 4"); + + await protocol.mintLpToken( + { + liquidityDelta, + pair, + index: positionId, + invProgram: INVARIANT_ADDRESS, + invState: stateAddress, + position: positionAddress, + lastPosition: lastPositionAddress, + pool: poolAddress, + positionList: positionListAddress, + lowerTick: lowerTickAddress, + upperTick: upperTickAddress, + tickmap, + accountX: userTokenXAccount.address, + accountY: userTokenYAccount.address, + invReserveX: tokenXReserve, + invReserveY: tokenYReserve, + invProgramAuthority: market.programAuthority, + }, + owner + ); const position = await market.getPosition( protocol.programAuthority, From b30776c9e77ce923d9daadf37246651f32c092ad Mon Sep 17 00:00:00 2001 From: DevRozaDev <158298065+DevRozaDev@users.noreply.github.com> Date: Wed, 21 Aug 2024 11:46:34 +0200 Subject: [PATCH 5/6] explicitly support only one pool for mint --- .../programs/protocol/src/contexts/init_pool.rs | 1 + protocol/programs/protocol/src/contexts/mint.rs | 14 ++++++++++++-- protocol/programs/protocol/src/states/lp_pool.rs | 1 + protocol/sdk/src/idl/protocol.ts | 8 ++++++++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/protocol/programs/protocol/src/contexts/init_pool.rs b/protocol/programs/protocol/src/contexts/init_pool.rs index c2801ae..7c831b2 100644 --- a/protocol/programs/protocol/src/contexts/init_pool.rs +++ b/protocol/programs/protocol/src/contexts/init_pool.rs @@ -78,6 +78,7 @@ impl InitPoolCtx<'_> { **lp_pool = LpPool { invariant_position: Pubkey::default(), + position_bump: 0, leftover_x: 0, leftover_y: 0, token_x, diff --git a/protocol/programs/protocol/src/contexts/mint.rs b/protocol/programs/protocol/src/contexts/mint.rs index aec0506..a1dd34a 100644 --- a/protocol/programs/protocol/src/contexts/mint.rs +++ b/protocol/programs/protocol/src/contexts/mint.rs @@ -295,10 +295,16 @@ impl<'info> MintLpTokenCtx<'info> { let lp_pool = &self.lp_pool.load()?; if lp_pool.invariant_position != Pubkey::default() { let upper_tick_index = get_max_tick(lp_pool.tick_spacing); - let lower_tick_index = -upper_tick_index; + let lower_tick_index = get_min_tick(lp_pool.tick_spacing); let position = try_from!(AccountLoader::, &self.position)?; require_eq!(position.load()?.upper_tick_index, upper_tick_index); require_eq!(position.load()?.lower_tick_index, lower_tick_index); + // explicitly support only one pool till more positions (pools) are supported + require_keys_eq!(self.position.key(), self.last_position.key()); + let owner = self.program_authority.key(); + let seeds = [b"positionv1", owner.as_ref(), &0i32.to_le_bytes()]; + let (pubkey, _bump) = Pubkey::find_program_address(&seeds, &invariant::ID); + require_keys_eq!(self.last_position.key(), pubkey); } Ok(()) } @@ -423,7 +429,11 @@ impl<'info> MintLpTokenCtx<'info> { )?; // TODO: adjust to support multiple pools - lp_pool.invariant_position = self.position.key(); + lp_pool.invariant_position = self.last_position.key(); + { + let new_position = try_from!(AccountLoader::, &self.last_position)?; + lp_pool.position_bump = new_position.load()?.bump; + } } // mint LP tokens for user mint_to( diff --git a/protocol/programs/protocol/src/states/lp_pool.rs b/protocol/programs/protocol/src/states/lp_pool.rs index a7922d4..9dc5191 100644 --- a/protocol/programs/protocol/src/states/lp_pool.rs +++ b/protocol/programs/protocol/src/states/lp_pool.rs @@ -11,6 +11,7 @@ impl DerivedAccountIdentifier for LpPool { #[derive(PartialEq, Default, Debug, InitSpace)] pub struct LpPool { pub invariant_position: Pubkey, + pub position_bump: u8, pub leftover_x: u64, pub leftover_y: u64, pub token_x: Pubkey, diff --git a/protocol/sdk/src/idl/protocol.ts b/protocol/sdk/src/idl/protocol.ts index bf0a6d9..c004cd2 100644 --- a/protocol/sdk/src/idl/protocol.ts +++ b/protocol/sdk/src/idl/protocol.ts @@ -847,6 +847,10 @@ export type Protocol = { "name": "invariantPosition", "type": "publicKey" }, + { + "name": "positionBump", + "type": "u8" + }, { "name": "leftoverX", "type": "u64" @@ -1851,6 +1855,10 @@ export const IDL: Protocol = { "name": "invariantPosition", "type": "publicKey" }, + { + "name": "positionBump", + "type": "u8" + }, { "name": "leftoverX", "type": "u64" From a767fcf9c8a25600c587e17a4ae47ed527ecb05e Mon Sep 17 00:00:00 2001 From: none00y Date: Wed, 21 Aug 2024 14:37:29 +0200 Subject: [PATCH 6/6] fix liquidity parameter --- protocol/programs/protocol/src/lib.rs | 2 +- protocol/tests/mint.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/protocol/programs/protocol/src/lib.rs b/protocol/programs/protocol/src/lib.rs index 8fb5a54..6c1a137 100644 --- a/protocol/programs/protocol/src/lib.rs +++ b/protocol/programs/protocol/src/lib.rs @@ -100,6 +100,6 @@ pub mod protocol { pub fn mint_lp_token(ctx: Context, liquidity: u128, index: u32) -> Result<()> { ctx.accounts - .process(Liquidity::from_integer(liquidity), index) + .process(Liquidity::new(liquidity), index) } } diff --git a/protocol/tests/mint.test.ts b/protocol/tests/mint.test.ts index b8034b4..d8cc6d5 100644 --- a/protocol/tests/mint.test.ts +++ b/protocol/tests/mint.test.ts @@ -134,7 +134,7 @@ describe("mint lp token", () => { const positionId = 0; const lastPositionId = 0; - const liquidityDelta = new BN(5000); + const liquidityDelta = new BN(500000000); const { address: stateAddress } = await market.getStateAddress(); const poolAddress = await pair.getAddress(INVARIANT_ADDRESS);