Skip to content

Commit 35abcfc

Browse files
committed
temporary commit with pure zero copy to demonstrate effectiveness of bytemuck
1 parent b626b77 commit 35abcfc

File tree

6 files changed

+2554
-262
lines changed

6 files changed

+2554
-262
lines changed

svm/programs/executor-quoter-native/src/instructions/get_quote.rs

Lines changed: 115 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,14 @@ use solana_program::{
66
use crate::{
77
error::ExecutorQuoterError,
88
math,
9-
state::{load_account, ChainInfo, Config, QuoteBody},
9+
state::{
10+
pack_quote_body_to_bytes32, read_bytes32, read_u64_le, read_u8, validate_account,
11+
CHAIN_INFO_DISCRIMINATOR, CHAIN_INFO_ENABLED_OFFSET, CHAIN_INFO_GAS_PRICE_DECIMALS_OFFSET,
12+
CHAIN_INFO_LEN, CHAIN_INFO_NATIVE_DECIMALS_OFFSET, CONFIG_DISCRIMINATOR, CONFIG_LEN,
13+
CONFIG_PAYEE_ADDRESS_OFFSET, QUOTE_BODY_BASE_FEE_OFFSET, QUOTE_BODY_DISCRIMINATOR,
14+
QUOTE_BODY_DST_GAS_PRICE_OFFSET, QUOTE_BODY_DST_PRICE_OFFSET, QUOTE_BODY_LEN,
15+
QUOTE_BODY_SRC_PRICE_OFFSET,
16+
},
1017
};
1118

1219
/// Relay instruction type constants (matching EVM)
@@ -110,15 +117,50 @@ pub fn process_request_quote(
110117
return Err(ProgramError::NotEnoughAccountKeys);
111118
};
112119

113-
// Load accounts (discriminator checked inside load_account)
114-
let _config = load_account::<Config>(config_account, program_id)?;
120+
// Validate config account
121+
validate_account(config_account, program_id, CONFIG_DISCRIMINATOR, CONFIG_LEN)?;
115122

116-
let chain_info = load_account::<ChainInfo>(chain_info_account, program_id)?;
117-
if !chain_info.is_enabled() {
118-
return Err(ExecutorQuoterError::ChainDisabled.into());
119-
}
123+
// Validate chain_info account and check if enabled
124+
validate_account(
125+
chain_info_account,
126+
program_id,
127+
CHAIN_INFO_DISCRIMINATOR,
128+
CHAIN_INFO_LEN,
129+
)?;
130+
131+
// Validate quote_body account
132+
validate_account(
133+
quote_body_account,
134+
program_id,
135+
QUOTE_BODY_DISCRIMINATOR,
136+
QUOTE_BODY_LEN,
137+
)?;
138+
139+
// Read chain_info fields
140+
let (gas_price_decimals, native_decimals) = {
141+
let chain_info_data = chain_info_account.try_borrow_data()?;
142+
143+
// Check if chain is enabled
144+
if read_u8(&chain_info_data, CHAIN_INFO_ENABLED_OFFSET) == 0 {
145+
return Err(ExecutorQuoterError::ChainDisabled.into());
146+
}
147+
148+
(
149+
read_u8(&chain_info_data, CHAIN_INFO_GAS_PRICE_DECIMALS_OFFSET),
150+
read_u8(&chain_info_data, CHAIN_INFO_NATIVE_DECIMALS_OFFSET),
151+
)
152+
};
120153

121-
let quote_body = load_account::<QuoteBody>(quote_body_account, program_id)?;
154+
// Read quote_body fields
155+
let (base_fee, src_price, dst_price, dst_gas_price) = {
156+
let quote_body_data = quote_body_account.try_borrow_data()?;
157+
(
158+
read_u64_le(&quote_body_data, QUOTE_BODY_BASE_FEE_OFFSET),
159+
read_u64_le(&quote_body_data, QUOTE_BODY_SRC_PRICE_OFFSET),
160+
read_u64_le(&quote_body_data, QUOTE_BODY_DST_PRICE_OFFSET),
161+
read_u64_le(&quote_body_data, QUOTE_BODY_DST_GAS_PRICE_OFFSET),
162+
)
163+
};
122164

123165
// Parse instruction data to get relay_instructions
124166
// Skip: dst_chain (2) + dst_addr (32) + refund_addr (32) = 66 bytes
@@ -152,12 +194,12 @@ pub fn process_request_quote(
152194

153195
// Calculate quote - returns u64 in SVM native decimals (lamports)
154196
let required_payment = math::estimate_quote(
155-
quote_body.base_fee,
156-
quote_body.src_price,
157-
quote_body.dst_price,
158-
quote_body.dst_gas_price,
159-
chain_info.gas_price_decimals,
160-
chain_info.native_decimals,
197+
base_fee,
198+
src_price,
199+
dst_price,
200+
dst_gas_price,
201+
gas_price_decimals,
202+
native_decimals,
161203
gas_limit,
162204
msg_value,
163205
)?;
@@ -182,15 +224,50 @@ pub fn process_request_execution_quote(
182224
return Err(ProgramError::NotEnoughAccountKeys);
183225
};
184226

185-
// Load accounts (discriminator checked inside load_account)
186-
let config = load_account::<Config>(config_account, program_id)?;
227+
// Validate config account
228+
validate_account(config_account, program_id, CONFIG_DISCRIMINATOR, CONFIG_LEN)?;
187229

188-
let chain_info = load_account::<ChainInfo>(chain_info_account, program_id)?;
189-
if !chain_info.is_enabled() {
190-
return Err(ExecutorQuoterError::ChainDisabled.into());
191-
}
230+
// Validate chain_info account
231+
validate_account(
232+
chain_info_account,
233+
program_id,
234+
CHAIN_INFO_DISCRIMINATOR,
235+
CHAIN_INFO_LEN,
236+
)?;
192237

193-
let quote_body = load_account::<QuoteBody>(quote_body_account, program_id)?;
238+
// Validate quote_body account
239+
validate_account(
240+
quote_body_account,
241+
program_id,
242+
QUOTE_BODY_DISCRIMINATOR,
243+
QUOTE_BODY_LEN,
244+
)?;
245+
246+
// Read chain_info fields
247+
let (gas_price_decimals, native_decimals) = {
248+
let chain_info_data = chain_info_account.try_borrow_data()?;
249+
250+
// Check if chain is enabled
251+
if read_u8(&chain_info_data, CHAIN_INFO_ENABLED_OFFSET) == 0 {
252+
return Err(ExecutorQuoterError::ChainDisabled.into());
253+
}
254+
255+
(
256+
read_u8(&chain_info_data, CHAIN_INFO_GAS_PRICE_DECIMALS_OFFSET),
257+
read_u8(&chain_info_data, CHAIN_INFO_NATIVE_DECIMALS_OFFSET),
258+
)
259+
};
260+
261+
// Read quote_body fields and config payee_address
262+
let (base_fee, src_price, dst_price, dst_gas_price) = {
263+
let quote_body_data = quote_body_account.try_borrow_data()?;
264+
(
265+
read_u64_le(&quote_body_data, QUOTE_BODY_BASE_FEE_OFFSET),
266+
read_u64_le(&quote_body_data, QUOTE_BODY_SRC_PRICE_OFFSET),
267+
read_u64_le(&quote_body_data, QUOTE_BODY_DST_PRICE_OFFSET),
268+
read_u64_le(&quote_body_data, QUOTE_BODY_DST_GAS_PRICE_OFFSET),
269+
)
270+
};
194271

195272
// Parse instruction data to get relay_instructions
196273
if data.len() < 70 {
@@ -222,12 +299,12 @@ pub fn process_request_execution_quote(
222299

223300
// Calculate quote - returns u64 in SVM native decimals (lamports)
224301
let required_payment = math::estimate_quote(
225-
quote_body.base_fee,
226-
quote_body.src_price,
227-
quote_body.dst_price,
228-
quote_body.dst_gas_price,
229-
chain_info.gas_price_decimals,
230-
chain_info.native_decimals,
302+
base_fee,
303+
src_price,
304+
dst_price,
305+
dst_gas_price,
306+
gas_price_decimals,
307+
native_decimals,
231308
gas_limit,
232309
msg_value,
233310
)?;
@@ -238,8 +315,17 @@ pub fn process_request_execution_quote(
238315
// - bytes 40-71: quote_body (32 bytes, EQ01 format)
239316
let mut return_data = [0u8; 72];
240317
return_data[0..8].copy_from_slice(&required_payment.to_be_bytes());
241-
return_data[8..40].copy_from_slice(&config.payee_address);
242-
return_data[40..72].copy_from_slice(&quote_body.to_bytes32());
318+
319+
// Read payee_address from config
320+
{
321+
let config_data = config_account.try_borrow_data()?;
322+
let payee_address = read_bytes32(&config_data, CONFIG_PAYEE_ADDRESS_OFFSET);
323+
return_data[8..40].copy_from_slice(payee_address);
324+
}
325+
326+
// Pack quote body to bytes32 (EQ01 format)
327+
let quote_body_bytes32 = pack_quote_body_to_bytes32(base_fee, dst_gas_price, src_price, dst_price);
328+
return_data[40..72].copy_from_slice(&quote_body_bytes32);
243329

244330
set_return_data(&return_data);
245331

Lines changed: 63 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use bytemuck::{Pod, Zeroable};
21
use solana_program::{
32
account_info::AccountInfo,
43
entrypoint::ProgramResult,
@@ -12,24 +11,27 @@ use solana_program::{
1211

1312
use crate::{
1413
error::ExecutorQuoterError,
15-
state::{Config, CONFIG_DISCRIMINATOR, CONFIG_SEED},
14+
state::{
15+
write_bytes32, write_u8, CONFIG_BUMP_OFFSET, CONFIG_DISCRIMINATOR, CONFIG_LEN,
16+
CONFIG_PAYEE_ADDRESS_OFFSET, CONFIG_QUOTER_ADDRESS_OFFSET, CONFIG_SEED,
17+
CONFIG_SRC_TOKEN_DECIMALS_OFFSET, CONFIG_UPDATER_ADDRESS_OFFSET,
18+
},
1619
};
1720

18-
/// Instruction data for Initialize
19-
#[repr(C)]
20-
#[derive(Pod, Zeroable, Clone, Copy)]
21-
pub struct InitializeData {
22-
pub quoter_address: [u8; 32],
23-
pub updater_address: [u8; 32],
24-
pub src_token_decimals: u8,
25-
pub bump: u8,
26-
pub _padding: [u8; 30],
27-
pub payee_address: [u8; 32],
28-
}
29-
30-
impl InitializeData {
31-
pub const LEN: usize = core::mem::size_of::<Self>();
32-
}
21+
// InitializeData layout (128 bytes):
22+
// Offset Field Type Size
23+
// 0-31 quoter_address [u8; 32] 32
24+
// 32-63 updater_address [u8; 32] 32
25+
// 64 src_token_decimals u8 1
26+
// 65 bump u8 1
27+
// 66-95 _padding [u8; 30] 30
28+
// 96-127 payee_address [u8; 32] 32
29+
const IX_DATA_LEN: usize = 128;
30+
const IX_QUOTER_ADDRESS_OFFSET: usize = 0;
31+
const IX_UPDATER_ADDRESS_OFFSET: usize = 32;
32+
const IX_SRC_TOKEN_DECIMALS_OFFSET: usize = 64;
33+
const IX_BUMP_OFFSET: usize = 65;
34+
const IX_PAYEE_ADDRESS_OFFSET: usize = 96;
3335

3436
/// Process the Initialize instruction.
3537
/// Creates and initializes the Config PDA.
@@ -52,18 +54,17 @@ pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], data: &[u8]) -> Pr
5254
return Err(ProgramError::MissingRequiredSignature);
5355
}
5456

55-
// Parse instruction data
56-
if data.len() < InitializeData::LEN {
57+
// Validate instruction data length
58+
if data.len() < IX_DATA_LEN {
5759
return Err(ExecutorQuoterError::InvalidInstructionData.into());
5860
}
59-
let ix_data: InitializeData =
60-
bytemuck::try_pod_read_unaligned(&data[..InitializeData::LEN])
61-
.map_err(|_| ExecutorQuoterError::InvalidInstructionData)?;
6261

63-
// Validate Config PDA using bump from instruction data
64-
let bump = ix_data.bump;
62+
// Read bump from instruction data
63+
let bump = data[IX_BUMP_OFFSET];
6564
let bump_seed = [bump];
6665
let seeds: &[&[u8]] = &[CONFIG_SEED, &bump_seed];
66+
67+
// Validate Config PDA
6768
let derived_pda = Pubkey::create_program_address(seeds, program_id)
6869
.map_err(|_| ExecutorQuoterError::InvalidPda)?;
6970
if derived_pda != *config_account.key {
@@ -77,31 +78,58 @@ pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], data: &[u8]) -> Pr
7778

7879
// Get rent
7980
let rent = Rent::get()?;
80-
let lamports = rent.minimum_balance(Config::LEN);
81+
let lamports = rent.minimum_balance(CONFIG_LEN);
8182

8283
// Create account via CPI
8384
let create_account_ix = system_instruction::create_account(
8485
payer.key,
8586
config_account.key,
8687
lamports,
87-
Config::LEN as u64,
88+
CONFIG_LEN as u64,
8889
program_id,
8990
);
9091

9192
invoke_signed(&create_account_ix, accounts, &[seeds])?;
9293

93-
// Initialize account data
94+
// Initialize account data using byte offsets
9495
let mut account_data = config_account.try_borrow_mut_data()?;
95-
let config = bytemuck::try_from_bytes_mut::<Config>(&mut account_data[..Config::LEN])
96+
97+
// Write discriminator
98+
write_u8(&mut account_data, 0, CONFIG_DISCRIMINATOR);
99+
100+
// Write bump
101+
write_u8(&mut account_data, CONFIG_BUMP_OFFSET, bump);
102+
103+
// Write src_token_decimals
104+
write_u8(
105+
&mut account_data,
106+
CONFIG_SRC_TOKEN_DECIMALS_OFFSET,
107+
data[IX_SRC_TOKEN_DECIMALS_OFFSET],
108+
);
109+
110+
// Write padding (bytes 3-7 are zeroed by account creation)
111+
112+
// Write quoter_address
113+
let quoter_address: &[u8; 32] = data[IX_QUOTER_ADDRESS_OFFSET..IX_QUOTER_ADDRESS_OFFSET + 32]
114+
.try_into()
96115
.map_err(|_| ExecutorQuoterError::InvalidInstructionData)?;
116+
write_bytes32(&mut account_data, CONFIG_QUOTER_ADDRESS_OFFSET, quoter_address);
97117

98-
config.discriminator = CONFIG_DISCRIMINATOR;
99-
config.bump = bump;
100-
config.src_token_decimals = ix_data.src_token_decimals;
101-
config._padding = [0u8; 5];
102-
config.quoter_address = ix_data.quoter_address;
103-
config.updater_address = ix_data.updater_address;
104-
config.payee_address = ix_data.payee_address;
118+
// Write updater_address
119+
let updater_address: &[u8; 32] = data[IX_UPDATER_ADDRESS_OFFSET..IX_UPDATER_ADDRESS_OFFSET + 32]
120+
.try_into()
121+
.map_err(|_| ExecutorQuoterError::InvalidInstructionData)?;
122+
write_bytes32(
123+
&mut account_data,
124+
CONFIG_UPDATER_ADDRESS_OFFSET,
125+
updater_address,
126+
);
127+
128+
// Write payee_address
129+
let payee_address: &[u8; 32] = data[IX_PAYEE_ADDRESS_OFFSET..IX_PAYEE_ADDRESS_OFFSET + 32]
130+
.try_into()
131+
.map_err(|_| ExecutorQuoterError::InvalidInstructionData)?;
132+
write_bytes32(&mut account_data, CONFIG_PAYEE_ADDRESS_OFFSET, payee_address);
105133

106134
Ok(())
107135
}

0 commit comments

Comments
 (0)