Skip to content

Commit 9826453

Browse files
authored
zero copy + permissionless crank ixs (#1581)
1 parent 5d3fd5e commit 9826453

File tree

7 files changed

+352
-48
lines changed

7 files changed

+352
-48
lines changed

programs/drift/src/instructions/admin.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4390,7 +4390,7 @@ pub fn handle_initialize_lp_pool(
43904390
last_revenue_rebalance_ts: 0,
43914391
total_fees_received: 0,
43924392
total_fees_paid: 0,
4393-
padding: [0; 6],
4393+
_padding: [0; 6],
43944394
};
43954395

43964396
let signature_seeds = get_signer_seeds(&state.signer_nonce);
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
use anchor_lang::{
2+
prelude::{Account, AccountInfo, AccountLoader, Context, Pubkey, Signer, SolanaSysvar},
3+
Accounts, Result,
4+
};
5+
6+
use crate::state::{
7+
lp_pool::{
8+
AmmConstituentDatum, AmmConstituentMappingFixed, ConstituentTargetWeightsFixed, LPPool,
9+
WeightDatum,
10+
},
11+
perp_market_map::MarketSet,
12+
state::State,
13+
zero_copy::{AccountZeroCopy, AccountZeroCopyMut, ZeroCopyLoader},
14+
};
15+
use solana_program::sysvar::clock::Clock;
16+
17+
use super::optional_accounts::{load_maps, AccountMaps};
18+
19+
pub fn handle_update_dlp_target_weights<'info, 'c: 'info>(
20+
ctx: Context<'_, 'info, 'c, 'info, UpdateDlpTargetWeights<'info>>,
21+
constituent_indexes: Vec<u16>,
22+
) -> Result<()> {
23+
let state = &ctx.accounts.state;
24+
let lp_pool = &ctx.accounts.lp_pool.load()?;
25+
let slot = Clock::get()?.slot;
26+
27+
let amm_constituent_mapping: AccountZeroCopy<
28+
'info,
29+
AmmConstituentDatum,
30+
AmmConstituentMappingFixed,
31+
> = ctx.accounts.amm_constituent_mapping.load_zc()?;
32+
33+
let mut target_weights: AccountZeroCopyMut<'info, WeightDatum, ConstituentTargetWeightsFixed> =
34+
ctx.accounts.constituent_target_weights.load_zc_mut()?;
35+
36+
let AccountMaps {
37+
perp_market_map,
38+
spot_market_map,
39+
mut oracle_map,
40+
} = load_maps(
41+
&mut ctx.remaining_accounts.iter().peekable(),
42+
&MarketSet::new(),
43+
&MarketSet::new(),
44+
slot,
45+
Some(state.oracle_guard_rails),
46+
)?;
47+
48+
let mut amm_inventories: Vec<(u16, i64)> = vec![];
49+
let mut oracle_prices: Vec<i64> = vec![];
50+
for (i, datum) in amm_constituent_mapping.iter().enumerate() {
51+
let perp_market = perp_market_map.get_ref(&datum.perp_market_index)?;
52+
let amm_inventory = perp_market.amm.get_protocol_owned_position()?;
53+
amm_inventories.push((datum.perp_market_index, amm_inventory));
54+
55+
let oracle_data = oracle_map.get_price_data_and_guard_rails(&(
56+
perp_market.amm.oracle,
57+
perp_market.amm.oracle_source,
58+
))?;
59+
60+
oracle_prices.push(oracle_data.0.price);
61+
}
62+
63+
target_weights.update_target_weights(
64+
&amm_constituent_mapping,
65+
amm_inventories.as_slice(),
66+
constituent_indexes.as_slice(),
67+
&oracle_prices.as_slice(),
68+
lp_pool.last_aum,
69+
slot,
70+
)?;
71+
72+
Ok(())
73+
}
74+
75+
#[derive(Accounts)]
76+
pub struct UpdateDlpTargetWeights<'info> {
77+
pub state: Box<Account<'info, State>>,
78+
#[account(mut)]
79+
pub keeper: Signer<'info>,
80+
/// CHECK: checked in AmmConstituentMappingZeroCopy checks
81+
pub amm_constituent_mapping: AccountInfo<'info>,
82+
/// CHECK: checked in ConstituentTargetWeightsZeroCopy checks
83+
pub constituent_target_weights: AccountInfo<'info>,
84+
pub lp_pool: AccountLoader<'info, LPPool>,
85+
}

