Skip to content

Commit

Permalink
lock position
Browse files Browse the repository at this point in the history
  • Loading branch information
0x777A committed Aug 26, 2024
1 parent 0059ce4 commit fb290e2
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 12 deletions.
4 changes: 2 additions & 2 deletions programs/amm/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ pub enum ErrorCode {
InvaildTickIndex,
#[msg("The lower tick must be below the upper tick")]
TickInvaildOrder,
#[msg("The tick must be greater, or equal to the minimum tick(-221818)")]
#[msg("The tick must be greater, or equal to the minimum tick(-443636)")]
TickLowerOverflow,
#[msg("The tick must be lesser than, or equal to the maximum tick(221818)")]
#[msg("The tick must be lesser than, or equal to the maximum tick(443636)")]
TickUpperOverflow,
#[msg("tick % tick_spacing must be zero")]
TickAndSpacingNotMatch,
Expand Down
2 changes: 1 addition & 1 deletion programs/amm/src/instructions/create_pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub struct CreatePool<'info> {
)]
pub pool_state: AccountLoader<'info, PoolState>,

/// Token_0 mint, the key must grater then token_1 mint.
/// Token_0 mint, the key must be grater then token_1 mint.
#[account(
constraint = token_mint_0.key() < token_mint_1.key(),
mint::token_program = token_program_0
Expand Down
29 changes: 24 additions & 5 deletions programs/amm/src/instructions/decrease_liquidity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ pub fn decrease_liquidity_v1<'a, 'b, 'c: 'info, 'info>(
amount_1_min: u64,
) -> Result<()> {
decrease_liquidity(
&ctx.accounts.nft_owner.to_account_info(),
&ctx.accounts.nft_account,
&ctx.accounts.pool_state,
&mut ctx.accounts.protocol_position,
&mut ctx.accounts.personal_position,
Expand Down Expand Up @@ -230,6 +232,8 @@ pub fn decrease_liquidity_v2<'a, 'b, 'c: 'info, 'info>(
amount_1_min: u64,
) -> Result<()> {
decrease_liquidity(
&ctx.accounts.nft_owner.to_account_info(),
&ctx.accounts.nft_account,
&ctx.accounts.pool_state,
&mut ctx.accounts.protocol_position,
&mut ctx.accounts.personal_position,
Expand All @@ -252,6 +256,8 @@ pub fn decrease_liquidity_v2<'a, 'b, 'c: 'info, 'info>(
}

pub fn decrease_liquidity<'a, 'b, 'c: 'info, 'info>(
nft_owner: &'b AccountInfo,
nft_account: &'b Box<InterfaceAccount<'info, TokenAccount>>,
pool_state_loader: &'b AccountLoader<'info, PoolState>,
protocol_position: &'b mut Box<Account<'info, ProtocolPositionState>>,
personal_position: &'b mut Box<Account<'info, PersonalPositionState>>,
Expand All @@ -275,12 +281,13 @@ pub fn decrease_liquidity<'a, 'b, 'c: 'info, 'info>(
// let memp_program = accounts.memo_program.as_ref().unwrap().to_account_info();
// invoke_memo_instruction(DECREASE_MEMO_MSG, memp_program)?;
// }
require!(nft_account.amount == 1, ErrorCode::NotApproved);
assert!(liquidity <= personal_position.liquidity);
let liquidity_before;
let pool_sqrt_price_x64;
let pool_tick_current;
let mut tickarray_bitmap_extension = None;

let mut locked_position_info = None;
let remaining_collect_accounts = &mut Vec::new();
{
let pool_state = pool_state_loader.load()?;
Expand All @@ -299,13 +306,15 @@ pub fn decrease_liquidity<'a, 'b, 'c: 'info, 'info>(
tick_array_upper_loader.load()?.start_tick_index,
]);

let expect_tickarray_bitmap_extension = TickArrayBitmapExtension::key(pool_state.key());
let expect_locked_position = LockedPositionState::key(personal_position.key());
for account_info in remaining_accounts.into_iter() {
if account_info
.key()
.eq(&TickArrayBitmapExtension::key(pool_state.key()))
{
if account_info.key().eq(&expect_tickarray_bitmap_extension) {
tickarray_bitmap_extension = Some(account_info);
continue;
} else if account_info.key().eq(&expect_locked_position) {
locked_position_info = Some(account_info);
continue;
}
remaining_collect_accounts.push(account_info);
}
Expand All @@ -317,6 +326,16 @@ pub fn decrease_liquidity<'a, 'b, 'c: 'info, 'info>(
}
}

