use crate::{
constants::MAX_BINS_PER_POSITION,
errors::DloomError,
events::DlmmPositionOpened,
dlmm::{state::{DlmmPool, Position}},
};
use anchor_lang::prelude::*;
use anchor_spl::{
associated_token::AssociatedToken,
token_interface::{self, Mint, MintTo, TokenAccount, TokenInterface},
};
use mpl_token_metadata::{
accounts::{MasterEdition, Metadata},
instructions::{
CreateMasterEditionV3Cpi, CreateMasterEditionV3CpiAccounts,
CreateMasterEditionV3InstructionArgs, CreateMetadataAccountV3Cpi,
CreateMetadataAccountV3CpiAccounts, CreateMetadataAccountV3InstructionArgs,
},
types::DataV2,
ID as TOKEN_METADATA_ID,
};
pub fn handle_dlmm_open_position(
ctx: Context<DlmmOpenPosition>,
lower_bin_id: i32,
upper_bin_id: i32,
) -> Result<()> {
require!(lower_bin_id < upper_bin_id, DloomError::InvalidBinRange);
let bin_step = ctx.accounts.dlmm_pool.bin_step as i32;
require!(
lower_bin_id % bin_step == 0 && upper_bin_id % bin_step == 0,
DloomError::InvalidBinId
);
let range = (upper_bin_id - lower_bin_id) / bin_step as i32;
require!(range <= MAX_BINS_PER_POSITION, DloomError::RangeTooWide);
let position = &mut ctx.accounts.position;
position.pool = ctx.accounts.dlmm_pool.key(); position.owner = ctx.accounts.owner.key();
position.lower_bin_id = lower_bin_id;
position.upper_bin_id = upper_bin_id;
position.liquidity = 0; position.position_mint = ctx.accounts.position_mint.key();
position.fee_growth_snapshot_a = 0;
position.fee_growth_snapshot_b = 0;
token_interface::mint_to(
CpiContext::new(
ctx.accounts.token_program.to_account_info(),
MintTo {
mint: ctx.accounts.position_mint.to_account_info(),
to: ctx.accounts.user_position_nft_account.to_account_info(),
authority: ctx.accounts.owner.to_account_info(),
},
),
1, )?;
let base_uri = "https://api.yourprotocol.com/metadata/";
let mint_key_string = ctx.accounts.position_mint.key().to_string();
let dynamic_uri = format!("{}{}", base_uri, mint_key_string);
CreateMetadataAccountV3Cpi::new(
&ctx.accounts.token_metadata_program,
CreateMetadataAccountV3CpiAccounts {
metadata: &ctx.accounts.metadata_account,
mint: &ctx.accounts.position_mint.to_account_info(),
mint_authority: &ctx.accounts.owner.to_account_info(),
payer: &ctx.accounts.owner.to_account_info(),
update_authority: (&ctx.accounts.owner.to_account_info(), true),
system_program: &ctx.accounts.system_program,
rent: Some(&ctx.accounts.rent.to_account_info()),
},
CreateMetadataAccountV3InstructionArgs {
data: DataV2 {
name: "DLMM Position".to_string(),
symbol: "DLMMLP".to_string(),
uri: dynamic_uri,
seller_fee_basis_points: 0,
creators: None,
collection: None,
uses: None,
},
is_mutable: true,
collection_details: None,
},
)
.invoke()?;
CreateMasterEditionV3Cpi::new(
&ctx.accounts.token_metadata_program,
CreateMasterEditionV3CpiAccounts {
edition: &ctx.accounts.master_edition_account,
mint: &ctx.accounts.position_mint.to_account_info(),
update_authority: &ctx.accounts.owner.to_account_info(),
mint_authority: &ctx.accounts.owner.to_account_info(),
payer: &ctx.accounts.owner.to_account_info(),
metadata: &ctx.accounts.metadata_account,
token_program: &ctx.accounts.token_program,
system_program: &ctx.accounts.system_program,
rent: Some(&ctx.accounts.rent.to_account_info()),
},
CreateMasterEditionV3InstructionArgs {
max_supply: Some(0),
},
)
.invoke()?;
emit!(DlmmPositionOpened {
pool_address: ctx.accounts.dlmm_pool.key(),
owner: ctx.accounts.owner.key(),
position_address: ctx.accounts.position.key(),
position_mint: ctx.accounts.position_mint.key(),
lower_bin_id,
upper_bin_id,
});
Ok(())
}
#[derive(Accounts)]
pub struct DlmmOpenPosition<'info> {
#[account(mut)]
pub owner: Signer<'info>,
pub dlmm_pool: Box<Account<'info, DlmmPool>>,
#[account(
init,
payer = owner,
space = 8 + 256, // Allocate space for Position struct
seeds = [b"position", position_mint.key().as_ref()],
bump
)]
pub position: Box<Account<'info, Position>>,
#[account(
init,
payer = owner,
mint::decimals = 0,
mint::authority = owner,
mint::freeze_authority = owner
)]
pub position_mint: InterfaceAccount<'info, Mint>,
#[account(
init,
payer = owner,
associated_token::mint = position_mint,
associated_token::authority = owner
)]
pub user_position_nft_account: InterfaceAccount<'info, TokenAccount>,
#[account(mut, address = Metadata::find_pda(&position_mint.key()).0)]
pub metadata_account: AccountInfo<'info>,
#[account(mut, address = MasterEdition::find_pda(&position_mint.key()).0)]
pub master_edition_account: AccountInfo<'info>,
pub system_program: Program<'info, System>,
pub token_program: Interface<'info, TokenInterface>,
pub associated_token_program: Program<'info, AssociatedToken>,
#[account(address = TOKEN_METADATA_ID)]
pub token_metadata_program: AccountInfo<'info>,
pub rent: Sysvar<'info, Rent>,
}