programs/drift/src/instructions/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub use admin::*;
22
pub use constraints::*;
33
pub use if_staker::*;
44
pub use keeper::*;
5+
pub use lp_pool::*;
56
pub use pyth_lazer_oracle::*;
67
pub use pyth_pull_oracle::*;
78
pub use user::*;
@@ -10,6 +11,7 @@ mod admin;
1011
mod constraints;
1112
mod if_staker;
1213
mod keeper;
14+
mod lp_pool;
1315
pub mod optional_accounts;
1416
mod pyth_lazer_oracle;
1517
mod pyth_pull_oracle;

programs/drift/src/state/lp_pool.rs

Lines changed: 69 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1-
use crate::error::{DriftResult, ErrorCode};
1+
use crate::error::DriftResult;
22
use crate::math::casting::Cast;
33
use crate::math::constants::{PERCENTAGE_PRECISION_I128, PERCENTAGE_PRECISION_I64};
44
use crate::math::safe_math::SafeMath;
5-
use crate::state::oracle::OracleSource;
6-
use crate::state::spot_market::{SpotBalance, SpotBalanceType, SpotMarket};
5+
use crate::state::spot_market::{SpotBalance, SpotBalanceType};
76
use crate::state::traits::Size;
87
use anchor_lang::prelude::*;
8+
use anchor_lang::Discriminator;
99
use borsh::{BorshDeserialize, BorshSerialize};
1010

11+
use super::zero_copy::{AccountZeroCopy, AccountZeroCopyMut, HasLen};
12+
use crate::impl_zero_copy_loader;
13+
1114
#[cfg(test)]
1215
mod tests;
1316

