dloom_flow/dlmm/instructions/
open_position.rs1use crate::{
4 constants::MAX_BINS_PER_POSITION,
5 errors::DloomError,
6 events::DlmmPositionOpened,
7 dlmm::{state::{DlmmPool, Position}},
8};
9use anchor_lang::prelude::*;
10use anchor_spl::{
11 associated_token::AssociatedToken,
12 token_interface::{self, Mint, MintTo, TokenAccount, TokenInterface},
13};
14use mpl_token_metadata::{
15 accounts::{MasterEdition, Metadata},
16 instructions::{
17 CreateMasterEditionV3Cpi, CreateMasterEditionV3CpiAccounts,
18 CreateMasterEditionV3InstructionArgs, CreateMetadataAccountV3Cpi,
19 CreateMetadataAccountV3CpiAccounts, CreateMetadataAccountV3InstructionArgs,
20 },
21 types::DataV2,
22 ID as TOKEN_METADATA_ID,
23};
24
25pub fn handle_dlmm_open_position(
27 ctx: Context<DlmmOpenPosition>,
28 lower_bin_id: i32,
29 upper_bin_id: i32,
30) -> Result<()> {
31 require!(lower_bin_id < upper_bin_id, DloomError::InvalidBinRange);
33 let bin_step = ctx.accounts.dlmm_pool.bin_step as i32;
34 require!(
35 lower_bin_id % bin_step == 0 && upper_bin_id % bin_step == 0,
36 DloomError::InvalidBinId
37 );
38
39 let range = (upper_bin_id - lower_bin_id) / bin_step as i32;
40 require!(range <= MAX_BINS_PER_POSITION, DloomError::RangeTooWide);
41
42 let position = &mut ctx.accounts.position;
44 position.pool = ctx.accounts.dlmm_pool.key(); position.owner = ctx.accounts.owner.key();
46 position.lower_bin_id = lower_bin_id;
47 position.upper_bin_id = upper_bin_id;
48 position.liquidity = 0; position.position_mint = ctx.accounts.position_mint.key();
50 position.fee_growth_snapshot_a = 0;
51 position.fee_growth_snapshot_b = 0;
52
53 token_interface::mint_to(
55 CpiContext::new(
56 ctx.accounts.token_program.to_account_info(),
57 MintTo {
58 mint: ctx.accounts.position_mint.to_account_info(),
59 to: ctx.accounts.user_position_nft_account.to_account_info(),
60 authority: ctx.accounts.owner.to_account_info(),
61 },
62 ),
63 1, )?;
65
66 let base_uri = "https://api.yourprotocol.com/metadata/";
67 let mint_key_string = ctx.accounts.position_mint.key().to_string();
68 let dynamic_uri = format!("{}{}", base_uri, mint_key_string);
69 CreateMetadataAccountV3Cpi::new(
71 &ctx.accounts.token_metadata_program,
72 CreateMetadataAccountV3CpiAccounts {
73 metadata: &ctx.accounts.metadata_account,
74 mint: &ctx.accounts.position_mint.to_account_info(),
75 mint_authority: &ctx.accounts.owner.to_account_info(),
76 payer: &ctx.accounts.owner.to_account_info(),
77 update_authority: (&ctx.accounts.owner.to_account_info(), true),
78 system_program: &ctx.accounts.system_program,
79 rent: Some(&ctx.accounts.rent.to_account_info()),
80 },
81 CreateMetadataAccountV3InstructionArgs {
82 data: DataV2 {
83 name: "DLMM Position".to_string(),
84 symbol: "DLMMLP".to_string(),
85 uri: dynamic_uri,
86 seller_fee_basis_points: 0,
87 creators: None,
88 collection: None,
89 uses: None,
90 },
91 is_mutable: true,
92 collection_details: None,
93 },
94 )
95 .invoke()?;
96
97 CreateMasterEditionV3Cpi::new(
99 &ctx.accounts.token_metadata_program,
100 CreateMasterEditionV3CpiAccounts {
101 edition: &ctx.accounts.master_edition_account,
102 mint: &ctx.accounts.position_mint.to_account_info(),
103 update_authority: &ctx.accounts.owner.to_account_info(),
104 mint_authority: &ctx.accounts.owner.to_account_info(),
105 payer: &ctx.accounts.owner.to_account_info(),
106 metadata: &ctx.accounts.metadata_account,
107 token_program: &ctx.accounts.token_program,
108 system_program: &ctx.accounts.system_program,
109 rent: Some(&ctx.accounts.rent.to_account_info()),
110 },
111 CreateMasterEditionV3InstructionArgs {
112 max_supply: Some(0),
113 },
114 )
115 .invoke()?;
116
117 emit!(DlmmPositionOpened {
118 pool_address: ctx.accounts.dlmm_pool.key(),
119 owner: ctx.accounts.owner.key(),
120 position_address: ctx.accounts.position.key(),
121 position_mint: ctx.accounts.position_mint.key(),
122 lower_bin_id,
123 upper_bin_id,
124 });
125
126 Ok(())
127}
128
129#[derive(Accounts)]
130pub struct DlmmOpenPosition<'info> {
131 #[account(mut)]
132 pub owner: Signer<'info>,
133
134 pub dlmm_pool: Box<Account<'info, DlmmPool>>,
136
137 #[account(
138 init,
139 payer = owner,
140 space = 8 + 256, seeds = [b"position", position_mint.key().as_ref()],
142 bump
143 )]
144 pub position: Box<Account<'info, Position>>,
145
146 #[account(
147 init,
148 payer = owner,
149 mint::decimals = 0,
150 mint::authority = owner,
151 mint::freeze_authority = owner
152 )]
153 pub position_mint: InterfaceAccount<'info, Mint>,
154
155 #[account(
156 init,
157 payer = owner,
158 associated_token::mint = position_mint,
159 associated_token::authority = owner
160 )]
161 pub user_position_nft_account: InterfaceAccount<'info, TokenAccount>,
162
163 #[account(mut, address = Metadata::find_pda(&position_mint.key()).0)]
165 pub metadata_account: AccountInfo<'info>,
167
168 #[account(mut, address = MasterEdition::find_pda(&position_mint.key()).0)]
169 pub master_edition_account: AccountInfo<'info>,
171
172 pub system_program: Program<'info, System>,
173 pub token_program: Interface<'info, TokenInterface>,
174 pub associated_token_program: Program<'info, AssociatedToken>,
175 #[account(address = TOKEN_METADATA_ID)]
176 pub token_metadata_program: AccountInfo<'info>,
178 pub rent: Sysvar<'info, Rent>,
179}