diff --git a/Cargo.lock b/Cargo.lock
index 372ca85..83ec4b3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -852,8 +852,10 @@ dependencies = [
"dropset-interface",
"heck",
"mollusk-svm",
+ "mollusk-svm-programs-token",
"solana-account 3.4.0",
"solana-sdk",
+ "spl-token-interface",
]
[[package]]
@@ -1465,9 +1467,9 @@ dependencies = [
[[package]]
name = "mollusk-svm"
-version = "0.11.0"
+version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03a3cac3aca9ffb1e97c0b60ac4fca70f1ef2cbe532010e62cfdcdf2a2a2a2ef"
+checksum = "9506b7859ad9716fff8c5fcdb23abf9801793571e6106c29dd74a10374aa7d93"
dependencies = [
"agave-feature-set",
"agave-syscalls",
@@ -1509,19 +1511,34 @@ dependencies = [
[[package]]
name = "mollusk-svm-error"
-version = "0.11.0"
+version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e315980684803972f443b2d8dd765e3def7e4e20bb67d5125bb80639e858a040"
+checksum = "ff7c52dc834fc5962f54c7ffcb61a73b9f0ae0ab63b5e84043fa387b751254c2"
dependencies = [
"solana-pubkey 4.1.0",
- "thiserror 1.0.69",
+ "thiserror 2.0.18",
+]
+
+[[package]]
+name = "mollusk-svm-programs-token"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba8a9b81a9d39a7ed52fc3c9ed5872b80659c7b9ff34e650f523780e71b13afa"
+dependencies = [
+ "mollusk-svm",
+ "solana-account 3.4.0",
+ "solana-program-pack",
+ "solana-pubkey 4.1.0",
+ "solana-rent 3.1.0",
+ "spl-associated-token-account-interface",
+ "spl-token-interface",
]
[[package]]
name = "mollusk-svm-result"
-version = "0.11.0"
+version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d5087302293e891db8627166c41a8cded1d74d1ba6c5b7b3a823bf4d7503e37"
+checksum = "92376062d0cad8a3b28f86e57b58c8949302aa6eaed518f584fdfc2f75d8face"
dependencies = [
"solana-account 3.4.0",
"solana-instruction",
@@ -1586,6 +1603,17 @@ dependencies = [
"num-traits",
]
+[[package]]
+name = "num-derive"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
[[package]]
name = "num-integer"
version = "0.1.46"
@@ -1732,10 +1760,10 @@ version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06810dac15a4ef83d3dabdb4f2f22fb39c9adff669cd2781da4f716510a647c"
dependencies = [
- "solana-account-view",
+ "solana-account-view 1.0.0",
"solana-address 2.3.0",
"solana-define-syscall 4.0.1",
- "solana-instruction-view",
+ "solana-instruction-view 1.0.0",
"solana-program-error",
]
@@ -1745,21 +1773,20 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "febf3bbe37f4e2723b9b41a1768c6542a1ae1b1d7bcac27f892f30cabcf70ec4"
dependencies = [
- "solana-account-view",
+ "solana-account-view 1.0.0",
"solana-address 2.3.0",
- "solana-instruction-view",
+ "solana-instruction-view 1.0.0",
"solana-program-error",
]
[[package]]
name = "pinocchio-token-2022"
version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbe4f1997ce2443f99333d8ae2ee1075f9c94ed13ff941178663ae3601ad99ad"
+source = "git+https://github.com/anza-xyz/pinocchio?branch=main#009301423f920fd105bd32a25560d127b6f0bf4f"
dependencies = [
- "solana-account-view",
+ "solana-account-view 2.0.0",
"solana-address 2.3.0",
- "solana-instruction-view",
+ "solana-instruction-view 2.0.0",
"solana-program-error",
]
@@ -2313,6 +2340,16 @@ dependencies = [
"solana-program-error",
]
+[[package]]
+name = "solana-account-view"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc141b940560430425ebaadb7645496c45f6a10fad9911d719bd03eab7f4d422"
+dependencies = [
+ "solana-address 2.3.0",
+ "solana-program-error",
+]
+
[[package]]
name = "solana-address"
version = "1.1.0"
@@ -2692,12 +2729,24 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60147e4d0a4620013df40bf30a86dd299203ff12fcb8b593cd51014fce0875d8"
dependencies = [
- "solana-account-view",
+ "solana-account-view 1.0.0",
"solana-address 2.3.0",
"solana-define-syscall 4.0.1",
"solana-program-error",
]
+[[package]]
+name = "solana-instruction-view"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c35d02cd27575b1fd05938d3624e476b0173208139053b51aa48dd9bec149f1"
+dependencies = [
+ "solana-account-view 2.0.0",
+ "solana-address 2.3.0",
+ "solana-define-syscall 5.0.0",
+ "solana-program-error",
+]
+
[[package]]
name = "solana-instructions-sysvar"
version = "3.0.0"
@@ -3673,6 +3722,36 @@ dependencies = [
"der",
]
+[[package]]
+name = "spl-associated-token-account-interface"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6433917b60441d68d99a17e121d9db0ea15a9a69c0e5afa34649cf5ba12612f"
+dependencies = [
+ "solana-instruction",
+ "solana-pubkey 3.0.0",
+]
+
+[[package]]
+name = "spl-token-interface"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c564ac05a7c8d8b12e988a37d82695b5ba4db376d07ea98bc4882c81f96c7f3"
+dependencies = [
+ "arrayref",
+ "bytemuck",
+ "num-derive",
+ "num-traits",
+ "num_enum",
+ "solana-instruction",
+ "solana-program-error",
+ "solana-program-option",
+ "solana-program-pack",
+ "solana-pubkey 3.0.0",
+ "solana-sdk-ids",
+ "thiserror 2.0.18",
+]
+
[[package]]
name = "strsim"
version = "0.11.1"
diff --git a/Cargo.toml b/Cargo.toml
index 44eb5c6..db3bfc5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,15 +14,18 @@ resolver = "3"
[workspace.dependencies]
bindgen = "0.72.1"
heck = "0.5"
-mollusk-svm = "0.11.0"
+mollusk-svm = "0.12.0"
+mollusk-svm-programs-token = "0.12.0"
pinocchio = "0.10.2"
pinocchio-token = "0.5.0"
-pinocchio-token-2022 = "0.2.0"
+# "0.2.0" does not export GetAccountDataSize.
+pinocchio-token-2022 = {git = "https://github.com/anza-xyz/pinocchio", branch = "main"}
proc-macro2 = "1.0.106"
quote = "1.0.45"
solana-account = "3.2"
solana-sbpf = "0.16.0"
solana-sdk = "4.0.1"
+spl-token-interface = "2.0.0"
syn = {version = "2.0.117", features = ["full"]}
ureq = "3.3.0"
diff --git a/docs/algorithms/INIT-MARKET-PDA.tex b/docs/algorithms/INIT-MARKET-PDA.tex
index 8ed6005..9ba2f89 100644
--- a/docs/algorithms/INIT-MARKET-PDA.tex
+++ b/docs/algorithms/INIT-MARKET-PDA.tex
@@ -11,15 +11,20 @@
\INPUT $r_{10}$ = frame
\COMMENT{Pointer to shifted input buffer for quote mint offsets.}
\REQUIRE frame.input\_shifted
- \COMMENT{Pointer to System Program ID in input buffer.}
- \REQUIRE frame.sol\_instruction.program\_id
+ \REQUIRE *frame.system\_program\_id = input.system\_program.address
+ \REQUIRE frame.cpi[0].info.data\_len = \texttt{data.LEN\_ZERO}
+ \REQUIRE frame.cpi[1].info.data\_len = \texttt{data.LEN\_ZERO}
\ENSURE $r_9$ = acct
\ENSURE frame.input = input
+ \ENSURE frame.lamports\_per\_byte = acct.data.lamports\_per\_byte
+ \ENSURE frame.signers\_seeds.addr = \&frame.pda\_seeds
+ \ENSURE frame.signers\_seeds.len = \texttt{RegisterMarketFrame.PDA\_SEEDS\_N\_SEEDS}
\PROCEDURE{INIT-MARKET-PDA}{input, insn, acct, frame}
\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.lamports\_per\_byte = lamports\_per\_byte
\STATE frame.create\_account\_data.lamports = acct\_size $\times$ lamports\_per\_byte
\COMMENT{Initialize market PDA signer seeds.}
\STATE frame.pda\_seeds[0].addr = input.base\_mint.address
@@ -68,6 +73,7 @@
\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.program\_id = frame.system\_program\_id
\STATE frame.sol\_instruction.accounts = \&frame.cpi.account\_metas
\STATE frame.sol\_instruction.account\_len =
\texttt{register\_misc.CREATE\_ACCOUNT\_N\_ACCOUNTS}
diff --git a/docs/algorithms/INIT-VAULT.tex b/docs/algorithms/INIT-VAULT.tex
index a1f3844..affa6cd 100644
--- a/docs/algorithms/INIT-VAULT.tex
+++ b/docs/algorithms/INIT-VAULT.tex
@@ -13,11 +13,23 @@
\texttt{true},
\texttt{false}
\}
- \COMMENT{Pointer to owning token program address.}
+ \REQUIRE *frame.mint $\in$ \{
+ input.base\_mint,
+ input\_shifted.quote\_mint
+ \}
\REQUIRE frame.token\_program\_id
- \COMMENT{Pointer to input buffer.}
+ \REQUIRE frame.program\_id
\REQUIRE frame.input
+ \REQUIRE frame.lamports\_per\_byte
\REQUIRE frame.pda\_seeds[0].len = \texttt{Address.size}
+ \REQUIRE frame.pda\_seeds[2].addr = \&frame.bump
+ \REQUIRE frame.pda\_seeds[2].len = \texttt{u8.size}
+ \REQUIRE *frame.system\_program\_id = input.system\_program.address
+ \REQUIRE frame.signers\_seeds.addr = \&frame.pda\_seeds
+ \REQUIRE frame.signers\_seeds.len = \texttt{RegisterMarketFrame.PDA\_SEEDS\_N\_SEEDS}
+ \REQUIRE input.user.data\_len = \texttt{data.LEN\_ZERO}
+ \REQUIRE acct.data\_len = \texttt{data.LEN\_ZERO}
+ \REQUIRE frame.cpi[0].info.executable = \texttt{false}
\FUNCTION{INIT-VAULT}{acct, frame}
\STATE \CALL{Store}{frame}
\STATE \CALL{Store}{acct}
@@ -27,7 +39,7 @@
\STATE frame.pda\_seeds[1].len = \texttt{u8.size}
\STATE syscall.seeds = \&frame.pda\_seeds
\STATE syscall.seeds\_len = \texttt{register\_misc.TRY\_FIND\_VAULT\_PDA\_SEEDS\_LEN}
- \STATE syscall.program\_id = frame.token\_program\_id
+ \STATE syscall.program\_id = frame.program\_id
\STATE syscall.program\_address = \&frame.pda
\STATE syscall.bump\_seed = \&frame.bump
\STATE \CALL{sol-try-find-program-address}{}
@@ -39,6 +51,89 @@
\RETURN \texttt{ErrorCode::InvalidQuoteVaultPubkey}
\ENDIF
\ENDIF
+ \COMMENT{Determine token account size.}
+ \IF{frame.token\_program\_is\_2022}
+ \COMMENT{Set up mint as CPI account.}
+ \STATE frame.cpi[0].info.is\_signer = \texttt{false}
+ \STATE frame.cpi[0].info.is\_writable = \texttt{false}
+ \STATE frame.cpi[0].meta.is\_signer = \texttt{false}
+ \STATE frame.cpi[0].meta.is\_writable = \texttt{false}
+ \STATE frame.cpi[0].meta.pubkey = \&mint.address
+ \STATE frame.cpi[0].info.key = \&mint.address
+ \STATE frame.cpi[0].info.owner = \&mint.owner
+ \STATE frame.cpi[0].info.lamports = \&mint.lamports
+ \STATE frame.cpi[0].info.data\_len = mint.data\_len
+ \STATE frame.cpi[0].info.data = \&mint.data
+ \COMMENT{Populate SolInstruction for GetAccountDataSize CPI.}
+ \STATE frame.sol\_instruction.program\_id = frame.token\_program\_id
+ \STATE frame.sol\_instruction.accounts = \&frame.cpi[0].meta
+ \STATE frame.sol\_instruction.account\_len =
+ \texttt{token.GET\_ACCOUNT\_DATA\_SIZE\_N\_ACCOUNTS}
+ \STATE frame.get\_account\_data\_size\_data =
+ \texttt{token.GET\_ACCOUNT\_DATA\_SIZE\_DISC}
+ \STATE frame.sol\_instruction.data = \&frame.get\_account\_data\_size\_data
+ \STATE frame.sol\_instruction.data\_len = \texttt{u8.size}
+ \COMMENT{Invoke GetAccountDataSize CPI (no signers).}
+ \STATE syscall.instruction = \&frame.sol\_instruction
+ \STATE syscall.account\_infos = \&frame.cpi[0].info
+ \STATE syscall.account\_infos\_len =
+ \texttt{token.GET\_ACCOUNT\_DATA\_SIZE\_N\_ACCOUNTS}
+ \STATE syscall.seeds\_len = \texttt{token.GET\_ACCOUNT\_DATA\_SIZE\_N\_SEEDS}
+ \STATE \CALL{sol-invoke-signed-c}{}
+ \COMMENT{Get return data.}
+ \STATE syscall.bytes = \&frame.token\_account\_data\_size
+ \STATE syscall.bytes\_len = \texttt{u64.size}
+ \STATE syscall.program\_id = \&frame.get\_return\_data\_program\_id
+ \STATE \CALL{sol-get-return-data}{}
+ \STATE acct\_size = frame.token\_account\_data\_size
+ \COMMENT{Override default return data nonzero return code.}
+ \STATE result = \texttt{entrypoint.RETURN\_SUCCESS}
+ \ELSE
+ \STATE acct\_size = \texttt{token.ACCOUNT\_SIZE}
+ \ENDIF
+ \COMMENT{Prepare CreateAccount instruction fields.}
+ \STATE frame.create\_account\_data.space = acct\_size
+ \STATE acct\_size = acct\_size + \texttt{account.STORAGE\_OVERHEAD}
+ \STATE frame.create\_account\_data.lamports =
+ acct\_size $\times$ frame.lamports\_per\_byte
+ \STATE frame.create\_account\_data.owner = frame.token\_program\_id
+ \COMMENT{Assign CPI account fields via immediates.}
+ \STATE frame.cpi[0].info.is\_signer = \texttt{true}
+ \STATE frame.cpi[0].info.is\_writable = \texttt{true}
+ \STATE frame.cpi[0].meta.is\_signer = \texttt{true}
+ \STATE frame.cpi[0].meta.is\_writable = \texttt{true}
+ \STATE frame.cpi[1].info.is\_signer = \texttt{true}
+ \STATE frame.cpi[1].info.is\_writable = \texttt{true}
+ \STATE frame.cpi[1].meta.is\_signer = \texttt{true}
+ \STATE frame.cpi[1].meta.is\_writable = \texttt{true}
+ \STATE frame.cpi[0].info.data\_len = \texttt{data.LEN\_ZERO}
+ \STATE frame.cpi[1].info.data\_len = \texttt{data.LEN\_ZERO}
+ \COMMENT{Assign CPI account fields via pointers.}
+ \STATE frame.cpi[0].meta.pubkey = \&input.user.address
+ \STATE frame.cpi[0].info.key = \&input.user.address
+ \STATE frame.cpi[0].info.owner = \&input.user.owner
+ \STATE frame.cpi[0].info.lamports = \&input.user.lamports
+ \STATE frame.cpi[0].info.data = \&input.user.data
+ \STATE frame.cpi[1].meta.pubkey = \&acct.address
+ \STATE frame.cpi[1].info.key = \&acct.address
+ \STATE frame.cpi[1].info.owner = \&acct.owner
+ \STATE frame.cpi[1].info.lamports = \&acct.lamports
+ \STATE frame.cpi[1].info.data = \&acct.data
+ \COMMENT{Populate SolInstruction for CreateAccount CPI.}
+ \STATE frame.sol\_instruction.program\_id = frame.system\_program\_id
+ \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}{}
\ENDFUNCTION
\end{algorithmic}
\end{algorithm}
diff --git a/docs/algorithms/REGISTER-MARKET.tex b/docs/algorithms/REGISTER-MARKET.tex
index e87f4c9..5ca59cf 100644
--- a/docs/algorithms/REGISTER-MARKET.tex
+++ b/docs/algorithms/REGISTER-MARKET.tex
@@ -20,6 +20,7 @@
\IF{insn\_len $\neq$ \texttt{RegisterMarketData.LEN}}
\RETURN \texttt{ErrorCode::InvalidInstructionLength}
\ENDIF
+ \STATE frame.program\_id = \&insn.program\_id
\COMMENT{Check user, market accounts.}
\IF{input.user.data\_len $\neq$ 0}
\RETURN \texttt{ErrorCode::UserHasData}
@@ -52,8 +53,7 @@
\IF{acct.address $\neq$ frame.system\_program\_pubkey}
\RETURN \texttt{ErrorCode::InvalidSystemProgramPubkey}
\ENDIF
- \COMMENT{Populate CPI program ID field.}
- \STATE frame.sol\_instruction.program\_id = \&acct.address
+ \STATE frame.system\_program\_id = \&acct.address
\COMMENT{Advance to Rent sysvar account.}
\STATE system\_program\_padded\_data\_len = acct.padded\_data\_len
\STATE acct += system\_program\_padded\_data\_len + \texttt{EmptyAccount.size}
@@ -90,15 +90,22 @@
\COMMENT{Advance to base vault account.}
\STATE base\_token\_program\_padded\_data\_len = acct.padded\_data\_len
\STATE acct += base\_token\_program\_padded\_data\_len + \texttt{EmptyAccount.size}
+ \COMMENT{Check base vault account.}
+ \IF{acct.duplicate $\neq$ \texttt{account.NON\_DUP\_MARKER}}
+ \RETURN \texttt{ErrorCode::BaseVaultIsDuplicate}
+ \ENDIF
+ \IF{acct.data\_len $\neq$ \texttt{data.LEN\_ZERO}}
+ \RETURN \texttt{ErrorCode::BaseVaultHasData}
+ \ENDIF
\COMMENT{Initialize base vault account.}
\STATE frame.vault\_index = \texttt{register\_misc.VAULT\_INDEX\_BASE}
+ \STATE frame.mint = \&input.base\_mint
\STATE result = \CALL{INIT-VAULT}{acct, frame}
\IF{result $\neq$ \texttt{entrypoint.RETURN\_SUCCESS}}
\RETURN result
\ENDIF
\COMMENT{Advance to quote token program account.}
- \STATE base\_vault\_padded\_data\_len = acct.padded\_data\_len
- \STATE acct += base\_vault\_padded\_data\_len + \texttt{EmptyAccount.size}
+ \STATE acct += \texttt{EmptyAccount.size}
\COMMENT{Check quote token program account.}
\IF{acct.duplicate == \texttt{account.NON\_DUP\_MARKER}}
\COMMENT{Non-duplicate; verify quote token program owns the quote mint.}
@@ -130,8 +137,16 @@
\COMMENT{Advance to quote vault account.}
\STATE acct += \texttt{u64.size}
\ENDIF
+ \COMMENT{Check quote vault account.}
+ \IF{acct.duplicate $\neq$ \texttt{account.NON\_DUP\_MARKER}}
+ \RETURN \texttt{ErrorCode::QuoteVaultIsDuplicate}
+ \ENDIF
+ \IF{acct.data\_len $\neq$ \texttt{data.LEN\_ZERO}}
+ \RETURN \texttt{ErrorCode::QuoteVaultHasData}
+ \ENDIF
\COMMENT{Initialize quote vault account.}
\STATE frame.vault\_index = \texttt{register\_misc.VAULT\_INDEX\_QUOTE}
+ \STATE frame.mint = \&input\_shifted.quote\_mint
\STATE \CALL{INIT-VAULT}{acct, frame}
\ENDPROCEDURE
\end{algorithmic}
diff --git a/docs/algorithms/syscalls.json b/docs/algorithms/syscalls.json
index 9d8a303..1accb1d 100644
--- a/docs/algorithms/syscalls.json
+++ b/docs/algorithms/syscalls.json
@@ -1,4 +1,5 @@
{
+ "sol_get_return_data": "https://github.com/anza-xyz/agave/blob/v4.0.0-beta.5/platform-tools-sdk/sbf/c/inc/sol/inc/return_data.inc#L27-L35",
"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/program/layout.md b/docs/src/program/layout.md
index 913000d..1373884 100644
--- a/docs/src/program/layout.md
+++ b/docs/src/program/layout.md
@@ -14,9 +14,11 @@ program/src/dropset/
│ ├── discriminant.s # Instruction discriminants
│ ├── error.s # Error codes and subroutines
│ ├── memory.s # Memory layout constants
-│ └── pubkey.s # Pubkey chunk offsets and known addresses
+│ ├── pubkey.s # Pubkey chunk offsets and known addresses
+│ └── token.s # SPL Token constants
└── market/
- ├── init_vault.s # InitVault function
+ ├── init_market_pda.s # Market PDA initialization
+ ├── init_vault.s # Vault initialization
└── register.s # RegisterMarket handler
```
@@ -91,6 +93,13 @@ emits the same set of constants with a `_UOFF` suffix instead of `_OFF`.
+### Token
+
+SPL Token constants (account size, instruction discriminators) are injected
+from the [`token`][token-mod] module via [`constant_group!`][bs-constant-group]:
+
+
+
[input buffer]: ../program/inputs#input-buffer
[`program/src/dropset/`]: https://github.com/DASMAC-com/dropset-beta/tree/main/program/src/dropset
[multi-file assembly]: https://github.com/blueshift-gg/sbpf/pull/109
@@ -98,5 +107,6 @@ emits the same set of constants with a `_UOFF` suffix instead of `_OFF`.
[bs-discriminant]: ../development/build-scaffolding#discriminant-enum-target
[bs-error]: ../development/build-scaffolding#error-enum-target
[pubkey-mod]: https://github.com/DASMAC-com/dropset-beta/blob/main/interface/src/pubkey.rs
+[token-mod]: https://github.com/DASMAC-com/dropset-beta/blob/main/interface/src/token.rs
[bs-constant-group]: ../development/build-scaffolding#constant_group
[build scaffolding]: ../development/build-scaffolding
diff --git a/interface/src/lib.rs b/interface/src/lib.rs
index a135690..b1fcab7 100644
--- a/interface/src/lib.rs
+++ b/interface/src/lib.rs
@@ -6,6 +6,7 @@ pub mod memory;
pub mod order;
pub mod pubkey;
pub mod seat;
+pub mod token;
// region: discriminant_enum
#[discriminant_enum("common/discriminant")]
@@ -62,6 +63,14 @@ pub enum ErrorCode {
InvalidBaseVaultPubkey,
/// The quote vault account pubkey is invalid.
InvalidQuoteVaultPubkey,
+ /// The base vault account is a duplicate.
+ BaseVaultIsDuplicate,
+ /// The base vault account already has data.
+ BaseVaultHasData,
+ /// The quote vault account is a duplicate.
+ QuoteVaultIsDuplicate,
+ /// The quote vault account already has data.
+ QuoteVaultHasData,
}
// endregion: error_enum
@@ -93,4 +102,5 @@ pub const INJECTION_GROUPS: &[&dropset_build::ConstantGroup] = &[
&memory::input_buffer::GROUP,
&memory::size_of::GROUP,
&pubkey::pubkey::GROUP,
+ &token::token::GROUP,
];
diff --git a/interface/src/market.rs b/interface/src/market.rs
index ebd8617..2132828 100644
--- a/interface/src/market.rs
+++ b/interface/src/market.rs
@@ -10,6 +10,7 @@ use dropset_macros::{
svm_data,
};
use pinocchio::Address;
+use pinocchio::account::RuntimeAccount;
// region: market_header
#[svm_data]
@@ -147,20 +148,34 @@ signer_seeds! {
#[frame]
/// Stack frame for REGISTER-MARKET.
pub struct RegisterMarketFrame {
- /// Pointer to owning token program address.
- pub token_program_id: u64,
+ /// Pointer to token program address.
+ pub token_program_id: *const Address,
+ /// Pointer to program ID in input buffer.
+ pub program_id: *const Address,
/// Saved input buffer pointer.
pub input: u64,
/// Saved input_shifted pointer.
pub input_shifted: u64,
+ /// From Rent sysvar.
+ pub lamports_per_byte: u64,
+ /// Return value from GetAccountDataSize CPI, to check token account data size at runtime.
+ pub token_account_data_size: u64,
+ /// Pointer to mint account for vault initialization.
+ pub mint: *const RuntimeAccount,
/// Signer seeds for PDA derivation and CPI signing.
pub pda_seeds: PDASignerSeeds,
/// From `sol_try_find_program_address`.
pub pda: Address,
/// System Program pubkey, zero-initialized on stack
pub system_program_pubkey: Address,
+ /// Pointer to System Program ID in input buffer.
+ pub system_program_id: *const Address,
+ /// Get return data program ID for CPI calls, zero-initialized on stack.
+ pub get_return_data_program_id: Address,
/// CPI instruction data for CreateAccount.
pub create_account_data: CreateAccountData,
+ /// GetAccountDataSize CPI instruction data.
+ pub get_account_data_size_data: u8,
/// CPI accounts for CreateAccount and InitializeAccount2.
pub cpi_accounts: CPIAccounts,
/// Signers seeds for CPI.
@@ -169,9 +184,9 @@ pub struct RegisterMarketFrame {
pub sol_instruction: SolInstruction,
/// From `sol_try_find_program_address`.
pub bump: u8,
- /// Vault index for PDA derivation.
+ /// Vault index for vault PDA derivation.
pub vault_index: u8,
- /// Whether the current token program is Token 2022.
+ /// Whether the current token program is Token 2022 (zero-initialized on stack).
pub token_program_is_2022: u8,
}
// endregion: frame_example
@@ -181,18 +196,30 @@ constant_group! {
#[inject("market/register")]
#[frame(RegisterMarketFrame)]
frame {
- /// Pointer to owning token program address.
+ /// Pointer to token program address.
TOKEN_PROGRAM_ID = offset!(token_program_id),
+ /// Pointer to program ID in input buffer.
+ PROGRAM_ID = offset!(program_id),
/// Saved input buffer pointer.
INPUT = offset!(input),
/// Saved input_shifted pointer.
INPUT_SHIFTED = offset!(input_shifted),
+ /// From Rent sysvar.
+ LAMPORTS_PER_BYTE = offset!(lamports_per_byte),
+ /// Return value from GetAccountDataSize CPI, to check token account data size at runtime.
+ TOKEN_ACCOUNT_DATA_SIZE = offset!(token_account_data_size),
+ /// Pointer to mint account for vault initialization.
+ MINT = offset!(mint),
/// 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),
+ /// System Program ID in input buffer.
+ SYSTEM_PROGRAM_ID = offset!(system_program_id),
+ /// Get return data program ID for CPI calls.
+ GET_RETURN_DATA_PROGRAM_ID = offset!(get_return_data_program_id),
/// CreateAccount instruction data.
CREATE_ACCT_DATA = offset!(create_account_data),
/// Lamports field within CreateAccount instruction data.
@@ -201,6 +228,8 @@ constant_group! {
CREATE_ACCT_SPACE = unaligned_offset!(create_account_data.space),
/// Owner field within CreateAccount instruction data.
CREATE_ACCT_OWNER = unaligned_pubkey_offsets!(create_account_data.owner),
+ /// GetAccountDataSize CPI instruction data.
+ GET_ACCOUNT_DATA_SIZE_DATA = unaligned_offset!(get_account_data_size_data),
/// CPI accounts.
CPI = cpi_accounts!(cpi_accounts),
/// Signers seeds address.
diff --git a/interface/src/memory.rs b/interface/src/memory.rs
index 9c8fb7e..302b95e 100644
--- a/interface/src/memory.rs
+++ b/interface/src/memory.rs
@@ -81,6 +81,8 @@ constant_group! {
cpi {
/// Mask for writable signer (is_writable | is_signer).
WRITABLE_SIGNER = immediate!(0x0101),
+ /// Mask for readonly non-signer.
+ READONLY_NON_SIGNER = immediate!(0x0000),
}
}
diff --git a/interface/src/token.rs b/interface/src/token.rs
new file mode 100644
index 0000000..2690c56
--- /dev/null
+++ b/interface/src/token.rs
@@ -0,0 +1,20 @@
+use dropset_macros::constant_group;
+use pinocchio_token::state::TokenAccount;
+// pinocchio-token does not export GetAccountDataSize yet.
+use pinocchio_token_2022::instructions::GetAccountDataSize;
+
+constant_group! {
+ #[prefix("TOKEN")]
+ #[inject("common/token")]
+ /// SPL Token constants.
+ token {
+ /// Size of a token account (SPL Token and Token 2022 base).
+ ACCOUNT_SIZE = immediate!(TokenAccount::LEN),
+ /// GetAccountDataSize instruction discriminator (Token 2022).
+ GET_ACCOUNT_DATA_SIZE_DISC = immediate!(GetAccountDataSize::DISCRIMINATOR),
+ /// GetAccountDataSize number of accounts.
+ GET_ACCOUNT_DATA_SIZE_N_ACCOUNTS = immediate!(1),
+ /// GetAccountDataSize number of seeds.
+ GET_ACCOUNT_DATA_SIZE_N_SEEDS = immediate!(0),
+ }
+}
diff --git a/program/src/dropset/common/error.s b/program/src/dropset/common/error.s
index 4e78e23..d3ef38a 100644
--- a/program/src/dropset/common/error.s
+++ b/program/src/dropset/common/error.s
@@ -36,6 +36,11 @@
.equ E_INVALID_BASE_VAULT_PUBKEY, 21
# The quote vault account pubkey is invalid.
.equ E_INVALID_QUOTE_VAULT_PUBKEY, 22
+.equ E_BASE_VAULT_IS_DUPLICATE, 23 # The base vault account is a duplicate.
+.equ E_BASE_VAULT_HAS_DATA, 24 # The base vault account already has data.
+# The quote vault account is a duplicate.
+.equ E_QUOTE_VAULT_IS_DUPLICATE, 25
+.equ E_QUOTE_VAULT_HAS_DATA, 26 # The quote vault account already has data.
e_invalid_instruction_length:
mov32 r0, E_INVALID_INSTRUCTION_LENGTH
@@ -116,3 +121,19 @@ e_quote_token_program_not_token_program:
e_invalid_base_vault_pubkey:
mov32 r0, E_INVALID_BASE_VAULT_PUBKEY
exit
+
+e_base_vault_is_duplicate:
+ mov32 r0, E_BASE_VAULT_IS_DUPLICATE
+ exit
+
+e_base_vault_has_data:
+ mov32 r0, E_BASE_VAULT_HAS_DATA
+ exit
+
+e_quote_vault_is_duplicate:
+ mov32 r0, E_QUOTE_VAULT_IS_DUPLICATE
+ exit
+
+e_quote_vault_has_data:
+ mov32 r0, E_QUOTE_VAULT_HAS_DATA
+ exit
diff --git a/program/src/dropset/common/memory.s b/program/src/dropset/common/memory.s
index 394483e..e379a84 100644
--- a/program/src/dropset/common/memory.s
+++ b/program/src/dropset/common/memory.s
@@ -26,6 +26,7 @@
# -------------------------------------------------------------------------
# Mask for writable signer (is_writable | is_signer).
.equ CPI_WRITABLE_SIGNER, 257
+.equ CPI_READONLY_NON_SIGNER, 0 # Mask for readonly non-signer.
# -------------------------------------------------------------------------
# Common data-related constants.
diff --git a/program/src/dropset/common/token.s b/program/src/dropset/common/token.s
new file mode 100644
index 0000000..c91133a
--- /dev/null
+++ b/program/src/dropset/common/token.s
@@ -0,0 +1,11 @@
+# SPL Token constants.
+# -------------------------------------------------------------------------
+# Size of a token account (SPL Token and Token 2022 base).
+.equ TOKEN_ACCOUNT_SIZE, 165
+# GetAccountDataSize instruction discriminator (Token 2022).
+.equ TOKEN_GET_ACCOUNT_DATA_SIZE_DISC, 21
+# GetAccountDataSize number of accounts.
+.equ TOKEN_GET_ACCOUNT_DATA_SIZE_N_ACCOUNTS, 1
+# GetAccountDataSize number of seeds.
+.equ TOKEN_GET_ACCOUNT_DATA_SIZE_N_SEEDS, 0
+# -------------------------------------------------------------------------
diff --git a/program/src/dropset/dropset.s b/program/src/dropset/dropset.s
index 424aae9..dcfb79e 100644
--- a/program/src/dropset/dropset.s
+++ b/program/src/dropset/dropset.s
@@ -5,6 +5,7 @@
.include "common/error.s"
.include "common/memory.s"
.include "common/pubkey.s"
+.include "common/token.s"
.include "entrypoint.s"
.include "market/register.s"
.include "market/init_market_pda.s"
diff --git a/program/src/dropset/market/init_market_pda.s b/program/src/dropset/market/init_market_pda.s
index cbe1a43..f503d84 100644
--- a/program/src/dropset/market/init_market_pda.s
+++ b/program/src/dropset/market/init_market_pda.s
@@ -6,6 +6,8 @@ init_market_pda:
add64 r7, ACCT_STORAGE_OVERHEAD
# lamports_per_byte = acct.data.lamports_per_byte
ldxdw r8, [r9 + ACCT_DATA_OFF]
+ # frame.lamports_per_byte = lamports_per_byte
+ stxdw [r10 + RM_FM_LAMPORTS_PER_BYTE_OFF], r8
# frame.create_account_data.lamports = acct_size * lamports_per_byte
mul64 r7, r8
stxdw [r10 + RM_FM_CREATE_ACCT_LAMPORTS_UOFF], r7
@@ -115,6 +117,9 @@ init_market_pda:
# 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.program_id = frame.system_program_id
+ ldxdw r7, [r10 + RM_FM_SYSTEM_PROGRAM_ID_OFF]
+ stxdw [r10 + RM_FM_SOL_INSN_PROGRAM_ID_UOFF], r7
# frame.sol_instruction.data = &frame.create_account_data
mov64 r7, r10
add64 r7, RM_FM_CREATE_ACCT_DATA_OFF
diff --git a/program/src/dropset/market/init_vault.s b/program/src/dropset/market/init_vault.s
index d868784..0403bde 100644
--- a/program/src/dropset/market/init_vault.s
+++ b/program/src/dropset/market/init_vault.s
@@ -19,8 +19,8 @@ init_vault:
add64 r1, RM_FM_PDA_SEEDS_OFF
# syscall.seeds_len = register_misc.TRY_FIND_VAULT_PDA_SEEDS_LEN
mov64 r2, RM_MISC_TRY_FIND_VAULT_PDA_SEEDS_LEN
- # syscall.program_id = frame.token_program_id
- ldxdw r3, [r6 + RM_FM_TOKEN_PROGRAM_ID_OFF]
+ # syscall.program_id = frame.program_id
+ ldxdw r3, [r6 + RM_FM_PROGRAM_ID_OFF]
# syscall.program_address = &frame.pda
mov64 r4, r6
add64 r4, RM_FM_PDA_OFF
@@ -41,6 +41,178 @@ init_vault:
ldxdw r1, [r7 + ACCT_ADDRESS_CHUNK_3_OFF]
ldxdw r2, [r6 + RM_FM_PDA_CHUNK_3_OFF]
jne r1, r2, init_vault_invalid_pda
+ # if !frame.token_program_is_2022
+ # acct_size = token.ACCOUNT_SIZE
+ ldxb r1, [r6 + RM_FM_TOKEN_PROGRAM_IS_2022_UOFF]
+ jne r1, DATA_BOOL_FALSE, init_vault_get_account_data_size
+ mov64 r1, TOKEN_ACCOUNT_SIZE
+ stxdw [r6 + RM_FM_TOKEN_ACCOUNT_DATA_SIZE_OFF], r1
+ ja init_vault_create_account
+init_vault_get_account_data_size:
+ # frame.cpi[0].info.is_signer = false
+ # frame.cpi[0].info.is_writable = false
+ sth [r6 + RM_FM_CPI_IDX_0_ACCT_INFO_IS_SIGNER_UOFF], CPI_READONLY_NON_SIGNER
+ # frame.cpi[0].meta.is_writable = false
+ # frame.cpi[0].meta.is_signer = false
+ sth [r6 + RM_FM_CPI_IDX_0_ACCT_META_IS_WRITABLE_UOFF], CPI_READONLY_NON_SIGNER
+ # mint = frame.mint
+ ldxdw r8, [r6 + RM_FM_MINT_OFF]
+ # frame.cpi[0].meta.pubkey = &mint.address
+ mov64 r9, r8
+ add64 r9, ACCT_ADDRESS_OFF
+ stxdw [r6 + RM_FM_CPI_IDX_0_ACCT_META_PUBKEY_UOFF], r9
+ # frame.cpi[0].info.key = &mint.address
+ stxdw [r6 + RM_FM_CPI_IDX_0_ACCT_INFO_KEY_UOFF], r9
+ # frame.cpi[0].info.owner = &mint.owner
+ add64 r9, IB_ADDRESS_TO_OWNER_REL_OFF_IMM
+ stxdw [r6 + RM_FM_CPI_IDX_0_ACCT_INFO_OWNER_UOFF], r9
+ # frame.cpi[0].info.lamports = &mint.lamports
+ add64 r9, IB_OWNER_TO_LAMPORTS_REL_OFF_IMM
+ stxdw [r6 + RM_FM_CPI_IDX_0_ACCT_INFO_LAMPORTS_UOFF], r9
+ # frame.cpi[0].info.data_len = mint.data_len
+ ldxdw r9, [r8 + ACCT_DATA_LEN_OFF]
+ stxdw [r6 + RM_FM_CPI_IDX_0_ACCT_INFO_DATA_LEN_UOFF], r9
+ # frame.cpi[0].info.data = &mint.data
+ mov64 r9, r8
+ add64 r9, ACCT_DATA_OFF
+ stxdw [r6 + RM_FM_CPI_IDX_0_ACCT_INFO_DATA_UOFF], r9
+ # frame.sol_instruction.program_id = frame.token_program_id
+ ldxdw r9, [r6 + RM_FM_TOKEN_PROGRAM_ID_OFF]
+ stxdw [r6 + RM_FM_SOL_INSN_PROGRAM_ID_UOFF], r9
+ # frame.sol_instruction.accounts = &frame.cpi[0].meta
+ mov64 r9, r6
+ add64 r9, RM_FM_CPI_IDX_0_ACCT_META_PUBKEY_UOFF
+ stxdw [r6 + RM_FM_SOL_INSN_ACCOUNTS_UOFF], r9
+ # frame.sol_instruction.account_len = token.GET_ACCOUNT_DATA_SIZE_N_ACCOUNTS
+ mov64 r9, TOKEN_GET_ACCOUNT_DATA_SIZE_N_ACCOUNTS
+ stxdw [r6 + RM_FM_SOL_INSN_ACCOUNT_LEN_UOFF], r9
+ # frame.get_account_data_size_data = token.GET_ACCOUNT_DATA_SIZE_DISC
+ stb [r6 + RM_FM_GET_ACCOUNT_DATA_SIZE_DATA_UOFF], TOKEN_GET_ACCOUNT_DATA_SIZE_DISC
+ # frame.sol_instruction.data = &frame.get_account_data_size_data
+ mov64 r9, r6
+ add64 r9, RM_FM_GET_ACCOUNT_DATA_SIZE_DATA_UOFF
+ stxdw [r6 + RM_FM_SOL_INSN_DATA_UOFF], r9
+ # frame.sol_instruction.data_len = u8.size
+ mov64 r9, SIZE_OF_U8
+ stxdw [r6 + RM_FM_SOL_INSN_DATA_LEN_UOFF], r9
+ # syscall.instruction = &frame.sol_instruction
+ mov64 r1, r6
+ add64 r1, RM_FM_SOL_INSN_OFF
+ # syscall.account_infos = &frame.cpi[0].info
+ mov64 r2, r6
+ add64 r2, RM_FM_CPI_SOL_ACCT_INFO_OFF
+ # syscall.account_infos_len = token.GET_ACCOUNT_DATA_SIZE_N_ACCOUNTS
+ mov64 r3, TOKEN_GET_ACCOUNT_DATA_SIZE_N_ACCOUNTS
+ # syscall.seeds_len = token.GET_ACCOUNT_DATA_SIZE_N_SEEDS
+ mov64 r5, TOKEN_GET_ACCOUNT_DATA_SIZE_N_SEEDS
+ call sol_invoke_signed_c
+ # syscall.bytes = &frame.token_account_data_size
+ mov64 r1, r6
+ add64 r1, RM_FM_TOKEN_ACCOUNT_DATA_SIZE_OFF
+ # syscall.bytes_len = u64.size
+ mov64 r2, SIZE_OF_U64
+ # syscall.program_id = &frame.get_return_data_program_id
+ mov64 r3, r6
+ add64 r3, RM_FM_GET_RETURN_DATA_PROGRAM_ID_OFF
+ call sol_get_return_data
+init_vault_create_account:
+ # acct_size = frame.token_account_data_size
+ ldxdw r8, [r6 + RM_FM_TOKEN_ACCOUNT_DATA_SIZE_OFF]
+ # frame.create_account_data.space = acct_size
+ stxdw [r6 + RM_FM_CREATE_ACCT_SPACE_UOFF], r8
+ # acct_size = acct_size + account.STORAGE_OVERHEAD
+ add64 r8, ACCT_STORAGE_OVERHEAD
+ # frame.create_account_data.lamports = acct_size * frame.lamports_per_byte
+ ldxdw r9, [r6 + RM_FM_LAMPORTS_PER_BYTE_OFF]
+ mul64 r8, r9
+ stxdw [r6 + RM_FM_CREATE_ACCT_LAMPORTS_UOFF], r8
+ # frame.create_account_data.owner = frame.token_program_id
+ ldxdw r8, [r6 + RM_FM_TOKEN_PROGRAM_ID_OFF]
+ ldxdw r9, [r8 + PUBKEY_CHUNK_0_OFF]
+ stxdw [r6 + RM_FM_CREATE_ACCT_OWNER_CHUNK_0_UOFF], r9
+ ldxdw r9, [r8 + PUBKEY_CHUNK_1_OFF]
+ stxdw [r6 + RM_FM_CREATE_ACCT_OWNER_CHUNK_1_UOFF], r9
+ ldxdw r9, [r8 + PUBKEY_CHUNK_2_OFF]
+ stxdw [r6 + RM_FM_CREATE_ACCT_OWNER_CHUNK_2_UOFF], r9
+ ldxdw r9, [r8 + PUBKEY_CHUNK_3_OFF]
+ stxdw [r6 + RM_FM_CREATE_ACCT_OWNER_CHUNK_3_UOFF], r9
+ # frame.cpi[0].info.is_signer = true
+ # frame.cpi[0].info.is_writable = true
+ sth [r6 + RM_FM_CPI_IDX_0_ACCT_INFO_IS_SIGNER_UOFF], CPI_WRITABLE_SIGNER
+ # frame.cpi[0].meta.is_writable = true
+ # frame.cpi[0].meta.is_signer = true
+ sth [r6 + RM_FM_CPI_IDX_0_ACCT_META_IS_WRITABLE_UOFF], CPI_WRITABLE_SIGNER
+ # frame.cpi[1].info.is_signer = true
+ # frame.cpi[1].info.is_writable = true
+ sth [r6 + RM_FM_CPI_IDX_1_ACCT_INFO_IS_SIGNER_UOFF], CPI_WRITABLE_SIGNER
+ # frame.cpi[1].meta.is_writable = true
+ # frame.cpi[1].meta.is_signer = true
+ sth [r6 + RM_FM_CPI_IDX_1_ACCT_META_IS_WRITABLE_UOFF], CPI_WRITABLE_SIGNER
+ # frame.cpi[0].info.data_len = data.LEN_ZERO
+ mov64 r9, DATA_LEN_ZERO
+ stxdw [r6 + RM_FM_CPI_IDX_0_ACCT_INFO_DATA_LEN_UOFF], r9
+ # frame.cpi[1].info.data_len = data.LEN_ZERO
+ stxdw [r6 + RM_FM_CPI_IDX_1_ACCT_INFO_DATA_LEN_UOFF], r9
+ # frame.cpi[0].meta.pubkey = &input.user.address
+ # frame.cpi[0].info.key = &input.user.address
+ ldxdw r8, [r6 + RM_FM_INPUT_OFF]
+ add64 r8, IB_USER_PUBKEY_OFF
+ stxdw [r6 + RM_FM_CPI_IDX_0_ACCT_META_PUBKEY_UOFF], r8
+ stxdw [r6 + RM_FM_CPI_IDX_0_ACCT_INFO_KEY_UOFF], r8
+ # frame.cpi[0].info.owner = &input.user.owner
+ add64 r8, IB_ADDRESS_TO_OWNER_REL_OFF_IMM
+ stxdw [r6 + RM_FM_CPI_IDX_0_ACCT_INFO_OWNER_UOFF], r8
+ # frame.cpi[0].info.lamports = &input.user.lamports
+ add64 r8, IB_OWNER_TO_LAMPORTS_REL_OFF_IMM
+ stxdw [r6 + RM_FM_CPI_IDX_0_ACCT_INFO_LAMPORTS_UOFF], r8
+ # frame.cpi[0].info.data = &input.user.data
+ add64 r8, IB_LAMPORTS_TO_DATA_REL_OFF_IMM
+ stxdw [r6 + RM_FM_CPI_IDX_0_ACCT_INFO_DATA_UOFF], r8
+ # frame.cpi[1].meta.pubkey = &acct.address
+ # frame.cpi[1].info.key = &acct.address
+ mov64 r8, r7
+ add64 r8, ACCT_ADDRESS_OFF
+ stxdw [r6 + RM_FM_CPI_IDX_1_ACCT_META_PUBKEY_UOFF], r8
+ stxdw [r6 + RM_FM_CPI_IDX_1_ACCT_INFO_KEY_UOFF], r8
+ # frame.cpi[1].info.owner = &acct.owner
+ add64 r8, IB_ADDRESS_TO_OWNER_REL_OFF_IMM
+ stxdw [r6 + RM_FM_CPI_IDX_1_ACCT_INFO_OWNER_UOFF], r8
+ # frame.cpi[1].info.lamports = &acct.lamports
+ add64 r8, IB_OWNER_TO_LAMPORTS_REL_OFF_IMM
+ stxdw [r6 + RM_FM_CPI_IDX_1_ACCT_INFO_LAMPORTS_UOFF], r8
+ # frame.cpi[1].info.data = &acct.data
+ add64 r8, IB_LAMPORTS_TO_DATA_REL_OFF_IMM
+ stxdw [r6 + RM_FM_CPI_IDX_1_ACCT_INFO_DATA_UOFF], r8
+ # frame.sol_instruction.program_id = frame.system_program_id
+ ldxdw r8, [r6 + RM_FM_SYSTEM_PROGRAM_ID_OFF]
+ stxdw [r6 + RM_FM_SOL_INSN_PROGRAM_ID_UOFF], r8
+ # frame.sol_instruction.data = &frame.create_account_data
+ mov64 r8, r6
+ add64 r8, RM_FM_CREATE_ACCT_DATA_OFF
+ stxdw [r6 + RM_FM_SOL_INSN_DATA_UOFF], r8
+ # frame.sol_instruction.accounts = &frame.cpi.account_metas
+ add64 r8, RM_FM_CREATE_ACCT_DATA_TO_CPI_ACCT_METAS_REL_OFF_IMM
+ stxdw [r6 + RM_FM_SOL_INSN_ACCOUNTS_UOFF], r8
+ # frame.sol_instruction.account_len = register_misc.CREATE_ACCOUNT_N_ACCOUNTS
+ mov64 r8, RM_MISC_CREATE_ACCOUNT_N_ACCOUNTS
+ stxdw [r6 + RM_FM_SOL_INSN_ACCOUNT_LEN_UOFF], r8
+ # frame.sol_instruction.data_len = CreateAccountData.size
+ mov64 r8, SIZE_OF_CREATE_ACCOUNT_DATA
+ stxdw [r6 + RM_FM_SOL_INSN_DATA_LEN_UOFF], r8
+ # syscall.instruction = &frame.sol_instruction
+ mov64 r1, r6
+ add64 r1, RM_FM_SOL_INSN_OFF
+ # syscall.account_infos = &frame.cpi.account_infos
+ mov64 r2, r6
+ 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
+ mov64 r4, r6
+ add64 r4, RM_FM_SIGNERS_SEEDS_ADDR_UOFF
+ # syscall.seeds_len = register_misc.N_PDA_SIGNERS
+ mov64 r5, RM_MISC_N_PDA_SIGNERS
+ call sol_invoke_signed_c
exit
init_vault_invalid_pda:
# if frame.vault_index == register_misc.VAULT_INDEX_BASE
diff --git a/program/src/dropset/market/register.s b/program/src/dropset/market/register.s
index bed72d7..3ed1f3c 100644
--- a/program/src/dropset/market/register.s
+++ b/program/src/dropset/market/register.s
@@ -24,47 +24,57 @@
# Stack frame for REGISTER-MARKET.
# -------------------------------------------------------------------------
-# Pointer to owning token program address.
-.equ RM_FM_TOKEN_PROGRAM_ID_OFF, -472
-.equ RM_FM_INPUT_OFF, -464 # Saved input buffer pointer.
-.equ RM_FM_INPUT_SHIFTED_OFF, -456 # Saved input_shifted pointer.
-.equ RM_FM_PDA_SEEDS_OFF, -448 # Signer seeds offset.
+.equ RM_FM_TOKEN_PROGRAM_ID_OFF, -552 # Pointer to token program address.
+.equ RM_FM_PROGRAM_ID_OFF, -544 # Pointer to program ID in input buffer.
+.equ RM_FM_INPUT_OFF, -536 # Saved input buffer pointer.
+.equ RM_FM_INPUT_SHIFTED_OFF, -528 # Saved input_shifted pointer.
+.equ RM_FM_LAMPORTS_PER_BYTE_OFF, -520 # From Rent sysvar.
+# Return value from GetAccountDataSize CPI, to check token account data size at runtime.
+.equ RM_FM_TOKEN_ACCOUNT_DATA_SIZE_OFF, -512
+# Pointer to mint account for vault initialization.
+.equ RM_FM_MINT_OFF, -504
+.equ RM_FM_PDA_SEEDS_OFF, -496 # Signer seeds offset.
.equ RM_FM_PDA_SEEDS_N_SEEDS, 3 # Number of signer seeds.
-.equ RM_FM_PDA_SEEDS_IDX_0_ADDR_OFF, -448 # Idx 0 signer seed address.
-.equ RM_FM_PDA_SEEDS_IDX_0_LEN_OFF, -440 # Idx 0 signer seed length.
-.equ RM_FM_PDA_SEEDS_IDX_1_ADDR_OFF, -432 # Idx 1 signer seed address.
-.equ RM_FM_PDA_SEEDS_IDX_1_LEN_OFF, -424 # Idx 1 signer seed length.
-.equ RM_FM_PDA_SEEDS_IDX_2_ADDR_OFF, -416 # Idx 2 signer seed address.
-.equ RM_FM_PDA_SEEDS_IDX_2_LEN_OFF, -408 # Idx 2 signer seed length.
-.equ RM_FM_PDA_OFF, -400 # PDA address.
-.equ RM_FM_PDA_CHUNK_0_OFF, -400 # PDA address (chunk 0).
-.equ RM_FM_PDA_CHUNK_1_OFF, -392 # PDA address (chunk 1).
-.equ RM_FM_PDA_CHUNK_2_OFF, -384 # PDA address (chunk 2).
-.equ RM_FM_PDA_CHUNK_3_OFF, -376 # PDA address (chunk 3).
-.equ RM_FM_SYSTEM_PROGRAM_PUBKEY_OFF, -368 # System Program pubkey.
+.equ RM_FM_PDA_SEEDS_IDX_0_ADDR_OFF, -496 # Idx 0 signer seed address.
+.equ RM_FM_PDA_SEEDS_IDX_0_LEN_OFF, -488 # Idx 0 signer seed length.
+.equ RM_FM_PDA_SEEDS_IDX_1_ADDR_OFF, -480 # Idx 1 signer seed address.
+.equ RM_FM_PDA_SEEDS_IDX_1_LEN_OFF, -472 # Idx 1 signer seed length.
+.equ RM_FM_PDA_SEEDS_IDX_2_ADDR_OFF, -464 # Idx 2 signer seed address.
+.equ RM_FM_PDA_SEEDS_IDX_2_LEN_OFF, -456 # Idx 2 signer seed length.
+.equ RM_FM_PDA_OFF, -448 # PDA address.
+.equ RM_FM_PDA_CHUNK_0_OFF, -448 # PDA address (chunk 0).
+.equ RM_FM_PDA_CHUNK_1_OFF, -440 # PDA address (chunk 1).
+.equ RM_FM_PDA_CHUNK_2_OFF, -432 # PDA address (chunk 2).
+.equ RM_FM_PDA_CHUNK_3_OFF, -424 # PDA address (chunk 3).
+.equ RM_FM_SYSTEM_PROGRAM_PUBKEY_OFF, -416 # System Program pubkey.
# System Program pubkey (chunk 0).
-.equ RM_FM_SYSTEM_PROGRAM_PUBKEY_CHUNK_0_OFF, -368
+.equ RM_FM_SYSTEM_PROGRAM_PUBKEY_CHUNK_0_OFF, -416
# System Program pubkey (chunk 1).
-.equ RM_FM_SYSTEM_PROGRAM_PUBKEY_CHUNK_1_OFF, -360
+.equ RM_FM_SYSTEM_PROGRAM_PUBKEY_CHUNK_1_OFF, -408
# System Program pubkey (chunk 2).
-.equ RM_FM_SYSTEM_PROGRAM_PUBKEY_CHUNK_2_OFF, -352
+.equ RM_FM_SYSTEM_PROGRAM_PUBKEY_CHUNK_2_OFF, -400
# System Program pubkey (chunk 3).
-.equ RM_FM_SYSTEM_PROGRAM_PUBKEY_CHUNK_3_OFF, -344
-.equ RM_FM_CREATE_ACCT_DATA_OFF, -336 # CreateAccount instruction data.
+.equ RM_FM_SYSTEM_PROGRAM_PUBKEY_CHUNK_3_OFF, -392
+.equ RM_FM_SYSTEM_PROGRAM_ID_OFF, -384 # System Program ID in input buffer.
+# Get return data program ID for CPI calls.
+.equ RM_FM_GET_RETURN_DATA_PROGRAM_ID_OFF, -376
+.equ RM_FM_CREATE_ACCT_DATA_OFF, -344 # CreateAccount instruction data.
# Lamports field within CreateAccount instruction data.
-.equ RM_FM_CREATE_ACCT_LAMPORTS_UOFF, -332
+.equ RM_FM_CREATE_ACCT_LAMPORTS_UOFF, -340
# Space field within CreateAccount instruction data.
-.equ RM_FM_CREATE_ACCT_SPACE_UOFF, -324
+.equ RM_FM_CREATE_ACCT_SPACE_UOFF, -332
# Owner field within CreateAccount instruction data.
-.equ RM_FM_CREATE_ACCT_OWNER_UOFF, -316
+.equ RM_FM_CREATE_ACCT_OWNER_UOFF, -324
# Owner field within CreateAccount instruction data (chunk 0).
-.equ RM_FM_CREATE_ACCT_OWNER_CHUNK_0_UOFF, -316
+.equ RM_FM_CREATE_ACCT_OWNER_CHUNK_0_UOFF, -324
# Owner field within CreateAccount instruction data (chunk 1).
-.equ RM_FM_CREATE_ACCT_OWNER_CHUNK_1_UOFF, -308
+.equ RM_FM_CREATE_ACCT_OWNER_CHUNK_1_UOFF, -316
# Owner field within CreateAccount instruction data (chunk 2).
-.equ RM_FM_CREATE_ACCT_OWNER_CHUNK_2_UOFF, -300
+.equ RM_FM_CREATE_ACCT_OWNER_CHUNK_2_UOFF, -308
# Owner field within CreateAccount instruction data (chunk 3).
-.equ RM_FM_CREATE_ACCT_OWNER_CHUNK_3_UOFF, -292
+.equ RM_FM_CREATE_ACCT_OWNER_CHUNK_3_UOFF, -300
+# GetAccountDataSize CPI instruction data.
+.equ RM_FM_GET_ACCOUNT_DATA_SIZE_DATA_UOFF, -288
.equ RM_FM_CPI_N_ACCOUNTS, 3 # Number of CPI accounts.
.equ RM_FM_CPI_SOL_ACCT_INFO_OFF, -280 # Start of SolAccountInfo vector.
.equ RM_FM_CPI_SOL_ACCT_META_OFF, -112 # Start of SolAccountMeta vector.
@@ -144,11 +154,11 @@
# Whether the current token program is Token 2022.
.equ RM_FM_TOKEN_PROGRAM_IS_2022_UOFF, -6
# From pda_seeds to sol_instruction.
-.equ RM_FM_PDA_SEEDS_TO_SOL_INSN_REL_OFF_IMM, 400
+.equ RM_FM_PDA_SEEDS_TO_SOL_INSN_REL_OFF_IMM, 448
# From pda to signers_seeds.
-.equ RM_FM_PDA_TO_SIGNERS_SEEDS_REL_OFF_IMM, 336
+.equ RM_FM_PDA_TO_SIGNERS_SEEDS_REL_OFF_IMM, 384
# From create_account_data to CPI account metas.
-.equ RM_FM_CREATE_ACCT_DATA_TO_CPI_ACCT_METAS_REL_OFF_IMM, 224
+.equ RM_FM_CREATE_ACCT_DATA_TO_CPI_ACCT_METAS_REL_OFF_IMM, 232
# -------------------------------------------------------------------------
# Miscellaneous market registration constants.
@@ -204,6 +214,10 @@ register_market:
# if insn_len != RegisterMarketData.LEN
# return ErrorCode::InvalidInstructionLength
jne r4, REGISTER_MARKET_DATA_LEN, e_invalid_instruction_length
+ # frame.program_id = &insn.program_id
+ mov64 r4, r2
+ add64 r4, REGISTER_MARKET_DATA_LEN
+ stxdw [r10 + RM_FM_PROGRAM_ID_OFF], r4
# if input.user.data_len != data.DATA_LEN_ZERO
# return ErrorCode::UserHasData
ldxdw r9, [r1 + IB_USER_DATA_LEN_OFF]
@@ -258,10 +272,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
+ # frame.system_program_id = &acct.address
mov64 r7, r9
add64 r7, ACCT_ADDRESS_OFF
- stxdw [r10 + RM_FM_SOL_INSN_PROGRAM_ID_UOFF], r7
+ stxdw [r10 + RM_FM_SYSTEM_PROGRAM_ID_OFF], r7
# system_program_padded_data_len = acct.padded_data_len
ldxdw r7, [r9 + ACCT_DATA_LEN_OFF]
add64 r7, DATA_LEN_MAX_PAD
@@ -363,8 +377,20 @@ register_market_base_vault:
# acct += base_token_program_padded_data_len + EmptyAccount.size
add64 r9, r7
add64 r9, SIZE_OF_EMPTY_ACCOUNT
+ # if acct.duplicate != account.NON_DUP_MARKER
+ # return ErrorCode::BaseVaultIsDuplicate
+ ldxb r7, [r9 + ACCT_DUPLICATE_OFF]
+ jne r7, ACCT_NON_DUP_MARKER, e_base_vault_is_duplicate
+ # if acct.data_len != data.LEN_ZERO
+ # return ErrorCode::BaseVaultHasData
+ ldxdw r7, [r9 + ACCT_DATA_LEN_OFF]
+ jne r7, DATA_LEN_ZERO, e_base_vault_has_data
# frame.vault_index = register_misc.VAULT_INDEX_BASE
stb [r10 + RM_FM_VAULT_INDEX_UOFF], RM_MISC_VAULT_INDEX_BASE
+ # frame.mint = &input.base_mint
+ mov64 r7, r8
+ add64 r7, RM_MISC_BASE_DUPLICATE_OFF
+ stxdw [r10 + RM_FM_MINT_OFF], r7
# result = INIT-VAULT(acct, frame)
mov64 r1, r9
mov64 r2, r10
@@ -374,12 +400,7 @@ register_market_base_vault:
jeq r0, RETURN_SUCCESS, register_market_quote_token_program
exit
register_market_quote_token_program:
- # base_vault_padded_data_len = acct.padded_data_len
- ldxdw r7, [r9 + ACCT_DATA_LEN_OFF]
- add64 r7, DATA_LEN_MAX_PAD
- and64 r7, DATA_LEN_AND_MASK
- # acct += base_vault_padded_data_len + EmptyAccount.size
- add64 r9, r7
+ # acct += EmptyAccount.size
add64 r9, SIZE_OF_EMPTY_ACCOUNT
# if acct.duplicate == account.NON_DUP_MARKER
ldxb r7, [r9 + ACCT_DUPLICATE_OFF]
@@ -466,8 +487,20 @@ register_market_base_vault_dup:
# acct += u64.size
add64 r9, SIZE_OF_U64
register_market_done_token_programs:
+ # if acct.duplicate != account.NON_DUP_MARKER
+ # return ErrorCode::QuoteVaultIsDuplicate
+ ldxb r7, [r9 + ACCT_DUPLICATE_OFF]
+ jne r7, ACCT_NON_DUP_MARKER, e_quote_vault_is_duplicate
+ # if acct.data_len != data.LEN_ZERO
+ # return ErrorCode::QuoteVaultHasData
+ ldxdw r7, [r9 + ACCT_DATA_LEN_OFF]
+ jne r7, DATA_LEN_ZERO, e_quote_vault_has_data
# frame.vault_index = register_misc.VAULT_INDEX_QUOTE
stb [r10 + RM_FM_VAULT_INDEX_UOFF], RM_MISC_VAULT_INDEX_QUOTE
+ # frame.mint = &input_shifted.quote_mint
+ mov64 r7, r6
+ add64 r7, RM_MISC_QUOTE_DUPLICATE_OFF
+ stxdw [r10 + RM_FM_MINT_OFF], r7
# INIT-VAULT(acct, frame)
mov64 r1, r9
mov64 r2, r10
diff --git a/tests/Cargo.toml b/tests/Cargo.toml
index 897816e..5cd71ac 100644
--- a/tests/Cargo.toml
+++ b/tests/Cargo.toml
@@ -1,8 +1,10 @@
[dependencies]
dropset-interface = {path = "../interface"}
mollusk-svm = {workspace = true}
+mollusk-svm-programs-token = {workspace = true}
solana-account = {workspace = true}
solana-sdk = {workspace = true}
+spl-token-interface = {workspace = true}
heck.workspace = true
[package]
diff --git a/tests/src/lib.rs b/tests/src/lib.rs
index 3867c03..b525aca 100644
--- a/tests/src/lib.rs
+++ b/tests/src/lib.rs
@@ -51,6 +51,8 @@ pub struct TestSetup {
pub fn setup() -> TestSetup {
let mut setup = setup_program(DEFAULT_PROGRAM);
setup.mollusk.sysvars.rent.exemption_threshold = 1.0;
+ mollusk_svm_programs_token::token::add_program(&mut setup.mollusk);
+ mollusk_svm_programs_token::token2022::add_program(&mut setup.mollusk);
setup
}
diff --git a/tests/tests/cases/register_market.rs b/tests/tests/cases/register_market.rs
index 9e364a8..e050006 100644
--- a/tests/tests/cases/register_market.rs
+++ b/tests/tests/cases/register_market.rs
@@ -63,6 +63,12 @@ test_cases! {
DupQuoteTokenProgramNotQuoteMintOwnerChunk1,
DupQuoteTokenProgramNotQuoteMintOwnerChunk2,
DupQuoteTokenProgramNotQuoteMintOwnerChunk3,
+ BaseVaultIsDuplicate,
+ BaseVaultHasData,
+ QuoteVaultIsDuplicateDup,
+ QuoteVaultIsDuplicateNonDup,
+ QuoteVaultHasDataDup,
+ QuoteVaultHasDataNonDup,
InvalidBaseVaultPubkeyChunk0,
InvalidBaseVaultPubkeyChunk1,
InvalidBaseVaultPubkeyChunk2,
@@ -105,8 +111,67 @@ fn into_metas_and_accounts(
(metas, paired)
}
-const USER_LAMPORTS: u64 = 1_000_000;
+const USER_LAMPORTS: u64 = 10_000_000;
const MARKET_HEADER_SIZE: usize = size_of::();
+const TOKEN_ACCOUNT_SIZE: usize = 165;
+
+macro_rules! check_vault {
+ ($errors:expr, $label:expr, $vault:expr, $expected_owner:expr, $rent:expr) => {{
+ let vault = $vault;
+ let expected_owner = $expected_owner;
+ if vault.owner != *expected_owner {
+ $errors.push(format!(
+ "{} owner: expected {:?}, got {:?}",
+ $label, expected_owner, vault.owner
+ ));
+ }
+ if vault.data.len() != TOKEN_ACCOUNT_SIZE {
+ $errors.push(format!(
+ "{} data len: expected {}, got {}",
+ $label,
+ TOKEN_ACCOUNT_SIZE,
+ vault.data.len()
+ ));
+ }
+ if !$rent.is_exempt(vault.lamports, vault.data.len()) {
+ $errors.push(format!(
+ "{} not rent exempt: {} lamports for {} bytes",
+ $label,
+ vault.lamports,
+ vault.data.len()
+ ));
+ }
+ }};
+}
+
+fn default_mint() -> spl_token_interface::state::Mint {
+ spl_token_interface::state::Mint {
+ is_initialized: true,
+ ..Default::default()
+ }
+}
+
+fn mint_account(owner: Pubkey) -> Account {
+ if owner == Pubkey::from(TOKEN_PROGRAM_ID) {
+ mollusk_svm_programs_token::token::create_account_for_mint(default_mint())
+ } else if owner == Pubkey::from(TOKEN_2022_PROGRAM_ID) {
+ mollusk_svm_programs_token::token2022::create_account_for_mint(default_mint())
+ } else {
+ let mut acct = Account::default();
+ acct.owner = owner;
+ acct
+ }
+}
+
+fn token_program_account(id: Pubkey) -> Account {
+ if id == Pubkey::from(TOKEN_PROGRAM_ID) {
+ mollusk_svm_programs_token::token::account()
+ } else if id == Pubkey::from(TOKEN_2022_PROGRAM_ID) {
+ mollusk_svm_programs_token::token2022::account()
+ } else {
+ Account::default()
+ }
+}
/// Build valid accounts that pass all checks for a successful CreateAccount CPI.
/// When `base_token_program` and `quote_token_program` share the same key,
@@ -123,7 +188,7 @@ fn happy_path_accounts(
let pda = keys[RegisterMarketAccounts::Market as usize];
let (quote_vault_pda, _) = Pubkey::find_program_address(
&[pda.as_ref(), &[VAULT_INDEX_QUOTE as u8]],
- "e_token_program,
+ &setup.program_id,
);
keys[RegisterMarketAccounts::QuoteVault as usize] = quote_vault_pda;
@@ -231,7 +296,7 @@ fn token_program_base_accounts(
);
let (_vault_pda, vault_bump) = Pubkey::find_program_address(
&[pda.as_ref(), &[VAULT_INDEX_BASE as u8]],
- &base_token_program,
+ &setup.program_id,
);
if vault_bump != u8::MAX {
continue;
@@ -239,7 +304,7 @@ fn token_program_base_accounts(
if require_quote_vault_bump {
let (_quote_vault, quote_bump) = Pubkey::find_program_address(
&[pda.as_ref(), &[VAULT_INDEX_QUOTE as u8]],
- "e_token_program,
+ &setup.program_id,
);
if quote_bump != u8::MAX {
continue;
@@ -263,16 +328,20 @@ fn token_program_base_accounts(
keys[RegisterMarketAccounts::RentSysvar as usize] = rent_sysvar_pubkey;
accounts[RegisterMarketAccounts::RentSysvar as usize] = rent_sysvar_account;
- accounts[RegisterMarketAccounts::BaseMint as usize].owner = base_token_program;
- accounts[RegisterMarketAccounts::QuoteMint as usize].owner = quote_token_program;
+ accounts[RegisterMarketAccounts::BaseMint as usize] = mint_account(base_token_program);
+ accounts[RegisterMarketAccounts::QuoteMint as usize] = mint_account(quote_token_program);
keys[RegisterMarketAccounts::BaseTokenProgram as usize] = base_token_program;
+ accounts[RegisterMarketAccounts::BaseTokenProgram as usize] =
+ token_program_account(base_token_program);
keys[RegisterMarketAccounts::QuoteTokenProgram as usize] = quote_token_program;
+ accounts[RegisterMarketAccounts::QuoteTokenProgram as usize] =
+ token_program_account(quote_token_program);
// Derive base vault PDA from market address and vault index.
let (base_vault_pda, _) = Pubkey::find_program_address(
&[pda.as_ref(), &[VAULT_INDEX_BASE as u8]],
- &base_token_program,
+ &setup.program_id,
);
keys[RegisterMarketAccounts::BaseVault as usize] = base_vault_pda;
@@ -288,8 +357,12 @@ fn writable_metas_and_accounts(
.enumerate()
.map(|(i, k)| {
let writable = i == RegisterMarketAccounts::User as usize
- || i == RegisterMarketAccounts::Market as usize;
- let signer = i == RegisterMarketAccounts::User as usize;
+ || i == RegisterMarketAccounts::Market as usize
+ || i == RegisterMarketAccounts::BaseVault as usize
+ || i == RegisterMarketAccounts::QuoteVault as usize;
+ let signer = i == RegisterMarketAccounts::User as usize
+ || i == RegisterMarketAccounts::BaseVault as usize
+ || i == RegisterMarketAccounts::QuoteVault as usize;
if writable {
AccountMeta::new(*k, signer)
} else {
@@ -338,7 +411,7 @@ fn quote_vault_mismatch_accounts(
let pda = keys[RegisterMarketAccounts::Market as usize];
let (mut quote_vault_pda, _) = Pubkey::find_program_address(
&[pda.as_ref(), &[VAULT_INDEX_QUOTE as u8]],
- "e_token_program,
+ &setup.program_id,
);
quote_vault_pda.as_mut()[corrupt_byte] ^= 0xFF;
keys[RegisterMarketAccounts::QuoteVault as usize] = quote_vault_pda;
@@ -1063,6 +1136,155 @@ impl TestCase for Case {
)
}
// Verifies: REGISTER-MARKET
+ Self::BaseVaultIsDuplicate => {
+ let base_token_program = Pubkey::from(TOKEN_PROGRAM_ID);
+ let (mut keys, accounts) = token_program_base_accounts(
+ setup,
+ base_token_program,
+ base_token_program,
+ false,
+ );
+ // Base vault shares key with User, causing the runtime
+ // to serialize it as a duplicate.
+ keys[RegisterMarketAccounts::BaseVault as usize] =
+ keys[RegisterMarketAccounts::User as usize];
+ let (metas, accounts) = writable_metas_and_accounts(keys, accounts);
+ check_custom(
+ setup,
+ insn,
+ metas,
+ accounts,
+ Some(ErrorCode::BaseVaultIsDuplicate),
+ )
+ }
+ // Verifies: REGISTER-MARKET
+ Self::BaseVaultHasData => {
+ let base_token_program = Pubkey::from(TOKEN_PROGRAM_ID);
+ let (keys, mut accounts) = token_program_base_accounts(
+ setup,
+ base_token_program,
+ base_token_program,
+ false,
+ );
+ accounts[RegisterMarketAccounts::BaseVault as usize].data = vec![0u8; 32];
+ let (metas, accounts) = writable_metas_and_accounts(keys, accounts);
+ check_custom(
+ setup,
+ insn,
+ metas,
+ accounts,
+ Some(ErrorCode::BaseVaultHasData),
+ )
+ }
+ // Verifies: REGISTER-MARKET
+ Self::QuoteVaultIsDuplicateDup => {
+ let base_token_program = Pubkey::from(TOKEN_PROGRAM_ID);
+ let (mut keys, accounts) = token_program_base_accounts(
+ setup,
+ base_token_program,
+ base_token_program,
+ true,
+ );
+ let pda = keys[RegisterMarketAccounts::Market as usize];
+ let (quote_vault_pda, _) = Pubkey::find_program_address(
+ &[pda.as_ref(), &[VAULT_INDEX_QUOTE as u8]],
+ &setup.program_id,
+ );
+ keys[RegisterMarketAccounts::QuoteVault as usize] = quote_vault_pda;
+ // Quote vault shares key with User, causing the runtime
+ // to serialize it as a duplicate.
+ keys[RegisterMarketAccounts::QuoteVault as usize] =
+ keys[RegisterMarketAccounts::User as usize];
+ let (metas, accounts) = writable_metas_and_accounts(keys, accounts);
+ check_custom(
+ setup,
+ insn,
+ metas,
+ accounts,
+ Some(ErrorCode::QuoteVaultIsDuplicate),
+ )
+ }
+ // Verifies: REGISTER-MARKET
+ Self::QuoteVaultIsDuplicateNonDup => {
+ let base_token_program = Pubkey::from(TOKEN_PROGRAM_ID);
+ let quote_token_program = Pubkey::from(TOKEN_2022_PROGRAM_ID);
+ let (mut keys, accounts) = token_program_base_accounts(
+ setup,
+ base_token_program,
+ quote_token_program,
+ true,
+ );
+ let pda = keys[RegisterMarketAccounts::Market as usize];
+ let (quote_vault_pda, _) = Pubkey::find_program_address(
+ &[pda.as_ref(), &[VAULT_INDEX_QUOTE as u8]],
+ &setup.program_id,
+ );
+ keys[RegisterMarketAccounts::QuoteVault as usize] = quote_vault_pda;
+ // Quote vault shares key with User, causing the runtime
+ // to serialize it as a duplicate.
+ keys[RegisterMarketAccounts::QuoteVault as usize] =
+ keys[RegisterMarketAccounts::User as usize];
+ let (metas, accounts) = writable_metas_and_accounts(keys, accounts);
+ check_custom(
+ setup,
+ insn,
+ metas,
+ accounts,
+ Some(ErrorCode::QuoteVaultIsDuplicate),
+ )
+ }
+ // Verifies: REGISTER-MARKET
+ Self::QuoteVaultHasDataDup => {
+ let base_token_program = Pubkey::from(TOKEN_PROGRAM_ID);
+ let (mut keys, mut accounts) = token_program_base_accounts(
+ setup,
+ base_token_program,
+ base_token_program,
+ true,
+ );
+ let pda = keys[RegisterMarketAccounts::Market as usize];
+ let (quote_vault_pda, _) = Pubkey::find_program_address(
+ &[pda.as_ref(), &[VAULT_INDEX_QUOTE as u8]],
+ &setup.program_id,
+ );
+ keys[RegisterMarketAccounts::QuoteVault as usize] = quote_vault_pda;
+ accounts[RegisterMarketAccounts::QuoteVault as usize].data = vec![0u8; 32];
+ let (metas, accounts) = writable_metas_and_accounts(keys, accounts);
+ check_custom(
+ setup,
+ insn,
+ metas,
+ accounts,
+ Some(ErrorCode::QuoteVaultHasData),
+ )
+ }
+ // Verifies: REGISTER-MARKET
+ Self::QuoteVaultHasDataNonDup => {
+ let base_token_program = Pubkey::from(TOKEN_PROGRAM_ID);
+ let quote_token_program = Pubkey::from(TOKEN_2022_PROGRAM_ID);
+ let (mut keys, mut accounts) = token_program_base_accounts(
+ setup,
+ base_token_program,
+ quote_token_program,
+ true,
+ );
+ let pda = keys[RegisterMarketAccounts::Market as usize];
+ let (quote_vault_pda, _) = Pubkey::find_program_address(
+ &[pda.as_ref(), &[VAULT_INDEX_QUOTE as u8]],
+ &setup.program_id,
+ );
+ keys[RegisterMarketAccounts::QuoteVault as usize] = quote_vault_pda;
+ accounts[RegisterMarketAccounts::QuoteVault as usize].data = vec![0u8; 32];
+ let (metas, accounts) = writable_metas_and_accounts(keys, accounts);
+ check_custom(
+ setup,
+ insn,
+ metas,
+ accounts,
+ Some(ErrorCode::QuoteVaultHasData),
+ )
+ }
+ // Verifies: REGISTER-MARKET
// Verifies: INIT-MARKET-PDA
// Verifies: INIT-VAULT
Self::InvalidBaseVaultPubkeyChunk0 => {
@@ -1263,6 +1485,16 @@ impl TestCase for Case {
market.data.len()
));
}
+
+ let base_vault = &result.resulting_accounts
+ [RegisterMarketAccounts::BaseVault as usize]
+ .1;
+ check_vault!(errors, "base vault", base_vault, &token_program_id, rent);
+
+ let quote_vault = &result.resulting_accounts
+ [RegisterMarketAccounts::QuoteVault as usize]
+ .1;
+ check_vault!(errors, "quote vault", quote_vault, &token_program_id, rent);
}
other => {
errors.push(format!("expected success, got {:?}", other));
@@ -1315,6 +1547,16 @@ impl TestCase for Case {
market.data.len()
));
}
+
+ let base_vault = &result.resulting_accounts
+ [RegisterMarketAccounts::BaseVault as usize]
+ .1;
+ check_vault!(errors, "base vault", base_vault, &token_program_id, rent);
+
+ let quote_vault = &result.resulting_accounts
+ [RegisterMarketAccounts::QuoteVault as usize]
+ .1;
+ check_vault!(errors, "quote vault", quote_vault, &token_2022_id, rent);
}
other => {
errors.push(format!("expected success, got {:?}", other));
@@ -1366,6 +1608,16 @@ impl TestCase for Case {
market.data.len()
));
}
+
+ let base_vault = &result.resulting_accounts
+ [RegisterMarketAccounts::BaseVault as usize]
+ .1;
+ check_vault!(errors, "base vault", base_vault, &token_2022_id, rent);
+
+ let quote_vault = &result.resulting_accounts
+ [RegisterMarketAccounts::QuoteVault as usize]
+ .1;
+ check_vault!(errors, "quote vault", quote_vault, &token_2022_id, rent);
}
other => {
errors.push(format!("expected success, got {:?}", other));
@@ -1418,6 +1670,16 @@ impl TestCase for Case {
market.data.len()
));
}
+
+ let base_vault = &result.resulting_accounts
+ [RegisterMarketAccounts::BaseVault as usize]
+ .1;
+ check_vault!(errors, "base vault", base_vault, &token_2022_id, rent);
+
+ let quote_vault = &result.resulting_accounts
+ [RegisterMarketAccounts::QuoteVault as usize]
+ .1;
+ check_vault!(errors, "quote vault", quote_vault, &token_program_id, rent);
}
other => {
errors.push(format!("expected success, got {:?}", other));