14-
#[account(zero_copy)]
17+
#[account(zero_copy(unsafe))]
1518
#[derive(Default, Debug)]
1619
#[repr(C)]
1720
pub struct LPPool {
@@ -40,7 +43,7 @@ pub struct LPPool {
4043
/// timestamp of last AUM slot
4144
pub last_aum_slot: u64, // 8, 152
4245
/// timestamp of last AUM update
43-
pub last_aum_ts: u64, // 8, 160
46+
pub last_aum_ts: u64, // 8, 160``
4447

4548
/// timestamp of last vAMM revenue rebalance
4649
pub last_revenue_rebalance_ts: u64, // 8, 168
@@ -51,7 +54,7 @@ pub struct LPPool {
5154
pub total_fees_paid: u128, // 16, 192
5255

5356
pub constituents: u16, // 2, 194
54-
pub padding: [u8; 6],
57+
pub _padding: [u8; 6],
5558
}
5659

5760
impl Size for LPPool {
@@ -131,9 +134,6 @@ pub struct Constituent {
131134
pub padding: [u8; 16],
132135
}
133136

134-
// pub struct PerpConstituent {
135-
// }
136-
137137
#[zero_copy]
138138
#[derive(Debug, BorshDeserialize, BorshSerialize)]
139139
#[repr(C)]
@@ -146,6 +146,34 @@ pub struct AmmConstituentDatum {
146146
pub last_slot: u64,
147147
}
148148

149+
#[zero_copy]
150+
pub struct AmmConstituentMappingFixed {
151+
pub len: u32,
152+
}
153+
154+
impl HasLen for AmmConstituentMappingFixed {
155+
fn len(&self) -> u32 {
156+
self.len
157+
}
158+
}
159+
160+
#[account]
161+
#[derive(Debug)]
162+
#[repr(C)]
163+
pub struct AmmConstituentMapping {
164+
// PERCENTAGE_PRECISION. Each datum represents the target weight for a single (AMM, Constituent) pair.
165+
// An AMM may be partially backed by multiple Constituents
166+
pub data: Vec<AmmConstituentDatum>,
167+
}
168+
169+
impl_zero_copy_loader!(
170+
AmmConstituentMapping,
171+
crate::id,
172+
AmmConstituentMappingFixed,
173+
AmmConstituentDatum,
174+
AmmConstituentMapping::discriminator()
175+
);
176+
149177
#[zero_copy]
150178
#[derive(Debug, BorshDeserialize, BorshSerialize)]
151179
#[repr(C)]
@@ -157,13 +185,18 @@ pub struct WeightDatum {
157185
pub last_slot: u64,
158186
}
159187

160-
#[account]
188+
#[zero_copy]
161189
#[derive(Debug)]
162190
#[repr(C)]
163-
pub struct AmmConstituentMapping {
164-
// PERCENTAGE_PRECISION. Each datum represents the target weight for a single (AMM, Constituent) pair.
165-
// An AMM may be partially backed by multiple Constituents
166-
pub data: Vec<AmmConstituentDatum>,
191+
pub struct ConstituentTargetWeightsFixed {
192+
/// total elements in the flattened `data` vec
193+
pub len: u32,
194+
}
195+
196+
impl HasLen for ConstituentTargetWeightsFixed {
197+
fn len(&self) -> u32 {
198+
self.len
199+
}
167200
}
168201

169202
#[account]
@@ -174,6 +207,14 @@ pub struct ConstituentTargetWeights {
174207
pub data: Vec<WeightDatum>,
175208
}
176209

210+
impl_zero_copy_loader!(
211+
ConstituentTargetWeights,
212+
crate::id,
213+
ConstituentTargetWeightsFixed,
214+
WeightDatum,
215+
ConstituentTargetWeights::discriminator()
216+
);
217+
177218
impl Default for ConstituentTargetWeights {
178219
fn default() -> Self {
179220
ConstituentTargetWeights {
@@ -182,61 +223,46 @@ impl Default for ConstituentTargetWeights {
182223
}
183224
}
184225

185-
impl ConstituentTargetWeights {
226+
impl<'a> AccountZeroCopyMut<'a, WeightDatum, ConstituentTargetWeightsFixed> {
186227
/// Update target weights based on amm_inventory and mapping
187228
pub fn update_target_weights(
188229
&mut self,
189-
mapping: &AmmConstituentMapping,
190-
amm_inventory: &[(u16, i64)],
191-
constituents: &[Constituent],
192-
prices: &[u64], // same order as constituents
230+
mapping: &AccountZeroCopy<'a, AmmConstituentDatum, AmmConstituentMappingFixed>,
231+
// (perp market index, inventory, price)
232+
amm_inventory: &[(u16, i64)], // length = mapping.num_rows
233+
constituents_indexes: &[u16],
234+
prices: &[i64], // length = mapping.num_rows
193235
aum: u64,
194236
slot: u64,
195237
) -> DriftResult<()> {
196-
// assert_ne!(aum, 0);
197-
assert_eq!(prices.len(), constituents.len());
198-
199-
self.data.clear();
200-
201-
for (constituent_index, constituent) in constituents.iter().enumerate() {
238+
for (i, constituent_index) in constituents_indexes.iter().enumerate() {
202239
let mut target_amount = 0i128;
203240

204241
for (perp_market_index, inventory) in amm_inventory.iter() {
205242
let idx = mapping
206-
.data
207243
.iter()
208244
.position(|d| &d.perp_market_index == perp_market_index)
209245
.expect("missing mapping for this market index");
210-
let weight = mapping.data[idx].data; // PERCENTAGE_PRECISION
246+
let weight = mapping.get(idx).data; // PERCENTAGE_PRECISION
211247
target_amount +=
212248
(*inventory as i128) * weight as i128 / PERCENTAGE_PRECISION_I64 as i128;
213249
}
214250

215-
let price = prices[constituent_index] as i128;
251+
let price = prices[i] as i128;
216252
let target_weight = target_amount
217253
.saturating_mul(price)
218254
.saturating_div(aum.max(1) as i128);
219255

220256
// PERCENTAGE_PRECISION capped
221257
let weight_datum = (target_weight).min(PERCENTAGE_PRECISION_I128);
222258

223-
self.data.push(WeightDatum {
224-
constituent_index: constituent_index as u16,
225-
padding: [0; 6],
226-
data: weight_datum as i64,
227-
last_slot: slot,
228-
});
259+
let cell = self.get_mut(*constituent_index as u32);
260+
cell.constituent_index = *constituent_index;
261+
cell.padding = [0; 6];
262+
cell.data = weight_datum as i64;
263+
cell.last_slot = slot;
229264
}
230265

231266
Ok(())
232267
}
233-
234-
pub fn get_target_weight(&self, constituent_index: u16) -> DriftResult<i64> {
235-
let weight_datum = self
236-
.data
237-
.iter()
238-
.find(|d| d.constituent_index == constituent_index)
239-
.expect("missing target weight for this constituent");
240-
Ok(weight_datum.data)
241-
}
242268
}

programs/drift/src/state/lp_pool/tests.rs

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,41 @@ mod tests {
44
use crate::state::lp_pool::*;
55
use crate::state::oracle::OracleSource;
66
use crate::state::spot_market::SpotBalanceType;
7+
use crate::state::zero_copy::ZeroCopyLoader;
78
use anchor_lang::prelude::Pubkey;
89

910
const PERCENTAGE_PRECISION_I64: i64 = 1_000_000;
1011

12+
fn constituent_target_weights_zc(
13+
) -> AccountZeroCopyMut<WeightDatum, ConstituentTargetWeightsFixed> {
14+
let disc = ConstituentTargetWeights::discriminator();
15+
let fixed = ConstituentTargetWeightsFixed { len: 1 };
16+
let mut buf = Vec::new();
17+
buf.extend_from_slice(&disc);
18+
buf.extend_from_slice(bytemuck::bytes_of(&fixed));
19+
buf.resize(buf.len() + std::mem::size_of::<WeightDatum>() * 1, 0);
20+
21+
// 2) wrap in AccountInfo
22+
let mut lamports = 0u64;
23+
let pubkey = Pubkey::default();
24+
let owner = crate::ID;
25+
let mut ai = AccountInfo::new(
26+
&pubkey,
27+
false,
28+
true,
29+
&mut lamports,
30+
&mut buf,
31+
&owner,
32+
false,
33+
0,
34+
);
35+
36+
// 3) call your loader
37+
let mut zc: AccountZeroCopyMut<WeightDatum, ConstituentTargetWeightsFixed> =
38+
ai.load_zc_mut().unwrap();
39+
zc
40+
}
41+
1142
fn weight_datum(constituent_index: u16, data: i64, last_slot: u64) -> WeightDatum {
1243
WeightDatum {
1344
constituent_index,
@@ -62,7 +93,7 @@ mod tests {
6293
let aum = 1_000_000;
6394
let now_ts = 1000;
6495

65-
let mut target = ConstituentTargetWeights::default();
96+
let mut target = constituent_target_weights_zc();
6697
target
6798
.update_target_weights(
6899
&mapping,
@@ -91,7 +122,7 @@ mod tests {
91122
let aum = 1_000_000;
92123
let now_ts = 1234;
93124

94-
let mut target = ConstituentTargetWeights::default();
125+
let mut target = constituent_target_weights_zc();
95126
target
96127
.update_target_weights(
97128
&mapping,
@@ -123,7 +154,7 @@ mod tests {
123154
let aum = 1_000_000;
124155
let now_ts = 999;
125156

126-
let mut target = ConstituentTargetWeights::default();
157+
let mut target = constituent_target_weights_zc();
127158
target
128159
.update_target_weights(
129160
&mapping,
@@ -180,7 +211,7 @@ mod tests {
180211

181212
let amm_inventory = vec![(0, i64::MAX)];
182213
let prices = vec![u64::MAX];
183-
let constituents = vec![dummy_constituent(0)];
214+
let constituents = vec![0];
184215
let aum = 1;
185216
let now_ts = 222;
186217

programs/drift/src/state/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@ pub mod state;
2525
pub mod traits;
2626
pub mod user;
2727
pub mod user_map;
28+
pub mod zero_copy;

0 commit comments

Comments
 (0)