if nft_account.owner != nft_owner.key() {
require!(
locked_position_info.is_some() && nft_account.owner == crate::id() && liquidity == 0,
ErrorCode::NotApproved
);
let locked_position_state =
Account::<LockedPositionState>::try_from(locked_position_info.unwrap())?;
locked_position_state.check(nft_owner.key(), personal_position.key(), nft_account.key())?;
}

let (decrease_amount_0, latest_fees_owed_0, decrease_amount_1, latest_fees_owed_1) =
decrease_liquidity_and_update_position(
pool_state_loader,
Expand Down
66 changes: 66 additions & 0 deletions programs/amm/src/instructions/lock_position.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use crate::states::*;
use anchor_lang::prelude::*;
use anchor_spl::token::spl_token::instruction::AuthorityType;
use anchor_spl::token::{set_authority, SetAuthority, Token};
use anchor_spl::token_interface::TokenAccount;

pub const LOCK_POSITION_SEED: &str = "locked_position";
#[derive(Accounts)]
pub struct LockPosition<'info> {
/// The position owner or delegated authority
#[account(mut)]
pub nft_owner: Signer<'info>,

/// The token account for the tokenized position
#[account(
constraint = nft_account.mint == personal_position.nft_mint,
token::token_program = token_program,
)]
pub nft_account: Box<InterfaceAccount<'info, TokenAccount>>,

/// Decrease liquidity for this position
#[account(mut)]
pub personal_position: Box<Account<'info, PersonalPositionState>>,

#[account(
init,
seeds = [
LOCKED_POSITION_SEED.as_bytes(),
personal_position.key().as_ref(),
],
bump,
payer = nft_owner,
space = LockedPositionState::LEN
)]
pub locked_position: Box<Account<'info, LockedPositionState>>,

/// SPL program to transfer out tokens
pub token_program: Program<'info, Token>,

/// Program to create the position manager state account
pub system_program: Program<'info, System>,
}

pub fn lock_position<'a, 'b, 'c: 'info, 'info>(
ctx: Context<'a, 'b, 'c, 'info, LockPosition<'info>>,
) -> Result<()> {
require_gt!(ctx.accounts.personal_position.liquidity, 0);
ctx.accounts.locked_position.initialize(
ctx.bumps.locked_position,
ctx.accounts.nft_owner.key(),
ctx.accounts.personal_position.pool_id,
ctx.accounts.personal_position.key(),
ctx.accounts.nft_account.key(),
);

let cpi_context = CpiContext::new(
ctx.accounts.token_program.to_account_info(),
SetAuthority {
current_authority: ctx.accounts.nft_owner.to_account_info(),
account_or_mint: ctx.accounts.nft_account.to_account_info(),
},
);
set_authority(cpi_context, AuthorityType::AccountOwner, Some(crate::id()))?;

Ok(())
}
3 changes: 3 additions & 0 deletions programs/amm/src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ pub use collect_remaining_rewards::*;

pub mod admin;
pub use admin::*;

pub mod lock_position;
pub use lock_position::*;
1 change: 0 additions & 1 deletion programs/amm/src/instructions/open_position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,6 @@ pub fn open_position<'a, 'b, 'c: 'info, 'info>(
personal_position.pool_id = pool_state_loader.key();
personal_position.tick_lower_index = tick_lower_index;
personal_position.tick_upper_index = tick_upper_index;

