Skip to content
16 changes: 8 additions & 8 deletions docs/algorithms/ENTRYPOINT.tex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
121 changes: 83 additions & 38 deletions docs/algorithms/REGISTER-MARKET.tex
Original file line number Diff line number Diff line change
Expand Up @@ -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}
3 changes: 2 additions & 1 deletion docs/algorithms/syscalls.json
Original file line number Diff line number Diff line change
@@ -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"
}
6 changes: 5 additions & 1 deletion docs/src/development/build-scaffolding.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

<Include rs="interface::memory#constant_group_example" collapsible/>

Expand Down
3 changes: 2 additions & 1 deletion interface/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
39 changes: 30 additions & 9 deletions interface/src/market.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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),
}
}

Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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.
Expand All @@ -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,
}
Expand All @@ -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.
Expand All @@ -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
),
}
}

Expand Down
Loading
Loading