Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add lock position instruction #88

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
68 changes: 68 additions & 0 deletions programs/amm/src/instructions/lock_position.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use crate::states::*;
use crate::util::get_recent_epoch;
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()]
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(),
get_recent_epoch()?,
);

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
58 changes: 58 additions & 0 deletions programs/amm/src/states/locked_position.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
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,
/// account update recent epoch
pub recent_epoch: u64,
/// Unused bytes for future upgrades.
pub padding: [u64; 8],
}

impl LockedPositionState {
pub const LEN: usize = 8 + 1 + 32 + 32 + 32 + 32 + 8 + 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,
recent_epoch:u64,
) {
self.bump = [bump];
self.owner = owner;
self.pool_id = pool_id;
self.position_id = position_id;
self.nft_account = nft_account;
self.recent_epoch = recent_epoch;
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