personal_position.fee_growth_inside_0_last_x64 =
protocol_position.fee_growth_inside_0_last_x64;
personal_position.fee_growth_inside_1_last_x64 =
Expand Down
17 changes: 15 additions & 2 deletions programs/amm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ pub mod amm_v3 {
/// * `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity
/// * `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity
///
#[access_control(is_authorized_for_token(&ctx.accounts.nft_owner, &ctx.accounts.nft_account))]
// #[access_control(is_authorized_for_token(&ctx.accounts.nft_owner, &ctx.accounts.nft_account))]
pub fn decrease_liquidity<'a, 'b, 'c: 'info, 'info>(
ctx: Context<'a, 'b, 'c, 'info, DecreaseLiquidity<'info>>,
liquidity: u128,
Expand All @@ -420,7 +420,7 @@ pub mod amm_v3 {
/// * `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity
/// * `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity
///
#[access_control(is_authorized_for_token(&ctx.accounts.nft_owner, &ctx.accounts.nft_account))]
// #[access_control(is_authorized_for_token(&ctx.accounts.nft_owner, &ctx.accounts.nft_account))]
pub fn decrease_liquidity_v2<'a, 'b, 'c: 'info, 'info>(
ctx: Context<'a, 'b, 'c, 'info, DecreaseLiquidityV2<'info>>,
liquidity: u128,
Expand All @@ -430,6 +430,19 @@ pub mod amm_v3 {
instructions::decrease_liquidity_v2(ctx, liquidity, amount_0_min, amount_1_min)
}

/// Lock a exist position
///
/// # Arguments
///
/// * `ctx` - The context of accounts
///
#[access_control(is_authorized_for_token(&ctx.accounts.nft_owner, &ctx.accounts.nft_account))]
pub fn lock_position<'a, 'b, 'c: 'info, 'info>(
ctx: Context<'a, 'b, 'c, 'info, LockPosition<'info>>,
) -> Result<()> {
instructions::lock_position(ctx)
}

/// Swaps one token for as much as possible of another token across a single pool
///
/// # Arguments
Expand Down
54 changes: 54 additions & 0 deletions programs/amm/src/states/locked_position.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use anchor_lang::prelude::*;
pub const LOCKED_POSITION_SEED: &str = "locked_position";

#[account]
#[derive(Default, Debug)]
pub struct LockedPositionState {
/// Bump to identify PDA
pub bump: [u8; 1],
/// Record owner
pub owner: Pubkey,
/// The ID of the pool with which this record is connected
pub pool_id: Pubkey,
/// The ID of the position with which this record is connected
pub position_id: Pubkey,
/// NFT Account
pub nft_account: Pubkey,
/// Unused bytes for future upgrades.
pub padding: [u64; 8],
}

impl LockedPositionState {
pub const LEN: usize = 8 + 1 + 32 + 32 + 32 + 32 + 8 * 8;

pub fn key(position_id: Pubkey) -> Pubkey {
Pubkey::find_program_address(
&[LOCKED_POSITION_SEED.as_bytes(), position_id.as_ref()],
&crate::id(),
)
.0
}

pub fn initialize(
&mut self,
bump: u8,
owner: Pubkey,
pool_id: Pubkey,
position_id: Pubkey,
nft_account: Pubkey,
) {
self.bump = [bump];
self.owner = owner;
self.pool_id = pool_id;
self.position_id = position_id;
self.nft_account = nft_account;
self.padding = [0; 8];
}

pub fn check(&self, owner: Pubkey, position_id: Pubkey, nft_account: Pubkey) -> Result<()> {
require_keys_eq!(self.owner, owner);
require_keys_eq!(self.position_id, position_id);
require_keys_eq!(self.nft_account, nft_account);
Ok(())
}
}
2 changes: 2 additions & 0 deletions programs/amm/src/states/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod config;
pub mod locked_position;
pub mod operation_account;
pub mod oracle;
pub mod personal_position;
Expand All @@ -8,6 +9,7 @@ pub mod tick_array;
pub mod tickarray_bitmap_extension;

pub use config::*;
pub use locked_position::*;
pub use operation_account::*;
pub use oracle::*;
pub use personal_position::*;
Expand Down
2 changes: 1 addition & 1 deletion programs/amm/src/states/oracle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ impl ObservationState {
Ok(())
}

// Writes an oracle observation to the account
/// Writes an oracle observation to the account
///
/// # Arguments
///
Expand Down
2 changes: 2 additions & 0 deletions programs/amm/src/states/personal_position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ pub struct PersonalPositionState {

// Position reward info
pub reward_infos: [PositionRewardInfo; REWARD_NUM],

// account update recent epoch
pub recent_epoch: u64,

// Unused bytes for future upgrades.
pub padding: [u64; 7],
}
Expand Down

0 comments on commit fb290e2

Please sign in to comment.