-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathinitialize.rs
More file actions
203 lines (176 loc) · 7.1 KB
/
Copy pathinitialize.rs
File metadata and controls
203 lines (176 loc) · 7.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
// external dependencies
use anchor_lang::prelude::*;
use anchor_spl::token_interface::{Mint, Token2022, TokenAccount};
use cfg_if::cfg_if;
#[cfg(feature = "scaled-ui")]
use anchor_spl::token_2022_extensions::spl_pod::optional_keys::OptionalNonZeroPubkey;
#[cfg(feature = "scaled-ui")]
use spl_token_2022::extension::{
scaled_ui_amount::ScaledUiAmountConfig, BaseStateWithExtensions, ExtensionType,
StateWithExtensions,
};
// local dependencies
#[cfg(feature = "scaled-ui")]
use crate::{
constants::{INDEX_SCALE_U64, ONE_HUNDRED_PERCENT_U64},
utils::conversion::sync_multiplier,
};
use crate::{
constants::ANCHOR_DISCRIMINATOR_SIZE,
errors::ExtError,
state::{ExtGlobal, YieldConfig, EXT_GLOBAL_SEED, MINT_AUTHORITY_SEED, M_VAULT_SEED},
};
use earn::{
state::{Global as EarnGlobal, GLOBAL_SEED as EARN_GLOBAL_SEED},
ID as EARN_PROGRAM,
};
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(mut)]
pub admin: Signer<'info>,
#[account(
init,
payer = admin,
space = ANCHOR_DISCRIMINATOR_SIZE + ExtGlobal::INIT_SPACE,
seeds = [EXT_GLOBAL_SEED],
bump
)]
pub global_account: Account<'info, ExtGlobal>,
#[account(
mint::token_program = m_token_program,
address = m_earn_global_account.mint,
)]
pub m_mint: InterfaceAccount<'info, Mint>,
#[account(
mut,
mint::token_program = ext_token_program,
mint::decimals = m_mint.decimals,
constraint = ext_mint.supply == 0 @ ExtError::InvalidMint,
)]
pub ext_mint: InterfaceAccount<'info, Mint>,
/// CHECK: Validated by the seeds, stores no data
#[account(
seeds = [MINT_AUTHORITY_SEED],
bump
)]
pub ext_mint_authority: AccountInfo<'info>,
/// CHECK: Validated by the seeds, stores no data
#[account(
seeds = [M_VAULT_SEED],
bump
)]
pub m_vault: AccountInfo<'info>,
#[account(
associated_token::mint = m_mint,
associated_token::authority = m_vault,
associated_token::token_program = m_token_program,
)]
pub vault_m_token_account: InterfaceAccount<'info, TokenAccount>,
#[account(
seeds = [EARN_GLOBAL_SEED],
seeds::program = EARN_PROGRAM,
bump = m_earn_global_account.bump,
)]
pub m_earn_global_account: Account<'info, EarnGlobal>,
pub m_token_program: Program<'info, Token2022>, // we have duplicate entries for the token2022 program bc the M token program could change in the future
pub ext_token_program: Program<'info, Token2022>,
pub system_program: Program<'info, System>,
}
impl Initialize<'_> {
// This instruction initializes the Scaled UI M extension for a given ext mint.
// It sets up the global account, validates the mint and its authority,
// and initializes the Scaled UI multiplier to 1.0.
// The ext_mint must have a supply of 0 to start.
// The wrap authorities are validated and stored in the global account.
// The fee_bps is validated to be within the allowed range.
fn validate(&self, wrap_authorities: &[Pubkey], _fee_bps: u64) -> Result<()> {
// Validate the ext_mint_authority PDA is the mint authority for the ext mint
let ext_mint_authority = self.ext_mint_authority.key();
if self.ext_mint.mint_authority.unwrap_or_default() != ext_mint_authority {
return err!(ExtError::InvalidMint);
}
// Validate and create the wrap authorities array
if wrap_authorities.len() > 10 {
return err!(ExtError::InvalidParam);
}
cfg_if! {
if #[cfg(feature = "scaled-ui")] {
// Validate that the ext mint has the ScaledUiAmount extension and
// that the ext mint authority is the extension authority
{
// explicit scope to drop the borrow at the end of the code block
let ext_account_info = &self.ext_mint.to_account_info();
let ext_data = ext_account_info.try_borrow_data()?;
let ext_mint_data =
StateWithExtensions::<spl_token_2022::state::Mint>::unpack(&ext_data)?;
let extensions = ext_mint_data.get_extension_types()?;
if !extensions.contains(&ExtensionType::ScaledUiAmount) {
return err!(ExtError::InvalidMint);
}
let scaled_ui_config = ext_mint_data.get_extension::<ScaledUiAmountConfig>()?;
if scaled_ui_config.authority != OptionalNonZeroPubkey(ext_mint_authority) {
return err!(ExtError::InvalidMint);
}
}
// Validate the fee_bps is within the allowed range
if _fee_bps > ONE_HUNDRED_PERCENT_U64 {
return err!(ExtError::InvalidParam);
}
}
}
Ok(())
}
#[access_control(ctx.accounts.validate(&wrap_authorities, fee_bps))]
pub fn handler(
ctx: Context<Initialize>,
wrap_authorities: Vec<Pubkey>,
fee_bps: u64,
) -> Result<()> {
let mut wrap_authorities_array = [Pubkey::default(); 10];
for (i, authority) in wrap_authorities.iter().enumerate() {
if wrap_authorities_array.contains(authority) {
return err!(ExtError::InvalidParam);
}
wrap_authorities_array[i] = *authority;
}
let yield_config: YieldConfig;
cfg_if! {
if #[cfg(feature = "scaled-ui")] {
yield_config = YieldConfig {
fee_bps,
last_m_index: ctx.accounts.m_earn_global_account.index,
last_ext_index: INDEX_SCALE_U64, // we set the extension index to 1.0 initially
};
} else {
yield_config = YieldConfig {};
}
}
// Initialize the ExtGlobal account
ctx.accounts.global_account.set_inner(ExtGlobal {
admin: ctx.accounts.admin.key(),
ext_mint: ctx.accounts.ext_mint.key(),
m_mint: ctx.accounts.m_mint.key(),
m_earn_global_account: ctx.accounts.m_earn_global_account.key(),
bump: ctx.bumps.global_account,
m_vault_bump: ctx.bumps.m_vault,
ext_mint_authority_bump: ctx.bumps.ext_mint_authority,
wrap_authorities: wrap_authorities_array,
yield_config,
});
// Set the ScaledUi multiplier to 1.0
// We can do this by calling the sync_multiplier function
// when the last_m_index equals the index on the m_earn_global_account
// and having last_ext_index set to 1e12
#[cfg(feature = "scaled-ui")]
sync_multiplier(
&mut ctx.accounts.ext_mint,
&mut ctx.accounts.global_account,
&ctx.accounts.m_earn_global_account,
&ctx.accounts.vault_m_token_account,
&ctx.accounts.ext_mint_authority,
&[&[MINT_AUTHORITY_SEED, &[ctx.bumps.ext_mint_authority]]],
&ctx.accounts.ext_token_program,
)?;
Ok(())
}
}