diff --git a/docs/algorithms/ENTRYPOINT.tex b/docs/algorithms/ENTRYPOINT.tex
index df2d1cb..5c20040 100644
--- a/docs/algorithms/ENTRYPOINT.tex
+++ b/docs/algorithms/ENTRYPOINT.tex
@@ -2,15 +2,15 @@
\caption{ENTRYPOINT}
\begin{algorithmic}
\COMMENT{Pointer to input buffer.}
- \INPUT $r_1 = input$
+ \INPUT $r_1$ = input
\COMMENT{Pointer to instruction data.}
- \INPUT $r_2 = insn$
-\PROCEDURE{ENTRYPOINT}{$input, insn$}
- \STATE $n\_accounts = input.n\_accounts$
- \STATE $insn\_len = insn.length$
- \STATE $insn\_disc = insn.discriminant$
- \IF{$insn\_disc ==$ \texttt{Discriminant::RegisterMarket}}
- \RETURN \CALL{REGISTER-MARKET}{$input$, $insn$, $n\_accounts$, $insn\_len$}
+ \INPUT $r_2$ = insn
+\PROCEDURE{ENTRYPOINT}{input, insn}
+ \STATE n\_accounts = input.n\_accounts
+ \STATE insn\_len = insn.length
+ \STATE insn\_disc = insn.discriminant
+ \IF{insn\_disc == \texttt{Discriminant::RegisterMarket}}
+ \RETURN \CALL{REGISTER-MARKET}{input, insn, n\_accounts, insn\_len}
\ENDIF
\RETURN \texttt{ErrorCode::InvalidDiscriminant}
\ENDPROCEDURE
diff --git a/docs/algorithms/REGISTER-MARKET.tex b/docs/algorithms/REGISTER-MARKET.tex
index 0de32e3..e315fad 100644
--- a/docs/algorithms/REGISTER-MARKET.tex
+++ b/docs/algorithms/REGISTER-MARKET.tex
@@ -2,82 +2,127 @@
\caption{REGISTER-MARKET}
\begin{algorithmic}
\COMMENT{Pointer to input buffer.}
- \INPUT $r_1 = input$
+ \INPUT $r_1$ = input
\COMMENT{Pointer to instruction data.}
- \INPUT $r_2 = insn$
+ \INPUT $r_2$ = insn
\COMMENT{Number of accounts.}
- \INPUT $r_3 = n\_accounts$
+ \INPUT $r_3$ = n\_accounts
\COMMENT{Instruction data length.}
- \INPUT $r_4 = insn\_len$
- \REQUIRE $insn.discriminant ==$ \texttt{Discriminant::RegisterMarket}
-\PROCEDURE{REGISTER-MARKET}{$input, insn, n\_accounts, insn\_len$}
- \IF{$n\_accounts <$ \texttt{RegisterMarketAccounts.LEN}}
+ \INPUT $r_4$ = insn\_len
+ \REQUIRE insn.discriminant == \texttt{Discriminant::RegisterMarket}
+\PROCEDURE{REGISTER-MARKET}{input, insn, n\_accounts, insn\_len}
+ \IF{n\_accounts $<$ \texttt{RegisterMarketAccounts.LEN}}
\RETURN \texttt{ErrorCode::InvalidNumberOfAccounts}
\ENDIF
- \IF{$insn\_len \neq$ \texttt{RegisterMarketData.LEN}}
+ \IF{insn\_len $\neq$ \texttt{RegisterMarketData.LEN}}
\RETURN \texttt{ErrorCode::InvalidInstructionLength}
\ENDIF
\COMMENT{Check user, market accounts.}
- \IF{$input.user.data\_len \neq 0$}
+ \IF{input.user.data\_len $\neq$ 0}
\RETURN \texttt{ErrorCode::UserHasData}
\ENDIF
- \IF{$input.market.duplicate \neq$ \texttt{account.NON\_DUP\_MARKER}}
+ \IF{input.market.duplicate $\neq$ \texttt{account.NON\_DUP\_MARKER}}
\RETURN \texttt{ErrorCode::MarketAccountIsDuplicate}
\ENDIF
- \IF{$input.market.data\_len \neq 0$}
+ \IF{input.market.data\_len $\neq$ 0}
\RETURN \texttt{ErrorCode::MarketHasData}
\ENDIF
\COMMENT{Check base mint account, create signer seed.}
- \IF{$input.base\_mint.duplicate \neq$ \texttt{account.NON\_DUP\_MARKER}}
+ \IF{input.base\_mint.duplicate $\neq$ \texttt{account.NON\_DUP\_MARKER}}
\RETURN \texttt{ErrorCode::BaseMintIsDuplicate}
\ENDIF
- \STATE $frame.pda\_seeds.base.addr = input.base\_mint.pubkey$
- \STATE $frame.pda\_seeds.base.len =$ \texttt{Address.size}
+ \STATE frame.pda\_seeds.base.addr = input.base\_mint.pubkey
+ \STATE frame.pda\_seeds.base.len = \texttt{Address.size}
\COMMENT{Increment input buffer by base mint padded data length for quote offsets.}
- \STATE $input\_shifted = input + input.base\_mint.padded\_data\_len$
+ \STATE input\_shifted = input + input.base\_mint.padded\_data\_len
\COMMENT{Check quote mint account, create signer seed.}
- \IF{$input\_shifted.quote\_mint.duplicate \neq$
- \texttt{account.NON\_DUP\_MARKER}
- }
+ \IF{input\_shifted.quote\_mint.duplicate $\neq$ \texttt{account.NON\_DUP\_MARKER}}
\RETURN \texttt{ErrorCode::QuoteMintIsDuplicate}
\ENDIF
- \STATE $frame.pda\_seeds.quote.addr = input\_shifted.quote\_mint.pubkey$
- \STATE $frame.pda\_seeds.quote.len =$ \texttt{Address.size}
+ \STATE frame.pda\_seeds.quote.addr = input\_shifted.quote\_mint.pubkey
+ \STATE frame.pda\_seeds.quote.len = \texttt{Address.size}
\COMMENT{Advance to System Program account.}
- \STATE $quote\_mint\_padded\_data\_len = input\_shifted.quote\_mint.padded\_data\_len$
- \STATE $acct = \&input\_shifted.quote\_mint$
- \STATE $acct \mathrel{{+}{=}} quote\_mint\_padded\_data\_len$ +
- \texttt{EmptyAccount.size}
+ \STATE quote\_mint\_padded\_data\_len = input\_shifted.quote\_mint.padded\_data\_len
+ \STATE acct = \&input\_shifted.quote\_mint
+ \STATE acct += quote\_mint\_padded\_data\_len + \texttt{EmptyAccount.size}
\COMMENT{Derive market PDA.}
- \STATE \CALL{Store}{$input$}
- \STATE $syscall.seeds = \&frame.pda\_seeds$
- \STATE $syscall.program\_id = \&insn.program\_id$
- \STATE $syscall.seeds\_len =$ \texttt{register\_misc.TRY\_FIND\_PDA\_SEEDS\_LEN}
- \STATE $syscall.program\_address = frame.pda$
- \STATE $syscall.bump\_seed = frame.bump$
+ \STATE \CALL{Store}{input}
+ \STATE syscall.seeds = \&frame.pda\_seeds
+ \STATE syscall.program\_id = \&insn.program\_id
+ \STATE syscall.seeds\_len = \texttt{register\_misc.TRY\_FIND\_PDA\_SEEDS\_LEN}
+ \STATE syscall.program\_address = \&frame.pda
+ \STATE syscall.bump\_seed = \&frame.bump
\STATE \CALL{sol-try-find-program-address}{}
\COMMENT{Verify derived market PDA matches market account pubkey.}
- \IF{$input.market.pubkey \neq frame.market\_pda$}
+ \IF{input.market.pubkey $\neq$ frame.market\_pda}
\RETURN \texttt{ErrorCode::InvalidMarketPubkey}
\ENDIF
+ \COMMENT{Populate bump signer seed from derived bump.}
+ \STATE frame.pda\_seeds.bump.addr = \&syscall.bump\_seed
+ \STATE frame.pda\_seeds.bump.len = \texttt{u8.size}
+ \COMMENT{Populate CreateAccount CPI instruction data owner field.}
+ \STATE frame.create\_account\_data.owner = syscall.program\_id
\COMMENT{Check System Program account.}
- \IF{$acct.duplicate \neq$ \texttt{account.NON\_DUP\_MARKER}}
+ \IF{acct.duplicate $\neq$ \texttt{account.NON\_DUP\_MARKER}}
\RETURN \texttt{ErrorCode::SystemProgramIsDuplicate}
\ENDIF
- \IF{$acct.pubkey \neq frame.system\_program\_pubkey$}
+ \IF{acct.pubkey $\neq$ frame.system\_program\_pubkey}
\RETURN \texttt{ErrorCode::InvalidSystemProgramPubkey}
\ENDIF
+ \COMMENT{Populate CPI program ID field.}
+ \STATE frame.sol\_instruction.program\_id = \&acct.address
\COMMENT{Advance to Rent sysvar account.}
- \STATE $system\_program\_padded\_data\_len = acct.padded\_data\_len$
- \STATE $acct \mathrel{{+}{=}} system\_program\_padded\_data\_len$ +
- \texttt{EmptyAccount.size}
+ \STATE system\_program\_padded\_data\_len = acct.padded\_data\_len
+ \STATE acct += system\_program\_padded\_data\_len + \texttt{EmptyAccount.size}
\COMMENT{Check Rent sysvar account.}
- \IF{$acct.duplicate \neq$ \texttt{account.NON\_DUP\_MARKER}}
+ \IF{acct.duplicate $\neq$ \texttt{account.NON\_DUP\_MARKER}}
\RETURN \texttt{ErrorCode::RentSysvarIsDuplicate}
\ENDIF
- \IF{$acct.pubkey \neq$ \texttt{pubkey.RENT}}
+ \IF{acct.pubkey $\neq$ \texttt{pubkey.RENT}}
\RETURN \texttt{ErrorCode::InvalidRentSysvarPubkey}
\ENDIF
+ \COMMENT{Prepare CreateAccount instruction lamports, space fields.}
+ \STATE frame.create\_account\_data.space = \texttt{MarketHeader.size}
+ \STATE acct\_size = \texttt{MarketHeader.size} + \texttt{account.STORAGE\_OVERHEAD}
+ \STATE lamports\_per\_byte = acct.data.lamports\_per\_byte
+ \STATE frame.create\_account\_data.lamports = acct\_size $\times$ lamports\_per\_byte
+ \COMMENT{Assign CPI account fields via immediates.}
+ \STATE frame.cpi.user\_info.is\_signer = \texttt{true}
+ \STATE frame.cpi.user\_info.is\_writable = \texttt{true}
+ \STATE frame.cpi.user\_meta.is\_signer = \texttt{true}
+ \STATE frame.cpi.user\_meta.is\_writable = \texttt{true}
+ \STATE frame.cpi.target\_info.is\_signer = \texttt{true}
+ \STATE frame.cpi.target\_info.is\_writable = \texttt{true}
+ \STATE frame.cpi.target\_meta.is\_signer = \texttt{true}
+ \STATE frame.cpi.target\_meta.is\_writable = \texttt{true}
+ \COMMENT{Assign CPI account fields via pointers.}
+ \STATE frame.cpi.user\_meta.pubkey = \&input.user.address
+ \STATE frame.cpi.user\_info.key = \&input.user.address
+ \STATE frame.cpi.user\_info.owner = \&input.user.owner
+ \STATE frame.cpi.user\_info.lamports = \&input.user.lamports
+ \STATE frame.cpi.user\_info.data = \&input.user.data
+ \STATE frame.cpi.target\_meta.pubkey = \&input.market.address
+ \STATE frame.cpi.target\_info.key = \&input.market.address
+ \STATE frame.cpi.target\_info.owner = \&input.market.owner
+ \STATE frame.cpi.target\_info.lamports = \&input.market.lamports
+ \STATE frame.cpi.target\_info.data = \&input.market.data
+ \COMMENT{Populate signers seeds for CPI.}
+ \STATE frame.signers\_seeds.addr = \&frame.pda\_seeds
+ \STATE frame.signers\_seeds.len = \texttt{RegisterMarketFrame.PDA\_SEEDS\_N\_SEEDS}
+ \COMMENT{Populate SolInstruction for CreateAccount CPI.}
+ \STATE frame.sol\_instruction.accounts = \&frame.cpi.account\_metas
+ \STATE frame.sol\_instruction.account\_len =
+ \texttt{register\_misc.CREATE\_ACCOUNT\_N\_ACCOUNTS}
+ \STATE frame.sol\_instruction.data = \&frame.create\_account\_data
+ \STATE frame.sol\_instruction.data\_len = \texttt{CreateAccountData.size}
+ \COMMENT{Invoke CreateAccount CPI.}
+ \STATE syscall.instruction = \&frame.sol\_instruction
+ \STATE syscall.account\_infos = \&frame.cpi.account\_infos
+ \STATE syscall.account\_infos\_len =
+ \texttt{register\_misc.CREATE\_ACCOUNT\_N\_ACCOUNTS}
+ \STATE syscall.seeds = \&frame.signers\_seeds
+ \STATE syscall.seeds\_len = \texttt{register\_misc.N\_PDA\_SIGNERS}
+ \STATE \CALL{sol-invoke-signed-c}{}
\ENDPROCEDURE
\end{algorithmic}
\end{algorithm}
diff --git a/docs/algorithms/syscalls.json b/docs/algorithms/syscalls.json
index 1ac3ad1..9d8a303 100644
--- a/docs/algorithms/syscalls.json
+++ b/docs/algorithms/syscalls.json
@@ -1,3 +1,4 @@
{
- "sol_try_find_program_address": "https://github.com/anza-xyz/agave/blob/v3.1.6/platform-tools-sdk/sbf/c/inc/sol/inc/pubkey.inc#L74-L83"
+ "sol_invoke_signed_c": "https://github.com/anza-xyz/agave/blob/v4.0.0-beta.5/platform-tools-sdk/sbf/c/inc/sol/inc/cpi.inc#L56-L90",
+ "sol_try_find_program_address": "https://github.com/anza-xyz/agave/blob/v4.0.0-beta.5/platform-tools-sdk/sbf/c/inc/sol/inc/pubkey.inc#L74-L83"
}
diff --git a/docs/src/development/build-scaffolding.md b/docs/src/development/build-scaffolding.md
index 0b4b050..0fff373 100644
--- a/docs/src/development/build-scaffolding.md
+++ b/docs/src/development/build-scaffolding.md
@@ -41,7 +41,7 @@ Defines a group of named assembly constants with an injection target. The
constants. An optional `#[prefix("...")]` attribute prepends a prefix to all
generated constant names. An optional `///` doc comment on the group itself
adds a header comment and separator lines around the group in the output
-assembly file. Each constant is assigned a value using one of nine custom
+assembly file. Each constant is assigned a value using one of the following
syntax forms (parsed within the proc macro, not standalone macros):
- `offset!(expr)`: an `i16` memory offset, the generated name is suffixed with
@@ -69,6 +69,10 @@ syntax forms (parsed within the proc macro, not standalone macros):
offsets for each `SolAccountInfo` and `SolAccountMeta` field (requires
`#[frame(Type)]`, field type must be defined with
[`cpi_accounts!`](#cpi_accounts))
+- `relative_offset!(Struct, from_field, to_field)`: computes the difference
+ between two field offsets within the same struct, emitted as an `i32`
+ immediate with `_REL_OFF_IMM` suffix. In `#[frame(Type)]` context the
+ struct is inferred and only the two field paths are required
diff --git a/interface/src/lib.rs b/interface/src/lib.rs
index b85e73b..419e7c1 100644
--- a/interface/src/lib.rs
+++ b/interface/src/lib.rs
@@ -65,9 +65,10 @@ pub const INJECTION_GROUPS: &[&dropset_build::ConstantGroup] = &[
&error_code::GROUP,
&market::register_market_data::GROUP,
&market::register_market_accounts::GROUP,
- &market::register_market_frame::GROUP,
+ &market::frame::GROUP,
&market::register_misc::GROUP,
&memory::account::GROUP,
+ &memory::cpi::GROUP,
&memory::data::GROUP,
&memory::input_buffer::GROUP,
&memory::size_of::GROUP,
diff --git a/interface/src/market.rs b/interface/src/market.rs
index cd2dea9..b875964 100644
--- a/interface/src/market.rs
+++ b/interface/src/market.rs
@@ -1,4 +1,6 @@
-use crate::cpi_bindings::{SolAccountInfo, SolAccountMeta, SolInstruction, SolSignerSeed};
+use crate::cpi_bindings::{
+ SolAccountInfo, SolAccountMeta, SolInstruction, SolSignerSeed, SolSignerSeeds,
+};
use crate::memory::EmptyAccount;
use crate::memory::StackNode;
use crate::order::Order;
@@ -64,6 +66,10 @@ constant_group! {
QUOTE_DATA_LEN = offset!(RegisterMarketInputBuffer.quote_mint.header.data_len),
/// Number of seeds for market PDA derivation (base, quote).
TRY_FIND_PDA_SEEDS_LEN = immediate!(2),
+ /// Number of accounts for CreateAccount CPI (user, target).
+ CREATE_ACCOUNT_N_ACCOUNTS = immediate!(2),
+ /// Number of PDA signers for CPI.
+ N_PDA_SIGNERS = immediate!(1),
}
}
@@ -90,6 +96,7 @@ pub struct CreateAccountData {
pub discriminator: u32,
pub lamports: u64,
pub space: u64,
+ /// Zero-initialized on stack.
pub owner: Address,
/// Included for alignment on stack.
_pad: u32,
@@ -99,10 +106,10 @@ cpi_accounts! {
/// CPI accounts for CreateAccount and ATA creation.
///
/// CreateAccount uses the first two accounts (user, target). ATA creation requires all six.
- pub struct CPIAccounts {
+ CPIAccounts {
/// User account.
user,
- /// Target account.
+ /// Target account (the account to create, either market account or ATA).
target,
/// Proprietor account.
proprietor,
@@ -118,7 +125,7 @@ cpi_accounts! {
// region: register_market_stack
// region: signer_seeds_example
signer_seeds! {
- pub struct PDASignerSeeds {
+ PDASignerSeeds {
/// Base mint seed.
base,
/// Quote mint seed.
@@ -143,8 +150,10 @@ pub struct RegisterMarketFrame {
pub create_account_data: CreateAccountData,
/// CPI accounts for CreateAccount and ATA creation.
pub cpi_accounts: CPIAccounts,
+ /// Signers seeds for CPI.
+ pub signers_seeds: SolSignerSeeds,
/// Re-used across CPIs, zero-initialized on stack.
- pub cpi_instruction: SolInstruction,
+ pub sol_instruction: SolInstruction,
/// From `sol_try_find_program_address`.
pub bump: u8,
}
@@ -154,15 +163,13 @@ constant_group! {
#[prefix("RM")]
#[inject("market/register")]
#[frame(RegisterMarketFrame)]
- register_market_frame {
+ frame {
/// PDA signer seeds.
PDA_SEEDS = signer_seeds!(pda_seeds),
/// PDA address.
PDA = pubkey_offsets!(pda),
/// System Program pubkey.
SYSTEM_PROGRAM_PUBKEY = pubkey_offsets!(system_program_pubkey),
- /// Bump seed.
- BUMP = offset!(bump),
/// CreateAccount instruction data.
CREATE_ACCT_DATA = offset!(create_account_data),
/// Lamports field within CreateAccount instruction data.
@@ -173,8 +180,22 @@ constant_group! {
CREATE_ACCT_OWNER = unaligned_pubkey_offsets!(create_account_data.owner),
/// CPI accounts.
CPI = cpi_accounts!(cpi_accounts),
+ /// Signers seeds address.
+ SIGNERS_SEEDS_ADDR = unaligned_offset!(signers_seeds.addr),
+ /// Signers seeds length.
+ SIGNERS_SEEDS_LEN = unaligned_offset!(signers_seeds.len),
/// Solana instruction.
- SOL_INSN = sol_instruction!(cpi_instruction),
+ SOL_INSN = sol_instruction!(sol_instruction),
+ /// Bump seed.
+ BUMP = offset!(bump),
+ /// From pda_seeds to sol_instruction.
+ PDA_SEEDS_TO_SOL_INSN = relative_offset!(pda_seeds, sol_instruction),
+ /// From pda to signers_seeds.
+ PDA_TO_SIGNERS_SEEDS = relative_offset!(pda, signers_seeds),
+ /// From create_account_data to cpi account metas.
+ CREATE_ACCT_DATA_TO_CPI_ACCT_METAS = relative_offset!(
+ create_account_data, cpi_accounts.user_meta
+ ),
}
}
diff --git a/interface/src/memory.rs b/interface/src/memory.rs
index cce7df9..3a5f803 100644
--- a/interface/src/memory.rs
+++ b/interface/src/memory.rs
@@ -1,7 +1,9 @@
+use crate::market::{CreateAccountData, MarketHeader};
use dropset_macros::{constant_group, size_of_group, svm_data};
use pinocchio::Address;
use pinocchio::account::{MAX_PERMITTED_DATA_INCREASE, RuntimeAccount};
use pinocchio::entrypoint::NON_DUP_MARKER;
+use pinocchio::sysvars::rent::ACCOUNT_STORAGE_OVERHEAD;
#[svm_data]
pub struct StackNode {
@@ -41,7 +43,7 @@ pub type EmptyAccount = FullRuntimeAccount<{ runtime_data_size(data::LEN_ZERO) }
constant_group! {
#[prefix("ACCT")]
#[inject("common/memory")]
- /// Field offsets within a runtime account.
+ /// Assorted runtime account constants.
account {
/// Borrow state / duplicate marker.
DUPLICATE = offset!(EmptyAccount.header.borrow_state),
@@ -59,8 +61,22 @@ constant_group! {
OWNER = pubkey_offsets!(EmptyAccount.header.owner),
/// Account data length.
DATA_LEN = offset!(EmptyAccount.header.data_len),
+ /// Account data start.
+ DATA = offset!(EmptyAccount.data),
/// Non-dup marker for accounts.
NON_DUP_MARKER = immediate!(NON_DUP_MARKER as i32),
+ /// Account storage overhead for rent calculation.
+ STORAGE_OVERHEAD = immediate!(ACCOUNT_STORAGE_OVERHEAD as i32),
+ }
+}
+
+constant_group! {
+ #[prefix("CPI")]
+ #[inject("common/memory")]
+ /// CPI-related constants.
+ cpi {
+ /// Mask for writable signer (is_writable | is_signer).
+ WRITABLE_SIGNER = immediate!(0x0101),
}
}
@@ -82,12 +98,24 @@ constant_group! {
input_buffer {
/// From input buffer to user data length.
USER_DATA_LEN = offset!(InputBufferHeader.user.header.data_len),
+ /// From input buffer to user pubkey.
+ USER_PUBKEY = pubkey_offsets!(InputBufferHeader.user.header.address),
/// From input buffer to market duplicate flag.
MARKET_DUPLICATE = offset!(InputBufferHeader.market.borrow_state),
/// From input buffer to market data length.
MARKET_DATA_LEN = offset!(InputBufferHeader.market.data_len),
/// From input buffer to market address.
MARKET_PUBKEY = pubkey_offsets!(InputBufferHeader.market.address),
+ /// From address to owner in a runtime account.
+ ADDRESS_TO_OWNER = relative_offset!(RuntimeAccount, address, owner),
+ /// From owner to lamports in a runtime account.
+ OWNER_TO_LAMPORTS = relative_offset!(RuntimeAccount, owner, lamports),
+ /// From lamports to data start in a runtime account.
+ LAMPORTS_TO_DATA = relative_offset!(EmptyAccount, header.lamports, data),
+ /// From user data to market address in the input buffer.
+ USER_DATA_TO_MARKET_ADDRESS = relative_offset!(
+ InputBufferHeader, user.data, market.address
+ ),
}
}
// endregion: constant_group_example
@@ -95,7 +123,7 @@ constant_group! {
// region: size_of_group_example
size_of_group! {
#[inject("common/memory")]
- [Address, EmptyAccount]
+ [u8, Address, EmptyAccount, MarketHeader, CreateAccountData]
}
// endregion: size_of_group_example
diff --git a/macros/src/constant_group/expand/mod.rs b/macros/src/constant_group/expand/mod.rs
index 2e42d07..b1c6dd1 100644
--- a/macros/src/constant_group/expand/mod.rs
+++ b/macros/src/constant_group/expand/mod.rs
@@ -130,6 +130,28 @@ pub fn expand(input: &ConstantGroupInput) -> proc_macro2::TokenStream {
&mut meta_idents,
);
}
+ ConstantKind::RelativeOffset {
+ ty,
+ from_fields,
+ to_fields,
+ } => {
+ let resolved_ty = ty.as_ref().unwrap_or_else(|| {
+ input
+ .frame_type
+ .as_ref()
+ .expect("frame_type must be set for RelativeOffset without explicit type")
+ });
+ let (def, meta) = offset::expand_relative_offset(
+ base_name,
+ &asm_name,
+ doc,
+ resolved_ty,
+ from_fields,
+ to_fields,
+ );
+ const_defs.push(def);
+ meta_idents.push(meta);
+ }
ConstantKind::UnalignedFramePubkeyOffsets { fields } => {
let frame_ty = input
.frame_type
diff --git a/macros/src/constant_group/expand/offset.rs b/macros/src/constant_group/expand/offset.rs
index cacb502..42e2efd 100644
--- a/macros/src/constant_group/expand/offset.rs
+++ b/macros/src/constant_group/expand/offset.rs
@@ -334,6 +334,35 @@ pub fn expand_unaligned_frame_offset(
(def, meta_ident)
}
+/// Expand `relative_offset!(...)` into an i32 immediate with `_REL_OFF_IMM`
+/// suffix. Computes `offset_of!(Struct, to) - offset_of!(Struct, from)`.
+pub fn expand_relative_offset(
+ base_name: &Ident,
+ asm_name: &str,
+ doc: &str,
+ ty: &syn::Path,
+ from_fields: &[syn::Member],
+ to_fields: &[syn::Member],
+) -> (proc_macro2::TokenStream, Ident) {
+ let rust_name = Ident::new(&format!("{}_REL_OFF_IMM", base_name), base_name.span());
+ let asm_name = format!("{}_REL_OFF_IMM", asm_name);
+ let meta_ident = codegen::meta_ident(&asm_name, base_name.span());
+ let meta = codegen::immediate_meta(&meta_ident, &asm_name, doc, quote! { #rust_name });
+
+ let def = quote! {
+ #[doc = #doc]
+ pub const #rust_name: i32 = {
+ use super::*;
+ (core::mem::offset_of!(#ty, #(#to_fields).*)
+ - core::mem::offset_of!(#ty, #(#from_fields).*)) as i32
+ };
+
+ #meta
+ };
+
+ (def, meta_ident)
+}
+
/// Expand `offset!(field)` inside a `#[frame(Type)]` group.
pub fn expand_frame_offset(
base_name: &Ident,
diff --git a/macros/src/constant_group/mod.rs b/macros/src/constant_group/mod.rs
index 7a16eee..d588273 100644
--- a/macros/src/constant_group/mod.rs
+++ b/macros/src/constant_group/mod.rs
@@ -52,6 +52,18 @@ pub(crate) enum ConstantKind {
/// like `FramePubkeyOffsets` but without the alignment assertion. Names
/// get `_UOFF` suffix.
UnalignedFramePubkeyOffsets { fields: Vec },
+ /// `relative_offset!(Struct, from_field, to_field)`: difference between
+ /// two field offsets within the same struct, emitted as an i32 immediate
+ /// with `_REL_OFF_IMM` suffix. In `#[frame(Type)]` context the struct
+ /// is inferred and both paths are bare field chains.
+ RelativeOffset {
+ /// Explicit struct type (non-frame context).
+ ty: Option,
+ /// Field chain for the "from" position.
+ from_fields: Vec,
+ /// Field chain for the "to" position.
+ to_fields: Vec,
+ },
}
impl ConstantKind {
diff --git a/macros/src/constant_group/parse/mod.rs b/macros/src/constant_group/parse/mod.rs
index f7ecb02..5cecd43 100644
--- a/macros/src/constant_group/parse/mod.rs
+++ b/macros/src/constant_group/parse/mod.rs
@@ -140,6 +140,11 @@ impl Parse for ConstantGroupInput {
.into_unaligned_pubkey_offsets()
.map_err(|msg| syn::Error::new(kind_ident.span(), msg))?
}
+ "relative_offset" => {
+ let inner;
+ syn::parenthesized!(inner in content);
+ offset::parse_relative_offset(&inner, &frame_type, kind_ident.span())?
+ }
"pubkey_offsets" => {
let inner;
syn::parenthesized!(inner in content);
diff --git a/macros/src/constant_group/parse/offset.rs b/macros/src/constant_group/parse/offset.rs
index 55e7231..1adeae2 100644
--- a/macros/src/constant_group/parse/offset.rs
+++ b/macros/src/constant_group/parse/offset.rs
@@ -2,6 +2,52 @@ use syn::{Expr, Ident, Token, parse::ParseStream};
use super::super::ConstantKind;
+/// Parse a bare field chain: `field.subfield.nested`.
+fn parse_field_chain(input: ParseStream) -> syn::Result> {
+ let mut fields = Vec::new();
+ let ident: Ident = input.parse()?;
+ fields.push(syn::Member::Named(ident));
+ while input.peek(Token![.]) {
+ input.parse::()?;
+ let member: Ident = input.parse()?;
+ fields.push(syn::Member::Named(member));
+ }
+ Ok(fields)
+}
+
+/// Parse `relative_offset!(Struct, from, to)` or `relative_offset!(from, to)`
+/// (frame context).
+pub fn parse_relative_offset(
+ inner: ParseStream,
+ frame_type: &Option,
+ _span: proc_macro2::Span,
+) -> syn::Result {
+ if let Some(frame_path) = frame_type {
+ // Frame context: relative_offset!(from_field.sub, to_field.sub)
+ let _ = frame_path; // struct inferred from frame
+ let from_fields = parse_field_chain(inner)?;
+ inner.parse::()?;
+ let to_fields = parse_field_chain(inner)?;
+ Ok(ConstantKind::RelativeOffset {
+ ty: None,
+ from_fields,
+ to_fields,
+ })
+ } else {
+ // Non-frame: relative_offset!(Struct, from_field.sub, to_field.sub)
+ let ty: syn::Path = inner.parse()?;
+ inner.parse::()?;
+ let from_fields = parse_field_chain(inner)?;
+ inner.parse::()?;
+ let to_fields = parse_field_chain(inner)?;
+ Ok(ConstantKind::RelativeOffset {
+ ty: Some(ty),
+ from_fields,
+ to_fields,
+ })
+ }
+}
+
/// Parse the inside of `offset!(...)`.
///
/// When a `#[frame(Type)]` is present, a bare identifier like `offset!(bump)`
diff --git a/macros/src/cpi_accounts.rs b/macros/src/cpi_accounts.rs
index c780350..2cc9e09 100644
--- a/macros/src/cpi_accounts.rs
+++ b/macros/src/cpi_accounts.rs
@@ -1,7 +1,7 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::{
- Ident, Token, Visibility, braced,
+ Ident, Token, braced,
parse::{Parse, ParseStream},
};
@@ -16,17 +16,14 @@ struct CpiAccountField {
/// Parsed input for the `cpi_accounts!` macro.
pub struct CpiAccountsInput {
- vis: Visibility,
name: Ident,
fields: Vec,
}
impl Parse for CpiAccountsInput {
fn parse(input: ParseStream) -> syn::Result {
- // Consume any outer attributes (e.g. doc comments) before the struct.
+ // Consume any outer attributes (e.g. doc comments) before the name.
let _attrs = input.call(syn::Attribute::parse_outer)?;
- let vis: Visibility = input.parse()?;
- input.parse::()?;
let name: Ident = input.parse()?;
let content;
@@ -48,7 +45,7 @@ impl Parse for CpiAccountsInput {
return Err(input.error("cpi_accounts! must have at least one field"));
}
- Ok(CpiAccountsInput { vis, name, fields })
+ Ok(CpiAccountsInput { name, fields })
}
}
@@ -56,7 +53,6 @@ impl Parse for CpiAccountsInput {
/// `SolAccountInfo` fields first (contiguous), then `SolAccountMeta` fields
/// (contiguous). Registers field names in shared state.
pub fn expand(input: &CpiAccountsInput) -> TokenStream {
- let vis = &input.vis;
let name = &input.name;
// SolAccountInfo fields first, then SolAccountMeta fields.
@@ -102,7 +98,7 @@ pub fn expand(input: &CpiAccountsInput) -> TokenStream {
quote! {
#[repr(C)]
- #vis struct #name {
+ pub struct #name {
#(#info_fields),*,
#(#meta_fields),*
}
diff --git a/macros/src/lib.rs b/macros/src/lib.rs
index 4265056..5bd9aa5 100644
--- a/macros/src/lib.rs
+++ b/macros/src/lib.rs
@@ -51,7 +51,7 @@ pub fn constant_group(input: TokenStream) -> TokenStream {
///
/// ```ignore
/// signer_seeds! {
-/// pub struct PdaSignerSeeds {
+/// PdaSignerSeeds {
/// /// Base mint seed.
/// base,
/// /// Quote mint seed.
@@ -77,7 +77,7 @@ pub fn signer_seeds(input: TokenStream) -> TokenStream {
///
/// ```ignore
/// cpi_accounts! {
-/// pub struct CreateAccountCPIAccounts {
+/// CreateAccountCPIAccounts {
/// /// User account.
/// user,
/// /// Market account.
diff --git a/macros/src/signer_seeds.rs b/macros/src/signer_seeds.rs
index 7fd75aa..d0548d6 100644
--- a/macros/src/signer_seeds.rs
+++ b/macros/src/signer_seeds.rs
@@ -1,7 +1,7 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::{
- Ident, Token, Visibility, braced,
+ Ident, Token, braced,
parse::{Parse, ParseStream},
};
@@ -16,15 +16,12 @@ struct SignerSeedField {
/// Parsed input for the `signer_seeds!` macro.
pub struct SignerSeedsInput {
- vis: Visibility,
name: Ident,
fields: Vec,
}
impl Parse for SignerSeedsInput {
fn parse(input: ParseStream) -> syn::Result {
- let vis: Visibility = input.parse()?;
- input.parse::()?;
let name: Ident = input.parse()?;
let content;
@@ -46,14 +43,13 @@ impl Parse for SignerSeedsInput {
return Err(input.error("signer_seeds! must have at least one field"));
}
- Ok(SignerSeedsInput { vis, name, fields })
+ Ok(SignerSeedsInput { name, fields })
}
}
/// Expand a `signer_seeds!` invocation into a `#[repr(C)]` struct with all
/// fields typed as `SolSignerSeed`, and register field names in shared state.
pub fn expand(input: &SignerSeedsInput) -> TokenStream {
- let vis = &input.vis;
let name = &input.name;
let field_defs: Vec<_> = input
@@ -79,7 +75,7 @@ pub fn expand(input: &SignerSeedsInput) -> TokenStream {
quote! {
#[repr(C)]
- #vis struct #name {
+ pub struct #name {
#(#field_defs),*
}
}
diff --git a/macros/src/size_of_group.rs b/macros/src/size_of_group.rs
index 0a61fba..76a57bb 100644
--- a/macros/src/size_of_group.rs
+++ b/macros/src/size_of_group.rs
@@ -44,11 +44,33 @@ pub fn expand(input: &SizeOfGroupInput) -> proc_macro2::TokenStream {
let doc = format!("Size of {} in bytes.", type_str);
let meta_ident = codegen::meta_ident(&asm_name, Span::call_site());
+ // Primitives (e.g. u8, u64) live in the prelude, not in `super`.
+ let is_primitive = matches!(
+ type_str.as_str(),
+ "u8" | "u16"
+ | "u32"
+ | "u64"
+ | "u128"
+ | "i8"
+ | "i16"
+ | "i32"
+ | "i64"
+ | "i128"
+ | "usize"
+ | "isize"
+ | "bool"
+ );
+ let qualified_ty = if is_primitive {
+ quote! { #ty }
+ } else {
+ quote! { super::#ty }
+ };
+
let meta = codegen::immediate_meta(&meta_ident, &asm_name, &doc, quote! { #rust_name });
let def = quote! {
#[doc = #doc]
- pub const #rust_name: i32 = std::mem::size_of::() as i32;
+ pub const #rust_name: i32 = std::mem::size_of::<#qualified_ty>() as i32;
#meta
};
diff --git a/program/src/dropset/common/memory.s b/program/src/dropset/common/memory.s
index 2e1a4cd..0811054 100644
--- a/program/src/dropset/common/memory.s
+++ b/program/src/dropset/common/memory.s
@@ -1,4 +1,4 @@
-# Field offsets within a runtime account.
+# Assorted runtime account constants.
# -------------------------------------------------------------------------
.equ ACCT_DUPLICATE_OFF, 0 # Borrow state / duplicate marker.
.equ ACCT_IS_SIGNER_OFF, 1 # Whether the account is a signer.
@@ -16,7 +16,16 @@
.equ ACCT_OWNER_CHUNK_2_OFF, 56 # Account owner (chunk 2).
.equ ACCT_OWNER_CHUNK_3_OFF, 64 # Account owner (chunk 3).
.equ ACCT_DATA_LEN_OFF, 80 # Account data length.
+.equ ACCT_DATA_OFF, 88 # Account data start.
.equ ACCT_NON_DUP_MARKER, 255 # Non-dup marker for accounts.
+# Account storage overhead for rent calculation.
+.equ ACCT_STORAGE_OVERHEAD, 128
+# -------------------------------------------------------------------------
+
+# CPI-related constants.
+# -------------------------------------------------------------------------
+# Mask for writable signer (is_writable | is_signer).
+.equ CPI_WRITABLE_SIGNER, 257
# -------------------------------------------------------------------------
# Common data-related constants.
@@ -31,6 +40,15 @@
# Input buffer constants for static header.
# -------------------------------------------------------------------------
.equ IB_USER_DATA_LEN_OFF, 88 # From input buffer to user data length.
+.equ IB_USER_PUBKEY_OFF, 16 # From input buffer to user pubkey.
+# From input buffer to user pubkey (chunk 0).
+.equ IB_USER_PUBKEY_CHUNK_0_OFF, 16
+# From input buffer to user pubkey (chunk 1).
+.equ IB_USER_PUBKEY_CHUNK_1_OFF, 24
+# From input buffer to user pubkey (chunk 2).
+.equ IB_USER_PUBKEY_CHUNK_2_OFF, 32
+# From input buffer to user pubkey (chunk 3).
+.equ IB_USER_PUBKEY_CHUNK_3_OFF, 40
# From input buffer to market duplicate flag.
.equ IB_MARKET_DUPLICATE_OFF, 10344
# From input buffer to market data length.
@@ -44,7 +62,18 @@
.equ IB_MARKET_PUBKEY_CHUNK_2_OFF, 10368
# From input buffer to market address (chunk 3).
.equ IB_MARKET_PUBKEY_CHUNK_3_OFF, 10376
+# From address to owner in a runtime account.
+.equ IB_ADDRESS_TO_OWNER_REL_OFF_IMM, 32
+# From owner to lamports in a runtime account.
+.equ IB_OWNER_TO_LAMPORTS_REL_OFF_IMM, 32
+# From lamports to data start in a runtime account.
+.equ IB_LAMPORTS_TO_DATA_REL_OFF_IMM, 16
+# From user data to market address in the input buffer.
+.equ IB_USER_DATA_TO_MARKET_ADDRESS_REL_OFF_IMM, 10256
# -------------------------------------------------------------------------
+.equ SIZE_OF_U8, 1 # Size of u8 in bytes.
.equ SIZE_OF_ADDRESS, 32 # Size of Address in bytes.
.equ SIZE_OF_EMPTY_ACCOUNT, 10336 # Size of EmptyAccount in bytes.
+.equ SIZE_OF_MARKET_HEADER, 40 # Size of MarketHeader in bytes.
+.equ SIZE_OF_CREATE_ACCOUNT_DATA, 56 # Size of CreateAccountData in bytes.
diff --git a/program/src/dropset/market/register.s b/program/src/dropset/market/register.s
index e1fe465..ce498bb 100644
--- a/program/src/dropset/market/register.s
+++ b/program/src/dropset/market/register.s
@@ -6,187 +6,195 @@
# Stack frame for REGISTER-MARKET.
# -------------------------------------------------------------------------
-.equ RM_FM_PDA_SEEDS_OFF, -648 # Signer seeds offset.
+.equ RM_FM_PDA_SEEDS_OFF, -664 # Signer seeds offset.
.equ RM_FM_PDA_SEEDS_N_SEEDS, 3 # Number of signer seeds.
-.equ RM_FM_PDA_SEEDS_BASE_ADDR_OFF, -648 # Base signer seed address.
-.equ RM_FM_PDA_SEEDS_BASE_LEN_OFF, -640 # Base signer seed length.
-.equ RM_FM_PDA_SEEDS_QUOTE_ADDR_OFF, -632 # Quote signer seed address.
-.equ RM_FM_PDA_SEEDS_QUOTE_LEN_OFF, -624 # Quote signer seed length.
-.equ RM_FM_PDA_SEEDS_BUMP_ADDR_OFF, -616 # Bump signer seed address.
-.equ RM_FM_PDA_SEEDS_BUMP_LEN_OFF, -608 # Bump signer seed length.
-.equ RM_FM_PDA_OFF, -600 # PDA address.
-.equ RM_FM_PDA_CHUNK_0_OFF, -600 # PDA address (chunk 0).
-.equ RM_FM_PDA_CHUNK_1_OFF, -592 # PDA address (chunk 1).
-.equ RM_FM_PDA_CHUNK_2_OFF, -584 # PDA address (chunk 2).
-.equ RM_FM_PDA_CHUNK_3_OFF, -576 # PDA address (chunk 3).
-.equ RM_FM_SYSTEM_PROGRAM_PUBKEY_OFF, -568 # System Program pubkey.
+.equ RM_FM_PDA_SEEDS_BASE_ADDR_OFF, -664 # Base signer seed address.
+.equ RM_FM_PDA_SEEDS_BASE_LEN_OFF, -656 # Base signer seed length.
+.equ RM_FM_PDA_SEEDS_QUOTE_ADDR_OFF, -648 # Quote signer seed address.
+.equ RM_FM_PDA_SEEDS_QUOTE_LEN_OFF, -640 # Quote signer seed length.
+.equ RM_FM_PDA_SEEDS_BUMP_ADDR_OFF, -632 # Bump signer seed address.
+.equ RM_FM_PDA_SEEDS_BUMP_LEN_OFF, -624 # Bump signer seed length.
+.equ RM_FM_PDA_OFF, -616 # PDA address.
+.equ RM_FM_PDA_CHUNK_0_OFF, -616 # PDA address (chunk 0).
+.equ RM_FM_PDA_CHUNK_1_OFF, -608 # PDA address (chunk 1).
+.equ RM_FM_PDA_CHUNK_2_OFF, -600 # PDA address (chunk 2).
+.equ RM_FM_PDA_CHUNK_3_OFF, -592 # PDA address (chunk 3).
+.equ RM_FM_SYSTEM_PROGRAM_PUBKEY_OFF, -584 # System Program pubkey.
# System Program pubkey (chunk 0).
-.equ RM_FM_SYSTEM_PROGRAM_PUBKEY_CHUNK_0_OFF, -568
+.equ RM_FM_SYSTEM_PROGRAM_PUBKEY_CHUNK_0_OFF, -584
# System Program pubkey (chunk 1).
-.equ RM_FM_SYSTEM_PROGRAM_PUBKEY_CHUNK_1_OFF, -560
+.equ RM_FM_SYSTEM_PROGRAM_PUBKEY_CHUNK_1_OFF, -576
# System Program pubkey (chunk 2).
-.equ RM_FM_SYSTEM_PROGRAM_PUBKEY_CHUNK_2_OFF, -552
+.equ RM_FM_SYSTEM_PROGRAM_PUBKEY_CHUNK_2_OFF, -568
# System Program pubkey (chunk 3).
-.equ RM_FM_SYSTEM_PROGRAM_PUBKEY_CHUNK_3_OFF, -544
-.equ RM_FM_BUMP_OFF, -8 # Bump seed.
-.equ RM_FM_CREATE_ACCT_DATA_OFF, -536 # CreateAccount instruction data.
+.equ RM_FM_SYSTEM_PROGRAM_PUBKEY_CHUNK_3_OFF, -560
+.equ RM_FM_CREATE_ACCT_DATA_OFF, -552 # CreateAccount instruction data.
# Lamports field within CreateAccount instruction data.
-.equ RM_FM_CREATE_ACCT_LAMPORTS_UOFF, -532
+.equ RM_FM_CREATE_ACCT_LAMPORTS_UOFF, -548
# Space field within CreateAccount instruction data.
-.equ RM_FM_CREATE_ACCT_SPACE_UOFF, -524
+.equ RM_FM_CREATE_ACCT_SPACE_UOFF, -540
# Owner field within CreateAccount instruction data.
-.equ RM_FM_CREATE_ACCT_OWNER_UOFF, -516
+.equ RM_FM_CREATE_ACCT_OWNER_UOFF, -532
# Owner field within CreateAccount instruction data (chunk 0).
-.equ RM_FM_CREATE_ACCT_OWNER_CHUNK_0_UOFF, -516
+.equ RM_FM_CREATE_ACCT_OWNER_CHUNK_0_UOFF, -532
# Owner field within CreateAccount instruction data (chunk 1).
-.equ RM_FM_CREATE_ACCT_OWNER_CHUNK_1_UOFF, -508
+.equ RM_FM_CREATE_ACCT_OWNER_CHUNK_1_UOFF, -524
# Owner field within CreateAccount instruction data (chunk 2).
-.equ RM_FM_CREATE_ACCT_OWNER_CHUNK_2_UOFF, -500
+.equ RM_FM_CREATE_ACCT_OWNER_CHUNK_2_UOFF, -516
# Owner field within CreateAccount instruction data (chunk 3).
-.equ RM_FM_CREATE_ACCT_OWNER_CHUNK_3_UOFF, -492
+.equ RM_FM_CREATE_ACCT_OWNER_CHUNK_3_UOFF, -508
.equ RM_FM_CPI_N_ACCOUNTS, 6 # Number of CPI accounts.
-.equ RM_FM_CPI_SOL_ACCT_INFO_OFF, -480 # Start of SolAccountInfo vector.
-.equ RM_FM_CPI_SOL_ACCT_META_OFF, -144 # Start of SolAccountMeta vector.
-.equ RM_FM_CPI_USER_ACCT_INFO_KEY_UOFF, -480 # User account info key.
+.equ RM_FM_CPI_SOL_ACCT_INFO_OFF, -496 # Start of SolAccountInfo vector.
+.equ RM_FM_CPI_SOL_ACCT_META_OFF, -160 # Start of SolAccountMeta vector.
+.equ RM_FM_CPI_USER_ACCT_INFO_KEY_UOFF, -496 # User account info key.
# User account info lamports.
-.equ RM_FM_CPI_USER_ACCT_INFO_LAMPORTS_UOFF, -472
+.equ RM_FM_CPI_USER_ACCT_INFO_LAMPORTS_UOFF, -488
# User account info data length.
-.equ RM_FM_CPI_USER_ACCT_INFO_DATA_LEN_UOFF, -464
-.equ RM_FM_CPI_USER_ACCT_INFO_DATA_UOFF, -456 # User account info data.
-.equ RM_FM_CPI_USER_ACCT_INFO_OWNER_UOFF, -448 # User account info owner.
+.equ RM_FM_CPI_USER_ACCT_INFO_DATA_LEN_UOFF, -480
+.equ RM_FM_CPI_USER_ACCT_INFO_DATA_UOFF, -472 # User account info data.
+.equ RM_FM_CPI_USER_ACCT_INFO_OWNER_UOFF, -464 # User account info owner.
# User account info rent epoch.
-.equ RM_FM_CPI_USER_ACCT_INFO_RENT_EPOCH_UOFF, -440
+.equ RM_FM_CPI_USER_ACCT_INFO_RENT_EPOCH_UOFF, -456
# User account info is signer.
-.equ RM_FM_CPI_USER_ACCT_INFO_IS_SIGNER_UOFF, -432
+.equ RM_FM_CPI_USER_ACCT_INFO_IS_SIGNER_UOFF, -448
# User account info is writable.
-.equ RM_FM_CPI_USER_ACCT_INFO_IS_WRITABLE_UOFF, -431
+.equ RM_FM_CPI_USER_ACCT_INFO_IS_WRITABLE_UOFF, -447
# User account info executable.
-.equ RM_FM_CPI_USER_ACCT_INFO_EXECUTABLE_UOFF, -430
-.equ RM_FM_CPI_USER_ACCT_META_PUBKEY_UOFF, -144 # User account meta pubkey.
+.equ RM_FM_CPI_USER_ACCT_INFO_EXECUTABLE_UOFF, -446
+.equ RM_FM_CPI_USER_ACCT_META_PUBKEY_UOFF, -160 # User account meta pubkey.
# User account meta is writable.
-.equ RM_FM_CPI_USER_ACCT_META_IS_WRITABLE_UOFF, -136
+.equ RM_FM_CPI_USER_ACCT_META_IS_WRITABLE_UOFF, -152
# User account meta is signer.
-.equ RM_FM_CPI_USER_ACCT_META_IS_SIGNER_UOFF, -135
-.equ RM_FM_CPI_TARGET_ACCT_INFO_KEY_UOFF, -424 # Target account info key.
+.equ RM_FM_CPI_USER_ACCT_META_IS_SIGNER_UOFF, -151
+.equ RM_FM_CPI_TARGET_ACCT_INFO_KEY_UOFF, -440 # Target account info key.
# Target account info lamports.
-.equ RM_FM_CPI_TARGET_ACCT_INFO_LAMPORTS_UOFF, -416
+.equ RM_FM_CPI_TARGET_ACCT_INFO_LAMPORTS_UOFF, -432
# Target account info data length.
-.equ RM_FM_CPI_TARGET_ACCT_INFO_DATA_LEN_UOFF, -408
-.equ RM_FM_CPI_TARGET_ACCT_INFO_DATA_UOFF, -400 # Target account info data.
+.equ RM_FM_CPI_TARGET_ACCT_INFO_DATA_LEN_UOFF, -424
+.equ RM_FM_CPI_TARGET_ACCT_INFO_DATA_UOFF, -416 # Target account info data.
# Target account info owner.
-.equ RM_FM_CPI_TARGET_ACCT_INFO_OWNER_UOFF, -392
+.equ RM_FM_CPI_TARGET_ACCT_INFO_OWNER_UOFF, -408
# Target account info rent epoch.
-.equ RM_FM_CPI_TARGET_ACCT_INFO_RENT_EPOCH_UOFF, -384
+.equ RM_FM_CPI_TARGET_ACCT_INFO_RENT_EPOCH_UOFF, -400
# Target account info is signer.
-.equ RM_FM_CPI_TARGET_ACCT_INFO_IS_SIGNER_UOFF, -376
+.equ RM_FM_CPI_TARGET_ACCT_INFO_IS_SIGNER_UOFF, -392
# Target account info is writable.
-.equ RM_FM_CPI_TARGET_ACCT_INFO_IS_WRITABLE_UOFF, -375
+.equ RM_FM_CPI_TARGET_ACCT_INFO_IS_WRITABLE_UOFF, -391
# Target account info executable.
-.equ RM_FM_CPI_TARGET_ACCT_INFO_EXECUTABLE_UOFF, -374
+.equ RM_FM_CPI_TARGET_ACCT_INFO_EXECUTABLE_UOFF, -390
# Target account meta pubkey.
-.equ RM_FM_CPI_TARGET_ACCT_META_PUBKEY_UOFF, -128
+.equ RM_FM_CPI_TARGET_ACCT_META_PUBKEY_UOFF, -144
# Target account meta is writable.
-.equ RM_FM_CPI_TARGET_ACCT_META_IS_WRITABLE_UOFF, -120
+.equ RM_FM_CPI_TARGET_ACCT_META_IS_WRITABLE_UOFF, -136
# Target account meta is signer.
-.equ RM_FM_CPI_TARGET_ACCT_META_IS_SIGNER_UOFF, -119
+.equ RM_FM_CPI_TARGET_ACCT_META_IS_SIGNER_UOFF, -135
# Proprietor account info key.
-.equ RM_FM_CPI_PROPRIETOR_ACCT_INFO_KEY_UOFF, -368
+.equ RM_FM_CPI_PROPRIETOR_ACCT_INFO_KEY_UOFF, -384
# Proprietor account info lamports.
-.equ RM_FM_CPI_PROPRIETOR_ACCT_INFO_LAMPORTS_UOFF, -360
+.equ RM_FM_CPI_PROPRIETOR_ACCT_INFO_LAMPORTS_UOFF, -376
# Proprietor account info data length.
-.equ RM_FM_CPI_PROPRIETOR_ACCT_INFO_DATA_LEN_UOFF, -352
+.equ RM_FM_CPI_PROPRIETOR_ACCT_INFO_DATA_LEN_UOFF, -368
# Proprietor account info data.
-.equ RM_FM_CPI_PROPRIETOR_ACCT_INFO_DATA_UOFF, -344
+.equ RM_FM_CPI_PROPRIETOR_ACCT_INFO_DATA_UOFF, -360
# Proprietor account info owner.
-.equ RM_FM_CPI_PROPRIETOR_ACCT_INFO_OWNER_UOFF, -336
+.equ RM_FM_CPI_PROPRIETOR_ACCT_INFO_OWNER_UOFF, -352
# Proprietor account info rent epoch.
-.equ RM_FM_CPI_PROPRIETOR_ACCT_INFO_RENT_EPOCH_UOFF, -328
+.equ RM_FM_CPI_PROPRIETOR_ACCT_INFO_RENT_EPOCH_UOFF, -344
# Proprietor account info is signer.
-.equ RM_FM_CPI_PROPRIETOR_ACCT_INFO_IS_SIGNER_UOFF, -320
+.equ RM_FM_CPI_PROPRIETOR_ACCT_INFO_IS_SIGNER_UOFF, -336
# Proprietor account info is writable.
-.equ RM_FM_CPI_PROPRIETOR_ACCT_INFO_IS_WRITABLE_UOFF, -319
+.equ RM_FM_CPI_PROPRIETOR_ACCT_INFO_IS_WRITABLE_UOFF, -335
# Proprietor account info executable.
-.equ RM_FM_CPI_PROPRIETOR_ACCT_INFO_EXECUTABLE_UOFF, -318
+.equ RM_FM_CPI_PROPRIETOR_ACCT_INFO_EXECUTABLE_UOFF, -334
# Proprietor account meta pubkey.
-.equ RM_FM_CPI_PROPRIETOR_ACCT_META_PUBKEY_UOFF, -112
+.equ RM_FM_CPI_PROPRIETOR_ACCT_META_PUBKEY_UOFF, -128
# Proprietor account meta is writable.
-.equ RM_FM_CPI_PROPRIETOR_ACCT_META_IS_WRITABLE_UOFF, -104
+.equ RM_FM_CPI_PROPRIETOR_ACCT_META_IS_WRITABLE_UOFF, -120
# Proprietor account meta is signer.
-.equ RM_FM_CPI_PROPRIETOR_ACCT_META_IS_SIGNER_UOFF, -103
-.equ RM_FM_CPI_MINT_ACCT_INFO_KEY_UOFF, -312 # Mint account info key.
+.equ RM_FM_CPI_PROPRIETOR_ACCT_META_IS_SIGNER_UOFF, -119
+.equ RM_FM_CPI_MINT_ACCT_INFO_KEY_UOFF, -328 # Mint account info key.
# Mint account info lamports.
-.equ RM_FM_CPI_MINT_ACCT_INFO_LAMPORTS_UOFF, -304
+.equ RM_FM_CPI_MINT_ACCT_INFO_LAMPORTS_UOFF, -320
# Mint account info data length.
-.equ RM_FM_CPI_MINT_ACCT_INFO_DATA_LEN_UOFF, -296
-.equ RM_FM_CPI_MINT_ACCT_INFO_DATA_UOFF, -288 # Mint account info data.
-.equ RM_FM_CPI_MINT_ACCT_INFO_OWNER_UOFF, -280 # Mint account info owner.
+.equ RM_FM_CPI_MINT_ACCT_INFO_DATA_LEN_UOFF, -312
+.equ RM_FM_CPI_MINT_ACCT_INFO_DATA_UOFF, -304 # Mint account info data.
+.equ RM_FM_CPI_MINT_ACCT_INFO_OWNER_UOFF, -296 # Mint account info owner.
# Mint account info rent epoch.
-.equ RM_FM_CPI_MINT_ACCT_INFO_RENT_EPOCH_UOFF, -272
+.equ RM_FM_CPI_MINT_ACCT_INFO_RENT_EPOCH_UOFF, -288
# Mint account info is signer.
-.equ RM_FM_CPI_MINT_ACCT_INFO_IS_SIGNER_UOFF, -264
+.equ RM_FM_CPI_MINT_ACCT_INFO_IS_SIGNER_UOFF, -280
# Mint account info is writable.
-.equ RM_FM_CPI_MINT_ACCT_INFO_IS_WRITABLE_UOFF, -263
+.equ RM_FM_CPI_MINT_ACCT_INFO_IS_WRITABLE_UOFF, -279
# Mint account info executable.
-.equ RM_FM_CPI_MINT_ACCT_INFO_EXECUTABLE_UOFF, -262
-.equ RM_FM_CPI_MINT_ACCT_META_PUBKEY_UOFF, -96 # Mint account meta pubkey.
+.equ RM_FM_CPI_MINT_ACCT_INFO_EXECUTABLE_UOFF, -278
+.equ RM_FM_CPI_MINT_ACCT_META_PUBKEY_UOFF, -112 # Mint account meta pubkey.
# Mint account meta is writable.
-.equ RM_FM_CPI_MINT_ACCT_META_IS_WRITABLE_UOFF, -88
+.equ RM_FM_CPI_MINT_ACCT_META_IS_WRITABLE_UOFF, -104
# Mint account meta is signer.
-.equ RM_FM_CPI_MINT_ACCT_META_IS_SIGNER_UOFF, -87
+.equ RM_FM_CPI_MINT_ACCT_META_IS_SIGNER_UOFF, -103
# System Program account info key.
-.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_INFO_KEY_UOFF, -256
+.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_INFO_KEY_UOFF, -272
# System Program account info lamports.
-.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_INFO_LAMPORTS_UOFF, -248
+.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_INFO_LAMPORTS_UOFF, -264
# System Program account info data length.
-.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_INFO_DATA_LEN_UOFF, -240
+.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_INFO_DATA_LEN_UOFF, -256
# System Program account info data.
-.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_INFO_DATA_UOFF, -232
+.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_INFO_DATA_UOFF, -248
# System Program account info owner.
-.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_INFO_OWNER_UOFF, -224
+.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_INFO_OWNER_UOFF, -240
# System Program account info rent epoch.
-.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_INFO_RENT_EPOCH_UOFF, -216
+.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_INFO_RENT_EPOCH_UOFF, -232
# System Program account info is signer.
-.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_INFO_IS_SIGNER_UOFF, -208
+.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_INFO_IS_SIGNER_UOFF, -224
# System Program account info is writable.
-.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_INFO_IS_WRITABLE_UOFF, -207
+.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_INFO_IS_WRITABLE_UOFF, -223
# System Program account info executable.
-.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_INFO_EXECUTABLE_UOFF, -206
+.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_INFO_EXECUTABLE_UOFF, -222
# System Program account meta pubkey.
-.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_META_PUBKEY_UOFF, -80
+.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_META_PUBKEY_UOFF, -96
# System Program account meta is writable.
-.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_META_IS_WRITABLE_UOFF, -72
+.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_META_IS_WRITABLE_UOFF, -88
# System Program account meta is signer.
-.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_META_IS_SIGNER_UOFF, -71
+.equ RM_FM_CPI_SYSTEM_PROGRAM_ACCT_META_IS_SIGNER_UOFF, -87
# Token Program account info key.
-.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_INFO_KEY_UOFF, -200
+.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_INFO_KEY_UOFF, -216
# Token Program account info lamports.
-.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_INFO_LAMPORTS_UOFF, -192
+.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_INFO_LAMPORTS_UOFF, -208
# Token Program account info data length.
-.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_INFO_DATA_LEN_UOFF, -184
+.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_INFO_DATA_LEN_UOFF, -200
# Token Program account info data.
-.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_INFO_DATA_UOFF, -176
+.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_INFO_DATA_UOFF, -192
# Token Program account info owner.
-.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_INFO_OWNER_UOFF, -168
+.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_INFO_OWNER_UOFF, -184
# Token Program account info rent epoch.
-.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_INFO_RENT_EPOCH_UOFF, -160
+.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_INFO_RENT_EPOCH_UOFF, -176
# Token Program account info is signer.
-.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_INFO_IS_SIGNER_UOFF, -152
+.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_INFO_IS_SIGNER_UOFF, -168
# Token Program account info is writable.
-.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_INFO_IS_WRITABLE_UOFF, -151
+.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_INFO_IS_WRITABLE_UOFF, -167
# Token Program account info executable.
-.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_INFO_EXECUTABLE_UOFF, -150
+.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_INFO_EXECUTABLE_UOFF, -166
# Token Program account meta pubkey.
-.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_META_PUBKEY_UOFF, -64
+.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_META_PUBKEY_UOFF, -80
# Token Program account meta is writable.
-.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_META_IS_WRITABLE_UOFF, -56
+.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_META_IS_WRITABLE_UOFF, -72
# Token Program account meta is signer.
-.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_META_IS_SIGNER_UOFF, -55
+.equ RM_FM_CPI_TOKEN_PROGRAM_ACCT_META_IS_SIGNER_UOFF, -71
+.equ RM_FM_SIGNERS_SEEDS_ADDR_UOFF, -64 # Signers seeds address.
+.equ RM_FM_SIGNERS_SEEDS_LEN_UOFF, -56 # Signers seeds length.
.equ RM_FM_SOL_INSN_OFF, -48 # SolInstruction offset.
.equ RM_FM_SOL_INSN_PROGRAM_ID_UOFF, -48 # SolInstruction program ID.
.equ RM_FM_SOL_INSN_ACCOUNTS_UOFF, -40 # SolInstruction accounts pointer.
.equ RM_FM_SOL_INSN_ACCOUNT_LEN_UOFF, -32 # SolInstruction account length.
.equ RM_FM_SOL_INSN_DATA_UOFF, -24 # SolInstruction data pointer.
.equ RM_FM_SOL_INSN_DATA_LEN_UOFF, -16 # SolInstruction data length.
+.equ RM_FM_BUMP_OFF, -8 # Bump seed.
+# From pda_seeds to sol_instruction.
+.equ RM_FM_PDA_SEEDS_TO_SOL_INSN_REL_OFF_IMM, 616
+# From pda to signers_seeds.
+.equ RM_FM_PDA_TO_SIGNERS_SEEDS_REL_OFF_IMM, 552
+# From create_account_data to cpi account metas.
+.equ RM_FM_CREATE_ACCT_DATA_TO_CPI_ACCT_METAS_REL_OFF_IMM, 392
# -------------------------------------------------------------------------
# Miscellaneous market registration constants.
@@ -205,6 +213,9 @@
.equ RM_MISC_QUOTE_DATA_LEN_OFF, 31096
# Number of seeds for market PDA derivation (base, quote).
.equ RM_MISC_TRY_FIND_PDA_SEEDS_LEN, 2
+# Number of accounts for CreateAccount CPI (user, target).
+.equ RM_MISC_CREATE_ACCOUNT_N_ACCOUNTS, 2
+.equ RM_MISC_N_PDA_SIGNERS, 1 # Number of PDA signers for CPI.
# -------------------------------------------------------------------------
register_market:
@@ -272,10 +283,10 @@ register_market:
add64 r3, REGISTER_MARKET_DATA_LEN
# syscall.seeds_len = register_misc.TRY_FIND_PDA_SEEDS_LEN
mov64 r2, RM_MISC_TRY_FIND_PDA_SEEDS_LEN
- # syscall.program_address = RegisterMarketFrame.pda
+ # syscall.program_address = &frame.pda
mov64 r4, r10
add64 r4, RM_FM_PDA_OFF
- # syscall.bump_seed = RegisterMarketFrame.bump
+ # syscall.bump_seed = &frame.bump
mov64 r5, r10
add64 r5, RM_FM_BUMP_OFF
call sol_try_find_program_address
@@ -293,6 +304,20 @@ register_market:
ldxdw r7, [r6 + IB_MARKET_PUBKEY_CHUNK_3_OFF]
ldxdw r8, [r10 + RM_FM_PDA_CHUNK_3_OFF]
jne r7, r8, e_invalid_market_pubkey
+ # frame.pda_seeds.bump.addr = &frame.bump
+ stxdw [r10 + RM_FM_PDA_SEEDS_BUMP_ADDR_OFF], r5
+ # frame.pda_seeds.bump.len = u8.size
+ mov64 r7, SIZE_OF_U8
+ stxdw [r10 + RM_FM_PDA_SEEDS_BUMP_LEN_OFF], r7
+ # frame.create_account_data.owner = syscall.program_id
+ ldxdw r7, [r3 + PUBKEY_CHUNK_0_OFF]
+ stxdw [r10 + RM_FM_CREATE_ACCT_OWNER_CHUNK_0_UOFF], r7
+ ldxdw r7, [r3 + PUBKEY_CHUNK_1_OFF]
+ stxdw [r10 + RM_FM_CREATE_ACCT_OWNER_CHUNK_1_UOFF], r7
+ ldxdw r7, [r3 + PUBKEY_CHUNK_2_OFF]
+ stxdw [r10 + RM_FM_CREATE_ACCT_OWNER_CHUNK_2_UOFF], r7
+ ldxdw r7, [r3 + PUBKEY_CHUNK_3_OFF]
+ stxdw [r10 + RM_FM_CREATE_ACCT_OWNER_CHUNK_3_UOFF], r7
# if acct.duplicate != account.NON_DUP_MARKER
# return ErrorCode::SystemProgramIsDuplicate
ldxb r7, [r9 + ACCT_DUPLICATE_OFF]
@@ -311,6 +336,10 @@ register_market:
ldxdw r7, [r9 + ACCT_ADDRESS_CHUNK_3_OFF]
ldxdw r8, [r10 + RM_FM_SYSTEM_PROGRAM_PUBKEY_CHUNK_3_OFF]
jne r7, r8, e_invalid_system_program_pubkey
+ # frame.sol_instruction.program_id = &acct.address
+ mov64 r7, r9
+ add64 r7, ACCT_ADDRESS_OFF
+ stxdw [r10 + RM_FM_SOL_INSN_PROGRAM_ID_UOFF], r7
# system_program_padded_data_len = acct.padded_data_len
ldxdw r7, [r9 + ACCT_DATA_LEN_OFF]
add64 r7, DATA_LEN_MAX_PAD
@@ -338,4 +367,84 @@ register_market:
# (1 CU) replaces lddw (2 CUs).
mov32 r8, PUBKEY_RENT_CHUNK_3_LO
jne r7, r8, e_invalid_rent_sysvar_pubkey
+ # frame.create_account_data.space = MarketHeader.size
+ mov64 r7, SIZE_OF_MARKET_HEADER
+ stxdw [r10 + RM_FM_CREATE_ACCT_SPACE_UOFF], r7
+ # acct_size = MarketHeader.size + account.STORAGE_OVERHEAD
+ add64 r7, ACCT_STORAGE_OVERHEAD
+ # lamports_per_byte = acct.data.lamports_per_byte
+ ldxdw r8, [r9 + ACCT_DATA_OFF]
+ # frame.create_account_data.lamports = acct_size * lamports_per_byte
+ mul64 r7, r8
+ stxdw [r10 + RM_FM_CREATE_ACCT_LAMPORTS_UOFF], r7
+ # frame.cpi.user_info.is_signer = true
+ # frame.cpi.user_info.is_writable = true
+ sth [r10 + RM_FM_CPI_USER_ACCT_INFO_IS_SIGNER_UOFF], CPI_WRITABLE_SIGNER
+ # frame.cpi.user_meta.is_writable = true
+ # frame.cpi.user_meta.is_signer = true
+ sth [r10 + RM_FM_CPI_USER_ACCT_META_IS_WRITABLE_UOFF], CPI_WRITABLE_SIGNER
+ # frame.cpi.target_info.is_signer = true
+ # frame.cpi.target_info.is_writable = true
+ sth [r10 + RM_FM_CPI_TARGET_ACCT_INFO_IS_SIGNER_UOFF], CPI_WRITABLE_SIGNER
+ # frame.cpi.target_meta.is_writable = true
+ # frame.cpi.target_meta.is_signer = true
+ sth [r10 + RM_FM_CPI_TARGET_ACCT_META_IS_WRITABLE_UOFF], CPI_WRITABLE_SIGNER
+ # frame.cpi.user_meta.pubkey = &input.user.address
+ # frame.cpi.user_info.key = &input.user.address
+ add64 r6, IB_USER_PUBKEY_OFF
+ stxdw [r10 + RM_FM_CPI_USER_ACCT_META_PUBKEY_UOFF], r6
+ stxdw [r10 + RM_FM_CPI_USER_ACCT_INFO_KEY_UOFF], r6
+ # frame.cpi.user_info.owner = &input.user.owner
+ add64 r6, IB_ADDRESS_TO_OWNER_REL_OFF_IMM
+ stxdw [r10 + RM_FM_CPI_USER_ACCT_INFO_OWNER_UOFF], r6
+ # frame.cpi.user_info.lamports = &input.user.lamports
+ add64 r6, IB_OWNER_TO_LAMPORTS_REL_OFF_IMM
+ stxdw [r10 + RM_FM_CPI_USER_ACCT_INFO_LAMPORTS_UOFF], r6
+ # frame.cpi.user_info.data = &input.user.data
+ add64 r6, IB_LAMPORTS_TO_DATA_REL_OFF_IMM
+ stxdw [r10 + RM_FM_CPI_USER_ACCT_INFO_DATA_UOFF], r6
+ # frame.cpi.target_meta.pubkey = &input.market.address
+ # frame.cpi.target_info.key = &input.market.address
+ add64 r6, IB_USER_DATA_TO_MARKET_ADDRESS_REL_OFF_IMM
+ stxdw [r10 + RM_FM_CPI_TARGET_ACCT_META_PUBKEY_UOFF], r6
+ stxdw [r10 + RM_FM_CPI_TARGET_ACCT_INFO_KEY_UOFF], r6
+ # frame.cpi.target_info.owner = &input.market.owner
+ add64 r6, IB_ADDRESS_TO_OWNER_REL_OFF_IMM
+ stxdw [r10 + RM_FM_CPI_TARGET_ACCT_INFO_OWNER_UOFF], r6
+ # frame.cpi.target_info.lamports = &input.market.lamports
+ add64 r6, IB_OWNER_TO_LAMPORTS_REL_OFF_IMM
+ stxdw [r10 + RM_FM_CPI_TARGET_ACCT_INFO_LAMPORTS_UOFF], r6
+ # frame.cpi.target_info.data = &input.market.data
+ add64 r6, IB_LAMPORTS_TO_DATA_REL_OFF_IMM
+ stxdw [r10 + RM_FM_CPI_TARGET_ACCT_INFO_DATA_UOFF], r6
+ # frame.signers_seeds.addr = &frame.pda_seeds
+ stxdw [r10 + RM_FM_SIGNERS_SEEDS_ADDR_UOFF], r1
+ # frame.signers_seeds.len = frame.PDA_SEEDS_N_SEEDS
+ mov64 r7, RM_FM_PDA_SEEDS_N_SEEDS
+ stxdw [r10 + RM_FM_SIGNERS_SEEDS_LEN_UOFF], r7
+ # frame.sol_instruction.data = &frame.create_account_data
+ mov64 r7, r10
+ add64 r7, RM_FM_CREATE_ACCT_DATA_OFF
+ stxdw [r10 + RM_FM_SOL_INSN_DATA_UOFF], r7
+ # frame.sol_instruction.accounts = &frame.cpi.account_metas
+ add64 r7, RM_FM_CREATE_ACCT_DATA_TO_CPI_ACCT_METAS_REL_OFF_IMM
+ stxdw [r10 + RM_FM_SOL_INSN_ACCOUNTS_UOFF], r7
+ # frame.sol_instruction.account_len = register_misc.CREATE_ACCOUNT_N_ACCOUNTS
+ mov64 r7, RM_MISC_CREATE_ACCOUNT_N_ACCOUNTS
+ stxdw [r10 + RM_FM_SOL_INSN_ACCOUNT_LEN_UOFF], r7
+ # frame.sol_instruction.data_len = CreateAccountData.size
+ mov64 r7, SIZE_OF_CREATE_ACCOUNT_DATA
+ stxdw [r10 + RM_FM_SOL_INSN_DATA_LEN_UOFF], r7
+ # syscall.instruction = &frame.sol_instruction (r1 from pda_seeds)
+ add64 r1, RM_FM_PDA_SEEDS_TO_SOL_INSN_REL_OFF_IMM
+ # syscall.account_infos = &frame.cpi.account_infos
+ mov64 r2, r10
+ add64 r2, RM_FM_CPI_SOL_ACCT_INFO_OFF
+ # syscall.account_infos_len = register_misc.CREATE_ACCOUNT_N_ACCOUNTS
+ mov64 r3, RM_MISC_CREATE_ACCOUNT_N_ACCOUNTS
+ # syscall.seeds = &frame.signers_seeds (r4 from pda)
+ add64 r4, RM_FM_PDA_TO_SIGNERS_SEEDS_REL_OFF_IMM
+ # syscall.seeds_len = register_misc.N_PDA_SIGNERS
+ mov64 r5, RM_MISC_N_PDA_SIGNERS
+ call sol_invoke_signed_c
exit
diff --git a/tests/src/lib.rs b/tests/src/lib.rs
index b5bc8b0..6458721 100644
--- a/tests/src/lib.rs
+++ b/tests/src/lib.rs
@@ -45,8 +45,13 @@ pub struct TestSetup {
}
/// Creates a test environment for the default `dropset` program.
+///
+/// Sets `exemption_threshold = 1.0` (SIMD-0194) so the rent calculation
+/// in the program (`acct_size * lamports_per_byte`) matches the sysvar.
pub fn setup() -> TestSetup {
- setup_program(DEFAULT_PROGRAM)
+ let mut setup = setup_program(DEFAULT_PROGRAM);
+ setup.mollusk.sysvars.rent.exemption_threshold = 1.0;
+ setup
}
/// Creates a test environment for a named program binary under `target/asm/`.
diff --git a/tests/tests/cases/register_market.rs b/tests/tests/cases/register_market.rs
index e20b45b..1f216a8 100644
--- a/tests/tests/cases/register_market.rs
+++ b/tests/tests/cases/register_market.rs
@@ -1,11 +1,13 @@
-use dropset_interface::market::RegisterMarketAccounts;
+use dropset_interface::market::{MarketHeader, RegisterMarketAccounts};
use dropset_interface::{Discriminant, ErrorCode};
use dropset_tests::{
CaseResult, TestCase, TestSetup, check, check_custom, check_with_accounts, find_pda_seed_pair,
test_cases,
};
+use mollusk_svm::program;
+use mollusk_svm::result::ProgramResult as MolluskResult;
use solana_account::Account;
-use solana_sdk::instruction::AccountMeta;
+use solana_sdk::instruction::{AccountMeta, Instruction};
use solana_sdk::pubkey::Pubkey;
test_cases! {
@@ -33,6 +35,7 @@ test_cases! {
InvalidRentSysvarPubkeyChunk2,
InvalidRentSysvarPubkeyChunk3,
InvalidRentSysvarPubkeyChunk3Hi,
+ CreateAccountHappyPath,
}
}
@@ -57,6 +60,54 @@ fn into_metas_and_accounts(
(metas, paired)
}
+const USER_LAMPORTS: u64 = 1_000_000;
+const MARKET_HEADER_SIZE: usize = size_of::();
+
+/// Build valid accounts that pass all checks for a successful CreateAccount CPI.
+fn happy_path_accounts(setup: &TestSetup) -> (Vec, Vec<(Pubkey, Account)>) {
+ let (mut keys, mut accounts) = default_accounts();
+ let (base_key, quote_key) = find_pda_seed_pair(&setup.program_id);
+ keys[RegisterMarketAccounts::BaseMint as usize] = base_key;
+ keys[RegisterMarketAccounts::QuoteMint as usize] = quote_key;
+ let (pda, _bump) =
+ Pubkey::find_program_address(&[base_key.as_ref(), quote_key.as_ref()], &setup.program_id);
+ keys[RegisterMarketAccounts::Market as usize] = pda;
+
+ let (system_program_pubkey, system_program_account) =
+ program::keyed_account_for_system_program();
+ keys[RegisterMarketAccounts::SystemProgram as usize] = system_program_pubkey;
+ accounts[RegisterMarketAccounts::SystemProgram as usize] = system_program_account;
+
+ let (rent_sysvar_pubkey, rent_sysvar_account) =
+ setup.mollusk.sysvars.keyed_account_for_rent_sysvar();
+ keys[RegisterMarketAccounts::RentSysvar as usize] = rent_sysvar_pubkey;
+ accounts[RegisterMarketAccounts::RentSysvar as usize] = rent_sysvar_account;
+
+ // Fund the user account so it can pay for the CreateAccount CPI.
+ accounts[RegisterMarketAccounts::User as usize] =
+ Account::new(USER_LAMPORTS, 0, &system_program_pubkey);
+
+ let metas: Vec = keys
+ .iter()
+ .enumerate()
+ .map(|(i, k)| {
+ let writable = matches!(
+ i,
+ i if i == RegisterMarketAccounts::User as usize
+ || i == RegisterMarketAccounts::Market as usize
+ );
+ let signer = i == RegisterMarketAccounts::User as usize;
+ if writable {
+ AccountMeta::new(*k, signer)
+ } else {
+ AccountMeta::new_readonly(*k, signer)
+ }
+ })
+ .collect();
+ let paired = keys.into_iter().zip(accounts).collect();
+ (metas, paired)
+}
+
/// Build accounts where the market key is the correct PDA with one
/// 8-byte chunk flipped, so the comparison fails at exactly that chunk.
fn pda_mismatch_accounts(
@@ -402,6 +453,54 @@ impl TestCase for Case {
Some(ErrorCode::InvalidRentSysvarPubkey),
)
}
+ // Verifies: REGISTER-MARKET (CreateAccount CPI happy path)
+ Self::CreateAccountHappyPath => {
+ let (metas, accounts) = happy_path_accounts(setup);
+ let instruction = Instruction::new_with_bytes(setup.program_id, insn, metas);
+ let result = setup.mollusk.process_instruction(&instruction, &accounts);
+
+ let mut errors = Vec::new();
+ match &result.program_result {
+ MolluskResult::Success => {
+ let market =
+ &result.resulting_accounts[RegisterMarketAccounts::Market as usize].1;
+
+ if market.owner != setup.program_id {
+ errors.push(format!(
+ "owner: expected {:?}, got {:?}",
+ setup.program_id, market.owner
+ ));
+ }
+ if market.data.len() != MARKET_HEADER_SIZE {
+ errors.push(format!(
+ "data len: expected {}, got {}",
+ MARKET_HEADER_SIZE,
+ market.data.len()
+ ));
+ }
+ let rent = &setup.mollusk.sysvars.rent;
+ if !rent.is_exempt(market.lamports, market.data.len()) {
+ errors.push(format!(
+ "market not rent exempt: {} lamports for {} bytes",
+ market.lamports,
+ market.data.len()
+ ));
+ }
+ }
+ other => {
+ errors.push(format!("expected success, got {:?}", other));
+ }
+ }
+
+ CaseResult {
+ cu: result.compute_units_consumed,
+ error: if errors.is_empty() {
+ None
+ } else {
+ Some(errors.join("; "))
+ },
+ }
+ }
}
}
}