diff --git a/.changeset/add-event-codegen.md b/.changeset/add-event-codegen.md new file mode 100644 index 0000000..e3c7c4b --- /dev/null +++ b/.changeset/add-event-codegen.md @@ -0,0 +1,5 @@ +--- +'@codama/renderers-rust': minor +--- + +Add event codegen support diff --git a/Cargo.lock b/Cargo.lock index da4d9bb..ff5c422 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -753,6 +753,24 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "codama-renderers-rust-e2e-raydium-cpmm" +version = "0.0.0" +dependencies = [ + "anchor-lang", + "borsh 1.5.7", + "num-derive", + "num-traits", + "solana-account 3.0.0", + "solana-account-info 3.1.0", + "solana-address 2.2.0", + "solana-client", + "solana-cpi 3.1.0", + "solana-instruction 3.2.0", + "solana-program-error 3.0.0", + "thiserror 1.0.69", +] + [[package]] name = "codama-renderers-rust-e2e-system" version = "0.0.0" diff --git a/e2e/raydium-cpmm/Cargo.toml b/e2e/raydium-cpmm/Cargo.toml new file mode 100644 index 0000000..d1b615d --- /dev/null +++ b/e2e/raydium-cpmm/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "codama-renderers-rust-e2e-raydium-cpmm" +version = "0.0.0" +edition = "2021" + +[features] +anchor = ["dep:anchor-lang"] +anchor-idl-build = ["anchor", "anchor-lang?/idl-build"] +test-sbf = [] +fetch = ["dep:solana-client"] + +[dependencies] +anchor-lang = { workspace = true, optional = true } +borsh = { workspace = true } +num-derive = { workspace = true } +num-traits = { workspace = true } +solana-account = { workspace = true } +solana-account-info = { workspace = true } +solana-address = { workspace = true, features = ["borsh", "copy", "curve25519", "decode"] } +solana-client = { workspace = true, optional = true } +solana-cpi = { workspace = true } +solana-instruction = { workspace = true } +solana-program-error = { workspace = true } +thiserror = { workspace = true } diff --git a/e2e/raydium-cpmm/idl.json b/e2e/raydium-cpmm/idl.json new file mode 100644 index 0000000..5a93905 --- /dev/null +++ b/e2e/raydium-cpmm/idl.json @@ -0,0 +1,2115 @@ +{ + "address": "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C", + "metadata": { + "name": "raydium_cp_swap", + "version": "0.2.0", + "spec": "0.1.0", + "description": "Raydium constant product AMM, supports Token2022 and without Openbook" + }, + "instructions": [ + { + "name": "collect_fund_fee", + "docs": [ + "Collect the fund fee accrued to the pool", + "", + "# Arguments", + "", + "* `ctx` - The context of accounts", + "* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1", + "* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0", + "" + ], + "discriminator": [ + 167, + 138, + 78, + 149, + 223, + 194, + 6, + 126 + ], + "accounts": [ + { + "name": "owner", + "docs": [ + "Only admin or fund_owner can collect fee now" + ], + "signer": true + }, + { + "name": "authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 118, + 97, + 117, + 108, + 116, + 95, + 97, + 110, + 100, + 95, + 108, + 112, + 95, + 109, + 105, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 95, + 115, + 101, + 101, + 100 + ] + } + ] + } + }, + { + "name": "pool_state", + "docs": [ + "Pool state stores accumulated protocol fee amount" + ], + "writable": true + }, + { + "name": "amm_config", + "docs": [ + "Amm config account stores fund_owner" + ] + }, + { + "name": "token_0_vault", + "docs": [ + "The address that holds pool tokens for token_0" + ], + "writable": true + }, + { + "name": "token_1_vault", + "docs": [ + "The address that holds pool tokens for token_1" + ], + "writable": true + }, + { + "name": "vault_0_mint", + "docs": [ + "The mint of token_0 vault" + ] + }, + { + "name": "vault_1_mint", + "docs": [ + "The mint of token_1 vault" + ] + }, + { + "name": "recipient_token_0_account", + "docs": [ + "The address that receives the collected token_0 fund fees" + ], + "writable": true + }, + { + "name": "recipient_token_1_account", + "docs": [ + "The address that receives the collected token_1 fund fees" + ], + "writable": true + }, + { + "name": "token_program", + "docs": [ + "The SPL program to perform token transfers" + ], + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "token_program_2022", + "docs": [ + "The SPL program 2022 to perform token transfers" + ], + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + } + ], + "args": [ + { + "name": "amount_0_requested", + "type": "u64" + }, + { + "name": "amount_1_requested", + "type": "u64" + } + ] + }, + { + "name": "collect_protocol_fee", + "docs": [ + "Collect the protocol fee accrued to the pool", + "", + "# Arguments", + "", + "* `ctx` - The context of accounts", + "* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1", + "* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0", + "" + ], + "discriminator": [ + 136, + 136, + 252, + 221, + 194, + 66, + 126, + 89 + ], + "accounts": [ + { + "name": "owner", + "docs": [ + "Only admin or owner can collect fee now" + ], + "signer": true + }, + { + "name": "authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 118, + 97, + 117, + 108, + 116, + 95, + 97, + 110, + 100, + 95, + 108, + 112, + 95, + 109, + 105, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 95, + 115, + 101, + 101, + 100 + ] + } + ] + } + }, + { + "name": "pool_state", + "docs": [ + "Pool state stores accumulated protocol fee amount" + ], + "writable": true + }, + { + "name": "amm_config", + "docs": [ + "Amm config account stores owner" + ] + }, + { + "name": "token_0_vault", + "docs": [ + "The address that holds pool tokens for token_0" + ], + "writable": true + }, + { + "name": "token_1_vault", + "docs": [ + "The address that holds pool tokens for token_1" + ], + "writable": true + }, + { + "name": "vault_0_mint", + "docs": [ + "The mint of token_0 vault" + ] + }, + { + "name": "vault_1_mint", + "docs": [ + "The mint of token_1 vault" + ] + }, + { + "name": "recipient_token_0_account", + "docs": [ + "The address that receives the collected token_0 protocol fees" + ], + "writable": true + }, + { + "name": "recipient_token_1_account", + "docs": [ + "The address that receives the collected token_1 protocol fees" + ], + "writable": true + }, + { + "name": "token_program", + "docs": [ + "The SPL program to perform token transfers" + ], + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "token_program_2022", + "docs": [ + "The SPL program 2022 to perform token transfers" + ], + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + } + ], + "args": [ + { + "name": "amount_0_requested", + "type": "u64" + }, + { + "name": "amount_1_requested", + "type": "u64" + } + ] + }, + { + "name": "create_amm_config", + "docs": [ + "# Arguments", + "", + "* `ctx`- The accounts needed by instruction.", + "* `index` - The index of amm config, there may be multiple config.", + "* `trade_fee_rate` - Trade fee rate, can be changed.", + "* `protocol_fee_rate` - The rate of protocol fee within trade fee.", + "* `fund_fee_rate` - The rate of fund fee within trade fee.", + "" + ], + "discriminator": [ + 137, + 52, + 237, + 212, + 215, + 117, + 108, + 104 + ], + "accounts": [ + { + "name": "owner", + "docs": [ + "Address to be set as protocol owner." + ], + "writable": true, + "signer": true, + "address": "GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ" + }, + { + "name": "amm_config", + "docs": [ + "Initialize config state account to store protocol owner address and fee rates." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 97, + 109, + 109, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "arg", + "path": "index" + } + ] + } + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + } + ], + "args": [ + { + "name": "index", + "type": "u16" + }, + { + "name": "trade_fee_rate", + "type": "u64" + }, + { + "name": "protocol_fee_rate", + "type": "u64" + }, + { + "name": "fund_fee_rate", + "type": "u64" + }, + { + "name": "create_pool_fee", + "type": "u64" + } + ] + }, + { + "name": "deposit", + "docs": [ + "Deposit lp token to the pool", + "", + "# Arguments", + "", + "* `ctx`- The context of accounts", + "* `lp_token_amount` - Pool token amount to transfer. token_a and token_b amount are set by the current exchange rate and size of the pool", + "* `maximum_token_0_amount` - Maximum token 0 amount to deposit, prevents excessive slippage", + "* `maximum_token_1_amount` - Maximum token 1 amount to deposit, prevents excessive slippage", + "" + ], + "discriminator": [ + 242, + 35, + 198, + 137, + 82, + 225, + 242, + 182 + ], + "accounts": [ + { + "name": "owner", + "docs": [ + "Pays to mint the position" + ], + "signer": true + }, + { + "name": "authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 118, + 97, + 117, + 108, + 116, + 95, + 97, + 110, + 100, + 95, + 108, + 112, + 95, + 109, + 105, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 95, + 115, + 101, + 101, + 100 + ] + } + ] + } + }, + { + "name": "pool_state", + "writable": true + }, + { + "name": "owner_lp_token", + "docs": [ + "Owner lp token account" + ], + "writable": true + }, + { + "name": "token_0_account", + "docs": [ + "The payer's token account for token_0" + ], + "writable": true + }, + { + "name": "token_1_account", + "docs": [ + "The payer's token account for token_1" + ], + "writable": true + }, + { + "name": "token_0_vault", + "docs": [ + "The address that holds pool tokens for token_0" + ], + "writable": true + }, + { + "name": "token_1_vault", + "docs": [ + "The address that holds pool tokens for token_1" + ], + "writable": true + }, + { + "name": "token_program", + "docs": [ + "token Program" + ], + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "token_program_2022", + "docs": [ + "Token program 2022" + ], + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + }, + { + "name": "vault_0_mint", + "docs": [ + "The mint of token_0 vault" + ] + }, + { + "name": "vault_1_mint", + "docs": [ + "The mint of token_1 vault" + ] + }, + { + "name": "lp_mint", + "docs": [ + "Lp token mint" + ], + "writable": true + } + ], + "args": [ + { + "name": "lp_token_amount", + "type": "u64" + }, + { + "name": "maximum_token_0_amount", + "type": "u64" + }, + { + "name": "maximum_token_1_amount", + "type": "u64" + } + ] + }, + { + "name": "initialize", + "docs": [ + "Creates a pool for the given token pair and the initial price", + "", + "# Arguments", + "", + "* `ctx`- The context of accounts", + "* `init_amount_0` - the initial amount_0 to deposit", + "* `init_amount_1` - the initial amount_1 to deposit", + "* `open_time` - the timestamp allowed for swap", + "" + ], + "discriminator": [ + 175, + 175, + 109, + 31, + 13, + 152, + 155, + 237 + ], + "accounts": [ + { + "name": "creator", + "docs": [ + "Address paying to create the pool. Can be anyone" + ], + "writable": true, + "signer": true + }, + { + "name": "amm_config", + "docs": [ + "Which config the pool belongs to." + ] + }, + { + "name": "authority", + "docs": [ + "pool vault and lp mint authority" + ], + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 118, + 97, + 117, + 108, + 116, + 95, + 97, + 110, + 100, + 95, + 108, + 112, + 95, + 109, + 105, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 95, + 115, + 101, + 101, + 100 + ] + } + ] + } + }, + { + "name": "pool_state", + "docs": [ + "PDA account:", + "seeds = [", + "POOL_SEED.as_bytes(),", + "amm_config.key().as_ref(),", + "token_0_mint.key().as_ref(),", + "token_1_mint.key().as_ref(),", + "],", + "", + "Or random account: must be signed by cli" + ], + "writable": true + }, + { + "name": "token_0_mint", + "docs": [ + "Token_0 mint, the key must smaller than token_1 mint." + ] + }, + { + "name": "token_1_mint", + "docs": [ + "Token_1 mint, the key must grater then token_0 mint." + ] + }, + { + "name": "lp_mint", + "docs": [ + "pool lp mint" + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 111, + 111, + 108, + 95, + 108, + 112, + 95, + 109, + 105, + 110, + 116 + ] + }, + { + "kind": "account", + "path": "pool_state" + } + ] + } + }, + { + "name": "creator_token_0", + "docs": [ + "payer token0 account" + ], + "writable": true + }, + { + "name": "creator_token_1", + "docs": [ + "creator token1 account" + ], + "writable": true + }, + { + "name": "creator_lp_token", + "docs": [ + "creator lp token account" + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "creator" + }, + { + "kind": "const", + "value": [ + 6, + 221, + 246, + 225, + 215, + 101, + 161, + 147, + 217, + 203, + 225, + 70, + 206, + 235, + 121, + 172, + 28, + 180, + 133, + 237, + 95, + 91, + 55, + 145, + 58, + 140, + 245, + 133, + 126, + 255, + 0, + 169 + ] + }, + { + "kind": "account", + "path": "lp_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "token_0_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 111, + 111, + 108, + 95, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "pool_state" + }, + { + "kind": "account", + "path": "token_0_mint" + } + ] + } + }, + { + "name": "token_1_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 111, + 111, + 108, + 95, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "pool_state" + }, + { + "kind": "account", + "path": "token_1_mint" + } + ] + } + }, + { + "name": "create_pool_fee", + "docs": [ + "create pool fee account" + ], + "writable": true, + "address": "DNXgeM9EiiaAbaWvwjHj9fQQLAX5ZsfHyvmYUNRAdNC8" + }, + { + "name": "observation_state", + "docs": [ + "an account to store oracle observations" + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 111, + 98, + 115, + 101, + 114, + 118, + 97, + 116, + 105, + 111, + 110 + ] + }, + { + "kind": "account", + "path": "pool_state" + } + ] + } + }, + { + "name": "token_program", + "docs": [ + "Program to create mint account and mint tokens" + ], + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "token_0_program", + "docs": [ + "Spl token program or token program 2022" + ] + }, + { + "name": "token_1_program", + "docs": [ + "Spl token program or token program 2022" + ] + }, + { + "name": "associated_token_program", + "docs": [ + "Program to create an ATA for receiving position NFT" + ], + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "system_program", + "docs": [ + "To create a new program account" + ], + "address": "11111111111111111111111111111111" + }, + { + "name": "rent", + "docs": [ + "Sysvar for program account" + ], + "address": "SysvarRent111111111111111111111111111111111" + } + ], + "args": [ + { + "name": "init_amount_0", + "type": "u64" + }, + { + "name": "init_amount_1", + "type": "u64" + }, + { + "name": "open_time", + "type": "u64" + } + ] + }, + { + "name": "swap_base_input", + "docs": [ + "Swap the tokens in the pool base input amount", + "", + "# Arguments", + "", + "* `ctx`- The context of accounts", + "* `amount_in` - input amount to transfer, output to DESTINATION is based on the exchange rate", + "* `minimum_amount_out` - Minimum amount of output token, prevents excessive slippage", + "" + ], + "discriminator": [ + 143, + 190, + 90, + 218, + 196, + 30, + 51, + 222 + ], + "accounts": [ + { + "name": "payer", + "docs": [ + "The user performing the swap" + ], + "signer": true + }, + { + "name": "authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 118, + 97, + 117, + 108, + 116, + 95, + 97, + 110, + 100, + 95, + 108, + 112, + 95, + 109, + 105, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 95, + 115, + 101, + 101, + 100 + ] + } + ] + } + }, + { + "name": "amm_config", + "docs": [ + "The factory state to read protocol fees" + ] + }, + { + "name": "pool_state", + "docs": [ + "The program account of the pool in which the swap will be performed" + ], + "writable": true + }, + { + "name": "input_token_account", + "docs": [ + "The user token account for input token" + ], + "writable": true + }, + { + "name": "output_token_account", + "docs": [ + "The user token account for output token" + ], + "writable": true + }, + { + "name": "input_vault", + "docs": [ + "The vault token account for input token" + ], + "writable": true + }, + { + "name": "output_vault", + "docs": [ + "The vault token account for output token" + ], + "writable": true + }, + { + "name": "input_token_program", + "docs": [ + "SPL program for input token transfers" + ] + }, + { + "name": "output_token_program", + "docs": [ + "SPL program for output token transfers" + ] + }, + { + "name": "input_token_mint", + "docs": [ + "The mint of input token" + ] + }, + { + "name": "output_token_mint", + "docs": [ + "The mint of output token" + ] + }, + { + "name": "observation_state", + "docs": [ + "The program account for the most recent oracle observation" + ], + "writable": true + } + ], + "args": [ + { + "name": "amount_in", + "type": "u64" + }, + { + "name": "minimum_amount_out", + "type": "u64" + } + ] + }, + { + "name": "swap_base_output", + "docs": [ + "Swap the tokens in the pool base output amount", + "", + "# Arguments", + "", + "* `ctx`- The context of accounts", + "* `max_amount_in` - input amount prevents excessive slippage", + "* `amount_out` - amount of output token", + "" + ], + "discriminator": [ + 55, + 217, + 98, + 86, + 163, + 74, + 180, + 173 + ], + "accounts": [ + { + "name": "payer", + "docs": [ + "The user performing the swap" + ], + "signer": true + }, + { + "name": "authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 118, + 97, + 117, + 108, + 116, + 95, + 97, + 110, + 100, + 95, + 108, + 112, + 95, + 109, + 105, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 95, + 115, + 101, + 101, + 100 + ] + } + ] + } + }, + { + "name": "amm_config", + "docs": [ + "The factory state to read protocol fees" + ] + }, + { + "name": "pool_state", + "docs": [ + "The program account of the pool in which the swap will be performed" + ], + "writable": true + }, + { + "name": "input_token_account", + "docs": [ + "The user token account for input token" + ], + "writable": true + }, + { + "name": "output_token_account", + "docs": [ + "The user token account for output token" + ], + "writable": true + }, + { + "name": "input_vault", + "docs": [ + "The vault token account for input token" + ], + "writable": true + }, + { + "name": "output_vault", + "docs": [ + "The vault token account for output token" + ], + "writable": true + }, + { + "name": "input_token_program", + "docs": [ + "SPL program for input token transfers" + ] + }, + { + "name": "output_token_program", + "docs": [ + "SPL program for output token transfers" + ] + }, + { + "name": "input_token_mint", + "docs": [ + "The mint of input token" + ] + }, + { + "name": "output_token_mint", + "docs": [ + "The mint of output token" + ] + }, + { + "name": "observation_state", + "docs": [ + "The program account for the most recent oracle observation" + ], + "writable": true + } + ], + "args": [ + { + "name": "max_amount_in", + "type": "u64" + }, + { + "name": "amount_out", + "type": "u64" + } + ] + }, + { + "name": "update_amm_config", + "docs": [ + "Updates the owner of the amm config", + "Must be called by the current owner or admin", + "", + "# Arguments", + "", + "* `ctx`- The context of accounts", + "* `trade_fee_rate`- The new trade fee rate of amm config, be set when `param` is 0", + "* `protocol_fee_rate`- The new protocol fee rate of amm config, be set when `param` is 1", + "* `fund_fee_rate`- The new fund fee rate of amm config, be set when `param` is 2", + "* `new_owner`- The config's new owner, be set when `param` is 3", + "* `new_fund_owner`- The config's new fund owner, be set when `param` is 4", + "* `param`- The value can be 0 | 1 | 2 | 3 | 4, otherwise will report a error", + "" + ], + "discriminator": [ + 49, + 60, + 174, + 136, + 154, + 28, + 116, + 200 + ], + "accounts": [ + { + "name": "owner", + "docs": [ + "The amm config owner or admin" + ], + "signer": true, + "address": "GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ" + }, + { + "name": "amm_config", + "docs": [ + "Amm config account to be changed" + ], + "writable": true + } + ], + "args": [ + { + "name": "param", + "type": "u8" + }, + { + "name": "value", + "type": "u64" + } + ] + }, + { + "name": "update_pool_status", + "docs": [ + "Update pool status for given value", + "", + "# Arguments", + "", + "* `ctx`- The context of accounts", + "* `status` - The value of status", + "" + ], + "discriminator": [ + 130, + 87, + 108, + 6, + 46, + 224, + 117, + 123 + ], + "accounts": [ + { + "name": "authority", + "signer": true, + "address": "GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ" + }, + { + "name": "pool_state", + "writable": true + } + ], + "args": [ + { + "name": "status", + "type": "u8" + } + ] + }, + { + "name": "withdraw", + "docs": [ + "Withdraw lp for token0 and token1", + "", + "# Arguments", + "", + "* `ctx`- The context of accounts", + "* `lp_token_amount` - Amount of pool tokens to burn. User receives an output of token a and b based on the percentage of the pool tokens that are returned.", + "* `minimum_token_0_amount` - Minimum amount of token 0 to receive, prevents excessive slippage", + "* `minimum_token_1_amount` - Minimum amount of token 1 to receive, prevents excessive slippage", + "" + ], + "discriminator": [ + 183, + 18, + 70, + 156, + 148, + 109, + 161, + 34 + ], + "accounts": [ + { + "name": "owner", + "docs": [ + "Pays to mint the position" + ], + "signer": true + }, + { + "name": "authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 118, + 97, + 117, + 108, + 116, + 95, + 97, + 110, + 100, + 95, + 108, + 112, + 95, + 109, + 105, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 95, + 115, + 101, + 101, + 100 + ] + } + ] + } + }, + { + "name": "pool_state", + "docs": [ + "Pool state account" + ], + "writable": true + }, + { + "name": "owner_lp_token", + "docs": [ + "Owner lp token account" + ], + "writable": true + }, + { + "name": "token_0_account", + "docs": [ + "The token account for receive token_0," + ], + "writable": true + }, + { + "name": "token_1_account", + "docs": [ + "The token account for receive token_1" + ], + "writable": true + }, + { + "name": "token_0_vault", + "docs": [ + "The address that holds pool tokens for token_0" + ], + "writable": true + }, + { + "name": "token_1_vault", + "docs": [ + "The address that holds pool tokens for token_1" + ], + "writable": true + }, + { + "name": "token_program", + "docs": [ + "token Program" + ], + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "token_program_2022", + "docs": [ + "Token program 2022" + ], + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + }, + { + "name": "vault_0_mint", + "docs": [ + "The mint of token_0 vault" + ] + }, + { + "name": "vault_1_mint", + "docs": [ + "The mint of token_1 vault" + ] + }, + { + "name": "lp_mint", + "docs": [ + "Pool lp token mint" + ], + "writable": true + }, + { + "name": "memo_program", + "docs": [ + "memo program" + ], + "address": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" + } + ], + "args": [ + { + "name": "lp_token_amount", + "type": "u64" + }, + { + "name": "minimum_token_0_amount", + "type": "u64" + }, + { + "name": "minimum_token_1_amount", + "type": "u64" + } + ] + } + ], + "accounts": [ + { + "name": "AmmConfig", + "discriminator": [ + 218, + 244, + 33, + 104, + 203, + 203, + 43, + 111 + ] + }, + { + "name": "ObservationState", + "discriminator": [ + 122, + 174, + 197, + 53, + 129, + 9, + 165, + 132 + ] + }, + { + "name": "PoolState", + "discriminator": [ + 247, + 237, + 227, + 245, + 215, + 195, + 222, + 70 + ] + } + ], + "events": [ + { + "name": "LpChangeEvent", + "discriminator": [ + 121, + 163, + 205, + 201, + 57, + 218, + 117, + 60 + ] + }, + { + "name": "SwapEvent", + "discriminator": [ + 64, + 198, + 205, + 232, + 38, + 8, + 113, + 226 + ] + } + ], + "errors": [ + { + "code": 6000, + "name": "NotApproved", + "msg": "Not approved" + }, + { + "code": 6001, + "name": "InvalidOwner", + "msg": "Input account owner is not the program address" + }, + { + "code": 6002, + "name": "EmptySupply", + "msg": "Input token account empty" + }, + { + "code": 6003, + "name": "InvalidInput", + "msg": "InvalidInput" + }, + { + "code": 6004, + "name": "IncorrectLpMint", + "msg": "Address of the provided lp token mint is incorrect" + }, + { + "code": 6005, + "name": "ExceededSlippage", + "msg": "Exceeds desired slippage limit" + }, + { + "code": 6006, + "name": "ZeroTradingTokens", + "msg": "Given pool token amount results in zero trading tokens" + }, + { + "code": 6007, + "name": "NotSupportMint", + "msg": "Not support token_2022 mint extension" + }, + { + "code": 6008, + "name": "InvalidVault", + "msg": "invaild vault" + }, + { + "code": 6009, + "name": "InitLpAmountTooLess", + "msg": "Init lp amount is too less(Because 100 amount lp will be locked)" + }, + { + "code": 6010, + "name": "TransferFeeCalculateNotMatch", + "msg": "TransferFee calculate not match" + } + ], + "types": [ + { + "name": "AmmConfig", + "docs": [ + "Holds the current owner of the factory" + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "bump", + "docs": [ + "Bump to identify PDA" + ], + "type": "u8" + }, + { + "name": "disable_create_pool", + "docs": [ + "Status to control if new pool can be create" + ], + "type": "bool" + }, + { + "name": "index", + "docs": [ + "Config index" + ], + "type": "u16" + }, + { + "name": "trade_fee_rate", + "docs": [ + "The trade fee, denominated in hundredths of a bip (10^-6)" + ], + "type": "u64" + }, + { + "name": "protocol_fee_rate", + "docs": [ + "The protocol fee" + ], + "type": "u64" + }, + { + "name": "fund_fee_rate", + "docs": [ + "The fund fee, denominated in hundredths of a bip (10^-6)" + ], + "type": "u64" + }, + { + "name": "create_pool_fee", + "docs": [ + "Fee for create a new pool" + ], + "type": "u64" + }, + { + "name": "protocol_owner", + "docs": [ + "Address of the protocol fee owner" + ], + "type": "pubkey" + }, + { + "name": "fund_owner", + "docs": [ + "Address of the fund fee owner" + ], + "type": "pubkey" + }, + { + "name": "padding", + "docs": [ + "padding" + ], + "type": { + "array": [ + "u64", + 16 + ] + } + } + ] + } + }, + { + "name": "LpChangeEvent", + "docs": [ + "Emitted when deposit and withdraw" + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "pool_id", + "type": "pubkey" + }, + { + "name": "lp_amount_before", + "type": "u64" + }, + { + "name": "token_0_vault_before", + "docs": [ + "pool vault sub trade fees" + ], + "type": "u64" + }, + { + "name": "token_1_vault_before", + "docs": [ + "pool vault sub trade fees" + ], + "type": "u64" + }, + { + "name": "token_0_amount", + "docs": [ + "calculate result without transfer fee" + ], + "type": "u64" + }, + { + "name": "token_1_amount", + "docs": [ + "calculate result without transfer fee" + ], + "type": "u64" + }, + { + "name": "token_0_transfer_fee", + "type": "u64" + }, + { + "name": "token_1_transfer_fee", + "type": "u64" + }, + { + "name": "change_type", + "type": "u8" + } + ] + } + }, + { + "name": "Observation", + "docs": [ + "The element of observations in ObservationState" + ], + "serialization": "bytemuckunsafe", + "repr": { + "kind": "c", + "packed": true + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "block_timestamp", + "docs": [ + "The block timestamp of the observation" + ], + "type": "u64" + }, + { + "name": "cumulative_token_0_price_x32", + "docs": [ + "the cumulative of token0 price during the duration time, Q32.32, the remaining 64 bit for overflow" + ], + "type": "u128" + }, + { + "name": "cumulative_token_1_price_x32", + "docs": [ + "the cumulative of token1 price during the duration time, Q32.32, the remaining 64 bit for overflow" + ], + "type": "u128" + } + ] + } + }, + { + "name": "ObservationState", + "serialization": "bytemuckunsafe", + "repr": { + "kind": "c", + "packed": true + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "initialized", + "docs": [ + "Whether the ObservationState is initialized" + ], + "type": "bool" + }, + { + "name": "observation_index", + "docs": [ + "the most-recently updated index of the observations array" + ], + "type": "u16" + }, + { + "name": "pool_id", + "type": "pubkey" + }, + { + "name": "observations", + "docs": [ + "observation array" + ], + "type": { + "array": [ + { + "defined": { + "name": "Observation" + } + }, + 100 + ] + } + }, + { + "name": "padding", + "docs": [ + "padding for feature update" + ], + "type": { + "array": [ + "u64", + 4 + ] + } + } + ] + } + }, + { + "name": "PoolState", + "serialization": "bytemuckunsafe", + "repr": { + "kind": "c", + "packed": true + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "amm_config", + "docs": [ + "Which config the pool belongs" + ], + "type": "pubkey" + }, + { + "name": "pool_creator", + "docs": [ + "pool creator" + ], + "type": "pubkey" + }, + { + "name": "token_0_vault", + "docs": [ + "Token A" + ], + "type": "pubkey" + }, + { + "name": "token_1_vault", + "docs": [ + "Token B" + ], + "type": "pubkey" + }, + { + "name": "lp_mint", + "docs": [ + "Pool tokens are issued when A or B tokens are deposited.", + "Pool tokens can be withdrawn back to the original A or B token." + ], + "type": "pubkey" + }, + { + "name": "token_0_mint", + "docs": [ + "Mint information for token A" + ], + "type": "pubkey" + }, + { + "name": "token_1_mint", + "docs": [ + "Mint information for token B" + ], + "type": "pubkey" + }, + { + "name": "token_0_program", + "docs": [ + "token_0 program" + ], + "type": "pubkey" + }, + { + "name": "token_1_program", + "docs": [ + "token_1 program" + ], + "type": "pubkey" + }, + { + "name": "observation_key", + "docs": [ + "observation account to store oracle data" + ], + "type": "pubkey" + }, + { + "name": "auth_bump", + "type": "u8" + }, + { + "name": "status", + "docs": [ + "Bitwise representation of the state of the pool", + "bit0, 1: disable deposit(value is 1), 0: normal", + "bit1, 1: disable withdraw(value is 2), 0: normal", + "bit2, 1: disable swap(value is 4), 0: normal" + ], + "type": "u8" + }, + { + "name": "lp_mint_decimals", + "type": "u8" + }, + { + "name": "mint_0_decimals", + "docs": [ + "mint0 and mint1 decimals" + ], + "type": "u8" + }, + { + "name": "mint_1_decimals", + "type": "u8" + }, + { + "name": "lp_supply", + "docs": [ + "True circulating supply without burns and lock ups" + ], + "type": "u64" + }, + { + "name": "protocol_fees_token_0", + "docs": [ + "The amounts of token_0 and token_1 that are owed to the liquidity provider." + ], + "type": "u64" + }, + { + "name": "protocol_fees_token_1", + "type": "u64" + }, + { + "name": "fund_fees_token_0", + "type": "u64" + }, + { + "name": "fund_fees_token_1", + "type": "u64" + }, + { + "name": "open_time", + "docs": [ + "The timestamp allowed for swap in the pool." + ], + "type": "u64" + }, + { + "name": "recent_epoch", + "docs": [ + "recent epoch" + ], + "type": "u64" + }, + { + "name": "padding", + "docs": [ + "padding for future updates" + ], + "type": { + "array": [ + "u64", + 31 + ] + } + } + ] + } + }, + { + "name": "SwapEvent", + "docs": [ + "Emitted when swap" + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "pool_id", + "type": "pubkey" + }, + { + "name": "input_vault_before", + "docs": [ + "pool vault sub trade fees" + ], + "type": "u64" + }, + { + "name": "output_vault_before", + "docs": [ + "pool vault sub trade fees" + ], + "type": "u64" + }, + { + "name": "input_amount", + "docs": [ + "calculate result without transfer fee" + ], + "type": "u64" + }, + { + "name": "output_amount", + "docs": [ + "calculate result without transfer fee" + ], + "type": "u64" + }, + { + "name": "input_transfer_fee", + "type": "u64" + }, + { + "name": "output_transfer_fee", + "type": "u64" + }, + { + "name": "base_input", + "type": "bool" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/e2e/raydium-cpmm/src/generated/accounts/amm_config.rs b/e2e/raydium-cpmm/src/generated/accounts/amm_config.rs new file mode 100644 index 0000000..20d58d4 --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/accounts/amm_config.rs @@ -0,0 +1,150 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +use borsh::BorshDeserialize; +use borsh::BorshSerialize; +use solana_address::Address; + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct AmmConfig { + pub discriminator: [u8; 8], + /// Bump to identify PDA + pub bump: u8, + /// Status to control if new pool can be create + pub disable_create_pool: bool, + /// Config index + pub index: u16, + /// The trade fee, denominated in hundredths of a bip (10^-6) + pub trade_fee_rate: u64, + /// The protocol fee + pub protocol_fee_rate: u64, + /// The fund fee, denominated in hundredths of a bip (10^-6) + pub fund_fee_rate: u64, + /// Fee for create a new pool + pub create_pool_fee: u64, + /// Address of the protocol fee owner + pub protocol_owner: Address, + /// Address of the fund fee owner + pub fund_owner: Address, + /// padding + pub padding: [u64; 16], +} + +pub const AMM_CONFIG_DISCRIMINATOR: [u8; 8] = [218, 244, 33, 104, 203, 203, 43, 111]; + +impl AmmConfig { + pub const LEN: usize = 236; + + #[inline(always)] + pub fn from_bytes(data: &[u8]) -> Result { + let mut data = data; + Self::deserialize(&mut data) + } +} + +impl<'a> TryFrom<&solana_account_info::AccountInfo<'a>> for AmmConfig { + type Error = std::io::Error; + + fn try_from(account_info: &solana_account_info::AccountInfo<'a>) -> Result { + let mut data: &[u8] = &(*account_info.data).borrow(); + Self::deserialize(&mut data) + } +} + +#[cfg(feature = "fetch")] +pub fn fetch_amm_config( + rpc: &solana_client::rpc_client::RpcClient, + address: &solana_address::Address, +) -> Result, std::io::Error> { + let accounts = fetch_all_amm_config(rpc, &[*address])?; + Ok(accounts[0].clone()) +} + +#[cfg(feature = "fetch")] +pub fn fetch_all_amm_config( + rpc: &solana_client::rpc_client::RpcClient, + addresses: &[solana_address::Address], +) -> Result>, std::io::Error> { + let accounts = rpc + .get_multiple_accounts(addresses) + .map_err(|e| std::io::Error::other(e.to_string()))?; + let mut decoded_accounts: Vec> = Vec::new(); + for i in 0..addresses.len() { + let address = addresses[i]; + let account = accounts[i].as_ref().ok_or(std::io::Error::other(format!( + "Account not found: {address}" + )))?; + let data = AmmConfig::from_bytes(&account.data)?; + decoded_accounts.push(crate::shared::DecodedAccount { + address, + account: account.clone(), + data, + }); + } + Ok(decoded_accounts) +} + +#[cfg(feature = "fetch")] +pub fn fetch_maybe_amm_config( + rpc: &solana_client::rpc_client::RpcClient, + address: &solana_address::Address, +) -> Result, std::io::Error> { + let accounts = fetch_all_maybe_amm_config(rpc, &[*address])?; + Ok(accounts[0].clone()) +} + +#[cfg(feature = "fetch")] +pub fn fetch_all_maybe_amm_config( + rpc: &solana_client::rpc_client::RpcClient, + addresses: &[solana_address::Address], +) -> Result>, std::io::Error> { + let accounts = rpc + .get_multiple_accounts(addresses) + .map_err(|e| std::io::Error::other(e.to_string()))?; + let mut decoded_accounts: Vec> = Vec::new(); + for i in 0..addresses.len() { + let address = addresses[i]; + if let Some(account) = accounts[i].as_ref() { + let data = AmmConfig::from_bytes(&account.data)?; + decoded_accounts.push(crate::shared::MaybeAccount::Exists( + crate::shared::DecodedAccount { + address, + account: account.clone(), + data, + }, + )); + } else { + decoded_accounts.push(crate::shared::MaybeAccount::NotFound(address)); + } + } + Ok(decoded_accounts) +} + +#[cfg(feature = "anchor")] +impl anchor_lang::AccountDeserialize for AmmConfig { + fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result { + Ok(Self::deserialize(buf)?) + } +} + +#[cfg(feature = "anchor")] +impl anchor_lang::AccountSerialize for AmmConfig {} + +#[cfg(feature = "anchor")] +impl anchor_lang::Owner for AmmConfig { + fn owner() -> anchor_lang::solana_program::pubkey::Pubkey { + anchor_lang::solana_program::pubkey::Pubkey::from(crate::RAYDIUM_CP_SWAP_ID.to_bytes()) + } +} + +#[cfg(feature = "anchor-idl-build")] +impl anchor_lang::IdlBuild for AmmConfig {} + +#[cfg(feature = "anchor-idl-build")] +impl anchor_lang::Discriminator for AmmConfig { + const DISCRIMINATOR: &[u8] = &[0; 8]; +} diff --git a/e2e/raydium-cpmm/src/generated/accounts/mod.rs b/e2e/raydium-cpmm/src/generated/accounts/mod.rs new file mode 100644 index 0000000..8018f52 --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/accounts/mod.rs @@ -0,0 +1,14 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +pub(crate) mod r#amm_config; +pub(crate) mod r#observation_state; +pub(crate) mod r#pool_state; + +pub use self::r#amm_config::*; +pub use self::r#observation_state::*; +pub use self::r#pool_state::*; diff --git a/e2e/raydium-cpmm/src/generated/accounts/observation_state.rs b/e2e/raydium-cpmm/src/generated/accounts/observation_state.rs new file mode 100644 index 0000000..40742e8 --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/accounts/observation_state.rs @@ -0,0 +1,140 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +use crate::generated::types::Observation; +use borsh::BorshDeserialize; +use borsh::BorshSerialize; +use solana_address::Address; + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct ObservationState { + pub discriminator: [u8; 8], + /// Whether the ObservationState is initialized + pub initialized: bool, + /// the most-recently updated index of the observations array + pub observation_index: u16, + pub pool_id: Address, + /// observation array + pub observations: [Observation; 100], + /// padding for feature update + pub padding: [u64; 4], +} + +pub const OBSERVATION_STATE_DISCRIMINATOR: [u8; 8] = [122, 174, 197, 53, 129, 9, 165, 132]; + +impl ObservationState { + pub const LEN: usize = 4075; + + #[inline(always)] + pub fn from_bytes(data: &[u8]) -> Result { + let mut data = data; + Self::deserialize(&mut data) + } +} + +impl<'a> TryFrom<&solana_account_info::AccountInfo<'a>> for ObservationState { + type Error = std::io::Error; + + fn try_from(account_info: &solana_account_info::AccountInfo<'a>) -> Result { + let mut data: &[u8] = &(*account_info.data).borrow(); + Self::deserialize(&mut data) + } +} + +#[cfg(feature = "fetch")] +pub fn fetch_observation_state( + rpc: &solana_client::rpc_client::RpcClient, + address: &solana_address::Address, +) -> Result, std::io::Error> { + let accounts = fetch_all_observation_state(rpc, &[*address])?; + Ok(accounts[0].clone()) +} + +#[cfg(feature = "fetch")] +pub fn fetch_all_observation_state( + rpc: &solana_client::rpc_client::RpcClient, + addresses: &[solana_address::Address], +) -> Result>, std::io::Error> { + let accounts = rpc + .get_multiple_accounts(addresses) + .map_err(|e| std::io::Error::other(e.to_string()))?; + let mut decoded_accounts: Vec> = Vec::new(); + for i in 0..addresses.len() { + let address = addresses[i]; + let account = accounts[i].as_ref().ok_or(std::io::Error::other(format!( + "Account not found: {address}" + )))?; + let data = ObservationState::from_bytes(&account.data)?; + decoded_accounts.push(crate::shared::DecodedAccount { + address, + account: account.clone(), + data, + }); + } + Ok(decoded_accounts) +} + +#[cfg(feature = "fetch")] +pub fn fetch_maybe_observation_state( + rpc: &solana_client::rpc_client::RpcClient, + address: &solana_address::Address, +) -> Result, std::io::Error> { + let accounts = fetch_all_maybe_observation_state(rpc, &[*address])?; + Ok(accounts[0].clone()) +} + +#[cfg(feature = "fetch")] +pub fn fetch_all_maybe_observation_state( + rpc: &solana_client::rpc_client::RpcClient, + addresses: &[solana_address::Address], +) -> Result>, std::io::Error> { + let accounts = rpc + .get_multiple_accounts(addresses) + .map_err(|e| std::io::Error::other(e.to_string()))?; + let mut decoded_accounts: Vec> = Vec::new(); + for i in 0..addresses.len() { + let address = addresses[i]; + if let Some(account) = accounts[i].as_ref() { + let data = ObservationState::from_bytes(&account.data)?; + decoded_accounts.push(crate::shared::MaybeAccount::Exists( + crate::shared::DecodedAccount { + address, + account: account.clone(), + data, + }, + )); + } else { + decoded_accounts.push(crate::shared::MaybeAccount::NotFound(address)); + } + } + Ok(decoded_accounts) +} + +#[cfg(feature = "anchor")] +impl anchor_lang::AccountDeserialize for ObservationState { + fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result { + Ok(Self::deserialize(buf)?) + } +} + +#[cfg(feature = "anchor")] +impl anchor_lang::AccountSerialize for ObservationState {} + +#[cfg(feature = "anchor")] +impl anchor_lang::Owner for ObservationState { + fn owner() -> anchor_lang::solana_program::pubkey::Pubkey { + anchor_lang::solana_program::pubkey::Pubkey::from(crate::RAYDIUM_CP_SWAP_ID.to_bytes()) + } +} + +#[cfg(feature = "anchor-idl-build")] +impl anchor_lang::IdlBuild for ObservationState {} + +#[cfg(feature = "anchor-idl-build")] +impl anchor_lang::Discriminator for ObservationState { + const DISCRIMINATOR: &[u8] = &[0; 8]; +} diff --git a/e2e/raydium-cpmm/src/generated/accounts/pool_state.rs b/e2e/raydium-cpmm/src/generated/accounts/pool_state.rs new file mode 100644 index 0000000..f603489 --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/accounts/pool_state.rs @@ -0,0 +1,174 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +use borsh::BorshDeserialize; +use borsh::BorshSerialize; +use solana_address::Address; + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct PoolState { + pub discriminator: [u8; 8], + /// Which config the pool belongs + pub amm_config: Address, + /// pool creator + pub pool_creator: Address, + /// Token A + pub token0_vault: Address, + /// Token B + pub token1_vault: Address, + /// Pool tokens are issued when A or B tokens are deposited. + /// Pool tokens can be withdrawn back to the original A or B token. + pub lp_mint: Address, + /// Mint information for token A + pub token0_mint: Address, + /// Mint information for token B + pub token1_mint: Address, + /// token_0 program + pub token0_program: Address, + /// token_1 program + pub token1_program: Address, + /// observation account to store oracle data + pub observation_key: Address, + pub auth_bump: u8, + /// Bitwise representation of the state of the pool + /// bit0, 1: disable deposit(value is 1), 0: normal + /// bit1, 1: disable withdraw(value is 2), 0: normal + /// bit2, 1: disable swap(value is 4), 0: normal + pub status: u8, + pub lp_mint_decimals: u8, + /// mint0 and mint1 decimals + pub mint0_decimals: u8, + pub mint1_decimals: u8, + /// True circulating supply without burns and lock ups + pub lp_supply: u64, + /// The amounts of token_0 and token_1 that are owed to the liquidity provider. + pub protocol_fees_token0: u64, + pub protocol_fees_token1: u64, + pub fund_fees_token0: u64, + pub fund_fees_token1: u64, + /// The timestamp allowed for swap in the pool. + pub open_time: u64, + /// recent epoch + pub recent_epoch: u64, + /// padding for future updates + pub padding: [u64; 31], +} + +pub const POOL_STATE_DISCRIMINATOR: [u8; 8] = [247, 237, 227, 245, 215, 195, 222, 70]; + +impl PoolState { + pub const LEN: usize = 637; + + #[inline(always)] + pub fn from_bytes(data: &[u8]) -> Result { + let mut data = data; + Self::deserialize(&mut data) + } +} + +impl<'a> TryFrom<&solana_account_info::AccountInfo<'a>> for PoolState { + type Error = std::io::Error; + + fn try_from(account_info: &solana_account_info::AccountInfo<'a>) -> Result { + let mut data: &[u8] = &(*account_info.data).borrow(); + Self::deserialize(&mut data) + } +} + +#[cfg(feature = "fetch")] +pub fn fetch_pool_state( + rpc: &solana_client::rpc_client::RpcClient, + address: &solana_address::Address, +) -> Result, std::io::Error> { + let accounts = fetch_all_pool_state(rpc, &[*address])?; + Ok(accounts[0].clone()) +} + +#[cfg(feature = "fetch")] +pub fn fetch_all_pool_state( + rpc: &solana_client::rpc_client::RpcClient, + addresses: &[solana_address::Address], +) -> Result>, std::io::Error> { + let accounts = rpc + .get_multiple_accounts(addresses) + .map_err(|e| std::io::Error::other(e.to_string()))?; + let mut decoded_accounts: Vec> = Vec::new(); + for i in 0..addresses.len() { + let address = addresses[i]; + let account = accounts[i].as_ref().ok_or(std::io::Error::other(format!( + "Account not found: {address}" + )))?; + let data = PoolState::from_bytes(&account.data)?; + decoded_accounts.push(crate::shared::DecodedAccount { + address, + account: account.clone(), + data, + }); + } + Ok(decoded_accounts) +} + +#[cfg(feature = "fetch")] +pub fn fetch_maybe_pool_state( + rpc: &solana_client::rpc_client::RpcClient, + address: &solana_address::Address, +) -> Result, std::io::Error> { + let accounts = fetch_all_maybe_pool_state(rpc, &[*address])?; + Ok(accounts[0].clone()) +} + +#[cfg(feature = "fetch")] +pub fn fetch_all_maybe_pool_state( + rpc: &solana_client::rpc_client::RpcClient, + addresses: &[solana_address::Address], +) -> Result>, std::io::Error> { + let accounts = rpc + .get_multiple_accounts(addresses) + .map_err(|e| std::io::Error::other(e.to_string()))?; + let mut decoded_accounts: Vec> = Vec::new(); + for i in 0..addresses.len() { + let address = addresses[i]; + if let Some(account) = accounts[i].as_ref() { + let data = PoolState::from_bytes(&account.data)?; + decoded_accounts.push(crate::shared::MaybeAccount::Exists( + crate::shared::DecodedAccount { + address, + account: account.clone(), + data, + }, + )); + } else { + decoded_accounts.push(crate::shared::MaybeAccount::NotFound(address)); + } + } + Ok(decoded_accounts) +} + +#[cfg(feature = "anchor")] +impl anchor_lang::AccountDeserialize for PoolState { + fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result { + Ok(Self::deserialize(buf)?) + } +} + +#[cfg(feature = "anchor")] +impl anchor_lang::AccountSerialize for PoolState {} + +#[cfg(feature = "anchor")] +impl anchor_lang::Owner for PoolState { + fn owner() -> anchor_lang::solana_program::pubkey::Pubkey { + anchor_lang::solana_program::pubkey::Pubkey::from(crate::RAYDIUM_CP_SWAP_ID.to_bytes()) + } +} + +#[cfg(feature = "anchor-idl-build")] +impl anchor_lang::IdlBuild for PoolState {} + +#[cfg(feature = "anchor-idl-build")] +impl anchor_lang::Discriminator for PoolState { + const DISCRIMINATOR: &[u8] = &[0; 8]; +} diff --git a/e2e/raydium-cpmm/src/generated/errors/mod.rs b/e2e/raydium-cpmm/src/generated/errors/mod.rs new file mode 100644 index 0000000..164fee8 --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/errors/mod.rs @@ -0,0 +1,10 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +pub(crate) mod raydium_cp_swap; + +pub use self::raydium_cp_swap::RaydiumCpSwapError; diff --git a/e2e/raydium-cpmm/src/generated/errors/raydium_cp_swap.rs b/e2e/raydium-cpmm/src/generated/errors/raydium_cp_swap.rs new file mode 100644 index 0000000..33f3db2 --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/errors/raydium_cp_swap.rs @@ -0,0 +1,52 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +use num_derive::FromPrimitive; +use thiserror::Error; + +#[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)] +pub enum RaydiumCpSwapError { + /// 6000 - Not approved + #[error("Not approved")] + NotApproved = 0x1770, + /// 6001 - Input account owner is not the program address + #[error("Input account owner is not the program address")] + InvalidOwner = 0x1771, + /// 6002 - Input token account empty + #[error("Input token account empty")] + EmptySupply = 0x1772, + /// 6003 - InvalidInput + #[error("InvalidInput")] + InvalidInput = 0x1773, + /// 6004 - Address of the provided lp token mint is incorrect + #[error("Address of the provided lp token mint is incorrect")] + IncorrectLpMint = 0x1774, + /// 6005 - Exceeds desired slippage limit + #[error("Exceeds desired slippage limit")] + ExceededSlippage = 0x1775, + /// 6006 - Given pool token amount results in zero trading tokens + #[error("Given pool token amount results in zero trading tokens")] + ZeroTradingTokens = 0x1776, + /// 6007 - Not support token_2022 mint extension + #[error("Not support token_2022 mint extension")] + NotSupportMint = 0x1777, + /// 6008 - invaild vault + #[error("invaild vault")] + InvalidVault = 0x1778, + /// 6009 - Init lp amount is too less(Because 100 amount lp will be locked) + #[error("Init lp amount is too less(Because 100 amount lp will be locked)")] + InitLpAmountTooLess = 0x1779, + /// 6010 - TransferFee calculate not match + #[error("TransferFee calculate not match")] + TransferFeeCalculateNotMatch = 0x177A, +} + +impl From for solana_program_error::ProgramError { + fn from(e: RaydiumCpSwapError) -> Self { + solana_program_error::ProgramError::Custom(e as u32) + } +} diff --git a/e2e/raydium-cpmm/src/generated/events/lp_change_event.rs b/e2e/raydium-cpmm/src/generated/events/lp_change_event.rs new file mode 100644 index 0000000..15dbf98 --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/events/lp_change_event.rs @@ -0,0 +1,45 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +use borsh::BorshDeserialize; +use borsh::BorshSerialize; +use solana_address::Address; + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct LpChangeEvent { + pub pool_id: Address, + pub lp_amount_before: u64, + /// pool vault sub trade fees + pub token0_vault_before: u64, + /// pool vault sub trade fees + pub token1_vault_before: u64, + /// calculate result without transfer fee + pub token0_amount: u64, + /// calculate result without transfer fee + pub token1_amount: u64, + pub token0_transfer_fee: u64, + pub token1_transfer_fee: u64, + pub change_type: u8, +} + +pub const LP_CHANGE_EVENT_DISCRIMINATOR: [u8; 8] = [121, 163, 205, 201, 57, 218, 117, 60]; + +impl LpChangeEvent { + #[inline(always)] + pub fn from_bytes(data: &[u8]) -> Result { + if data.get(..LP_CHANGE_EVENT_DISCRIMINATOR.len()) + != Some(&LP_CHANGE_EVENT_DISCRIMINATOR[..]) + { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "invalid event discriminator", + )); + } + let mut data = &data[LP_CHANGE_EVENT_DISCRIMINATOR.len()..]; + Self::deserialize(&mut data) + } +} diff --git a/e2e/raydium-cpmm/src/generated/events/mod.rs b/e2e/raydium-cpmm/src/generated/events/mod.rs new file mode 100644 index 0000000..d36c7e6 --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/events/mod.rs @@ -0,0 +1,14 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +pub(crate) mod r#lp_change_event; +pub(crate) mod r#raydium_cp_swap_events; +pub(crate) mod r#swap_event; + +pub use self::r#lp_change_event::*; +pub use self::r#raydium_cp_swap_events::*; +pub use self::r#swap_event::*; diff --git a/e2e/raydium-cpmm/src/generated/events/raydium_cp_swap_events.rs b/e2e/raydium-cpmm/src/generated/events/raydium_cp_swap_events.rs new file mode 100644 index 0000000..affde9d --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/events/raydium_cp_swap_events.rs @@ -0,0 +1,54 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +use crate::generated::events::LpChangeEvent; +use crate::generated::events::SwapEvent; +use crate::generated::events::LP_CHANGE_EVENT_DISCRIMINATOR; +use crate::generated::events::SWAP_EVENT_DISCRIMINATOR; +use borsh::BorshDeserialize; + +/// Event kinds for the `raydium_cp_swap` program. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum RaydiumCpSwapEventKind { + LpChangeEvent, + SwapEvent, +} + +/// Identifies a `raydium_cp_swap` event from the provided data. +pub fn identify_raydium_cp_swap_event(data: &[u8]) -> Option { + if data.get(..LP_CHANGE_EVENT_DISCRIMINATOR.len()) == Some(&LP_CHANGE_EVENT_DISCRIMINATOR[..]) { + return Some(RaydiumCpSwapEventKind::LpChangeEvent); + } + if data.get(..SWAP_EVENT_DISCRIMINATOR.len()) == Some(&SWAP_EVENT_DISCRIMINATOR[..]) { + return Some(RaydiumCpSwapEventKind::SwapEvent); + } + None +} + +/// Parsed event variants for the `raydium_cp_swap` program. +#[derive(Clone, Debug, PartialEq)] +pub enum RaydiumCpSwapEvent { + LpChangeEvent(LpChangeEvent), + SwapEvent(SwapEvent), +} + +/// Tries to parse a `raydium_cp_swap` event from the provided data. +pub fn try_parse_raydium_cp_swap_event( + data: &[u8], +) -> Option> { + let event_kind = identify_raydium_cp_swap_event(data)?; + Some(match event_kind { + RaydiumCpSwapEventKind::LpChangeEvent => { + let mut data = &data[LP_CHANGE_EVENT_DISCRIMINATOR.len()..]; + LpChangeEvent::deserialize(&mut data).map(RaydiumCpSwapEvent::LpChangeEvent) + } + RaydiumCpSwapEventKind::SwapEvent => { + let mut data = &data[SWAP_EVENT_DISCRIMINATOR.len()..]; + SwapEvent::deserialize(&mut data).map(RaydiumCpSwapEvent::SwapEvent) + } + }) +} diff --git a/e2e/raydium-cpmm/src/generated/events/swap_event.rs b/e2e/raydium-cpmm/src/generated/events/swap_event.rs new file mode 100644 index 0000000..d3f68cf --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/events/swap_event.rs @@ -0,0 +1,42 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +use borsh::BorshDeserialize; +use borsh::BorshSerialize; +use solana_address::Address; + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct SwapEvent { + pub pool_id: Address, + /// pool vault sub trade fees + pub input_vault_before: u64, + /// pool vault sub trade fees + pub output_vault_before: u64, + /// calculate result without transfer fee + pub input_amount: u64, + /// calculate result without transfer fee + pub output_amount: u64, + pub input_transfer_fee: u64, + pub output_transfer_fee: u64, + pub base_input: bool, +} + +pub const SWAP_EVENT_DISCRIMINATOR: [u8; 8] = [64, 198, 205, 232, 38, 8, 113, 226]; + +impl SwapEvent { + #[inline(always)] + pub fn from_bytes(data: &[u8]) -> Result { + if data.get(..SWAP_EVENT_DISCRIMINATOR.len()) != Some(&SWAP_EVENT_DISCRIMINATOR[..]) { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "invalid event discriminator", + )); + } + let mut data = &data[SWAP_EVENT_DISCRIMINATOR.len()..]; + Self::deserialize(&mut data) + } +} diff --git a/e2e/raydium-cpmm/src/generated/instructions/collect_fund_fee.rs b/e2e/raydium-cpmm/src/generated/instructions/collect_fund_fee.rs new file mode 100644 index 0000000..a4c246a --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/instructions/collect_fund_fee.rs @@ -0,0 +1,806 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +use borsh::BorshDeserialize; +use borsh::BorshSerialize; + +pub const COLLECT_FUND_FEE_DISCRIMINATOR: [u8; 8] = [167, 138, 78, 149, 223, 194, 6, 126]; + +/// Accounts. +#[derive(Debug)] +pub struct CollectFundFee { + /// Only admin or fund_owner can collect fee now + pub owner: solana_address::Address, + + pub authority: solana_address::Address, + /// Pool state stores accumulated protocol fee amount + pub pool_state: solana_address::Address, + /// Amm config account stores fund_owner + pub amm_config: solana_address::Address, + /// The address that holds pool tokens for token_0 + pub token0_vault: solana_address::Address, + /// The address that holds pool tokens for token_1 + pub token1_vault: solana_address::Address, + /// The mint of token_0 vault + pub vault0_mint: solana_address::Address, + /// The mint of token_1 vault + pub vault1_mint: solana_address::Address, + /// The address that receives the collected token_0 fund fees + pub recipient_token0_account: solana_address::Address, + /// The address that receives the collected token_1 fund fees + pub recipient_token1_account: solana_address::Address, + /// The SPL program to perform token transfers + pub token_program: solana_address::Address, + /// The SPL program 2022 to perform token transfers + pub token_program2022: solana_address::Address, +} + +impl CollectFundFee { + pub fn instruction( + &self, + args: CollectFundFeeInstructionArgs, + ) -> solana_instruction::Instruction { + self.instruction_with_remaining_accounts(args, &[]) + } + #[allow(clippy::arithmetic_side_effects)] + #[allow(clippy::vec_init_then_push)] + pub fn instruction_with_remaining_accounts( + &self, + args: CollectFundFeeInstructionArgs, + remaining_accounts: &[solana_instruction::AccountMeta], + ) -> solana_instruction::Instruction { + let mut accounts = Vec::with_capacity(12 + remaining_accounts.len()); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.owner, true, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.authority, + false, + )); + accounts.push(solana_instruction::AccountMeta::new(self.pool_state, false)); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.amm_config, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.token0_vault, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.token1_vault, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.vault0_mint, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.vault1_mint, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.recipient_token0_account, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.recipient_token1_account, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.token_program, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.token_program2022, + false, + )); + accounts.extend_from_slice(remaining_accounts); + let mut data = CollectFundFeeInstructionData::new().try_to_vec().unwrap(); + let mut args = args.try_to_vec().unwrap(); + data.append(&mut args); + + solana_instruction::Instruction { + program_id: crate::RAYDIUM_CP_SWAP_ID, + accounts, + data, + } + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct CollectFundFeeInstructionData { + discriminator: [u8; 8], +} + +impl CollectFundFeeInstructionData { + pub fn new() -> Self { + Self { + discriminator: [167, 138, 78, 149, 223, 194, 6, 126], + } + } + + pub(crate) fn try_to_vec(&self) -> Result, std::io::Error> { + borsh::to_vec(self) + } +} + +impl Default for CollectFundFeeInstructionData { + fn default() -> Self { + Self::new() + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct CollectFundFeeInstructionArgs { + pub amount0_requested: u64, + pub amount1_requested: u64, +} + +impl CollectFundFeeInstructionArgs { + pub(crate) fn try_to_vec(&self) -> Result, std::io::Error> { + borsh::to_vec(self) + } +} + +/// Instruction builder for `CollectFundFee`. +/// +/// ### Accounts: +/// +/// 0. `[signer]` owner +/// 1. `[]` authority +/// 2. `[writable]` pool_state +/// 3. `[]` amm_config +/// 4. `[writable]` token0_vault +/// 5. `[writable]` token1_vault +/// 6. `[]` vault0_mint +/// 7. `[]` vault1_mint +/// 8. `[writable]` recipient_token0_account +/// 9. `[writable]` recipient_token1_account +/// 10. `[optional]` token_program (default to `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`) +/// 11. `[optional]` token_program2022 (default to `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb`) +#[derive(Clone, Debug, Default)] +pub struct CollectFundFeeBuilder { + owner: Option, + authority: Option, + pool_state: Option, + amm_config: Option, + token0_vault: Option, + token1_vault: Option, + vault0_mint: Option, + vault1_mint: Option, + recipient_token0_account: Option, + recipient_token1_account: Option, + token_program: Option, + token_program2022: Option, + amount0_requested: Option, + amount1_requested: Option, + __remaining_accounts: Vec, +} + +impl CollectFundFeeBuilder { + pub fn new() -> Self { + Self::default() + } + /// Only admin or fund_owner can collect fee now + #[inline(always)] + pub fn owner(&mut self, owner: solana_address::Address) -> &mut Self { + self.owner = Some(owner); + self + } + #[inline(always)] + pub fn authority(&mut self, authority: solana_address::Address) -> &mut Self { + self.authority = Some(authority); + self + } + /// Pool state stores accumulated protocol fee amount + #[inline(always)] + pub fn pool_state(&mut self, pool_state: solana_address::Address) -> &mut Self { + self.pool_state = Some(pool_state); + self + } + /// Amm config account stores fund_owner + #[inline(always)] + pub fn amm_config(&mut self, amm_config: solana_address::Address) -> &mut Self { + self.amm_config = Some(amm_config); + self + } + /// The address that holds pool tokens for token_0 + #[inline(always)] + pub fn token0_vault(&mut self, token0_vault: solana_address::Address) -> &mut Self { + self.token0_vault = Some(token0_vault); + self + } + /// The address that holds pool tokens for token_1 + #[inline(always)] + pub fn token1_vault(&mut self, token1_vault: solana_address::Address) -> &mut Self { + self.token1_vault = Some(token1_vault); + self + } + /// The mint of token_0 vault + #[inline(always)] + pub fn vault0_mint(&mut self, vault0_mint: solana_address::Address) -> &mut Self { + self.vault0_mint = Some(vault0_mint); + self + } + /// The mint of token_1 vault + #[inline(always)] + pub fn vault1_mint(&mut self, vault1_mint: solana_address::Address) -> &mut Self { + self.vault1_mint = Some(vault1_mint); + self + } + /// The address that receives the collected token_0 fund fees + #[inline(always)] + pub fn recipient_token0_account( + &mut self, + recipient_token0_account: solana_address::Address, + ) -> &mut Self { + self.recipient_token0_account = Some(recipient_token0_account); + self + } + /// The address that receives the collected token_1 fund fees + #[inline(always)] + pub fn recipient_token1_account( + &mut self, + recipient_token1_account: solana_address::Address, + ) -> &mut Self { + self.recipient_token1_account = Some(recipient_token1_account); + self + } + /// `[optional account, default to 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA']` + /// The SPL program to perform token transfers + #[inline(always)] + pub fn token_program(&mut self, token_program: solana_address::Address) -> &mut Self { + self.token_program = Some(token_program); + self + } + /// `[optional account, default to 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb']` + /// The SPL program 2022 to perform token transfers + #[inline(always)] + pub fn token_program2022(&mut self, token_program2022: solana_address::Address) -> &mut Self { + self.token_program2022 = Some(token_program2022); + self + } + #[inline(always)] + pub fn amount0_requested(&mut self, amount0_requested: u64) -> &mut Self { + self.amount0_requested = Some(amount0_requested); + self + } + #[inline(always)] + pub fn amount1_requested(&mut self, amount1_requested: u64) -> &mut Self { + self.amount1_requested = Some(amount1_requested); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account(&mut self, account: solana_instruction::AccountMeta) -> &mut Self { + self.__remaining_accounts.push(account); + self + } + /// Add additional accounts to the instruction. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[solana_instruction::AccountMeta], + ) -> &mut Self { + self.__remaining_accounts.extend_from_slice(accounts); + self + } + #[allow(clippy::clone_on_copy)] + pub fn instruction(&self) -> solana_instruction::Instruction { + let accounts = CollectFundFee { + owner: self.owner.expect("owner is not set"), + authority: self.authority.expect("authority is not set"), + pool_state: self.pool_state.expect("pool_state is not set"), + amm_config: self.amm_config.expect("amm_config is not set"), + token0_vault: self.token0_vault.expect("token0_vault is not set"), + token1_vault: self.token1_vault.expect("token1_vault is not set"), + vault0_mint: self.vault0_mint.expect("vault0_mint is not set"), + vault1_mint: self.vault1_mint.expect("vault1_mint is not set"), + recipient_token0_account: self + .recipient_token0_account + .expect("recipient_token0_account is not set"), + recipient_token1_account: self + .recipient_token1_account + .expect("recipient_token1_account is not set"), + token_program: self.token_program.unwrap_or(solana_address::address!( + "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + )), + token_program2022: self.token_program2022.unwrap_or(solana_address::address!( + "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + )), + }; + let args = CollectFundFeeInstructionArgs { + amount0_requested: self + .amount0_requested + .clone() + .expect("amount0_requested is not set"), + amount1_requested: self + .amount1_requested + .clone() + .expect("amount1_requested is not set"), + }; + + accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) + } +} + +/// `collect_fund_fee` CPI accounts. +pub struct CollectFundFeeCpiAccounts<'a, 'b> { + /// Only admin or fund_owner can collect fee now + pub owner: &'b solana_account_info::AccountInfo<'a>, + + pub authority: &'b solana_account_info::AccountInfo<'a>, + /// Pool state stores accumulated protocol fee amount + pub pool_state: &'b solana_account_info::AccountInfo<'a>, + /// Amm config account stores fund_owner + pub amm_config: &'b solana_account_info::AccountInfo<'a>, + /// The address that holds pool tokens for token_0 + pub token0_vault: &'b solana_account_info::AccountInfo<'a>, + /// The address that holds pool tokens for token_1 + pub token1_vault: &'b solana_account_info::AccountInfo<'a>, + /// The mint of token_0 vault + pub vault0_mint: &'b solana_account_info::AccountInfo<'a>, + /// The mint of token_1 vault + pub vault1_mint: &'b solana_account_info::AccountInfo<'a>, + /// The address that receives the collected token_0 fund fees + pub recipient_token0_account: &'b solana_account_info::AccountInfo<'a>, + /// The address that receives the collected token_1 fund fees + pub recipient_token1_account: &'b solana_account_info::AccountInfo<'a>, + /// The SPL program to perform token transfers + pub token_program: &'b solana_account_info::AccountInfo<'a>, + /// The SPL program 2022 to perform token transfers + pub token_program2022: &'b solana_account_info::AccountInfo<'a>, +} + +/// `collect_fund_fee` CPI instruction. +pub struct CollectFundFeeCpi<'a, 'b> { + /// The program to invoke. + pub __program: &'b solana_account_info::AccountInfo<'a>, + /// Only admin or fund_owner can collect fee now + pub owner: &'b solana_account_info::AccountInfo<'a>, + + pub authority: &'b solana_account_info::AccountInfo<'a>, + /// Pool state stores accumulated protocol fee amount + pub pool_state: &'b solana_account_info::AccountInfo<'a>, + /// Amm config account stores fund_owner + pub amm_config: &'b solana_account_info::AccountInfo<'a>, + /// The address that holds pool tokens for token_0 + pub token0_vault: &'b solana_account_info::AccountInfo<'a>, + /// The address that holds pool tokens for token_1 + pub token1_vault: &'b solana_account_info::AccountInfo<'a>, + /// The mint of token_0 vault + pub vault0_mint: &'b solana_account_info::AccountInfo<'a>, + /// The mint of token_1 vault + pub vault1_mint: &'b solana_account_info::AccountInfo<'a>, + /// The address that receives the collected token_0 fund fees + pub recipient_token0_account: &'b solana_account_info::AccountInfo<'a>, + /// The address that receives the collected token_1 fund fees + pub recipient_token1_account: &'b solana_account_info::AccountInfo<'a>, + /// The SPL program to perform token transfers + pub token_program: &'b solana_account_info::AccountInfo<'a>, + /// The SPL program 2022 to perform token transfers + pub token_program2022: &'b solana_account_info::AccountInfo<'a>, + /// The arguments for the instruction. + pub __args: CollectFundFeeInstructionArgs, +} + +impl<'a, 'b> CollectFundFeeCpi<'a, 'b> { + pub fn new( + program: &'b solana_account_info::AccountInfo<'a>, + accounts: CollectFundFeeCpiAccounts<'a, 'b>, + args: CollectFundFeeInstructionArgs, + ) -> Self { + Self { + __program: program, + owner: accounts.owner, + authority: accounts.authority, + pool_state: accounts.pool_state, + amm_config: accounts.amm_config, + token0_vault: accounts.token0_vault, + token1_vault: accounts.token1_vault, + vault0_mint: accounts.vault0_mint, + vault1_mint: accounts.vault1_mint, + recipient_token0_account: accounts.recipient_token0_account, + recipient_token1_account: accounts.recipient_token1_account, + token_program: accounts.token_program, + token_program2022: accounts.token_program2022, + __args: args, + } + } + #[inline(always)] + pub fn invoke(&self) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], &[]) + } + #[inline(always)] + pub fn invoke_with_remaining_accounts( + &self, + remaining_accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], remaining_accounts) + } + #[inline(always)] + pub fn invoke_signed(&self, signers_seeds: &[&[&[u8]]]) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(signers_seeds, &[]) + } + #[allow(clippy::arithmetic_side_effects)] + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed_with_remaining_accounts( + &self, + signers_seeds: &[&[&[u8]]], + remaining_accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> solana_program_error::ProgramResult { + let mut accounts = Vec::with_capacity(12 + remaining_accounts.len()); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.owner.key, + true, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.authority.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.pool_state.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.amm_config.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.token0_vault.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.token1_vault.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.vault0_mint.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.vault1_mint.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.recipient_token0_account.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.recipient_token1_account.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.token_program.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.token_program2022.key, + false, + )); + remaining_accounts.iter().for_each(|remaining_account| { + accounts.push(solana_instruction::AccountMeta { + pubkey: *remaining_account.0.key, + is_signer: remaining_account.1, + is_writable: remaining_account.2, + }) + }); + let mut data = CollectFundFeeInstructionData::new().try_to_vec().unwrap(); + let mut args = self.__args.try_to_vec().unwrap(); + data.append(&mut args); + + let instruction = solana_instruction::Instruction { + program_id: crate::RAYDIUM_CP_SWAP_ID, + accounts, + data, + }; + let mut account_infos = Vec::with_capacity(13 + remaining_accounts.len()); + account_infos.push(self.__program.clone()); + account_infos.push(self.owner.clone()); + account_infos.push(self.authority.clone()); + account_infos.push(self.pool_state.clone()); + account_infos.push(self.amm_config.clone()); + account_infos.push(self.token0_vault.clone()); + account_infos.push(self.token1_vault.clone()); + account_infos.push(self.vault0_mint.clone()); + account_infos.push(self.vault1_mint.clone()); + account_infos.push(self.recipient_token0_account.clone()); + account_infos.push(self.recipient_token1_account.clone()); + account_infos.push(self.token_program.clone()); + account_infos.push(self.token_program2022.clone()); + remaining_accounts + .iter() + .for_each(|remaining_account| account_infos.push(remaining_account.0.clone())); + + if signers_seeds.is_empty() { + solana_cpi::invoke(&instruction, &account_infos) + } else { + solana_cpi::invoke_signed(&instruction, &account_infos, signers_seeds) + } + } +} + +/// Instruction builder for `CollectFundFee` via CPI. +/// +/// ### Accounts: +/// +/// 0. `[signer]` owner +/// 1. `[]` authority +/// 2. `[writable]` pool_state +/// 3. `[]` amm_config +/// 4. `[writable]` token0_vault +/// 5. `[writable]` token1_vault +/// 6. `[]` vault0_mint +/// 7. `[]` vault1_mint +/// 8. `[writable]` recipient_token0_account +/// 9. `[writable]` recipient_token1_account +/// 10. `[]` token_program +/// 11. `[]` token_program2022 +#[derive(Clone, Debug)] +pub struct CollectFundFeeCpiBuilder<'a, 'b> { + instruction: Box>, +} + +impl<'a, 'b> CollectFundFeeCpiBuilder<'a, 'b> { + pub fn new(program: &'b solana_account_info::AccountInfo<'a>) -> Self { + let instruction = Box::new(CollectFundFeeCpiBuilderInstruction { + __program: program, + owner: None, + authority: None, + pool_state: None, + amm_config: None, + token0_vault: None, + token1_vault: None, + vault0_mint: None, + vault1_mint: None, + recipient_token0_account: None, + recipient_token1_account: None, + token_program: None, + token_program2022: None, + amount0_requested: None, + amount1_requested: None, + __remaining_accounts: Vec::new(), + }); + Self { instruction } + } + /// Only admin or fund_owner can collect fee now + #[inline(always)] + pub fn owner(&mut self, owner: &'b solana_account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.owner = Some(owner); + self + } + #[inline(always)] + pub fn authority(&mut self, authority: &'b solana_account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.authority = Some(authority); + self + } + /// Pool state stores accumulated protocol fee amount + #[inline(always)] + pub fn pool_state( + &mut self, + pool_state: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.pool_state = Some(pool_state); + self + } + /// Amm config account stores fund_owner + #[inline(always)] + pub fn amm_config( + &mut self, + amm_config: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.amm_config = Some(amm_config); + self + } + /// The address that holds pool tokens for token_0 + #[inline(always)] + pub fn token0_vault( + &mut self, + token0_vault: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token0_vault = Some(token0_vault); + self + } + /// The address that holds pool tokens for token_1 + #[inline(always)] + pub fn token1_vault( + &mut self, + token1_vault: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token1_vault = Some(token1_vault); + self + } + /// The mint of token_0 vault + #[inline(always)] + pub fn vault0_mint( + &mut self, + vault0_mint: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.vault0_mint = Some(vault0_mint); + self + } + /// The mint of token_1 vault + #[inline(always)] + pub fn vault1_mint( + &mut self, + vault1_mint: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.vault1_mint = Some(vault1_mint); + self + } + /// The address that receives the collected token_0 fund fees + #[inline(always)] + pub fn recipient_token0_account( + &mut self, + recipient_token0_account: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.recipient_token0_account = Some(recipient_token0_account); + self + } + /// The address that receives the collected token_1 fund fees + #[inline(always)] + pub fn recipient_token1_account( + &mut self, + recipient_token1_account: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.recipient_token1_account = Some(recipient_token1_account); + self + } + /// The SPL program to perform token transfers + #[inline(always)] + pub fn token_program( + &mut self, + token_program: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token_program = Some(token_program); + self + } + /// The SPL program 2022 to perform token transfers + #[inline(always)] + pub fn token_program2022( + &mut self, + token_program2022: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token_program2022 = Some(token_program2022); + self + } + #[inline(always)] + pub fn amount0_requested(&mut self, amount0_requested: u64) -> &mut Self { + self.instruction.amount0_requested = Some(amount0_requested); + self + } + #[inline(always)] + pub fn amount1_requested(&mut self, amount1_requested: u64) -> &mut Self { + self.instruction.amount1_requested = Some(amount1_requested); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account( + &mut self, + account: &'b solana_account_info::AccountInfo<'a>, + is_writable: bool, + is_signer: bool, + ) -> &mut Self { + self.instruction + .__remaining_accounts + .push((account, is_writable, is_signer)); + self + } + /// Add additional accounts to the instruction. + /// + /// Each account is represented by a tuple of the `AccountInfo`, a `bool` indicating whether the account is writable or not, + /// and a `bool` indicating whether the account is a signer or not. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> &mut Self { + self.instruction + .__remaining_accounts + .extend_from_slice(accounts); + self + } + #[inline(always)] + pub fn invoke(&self) -> solana_program_error::ProgramResult { + self.invoke_signed(&[]) + } + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed(&self, signers_seeds: &[&[&[u8]]]) -> solana_program_error::ProgramResult { + let args = CollectFundFeeInstructionArgs { + amount0_requested: self + .instruction + .amount0_requested + .clone() + .expect("amount0_requested is not set"), + amount1_requested: self + .instruction + .amount1_requested + .clone() + .expect("amount1_requested is not set"), + }; + let instruction = CollectFundFeeCpi { + __program: self.instruction.__program, + + owner: self.instruction.owner.expect("owner is not set"), + + authority: self.instruction.authority.expect("authority is not set"), + + pool_state: self.instruction.pool_state.expect("pool_state is not set"), + + amm_config: self.instruction.amm_config.expect("amm_config is not set"), + + token0_vault: self + .instruction + .token0_vault + .expect("token0_vault is not set"), + + token1_vault: self + .instruction + .token1_vault + .expect("token1_vault is not set"), + + vault0_mint: self + .instruction + .vault0_mint + .expect("vault0_mint is not set"), + + vault1_mint: self + .instruction + .vault1_mint + .expect("vault1_mint is not set"), + + recipient_token0_account: self + .instruction + .recipient_token0_account + .expect("recipient_token0_account is not set"), + + recipient_token1_account: self + .instruction + .recipient_token1_account + .expect("recipient_token1_account is not set"), + + token_program: self + .instruction + .token_program + .expect("token_program is not set"), + + token_program2022: self + .instruction + .token_program2022 + .expect("token_program2022 is not set"), + __args: args, + }; + instruction.invoke_signed_with_remaining_accounts( + signers_seeds, + &self.instruction.__remaining_accounts, + ) + } +} + +#[derive(Clone, Debug)] +struct CollectFundFeeCpiBuilderInstruction<'a, 'b> { + __program: &'b solana_account_info::AccountInfo<'a>, + owner: Option<&'b solana_account_info::AccountInfo<'a>>, + authority: Option<&'b solana_account_info::AccountInfo<'a>>, + pool_state: Option<&'b solana_account_info::AccountInfo<'a>>, + amm_config: Option<&'b solana_account_info::AccountInfo<'a>>, + token0_vault: Option<&'b solana_account_info::AccountInfo<'a>>, + token1_vault: Option<&'b solana_account_info::AccountInfo<'a>>, + vault0_mint: Option<&'b solana_account_info::AccountInfo<'a>>, + vault1_mint: Option<&'b solana_account_info::AccountInfo<'a>>, + recipient_token0_account: Option<&'b solana_account_info::AccountInfo<'a>>, + recipient_token1_account: Option<&'b solana_account_info::AccountInfo<'a>>, + token_program: Option<&'b solana_account_info::AccountInfo<'a>>, + token_program2022: Option<&'b solana_account_info::AccountInfo<'a>>, + amount0_requested: Option, + amount1_requested: Option, + /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. + __remaining_accounts: Vec<(&'b solana_account_info::AccountInfo<'a>, bool, bool)>, +} diff --git a/e2e/raydium-cpmm/src/generated/instructions/collect_protocol_fee.rs b/e2e/raydium-cpmm/src/generated/instructions/collect_protocol_fee.rs new file mode 100644 index 0000000..7b4e5f1 --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/instructions/collect_protocol_fee.rs @@ -0,0 +1,810 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +use borsh::BorshDeserialize; +use borsh::BorshSerialize; + +pub const COLLECT_PROTOCOL_FEE_DISCRIMINATOR: [u8; 8] = [136, 136, 252, 221, 194, 66, 126, 89]; + +/// Accounts. +#[derive(Debug)] +pub struct CollectProtocolFee { + /// Only admin or owner can collect fee now + pub owner: solana_address::Address, + + pub authority: solana_address::Address, + /// Pool state stores accumulated protocol fee amount + pub pool_state: solana_address::Address, + /// Amm config account stores owner + pub amm_config: solana_address::Address, + /// The address that holds pool tokens for token_0 + pub token0_vault: solana_address::Address, + /// The address that holds pool tokens for token_1 + pub token1_vault: solana_address::Address, + /// The mint of token_0 vault + pub vault0_mint: solana_address::Address, + /// The mint of token_1 vault + pub vault1_mint: solana_address::Address, + /// The address that receives the collected token_0 protocol fees + pub recipient_token0_account: solana_address::Address, + /// The address that receives the collected token_1 protocol fees + pub recipient_token1_account: solana_address::Address, + /// The SPL program to perform token transfers + pub token_program: solana_address::Address, + /// The SPL program 2022 to perform token transfers + pub token_program2022: solana_address::Address, +} + +impl CollectProtocolFee { + pub fn instruction( + &self, + args: CollectProtocolFeeInstructionArgs, + ) -> solana_instruction::Instruction { + self.instruction_with_remaining_accounts(args, &[]) + } + #[allow(clippy::arithmetic_side_effects)] + #[allow(clippy::vec_init_then_push)] + pub fn instruction_with_remaining_accounts( + &self, + args: CollectProtocolFeeInstructionArgs, + remaining_accounts: &[solana_instruction::AccountMeta], + ) -> solana_instruction::Instruction { + let mut accounts = Vec::with_capacity(12 + remaining_accounts.len()); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.owner, true, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.authority, + false, + )); + accounts.push(solana_instruction::AccountMeta::new(self.pool_state, false)); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.amm_config, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.token0_vault, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.token1_vault, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.vault0_mint, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.vault1_mint, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.recipient_token0_account, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.recipient_token1_account, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.token_program, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.token_program2022, + false, + )); + accounts.extend_from_slice(remaining_accounts); + let mut data = CollectProtocolFeeInstructionData::new() + .try_to_vec() + .unwrap(); + let mut args = args.try_to_vec().unwrap(); + data.append(&mut args); + + solana_instruction::Instruction { + program_id: crate::RAYDIUM_CP_SWAP_ID, + accounts, + data, + } + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct CollectProtocolFeeInstructionData { + discriminator: [u8; 8], +} + +impl CollectProtocolFeeInstructionData { + pub fn new() -> Self { + Self { + discriminator: [136, 136, 252, 221, 194, 66, 126, 89], + } + } + + pub(crate) fn try_to_vec(&self) -> Result, std::io::Error> { + borsh::to_vec(self) + } +} + +impl Default for CollectProtocolFeeInstructionData { + fn default() -> Self { + Self::new() + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct CollectProtocolFeeInstructionArgs { + pub amount0_requested: u64, + pub amount1_requested: u64, +} + +impl CollectProtocolFeeInstructionArgs { + pub(crate) fn try_to_vec(&self) -> Result, std::io::Error> { + borsh::to_vec(self) + } +} + +/// Instruction builder for `CollectProtocolFee`. +/// +/// ### Accounts: +/// +/// 0. `[signer]` owner +/// 1. `[]` authority +/// 2. `[writable]` pool_state +/// 3. `[]` amm_config +/// 4. `[writable]` token0_vault +/// 5. `[writable]` token1_vault +/// 6. `[]` vault0_mint +/// 7. `[]` vault1_mint +/// 8. `[writable]` recipient_token0_account +/// 9. `[writable]` recipient_token1_account +/// 10. `[optional]` token_program (default to `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`) +/// 11. `[optional]` token_program2022 (default to `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb`) +#[derive(Clone, Debug, Default)] +pub struct CollectProtocolFeeBuilder { + owner: Option, + authority: Option, + pool_state: Option, + amm_config: Option, + token0_vault: Option, + token1_vault: Option, + vault0_mint: Option, + vault1_mint: Option, + recipient_token0_account: Option, + recipient_token1_account: Option, + token_program: Option, + token_program2022: Option, + amount0_requested: Option, + amount1_requested: Option, + __remaining_accounts: Vec, +} + +impl CollectProtocolFeeBuilder { + pub fn new() -> Self { + Self::default() + } + /// Only admin or owner can collect fee now + #[inline(always)] + pub fn owner(&mut self, owner: solana_address::Address) -> &mut Self { + self.owner = Some(owner); + self + } + #[inline(always)] + pub fn authority(&mut self, authority: solana_address::Address) -> &mut Self { + self.authority = Some(authority); + self + } + /// Pool state stores accumulated protocol fee amount + #[inline(always)] + pub fn pool_state(&mut self, pool_state: solana_address::Address) -> &mut Self { + self.pool_state = Some(pool_state); + self + } + /// Amm config account stores owner + #[inline(always)] + pub fn amm_config(&mut self, amm_config: solana_address::Address) -> &mut Self { + self.amm_config = Some(amm_config); + self + } + /// The address that holds pool tokens for token_0 + #[inline(always)] + pub fn token0_vault(&mut self, token0_vault: solana_address::Address) -> &mut Self { + self.token0_vault = Some(token0_vault); + self + } + /// The address that holds pool tokens for token_1 + #[inline(always)] + pub fn token1_vault(&mut self, token1_vault: solana_address::Address) -> &mut Self { + self.token1_vault = Some(token1_vault); + self + } + /// The mint of token_0 vault + #[inline(always)] + pub fn vault0_mint(&mut self, vault0_mint: solana_address::Address) -> &mut Self { + self.vault0_mint = Some(vault0_mint); + self + } + /// The mint of token_1 vault + #[inline(always)] + pub fn vault1_mint(&mut self, vault1_mint: solana_address::Address) -> &mut Self { + self.vault1_mint = Some(vault1_mint); + self + } + /// The address that receives the collected token_0 protocol fees + #[inline(always)] + pub fn recipient_token0_account( + &mut self, + recipient_token0_account: solana_address::Address, + ) -> &mut Self { + self.recipient_token0_account = Some(recipient_token0_account); + self + } + /// The address that receives the collected token_1 protocol fees + #[inline(always)] + pub fn recipient_token1_account( + &mut self, + recipient_token1_account: solana_address::Address, + ) -> &mut Self { + self.recipient_token1_account = Some(recipient_token1_account); + self + } + /// `[optional account, default to 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA']` + /// The SPL program to perform token transfers + #[inline(always)] + pub fn token_program(&mut self, token_program: solana_address::Address) -> &mut Self { + self.token_program = Some(token_program); + self + } + /// `[optional account, default to 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb']` + /// The SPL program 2022 to perform token transfers + #[inline(always)] + pub fn token_program2022(&mut self, token_program2022: solana_address::Address) -> &mut Self { + self.token_program2022 = Some(token_program2022); + self + } + #[inline(always)] + pub fn amount0_requested(&mut self, amount0_requested: u64) -> &mut Self { + self.amount0_requested = Some(amount0_requested); + self + } + #[inline(always)] + pub fn amount1_requested(&mut self, amount1_requested: u64) -> &mut Self { + self.amount1_requested = Some(amount1_requested); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account(&mut self, account: solana_instruction::AccountMeta) -> &mut Self { + self.__remaining_accounts.push(account); + self + } + /// Add additional accounts to the instruction. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[solana_instruction::AccountMeta], + ) -> &mut Self { + self.__remaining_accounts.extend_from_slice(accounts); + self + } + #[allow(clippy::clone_on_copy)] + pub fn instruction(&self) -> solana_instruction::Instruction { + let accounts = CollectProtocolFee { + owner: self.owner.expect("owner is not set"), + authority: self.authority.expect("authority is not set"), + pool_state: self.pool_state.expect("pool_state is not set"), + amm_config: self.amm_config.expect("amm_config is not set"), + token0_vault: self.token0_vault.expect("token0_vault is not set"), + token1_vault: self.token1_vault.expect("token1_vault is not set"), + vault0_mint: self.vault0_mint.expect("vault0_mint is not set"), + vault1_mint: self.vault1_mint.expect("vault1_mint is not set"), + recipient_token0_account: self + .recipient_token0_account + .expect("recipient_token0_account is not set"), + recipient_token1_account: self + .recipient_token1_account + .expect("recipient_token1_account is not set"), + token_program: self.token_program.unwrap_or(solana_address::address!( + "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + )), + token_program2022: self.token_program2022.unwrap_or(solana_address::address!( + "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + )), + }; + let args = CollectProtocolFeeInstructionArgs { + amount0_requested: self + .amount0_requested + .clone() + .expect("amount0_requested is not set"), + amount1_requested: self + .amount1_requested + .clone() + .expect("amount1_requested is not set"), + }; + + accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) + } +} + +/// `collect_protocol_fee` CPI accounts. +pub struct CollectProtocolFeeCpiAccounts<'a, 'b> { + /// Only admin or owner can collect fee now + pub owner: &'b solana_account_info::AccountInfo<'a>, + + pub authority: &'b solana_account_info::AccountInfo<'a>, + /// Pool state stores accumulated protocol fee amount + pub pool_state: &'b solana_account_info::AccountInfo<'a>, + /// Amm config account stores owner + pub amm_config: &'b solana_account_info::AccountInfo<'a>, + /// The address that holds pool tokens for token_0 + pub token0_vault: &'b solana_account_info::AccountInfo<'a>, + /// The address that holds pool tokens for token_1 + pub token1_vault: &'b solana_account_info::AccountInfo<'a>, + /// The mint of token_0 vault + pub vault0_mint: &'b solana_account_info::AccountInfo<'a>, + /// The mint of token_1 vault + pub vault1_mint: &'b solana_account_info::AccountInfo<'a>, + /// The address that receives the collected token_0 protocol fees + pub recipient_token0_account: &'b solana_account_info::AccountInfo<'a>, + /// The address that receives the collected token_1 protocol fees + pub recipient_token1_account: &'b solana_account_info::AccountInfo<'a>, + /// The SPL program to perform token transfers + pub token_program: &'b solana_account_info::AccountInfo<'a>, + /// The SPL program 2022 to perform token transfers + pub token_program2022: &'b solana_account_info::AccountInfo<'a>, +} + +/// `collect_protocol_fee` CPI instruction. +pub struct CollectProtocolFeeCpi<'a, 'b> { + /// The program to invoke. + pub __program: &'b solana_account_info::AccountInfo<'a>, + /// Only admin or owner can collect fee now + pub owner: &'b solana_account_info::AccountInfo<'a>, + + pub authority: &'b solana_account_info::AccountInfo<'a>, + /// Pool state stores accumulated protocol fee amount + pub pool_state: &'b solana_account_info::AccountInfo<'a>, + /// Amm config account stores owner + pub amm_config: &'b solana_account_info::AccountInfo<'a>, + /// The address that holds pool tokens for token_0 + pub token0_vault: &'b solana_account_info::AccountInfo<'a>, + /// The address that holds pool tokens for token_1 + pub token1_vault: &'b solana_account_info::AccountInfo<'a>, + /// The mint of token_0 vault + pub vault0_mint: &'b solana_account_info::AccountInfo<'a>, + /// The mint of token_1 vault + pub vault1_mint: &'b solana_account_info::AccountInfo<'a>, + /// The address that receives the collected token_0 protocol fees + pub recipient_token0_account: &'b solana_account_info::AccountInfo<'a>, + /// The address that receives the collected token_1 protocol fees + pub recipient_token1_account: &'b solana_account_info::AccountInfo<'a>, + /// The SPL program to perform token transfers + pub token_program: &'b solana_account_info::AccountInfo<'a>, + /// The SPL program 2022 to perform token transfers + pub token_program2022: &'b solana_account_info::AccountInfo<'a>, + /// The arguments for the instruction. + pub __args: CollectProtocolFeeInstructionArgs, +} + +impl<'a, 'b> CollectProtocolFeeCpi<'a, 'b> { + pub fn new( + program: &'b solana_account_info::AccountInfo<'a>, + accounts: CollectProtocolFeeCpiAccounts<'a, 'b>, + args: CollectProtocolFeeInstructionArgs, + ) -> Self { + Self { + __program: program, + owner: accounts.owner, + authority: accounts.authority, + pool_state: accounts.pool_state, + amm_config: accounts.amm_config, + token0_vault: accounts.token0_vault, + token1_vault: accounts.token1_vault, + vault0_mint: accounts.vault0_mint, + vault1_mint: accounts.vault1_mint, + recipient_token0_account: accounts.recipient_token0_account, + recipient_token1_account: accounts.recipient_token1_account, + token_program: accounts.token_program, + token_program2022: accounts.token_program2022, + __args: args, + } + } + #[inline(always)] + pub fn invoke(&self) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], &[]) + } + #[inline(always)] + pub fn invoke_with_remaining_accounts( + &self, + remaining_accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], remaining_accounts) + } + #[inline(always)] + pub fn invoke_signed(&self, signers_seeds: &[&[&[u8]]]) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(signers_seeds, &[]) + } + #[allow(clippy::arithmetic_side_effects)] + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed_with_remaining_accounts( + &self, + signers_seeds: &[&[&[u8]]], + remaining_accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> solana_program_error::ProgramResult { + let mut accounts = Vec::with_capacity(12 + remaining_accounts.len()); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.owner.key, + true, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.authority.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.pool_state.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.amm_config.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.token0_vault.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.token1_vault.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.vault0_mint.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.vault1_mint.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.recipient_token0_account.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.recipient_token1_account.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.token_program.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.token_program2022.key, + false, + )); + remaining_accounts.iter().for_each(|remaining_account| { + accounts.push(solana_instruction::AccountMeta { + pubkey: *remaining_account.0.key, + is_signer: remaining_account.1, + is_writable: remaining_account.2, + }) + }); + let mut data = CollectProtocolFeeInstructionData::new() + .try_to_vec() + .unwrap(); + let mut args = self.__args.try_to_vec().unwrap(); + data.append(&mut args); + + let instruction = solana_instruction::Instruction { + program_id: crate::RAYDIUM_CP_SWAP_ID, + accounts, + data, + }; + let mut account_infos = Vec::with_capacity(13 + remaining_accounts.len()); + account_infos.push(self.__program.clone()); + account_infos.push(self.owner.clone()); + account_infos.push(self.authority.clone()); + account_infos.push(self.pool_state.clone()); + account_infos.push(self.amm_config.clone()); + account_infos.push(self.token0_vault.clone()); + account_infos.push(self.token1_vault.clone()); + account_infos.push(self.vault0_mint.clone()); + account_infos.push(self.vault1_mint.clone()); + account_infos.push(self.recipient_token0_account.clone()); + account_infos.push(self.recipient_token1_account.clone()); + account_infos.push(self.token_program.clone()); + account_infos.push(self.token_program2022.clone()); + remaining_accounts + .iter() + .for_each(|remaining_account| account_infos.push(remaining_account.0.clone())); + + if signers_seeds.is_empty() { + solana_cpi::invoke(&instruction, &account_infos) + } else { + solana_cpi::invoke_signed(&instruction, &account_infos, signers_seeds) + } + } +} + +/// Instruction builder for `CollectProtocolFee` via CPI. +/// +/// ### Accounts: +/// +/// 0. `[signer]` owner +/// 1. `[]` authority +/// 2. `[writable]` pool_state +/// 3. `[]` amm_config +/// 4. `[writable]` token0_vault +/// 5. `[writable]` token1_vault +/// 6. `[]` vault0_mint +/// 7. `[]` vault1_mint +/// 8. `[writable]` recipient_token0_account +/// 9. `[writable]` recipient_token1_account +/// 10. `[]` token_program +/// 11. `[]` token_program2022 +#[derive(Clone, Debug)] +pub struct CollectProtocolFeeCpiBuilder<'a, 'b> { + instruction: Box>, +} + +impl<'a, 'b> CollectProtocolFeeCpiBuilder<'a, 'b> { + pub fn new(program: &'b solana_account_info::AccountInfo<'a>) -> Self { + let instruction = Box::new(CollectProtocolFeeCpiBuilderInstruction { + __program: program, + owner: None, + authority: None, + pool_state: None, + amm_config: None, + token0_vault: None, + token1_vault: None, + vault0_mint: None, + vault1_mint: None, + recipient_token0_account: None, + recipient_token1_account: None, + token_program: None, + token_program2022: None, + amount0_requested: None, + amount1_requested: None, + __remaining_accounts: Vec::new(), + }); + Self { instruction } + } + /// Only admin or owner can collect fee now + #[inline(always)] + pub fn owner(&mut self, owner: &'b solana_account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.owner = Some(owner); + self + } + #[inline(always)] + pub fn authority(&mut self, authority: &'b solana_account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.authority = Some(authority); + self + } + /// Pool state stores accumulated protocol fee amount + #[inline(always)] + pub fn pool_state( + &mut self, + pool_state: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.pool_state = Some(pool_state); + self + } + /// Amm config account stores owner + #[inline(always)] + pub fn amm_config( + &mut self, + amm_config: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.amm_config = Some(amm_config); + self + } + /// The address that holds pool tokens for token_0 + #[inline(always)] + pub fn token0_vault( + &mut self, + token0_vault: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token0_vault = Some(token0_vault); + self + } + /// The address that holds pool tokens for token_1 + #[inline(always)] + pub fn token1_vault( + &mut self, + token1_vault: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token1_vault = Some(token1_vault); + self + } + /// The mint of token_0 vault + #[inline(always)] + pub fn vault0_mint( + &mut self, + vault0_mint: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.vault0_mint = Some(vault0_mint); + self + } + /// The mint of token_1 vault + #[inline(always)] + pub fn vault1_mint( + &mut self, + vault1_mint: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.vault1_mint = Some(vault1_mint); + self + } + /// The address that receives the collected token_0 protocol fees + #[inline(always)] + pub fn recipient_token0_account( + &mut self, + recipient_token0_account: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.recipient_token0_account = Some(recipient_token0_account); + self + } + /// The address that receives the collected token_1 protocol fees + #[inline(always)] + pub fn recipient_token1_account( + &mut self, + recipient_token1_account: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.recipient_token1_account = Some(recipient_token1_account); + self + } + /// The SPL program to perform token transfers + #[inline(always)] + pub fn token_program( + &mut self, + token_program: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token_program = Some(token_program); + self + } + /// The SPL program 2022 to perform token transfers + #[inline(always)] + pub fn token_program2022( + &mut self, + token_program2022: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token_program2022 = Some(token_program2022); + self + } + #[inline(always)] + pub fn amount0_requested(&mut self, amount0_requested: u64) -> &mut Self { + self.instruction.amount0_requested = Some(amount0_requested); + self + } + #[inline(always)] + pub fn amount1_requested(&mut self, amount1_requested: u64) -> &mut Self { + self.instruction.amount1_requested = Some(amount1_requested); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account( + &mut self, + account: &'b solana_account_info::AccountInfo<'a>, + is_writable: bool, + is_signer: bool, + ) -> &mut Self { + self.instruction + .__remaining_accounts + .push((account, is_writable, is_signer)); + self + } + /// Add additional accounts to the instruction. + /// + /// Each account is represented by a tuple of the `AccountInfo`, a `bool` indicating whether the account is writable or not, + /// and a `bool` indicating whether the account is a signer or not. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> &mut Self { + self.instruction + .__remaining_accounts + .extend_from_slice(accounts); + self + } + #[inline(always)] + pub fn invoke(&self) -> solana_program_error::ProgramResult { + self.invoke_signed(&[]) + } + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed(&self, signers_seeds: &[&[&[u8]]]) -> solana_program_error::ProgramResult { + let args = CollectProtocolFeeInstructionArgs { + amount0_requested: self + .instruction + .amount0_requested + .clone() + .expect("amount0_requested is not set"), + amount1_requested: self + .instruction + .amount1_requested + .clone() + .expect("amount1_requested is not set"), + }; + let instruction = CollectProtocolFeeCpi { + __program: self.instruction.__program, + + owner: self.instruction.owner.expect("owner is not set"), + + authority: self.instruction.authority.expect("authority is not set"), + + pool_state: self.instruction.pool_state.expect("pool_state is not set"), + + amm_config: self.instruction.amm_config.expect("amm_config is not set"), + + token0_vault: self + .instruction + .token0_vault + .expect("token0_vault is not set"), + + token1_vault: self + .instruction + .token1_vault + .expect("token1_vault is not set"), + + vault0_mint: self + .instruction + .vault0_mint + .expect("vault0_mint is not set"), + + vault1_mint: self + .instruction + .vault1_mint + .expect("vault1_mint is not set"), + + recipient_token0_account: self + .instruction + .recipient_token0_account + .expect("recipient_token0_account is not set"), + + recipient_token1_account: self + .instruction + .recipient_token1_account + .expect("recipient_token1_account is not set"), + + token_program: self + .instruction + .token_program + .expect("token_program is not set"), + + token_program2022: self + .instruction + .token_program2022 + .expect("token_program2022 is not set"), + __args: args, + }; + instruction.invoke_signed_with_remaining_accounts( + signers_seeds, + &self.instruction.__remaining_accounts, + ) + } +} + +#[derive(Clone, Debug)] +struct CollectProtocolFeeCpiBuilderInstruction<'a, 'b> { + __program: &'b solana_account_info::AccountInfo<'a>, + owner: Option<&'b solana_account_info::AccountInfo<'a>>, + authority: Option<&'b solana_account_info::AccountInfo<'a>>, + pool_state: Option<&'b solana_account_info::AccountInfo<'a>>, + amm_config: Option<&'b solana_account_info::AccountInfo<'a>>, + token0_vault: Option<&'b solana_account_info::AccountInfo<'a>>, + token1_vault: Option<&'b solana_account_info::AccountInfo<'a>>, + vault0_mint: Option<&'b solana_account_info::AccountInfo<'a>>, + vault1_mint: Option<&'b solana_account_info::AccountInfo<'a>>, + recipient_token0_account: Option<&'b solana_account_info::AccountInfo<'a>>, + recipient_token1_account: Option<&'b solana_account_info::AccountInfo<'a>>, + token_program: Option<&'b solana_account_info::AccountInfo<'a>>, + token_program2022: Option<&'b solana_account_info::AccountInfo<'a>>, + amount0_requested: Option, + amount1_requested: Option, + /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. + __remaining_accounts: Vec<(&'b solana_account_info::AccountInfo<'a>, bool, bool)>, +} diff --git a/e2e/raydium-cpmm/src/generated/instructions/create_amm_config.rs b/e2e/raydium-cpmm/src/generated/instructions/create_amm_config.rs new file mode 100644 index 0000000..0a7b2ce --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/instructions/create_amm_config.rs @@ -0,0 +1,484 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +use borsh::BorshDeserialize; +use borsh::BorshSerialize; + +pub const CREATE_AMM_CONFIG_DISCRIMINATOR: [u8; 8] = [137, 52, 237, 212, 215, 117, 108, 104]; + +/// Accounts. +#[derive(Debug)] +pub struct CreateAmmConfig { + /// Address to be set as protocol owner. + pub owner: solana_address::Address, + /// Initialize config state account to store protocol owner address and fee rates. + pub amm_config: solana_address::Address, + + pub system_program: solana_address::Address, +} + +impl CreateAmmConfig { + pub fn instruction( + &self, + args: CreateAmmConfigInstructionArgs, + ) -> solana_instruction::Instruction { + self.instruction_with_remaining_accounts(args, &[]) + } + #[allow(clippy::arithmetic_side_effects)] + #[allow(clippy::vec_init_then_push)] + pub fn instruction_with_remaining_accounts( + &self, + args: CreateAmmConfigInstructionArgs, + remaining_accounts: &[solana_instruction::AccountMeta], + ) -> solana_instruction::Instruction { + let mut accounts = Vec::with_capacity(3 + remaining_accounts.len()); + accounts.push(solana_instruction::AccountMeta::new(self.owner, true)); + accounts.push(solana_instruction::AccountMeta::new(self.amm_config, false)); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.system_program, + false, + )); + accounts.extend_from_slice(remaining_accounts); + let mut data = CreateAmmConfigInstructionData::new().try_to_vec().unwrap(); + let mut args = args.try_to_vec().unwrap(); + data.append(&mut args); + + solana_instruction::Instruction { + program_id: crate::RAYDIUM_CP_SWAP_ID, + accounts, + data, + } + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct CreateAmmConfigInstructionData { + discriminator: [u8; 8], +} + +impl CreateAmmConfigInstructionData { + pub fn new() -> Self { + Self { + discriminator: [137, 52, 237, 212, 215, 117, 108, 104], + } + } + + pub(crate) fn try_to_vec(&self) -> Result, std::io::Error> { + borsh::to_vec(self) + } +} + +impl Default for CreateAmmConfigInstructionData { + fn default() -> Self { + Self::new() + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct CreateAmmConfigInstructionArgs { + pub index: u16, + pub trade_fee_rate: u64, + pub protocol_fee_rate: u64, + pub fund_fee_rate: u64, + pub create_pool_fee: u64, +} + +impl CreateAmmConfigInstructionArgs { + pub(crate) fn try_to_vec(&self) -> Result, std::io::Error> { + borsh::to_vec(self) + } +} + +/// Instruction builder for `CreateAmmConfig`. +/// +/// ### Accounts: +/// +/// 0. `[writable, signer, optional]` owner (default to `GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ`) +/// 1. `[writable]` amm_config +/// 2. `[optional]` system_program (default to `11111111111111111111111111111111`) +#[derive(Clone, Debug, Default)] +pub struct CreateAmmConfigBuilder { + owner: Option, + amm_config: Option, + system_program: Option, + index: Option, + trade_fee_rate: Option, + protocol_fee_rate: Option, + fund_fee_rate: Option, + create_pool_fee: Option, + __remaining_accounts: Vec, +} + +impl CreateAmmConfigBuilder { + pub fn new() -> Self { + Self::default() + } + /// `[optional account, default to 'GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ']` + /// Address to be set as protocol owner. + #[inline(always)] + pub fn owner(&mut self, owner: solana_address::Address) -> &mut Self { + self.owner = Some(owner); + self + } + /// Initialize config state account to store protocol owner address and fee rates. + #[inline(always)] + pub fn amm_config(&mut self, amm_config: solana_address::Address) -> &mut Self { + self.amm_config = Some(amm_config); + self + } + /// `[optional account, default to '11111111111111111111111111111111']` + #[inline(always)] + pub fn system_program(&mut self, system_program: solana_address::Address) -> &mut Self { + self.system_program = Some(system_program); + self + } + #[inline(always)] + pub fn index(&mut self, index: u16) -> &mut Self { + self.index = Some(index); + self + } + #[inline(always)] + pub fn trade_fee_rate(&mut self, trade_fee_rate: u64) -> &mut Self { + self.trade_fee_rate = Some(trade_fee_rate); + self + } + #[inline(always)] + pub fn protocol_fee_rate(&mut self, protocol_fee_rate: u64) -> &mut Self { + self.protocol_fee_rate = Some(protocol_fee_rate); + self + } + #[inline(always)] + pub fn fund_fee_rate(&mut self, fund_fee_rate: u64) -> &mut Self { + self.fund_fee_rate = Some(fund_fee_rate); + self + } + #[inline(always)] + pub fn create_pool_fee(&mut self, create_pool_fee: u64) -> &mut Self { + self.create_pool_fee = Some(create_pool_fee); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account(&mut self, account: solana_instruction::AccountMeta) -> &mut Self { + self.__remaining_accounts.push(account); + self + } + /// Add additional accounts to the instruction. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[solana_instruction::AccountMeta], + ) -> &mut Self { + self.__remaining_accounts.extend_from_slice(accounts); + self + } + #[allow(clippy::clone_on_copy)] + pub fn instruction(&self) -> solana_instruction::Instruction { + let accounts = CreateAmmConfig { + owner: self.owner.unwrap_or(solana_address::address!( + "GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ" + )), + amm_config: self.amm_config.expect("amm_config is not set"), + system_program: self + .system_program + .unwrap_or(solana_address::address!("11111111111111111111111111111111")), + }; + let args = CreateAmmConfigInstructionArgs { + index: self.index.clone().expect("index is not set"), + trade_fee_rate: self + .trade_fee_rate + .clone() + .expect("trade_fee_rate is not set"), + protocol_fee_rate: self + .protocol_fee_rate + .clone() + .expect("protocol_fee_rate is not set"), + fund_fee_rate: self + .fund_fee_rate + .clone() + .expect("fund_fee_rate is not set"), + create_pool_fee: self + .create_pool_fee + .clone() + .expect("create_pool_fee is not set"), + }; + + accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) + } +} + +/// `create_amm_config` CPI accounts. +pub struct CreateAmmConfigCpiAccounts<'a, 'b> { + /// Address to be set as protocol owner. + pub owner: &'b solana_account_info::AccountInfo<'a>, + /// Initialize config state account to store protocol owner address and fee rates. + pub amm_config: &'b solana_account_info::AccountInfo<'a>, + + pub system_program: &'b solana_account_info::AccountInfo<'a>, +} + +/// `create_amm_config` CPI instruction. +pub struct CreateAmmConfigCpi<'a, 'b> { + /// The program to invoke. + pub __program: &'b solana_account_info::AccountInfo<'a>, + /// Address to be set as protocol owner. + pub owner: &'b solana_account_info::AccountInfo<'a>, + /// Initialize config state account to store protocol owner address and fee rates. + pub amm_config: &'b solana_account_info::AccountInfo<'a>, + + pub system_program: &'b solana_account_info::AccountInfo<'a>, + /// The arguments for the instruction. + pub __args: CreateAmmConfigInstructionArgs, +} + +impl<'a, 'b> CreateAmmConfigCpi<'a, 'b> { + pub fn new( + program: &'b solana_account_info::AccountInfo<'a>, + accounts: CreateAmmConfigCpiAccounts<'a, 'b>, + args: CreateAmmConfigInstructionArgs, + ) -> Self { + Self { + __program: program, + owner: accounts.owner, + amm_config: accounts.amm_config, + system_program: accounts.system_program, + __args: args, + } + } + #[inline(always)] + pub fn invoke(&self) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], &[]) + } + #[inline(always)] + pub fn invoke_with_remaining_accounts( + &self, + remaining_accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], remaining_accounts) + } + #[inline(always)] + pub fn invoke_signed(&self, signers_seeds: &[&[&[u8]]]) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(signers_seeds, &[]) + } + #[allow(clippy::arithmetic_side_effects)] + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed_with_remaining_accounts( + &self, + signers_seeds: &[&[&[u8]]], + remaining_accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> solana_program_error::ProgramResult { + let mut accounts = Vec::with_capacity(3 + remaining_accounts.len()); + accounts.push(solana_instruction::AccountMeta::new(*self.owner.key, true)); + accounts.push(solana_instruction::AccountMeta::new( + *self.amm_config.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.system_program.key, + false, + )); + remaining_accounts.iter().for_each(|remaining_account| { + accounts.push(solana_instruction::AccountMeta { + pubkey: *remaining_account.0.key, + is_signer: remaining_account.1, + is_writable: remaining_account.2, + }) + }); + let mut data = CreateAmmConfigInstructionData::new().try_to_vec().unwrap(); + let mut args = self.__args.try_to_vec().unwrap(); + data.append(&mut args); + + let instruction = solana_instruction::Instruction { + program_id: crate::RAYDIUM_CP_SWAP_ID, + accounts, + data, + }; + let mut account_infos = Vec::with_capacity(4 + remaining_accounts.len()); + account_infos.push(self.__program.clone()); + account_infos.push(self.owner.clone()); + account_infos.push(self.amm_config.clone()); + account_infos.push(self.system_program.clone()); + remaining_accounts + .iter() + .for_each(|remaining_account| account_infos.push(remaining_account.0.clone())); + + if signers_seeds.is_empty() { + solana_cpi::invoke(&instruction, &account_infos) + } else { + solana_cpi::invoke_signed(&instruction, &account_infos, signers_seeds) + } + } +} + +/// Instruction builder for `CreateAmmConfig` via CPI. +/// +/// ### Accounts: +/// +/// 0. `[writable, signer]` owner +/// 1. `[writable]` amm_config +/// 2. `[]` system_program +#[derive(Clone, Debug)] +pub struct CreateAmmConfigCpiBuilder<'a, 'b> { + instruction: Box>, +} + +impl<'a, 'b> CreateAmmConfigCpiBuilder<'a, 'b> { + pub fn new(program: &'b solana_account_info::AccountInfo<'a>) -> Self { + let instruction = Box::new(CreateAmmConfigCpiBuilderInstruction { + __program: program, + owner: None, + amm_config: None, + system_program: None, + index: None, + trade_fee_rate: None, + protocol_fee_rate: None, + fund_fee_rate: None, + create_pool_fee: None, + __remaining_accounts: Vec::new(), + }); + Self { instruction } + } + /// Address to be set as protocol owner. + #[inline(always)] + pub fn owner(&mut self, owner: &'b solana_account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.owner = Some(owner); + self + } + /// Initialize config state account to store protocol owner address and fee rates. + #[inline(always)] + pub fn amm_config( + &mut self, + amm_config: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.amm_config = Some(amm_config); + self + } + #[inline(always)] + pub fn system_program( + &mut self, + system_program: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.system_program = Some(system_program); + self + } + #[inline(always)] + pub fn index(&mut self, index: u16) -> &mut Self { + self.instruction.index = Some(index); + self + } + #[inline(always)] + pub fn trade_fee_rate(&mut self, trade_fee_rate: u64) -> &mut Self { + self.instruction.trade_fee_rate = Some(trade_fee_rate); + self + } + #[inline(always)] + pub fn protocol_fee_rate(&mut self, protocol_fee_rate: u64) -> &mut Self { + self.instruction.protocol_fee_rate = Some(protocol_fee_rate); + self + } + #[inline(always)] + pub fn fund_fee_rate(&mut self, fund_fee_rate: u64) -> &mut Self { + self.instruction.fund_fee_rate = Some(fund_fee_rate); + self + } + #[inline(always)] + pub fn create_pool_fee(&mut self, create_pool_fee: u64) -> &mut Self { + self.instruction.create_pool_fee = Some(create_pool_fee); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account( + &mut self, + account: &'b solana_account_info::AccountInfo<'a>, + is_writable: bool, + is_signer: bool, + ) -> &mut Self { + self.instruction + .__remaining_accounts + .push((account, is_writable, is_signer)); + self + } + /// Add additional accounts to the instruction. + /// + /// Each account is represented by a tuple of the `AccountInfo`, a `bool` indicating whether the account is writable or not, + /// and a `bool` indicating whether the account is a signer or not. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> &mut Self { + self.instruction + .__remaining_accounts + .extend_from_slice(accounts); + self + } + #[inline(always)] + pub fn invoke(&self) -> solana_program_error::ProgramResult { + self.invoke_signed(&[]) + } + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed(&self, signers_seeds: &[&[&[u8]]]) -> solana_program_error::ProgramResult { + let args = CreateAmmConfigInstructionArgs { + index: self.instruction.index.clone().expect("index is not set"), + trade_fee_rate: self + .instruction + .trade_fee_rate + .clone() + .expect("trade_fee_rate is not set"), + protocol_fee_rate: self + .instruction + .protocol_fee_rate + .clone() + .expect("protocol_fee_rate is not set"), + fund_fee_rate: self + .instruction + .fund_fee_rate + .clone() + .expect("fund_fee_rate is not set"), + create_pool_fee: self + .instruction + .create_pool_fee + .clone() + .expect("create_pool_fee is not set"), + }; + let instruction = CreateAmmConfigCpi { + __program: self.instruction.__program, + + owner: self.instruction.owner.expect("owner is not set"), + + amm_config: self.instruction.amm_config.expect("amm_config is not set"), + + system_program: self + .instruction + .system_program + .expect("system_program is not set"), + __args: args, + }; + instruction.invoke_signed_with_remaining_accounts( + signers_seeds, + &self.instruction.__remaining_accounts, + ) + } +} + +#[derive(Clone, Debug)] +struct CreateAmmConfigCpiBuilderInstruction<'a, 'b> { + __program: &'b solana_account_info::AccountInfo<'a>, + owner: Option<&'b solana_account_info::AccountInfo<'a>>, + amm_config: Option<&'b solana_account_info::AccountInfo<'a>>, + system_program: Option<&'b solana_account_info::AccountInfo<'a>>, + index: Option, + trade_fee_rate: Option, + protocol_fee_rate: Option, + fund_fee_rate: Option, + create_pool_fee: Option, + /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. + __remaining_accounts: Vec<(&'b solana_account_info::AccountInfo<'a>, bool, bool)>, +} diff --git a/e2e/raydium-cpmm/src/generated/instructions/deposit.rs b/e2e/raydium-cpmm/src/generated/instructions/deposit.rs new file mode 100644 index 0000000..1645205 --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/instructions/deposit.rs @@ -0,0 +1,850 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +use borsh::BorshDeserialize; +use borsh::BorshSerialize; + +pub const DEPOSIT_DISCRIMINATOR: [u8; 8] = [242, 35, 198, 137, 82, 225, 242, 182]; + +/// Accounts. +#[derive(Debug)] +pub struct Deposit { + /// Pays to mint the position + pub owner: solana_address::Address, + + pub authority: solana_address::Address, + + pub pool_state: solana_address::Address, + /// Owner lp token account + pub owner_lp_token: solana_address::Address, + /// The payer's token account for token_0 + pub token0_account: solana_address::Address, + /// The payer's token account for token_1 + pub token1_account: solana_address::Address, + /// The address that holds pool tokens for token_0 + pub token0_vault: solana_address::Address, + /// The address that holds pool tokens for token_1 + pub token1_vault: solana_address::Address, + /// token Program + pub token_program: solana_address::Address, + /// Token program 2022 + pub token_program2022: solana_address::Address, + /// The mint of token_0 vault + pub vault0_mint: solana_address::Address, + /// The mint of token_1 vault + pub vault1_mint: solana_address::Address, + /// Lp token mint + pub lp_mint: solana_address::Address, +} + +impl Deposit { + pub fn instruction(&self, args: DepositInstructionArgs) -> solana_instruction::Instruction { + self.instruction_with_remaining_accounts(args, &[]) + } + #[allow(clippy::arithmetic_side_effects)] + #[allow(clippy::vec_init_then_push)] + pub fn instruction_with_remaining_accounts( + &self, + args: DepositInstructionArgs, + remaining_accounts: &[solana_instruction::AccountMeta], + ) -> solana_instruction::Instruction { + let mut accounts = Vec::with_capacity(13 + remaining_accounts.len()); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.owner, true, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.authority, + false, + )); + accounts.push(solana_instruction::AccountMeta::new(self.pool_state, false)); + accounts.push(solana_instruction::AccountMeta::new( + self.owner_lp_token, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.token0_account, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.token1_account, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.token0_vault, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.token1_vault, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.token_program, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.token_program2022, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.vault0_mint, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.vault1_mint, + false, + )); + accounts.push(solana_instruction::AccountMeta::new(self.lp_mint, false)); + accounts.extend_from_slice(remaining_accounts); + let mut data = DepositInstructionData::new().try_to_vec().unwrap(); + let mut args = args.try_to_vec().unwrap(); + data.append(&mut args); + + solana_instruction::Instruction { + program_id: crate::RAYDIUM_CP_SWAP_ID, + accounts, + data, + } + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct DepositInstructionData { + discriminator: [u8; 8], +} + +impl DepositInstructionData { + pub fn new() -> Self { + Self { + discriminator: [242, 35, 198, 137, 82, 225, 242, 182], + } + } + + pub(crate) fn try_to_vec(&self) -> Result, std::io::Error> { + borsh::to_vec(self) + } +} + +impl Default for DepositInstructionData { + fn default() -> Self { + Self::new() + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct DepositInstructionArgs { + pub lp_token_amount: u64, + pub maximum_token0_amount: u64, + pub maximum_token1_amount: u64, +} + +impl DepositInstructionArgs { + pub(crate) fn try_to_vec(&self) -> Result, std::io::Error> { + borsh::to_vec(self) + } +} + +/// Instruction builder for `Deposit`. +/// +/// ### Accounts: +/// +/// 0. `[signer]` owner +/// 1. `[]` authority +/// 2. `[writable]` pool_state +/// 3. `[writable]` owner_lp_token +/// 4. `[writable]` token0_account +/// 5. `[writable]` token1_account +/// 6. `[writable]` token0_vault +/// 7. `[writable]` token1_vault +/// 8. `[optional]` token_program (default to `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`) +/// 9. `[optional]` token_program2022 (default to `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb`) +/// 10. `[]` vault0_mint +/// 11. `[]` vault1_mint +/// 12. `[writable]` lp_mint +#[derive(Clone, Debug, Default)] +pub struct DepositBuilder { + owner: Option, + authority: Option, + pool_state: Option, + owner_lp_token: Option, + token0_account: Option, + token1_account: Option, + token0_vault: Option, + token1_vault: Option, + token_program: Option, + token_program2022: Option, + vault0_mint: Option, + vault1_mint: Option, + lp_mint: Option, + lp_token_amount: Option, + maximum_token0_amount: Option, + maximum_token1_amount: Option, + __remaining_accounts: Vec, +} + +impl DepositBuilder { + pub fn new() -> Self { + Self::default() + } + /// Pays to mint the position + #[inline(always)] + pub fn owner(&mut self, owner: solana_address::Address) -> &mut Self { + self.owner = Some(owner); + self + } + #[inline(always)] + pub fn authority(&mut self, authority: solana_address::Address) -> &mut Self { + self.authority = Some(authority); + self + } + #[inline(always)] + pub fn pool_state(&mut self, pool_state: solana_address::Address) -> &mut Self { + self.pool_state = Some(pool_state); + self + } + /// Owner lp token account + #[inline(always)] + pub fn owner_lp_token(&mut self, owner_lp_token: solana_address::Address) -> &mut Self { + self.owner_lp_token = Some(owner_lp_token); + self + } + /// The payer's token account for token_0 + #[inline(always)] + pub fn token0_account(&mut self, token0_account: solana_address::Address) -> &mut Self { + self.token0_account = Some(token0_account); + self + } + /// The payer's token account for token_1 + #[inline(always)] + pub fn token1_account(&mut self, token1_account: solana_address::Address) -> &mut Self { + self.token1_account = Some(token1_account); + self + } + /// The address that holds pool tokens for token_0 + #[inline(always)] + pub fn token0_vault(&mut self, token0_vault: solana_address::Address) -> &mut Self { + self.token0_vault = Some(token0_vault); + self + } + /// The address that holds pool tokens for token_1 + #[inline(always)] + pub fn token1_vault(&mut self, token1_vault: solana_address::Address) -> &mut Self { + self.token1_vault = Some(token1_vault); + self + } + /// `[optional account, default to 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA']` + /// token Program + #[inline(always)] + pub fn token_program(&mut self, token_program: solana_address::Address) -> &mut Self { + self.token_program = Some(token_program); + self + } + /// `[optional account, default to 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb']` + /// Token program 2022 + #[inline(always)] + pub fn token_program2022(&mut self, token_program2022: solana_address::Address) -> &mut Self { + self.token_program2022 = Some(token_program2022); + self + } + /// The mint of token_0 vault + #[inline(always)] + pub fn vault0_mint(&mut self, vault0_mint: solana_address::Address) -> &mut Self { + self.vault0_mint = Some(vault0_mint); + self + } + /// The mint of token_1 vault + #[inline(always)] + pub fn vault1_mint(&mut self, vault1_mint: solana_address::Address) -> &mut Self { + self.vault1_mint = Some(vault1_mint); + self + } + /// Lp token mint + #[inline(always)] + pub fn lp_mint(&mut self, lp_mint: solana_address::Address) -> &mut Self { + self.lp_mint = Some(lp_mint); + self + } + #[inline(always)] + pub fn lp_token_amount(&mut self, lp_token_amount: u64) -> &mut Self { + self.lp_token_amount = Some(lp_token_amount); + self + } + #[inline(always)] + pub fn maximum_token0_amount(&mut self, maximum_token0_amount: u64) -> &mut Self { + self.maximum_token0_amount = Some(maximum_token0_amount); + self + } + #[inline(always)] + pub fn maximum_token1_amount(&mut self, maximum_token1_amount: u64) -> &mut Self { + self.maximum_token1_amount = Some(maximum_token1_amount); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account(&mut self, account: solana_instruction::AccountMeta) -> &mut Self { + self.__remaining_accounts.push(account); + self + } + /// Add additional accounts to the instruction. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[solana_instruction::AccountMeta], + ) -> &mut Self { + self.__remaining_accounts.extend_from_slice(accounts); + self + } + #[allow(clippy::clone_on_copy)] + pub fn instruction(&self) -> solana_instruction::Instruction { + let accounts = Deposit { + owner: self.owner.expect("owner is not set"), + authority: self.authority.expect("authority is not set"), + pool_state: self.pool_state.expect("pool_state is not set"), + owner_lp_token: self.owner_lp_token.expect("owner_lp_token is not set"), + token0_account: self.token0_account.expect("token0_account is not set"), + token1_account: self.token1_account.expect("token1_account is not set"), + token0_vault: self.token0_vault.expect("token0_vault is not set"), + token1_vault: self.token1_vault.expect("token1_vault is not set"), + token_program: self.token_program.unwrap_or(solana_address::address!( + "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + )), + token_program2022: self.token_program2022.unwrap_or(solana_address::address!( + "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + )), + vault0_mint: self.vault0_mint.expect("vault0_mint is not set"), + vault1_mint: self.vault1_mint.expect("vault1_mint is not set"), + lp_mint: self.lp_mint.expect("lp_mint is not set"), + }; + let args = DepositInstructionArgs { + lp_token_amount: self + .lp_token_amount + .clone() + .expect("lp_token_amount is not set"), + maximum_token0_amount: self + .maximum_token0_amount + .clone() + .expect("maximum_token0_amount is not set"), + maximum_token1_amount: self + .maximum_token1_amount + .clone() + .expect("maximum_token1_amount is not set"), + }; + + accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) + } +} + +/// `deposit` CPI accounts. +pub struct DepositCpiAccounts<'a, 'b> { + /// Pays to mint the position + pub owner: &'b solana_account_info::AccountInfo<'a>, + + pub authority: &'b solana_account_info::AccountInfo<'a>, + + pub pool_state: &'b solana_account_info::AccountInfo<'a>, + /// Owner lp token account + pub owner_lp_token: &'b solana_account_info::AccountInfo<'a>, + /// The payer's token account for token_0 + pub token0_account: &'b solana_account_info::AccountInfo<'a>, + /// The payer's token account for token_1 + pub token1_account: &'b solana_account_info::AccountInfo<'a>, + /// The address that holds pool tokens for token_0 + pub token0_vault: &'b solana_account_info::AccountInfo<'a>, + /// The address that holds pool tokens for token_1 + pub token1_vault: &'b solana_account_info::AccountInfo<'a>, + /// token Program + pub token_program: &'b solana_account_info::AccountInfo<'a>, + /// Token program 2022 + pub token_program2022: &'b solana_account_info::AccountInfo<'a>, + /// The mint of token_0 vault + pub vault0_mint: &'b solana_account_info::AccountInfo<'a>, + /// The mint of token_1 vault + pub vault1_mint: &'b solana_account_info::AccountInfo<'a>, + /// Lp token mint + pub lp_mint: &'b solana_account_info::AccountInfo<'a>, +} + +/// `deposit` CPI instruction. +pub struct DepositCpi<'a, 'b> { + /// The program to invoke. + pub __program: &'b solana_account_info::AccountInfo<'a>, + /// Pays to mint the position + pub owner: &'b solana_account_info::AccountInfo<'a>, + + pub authority: &'b solana_account_info::AccountInfo<'a>, + + pub pool_state: &'b solana_account_info::AccountInfo<'a>, + /// Owner lp token account + pub owner_lp_token: &'b solana_account_info::AccountInfo<'a>, + /// The payer's token account for token_0 + pub token0_account: &'b solana_account_info::AccountInfo<'a>, + /// The payer's token account for token_1 + pub token1_account: &'b solana_account_info::AccountInfo<'a>, + /// The address that holds pool tokens for token_0 + pub token0_vault: &'b solana_account_info::AccountInfo<'a>, + /// The address that holds pool tokens for token_1 + pub token1_vault: &'b solana_account_info::AccountInfo<'a>, + /// token Program + pub token_program: &'b solana_account_info::AccountInfo<'a>, + /// Token program 2022 + pub token_program2022: &'b solana_account_info::AccountInfo<'a>, + /// The mint of token_0 vault + pub vault0_mint: &'b solana_account_info::AccountInfo<'a>, + /// The mint of token_1 vault + pub vault1_mint: &'b solana_account_info::AccountInfo<'a>, + /// Lp token mint + pub lp_mint: &'b solana_account_info::AccountInfo<'a>, + /// The arguments for the instruction. + pub __args: DepositInstructionArgs, +} + +impl<'a, 'b> DepositCpi<'a, 'b> { + pub fn new( + program: &'b solana_account_info::AccountInfo<'a>, + accounts: DepositCpiAccounts<'a, 'b>, + args: DepositInstructionArgs, + ) -> Self { + Self { + __program: program, + owner: accounts.owner, + authority: accounts.authority, + pool_state: accounts.pool_state, + owner_lp_token: accounts.owner_lp_token, + token0_account: accounts.token0_account, + token1_account: accounts.token1_account, + token0_vault: accounts.token0_vault, + token1_vault: accounts.token1_vault, + token_program: accounts.token_program, + token_program2022: accounts.token_program2022, + vault0_mint: accounts.vault0_mint, + vault1_mint: accounts.vault1_mint, + lp_mint: accounts.lp_mint, + __args: args, + } + } + #[inline(always)] + pub fn invoke(&self) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], &[]) + } + #[inline(always)] + pub fn invoke_with_remaining_accounts( + &self, + remaining_accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], remaining_accounts) + } + #[inline(always)] + pub fn invoke_signed(&self, signers_seeds: &[&[&[u8]]]) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(signers_seeds, &[]) + } + #[allow(clippy::arithmetic_side_effects)] + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed_with_remaining_accounts( + &self, + signers_seeds: &[&[&[u8]]], + remaining_accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> solana_program_error::ProgramResult { + let mut accounts = Vec::with_capacity(13 + remaining_accounts.len()); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.owner.key, + true, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.authority.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.pool_state.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.owner_lp_token.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.token0_account.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.token1_account.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.token0_vault.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.token1_vault.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.token_program.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.token_program2022.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.vault0_mint.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.vault1_mint.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.lp_mint.key, + false, + )); + remaining_accounts.iter().for_each(|remaining_account| { + accounts.push(solana_instruction::AccountMeta { + pubkey: *remaining_account.0.key, + is_signer: remaining_account.1, + is_writable: remaining_account.2, + }) + }); + let mut data = DepositInstructionData::new().try_to_vec().unwrap(); + let mut args = self.__args.try_to_vec().unwrap(); + data.append(&mut args); + + let instruction = solana_instruction::Instruction { + program_id: crate::RAYDIUM_CP_SWAP_ID, + accounts, + data, + }; + let mut account_infos = Vec::with_capacity(14 + remaining_accounts.len()); + account_infos.push(self.__program.clone()); + account_infos.push(self.owner.clone()); + account_infos.push(self.authority.clone()); + account_infos.push(self.pool_state.clone()); + account_infos.push(self.owner_lp_token.clone()); + account_infos.push(self.token0_account.clone()); + account_infos.push(self.token1_account.clone()); + account_infos.push(self.token0_vault.clone()); + account_infos.push(self.token1_vault.clone()); + account_infos.push(self.token_program.clone()); + account_infos.push(self.token_program2022.clone()); + account_infos.push(self.vault0_mint.clone()); + account_infos.push(self.vault1_mint.clone()); + account_infos.push(self.lp_mint.clone()); + remaining_accounts + .iter() + .for_each(|remaining_account| account_infos.push(remaining_account.0.clone())); + + if signers_seeds.is_empty() { + solana_cpi::invoke(&instruction, &account_infos) + } else { + solana_cpi::invoke_signed(&instruction, &account_infos, signers_seeds) + } + } +} + +/// Instruction builder for `Deposit` via CPI. +/// +/// ### Accounts: +/// +/// 0. `[signer]` owner +/// 1. `[]` authority +/// 2. `[writable]` pool_state +/// 3. `[writable]` owner_lp_token +/// 4. `[writable]` token0_account +/// 5. `[writable]` token1_account +/// 6. `[writable]` token0_vault +/// 7. `[writable]` token1_vault +/// 8. `[]` token_program +/// 9. `[]` token_program2022 +/// 10. `[]` vault0_mint +/// 11. `[]` vault1_mint +/// 12. `[writable]` lp_mint +#[derive(Clone, Debug)] +pub struct DepositCpiBuilder<'a, 'b> { + instruction: Box>, +} + +impl<'a, 'b> DepositCpiBuilder<'a, 'b> { + pub fn new(program: &'b solana_account_info::AccountInfo<'a>) -> Self { + let instruction = Box::new(DepositCpiBuilderInstruction { + __program: program, + owner: None, + authority: None, + pool_state: None, + owner_lp_token: None, + token0_account: None, + token1_account: None, + token0_vault: None, + token1_vault: None, + token_program: None, + token_program2022: None, + vault0_mint: None, + vault1_mint: None, + lp_mint: None, + lp_token_amount: None, + maximum_token0_amount: None, + maximum_token1_amount: None, + __remaining_accounts: Vec::new(), + }); + Self { instruction } + } + /// Pays to mint the position + #[inline(always)] + pub fn owner(&mut self, owner: &'b solana_account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.owner = Some(owner); + self + } + #[inline(always)] + pub fn authority(&mut self, authority: &'b solana_account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.authority = Some(authority); + self + } + #[inline(always)] + pub fn pool_state( + &mut self, + pool_state: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.pool_state = Some(pool_state); + self + } + /// Owner lp token account + #[inline(always)] + pub fn owner_lp_token( + &mut self, + owner_lp_token: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.owner_lp_token = Some(owner_lp_token); + self + } + /// The payer's token account for token_0 + #[inline(always)] + pub fn token0_account( + &mut self, + token0_account: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token0_account = Some(token0_account); + self + } + /// The payer's token account for token_1 + #[inline(always)] + pub fn token1_account( + &mut self, + token1_account: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token1_account = Some(token1_account); + self + } + /// The address that holds pool tokens for token_0 + #[inline(always)] + pub fn token0_vault( + &mut self, + token0_vault: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token0_vault = Some(token0_vault); + self + } + /// The address that holds pool tokens for token_1 + #[inline(always)] + pub fn token1_vault( + &mut self, + token1_vault: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token1_vault = Some(token1_vault); + self + } + /// token Program + #[inline(always)] + pub fn token_program( + &mut self, + token_program: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token_program = Some(token_program); + self + } + /// Token program 2022 + #[inline(always)] + pub fn token_program2022( + &mut self, + token_program2022: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token_program2022 = Some(token_program2022); + self + } + /// The mint of token_0 vault + #[inline(always)] + pub fn vault0_mint( + &mut self, + vault0_mint: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.vault0_mint = Some(vault0_mint); + self + } + /// The mint of token_1 vault + #[inline(always)] + pub fn vault1_mint( + &mut self, + vault1_mint: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.vault1_mint = Some(vault1_mint); + self + } + /// Lp token mint + #[inline(always)] + pub fn lp_mint(&mut self, lp_mint: &'b solana_account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.lp_mint = Some(lp_mint); + self + } + #[inline(always)] + pub fn lp_token_amount(&mut self, lp_token_amount: u64) -> &mut Self { + self.instruction.lp_token_amount = Some(lp_token_amount); + self + } + #[inline(always)] + pub fn maximum_token0_amount(&mut self, maximum_token0_amount: u64) -> &mut Self { + self.instruction.maximum_token0_amount = Some(maximum_token0_amount); + self + } + #[inline(always)] + pub fn maximum_token1_amount(&mut self, maximum_token1_amount: u64) -> &mut Self { + self.instruction.maximum_token1_amount = Some(maximum_token1_amount); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account( + &mut self, + account: &'b solana_account_info::AccountInfo<'a>, + is_writable: bool, + is_signer: bool, + ) -> &mut Self { + self.instruction + .__remaining_accounts + .push((account, is_writable, is_signer)); + self + } + /// Add additional accounts to the instruction. + /// + /// Each account is represented by a tuple of the `AccountInfo`, a `bool` indicating whether the account is writable or not, + /// and a `bool` indicating whether the account is a signer or not. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> &mut Self { + self.instruction + .__remaining_accounts + .extend_from_slice(accounts); + self + } + #[inline(always)] + pub fn invoke(&self) -> solana_program_error::ProgramResult { + self.invoke_signed(&[]) + } + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed(&self, signers_seeds: &[&[&[u8]]]) -> solana_program_error::ProgramResult { + let args = DepositInstructionArgs { + lp_token_amount: self + .instruction + .lp_token_amount + .clone() + .expect("lp_token_amount is not set"), + maximum_token0_amount: self + .instruction + .maximum_token0_amount + .clone() + .expect("maximum_token0_amount is not set"), + maximum_token1_amount: self + .instruction + .maximum_token1_amount + .clone() + .expect("maximum_token1_amount is not set"), + }; + let instruction = DepositCpi { + __program: self.instruction.__program, + + owner: self.instruction.owner.expect("owner is not set"), + + authority: self.instruction.authority.expect("authority is not set"), + + pool_state: self.instruction.pool_state.expect("pool_state is not set"), + + owner_lp_token: self + .instruction + .owner_lp_token + .expect("owner_lp_token is not set"), + + token0_account: self + .instruction + .token0_account + .expect("token0_account is not set"), + + token1_account: self + .instruction + .token1_account + .expect("token1_account is not set"), + + token0_vault: self + .instruction + .token0_vault + .expect("token0_vault is not set"), + + token1_vault: self + .instruction + .token1_vault + .expect("token1_vault is not set"), + + token_program: self + .instruction + .token_program + .expect("token_program is not set"), + + token_program2022: self + .instruction + .token_program2022 + .expect("token_program2022 is not set"), + + vault0_mint: self + .instruction + .vault0_mint + .expect("vault0_mint is not set"), + + vault1_mint: self + .instruction + .vault1_mint + .expect("vault1_mint is not set"), + + lp_mint: self.instruction.lp_mint.expect("lp_mint is not set"), + __args: args, + }; + instruction.invoke_signed_with_remaining_accounts( + signers_seeds, + &self.instruction.__remaining_accounts, + ) + } +} + +#[derive(Clone, Debug)] +struct DepositCpiBuilderInstruction<'a, 'b> { + __program: &'b solana_account_info::AccountInfo<'a>, + owner: Option<&'b solana_account_info::AccountInfo<'a>>, + authority: Option<&'b solana_account_info::AccountInfo<'a>>, + pool_state: Option<&'b solana_account_info::AccountInfo<'a>>, + owner_lp_token: Option<&'b solana_account_info::AccountInfo<'a>>, + token0_account: Option<&'b solana_account_info::AccountInfo<'a>>, + token1_account: Option<&'b solana_account_info::AccountInfo<'a>>, + token0_vault: Option<&'b solana_account_info::AccountInfo<'a>>, + token1_vault: Option<&'b solana_account_info::AccountInfo<'a>>, + token_program: Option<&'b solana_account_info::AccountInfo<'a>>, + token_program2022: Option<&'b solana_account_info::AccountInfo<'a>>, + vault0_mint: Option<&'b solana_account_info::AccountInfo<'a>>, + vault1_mint: Option<&'b solana_account_info::AccountInfo<'a>>, + lp_mint: Option<&'b solana_account_info::AccountInfo<'a>>, + lp_token_amount: Option, + maximum_token0_amount: Option, + maximum_token1_amount: Option, + /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. + __remaining_accounts: Vec<(&'b solana_account_info::AccountInfo<'a>, bool, bool)>, +} diff --git a/e2e/raydium-cpmm/src/generated/instructions/initialize.rs b/e2e/raydium-cpmm/src/generated/instructions/initialize.rs new file mode 100644 index 0000000..b7c4e2c --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/instructions/initialize.rs @@ -0,0 +1,1177 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +use borsh::BorshDeserialize; +use borsh::BorshSerialize; + +pub const INITIALIZE_DISCRIMINATOR: [u8; 8] = [175, 175, 109, 31, 13, 152, 155, 237]; + +/// Accounts. +#[derive(Debug)] +pub struct Initialize { + /// Address paying to create the pool. Can be anyone + pub creator: solana_address::Address, + /// Which config the pool belongs to. + pub amm_config: solana_address::Address, + /// pool vault and lp mint authority + pub authority: solana_address::Address, + /// PDA account: + /// seeds = [ + /// POOL_SEED.as_bytes(), + /// amm_config.key().as_ref(), + /// token_0_mint.key().as_ref(), + /// token_1_mint.key().as_ref(), + /// ], + /// + /// Or random account: must be signed by cli + pub pool_state: solana_address::Address, + /// Token_0 mint, the key must smaller than token_1 mint. + pub token0_mint: solana_address::Address, + /// Token_1 mint, the key must grater then token_0 mint. + pub token1_mint: solana_address::Address, + /// pool lp mint + pub lp_mint: solana_address::Address, + /// payer token0 account + pub creator_token0: solana_address::Address, + /// creator token1 account + pub creator_token1: solana_address::Address, + /// creator lp token account + pub creator_lp_token: solana_address::Address, + + pub token0_vault: solana_address::Address, + + pub token1_vault: solana_address::Address, + /// create pool fee account + pub create_pool_fee: solana_address::Address, + /// an account to store oracle observations + pub observation_state: solana_address::Address, + /// Program to create mint account and mint tokens + pub token_program: solana_address::Address, + /// Spl token program or token program 2022 + pub token0_program: solana_address::Address, + /// Spl token program or token program 2022 + pub token1_program: solana_address::Address, + /// Program to create an ATA for receiving position NFT + pub associated_token_program: solana_address::Address, + /// To create a new program account + pub system_program: solana_address::Address, + /// Sysvar for program account + pub rent: solana_address::Address, +} + +impl Initialize { + pub fn instruction(&self, args: InitializeInstructionArgs) -> solana_instruction::Instruction { + self.instruction_with_remaining_accounts(args, &[]) + } + #[allow(clippy::arithmetic_side_effects)] + #[allow(clippy::vec_init_then_push)] + pub fn instruction_with_remaining_accounts( + &self, + args: InitializeInstructionArgs, + remaining_accounts: &[solana_instruction::AccountMeta], + ) -> solana_instruction::Instruction { + let mut accounts = Vec::with_capacity(20 + remaining_accounts.len()); + accounts.push(solana_instruction::AccountMeta::new(self.creator, true)); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.amm_config, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.authority, + false, + )); + accounts.push(solana_instruction::AccountMeta::new(self.pool_state, false)); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.token0_mint, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.token1_mint, + false, + )); + accounts.push(solana_instruction::AccountMeta::new(self.lp_mint, false)); + accounts.push(solana_instruction::AccountMeta::new( + self.creator_token0, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.creator_token1, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.creator_lp_token, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.token0_vault, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.token1_vault, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.create_pool_fee, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.observation_state, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.token_program, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.token0_program, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.token1_program, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.associated_token_program, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.system_program, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.rent, false, + )); + accounts.extend_from_slice(remaining_accounts); + let mut data = InitializeInstructionData::new().try_to_vec().unwrap(); + let mut args = args.try_to_vec().unwrap(); + data.append(&mut args); + + solana_instruction::Instruction { + program_id: crate::RAYDIUM_CP_SWAP_ID, + accounts, + data, + } + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct InitializeInstructionData { + discriminator: [u8; 8], +} + +impl InitializeInstructionData { + pub fn new() -> Self { + Self { + discriminator: [175, 175, 109, 31, 13, 152, 155, 237], + } + } + + pub(crate) fn try_to_vec(&self) -> Result, std::io::Error> { + borsh::to_vec(self) + } +} + +impl Default for InitializeInstructionData { + fn default() -> Self { + Self::new() + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct InitializeInstructionArgs { + pub init_amount0: u64, + pub init_amount1: u64, + pub open_time: u64, +} + +impl InitializeInstructionArgs { + pub(crate) fn try_to_vec(&self) -> Result, std::io::Error> { + borsh::to_vec(self) + } +} + +/// Instruction builder for `Initialize`. +/// +/// ### Accounts: +/// +/// 0. `[writable, signer]` creator +/// 1. `[]` amm_config +/// 2. `[]` authority +/// 3. `[writable]` pool_state +/// 4. `[]` token0_mint +/// 5. `[]` token1_mint +/// 6. `[writable]` lp_mint +/// 7. `[writable]` creator_token0 +/// 8. `[writable]` creator_token1 +/// 9. `[writable]` creator_lp_token +/// 10. `[writable]` token0_vault +/// 11. `[writable]` token1_vault +/// 12. `[writable, optional]` create_pool_fee (default to `DNXgeM9EiiaAbaWvwjHj9fQQLAX5ZsfHyvmYUNRAdNC8`) +/// 13. `[writable]` observation_state +/// 14. `[optional]` token_program (default to `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`) +/// 15. `[]` token0_program +/// 16. `[]` token1_program +/// 17. `[optional]` associated_token_program (default to `ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL`) +/// 18. `[optional]` system_program (default to `11111111111111111111111111111111`) +/// 19. `[optional]` rent (default to `SysvarRent111111111111111111111111111111111`) +#[derive(Clone, Debug, Default)] +pub struct InitializeBuilder { + creator: Option, + amm_config: Option, + authority: Option, + pool_state: Option, + token0_mint: Option, + token1_mint: Option, + lp_mint: Option, + creator_token0: Option, + creator_token1: Option, + creator_lp_token: Option, + token0_vault: Option, + token1_vault: Option, + create_pool_fee: Option, + observation_state: Option, + token_program: Option, + token0_program: Option, + token1_program: Option, + associated_token_program: Option, + system_program: Option, + rent: Option, + init_amount0: Option, + init_amount1: Option, + open_time: Option, + __remaining_accounts: Vec, +} + +impl InitializeBuilder { + pub fn new() -> Self { + Self::default() + } + /// Address paying to create the pool. Can be anyone + #[inline(always)] + pub fn creator(&mut self, creator: solana_address::Address) -> &mut Self { + self.creator = Some(creator); + self + } + /// Which config the pool belongs to. + #[inline(always)] + pub fn amm_config(&mut self, amm_config: solana_address::Address) -> &mut Self { + self.amm_config = Some(amm_config); + self + } + /// pool vault and lp mint authority + #[inline(always)] + pub fn authority(&mut self, authority: solana_address::Address) -> &mut Self { + self.authority = Some(authority); + self + } + /// PDA account: + /// seeds = [ + /// POOL_SEED.as_bytes(), + /// amm_config.key().as_ref(), + /// token_0_mint.key().as_ref(), + /// token_1_mint.key().as_ref(), + /// ], + /// + /// Or random account: must be signed by cli + #[inline(always)] + pub fn pool_state(&mut self, pool_state: solana_address::Address) -> &mut Self { + self.pool_state = Some(pool_state); + self + } + /// Token_0 mint, the key must smaller than token_1 mint. + #[inline(always)] + pub fn token0_mint(&mut self, token0_mint: solana_address::Address) -> &mut Self { + self.token0_mint = Some(token0_mint); + self + } + /// Token_1 mint, the key must grater then token_0 mint. + #[inline(always)] + pub fn token1_mint(&mut self, token1_mint: solana_address::Address) -> &mut Self { + self.token1_mint = Some(token1_mint); + self + } + /// pool lp mint + #[inline(always)] + pub fn lp_mint(&mut self, lp_mint: solana_address::Address) -> &mut Self { + self.lp_mint = Some(lp_mint); + self + } + /// payer token0 account + #[inline(always)] + pub fn creator_token0(&mut self, creator_token0: solana_address::Address) -> &mut Self { + self.creator_token0 = Some(creator_token0); + self + } + /// creator token1 account + #[inline(always)] + pub fn creator_token1(&mut self, creator_token1: solana_address::Address) -> &mut Self { + self.creator_token1 = Some(creator_token1); + self + } + /// creator lp token account + #[inline(always)] + pub fn creator_lp_token(&mut self, creator_lp_token: solana_address::Address) -> &mut Self { + self.creator_lp_token = Some(creator_lp_token); + self + } + #[inline(always)] + pub fn token0_vault(&mut self, token0_vault: solana_address::Address) -> &mut Self { + self.token0_vault = Some(token0_vault); + self + } + #[inline(always)] + pub fn token1_vault(&mut self, token1_vault: solana_address::Address) -> &mut Self { + self.token1_vault = Some(token1_vault); + self + } + /// `[optional account, default to 'DNXgeM9EiiaAbaWvwjHj9fQQLAX5ZsfHyvmYUNRAdNC8']` + /// create pool fee account + #[inline(always)] + pub fn create_pool_fee(&mut self, create_pool_fee: solana_address::Address) -> &mut Self { + self.create_pool_fee = Some(create_pool_fee); + self + } + /// an account to store oracle observations + #[inline(always)] + pub fn observation_state(&mut self, observation_state: solana_address::Address) -> &mut Self { + self.observation_state = Some(observation_state); + self + } + /// `[optional account, default to 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA']` + /// Program to create mint account and mint tokens + #[inline(always)] + pub fn token_program(&mut self, token_program: solana_address::Address) -> &mut Self { + self.token_program = Some(token_program); + self + } + /// Spl token program or token program 2022 + #[inline(always)] + pub fn token0_program(&mut self, token0_program: solana_address::Address) -> &mut Self { + self.token0_program = Some(token0_program); + self + } + /// Spl token program or token program 2022 + #[inline(always)] + pub fn token1_program(&mut self, token1_program: solana_address::Address) -> &mut Self { + self.token1_program = Some(token1_program); + self + } + /// `[optional account, default to 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL']` + /// Program to create an ATA for receiving position NFT + #[inline(always)] + pub fn associated_token_program( + &mut self, + associated_token_program: solana_address::Address, + ) -> &mut Self { + self.associated_token_program = Some(associated_token_program); + self + } + /// `[optional account, default to '11111111111111111111111111111111']` + /// To create a new program account + #[inline(always)] + pub fn system_program(&mut self, system_program: solana_address::Address) -> &mut Self { + self.system_program = Some(system_program); + self + } + /// `[optional account, default to 'SysvarRent111111111111111111111111111111111']` + /// Sysvar for program account + #[inline(always)] + pub fn rent(&mut self, rent: solana_address::Address) -> &mut Self { + self.rent = Some(rent); + self + } + #[inline(always)] + pub fn init_amount0(&mut self, init_amount0: u64) -> &mut Self { + self.init_amount0 = Some(init_amount0); + self + } + #[inline(always)] + pub fn init_amount1(&mut self, init_amount1: u64) -> &mut Self { + self.init_amount1 = Some(init_amount1); + self + } + #[inline(always)] + pub fn open_time(&mut self, open_time: u64) -> &mut Self { + self.open_time = Some(open_time); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account(&mut self, account: solana_instruction::AccountMeta) -> &mut Self { + self.__remaining_accounts.push(account); + self + } + /// Add additional accounts to the instruction. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[solana_instruction::AccountMeta], + ) -> &mut Self { + self.__remaining_accounts.extend_from_slice(accounts); + self + } + #[allow(clippy::clone_on_copy)] + pub fn instruction(&self) -> solana_instruction::Instruction { + let accounts = Initialize { + creator: self.creator.expect("creator is not set"), + amm_config: self.amm_config.expect("amm_config is not set"), + authority: self.authority.expect("authority is not set"), + pool_state: self.pool_state.expect("pool_state is not set"), + token0_mint: self.token0_mint.expect("token0_mint is not set"), + token1_mint: self.token1_mint.expect("token1_mint is not set"), + lp_mint: self.lp_mint.expect("lp_mint is not set"), + creator_token0: self.creator_token0.expect("creator_token0 is not set"), + creator_token1: self.creator_token1.expect("creator_token1 is not set"), + creator_lp_token: self.creator_lp_token.expect("creator_lp_token is not set"), + token0_vault: self.token0_vault.expect("token0_vault is not set"), + token1_vault: self.token1_vault.expect("token1_vault is not set"), + create_pool_fee: self.create_pool_fee.unwrap_or(solana_address::address!( + "DNXgeM9EiiaAbaWvwjHj9fQQLAX5ZsfHyvmYUNRAdNC8" + )), + observation_state: self + .observation_state + .expect("observation_state is not set"), + token_program: self.token_program.unwrap_or(solana_address::address!( + "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + )), + token0_program: self.token0_program.expect("token0_program is not set"), + token1_program: self.token1_program.expect("token1_program is not set"), + associated_token_program: self.associated_token_program.unwrap_or( + solana_address::address!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"), + ), + system_program: self + .system_program + .unwrap_or(solana_address::address!("11111111111111111111111111111111")), + rent: self.rent.unwrap_or(solana_address::address!( + "SysvarRent111111111111111111111111111111111" + )), + }; + let args = InitializeInstructionArgs { + init_amount0: self.init_amount0.clone().expect("init_amount0 is not set"), + init_amount1: self.init_amount1.clone().expect("init_amount1 is not set"), + open_time: self.open_time.clone().expect("open_time is not set"), + }; + + accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) + } +} + +/// `initialize` CPI accounts. +pub struct InitializeCpiAccounts<'a, 'b> { + /// Address paying to create the pool. Can be anyone + pub creator: &'b solana_account_info::AccountInfo<'a>, + /// Which config the pool belongs to. + pub amm_config: &'b solana_account_info::AccountInfo<'a>, + /// pool vault and lp mint authority + pub authority: &'b solana_account_info::AccountInfo<'a>, + /// PDA account: + /// seeds = [ + /// POOL_SEED.as_bytes(), + /// amm_config.key().as_ref(), + /// token_0_mint.key().as_ref(), + /// token_1_mint.key().as_ref(), + /// ], + /// + /// Or random account: must be signed by cli + pub pool_state: &'b solana_account_info::AccountInfo<'a>, + /// Token_0 mint, the key must smaller than token_1 mint. + pub token0_mint: &'b solana_account_info::AccountInfo<'a>, + /// Token_1 mint, the key must grater then token_0 mint. + pub token1_mint: &'b solana_account_info::AccountInfo<'a>, + /// pool lp mint + pub lp_mint: &'b solana_account_info::AccountInfo<'a>, + /// payer token0 account + pub creator_token0: &'b solana_account_info::AccountInfo<'a>, + /// creator token1 account + pub creator_token1: &'b solana_account_info::AccountInfo<'a>, + /// creator lp token account + pub creator_lp_token: &'b solana_account_info::AccountInfo<'a>, + + pub token0_vault: &'b solana_account_info::AccountInfo<'a>, + + pub token1_vault: &'b solana_account_info::AccountInfo<'a>, + /// create pool fee account + pub create_pool_fee: &'b solana_account_info::AccountInfo<'a>, + /// an account to store oracle observations + pub observation_state: &'b solana_account_info::AccountInfo<'a>, + /// Program to create mint account and mint tokens + pub token_program: &'b solana_account_info::AccountInfo<'a>, + /// Spl token program or token program 2022 + pub token0_program: &'b solana_account_info::AccountInfo<'a>, + /// Spl token program or token program 2022 + pub token1_program: &'b solana_account_info::AccountInfo<'a>, + /// Program to create an ATA for receiving position NFT + pub associated_token_program: &'b solana_account_info::AccountInfo<'a>, + /// To create a new program account + pub system_program: &'b solana_account_info::AccountInfo<'a>, + /// Sysvar for program account + pub rent: &'b solana_account_info::AccountInfo<'a>, +} + +/// `initialize` CPI instruction. +pub struct InitializeCpi<'a, 'b> { + /// The program to invoke. + pub __program: &'b solana_account_info::AccountInfo<'a>, + /// Address paying to create the pool. Can be anyone + pub creator: &'b solana_account_info::AccountInfo<'a>, + /// Which config the pool belongs to. + pub amm_config: &'b solana_account_info::AccountInfo<'a>, + /// pool vault and lp mint authority + pub authority: &'b solana_account_info::AccountInfo<'a>, + /// PDA account: + /// seeds = [ + /// POOL_SEED.as_bytes(), + /// amm_config.key().as_ref(), + /// token_0_mint.key().as_ref(), + /// token_1_mint.key().as_ref(), + /// ], + /// + /// Or random account: must be signed by cli + pub pool_state: &'b solana_account_info::AccountInfo<'a>, + /// Token_0 mint, the key must smaller than token_1 mint. + pub token0_mint: &'b solana_account_info::AccountInfo<'a>, + /// Token_1 mint, the key must grater then token_0 mint. + pub token1_mint: &'b solana_account_info::AccountInfo<'a>, + /// pool lp mint + pub lp_mint: &'b solana_account_info::AccountInfo<'a>, + /// payer token0 account + pub creator_token0: &'b solana_account_info::AccountInfo<'a>, + /// creator token1 account + pub creator_token1: &'b solana_account_info::AccountInfo<'a>, + /// creator lp token account + pub creator_lp_token: &'b solana_account_info::AccountInfo<'a>, + + pub token0_vault: &'b solana_account_info::AccountInfo<'a>, + + pub token1_vault: &'b solana_account_info::AccountInfo<'a>, + /// create pool fee account + pub create_pool_fee: &'b solana_account_info::AccountInfo<'a>, + /// an account to store oracle observations + pub observation_state: &'b solana_account_info::AccountInfo<'a>, + /// Program to create mint account and mint tokens + pub token_program: &'b solana_account_info::AccountInfo<'a>, + /// Spl token program or token program 2022 + pub token0_program: &'b solana_account_info::AccountInfo<'a>, + /// Spl token program or token program 2022 + pub token1_program: &'b solana_account_info::AccountInfo<'a>, + /// Program to create an ATA for receiving position NFT + pub associated_token_program: &'b solana_account_info::AccountInfo<'a>, + /// To create a new program account + pub system_program: &'b solana_account_info::AccountInfo<'a>, + /// Sysvar for program account + pub rent: &'b solana_account_info::AccountInfo<'a>, + /// The arguments for the instruction. + pub __args: InitializeInstructionArgs, +} + +impl<'a, 'b> InitializeCpi<'a, 'b> { + pub fn new( + program: &'b solana_account_info::AccountInfo<'a>, + accounts: InitializeCpiAccounts<'a, 'b>, + args: InitializeInstructionArgs, + ) -> Self { + Self { + __program: program, + creator: accounts.creator, + amm_config: accounts.amm_config, + authority: accounts.authority, + pool_state: accounts.pool_state, + token0_mint: accounts.token0_mint, + token1_mint: accounts.token1_mint, + lp_mint: accounts.lp_mint, + creator_token0: accounts.creator_token0, + creator_token1: accounts.creator_token1, + creator_lp_token: accounts.creator_lp_token, + token0_vault: accounts.token0_vault, + token1_vault: accounts.token1_vault, + create_pool_fee: accounts.create_pool_fee, + observation_state: accounts.observation_state, + token_program: accounts.token_program, + token0_program: accounts.token0_program, + token1_program: accounts.token1_program, + associated_token_program: accounts.associated_token_program, + system_program: accounts.system_program, + rent: accounts.rent, + __args: args, + } + } + #[inline(always)] + pub fn invoke(&self) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], &[]) + } + #[inline(always)] + pub fn invoke_with_remaining_accounts( + &self, + remaining_accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], remaining_accounts) + } + #[inline(always)] + pub fn invoke_signed(&self, signers_seeds: &[&[&[u8]]]) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(signers_seeds, &[]) + } + #[allow(clippy::arithmetic_side_effects)] + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed_with_remaining_accounts( + &self, + signers_seeds: &[&[&[u8]]], + remaining_accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> solana_program_error::ProgramResult { + let mut accounts = Vec::with_capacity(20 + remaining_accounts.len()); + accounts.push(solana_instruction::AccountMeta::new( + *self.creator.key, + true, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.amm_config.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.authority.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.pool_state.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.token0_mint.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.token1_mint.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.lp_mint.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.creator_token0.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.creator_token1.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.creator_lp_token.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.token0_vault.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.token1_vault.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.create_pool_fee.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.observation_state.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.token_program.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.token0_program.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.token1_program.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.associated_token_program.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.system_program.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.rent.key, + false, + )); + remaining_accounts.iter().for_each(|remaining_account| { + accounts.push(solana_instruction::AccountMeta { + pubkey: *remaining_account.0.key, + is_signer: remaining_account.1, + is_writable: remaining_account.2, + }) + }); + let mut data = InitializeInstructionData::new().try_to_vec().unwrap(); + let mut args = self.__args.try_to_vec().unwrap(); + data.append(&mut args); + + let instruction = solana_instruction::Instruction { + program_id: crate::RAYDIUM_CP_SWAP_ID, + accounts, + data, + }; + let mut account_infos = Vec::with_capacity(21 + remaining_accounts.len()); + account_infos.push(self.__program.clone()); + account_infos.push(self.creator.clone()); + account_infos.push(self.amm_config.clone()); + account_infos.push(self.authority.clone()); + account_infos.push(self.pool_state.clone()); + account_infos.push(self.token0_mint.clone()); + account_infos.push(self.token1_mint.clone()); + account_infos.push(self.lp_mint.clone()); + account_infos.push(self.creator_token0.clone()); + account_infos.push(self.creator_token1.clone()); + account_infos.push(self.creator_lp_token.clone()); + account_infos.push(self.token0_vault.clone()); + account_infos.push(self.token1_vault.clone()); + account_infos.push(self.create_pool_fee.clone()); + account_infos.push(self.observation_state.clone()); + account_infos.push(self.token_program.clone()); + account_infos.push(self.token0_program.clone()); + account_infos.push(self.token1_program.clone()); + account_infos.push(self.associated_token_program.clone()); + account_infos.push(self.system_program.clone()); + account_infos.push(self.rent.clone()); + remaining_accounts + .iter() + .for_each(|remaining_account| account_infos.push(remaining_account.0.clone())); + + if signers_seeds.is_empty() { + solana_cpi::invoke(&instruction, &account_infos) + } else { + solana_cpi::invoke_signed(&instruction, &account_infos, signers_seeds) + } + } +} + +/// Instruction builder for `Initialize` via CPI. +/// +/// ### Accounts: +/// +/// 0. `[writable, signer]` creator +/// 1. `[]` amm_config +/// 2. `[]` authority +/// 3. `[writable]` pool_state +/// 4. `[]` token0_mint +/// 5. `[]` token1_mint +/// 6. `[writable]` lp_mint +/// 7. `[writable]` creator_token0 +/// 8. `[writable]` creator_token1 +/// 9. `[writable]` creator_lp_token +/// 10. `[writable]` token0_vault +/// 11. `[writable]` token1_vault +/// 12. `[writable]` create_pool_fee +/// 13. `[writable]` observation_state +/// 14. `[]` token_program +/// 15. `[]` token0_program +/// 16. `[]` token1_program +/// 17. `[]` associated_token_program +/// 18. `[]` system_program +/// 19. `[]` rent +#[derive(Clone, Debug)] +pub struct InitializeCpiBuilder<'a, 'b> { + instruction: Box>, +} + +impl<'a, 'b> InitializeCpiBuilder<'a, 'b> { + pub fn new(program: &'b solana_account_info::AccountInfo<'a>) -> Self { + let instruction = Box::new(InitializeCpiBuilderInstruction { + __program: program, + creator: None, + amm_config: None, + authority: None, + pool_state: None, + token0_mint: None, + token1_mint: None, + lp_mint: None, + creator_token0: None, + creator_token1: None, + creator_lp_token: None, + token0_vault: None, + token1_vault: None, + create_pool_fee: None, + observation_state: None, + token_program: None, + token0_program: None, + token1_program: None, + associated_token_program: None, + system_program: None, + rent: None, + init_amount0: None, + init_amount1: None, + open_time: None, + __remaining_accounts: Vec::new(), + }); + Self { instruction } + } + /// Address paying to create the pool. Can be anyone + #[inline(always)] + pub fn creator(&mut self, creator: &'b solana_account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.creator = Some(creator); + self + } + /// Which config the pool belongs to. + #[inline(always)] + pub fn amm_config( + &mut self, + amm_config: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.amm_config = Some(amm_config); + self + } + /// pool vault and lp mint authority + #[inline(always)] + pub fn authority(&mut self, authority: &'b solana_account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.authority = Some(authority); + self + } + /// PDA account: + /// seeds = [ + /// POOL_SEED.as_bytes(), + /// amm_config.key().as_ref(), + /// token_0_mint.key().as_ref(), + /// token_1_mint.key().as_ref(), + /// ], + /// + /// Or random account: must be signed by cli + #[inline(always)] + pub fn pool_state( + &mut self, + pool_state: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.pool_state = Some(pool_state); + self + } + /// Token_0 mint, the key must smaller than token_1 mint. + #[inline(always)] + pub fn token0_mint( + &mut self, + token0_mint: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token0_mint = Some(token0_mint); + self + } + /// Token_1 mint, the key must grater then token_0 mint. + #[inline(always)] + pub fn token1_mint( + &mut self, + token1_mint: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token1_mint = Some(token1_mint); + self + } + /// pool lp mint + #[inline(always)] + pub fn lp_mint(&mut self, lp_mint: &'b solana_account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.lp_mint = Some(lp_mint); + self + } + /// payer token0 account + #[inline(always)] + pub fn creator_token0( + &mut self, + creator_token0: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.creator_token0 = Some(creator_token0); + self + } + /// creator token1 account + #[inline(always)] + pub fn creator_token1( + &mut self, + creator_token1: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.creator_token1 = Some(creator_token1); + self + } + /// creator lp token account + #[inline(always)] + pub fn creator_lp_token( + &mut self, + creator_lp_token: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.creator_lp_token = Some(creator_lp_token); + self + } + #[inline(always)] + pub fn token0_vault( + &mut self, + token0_vault: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token0_vault = Some(token0_vault); + self + } + #[inline(always)] + pub fn token1_vault( + &mut self, + token1_vault: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token1_vault = Some(token1_vault); + self + } + /// create pool fee account + #[inline(always)] + pub fn create_pool_fee( + &mut self, + create_pool_fee: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.create_pool_fee = Some(create_pool_fee); + self + } + /// an account to store oracle observations + #[inline(always)] + pub fn observation_state( + &mut self, + observation_state: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.observation_state = Some(observation_state); + self + } + /// Program to create mint account and mint tokens + #[inline(always)] + pub fn token_program( + &mut self, + token_program: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token_program = Some(token_program); + self + } + /// Spl token program or token program 2022 + #[inline(always)] + pub fn token0_program( + &mut self, + token0_program: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token0_program = Some(token0_program); + self + } + /// Spl token program or token program 2022 + #[inline(always)] + pub fn token1_program( + &mut self, + token1_program: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token1_program = Some(token1_program); + self + } + /// Program to create an ATA for receiving position NFT + #[inline(always)] + pub fn associated_token_program( + &mut self, + associated_token_program: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.associated_token_program = Some(associated_token_program); + self + } + /// To create a new program account + #[inline(always)] + pub fn system_program( + &mut self, + system_program: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.system_program = Some(system_program); + self + } + /// Sysvar for program account + #[inline(always)] + pub fn rent(&mut self, rent: &'b solana_account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.rent = Some(rent); + self + } + #[inline(always)] + pub fn init_amount0(&mut self, init_amount0: u64) -> &mut Self { + self.instruction.init_amount0 = Some(init_amount0); + self + } + #[inline(always)] + pub fn init_amount1(&mut self, init_amount1: u64) -> &mut Self { + self.instruction.init_amount1 = Some(init_amount1); + self + } + #[inline(always)] + pub fn open_time(&mut self, open_time: u64) -> &mut Self { + self.instruction.open_time = Some(open_time); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account( + &mut self, + account: &'b solana_account_info::AccountInfo<'a>, + is_writable: bool, + is_signer: bool, + ) -> &mut Self { + self.instruction + .__remaining_accounts + .push((account, is_writable, is_signer)); + self + } + /// Add additional accounts to the instruction. + /// + /// Each account is represented by a tuple of the `AccountInfo`, a `bool` indicating whether the account is writable or not, + /// and a `bool` indicating whether the account is a signer or not. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> &mut Self { + self.instruction + .__remaining_accounts + .extend_from_slice(accounts); + self + } + #[inline(always)] + pub fn invoke(&self) -> solana_program_error::ProgramResult { + self.invoke_signed(&[]) + } + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed(&self, signers_seeds: &[&[&[u8]]]) -> solana_program_error::ProgramResult { + let args = InitializeInstructionArgs { + init_amount0: self + .instruction + .init_amount0 + .clone() + .expect("init_amount0 is not set"), + init_amount1: self + .instruction + .init_amount1 + .clone() + .expect("init_amount1 is not set"), + open_time: self + .instruction + .open_time + .clone() + .expect("open_time is not set"), + }; + let instruction = InitializeCpi { + __program: self.instruction.__program, + + creator: self.instruction.creator.expect("creator is not set"), + + amm_config: self.instruction.amm_config.expect("amm_config is not set"), + + authority: self.instruction.authority.expect("authority is not set"), + + pool_state: self.instruction.pool_state.expect("pool_state is not set"), + + token0_mint: self + .instruction + .token0_mint + .expect("token0_mint is not set"), + + token1_mint: self + .instruction + .token1_mint + .expect("token1_mint is not set"), + + lp_mint: self.instruction.lp_mint.expect("lp_mint is not set"), + + creator_token0: self + .instruction + .creator_token0 + .expect("creator_token0 is not set"), + + creator_token1: self + .instruction + .creator_token1 + .expect("creator_token1 is not set"), + + creator_lp_token: self + .instruction + .creator_lp_token + .expect("creator_lp_token is not set"), + + token0_vault: self + .instruction + .token0_vault + .expect("token0_vault is not set"), + + token1_vault: self + .instruction + .token1_vault + .expect("token1_vault is not set"), + + create_pool_fee: self + .instruction + .create_pool_fee + .expect("create_pool_fee is not set"), + + observation_state: self + .instruction + .observation_state + .expect("observation_state is not set"), + + token_program: self + .instruction + .token_program + .expect("token_program is not set"), + + token0_program: self + .instruction + .token0_program + .expect("token0_program is not set"), + + token1_program: self + .instruction + .token1_program + .expect("token1_program is not set"), + + associated_token_program: self + .instruction + .associated_token_program + .expect("associated_token_program is not set"), + + system_program: self + .instruction + .system_program + .expect("system_program is not set"), + + rent: self.instruction.rent.expect("rent is not set"), + __args: args, + }; + instruction.invoke_signed_with_remaining_accounts( + signers_seeds, + &self.instruction.__remaining_accounts, + ) + } +} + +#[derive(Clone, Debug)] +struct InitializeCpiBuilderInstruction<'a, 'b> { + __program: &'b solana_account_info::AccountInfo<'a>, + creator: Option<&'b solana_account_info::AccountInfo<'a>>, + amm_config: Option<&'b solana_account_info::AccountInfo<'a>>, + authority: Option<&'b solana_account_info::AccountInfo<'a>>, + pool_state: Option<&'b solana_account_info::AccountInfo<'a>>, + token0_mint: Option<&'b solana_account_info::AccountInfo<'a>>, + token1_mint: Option<&'b solana_account_info::AccountInfo<'a>>, + lp_mint: Option<&'b solana_account_info::AccountInfo<'a>>, + creator_token0: Option<&'b solana_account_info::AccountInfo<'a>>, + creator_token1: Option<&'b solana_account_info::AccountInfo<'a>>, + creator_lp_token: Option<&'b solana_account_info::AccountInfo<'a>>, + token0_vault: Option<&'b solana_account_info::AccountInfo<'a>>, + token1_vault: Option<&'b solana_account_info::AccountInfo<'a>>, + create_pool_fee: Option<&'b solana_account_info::AccountInfo<'a>>, + observation_state: Option<&'b solana_account_info::AccountInfo<'a>>, + token_program: Option<&'b solana_account_info::AccountInfo<'a>>, + token0_program: Option<&'b solana_account_info::AccountInfo<'a>>, + token1_program: Option<&'b solana_account_info::AccountInfo<'a>>, + associated_token_program: Option<&'b solana_account_info::AccountInfo<'a>>, + system_program: Option<&'b solana_account_info::AccountInfo<'a>>, + rent: Option<&'b solana_account_info::AccountInfo<'a>>, + init_amount0: Option, + init_amount1: Option, + open_time: Option, + /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. + __remaining_accounts: Vec<(&'b solana_account_info::AccountInfo<'a>, bool, bool)>, +} diff --git a/e2e/raydium-cpmm/src/generated/instructions/mod.rs b/e2e/raydium-cpmm/src/generated/instructions/mod.rs new file mode 100644 index 0000000..3520d1a --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/instructions/mod.rs @@ -0,0 +1,28 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +pub(crate) mod r#collect_fund_fee; +pub(crate) mod r#collect_protocol_fee; +pub(crate) mod r#create_amm_config; +pub(crate) mod r#deposit; +pub(crate) mod r#initialize; +pub(crate) mod r#swap_base_input; +pub(crate) mod r#swap_base_output; +pub(crate) mod r#update_amm_config; +pub(crate) mod r#update_pool_status; +pub(crate) mod r#withdraw; + +pub use self::r#collect_fund_fee::*; +pub use self::r#collect_protocol_fee::*; +pub use self::r#create_amm_config::*; +pub use self::r#deposit::*; +pub use self::r#initialize::*; +pub use self::r#swap_base_input::*; +pub use self::r#swap_base_output::*; +pub use self::r#update_amm_config::*; +pub use self::r#update_pool_status::*; +pub use self::r#withdraw::*; diff --git a/e2e/raydium-cpmm/src/generated/instructions/swap_base_input.rs b/e2e/raydium-cpmm/src/generated/instructions/swap_base_input.rs new file mode 100644 index 0000000..910a24c --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/instructions/swap_base_input.rs @@ -0,0 +1,853 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +use borsh::BorshDeserialize; +use borsh::BorshSerialize; + +pub const SWAP_BASE_INPUT_DISCRIMINATOR: [u8; 8] = [143, 190, 90, 218, 196, 30, 51, 222]; + +/// Accounts. +#[derive(Debug)] +pub struct SwapBaseInput { + /// The user performing the swap + pub payer: solana_address::Address, + + pub authority: solana_address::Address, + /// The factory state to read protocol fees + pub amm_config: solana_address::Address, + /// The program account of the pool in which the swap will be performed + pub pool_state: solana_address::Address, + /// The user token account for input token + pub input_token_account: solana_address::Address, + /// The user token account for output token + pub output_token_account: solana_address::Address, + /// The vault token account for input token + pub input_vault: solana_address::Address, + /// The vault token account for output token + pub output_vault: solana_address::Address, + /// SPL program for input token transfers + pub input_token_program: solana_address::Address, + /// SPL program for output token transfers + pub output_token_program: solana_address::Address, + /// The mint of input token + pub input_token_mint: solana_address::Address, + /// The mint of output token + pub output_token_mint: solana_address::Address, + /// The program account for the most recent oracle observation + pub observation_state: solana_address::Address, +} + +impl SwapBaseInput { + pub fn instruction( + &self, + args: SwapBaseInputInstructionArgs, + ) -> solana_instruction::Instruction { + self.instruction_with_remaining_accounts(args, &[]) + } + #[allow(clippy::arithmetic_side_effects)] + #[allow(clippy::vec_init_then_push)] + pub fn instruction_with_remaining_accounts( + &self, + args: SwapBaseInputInstructionArgs, + remaining_accounts: &[solana_instruction::AccountMeta], + ) -> solana_instruction::Instruction { + let mut accounts = Vec::with_capacity(13 + remaining_accounts.len()); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.payer, true, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.authority, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.amm_config, + false, + )); + accounts.push(solana_instruction::AccountMeta::new(self.pool_state, false)); + accounts.push(solana_instruction::AccountMeta::new( + self.input_token_account, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.output_token_account, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.input_vault, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.output_vault, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.input_token_program, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.output_token_program, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.input_token_mint, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.output_token_mint, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.observation_state, + false, + )); + accounts.extend_from_slice(remaining_accounts); + let mut data = SwapBaseInputInstructionData::new().try_to_vec().unwrap(); + let mut args = args.try_to_vec().unwrap(); + data.append(&mut args); + + solana_instruction::Instruction { + program_id: crate::RAYDIUM_CP_SWAP_ID, + accounts, + data, + } + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct SwapBaseInputInstructionData { + discriminator: [u8; 8], +} + +impl SwapBaseInputInstructionData { + pub fn new() -> Self { + Self { + discriminator: [143, 190, 90, 218, 196, 30, 51, 222], + } + } + + pub(crate) fn try_to_vec(&self) -> Result, std::io::Error> { + borsh::to_vec(self) + } +} + +impl Default for SwapBaseInputInstructionData { + fn default() -> Self { + Self::new() + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct SwapBaseInputInstructionArgs { + pub amount_in: u64, + pub minimum_amount_out: u64, +} + +impl SwapBaseInputInstructionArgs { + pub(crate) fn try_to_vec(&self) -> Result, std::io::Error> { + borsh::to_vec(self) + } +} + +/// Instruction builder for `SwapBaseInput`. +/// +/// ### Accounts: +/// +/// 0. `[signer]` payer +/// 1. `[]` authority +/// 2. `[]` amm_config +/// 3. `[writable]` pool_state +/// 4. `[writable]` input_token_account +/// 5. `[writable]` output_token_account +/// 6. `[writable]` input_vault +/// 7. `[writable]` output_vault +/// 8. `[]` input_token_program +/// 9. `[]` output_token_program +/// 10. `[]` input_token_mint +/// 11. `[]` output_token_mint +/// 12. `[writable]` observation_state +#[derive(Clone, Debug, Default)] +pub struct SwapBaseInputBuilder { + payer: Option, + authority: Option, + amm_config: Option, + pool_state: Option, + input_token_account: Option, + output_token_account: Option, + input_vault: Option, + output_vault: Option, + input_token_program: Option, + output_token_program: Option, + input_token_mint: Option, + output_token_mint: Option, + observation_state: Option, + amount_in: Option, + minimum_amount_out: Option, + __remaining_accounts: Vec, +} + +impl SwapBaseInputBuilder { + pub fn new() -> Self { + Self::default() + } + /// The user performing the swap + #[inline(always)] + pub fn payer(&mut self, payer: solana_address::Address) -> &mut Self { + self.payer = Some(payer); + self + } + #[inline(always)] + pub fn authority(&mut self, authority: solana_address::Address) -> &mut Self { + self.authority = Some(authority); + self + } + /// The factory state to read protocol fees + #[inline(always)] + pub fn amm_config(&mut self, amm_config: solana_address::Address) -> &mut Self { + self.amm_config = Some(amm_config); + self + } + /// The program account of the pool in which the swap will be performed + #[inline(always)] + pub fn pool_state(&mut self, pool_state: solana_address::Address) -> &mut Self { + self.pool_state = Some(pool_state); + self + } + /// The user token account for input token + #[inline(always)] + pub fn input_token_account( + &mut self, + input_token_account: solana_address::Address, + ) -> &mut Self { + self.input_token_account = Some(input_token_account); + self + } + /// The user token account for output token + #[inline(always)] + pub fn output_token_account( + &mut self, + output_token_account: solana_address::Address, + ) -> &mut Self { + self.output_token_account = Some(output_token_account); + self + } + /// The vault token account for input token + #[inline(always)] + pub fn input_vault(&mut self, input_vault: solana_address::Address) -> &mut Self { + self.input_vault = Some(input_vault); + self + } + /// The vault token account for output token + #[inline(always)] + pub fn output_vault(&mut self, output_vault: solana_address::Address) -> &mut Self { + self.output_vault = Some(output_vault); + self + } + /// SPL program for input token transfers + #[inline(always)] + pub fn input_token_program( + &mut self, + input_token_program: solana_address::Address, + ) -> &mut Self { + self.input_token_program = Some(input_token_program); + self + } + /// SPL program for output token transfers + #[inline(always)] + pub fn output_token_program( + &mut self, + output_token_program: solana_address::Address, + ) -> &mut Self { + self.output_token_program = Some(output_token_program); + self + } + /// The mint of input token + #[inline(always)] + pub fn input_token_mint(&mut self, input_token_mint: solana_address::Address) -> &mut Self { + self.input_token_mint = Some(input_token_mint); + self + } + /// The mint of output token + #[inline(always)] + pub fn output_token_mint(&mut self, output_token_mint: solana_address::Address) -> &mut Self { + self.output_token_mint = Some(output_token_mint); + self + } + /// The program account for the most recent oracle observation + #[inline(always)] + pub fn observation_state(&mut self, observation_state: solana_address::Address) -> &mut Self { + self.observation_state = Some(observation_state); + self + } + #[inline(always)] + pub fn amount_in(&mut self, amount_in: u64) -> &mut Self { + self.amount_in = Some(amount_in); + self + } + #[inline(always)] + pub fn minimum_amount_out(&mut self, minimum_amount_out: u64) -> &mut Self { + self.minimum_amount_out = Some(minimum_amount_out); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account(&mut self, account: solana_instruction::AccountMeta) -> &mut Self { + self.__remaining_accounts.push(account); + self + } + /// Add additional accounts to the instruction. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[solana_instruction::AccountMeta], + ) -> &mut Self { + self.__remaining_accounts.extend_from_slice(accounts); + self + } + #[allow(clippy::clone_on_copy)] + pub fn instruction(&self) -> solana_instruction::Instruction { + let accounts = SwapBaseInput { + payer: self.payer.expect("payer is not set"), + authority: self.authority.expect("authority is not set"), + amm_config: self.amm_config.expect("amm_config is not set"), + pool_state: self.pool_state.expect("pool_state is not set"), + input_token_account: self + .input_token_account + .expect("input_token_account is not set"), + output_token_account: self + .output_token_account + .expect("output_token_account is not set"), + input_vault: self.input_vault.expect("input_vault is not set"), + output_vault: self.output_vault.expect("output_vault is not set"), + input_token_program: self + .input_token_program + .expect("input_token_program is not set"), + output_token_program: self + .output_token_program + .expect("output_token_program is not set"), + input_token_mint: self.input_token_mint.expect("input_token_mint is not set"), + output_token_mint: self + .output_token_mint + .expect("output_token_mint is not set"), + observation_state: self + .observation_state + .expect("observation_state is not set"), + }; + let args = SwapBaseInputInstructionArgs { + amount_in: self.amount_in.clone().expect("amount_in is not set"), + minimum_amount_out: self + .minimum_amount_out + .clone() + .expect("minimum_amount_out is not set"), + }; + + accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) + } +} + +/// `swap_base_input` CPI accounts. +pub struct SwapBaseInputCpiAccounts<'a, 'b> { + /// The user performing the swap + pub payer: &'b solana_account_info::AccountInfo<'a>, + + pub authority: &'b solana_account_info::AccountInfo<'a>, + /// The factory state to read protocol fees + pub amm_config: &'b solana_account_info::AccountInfo<'a>, + /// The program account of the pool in which the swap will be performed + pub pool_state: &'b solana_account_info::AccountInfo<'a>, + /// The user token account for input token + pub input_token_account: &'b solana_account_info::AccountInfo<'a>, + /// The user token account for output token + pub output_token_account: &'b solana_account_info::AccountInfo<'a>, + /// The vault token account for input token + pub input_vault: &'b solana_account_info::AccountInfo<'a>, + /// The vault token account for output token + pub output_vault: &'b solana_account_info::AccountInfo<'a>, + /// SPL program for input token transfers + pub input_token_program: &'b solana_account_info::AccountInfo<'a>, + /// SPL program for output token transfers + pub output_token_program: &'b solana_account_info::AccountInfo<'a>, + /// The mint of input token + pub input_token_mint: &'b solana_account_info::AccountInfo<'a>, + /// The mint of output token + pub output_token_mint: &'b solana_account_info::AccountInfo<'a>, + /// The program account for the most recent oracle observation + pub observation_state: &'b solana_account_info::AccountInfo<'a>, +} + +/// `swap_base_input` CPI instruction. +pub struct SwapBaseInputCpi<'a, 'b> { + /// The program to invoke. + pub __program: &'b solana_account_info::AccountInfo<'a>, + /// The user performing the swap + pub payer: &'b solana_account_info::AccountInfo<'a>, + + pub authority: &'b solana_account_info::AccountInfo<'a>, + /// The factory state to read protocol fees + pub amm_config: &'b solana_account_info::AccountInfo<'a>, + /// The program account of the pool in which the swap will be performed + pub pool_state: &'b solana_account_info::AccountInfo<'a>, + /// The user token account for input token + pub input_token_account: &'b solana_account_info::AccountInfo<'a>, + /// The user token account for output token + pub output_token_account: &'b solana_account_info::AccountInfo<'a>, + /// The vault token account for input token + pub input_vault: &'b solana_account_info::AccountInfo<'a>, + /// The vault token account for output token + pub output_vault: &'b solana_account_info::AccountInfo<'a>, + /// SPL program for input token transfers + pub input_token_program: &'b solana_account_info::AccountInfo<'a>, + /// SPL program for output token transfers + pub output_token_program: &'b solana_account_info::AccountInfo<'a>, + /// The mint of input token + pub input_token_mint: &'b solana_account_info::AccountInfo<'a>, + /// The mint of output token + pub output_token_mint: &'b solana_account_info::AccountInfo<'a>, + /// The program account for the most recent oracle observation + pub observation_state: &'b solana_account_info::AccountInfo<'a>, + /// The arguments for the instruction. + pub __args: SwapBaseInputInstructionArgs, +} + +impl<'a, 'b> SwapBaseInputCpi<'a, 'b> { + pub fn new( + program: &'b solana_account_info::AccountInfo<'a>, + accounts: SwapBaseInputCpiAccounts<'a, 'b>, + args: SwapBaseInputInstructionArgs, + ) -> Self { + Self { + __program: program, + payer: accounts.payer, + authority: accounts.authority, + amm_config: accounts.amm_config, + pool_state: accounts.pool_state, + input_token_account: accounts.input_token_account, + output_token_account: accounts.output_token_account, + input_vault: accounts.input_vault, + output_vault: accounts.output_vault, + input_token_program: accounts.input_token_program, + output_token_program: accounts.output_token_program, + input_token_mint: accounts.input_token_mint, + output_token_mint: accounts.output_token_mint, + observation_state: accounts.observation_state, + __args: args, + } + } + #[inline(always)] + pub fn invoke(&self) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], &[]) + } + #[inline(always)] + pub fn invoke_with_remaining_accounts( + &self, + remaining_accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], remaining_accounts) + } + #[inline(always)] + pub fn invoke_signed(&self, signers_seeds: &[&[&[u8]]]) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(signers_seeds, &[]) + } + #[allow(clippy::arithmetic_side_effects)] + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed_with_remaining_accounts( + &self, + signers_seeds: &[&[&[u8]]], + remaining_accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> solana_program_error::ProgramResult { + let mut accounts = Vec::with_capacity(13 + remaining_accounts.len()); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.payer.key, + true, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.authority.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.amm_config.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.pool_state.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.input_token_account.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.output_token_account.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.input_vault.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.output_vault.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.input_token_program.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.output_token_program.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.input_token_mint.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.output_token_mint.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.observation_state.key, + false, + )); + remaining_accounts.iter().for_each(|remaining_account| { + accounts.push(solana_instruction::AccountMeta { + pubkey: *remaining_account.0.key, + is_signer: remaining_account.1, + is_writable: remaining_account.2, + }) + }); + let mut data = SwapBaseInputInstructionData::new().try_to_vec().unwrap(); + let mut args = self.__args.try_to_vec().unwrap(); + data.append(&mut args); + + let instruction = solana_instruction::Instruction { + program_id: crate::RAYDIUM_CP_SWAP_ID, + accounts, + data, + }; + let mut account_infos = Vec::with_capacity(14 + remaining_accounts.len()); + account_infos.push(self.__program.clone()); + account_infos.push(self.payer.clone()); + account_infos.push(self.authority.clone()); + account_infos.push(self.amm_config.clone()); + account_infos.push(self.pool_state.clone()); + account_infos.push(self.input_token_account.clone()); + account_infos.push(self.output_token_account.clone()); + account_infos.push(self.input_vault.clone()); + account_infos.push(self.output_vault.clone()); + account_infos.push(self.input_token_program.clone()); + account_infos.push(self.output_token_program.clone()); + account_infos.push(self.input_token_mint.clone()); + account_infos.push(self.output_token_mint.clone()); + account_infos.push(self.observation_state.clone()); + remaining_accounts + .iter() + .for_each(|remaining_account| account_infos.push(remaining_account.0.clone())); + + if signers_seeds.is_empty() { + solana_cpi::invoke(&instruction, &account_infos) + } else { + solana_cpi::invoke_signed(&instruction, &account_infos, signers_seeds) + } + } +} + +/// Instruction builder for `SwapBaseInput` via CPI. +/// +/// ### Accounts: +/// +/// 0. `[signer]` payer +/// 1. `[]` authority +/// 2. `[]` amm_config +/// 3. `[writable]` pool_state +/// 4. `[writable]` input_token_account +/// 5. `[writable]` output_token_account +/// 6. `[writable]` input_vault +/// 7. `[writable]` output_vault +/// 8. `[]` input_token_program +/// 9. `[]` output_token_program +/// 10. `[]` input_token_mint +/// 11. `[]` output_token_mint +/// 12. `[writable]` observation_state +#[derive(Clone, Debug)] +pub struct SwapBaseInputCpiBuilder<'a, 'b> { + instruction: Box>, +} + +impl<'a, 'b> SwapBaseInputCpiBuilder<'a, 'b> { + pub fn new(program: &'b solana_account_info::AccountInfo<'a>) -> Self { + let instruction = Box::new(SwapBaseInputCpiBuilderInstruction { + __program: program, + payer: None, + authority: None, + amm_config: None, + pool_state: None, + input_token_account: None, + output_token_account: None, + input_vault: None, + output_vault: None, + input_token_program: None, + output_token_program: None, + input_token_mint: None, + output_token_mint: None, + observation_state: None, + amount_in: None, + minimum_amount_out: None, + __remaining_accounts: Vec::new(), + }); + Self { instruction } + } + /// The user performing the swap + #[inline(always)] + pub fn payer(&mut self, payer: &'b solana_account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.payer = Some(payer); + self + } + #[inline(always)] + pub fn authority(&mut self, authority: &'b solana_account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.authority = Some(authority); + self + } + /// The factory state to read protocol fees + #[inline(always)] + pub fn amm_config( + &mut self, + amm_config: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.amm_config = Some(amm_config); + self + } + /// The program account of the pool in which the swap will be performed + #[inline(always)] + pub fn pool_state( + &mut self, + pool_state: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.pool_state = Some(pool_state); + self + } + /// The user token account for input token + #[inline(always)] + pub fn input_token_account( + &mut self, + input_token_account: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.input_token_account = Some(input_token_account); + self + } + /// The user token account for output token + #[inline(always)] + pub fn output_token_account( + &mut self, + output_token_account: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.output_token_account = Some(output_token_account); + self + } + /// The vault token account for input token + #[inline(always)] + pub fn input_vault( + &mut self, + input_vault: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.input_vault = Some(input_vault); + self + } + /// The vault token account for output token + #[inline(always)] + pub fn output_vault( + &mut self, + output_vault: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.output_vault = Some(output_vault); + self + } + /// SPL program for input token transfers + #[inline(always)] + pub fn input_token_program( + &mut self, + input_token_program: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.input_token_program = Some(input_token_program); + self + } + /// SPL program for output token transfers + #[inline(always)] + pub fn output_token_program( + &mut self, + output_token_program: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.output_token_program = Some(output_token_program); + self + } + /// The mint of input token + #[inline(always)] + pub fn input_token_mint( + &mut self, + input_token_mint: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.input_token_mint = Some(input_token_mint); + self + } + /// The mint of output token + #[inline(always)] + pub fn output_token_mint( + &mut self, + output_token_mint: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.output_token_mint = Some(output_token_mint); + self + } + /// The program account for the most recent oracle observation + #[inline(always)] + pub fn observation_state( + &mut self, + observation_state: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.observation_state = Some(observation_state); + self + } + #[inline(always)] + pub fn amount_in(&mut self, amount_in: u64) -> &mut Self { + self.instruction.amount_in = Some(amount_in); + self + } + #[inline(always)] + pub fn minimum_amount_out(&mut self, minimum_amount_out: u64) -> &mut Self { + self.instruction.minimum_amount_out = Some(minimum_amount_out); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account( + &mut self, + account: &'b solana_account_info::AccountInfo<'a>, + is_writable: bool, + is_signer: bool, + ) -> &mut Self { + self.instruction + .__remaining_accounts + .push((account, is_writable, is_signer)); + self + } + /// Add additional accounts to the instruction. + /// + /// Each account is represented by a tuple of the `AccountInfo`, a `bool` indicating whether the account is writable or not, + /// and a `bool` indicating whether the account is a signer or not. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> &mut Self { + self.instruction + .__remaining_accounts + .extend_from_slice(accounts); + self + } + #[inline(always)] + pub fn invoke(&self) -> solana_program_error::ProgramResult { + self.invoke_signed(&[]) + } + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed(&self, signers_seeds: &[&[&[u8]]]) -> solana_program_error::ProgramResult { + let args = SwapBaseInputInstructionArgs { + amount_in: self + .instruction + .amount_in + .clone() + .expect("amount_in is not set"), + minimum_amount_out: self + .instruction + .minimum_amount_out + .clone() + .expect("minimum_amount_out is not set"), + }; + let instruction = SwapBaseInputCpi { + __program: self.instruction.__program, + + payer: self.instruction.payer.expect("payer is not set"), + + authority: self.instruction.authority.expect("authority is not set"), + + amm_config: self.instruction.amm_config.expect("amm_config is not set"), + + pool_state: self.instruction.pool_state.expect("pool_state is not set"), + + input_token_account: self + .instruction + .input_token_account + .expect("input_token_account is not set"), + + output_token_account: self + .instruction + .output_token_account + .expect("output_token_account is not set"), + + input_vault: self + .instruction + .input_vault + .expect("input_vault is not set"), + + output_vault: self + .instruction + .output_vault + .expect("output_vault is not set"), + + input_token_program: self + .instruction + .input_token_program + .expect("input_token_program is not set"), + + output_token_program: self + .instruction + .output_token_program + .expect("output_token_program is not set"), + + input_token_mint: self + .instruction + .input_token_mint + .expect("input_token_mint is not set"), + + output_token_mint: self + .instruction + .output_token_mint + .expect("output_token_mint is not set"), + + observation_state: self + .instruction + .observation_state + .expect("observation_state is not set"), + __args: args, + }; + instruction.invoke_signed_with_remaining_accounts( + signers_seeds, + &self.instruction.__remaining_accounts, + ) + } +} + +#[derive(Clone, Debug)] +struct SwapBaseInputCpiBuilderInstruction<'a, 'b> { + __program: &'b solana_account_info::AccountInfo<'a>, + payer: Option<&'b solana_account_info::AccountInfo<'a>>, + authority: Option<&'b solana_account_info::AccountInfo<'a>>, + amm_config: Option<&'b solana_account_info::AccountInfo<'a>>, + pool_state: Option<&'b solana_account_info::AccountInfo<'a>>, + input_token_account: Option<&'b solana_account_info::AccountInfo<'a>>, + output_token_account: Option<&'b solana_account_info::AccountInfo<'a>>, + input_vault: Option<&'b solana_account_info::AccountInfo<'a>>, + output_vault: Option<&'b solana_account_info::AccountInfo<'a>>, + input_token_program: Option<&'b solana_account_info::AccountInfo<'a>>, + output_token_program: Option<&'b solana_account_info::AccountInfo<'a>>, + input_token_mint: Option<&'b solana_account_info::AccountInfo<'a>>, + output_token_mint: Option<&'b solana_account_info::AccountInfo<'a>>, + observation_state: Option<&'b solana_account_info::AccountInfo<'a>>, + amount_in: Option, + minimum_amount_out: Option, + /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. + __remaining_accounts: Vec<(&'b solana_account_info::AccountInfo<'a>, bool, bool)>, +} diff --git a/e2e/raydium-cpmm/src/generated/instructions/swap_base_output.rs b/e2e/raydium-cpmm/src/generated/instructions/swap_base_output.rs new file mode 100644 index 0000000..a0a8dd0 --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/instructions/swap_base_output.rs @@ -0,0 +1,853 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +use borsh::BorshDeserialize; +use borsh::BorshSerialize; + +pub const SWAP_BASE_OUTPUT_DISCRIMINATOR: [u8; 8] = [55, 217, 98, 86, 163, 74, 180, 173]; + +/// Accounts. +#[derive(Debug)] +pub struct SwapBaseOutput { + /// The user performing the swap + pub payer: solana_address::Address, + + pub authority: solana_address::Address, + /// The factory state to read protocol fees + pub amm_config: solana_address::Address, + /// The program account of the pool in which the swap will be performed + pub pool_state: solana_address::Address, + /// The user token account for input token + pub input_token_account: solana_address::Address, + /// The user token account for output token + pub output_token_account: solana_address::Address, + /// The vault token account for input token + pub input_vault: solana_address::Address, + /// The vault token account for output token + pub output_vault: solana_address::Address, + /// SPL program for input token transfers + pub input_token_program: solana_address::Address, + /// SPL program for output token transfers + pub output_token_program: solana_address::Address, + /// The mint of input token + pub input_token_mint: solana_address::Address, + /// The mint of output token + pub output_token_mint: solana_address::Address, + /// The program account for the most recent oracle observation + pub observation_state: solana_address::Address, +} + +impl SwapBaseOutput { + pub fn instruction( + &self, + args: SwapBaseOutputInstructionArgs, + ) -> solana_instruction::Instruction { + self.instruction_with_remaining_accounts(args, &[]) + } + #[allow(clippy::arithmetic_side_effects)] + #[allow(clippy::vec_init_then_push)] + pub fn instruction_with_remaining_accounts( + &self, + args: SwapBaseOutputInstructionArgs, + remaining_accounts: &[solana_instruction::AccountMeta], + ) -> solana_instruction::Instruction { + let mut accounts = Vec::with_capacity(13 + remaining_accounts.len()); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.payer, true, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.authority, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.amm_config, + false, + )); + accounts.push(solana_instruction::AccountMeta::new(self.pool_state, false)); + accounts.push(solana_instruction::AccountMeta::new( + self.input_token_account, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.output_token_account, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.input_vault, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.output_vault, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.input_token_program, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.output_token_program, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.input_token_mint, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.output_token_mint, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.observation_state, + false, + )); + accounts.extend_from_slice(remaining_accounts); + let mut data = SwapBaseOutputInstructionData::new().try_to_vec().unwrap(); + let mut args = args.try_to_vec().unwrap(); + data.append(&mut args); + + solana_instruction::Instruction { + program_id: crate::RAYDIUM_CP_SWAP_ID, + accounts, + data, + } + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct SwapBaseOutputInstructionData { + discriminator: [u8; 8], +} + +impl SwapBaseOutputInstructionData { + pub fn new() -> Self { + Self { + discriminator: [55, 217, 98, 86, 163, 74, 180, 173], + } + } + + pub(crate) fn try_to_vec(&self) -> Result, std::io::Error> { + borsh::to_vec(self) + } +} + +impl Default for SwapBaseOutputInstructionData { + fn default() -> Self { + Self::new() + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct SwapBaseOutputInstructionArgs { + pub max_amount_in: u64, + pub amount_out: u64, +} + +impl SwapBaseOutputInstructionArgs { + pub(crate) fn try_to_vec(&self) -> Result, std::io::Error> { + borsh::to_vec(self) + } +} + +/// Instruction builder for `SwapBaseOutput`. +/// +/// ### Accounts: +/// +/// 0. `[signer]` payer +/// 1. `[]` authority +/// 2. `[]` amm_config +/// 3. `[writable]` pool_state +/// 4. `[writable]` input_token_account +/// 5. `[writable]` output_token_account +/// 6. `[writable]` input_vault +/// 7. `[writable]` output_vault +/// 8. `[]` input_token_program +/// 9. `[]` output_token_program +/// 10. `[]` input_token_mint +/// 11. `[]` output_token_mint +/// 12. `[writable]` observation_state +#[derive(Clone, Debug, Default)] +pub struct SwapBaseOutputBuilder { + payer: Option, + authority: Option, + amm_config: Option, + pool_state: Option, + input_token_account: Option, + output_token_account: Option, + input_vault: Option, + output_vault: Option, + input_token_program: Option, + output_token_program: Option, + input_token_mint: Option, + output_token_mint: Option, + observation_state: Option, + max_amount_in: Option, + amount_out: Option, + __remaining_accounts: Vec, +} + +impl SwapBaseOutputBuilder { + pub fn new() -> Self { + Self::default() + } + /// The user performing the swap + #[inline(always)] + pub fn payer(&mut self, payer: solana_address::Address) -> &mut Self { + self.payer = Some(payer); + self + } + #[inline(always)] + pub fn authority(&mut self, authority: solana_address::Address) -> &mut Self { + self.authority = Some(authority); + self + } + /// The factory state to read protocol fees + #[inline(always)] + pub fn amm_config(&mut self, amm_config: solana_address::Address) -> &mut Self { + self.amm_config = Some(amm_config); + self + } + /// The program account of the pool in which the swap will be performed + #[inline(always)] + pub fn pool_state(&mut self, pool_state: solana_address::Address) -> &mut Self { + self.pool_state = Some(pool_state); + self + } + /// The user token account for input token + #[inline(always)] + pub fn input_token_account( + &mut self, + input_token_account: solana_address::Address, + ) -> &mut Self { + self.input_token_account = Some(input_token_account); + self + } + /// The user token account for output token + #[inline(always)] + pub fn output_token_account( + &mut self, + output_token_account: solana_address::Address, + ) -> &mut Self { + self.output_token_account = Some(output_token_account); + self + } + /// The vault token account for input token + #[inline(always)] + pub fn input_vault(&mut self, input_vault: solana_address::Address) -> &mut Self { + self.input_vault = Some(input_vault); + self + } + /// The vault token account for output token + #[inline(always)] + pub fn output_vault(&mut self, output_vault: solana_address::Address) -> &mut Self { + self.output_vault = Some(output_vault); + self + } + /// SPL program for input token transfers + #[inline(always)] + pub fn input_token_program( + &mut self, + input_token_program: solana_address::Address, + ) -> &mut Self { + self.input_token_program = Some(input_token_program); + self + } + /// SPL program for output token transfers + #[inline(always)] + pub fn output_token_program( + &mut self, + output_token_program: solana_address::Address, + ) -> &mut Self { + self.output_token_program = Some(output_token_program); + self + } + /// The mint of input token + #[inline(always)] + pub fn input_token_mint(&mut self, input_token_mint: solana_address::Address) -> &mut Self { + self.input_token_mint = Some(input_token_mint); + self + } + /// The mint of output token + #[inline(always)] + pub fn output_token_mint(&mut self, output_token_mint: solana_address::Address) -> &mut Self { + self.output_token_mint = Some(output_token_mint); + self + } + /// The program account for the most recent oracle observation + #[inline(always)] + pub fn observation_state(&mut self, observation_state: solana_address::Address) -> &mut Self { + self.observation_state = Some(observation_state); + self + } + #[inline(always)] + pub fn max_amount_in(&mut self, max_amount_in: u64) -> &mut Self { + self.max_amount_in = Some(max_amount_in); + self + } + #[inline(always)] + pub fn amount_out(&mut self, amount_out: u64) -> &mut Self { + self.amount_out = Some(amount_out); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account(&mut self, account: solana_instruction::AccountMeta) -> &mut Self { + self.__remaining_accounts.push(account); + self + } + /// Add additional accounts to the instruction. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[solana_instruction::AccountMeta], + ) -> &mut Self { + self.__remaining_accounts.extend_from_slice(accounts); + self + } + #[allow(clippy::clone_on_copy)] + pub fn instruction(&self) -> solana_instruction::Instruction { + let accounts = SwapBaseOutput { + payer: self.payer.expect("payer is not set"), + authority: self.authority.expect("authority is not set"), + amm_config: self.amm_config.expect("amm_config is not set"), + pool_state: self.pool_state.expect("pool_state is not set"), + input_token_account: self + .input_token_account + .expect("input_token_account is not set"), + output_token_account: self + .output_token_account + .expect("output_token_account is not set"), + input_vault: self.input_vault.expect("input_vault is not set"), + output_vault: self.output_vault.expect("output_vault is not set"), + input_token_program: self + .input_token_program + .expect("input_token_program is not set"), + output_token_program: self + .output_token_program + .expect("output_token_program is not set"), + input_token_mint: self.input_token_mint.expect("input_token_mint is not set"), + output_token_mint: self + .output_token_mint + .expect("output_token_mint is not set"), + observation_state: self + .observation_state + .expect("observation_state is not set"), + }; + let args = SwapBaseOutputInstructionArgs { + max_amount_in: self + .max_amount_in + .clone() + .expect("max_amount_in is not set"), + amount_out: self.amount_out.clone().expect("amount_out is not set"), + }; + + accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) + } +} + +/// `swap_base_output` CPI accounts. +pub struct SwapBaseOutputCpiAccounts<'a, 'b> { + /// The user performing the swap + pub payer: &'b solana_account_info::AccountInfo<'a>, + + pub authority: &'b solana_account_info::AccountInfo<'a>, + /// The factory state to read protocol fees + pub amm_config: &'b solana_account_info::AccountInfo<'a>, + /// The program account of the pool in which the swap will be performed + pub pool_state: &'b solana_account_info::AccountInfo<'a>, + /// The user token account for input token + pub input_token_account: &'b solana_account_info::AccountInfo<'a>, + /// The user token account for output token + pub output_token_account: &'b solana_account_info::AccountInfo<'a>, + /// The vault token account for input token + pub input_vault: &'b solana_account_info::AccountInfo<'a>, + /// The vault token account for output token + pub output_vault: &'b solana_account_info::AccountInfo<'a>, + /// SPL program for input token transfers + pub input_token_program: &'b solana_account_info::AccountInfo<'a>, + /// SPL program for output token transfers + pub output_token_program: &'b solana_account_info::AccountInfo<'a>, + /// The mint of input token + pub input_token_mint: &'b solana_account_info::AccountInfo<'a>, + /// The mint of output token + pub output_token_mint: &'b solana_account_info::AccountInfo<'a>, + /// The program account for the most recent oracle observation + pub observation_state: &'b solana_account_info::AccountInfo<'a>, +} + +/// `swap_base_output` CPI instruction. +pub struct SwapBaseOutputCpi<'a, 'b> { + /// The program to invoke. + pub __program: &'b solana_account_info::AccountInfo<'a>, + /// The user performing the swap + pub payer: &'b solana_account_info::AccountInfo<'a>, + + pub authority: &'b solana_account_info::AccountInfo<'a>, + /// The factory state to read protocol fees + pub amm_config: &'b solana_account_info::AccountInfo<'a>, + /// The program account of the pool in which the swap will be performed + pub pool_state: &'b solana_account_info::AccountInfo<'a>, + /// The user token account for input token + pub input_token_account: &'b solana_account_info::AccountInfo<'a>, + /// The user token account for output token + pub output_token_account: &'b solana_account_info::AccountInfo<'a>, + /// The vault token account for input token + pub input_vault: &'b solana_account_info::AccountInfo<'a>, + /// The vault token account for output token + pub output_vault: &'b solana_account_info::AccountInfo<'a>, + /// SPL program for input token transfers + pub input_token_program: &'b solana_account_info::AccountInfo<'a>, + /// SPL program for output token transfers + pub output_token_program: &'b solana_account_info::AccountInfo<'a>, + /// The mint of input token + pub input_token_mint: &'b solana_account_info::AccountInfo<'a>, + /// The mint of output token + pub output_token_mint: &'b solana_account_info::AccountInfo<'a>, + /// The program account for the most recent oracle observation + pub observation_state: &'b solana_account_info::AccountInfo<'a>, + /// The arguments for the instruction. + pub __args: SwapBaseOutputInstructionArgs, +} + +impl<'a, 'b> SwapBaseOutputCpi<'a, 'b> { + pub fn new( + program: &'b solana_account_info::AccountInfo<'a>, + accounts: SwapBaseOutputCpiAccounts<'a, 'b>, + args: SwapBaseOutputInstructionArgs, + ) -> Self { + Self { + __program: program, + payer: accounts.payer, + authority: accounts.authority, + amm_config: accounts.amm_config, + pool_state: accounts.pool_state, + input_token_account: accounts.input_token_account, + output_token_account: accounts.output_token_account, + input_vault: accounts.input_vault, + output_vault: accounts.output_vault, + input_token_program: accounts.input_token_program, + output_token_program: accounts.output_token_program, + input_token_mint: accounts.input_token_mint, + output_token_mint: accounts.output_token_mint, + observation_state: accounts.observation_state, + __args: args, + } + } + #[inline(always)] + pub fn invoke(&self) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], &[]) + } + #[inline(always)] + pub fn invoke_with_remaining_accounts( + &self, + remaining_accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], remaining_accounts) + } + #[inline(always)] + pub fn invoke_signed(&self, signers_seeds: &[&[&[u8]]]) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(signers_seeds, &[]) + } + #[allow(clippy::arithmetic_side_effects)] + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed_with_remaining_accounts( + &self, + signers_seeds: &[&[&[u8]]], + remaining_accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> solana_program_error::ProgramResult { + let mut accounts = Vec::with_capacity(13 + remaining_accounts.len()); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.payer.key, + true, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.authority.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.amm_config.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.pool_state.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.input_token_account.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.output_token_account.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.input_vault.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.output_vault.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.input_token_program.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.output_token_program.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.input_token_mint.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.output_token_mint.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.observation_state.key, + false, + )); + remaining_accounts.iter().for_each(|remaining_account| { + accounts.push(solana_instruction::AccountMeta { + pubkey: *remaining_account.0.key, + is_signer: remaining_account.1, + is_writable: remaining_account.2, + }) + }); + let mut data = SwapBaseOutputInstructionData::new().try_to_vec().unwrap(); + let mut args = self.__args.try_to_vec().unwrap(); + data.append(&mut args); + + let instruction = solana_instruction::Instruction { + program_id: crate::RAYDIUM_CP_SWAP_ID, + accounts, + data, + }; + let mut account_infos = Vec::with_capacity(14 + remaining_accounts.len()); + account_infos.push(self.__program.clone()); + account_infos.push(self.payer.clone()); + account_infos.push(self.authority.clone()); + account_infos.push(self.amm_config.clone()); + account_infos.push(self.pool_state.clone()); + account_infos.push(self.input_token_account.clone()); + account_infos.push(self.output_token_account.clone()); + account_infos.push(self.input_vault.clone()); + account_infos.push(self.output_vault.clone()); + account_infos.push(self.input_token_program.clone()); + account_infos.push(self.output_token_program.clone()); + account_infos.push(self.input_token_mint.clone()); + account_infos.push(self.output_token_mint.clone()); + account_infos.push(self.observation_state.clone()); + remaining_accounts + .iter() + .for_each(|remaining_account| account_infos.push(remaining_account.0.clone())); + + if signers_seeds.is_empty() { + solana_cpi::invoke(&instruction, &account_infos) + } else { + solana_cpi::invoke_signed(&instruction, &account_infos, signers_seeds) + } + } +} + +/// Instruction builder for `SwapBaseOutput` via CPI. +/// +/// ### Accounts: +/// +/// 0. `[signer]` payer +/// 1. `[]` authority +/// 2. `[]` amm_config +/// 3. `[writable]` pool_state +/// 4. `[writable]` input_token_account +/// 5. `[writable]` output_token_account +/// 6. `[writable]` input_vault +/// 7. `[writable]` output_vault +/// 8. `[]` input_token_program +/// 9. `[]` output_token_program +/// 10. `[]` input_token_mint +/// 11. `[]` output_token_mint +/// 12. `[writable]` observation_state +#[derive(Clone, Debug)] +pub struct SwapBaseOutputCpiBuilder<'a, 'b> { + instruction: Box>, +} + +impl<'a, 'b> SwapBaseOutputCpiBuilder<'a, 'b> { + pub fn new(program: &'b solana_account_info::AccountInfo<'a>) -> Self { + let instruction = Box::new(SwapBaseOutputCpiBuilderInstruction { + __program: program, + payer: None, + authority: None, + amm_config: None, + pool_state: None, + input_token_account: None, + output_token_account: None, + input_vault: None, + output_vault: None, + input_token_program: None, + output_token_program: None, + input_token_mint: None, + output_token_mint: None, + observation_state: None, + max_amount_in: None, + amount_out: None, + __remaining_accounts: Vec::new(), + }); + Self { instruction } + } + /// The user performing the swap + #[inline(always)] + pub fn payer(&mut self, payer: &'b solana_account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.payer = Some(payer); + self + } + #[inline(always)] + pub fn authority(&mut self, authority: &'b solana_account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.authority = Some(authority); + self + } + /// The factory state to read protocol fees + #[inline(always)] + pub fn amm_config( + &mut self, + amm_config: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.amm_config = Some(amm_config); + self + } + /// The program account of the pool in which the swap will be performed + #[inline(always)] + pub fn pool_state( + &mut self, + pool_state: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.pool_state = Some(pool_state); + self + } + /// The user token account for input token + #[inline(always)] + pub fn input_token_account( + &mut self, + input_token_account: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.input_token_account = Some(input_token_account); + self + } + /// The user token account for output token + #[inline(always)] + pub fn output_token_account( + &mut self, + output_token_account: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.output_token_account = Some(output_token_account); + self + } + /// The vault token account for input token + #[inline(always)] + pub fn input_vault( + &mut self, + input_vault: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.input_vault = Some(input_vault); + self + } + /// The vault token account for output token + #[inline(always)] + pub fn output_vault( + &mut self, + output_vault: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.output_vault = Some(output_vault); + self + } + /// SPL program for input token transfers + #[inline(always)] + pub fn input_token_program( + &mut self, + input_token_program: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.input_token_program = Some(input_token_program); + self + } + /// SPL program for output token transfers + #[inline(always)] + pub fn output_token_program( + &mut self, + output_token_program: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.output_token_program = Some(output_token_program); + self + } + /// The mint of input token + #[inline(always)] + pub fn input_token_mint( + &mut self, + input_token_mint: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.input_token_mint = Some(input_token_mint); + self + } + /// The mint of output token + #[inline(always)] + pub fn output_token_mint( + &mut self, + output_token_mint: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.output_token_mint = Some(output_token_mint); + self + } + /// The program account for the most recent oracle observation + #[inline(always)] + pub fn observation_state( + &mut self, + observation_state: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.observation_state = Some(observation_state); + self + } + #[inline(always)] + pub fn max_amount_in(&mut self, max_amount_in: u64) -> &mut Self { + self.instruction.max_amount_in = Some(max_amount_in); + self + } + #[inline(always)] + pub fn amount_out(&mut self, amount_out: u64) -> &mut Self { + self.instruction.amount_out = Some(amount_out); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account( + &mut self, + account: &'b solana_account_info::AccountInfo<'a>, + is_writable: bool, + is_signer: bool, + ) -> &mut Self { + self.instruction + .__remaining_accounts + .push((account, is_writable, is_signer)); + self + } + /// Add additional accounts to the instruction. + /// + /// Each account is represented by a tuple of the `AccountInfo`, a `bool` indicating whether the account is writable or not, + /// and a `bool` indicating whether the account is a signer or not. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> &mut Self { + self.instruction + .__remaining_accounts + .extend_from_slice(accounts); + self + } + #[inline(always)] + pub fn invoke(&self) -> solana_program_error::ProgramResult { + self.invoke_signed(&[]) + } + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed(&self, signers_seeds: &[&[&[u8]]]) -> solana_program_error::ProgramResult { + let args = SwapBaseOutputInstructionArgs { + max_amount_in: self + .instruction + .max_amount_in + .clone() + .expect("max_amount_in is not set"), + amount_out: self + .instruction + .amount_out + .clone() + .expect("amount_out is not set"), + }; + let instruction = SwapBaseOutputCpi { + __program: self.instruction.__program, + + payer: self.instruction.payer.expect("payer is not set"), + + authority: self.instruction.authority.expect("authority is not set"), + + amm_config: self.instruction.amm_config.expect("amm_config is not set"), + + pool_state: self.instruction.pool_state.expect("pool_state is not set"), + + input_token_account: self + .instruction + .input_token_account + .expect("input_token_account is not set"), + + output_token_account: self + .instruction + .output_token_account + .expect("output_token_account is not set"), + + input_vault: self + .instruction + .input_vault + .expect("input_vault is not set"), + + output_vault: self + .instruction + .output_vault + .expect("output_vault is not set"), + + input_token_program: self + .instruction + .input_token_program + .expect("input_token_program is not set"), + + output_token_program: self + .instruction + .output_token_program + .expect("output_token_program is not set"), + + input_token_mint: self + .instruction + .input_token_mint + .expect("input_token_mint is not set"), + + output_token_mint: self + .instruction + .output_token_mint + .expect("output_token_mint is not set"), + + observation_state: self + .instruction + .observation_state + .expect("observation_state is not set"), + __args: args, + }; + instruction.invoke_signed_with_remaining_accounts( + signers_seeds, + &self.instruction.__remaining_accounts, + ) + } +} + +#[derive(Clone, Debug)] +struct SwapBaseOutputCpiBuilderInstruction<'a, 'b> { + __program: &'b solana_account_info::AccountInfo<'a>, + payer: Option<&'b solana_account_info::AccountInfo<'a>>, + authority: Option<&'b solana_account_info::AccountInfo<'a>>, + amm_config: Option<&'b solana_account_info::AccountInfo<'a>>, + pool_state: Option<&'b solana_account_info::AccountInfo<'a>>, + input_token_account: Option<&'b solana_account_info::AccountInfo<'a>>, + output_token_account: Option<&'b solana_account_info::AccountInfo<'a>>, + input_vault: Option<&'b solana_account_info::AccountInfo<'a>>, + output_vault: Option<&'b solana_account_info::AccountInfo<'a>>, + input_token_program: Option<&'b solana_account_info::AccountInfo<'a>>, + output_token_program: Option<&'b solana_account_info::AccountInfo<'a>>, + input_token_mint: Option<&'b solana_account_info::AccountInfo<'a>>, + output_token_mint: Option<&'b solana_account_info::AccountInfo<'a>>, + observation_state: Option<&'b solana_account_info::AccountInfo<'a>>, + max_amount_in: Option, + amount_out: Option, + /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. + __remaining_accounts: Vec<(&'b solana_account_info::AccountInfo<'a>, bool, bool)>, +} diff --git a/e2e/raydium-cpmm/src/generated/instructions/update_amm_config.rs b/e2e/raydium-cpmm/src/generated/instructions/update_amm_config.rs new file mode 100644 index 0000000..d5ace99 --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/instructions/update_amm_config.rs @@ -0,0 +1,370 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +use borsh::BorshDeserialize; +use borsh::BorshSerialize; + +pub const UPDATE_AMM_CONFIG_DISCRIMINATOR: [u8; 8] = [49, 60, 174, 136, 154, 28, 116, 200]; + +/// Accounts. +#[derive(Debug)] +pub struct UpdateAmmConfig { + /// The amm config owner or admin + pub owner: solana_address::Address, + /// Amm config account to be changed + pub amm_config: solana_address::Address, +} + +impl UpdateAmmConfig { + pub fn instruction( + &self, + args: UpdateAmmConfigInstructionArgs, + ) -> solana_instruction::Instruction { + self.instruction_with_remaining_accounts(args, &[]) + } + #[allow(clippy::arithmetic_side_effects)] + #[allow(clippy::vec_init_then_push)] + pub fn instruction_with_remaining_accounts( + &self, + args: UpdateAmmConfigInstructionArgs, + remaining_accounts: &[solana_instruction::AccountMeta], + ) -> solana_instruction::Instruction { + let mut accounts = Vec::with_capacity(2 + remaining_accounts.len()); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.owner, true, + )); + accounts.push(solana_instruction::AccountMeta::new(self.amm_config, false)); + accounts.extend_from_slice(remaining_accounts); + let mut data = UpdateAmmConfigInstructionData::new().try_to_vec().unwrap(); + let mut args = args.try_to_vec().unwrap(); + data.append(&mut args); + + solana_instruction::Instruction { + program_id: crate::RAYDIUM_CP_SWAP_ID, + accounts, + data, + } + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct UpdateAmmConfigInstructionData { + discriminator: [u8; 8], +} + +impl UpdateAmmConfigInstructionData { + pub fn new() -> Self { + Self { + discriminator: [49, 60, 174, 136, 154, 28, 116, 200], + } + } + + pub(crate) fn try_to_vec(&self) -> Result, std::io::Error> { + borsh::to_vec(self) + } +} + +impl Default for UpdateAmmConfigInstructionData { + fn default() -> Self { + Self::new() + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct UpdateAmmConfigInstructionArgs { + pub param: u8, + pub value: u64, +} + +impl UpdateAmmConfigInstructionArgs { + pub(crate) fn try_to_vec(&self) -> Result, std::io::Error> { + borsh::to_vec(self) + } +} + +/// Instruction builder for `UpdateAmmConfig`. +/// +/// ### Accounts: +/// +/// 0. `[signer, optional]` owner (default to `GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ`) +/// 1. `[writable]` amm_config +#[derive(Clone, Debug, Default)] +pub struct UpdateAmmConfigBuilder { + owner: Option, + amm_config: Option, + param: Option, + value: Option, + __remaining_accounts: Vec, +} + +impl UpdateAmmConfigBuilder { + pub fn new() -> Self { + Self::default() + } + /// `[optional account, default to 'GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ']` + /// The amm config owner or admin + #[inline(always)] + pub fn owner(&mut self, owner: solana_address::Address) -> &mut Self { + self.owner = Some(owner); + self + } + /// Amm config account to be changed + #[inline(always)] + pub fn amm_config(&mut self, amm_config: solana_address::Address) -> &mut Self { + self.amm_config = Some(amm_config); + self + } + #[inline(always)] + pub fn param(&mut self, param: u8) -> &mut Self { + self.param = Some(param); + self + } + #[inline(always)] + pub fn value(&mut self, value: u64) -> &mut Self { + self.value = Some(value); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account(&mut self, account: solana_instruction::AccountMeta) -> &mut Self { + self.__remaining_accounts.push(account); + self + } + /// Add additional accounts to the instruction. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[solana_instruction::AccountMeta], + ) -> &mut Self { + self.__remaining_accounts.extend_from_slice(accounts); + self + } + #[allow(clippy::clone_on_copy)] + pub fn instruction(&self) -> solana_instruction::Instruction { + let accounts = UpdateAmmConfig { + owner: self.owner.unwrap_or(solana_address::address!( + "GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ" + )), + amm_config: self.amm_config.expect("amm_config is not set"), + }; + let args = UpdateAmmConfigInstructionArgs { + param: self.param.clone().expect("param is not set"), + value: self.value.clone().expect("value is not set"), + }; + + accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) + } +} + +/// `update_amm_config` CPI accounts. +pub struct UpdateAmmConfigCpiAccounts<'a, 'b> { + /// The amm config owner or admin + pub owner: &'b solana_account_info::AccountInfo<'a>, + /// Amm config account to be changed + pub amm_config: &'b solana_account_info::AccountInfo<'a>, +} + +/// `update_amm_config` CPI instruction. +pub struct UpdateAmmConfigCpi<'a, 'b> { + /// The program to invoke. + pub __program: &'b solana_account_info::AccountInfo<'a>, + /// The amm config owner or admin + pub owner: &'b solana_account_info::AccountInfo<'a>, + /// Amm config account to be changed + pub amm_config: &'b solana_account_info::AccountInfo<'a>, + /// The arguments for the instruction. + pub __args: UpdateAmmConfigInstructionArgs, +} + +impl<'a, 'b> UpdateAmmConfigCpi<'a, 'b> { + pub fn new( + program: &'b solana_account_info::AccountInfo<'a>, + accounts: UpdateAmmConfigCpiAccounts<'a, 'b>, + args: UpdateAmmConfigInstructionArgs, + ) -> Self { + Self { + __program: program, + owner: accounts.owner, + amm_config: accounts.amm_config, + __args: args, + } + } + #[inline(always)] + pub fn invoke(&self) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], &[]) + } + #[inline(always)] + pub fn invoke_with_remaining_accounts( + &self, + remaining_accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], remaining_accounts) + } + #[inline(always)] + pub fn invoke_signed(&self, signers_seeds: &[&[&[u8]]]) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(signers_seeds, &[]) + } + #[allow(clippy::arithmetic_side_effects)] + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed_with_remaining_accounts( + &self, + signers_seeds: &[&[&[u8]]], + remaining_accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> solana_program_error::ProgramResult { + let mut accounts = Vec::with_capacity(2 + remaining_accounts.len()); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.owner.key, + true, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.amm_config.key, + false, + )); + remaining_accounts.iter().for_each(|remaining_account| { + accounts.push(solana_instruction::AccountMeta { + pubkey: *remaining_account.0.key, + is_signer: remaining_account.1, + is_writable: remaining_account.2, + }) + }); + let mut data = UpdateAmmConfigInstructionData::new().try_to_vec().unwrap(); + let mut args = self.__args.try_to_vec().unwrap(); + data.append(&mut args); + + let instruction = solana_instruction::Instruction { + program_id: crate::RAYDIUM_CP_SWAP_ID, + accounts, + data, + }; + let mut account_infos = Vec::with_capacity(3 + remaining_accounts.len()); + account_infos.push(self.__program.clone()); + account_infos.push(self.owner.clone()); + account_infos.push(self.amm_config.clone()); + remaining_accounts + .iter() + .for_each(|remaining_account| account_infos.push(remaining_account.0.clone())); + + if signers_seeds.is_empty() { + solana_cpi::invoke(&instruction, &account_infos) + } else { + solana_cpi::invoke_signed(&instruction, &account_infos, signers_seeds) + } + } +} + +/// Instruction builder for `UpdateAmmConfig` via CPI. +/// +/// ### Accounts: +/// +/// 0. `[signer]` owner +/// 1. `[writable]` amm_config +#[derive(Clone, Debug)] +pub struct UpdateAmmConfigCpiBuilder<'a, 'b> { + instruction: Box>, +} + +impl<'a, 'b> UpdateAmmConfigCpiBuilder<'a, 'b> { + pub fn new(program: &'b solana_account_info::AccountInfo<'a>) -> Self { + let instruction = Box::new(UpdateAmmConfigCpiBuilderInstruction { + __program: program, + owner: None, + amm_config: None, + param: None, + value: None, + __remaining_accounts: Vec::new(), + }); + Self { instruction } + } + /// The amm config owner or admin + #[inline(always)] + pub fn owner(&mut self, owner: &'b solana_account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.owner = Some(owner); + self + } + /// Amm config account to be changed + #[inline(always)] + pub fn amm_config( + &mut self, + amm_config: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.amm_config = Some(amm_config); + self + } + #[inline(always)] + pub fn param(&mut self, param: u8) -> &mut Self { + self.instruction.param = Some(param); + self + } + #[inline(always)] + pub fn value(&mut self, value: u64) -> &mut Self { + self.instruction.value = Some(value); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account( + &mut self, + account: &'b solana_account_info::AccountInfo<'a>, + is_writable: bool, + is_signer: bool, + ) -> &mut Self { + self.instruction + .__remaining_accounts + .push((account, is_writable, is_signer)); + self + } + /// Add additional accounts to the instruction. + /// + /// Each account is represented by a tuple of the `AccountInfo`, a `bool` indicating whether the account is writable or not, + /// and a `bool` indicating whether the account is a signer or not. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> &mut Self { + self.instruction + .__remaining_accounts + .extend_from_slice(accounts); + self + } + #[inline(always)] + pub fn invoke(&self) -> solana_program_error::ProgramResult { + self.invoke_signed(&[]) + } + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed(&self, signers_seeds: &[&[&[u8]]]) -> solana_program_error::ProgramResult { + let args = UpdateAmmConfigInstructionArgs { + param: self.instruction.param.clone().expect("param is not set"), + value: self.instruction.value.clone().expect("value is not set"), + }; + let instruction = UpdateAmmConfigCpi { + __program: self.instruction.__program, + + owner: self.instruction.owner.expect("owner is not set"), + + amm_config: self.instruction.amm_config.expect("amm_config is not set"), + __args: args, + }; + instruction.invoke_signed_with_remaining_accounts( + signers_seeds, + &self.instruction.__remaining_accounts, + ) + } +} + +#[derive(Clone, Debug)] +struct UpdateAmmConfigCpiBuilderInstruction<'a, 'b> { + __program: &'b solana_account_info::AccountInfo<'a>, + owner: Option<&'b solana_account_info::AccountInfo<'a>>, + amm_config: Option<&'b solana_account_info::AccountInfo<'a>>, + param: Option, + value: Option, + /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. + __remaining_accounts: Vec<(&'b solana_account_info::AccountInfo<'a>, bool, bool)>, +} diff --git a/e2e/raydium-cpmm/src/generated/instructions/update_pool_status.rs b/e2e/raydium-cpmm/src/generated/instructions/update_pool_status.rs new file mode 100644 index 0000000..7880400 --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/instructions/update_pool_status.rs @@ -0,0 +1,349 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +use borsh::BorshDeserialize; +use borsh::BorshSerialize; + +pub const UPDATE_POOL_STATUS_DISCRIMINATOR: [u8; 8] = [130, 87, 108, 6, 46, 224, 117, 123]; + +/// Accounts. +#[derive(Debug)] +pub struct UpdatePoolStatus { + pub authority: solana_address::Address, + + pub pool_state: solana_address::Address, +} + +impl UpdatePoolStatus { + pub fn instruction( + &self, + args: UpdatePoolStatusInstructionArgs, + ) -> solana_instruction::Instruction { + self.instruction_with_remaining_accounts(args, &[]) + } + #[allow(clippy::arithmetic_side_effects)] + #[allow(clippy::vec_init_then_push)] + pub fn instruction_with_remaining_accounts( + &self, + args: UpdatePoolStatusInstructionArgs, + remaining_accounts: &[solana_instruction::AccountMeta], + ) -> solana_instruction::Instruction { + let mut accounts = Vec::with_capacity(2 + remaining_accounts.len()); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.authority, + true, + )); + accounts.push(solana_instruction::AccountMeta::new(self.pool_state, false)); + accounts.extend_from_slice(remaining_accounts); + let mut data = UpdatePoolStatusInstructionData::new().try_to_vec().unwrap(); + let mut args = args.try_to_vec().unwrap(); + data.append(&mut args); + + solana_instruction::Instruction { + program_id: crate::RAYDIUM_CP_SWAP_ID, + accounts, + data, + } + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct UpdatePoolStatusInstructionData { + discriminator: [u8; 8], +} + +impl UpdatePoolStatusInstructionData { + pub fn new() -> Self { + Self { + discriminator: [130, 87, 108, 6, 46, 224, 117, 123], + } + } + + pub(crate) fn try_to_vec(&self) -> Result, std::io::Error> { + borsh::to_vec(self) + } +} + +impl Default for UpdatePoolStatusInstructionData { + fn default() -> Self { + Self::new() + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct UpdatePoolStatusInstructionArgs { + pub status: u8, +} + +impl UpdatePoolStatusInstructionArgs { + pub(crate) fn try_to_vec(&self) -> Result, std::io::Error> { + borsh::to_vec(self) + } +} + +/// Instruction builder for `UpdatePoolStatus`. +/// +/// ### Accounts: +/// +/// 0. `[signer, optional]` authority (default to `GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ`) +/// 1. `[writable]` pool_state +#[derive(Clone, Debug, Default)] +pub struct UpdatePoolStatusBuilder { + authority: Option, + pool_state: Option, + status: Option, + __remaining_accounts: Vec, +} + +impl UpdatePoolStatusBuilder { + pub fn new() -> Self { + Self::default() + } + /// `[optional account, default to 'GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ']` + #[inline(always)] + pub fn authority(&mut self, authority: solana_address::Address) -> &mut Self { + self.authority = Some(authority); + self + } + #[inline(always)] + pub fn pool_state(&mut self, pool_state: solana_address::Address) -> &mut Self { + self.pool_state = Some(pool_state); + self + } + #[inline(always)] + pub fn status(&mut self, status: u8) -> &mut Self { + self.status = Some(status); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account(&mut self, account: solana_instruction::AccountMeta) -> &mut Self { + self.__remaining_accounts.push(account); + self + } + /// Add additional accounts to the instruction. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[solana_instruction::AccountMeta], + ) -> &mut Self { + self.__remaining_accounts.extend_from_slice(accounts); + self + } + #[allow(clippy::clone_on_copy)] + pub fn instruction(&self) -> solana_instruction::Instruction { + let accounts = UpdatePoolStatus { + authority: self.authority.unwrap_or(solana_address::address!( + "GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ" + )), + pool_state: self.pool_state.expect("pool_state is not set"), + }; + let args = UpdatePoolStatusInstructionArgs { + status: self.status.clone().expect("status is not set"), + }; + + accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) + } +} + +/// `update_pool_status` CPI accounts. +pub struct UpdatePoolStatusCpiAccounts<'a, 'b> { + pub authority: &'b solana_account_info::AccountInfo<'a>, + + pub pool_state: &'b solana_account_info::AccountInfo<'a>, +} + +/// `update_pool_status` CPI instruction. +pub struct UpdatePoolStatusCpi<'a, 'b> { + /// The program to invoke. + pub __program: &'b solana_account_info::AccountInfo<'a>, + + pub authority: &'b solana_account_info::AccountInfo<'a>, + + pub pool_state: &'b solana_account_info::AccountInfo<'a>, + /// The arguments for the instruction. + pub __args: UpdatePoolStatusInstructionArgs, +} + +impl<'a, 'b> UpdatePoolStatusCpi<'a, 'b> { + pub fn new( + program: &'b solana_account_info::AccountInfo<'a>, + accounts: UpdatePoolStatusCpiAccounts<'a, 'b>, + args: UpdatePoolStatusInstructionArgs, + ) -> Self { + Self { + __program: program, + authority: accounts.authority, + pool_state: accounts.pool_state, + __args: args, + } + } + #[inline(always)] + pub fn invoke(&self) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], &[]) + } + #[inline(always)] + pub fn invoke_with_remaining_accounts( + &self, + remaining_accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], remaining_accounts) + } + #[inline(always)] + pub fn invoke_signed(&self, signers_seeds: &[&[&[u8]]]) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(signers_seeds, &[]) + } + #[allow(clippy::arithmetic_side_effects)] + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed_with_remaining_accounts( + &self, + signers_seeds: &[&[&[u8]]], + remaining_accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> solana_program_error::ProgramResult { + let mut accounts = Vec::with_capacity(2 + remaining_accounts.len()); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.authority.key, + true, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.pool_state.key, + false, + )); + remaining_accounts.iter().for_each(|remaining_account| { + accounts.push(solana_instruction::AccountMeta { + pubkey: *remaining_account.0.key, + is_signer: remaining_account.1, + is_writable: remaining_account.2, + }) + }); + let mut data = UpdatePoolStatusInstructionData::new().try_to_vec().unwrap(); + let mut args = self.__args.try_to_vec().unwrap(); + data.append(&mut args); + + let instruction = solana_instruction::Instruction { + program_id: crate::RAYDIUM_CP_SWAP_ID, + accounts, + data, + }; + let mut account_infos = Vec::with_capacity(3 + remaining_accounts.len()); + account_infos.push(self.__program.clone()); + account_infos.push(self.authority.clone()); + account_infos.push(self.pool_state.clone()); + remaining_accounts + .iter() + .for_each(|remaining_account| account_infos.push(remaining_account.0.clone())); + + if signers_seeds.is_empty() { + solana_cpi::invoke(&instruction, &account_infos) + } else { + solana_cpi::invoke_signed(&instruction, &account_infos, signers_seeds) + } + } +} + +/// Instruction builder for `UpdatePoolStatus` via CPI. +/// +/// ### Accounts: +/// +/// 0. `[signer]` authority +/// 1. `[writable]` pool_state +#[derive(Clone, Debug)] +pub struct UpdatePoolStatusCpiBuilder<'a, 'b> { + instruction: Box>, +} + +impl<'a, 'b> UpdatePoolStatusCpiBuilder<'a, 'b> { + pub fn new(program: &'b solana_account_info::AccountInfo<'a>) -> Self { + let instruction = Box::new(UpdatePoolStatusCpiBuilderInstruction { + __program: program, + authority: None, + pool_state: None, + status: None, + __remaining_accounts: Vec::new(), + }); + Self { instruction } + } + #[inline(always)] + pub fn authority(&mut self, authority: &'b solana_account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.authority = Some(authority); + self + } + #[inline(always)] + pub fn pool_state( + &mut self, + pool_state: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.pool_state = Some(pool_state); + self + } + #[inline(always)] + pub fn status(&mut self, status: u8) -> &mut Self { + self.instruction.status = Some(status); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account( + &mut self, + account: &'b solana_account_info::AccountInfo<'a>, + is_writable: bool, + is_signer: bool, + ) -> &mut Self { + self.instruction + .__remaining_accounts + .push((account, is_writable, is_signer)); + self + } + /// Add additional accounts to the instruction. + /// + /// Each account is represented by a tuple of the `AccountInfo`, a `bool` indicating whether the account is writable or not, + /// and a `bool` indicating whether the account is a signer or not. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> &mut Self { + self.instruction + .__remaining_accounts + .extend_from_slice(accounts); + self + } + #[inline(always)] + pub fn invoke(&self) -> solana_program_error::ProgramResult { + self.invoke_signed(&[]) + } + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed(&self, signers_seeds: &[&[&[u8]]]) -> solana_program_error::ProgramResult { + let args = UpdatePoolStatusInstructionArgs { + status: self.instruction.status.clone().expect("status is not set"), + }; + let instruction = UpdatePoolStatusCpi { + __program: self.instruction.__program, + + authority: self.instruction.authority.expect("authority is not set"), + + pool_state: self.instruction.pool_state.expect("pool_state is not set"), + __args: args, + }; + instruction.invoke_signed_with_remaining_accounts( + signers_seeds, + &self.instruction.__remaining_accounts, + ) + } +} + +#[derive(Clone, Debug)] +struct UpdatePoolStatusCpiBuilderInstruction<'a, 'b> { + __program: &'b solana_account_info::AccountInfo<'a>, + authority: Option<&'b solana_account_info::AccountInfo<'a>>, + pool_state: Option<&'b solana_account_info::AccountInfo<'a>>, + status: Option, + /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. + __remaining_accounts: Vec<(&'b solana_account_info::AccountInfo<'a>, bool, bool)>, +} diff --git a/e2e/raydium-cpmm/src/generated/instructions/withdraw.rs b/e2e/raydium-cpmm/src/generated/instructions/withdraw.rs new file mode 100644 index 0000000..26e855e --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/instructions/withdraw.rs @@ -0,0 +1,897 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +use borsh::BorshDeserialize; +use borsh::BorshSerialize; + +pub const WITHDRAW_DISCRIMINATOR: [u8; 8] = [183, 18, 70, 156, 148, 109, 161, 34]; + +/// Accounts. +#[derive(Debug)] +pub struct Withdraw { + /// Pays to mint the position + pub owner: solana_address::Address, + + pub authority: solana_address::Address, + /// Pool state account + pub pool_state: solana_address::Address, + /// Owner lp token account + pub owner_lp_token: solana_address::Address, + /// The token account for receive token_0, + pub token0_account: solana_address::Address, + /// The token account for receive token_1 + pub token1_account: solana_address::Address, + /// The address that holds pool tokens for token_0 + pub token0_vault: solana_address::Address, + /// The address that holds pool tokens for token_1 + pub token1_vault: solana_address::Address, + /// token Program + pub token_program: solana_address::Address, + /// Token program 2022 + pub token_program2022: solana_address::Address, + /// The mint of token_0 vault + pub vault0_mint: solana_address::Address, + /// The mint of token_1 vault + pub vault1_mint: solana_address::Address, + /// Pool lp token mint + pub lp_mint: solana_address::Address, + /// memo program + pub memo_program: solana_address::Address, +} + +impl Withdraw { + pub fn instruction(&self, args: WithdrawInstructionArgs) -> solana_instruction::Instruction { + self.instruction_with_remaining_accounts(args, &[]) + } + #[allow(clippy::arithmetic_side_effects)] + #[allow(clippy::vec_init_then_push)] + pub fn instruction_with_remaining_accounts( + &self, + args: WithdrawInstructionArgs, + remaining_accounts: &[solana_instruction::AccountMeta], + ) -> solana_instruction::Instruction { + let mut accounts = Vec::with_capacity(14 + remaining_accounts.len()); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.owner, true, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.authority, + false, + )); + accounts.push(solana_instruction::AccountMeta::new(self.pool_state, false)); + accounts.push(solana_instruction::AccountMeta::new( + self.owner_lp_token, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.token0_account, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.token1_account, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.token0_vault, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + self.token1_vault, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.token_program, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.token_program2022, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.vault0_mint, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.vault1_mint, + false, + )); + accounts.push(solana_instruction::AccountMeta::new(self.lp_mint, false)); + accounts.push(solana_instruction::AccountMeta::new_readonly( + self.memo_program, + false, + )); + accounts.extend_from_slice(remaining_accounts); + let mut data = WithdrawInstructionData::new().try_to_vec().unwrap(); + let mut args = args.try_to_vec().unwrap(); + data.append(&mut args); + + solana_instruction::Instruction { + program_id: crate::RAYDIUM_CP_SWAP_ID, + accounts, + data, + } + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct WithdrawInstructionData { + discriminator: [u8; 8], +} + +impl WithdrawInstructionData { + pub fn new() -> Self { + Self { + discriminator: [183, 18, 70, 156, 148, 109, 161, 34], + } + } + + pub(crate) fn try_to_vec(&self) -> Result, std::io::Error> { + borsh::to_vec(self) + } +} + +impl Default for WithdrawInstructionData { + fn default() -> Self { + Self::new() + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct WithdrawInstructionArgs { + pub lp_token_amount: u64, + pub minimum_token0_amount: u64, + pub minimum_token1_amount: u64, +} + +impl WithdrawInstructionArgs { + pub(crate) fn try_to_vec(&self) -> Result, std::io::Error> { + borsh::to_vec(self) + } +} + +/// Instruction builder for `Withdraw`. +/// +/// ### Accounts: +/// +/// 0. `[signer]` owner +/// 1. `[]` authority +/// 2. `[writable]` pool_state +/// 3. `[writable]` owner_lp_token +/// 4. `[writable]` token0_account +/// 5. `[writable]` token1_account +/// 6. `[writable]` token0_vault +/// 7. `[writable]` token1_vault +/// 8. `[optional]` token_program (default to `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`) +/// 9. `[optional]` token_program2022 (default to `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb`) +/// 10. `[]` vault0_mint +/// 11. `[]` vault1_mint +/// 12. `[writable]` lp_mint +/// 13. `[optional]` memo_program (default to `MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr`) +#[derive(Clone, Debug, Default)] +pub struct WithdrawBuilder { + owner: Option, + authority: Option, + pool_state: Option, + owner_lp_token: Option, + token0_account: Option, + token1_account: Option, + token0_vault: Option, + token1_vault: Option, + token_program: Option, + token_program2022: Option, + vault0_mint: Option, + vault1_mint: Option, + lp_mint: Option, + memo_program: Option, + lp_token_amount: Option, + minimum_token0_amount: Option, + minimum_token1_amount: Option, + __remaining_accounts: Vec, +} + +impl WithdrawBuilder { + pub fn new() -> Self { + Self::default() + } + /// Pays to mint the position + #[inline(always)] + pub fn owner(&mut self, owner: solana_address::Address) -> &mut Self { + self.owner = Some(owner); + self + } + #[inline(always)] + pub fn authority(&mut self, authority: solana_address::Address) -> &mut Self { + self.authority = Some(authority); + self + } + /// Pool state account + #[inline(always)] + pub fn pool_state(&mut self, pool_state: solana_address::Address) -> &mut Self { + self.pool_state = Some(pool_state); + self + } + /// Owner lp token account + #[inline(always)] + pub fn owner_lp_token(&mut self, owner_lp_token: solana_address::Address) -> &mut Self { + self.owner_lp_token = Some(owner_lp_token); + self + } + /// The token account for receive token_0, + #[inline(always)] + pub fn token0_account(&mut self, token0_account: solana_address::Address) -> &mut Self { + self.token0_account = Some(token0_account); + self + } + /// The token account for receive token_1 + #[inline(always)] + pub fn token1_account(&mut self, token1_account: solana_address::Address) -> &mut Self { + self.token1_account = Some(token1_account); + self + } + /// The address that holds pool tokens for token_0 + #[inline(always)] + pub fn token0_vault(&mut self, token0_vault: solana_address::Address) -> &mut Self { + self.token0_vault = Some(token0_vault); + self + } + /// The address that holds pool tokens for token_1 + #[inline(always)] + pub fn token1_vault(&mut self, token1_vault: solana_address::Address) -> &mut Self { + self.token1_vault = Some(token1_vault); + self + } + /// `[optional account, default to 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA']` + /// token Program + #[inline(always)] + pub fn token_program(&mut self, token_program: solana_address::Address) -> &mut Self { + self.token_program = Some(token_program); + self + } + /// `[optional account, default to 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb']` + /// Token program 2022 + #[inline(always)] + pub fn token_program2022(&mut self, token_program2022: solana_address::Address) -> &mut Self { + self.token_program2022 = Some(token_program2022); + self + } + /// The mint of token_0 vault + #[inline(always)] + pub fn vault0_mint(&mut self, vault0_mint: solana_address::Address) -> &mut Self { + self.vault0_mint = Some(vault0_mint); + self + } + /// The mint of token_1 vault + #[inline(always)] + pub fn vault1_mint(&mut self, vault1_mint: solana_address::Address) -> &mut Self { + self.vault1_mint = Some(vault1_mint); + self + } + /// Pool lp token mint + #[inline(always)] + pub fn lp_mint(&mut self, lp_mint: solana_address::Address) -> &mut Self { + self.lp_mint = Some(lp_mint); + self + } + /// `[optional account, default to 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr']` + /// memo program + #[inline(always)] + pub fn memo_program(&mut self, memo_program: solana_address::Address) -> &mut Self { + self.memo_program = Some(memo_program); + self + } + #[inline(always)] + pub fn lp_token_amount(&mut self, lp_token_amount: u64) -> &mut Self { + self.lp_token_amount = Some(lp_token_amount); + self + } + #[inline(always)] + pub fn minimum_token0_amount(&mut self, minimum_token0_amount: u64) -> &mut Self { + self.minimum_token0_amount = Some(minimum_token0_amount); + self + } + #[inline(always)] + pub fn minimum_token1_amount(&mut self, minimum_token1_amount: u64) -> &mut Self { + self.minimum_token1_amount = Some(minimum_token1_amount); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account(&mut self, account: solana_instruction::AccountMeta) -> &mut Self { + self.__remaining_accounts.push(account); + self + } + /// Add additional accounts to the instruction. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[solana_instruction::AccountMeta], + ) -> &mut Self { + self.__remaining_accounts.extend_from_slice(accounts); + self + } + #[allow(clippy::clone_on_copy)] + pub fn instruction(&self) -> solana_instruction::Instruction { + let accounts = Withdraw { + owner: self.owner.expect("owner is not set"), + authority: self.authority.expect("authority is not set"), + pool_state: self.pool_state.expect("pool_state is not set"), + owner_lp_token: self.owner_lp_token.expect("owner_lp_token is not set"), + token0_account: self.token0_account.expect("token0_account is not set"), + token1_account: self.token1_account.expect("token1_account is not set"), + token0_vault: self.token0_vault.expect("token0_vault is not set"), + token1_vault: self.token1_vault.expect("token1_vault is not set"), + token_program: self.token_program.unwrap_or(solana_address::address!( + "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + )), + token_program2022: self.token_program2022.unwrap_or(solana_address::address!( + "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + )), + vault0_mint: self.vault0_mint.expect("vault0_mint is not set"), + vault1_mint: self.vault1_mint.expect("vault1_mint is not set"), + lp_mint: self.lp_mint.expect("lp_mint is not set"), + memo_program: self.memo_program.unwrap_or(solana_address::address!( + "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" + )), + }; + let args = WithdrawInstructionArgs { + lp_token_amount: self + .lp_token_amount + .clone() + .expect("lp_token_amount is not set"), + minimum_token0_amount: self + .minimum_token0_amount + .clone() + .expect("minimum_token0_amount is not set"), + minimum_token1_amount: self + .minimum_token1_amount + .clone() + .expect("minimum_token1_amount is not set"), + }; + + accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) + } +} + +/// `withdraw` CPI accounts. +pub struct WithdrawCpiAccounts<'a, 'b> { + /// Pays to mint the position + pub owner: &'b solana_account_info::AccountInfo<'a>, + + pub authority: &'b solana_account_info::AccountInfo<'a>, + /// Pool state account + pub pool_state: &'b solana_account_info::AccountInfo<'a>, + /// Owner lp token account + pub owner_lp_token: &'b solana_account_info::AccountInfo<'a>, + /// The token account for receive token_0, + pub token0_account: &'b solana_account_info::AccountInfo<'a>, + /// The token account for receive token_1 + pub token1_account: &'b solana_account_info::AccountInfo<'a>, + /// The address that holds pool tokens for token_0 + pub token0_vault: &'b solana_account_info::AccountInfo<'a>, + /// The address that holds pool tokens for token_1 + pub token1_vault: &'b solana_account_info::AccountInfo<'a>, + /// token Program + pub token_program: &'b solana_account_info::AccountInfo<'a>, + /// Token program 2022 + pub token_program2022: &'b solana_account_info::AccountInfo<'a>, + /// The mint of token_0 vault + pub vault0_mint: &'b solana_account_info::AccountInfo<'a>, + /// The mint of token_1 vault + pub vault1_mint: &'b solana_account_info::AccountInfo<'a>, + /// Pool lp token mint + pub lp_mint: &'b solana_account_info::AccountInfo<'a>, + /// memo program + pub memo_program: &'b solana_account_info::AccountInfo<'a>, +} + +/// `withdraw` CPI instruction. +pub struct WithdrawCpi<'a, 'b> { + /// The program to invoke. + pub __program: &'b solana_account_info::AccountInfo<'a>, + /// Pays to mint the position + pub owner: &'b solana_account_info::AccountInfo<'a>, + + pub authority: &'b solana_account_info::AccountInfo<'a>, + /// Pool state account + pub pool_state: &'b solana_account_info::AccountInfo<'a>, + /// Owner lp token account + pub owner_lp_token: &'b solana_account_info::AccountInfo<'a>, + /// The token account for receive token_0, + pub token0_account: &'b solana_account_info::AccountInfo<'a>, + /// The token account for receive token_1 + pub token1_account: &'b solana_account_info::AccountInfo<'a>, + /// The address that holds pool tokens for token_0 + pub token0_vault: &'b solana_account_info::AccountInfo<'a>, + /// The address that holds pool tokens for token_1 + pub token1_vault: &'b solana_account_info::AccountInfo<'a>, + /// token Program + pub token_program: &'b solana_account_info::AccountInfo<'a>, + /// Token program 2022 + pub token_program2022: &'b solana_account_info::AccountInfo<'a>, + /// The mint of token_0 vault + pub vault0_mint: &'b solana_account_info::AccountInfo<'a>, + /// The mint of token_1 vault + pub vault1_mint: &'b solana_account_info::AccountInfo<'a>, + /// Pool lp token mint + pub lp_mint: &'b solana_account_info::AccountInfo<'a>, + /// memo program + pub memo_program: &'b solana_account_info::AccountInfo<'a>, + /// The arguments for the instruction. + pub __args: WithdrawInstructionArgs, +} + +impl<'a, 'b> WithdrawCpi<'a, 'b> { + pub fn new( + program: &'b solana_account_info::AccountInfo<'a>, + accounts: WithdrawCpiAccounts<'a, 'b>, + args: WithdrawInstructionArgs, + ) -> Self { + Self { + __program: program, + owner: accounts.owner, + authority: accounts.authority, + pool_state: accounts.pool_state, + owner_lp_token: accounts.owner_lp_token, + token0_account: accounts.token0_account, + token1_account: accounts.token1_account, + token0_vault: accounts.token0_vault, + token1_vault: accounts.token1_vault, + token_program: accounts.token_program, + token_program2022: accounts.token_program2022, + vault0_mint: accounts.vault0_mint, + vault1_mint: accounts.vault1_mint, + lp_mint: accounts.lp_mint, + memo_program: accounts.memo_program, + __args: args, + } + } + #[inline(always)] + pub fn invoke(&self) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], &[]) + } + #[inline(always)] + pub fn invoke_with_remaining_accounts( + &self, + remaining_accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], remaining_accounts) + } + #[inline(always)] + pub fn invoke_signed(&self, signers_seeds: &[&[&[u8]]]) -> solana_program_error::ProgramResult { + self.invoke_signed_with_remaining_accounts(signers_seeds, &[]) + } + #[allow(clippy::arithmetic_side_effects)] + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed_with_remaining_accounts( + &self, + signers_seeds: &[&[&[u8]]], + remaining_accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> solana_program_error::ProgramResult { + let mut accounts = Vec::with_capacity(14 + remaining_accounts.len()); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.owner.key, + true, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.authority.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.pool_state.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.owner_lp_token.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.token0_account.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.token1_account.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.token0_vault.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.token1_vault.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.token_program.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.token_program2022.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.vault0_mint.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.vault1_mint.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new( + *self.lp_mint.key, + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly( + *self.memo_program.key, + false, + )); + remaining_accounts.iter().for_each(|remaining_account| { + accounts.push(solana_instruction::AccountMeta { + pubkey: *remaining_account.0.key, + is_signer: remaining_account.1, + is_writable: remaining_account.2, + }) + }); + let mut data = WithdrawInstructionData::new().try_to_vec().unwrap(); + let mut args = self.__args.try_to_vec().unwrap(); + data.append(&mut args); + + let instruction = solana_instruction::Instruction { + program_id: crate::RAYDIUM_CP_SWAP_ID, + accounts, + data, + }; + let mut account_infos = Vec::with_capacity(15 + remaining_accounts.len()); + account_infos.push(self.__program.clone()); + account_infos.push(self.owner.clone()); + account_infos.push(self.authority.clone()); + account_infos.push(self.pool_state.clone()); + account_infos.push(self.owner_lp_token.clone()); + account_infos.push(self.token0_account.clone()); + account_infos.push(self.token1_account.clone()); + account_infos.push(self.token0_vault.clone()); + account_infos.push(self.token1_vault.clone()); + account_infos.push(self.token_program.clone()); + account_infos.push(self.token_program2022.clone()); + account_infos.push(self.vault0_mint.clone()); + account_infos.push(self.vault1_mint.clone()); + account_infos.push(self.lp_mint.clone()); + account_infos.push(self.memo_program.clone()); + remaining_accounts + .iter() + .for_each(|remaining_account| account_infos.push(remaining_account.0.clone())); + + if signers_seeds.is_empty() { + solana_cpi::invoke(&instruction, &account_infos) + } else { + solana_cpi::invoke_signed(&instruction, &account_infos, signers_seeds) + } + } +} + +/// Instruction builder for `Withdraw` via CPI. +/// +/// ### Accounts: +/// +/// 0. `[signer]` owner +/// 1. `[]` authority +/// 2. `[writable]` pool_state +/// 3. `[writable]` owner_lp_token +/// 4. `[writable]` token0_account +/// 5. `[writable]` token1_account +/// 6. `[writable]` token0_vault +/// 7. `[writable]` token1_vault +/// 8. `[]` token_program +/// 9. `[]` token_program2022 +/// 10. `[]` vault0_mint +/// 11. `[]` vault1_mint +/// 12. `[writable]` lp_mint +/// 13. `[]` memo_program +#[derive(Clone, Debug)] +pub struct WithdrawCpiBuilder<'a, 'b> { + instruction: Box>, +} + +impl<'a, 'b> WithdrawCpiBuilder<'a, 'b> { + pub fn new(program: &'b solana_account_info::AccountInfo<'a>) -> Self { + let instruction = Box::new(WithdrawCpiBuilderInstruction { + __program: program, + owner: None, + authority: None, + pool_state: None, + owner_lp_token: None, + token0_account: None, + token1_account: None, + token0_vault: None, + token1_vault: None, + token_program: None, + token_program2022: None, + vault0_mint: None, + vault1_mint: None, + lp_mint: None, + memo_program: None, + lp_token_amount: None, + minimum_token0_amount: None, + minimum_token1_amount: None, + __remaining_accounts: Vec::new(), + }); + Self { instruction } + } + /// Pays to mint the position + #[inline(always)] + pub fn owner(&mut self, owner: &'b solana_account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.owner = Some(owner); + self + } + #[inline(always)] + pub fn authority(&mut self, authority: &'b solana_account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.authority = Some(authority); + self + } + /// Pool state account + #[inline(always)] + pub fn pool_state( + &mut self, + pool_state: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.pool_state = Some(pool_state); + self + } + /// Owner lp token account + #[inline(always)] + pub fn owner_lp_token( + &mut self, + owner_lp_token: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.owner_lp_token = Some(owner_lp_token); + self + } + /// The token account for receive token_0, + #[inline(always)] + pub fn token0_account( + &mut self, + token0_account: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token0_account = Some(token0_account); + self + } + /// The token account for receive token_1 + #[inline(always)] + pub fn token1_account( + &mut self, + token1_account: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token1_account = Some(token1_account); + self + } + /// The address that holds pool tokens for token_0 + #[inline(always)] + pub fn token0_vault( + &mut self, + token0_vault: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token0_vault = Some(token0_vault); + self + } + /// The address that holds pool tokens for token_1 + #[inline(always)] + pub fn token1_vault( + &mut self, + token1_vault: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token1_vault = Some(token1_vault); + self + } + /// token Program + #[inline(always)] + pub fn token_program( + &mut self, + token_program: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token_program = Some(token_program); + self + } + /// Token program 2022 + #[inline(always)] + pub fn token_program2022( + &mut self, + token_program2022: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.token_program2022 = Some(token_program2022); + self + } + /// The mint of token_0 vault + #[inline(always)] + pub fn vault0_mint( + &mut self, + vault0_mint: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.vault0_mint = Some(vault0_mint); + self + } + /// The mint of token_1 vault + #[inline(always)] + pub fn vault1_mint( + &mut self, + vault1_mint: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.vault1_mint = Some(vault1_mint); + self + } + /// Pool lp token mint + #[inline(always)] + pub fn lp_mint(&mut self, lp_mint: &'b solana_account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.lp_mint = Some(lp_mint); + self + } + /// memo program + #[inline(always)] + pub fn memo_program( + &mut self, + memo_program: &'b solana_account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.memo_program = Some(memo_program); + self + } + #[inline(always)] + pub fn lp_token_amount(&mut self, lp_token_amount: u64) -> &mut Self { + self.instruction.lp_token_amount = Some(lp_token_amount); + self + } + #[inline(always)] + pub fn minimum_token0_amount(&mut self, minimum_token0_amount: u64) -> &mut Self { + self.instruction.minimum_token0_amount = Some(minimum_token0_amount); + self + } + #[inline(always)] + pub fn minimum_token1_amount(&mut self, minimum_token1_amount: u64) -> &mut Self { + self.instruction.minimum_token1_amount = Some(minimum_token1_amount); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account( + &mut self, + account: &'b solana_account_info::AccountInfo<'a>, + is_writable: bool, + is_signer: bool, + ) -> &mut Self { + self.instruction + .__remaining_accounts + .push((account, is_writable, is_signer)); + self + } + /// Add additional accounts to the instruction. + /// + /// Each account is represented by a tuple of the `AccountInfo`, a `bool` indicating whether the account is writable or not, + /// and a `bool` indicating whether the account is a signer or not. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[(&'b solana_account_info::AccountInfo<'a>, bool, bool)], + ) -> &mut Self { + self.instruction + .__remaining_accounts + .extend_from_slice(accounts); + self + } + #[inline(always)] + pub fn invoke(&self) -> solana_program_error::ProgramResult { + self.invoke_signed(&[]) + } + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed(&self, signers_seeds: &[&[&[u8]]]) -> solana_program_error::ProgramResult { + let args = WithdrawInstructionArgs { + lp_token_amount: self + .instruction + .lp_token_amount + .clone() + .expect("lp_token_amount is not set"), + minimum_token0_amount: self + .instruction + .minimum_token0_amount + .clone() + .expect("minimum_token0_amount is not set"), + minimum_token1_amount: self + .instruction + .minimum_token1_amount + .clone() + .expect("minimum_token1_amount is not set"), + }; + let instruction = WithdrawCpi { + __program: self.instruction.__program, + + owner: self.instruction.owner.expect("owner is not set"), + + authority: self.instruction.authority.expect("authority is not set"), + + pool_state: self.instruction.pool_state.expect("pool_state is not set"), + + owner_lp_token: self + .instruction + .owner_lp_token + .expect("owner_lp_token is not set"), + + token0_account: self + .instruction + .token0_account + .expect("token0_account is not set"), + + token1_account: self + .instruction + .token1_account + .expect("token1_account is not set"), + + token0_vault: self + .instruction + .token0_vault + .expect("token0_vault is not set"), + + token1_vault: self + .instruction + .token1_vault + .expect("token1_vault is not set"), + + token_program: self + .instruction + .token_program + .expect("token_program is not set"), + + token_program2022: self + .instruction + .token_program2022 + .expect("token_program2022 is not set"), + + vault0_mint: self + .instruction + .vault0_mint + .expect("vault0_mint is not set"), + + vault1_mint: self + .instruction + .vault1_mint + .expect("vault1_mint is not set"), + + lp_mint: self.instruction.lp_mint.expect("lp_mint is not set"), + + memo_program: self + .instruction + .memo_program + .expect("memo_program is not set"), + __args: args, + }; + instruction.invoke_signed_with_remaining_accounts( + signers_seeds, + &self.instruction.__remaining_accounts, + ) + } +} + +#[derive(Clone, Debug)] +struct WithdrawCpiBuilderInstruction<'a, 'b> { + __program: &'b solana_account_info::AccountInfo<'a>, + owner: Option<&'b solana_account_info::AccountInfo<'a>>, + authority: Option<&'b solana_account_info::AccountInfo<'a>>, + pool_state: Option<&'b solana_account_info::AccountInfo<'a>>, + owner_lp_token: Option<&'b solana_account_info::AccountInfo<'a>>, + token0_account: Option<&'b solana_account_info::AccountInfo<'a>>, + token1_account: Option<&'b solana_account_info::AccountInfo<'a>>, + token0_vault: Option<&'b solana_account_info::AccountInfo<'a>>, + token1_vault: Option<&'b solana_account_info::AccountInfo<'a>>, + token_program: Option<&'b solana_account_info::AccountInfo<'a>>, + token_program2022: Option<&'b solana_account_info::AccountInfo<'a>>, + vault0_mint: Option<&'b solana_account_info::AccountInfo<'a>>, + vault1_mint: Option<&'b solana_account_info::AccountInfo<'a>>, + lp_mint: Option<&'b solana_account_info::AccountInfo<'a>>, + memo_program: Option<&'b solana_account_info::AccountInfo<'a>>, + lp_token_amount: Option, + minimum_token0_amount: Option, + minimum_token1_amount: Option, + /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. + __remaining_accounts: Vec<(&'b solana_account_info::AccountInfo<'a>, bool, bool)>, +} diff --git a/e2e/raydium-cpmm/src/generated/mod.rs b/e2e/raydium-cpmm/src/generated/mod.rs new file mode 100644 index 0000000..f3510ac --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/mod.rs @@ -0,0 +1,16 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +pub mod accounts; +pub mod errors; +pub mod events; +pub mod instructions; +pub mod programs; +pub mod shared; +pub mod types; + +pub(crate) use programs::*; diff --git a/e2e/raydium-cpmm/src/generated/programs.rs b/e2e/raydium-cpmm/src/generated/programs.rs new file mode 100644 index 0000000..69d4d08 --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/programs.rs @@ -0,0 +1,11 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +use solana_address::{address, Address}; + +/// `raydium_cp_swap` program ID. +pub const RAYDIUM_CP_SWAP_ID: Address = address!("CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"); diff --git a/e2e/raydium-cpmm/src/generated/shared.rs b/e2e/raydium-cpmm/src/generated/shared.rs new file mode 100644 index 0000000..42eae7f --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/shared.rs @@ -0,0 +1,21 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +#[cfg(feature = "fetch")] +#[derive(Debug, Clone)] +pub struct DecodedAccount { + pub address: solana_address::Address, + pub account: solana_account::Account, + pub data: T, +} + +#[cfg(feature = "fetch")] +#[derive(Debug, Clone)] +pub enum MaybeAccount { + Exists(DecodedAccount), + NotFound(solana_address::Address), +} diff --git a/e2e/raydium-cpmm/src/generated/types/mod.rs b/e2e/raydium-cpmm/src/generated/types/mod.rs new file mode 100644 index 0000000..86bb676 --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/types/mod.rs @@ -0,0 +1,10 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +pub(crate) mod r#observation; + +pub use self::r#observation::*; diff --git a/e2e/raydium-cpmm/src/generated/types/observation.rs b/e2e/raydium-cpmm/src/generated/types/observation.rs new file mode 100644 index 0000000..ce27025 --- /dev/null +++ b/e2e/raydium-cpmm/src/generated/types/observation.rs @@ -0,0 +1,20 @@ +//! This code was AUTOGENERATED using the codama library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun codama to update it. +//! +//! +//! + +use borsh::BorshDeserialize; +use borsh::BorshSerialize; + +/// The element of observations in ObservationState +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct Observation { + /// The block timestamp of the observation + pub block_timestamp: u64, + /// the cumulative of token0 price during the duration time, Q32.32, the remaining 64 bit for overflow + pub cumulative_token0_price_x32: u128, + /// the cumulative of token1 price during the duration time, Q32.32, the remaining 64 bit for overflow + pub cumulative_token1_price_x32: u128, +} diff --git a/e2e/raydium-cpmm/src/lib.rs b/e2e/raydium-cpmm/src/lib.rs new file mode 100644 index 0000000..39f6b55 --- /dev/null +++ b/e2e/raydium-cpmm/src/lib.rs @@ -0,0 +1,4 @@ +mod generated; + +pub use generated::programs::RAYDIUM_CP_SWAP_ID as ID; +pub use generated::*; diff --git a/e2e/test.sh b/e2e/test.sh index 498b7a4..74582ab 100755 --- a/e2e/test.sh +++ b/e2e/test.sh @@ -19,4 +19,5 @@ test_project dummy test_project system test_project memo # test_project meteora # TODO: uncomment after some internal fixes -test_anchor_project anchor \ No newline at end of file +test_anchor_project anchor +test_anchor_project raydium-cpmm \ No newline at end of file diff --git a/package.json b/package.json index 4e9bca1..162c4a0 100644 --- a/package.json +++ b/package.json @@ -41,10 +41,10 @@ "test:unit": "vitest run" }, "dependencies": { - "@codama/errors": "^1.5.0", - "@codama/nodes": "^1.5.0", + "@codama/errors": "^1.6.0", + "@codama/nodes": "^1.6.0", "@codama/renderers-core": "^1.3.5", - "@codama/visitors-core": "^1.5.0", + "@codama/visitors-core": "^1.6.0", "@iarna/toml": "^2.2.5", "@solana/codecs-strings": "^6.0.0", "nunjucks": "^3.2.4", @@ -53,7 +53,7 @@ "devDependencies": { "@changesets/changelog-github": "^0.5.1", "@changesets/cli": "^2.29.7", - "@codama/nodes-from-anchor": "^1.3.6", + "@codama/nodes-from-anchor": "^1.4.1", "@solana/eslint-config-solana": "^5.0.0", "@solana/prettier-config-solana": "0.0.5", "@types/node": "^25", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0146059..41dcce3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,17 +9,17 @@ importers: .: dependencies: '@codama/errors': - specifier: ^1.5.0 - version: 1.5.0 + specifier: ^1.6.0 + version: 1.6.0 '@codama/nodes': - specifier: ^1.5.0 - version: 1.5.0 + specifier: ^1.6.0 + version: 1.6.0 '@codama/renderers-core': specifier: ^1.3.5 version: 1.3.5 '@codama/visitors-core': - specifier: ^1.5.0 - version: 1.5.0 + specifier: ^1.6.0 + version: 1.6.0 '@iarna/toml': specifier: ^2.2.5 version: 2.2.5 @@ -40,8 +40,8 @@ importers: specifier: ^2.29.7 version: 2.29.8(@types/node@25.2.3) '@codama/nodes-from-anchor': - specifier: ^1.3.6 - version: 1.3.6(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + specifier: ^1.4.1 + version: 1.4.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/eslint-config-solana': specifier: ^5.0.0 version: 5.0.0(@eslint/js@9.39.1)(@types/eslint__js@8.42.3)(eslint-plugin-jest@29.0.1(@typescript-eslint/eslint-plugin@8.43.0(@typescript-eslint/parser@8.43.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(jest@30.1.3(@types/node@25.2.3))(typescript@5.9.3))(eslint-plugin-react-hooks@5.2.0(eslint@9.39.1))(eslint-plugin-simple-import-sort@12.1.1(eslint@9.39.1))(eslint-plugin-sort-keys-fix@1.1.2)(eslint-plugin-typescript-sort-keys@3.3.0(@typescript-eslint/parser@8.43.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(globals@14.0.0)(jest@30.1.3(@types/node@25.2.3))(typescript-eslint@8.43.0(eslint@9.39.1)(typescript@5.9.3))(typescript@5.9.3) @@ -314,40 +314,40 @@ packages: '@changesets/write@0.4.0': resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} - '@codama/errors@1.4.4': - resolution: {integrity: sha512-XC86H5X+zGTi0cSRKLc+wFkeXNsvnh+ttOgVnVHIljmXOJWbUt9wXhKding3UftipLWwlHPuoswERJ0vS0mO2A==} - hasBin: true - '@codama/errors@1.5.0': resolution: {integrity: sha512-i4cS+S7JaZXhofQHFY3cwzt8rqxUVPNaeJND5VOyKUbtcOi933YXJXk52gDG4mc+CpGqHJijsJjfSpr1lJGxzg==} hasBin: true - '@codama/node-types@1.4.4': - resolution: {integrity: sha512-uUeIz34Id/TTAMi4k5OVl9FByM/PawnlNIIVqgpooH9AS0UlniICZ+KJ/mdHZidJs/AGo6bSRoOPS1BLtMajyw==} + '@codama/errors@1.6.0': + resolution: {integrity: sha512-Evj9wO5lqvxvbjxG856ITY5lhRN7SqoYfRX4tMMBjs8J/kT+pKQ8qL0hz9OynOOv/5mWn9Q/sPCNzQ6CUscibQ==} + hasBin: true '@codama/node-types@1.5.0': resolution: {integrity: sha512-Ebz2vOUukmNaFXWdkni1ZihXkAIUnPYtqIMXYxKXOxjMP+TGz2q0lGtRo7sqw1pc2ksFBIkfBp5pZsl5p6gwXA==} - '@codama/nodes-from-anchor@1.3.6': - resolution: {integrity: sha512-614DZS9H5gW16Rkeu0ES8BHnDvbd8M9FLqPWnp9QUE0b+wCvWB36yZiRylJ4fw8gRDSc+FR7C2i3NXKycpvBEg==} + '@codama/node-types@1.6.0': + resolution: {integrity: sha512-atIJW2/3MjPYey0bNlE86W9Gvq9aq8bud7zT7PMyyhj98mbmLqPwT4wclPdbFua0fROLkq17z3bXaaJy5FqSEw==} - '@codama/nodes@1.4.4': - resolution: {integrity: sha512-JzlY5qLk3rhsnu0nerC/Vkc9/2HjdsLtEpBtST0dxC1j9kpfHvIc2uyIj+5hlB1YIBRJIDNo+UOHGla8hidkaA==} + '@codama/nodes-from-anchor@1.4.1': + resolution: {integrity: sha512-2RuLk/pCn+KXYSZoSA1k/sP1C4VzlbNFtMu2AxiBMXyyR8WYKFkrRghjI4IR+otwhknV7QhdFr8h5tuD+TSQ4g==} '@codama/nodes@1.5.0': resolution: {integrity: sha512-yg+xmorWiMNjS3n19CGIt/FZ/ZCuDIu+HEY45bq6gHu1MN3RtJZY+Q3v0ErnBPA60D8mNWkvkKoeSZXfzcAvfw==} + '@codama/nodes@1.6.0': + resolution: {integrity: sha512-F6Hy3REfl+Ih5R3jldPqEMjFqaPj871iBWX/LV0EtNK0xn7E4DG/3XCK4wlbHrOT9Z1NsiA70e0M1uChzmIrsw==} + '@codama/renderers-core@1.3.5': resolution: {integrity: sha512-MuZLU+3LZPQb1HuZffwZl+v5JHQDe5LYHGhA1wTMNlwRedYIysSxBjogHNciNIHsKP3JjmqyYmLO5LCEp3hjaQ==} - '@codama/visitors-core@1.4.4': - resolution: {integrity: sha512-vk/4tczViAUHa7c8PF7FxN+JWbuTcDB0pIdrDbbO6eBPKDPQGZCUCEp6rXIYBVxfO129jWrNf2+CuyYre/c/vA==} - '@codama/visitors-core@1.5.0': resolution: {integrity: sha512-3PIAlBX0a06hIxzyPtQMfQcqWGFBgfbwysSwcXBbvHUYbemwhD6xwlBKJuqTwm9DyFj3faStp5fpvcp03Rjxtw==} - '@codama/visitors@1.4.4': - resolution: {integrity: sha512-3w2aRNvGV6/rXTfRDynXR82zoAqX0P4tlfQ/zT4I4Bby4xTobKgDZLyAstodmA0D878eKW7sMg4Gb1m1R5dOig==} + '@codama/visitors-core@1.6.0': + resolution: {integrity: sha512-YG0rExvLbBCDAzXnZX6Imu4KwDoZrZz9NF232/nzs9Dr8uQuEWJ81x4VR9UxIcANHcF0+XwJzHamSwhZroAtjQ==} + + '@codama/visitors@1.6.0': + resolution: {integrity: sha512-11/adC2WiH3+iMWluXkb+ae46sjoDm2xztI+CBEeIcBQd6mm4iuJTTRS0yrGfDwAJE1XzI/nc2MrR0Pvn+Rvvw==} '@emnapi/core@1.7.1': resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} @@ -1125,11 +1125,14 @@ packages: '@sinonjs/fake-timers@13.0.5': resolution: {integrity: sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==} - '@solana/codecs-core@5.0.0': - resolution: {integrity: sha512-rCG2d8OaamVF2/J//YyCgDqNJpUytVVltw9C8mJtEz5c6Se/LR6BFuG8g4xeJswq/ab4RFk5/HFdgbvNjKgQjA==} + '@solana/codecs-core@5.5.1': + resolution: {integrity: sha512-TgBt//bbKBct0t6/MpA8ElaOA3sa8eYVvR7LGslCZ84WiAwwjCY0lW/lOYsFHJQzwREMdUyuEyy5YWBKtdh8Rw==} engines: {node: '>=20.18.0'} peerDependencies: - typescript: '>=5.3.3' + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true '@solana/codecs-core@6.0.1': resolution: {integrity: sha512-OnUQk94qfvfE0nVveZ638aNUL3tyRJoorUFiAG0ICTGUo3c6fkYb8vH23o/5O2qmuSmYND1sn+UCaldNMVkFpg==} @@ -1140,17 +1143,23 @@ packages: typescript: optional: true - '@solana/codecs-data-structures@5.0.0': - resolution: {integrity: sha512-y503Pqmv0LHcfcf0vQJGaxDvydQJbyCo8nK3nxn56EhFj5lBQ1NWb3WvTd83epigwuZurW2MhJARrpikfhQglQ==} + '@solana/codecs-data-structures@5.5.1': + resolution: {integrity: sha512-97bJWGyUY9WvBz3mX1UV3YPWGDTez6btCfD0ip3UVEXJbItVuUiOkzcO5iFDUtQT5riKT6xC+Mzl+0nO76gd0w==} engines: {node: '>=20.18.0'} peerDependencies: - typescript: '>=5.3.3' + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@solana/codecs-numbers@5.0.0': - resolution: {integrity: sha512-a2+skRLuUK02f/XFe4L0e1+wHCyfK25PkyseFps1v1l4pvevukFwth/EhSyrs6w5CsTJRVoR7MuE3E00PM4egw==} + '@solana/codecs-numbers@5.5.1': + resolution: {integrity: sha512-rllMIZAHqmtvC0HO/dc/21wDuWaD0B8Ryv8o+YtsICQBuiL/0U4AGwH7Pi5GNFySYk0/crSuwfIqQFtmxNSPFw==} engines: {node: '>=20.18.0'} peerDependencies: - typescript: '>=5.3.3' + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true '@solana/codecs-numbers@6.0.1': resolution: {integrity: sha512-ZrI1NjUsf4I+Klue/2rlQbZLcGRom/G2E4VB/8x4IEHGOeFLQhXcxmnib8kdgomQRYOzF1BjVDmCYxvZr+6AWA==} @@ -1161,12 +1170,17 @@ packages: typescript: optional: true - '@solana/codecs-strings@5.0.0': - resolution: {integrity: sha512-ALkRwpV8bGR6qjAYw0YXZwp2YI4wzvKOJGmx04Ut8gMdbaUx7qOcJkhEQKI6ZVC3lAWSIS1N1wGccUZDwvfKxw==} + '@solana/codecs-strings@5.5.1': + resolution: {integrity: sha512-7klX4AhfHYA+uKKC/nxRGP2MntbYQCR3N6+v7bk1W/rSxYuhNmt+FN8aoThSZtWIKwN6BEyR1167ka8Co1+E7A==} engines: {node: '>=20.18.0'} peerDependencies: fastestsmallesttextencoderdecoder: ^1.0.22 - typescript: '>=5.3.3' + typescript: ^5.0.0 + peerDependenciesMeta: + fastestsmallesttextencoderdecoder: + optional: true + typescript: + optional: true '@solana/codecs-strings@6.0.1': resolution: {integrity: sha512-OmMIfMFbbJVIxveBeATKCj9DsmZ8l4vJPnOLHUop0hLWRiYHTQ1qokMqfk/X8PCmUjXmbXnlp63BikGtdKN3/g==} @@ -1180,18 +1194,24 @@ packages: typescript: optional: true - '@solana/codecs@5.0.0': - resolution: {integrity: sha512-KOw0gFUSBxIMDWLJ3AkVFkEci91dw0Rpx3C6y83Our7fSW+SEP8vRZklCElieYR85LHVB1QIEhoeHR7rc+Ifkw==} + '@solana/codecs@5.5.1': + resolution: {integrity: sha512-Vea29nJub/bXjfzEV7ZZQ/PWr1pYLZo3z0qW0LQL37uKKVzVFRQlwetd7INk3YtTD3xm9WUYr7bCvYUk3uKy2g==} engines: {node: '>=20.18.0'} peerDependencies: - typescript: '>=5.3.3' + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@solana/errors@5.0.0': - resolution: {integrity: sha512-gTuhzO6E+ydfAAzqmqdPcvFyJwAzFKKIrqtnZPpgAuomcPYu+HSo0tuwSM/cTX0djmHt+GoOsf/julph+nvs2w==} + '@solana/errors@5.5.1': + resolution: {integrity: sha512-vFO3p+S7HoyyrcAectnXbdsMfwUzY2zYFUc2DEe5BwpiE9J1IAxPBGjOWO6hL1bbYdBrlmjNx8DXCslqS+Kcmg==} engines: {node: '>=20.18.0'} hasBin: true peerDependencies: - typescript: '>=5.3.3' + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true '@solana/errors@6.0.1': resolution: {integrity: sha512-sMe5GCsXto8F1KDeq9GbZR0+m841SqEYep3NAcYlC0lqF2RG4giaaPQHgrWI5DJR/L7yc8FzUIQfTxnaN7bwOQ==} @@ -1219,11 +1239,14 @@ packages: typescript: ^5.6 typescript-eslint: ^8.11.0 - '@solana/options@5.0.0': - resolution: {integrity: sha512-ezHVBFb9FXVSn8LUVRD2tLb6fejU0x8KtGEYyCYh0J0pQuXSITV0IQCjcEopvu/ZxWdXOJyzjvmymnhz90on5A==} + '@solana/options@5.5.1': + resolution: {integrity: sha512-eo971c9iLNLmk+yOFyo7yKIJzJ/zou6uKpy6mBuyb/thKtS/haiKIc3VLhyTXty3OH2PW8yOlORJnv4DexJB8A==} engines: {node: '>=20.18.0'} peerDependencies: - typescript: '>=5.3.3' + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true '@solana/prettier-config-solana@0.0.5': resolution: {integrity: sha512-igtLH1QaX5xzSLlqteexRIg9X1QKA03xKYQc2qY1TrMDDhxKXoRZOStQPWdita2FVJzxTGz/tdMGC1vS0biRcg==} @@ -1781,10 +1804,6 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - commander@14.0.1: - resolution: {integrity: sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A==} - engines: {node: '>=20'} - commander@14.0.2: resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} engines: {node: '>=20'} @@ -3628,66 +3647,66 @@ snapshots: human-id: 4.1.3 prettier: 2.8.8 - '@codama/errors@1.4.4': - dependencies: - '@codama/node-types': 1.4.4 - commander: 14.0.2 - picocolors: 1.1.1 - '@codama/errors@1.5.0': dependencies: '@codama/node-types': 1.5.0 commander: 14.0.2 picocolors: 1.1.1 - '@codama/node-types@1.4.4': {} + '@codama/errors@1.6.0': + dependencies: + '@codama/node-types': 1.6.0 + commander: 14.0.3 + picocolors: 1.1.1 '@codama/node-types@1.5.0': {} - '@codama/nodes-from-anchor@1.3.6(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@codama/node-types@1.6.0': {} + + '@codama/nodes-from-anchor@1.4.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@codama/errors': 1.4.4 - '@codama/nodes': 1.4.4 - '@codama/visitors': 1.4.4 + '@codama/errors': 1.6.0 + '@codama/nodes': 1.6.0 + '@codama/visitors': 1.6.0 '@noble/hashes': 2.0.1 - '@solana/codecs': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/codecs': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) transitivePeerDependencies: - fastestsmallesttextencoderdecoder - typescript - '@codama/nodes@1.4.4': - dependencies: - '@codama/errors': 1.4.4 - '@codama/node-types': 1.4.4 - '@codama/nodes@1.5.0': dependencies: '@codama/errors': 1.5.0 '@codama/node-types': 1.5.0 + '@codama/nodes@1.6.0': + dependencies: + '@codama/errors': 1.6.0 + '@codama/node-types': 1.6.0 + '@codama/renderers-core@1.3.5': dependencies: '@codama/errors': 1.5.0 '@codama/nodes': 1.5.0 '@codama/visitors-core': 1.5.0 - '@codama/visitors-core@1.4.4': - dependencies: - '@codama/errors': 1.4.4 - '@codama/nodes': 1.4.4 - json-stable-stringify: 1.3.0 - '@codama/visitors-core@1.5.0': dependencies: '@codama/errors': 1.5.0 '@codama/nodes': 1.5.0 json-stable-stringify: 1.3.0 - '@codama/visitors@1.4.4': + '@codama/visitors-core@1.6.0': + dependencies: + '@codama/errors': 1.6.0 + '@codama/nodes': 1.6.0 + json-stable-stringify: 1.3.0 + + '@codama/visitors@1.6.0': dependencies: - '@codama/errors': 1.4.4 - '@codama/nodes': 1.4.4 - '@codama/visitors-core': 1.4.4 + '@codama/errors': 1.6.0 + '@codama/nodes': 1.6.0 + '@codama/visitors-core': 1.6.0 '@emnapi/core@1.7.1': dependencies: @@ -4338,9 +4357,10 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 - '@solana/codecs-core@5.0.0(typescript@5.9.3)': + '@solana/codecs-core@5.5.1(typescript@5.9.3)': dependencies: - '@solana/errors': 5.0.0(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: typescript: 5.9.3 '@solana/codecs-core@6.0.1(typescript@5.9.3)': @@ -4349,17 +4369,19 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@solana/codecs-data-structures@5.0.0(typescript@5.9.3)': + '@solana/codecs-data-structures@5.5.1(typescript@5.9.3)': dependencies: - '@solana/codecs-core': 5.0.0(typescript@5.9.3) - '@solana/codecs-numbers': 5.0.0(typescript@5.9.3) - '@solana/errors': 5.0.0(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: typescript: 5.9.3 - '@solana/codecs-numbers@5.0.0(typescript@5.9.3)': + '@solana/codecs-numbers@5.5.1(typescript@5.9.3)': dependencies: - '@solana/codecs-core': 5.0.0(typescript@5.9.3) - '@solana/errors': 5.0.0(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: typescript: 5.9.3 '@solana/codecs-numbers@6.0.1(typescript@5.9.3)': @@ -4369,11 +4391,12 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@solana/codecs-strings@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@solana/codecs-strings@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@solana/codecs-core': 5.0.0(typescript@5.9.3) - '@solana/codecs-numbers': 5.0.0(typescript@5.9.3) - '@solana/errors': 5.0.0(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: fastestsmallesttextencoderdecoder: 1.0.22 typescript: 5.9.3 @@ -4386,21 +4409,23 @@ snapshots: fastestsmallesttextencoderdecoder: 1.0.22 typescript: 5.9.3 - '@solana/codecs@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@solana/codecs@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@solana/codecs-core': 5.0.0(typescript@5.9.3) - '@solana/codecs-data-structures': 5.0.0(typescript@5.9.3) - '@solana/codecs-numbers': 5.0.0(typescript@5.9.3) - '@solana/codecs-strings': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/options': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/options': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/errors@5.0.0(typescript@5.9.3)': + '@solana/errors@5.5.1(typescript@5.9.3)': dependencies: chalk: 5.6.2 - commander: 14.0.1 + commander: 14.0.2 + optionalDependencies: typescript: 5.9.3 '@solana/errors@6.0.1(typescript@5.9.3)': @@ -4425,13 +4450,14 @@ snapshots: typescript: 5.9.3 typescript-eslint: 8.43.0(eslint@9.39.1)(typescript@5.9.3) - '@solana/options@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@solana/options@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@solana/codecs-core': 5.0.0(typescript@5.9.3) - '@solana/codecs-data-structures': 5.0.0(typescript@5.9.3) - '@solana/codecs-numbers': 5.0.0(typescript@5.9.3) - '@solana/codecs-strings': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 5.0.0(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder @@ -5039,8 +5065,6 @@ snapshots: color-name@1.1.4: {} - commander@14.0.1: {} - commander@14.0.2: {} commander@14.0.3: {} @@ -5355,7 +5379,8 @@ snapshots: fast-levenshtein@2.0.6: {} - fastestsmallesttextencoderdecoder@1.0.22: {} + fastestsmallesttextencoderdecoder@1.0.22: + optional: true fastq@1.19.1: dependencies: diff --git a/public/templates/eventsMod.njk b/public/templates/eventsMod.njk new file mode 100644 index 0000000..db434ef --- /dev/null +++ b/public/templates/eventsMod.njk @@ -0,0 +1,23 @@ +{% extends "layout.njk" %} + +{% block main %} + +{% for event in eventsToExport | sort(false, false, 'name') %} + pub(crate) mod r#{{ event.name | snakeCase }}; +{% endfor %} +{% for program in programsToExport | sort(false, false, 'name') %} +{% if programsWithEventEnum.has(program.name) %} + pub(crate) mod r#{{ program.name | snakeCase }}_events; +{% endif %} +{% endfor %} + +{% for event in eventsToExport | sort(false, false, 'name') %} + pub use self::r#{{ event.name | snakeCase }}::*; +{% endfor %} +{% for program in programsToExport | sort(false, false, 'name') %} +{% if programsWithEventEnum.has(program.name) %} + pub use self::r#{{ program.name | snakeCase }}_events::*; +{% endif %} +{% endfor %} + +{% endblock %} diff --git a/public/templates/eventsPage.njk b/public/templates/eventsPage.njk new file mode 100644 index 0000000..05b176a --- /dev/null +++ b/public/templates/eventsPage.njk @@ -0,0 +1,39 @@ +{% extends "layout.njk" %} +{% import "macros.njk" as macros %} + +{% block main %} + +{{ imports }} + +{{ macros.docblock(event.docs) }} +{{- typeManifest.type }} + +{% for nestedStruct in typeManifest.nestedStructs %} +{{ nestedStruct }} +{% endfor %} + +{{ discriminatorConstants }} + +{% if constantDiscriminators.length > 0 %} +impl {{ event.name | pascalCase }} { + #[inline(always)] + pub fn from_bytes(data: &[u8]) -> Result { +{% for disc in constantDiscriminators %} +{% if disc.offset === 0 %} + if data.get(..{{ disc.name }}.len()) != Some(&{{ disc.name }}[..]) { +{% else %} + if data.get({{ disc.offset }}..{{ disc.offset }} + {{ disc.name }}.len()) != Some(&{{ disc.name }}[..]) { +{% endif %} + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "invalid event discriminator", + )); + } +{% endfor %} + let mut data = &data[{{ hiddenPrefixSkip }}..]; + Self::deserialize(&mut data) + } +} +{% endif %} + +{% endblock %} diff --git a/public/templates/programEventsPage.njk b/public/templates/programEventsPage.njk new file mode 100644 index 0000000..71a15fa --- /dev/null +++ b/public/templates/programEventsPage.njk @@ -0,0 +1,55 @@ +{% extends "layout.njk" %} + +{% block main %} + +{{ imports }} + +/// Event kinds for the `{{ program.name | snakeCase }}` program. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum {{ program.name | pascalCase }}EventKind { +{% for event in eventsWithDiscriminators | sort(false, false, 'name') %} + {{ event.name | pascalCase }}, +{% endfor %} +} + +/// Identifies a `{{ program.name | snakeCase }}` event from the provided data. +pub fn identify_{{ program.name | snakeCase }}_event(data: &[u8]) -> Option<{{ program.name | pascalCase }}EventKind> { +{% for event in eventsWithDiscriminators | sort(false, false, 'name') %} + if {{ event.conditions | join(' && ') }} { + return Some({{ program.name | pascalCase }}EventKind::{{ event.name | pascalCase }}); + } +{% endfor %} + None +} + +/// Parsed event variants for the `{{ program.name | snakeCase }}` program. +#[derive(Clone, Debug, PartialEq)] +pub enum {{ program.name | pascalCase }}Event { +{% for event in eventsWithDiscriminators | sort(false, false, 'name') %} + {{ event.name | pascalCase }}({{ event.name | pascalCase }}), +{% endfor %} +} + +/// Tries to parse a `{{ program.name | snakeCase }}` event from the provided data. +pub fn try_parse_{{ program.name | snakeCase }}_event(data: &[u8]) -> Option> { + let event_kind = identify_{{ program.name | snakeCase }}_event(data)?; + Some(match event_kind { +{% for event in eventsWithDiscriminators | sort(false, false, 'name') %} +{% if event.hiddenPrefixSkip != '0' %} + {{ program.name | pascalCase }}EventKind::{{ event.name | pascalCase }} => { + let mut data = &data[{{ event.hiddenPrefixSkip }}..]; + {{ event.name | pascalCase }}::deserialize(&mut data) + .map({{ program.name | pascalCase }}Event::{{ event.name | pascalCase }}) + } +{% else %} + {{ program.name | pascalCase }}EventKind::{{ event.name | pascalCase }} => { + let mut data = data; + {{ event.name | pascalCase }}::deserialize(&mut data) + .map({{ program.name | pascalCase }}Event::{{ event.name | pascalCase }}) + } +{% endif %} +{% endfor %} + }) +} + +{% endblock %} diff --git a/public/templates/rootMod.njk b/public/templates/rootMod.njk index d792bcb..d4c553c 100644 --- a/public/templates/rootMod.njk +++ b/public/templates/rootMod.njk @@ -9,6 +9,9 @@ {% if programsToExport.length > 0 %} pub mod errors; {% endif %} + {% if eventsToExport.length > 0 %} + pub mod events; + {% endif %} {% if instructionsToExport.length > 0 %} pub mod instructions; {% endif %} diff --git a/src/ImportMap.ts b/src/ImportMap.ts index f4a59c6..9dee67e 100644 --- a/src/ImportMap.ts +++ b/src/ImportMap.ts @@ -7,6 +7,7 @@ const DEFAULT_MODULE_MAP: Record = { generated: 'crate::generated', generatedAccounts: 'crate::generated::accounts', generatedErrors: 'crate::generated::errors', + generatedEvents: 'crate::generated::events', generatedInstructions: 'crate::generated::instructions', generatedTypes: 'crate::generated::types', hooked: 'crate::hooked', diff --git a/src/getRenderMapVisitor.ts b/src/getRenderMapVisitor.ts index 55e65a1..21c6a3e 100644 --- a/src/getRenderMapVisitor.ts +++ b/src/getRenderMapVisitor.ts @@ -1,7 +1,10 @@ import { logWarn } from '@codama/errors'; import { + definedTypeNode, + EventNode, getAllAccounts, getAllDefinedTypes, + getAllEvents, getAllInstructionsWithSubs, getAllPrograms, InstructionNode, @@ -31,9 +34,12 @@ import { ImportMap } from './ImportMap'; import { renderValueNode } from './renderValueNodeVisitor'; import { CargoDependencies, + constantDiscriminatorName, Fragment, + getDiscriminatorConditions, getDiscriminatorConstants, getImportFromFactory, + GetImportFromFunction, getTraitsFromNodeFactory, LinkOverrides, render, @@ -54,6 +60,7 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) { const linkables = new LinkableDictionary(); const stack = new NodeStack(); let program: ProgramNode | null = null; + const programsWithEventEnum = new Set(); const renderParentInstructions = options.renderParentInstructions ?? false; const dependencyMap = options.dependencyMap ?? {}; @@ -68,7 +75,7 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) { return pipe( staticVisitor(() => createRenderMap(), { - keys: ['rootNode', 'programNode', 'instructionNode', 'accountNode', 'definedTypeNode'], + keys: ['rootNode', 'programNode', 'instructionNode', 'accountNode', 'definedTypeNode', 'eventNode'], }), v => extendVisitor(v, { @@ -148,6 +155,65 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) { }); }, + visitEvent(node) { + const discriminators = node.discriminators ?? []; + const innerType = resolveNestedTypeNode(node.data); + // Wrap as definedTypeNode so typeManifestVisitor generates the struct with derives. + const syntheticType = definedTypeNode({ + docs: node.docs, + name: node.name, + type: innerType, + }); + const typeManifest = visit(syntheticType, typeManifestVisitor); + + // Discriminator constants. + const fields = isNode(innerType, 'structTypeNode') ? innerType.fields : []; + const discriminatorConstants = getDiscriminatorConstants({ + discriminatorNodes: discriminators, + fields, + getImportFrom, + prefix: node.name, + typeManifestVisitor, + }); + + const hasFromBytes = eventHasFromBytes(node); + const allConstantDiscriminators = hasFromBytes + ? discriminators + .filter(isNodeFilter('constantDiscriminatorNode')) + .map(d => ({ + name: snakeCase( + constantDiscriminatorName(node.name, d, discriminators), + ).toUpperCase(), + offset: d.offset, + })) + .sort((a, b) => a.offset - b.offset) + : []; + + const hiddenPrefixSkipResult = hasFromBytes + ? getHiddenPrefixSkip(node, allConstantDiscriminators) + : null; + const generateFromBytes = hasFromBytes && hiddenPrefixSkipResult !== null; + const hiddenPrefixSkip = hiddenPrefixSkipResult ?? '0'; + const constantDiscriminators = generateFromBytes ? allConstantDiscriminators : []; + + const imports = new ImportMap() + .mergeWithManifest(typeManifest) + .mergeWith(discriminatorConstants.imports) + .remove(`generatedEvents::${pascalCase(node.name)}`); + + return createRenderMap(`events/${snakeCase(node.name)}.rs`, { + content: render('eventsPage.njk', { + constantDiscriminators, + discriminatorConstants: discriminatorConstants.render, + event: node, + hiddenPrefixSkip, + imports: imports.toString(dependencyMap), + typeManifest, + }), + imports, + }); + }, + visitInstruction(node) { // Imports. const imports = new ImportMap(); @@ -260,6 +326,7 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) { let renders = mergeRenderMaps([ ...node.accounts.map(account => visit(account, self)), ...node.definedTypes.map(type => visit(type, self)), + ...(node.events ?? []).map(event => visit(event, self)), ...getAllInstructionsWithSubs(node, { leavesOnly: !renderParentInstructions, }).map(ix => visit(ix, self)), @@ -277,6 +344,23 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) { }); } + // Program events (enum + identify + try_parse). + const programEventsRender = buildProgramEventsRender( + node.events ?? [], + node, + getImportFrom, + typeManifestVisitor, + dependencyMap, + ); + if (programEventsRender) { + programsWithEventEnum.add(node.name); + renders = addToRenderMap( + renders, + `events/${snakeCase(node.name)}_events.rs`, + programEventsRender, + ); + } + program = null; return renders; }, @@ -288,21 +372,28 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) { leavesOnly: !renderParentInstructions, }); const definedTypesToExport = getAllDefinedTypes(node); + const eventsToExport = getAllEvents(node).filter(Boolean); const hasAnythingToExport = programsToExport.length > 0 || accountsToExport.length > 0 || instructionsToExport.length > 0 || - definedTypesToExport.length > 0; + definedTypesToExport.length > 0 || + eventsToExport.length > 0; const ctx = { accountsToExport, definedTypesToExport, + eventsToExport, hasAnythingToExport, instructionsToExport, programsToExport, + programsWithEventEnum, root: node, }; + // Visit programs first so programsWithEventEnum is populated. + const programRenders = getAllPrograms(node).map(p => visit(p, self)); + return mergeRenderMaps([ createRenderMap({ ['accounts/mod.rs']: @@ -313,6 +404,10 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) { programsToExport.length > 0 ? { content: render('errorsMod.njk', ctx), imports: new ImportMap() } : undefined, + ['events/mod.rs']: + eventsToExport.length > 0 + ? { content: render('eventsMod.njk', ctx), imports: new ImportMap() } + : undefined, ['instructions/mod.rs']: instructionsToExport.length > 0 ? { content: render('instructionsMod.njk', ctx), imports: new ImportMap() } @@ -331,7 +426,7 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) { ? { content: render('definedTypesMod.njk', ctx), imports: new ImportMap() } : undefined, }), - ...getAllPrograms(node).map(p => visit(p, self)), + ...programRenders, ]); }, }), @@ -340,6 +435,103 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) { ); } +function eventHasFromBytes(event: EventNode): boolean { + const hasConstantDiscriminator = (event.discriminators ?? []).some(d => isNode(d, 'constantDiscriminatorNode')); + const dataHasHiddenPrefix = isNode(event.data, 'hiddenPrefixTypeNode'); + return hasConstantDiscriminator && dataHasHiddenPrefix; +} + +function getHiddenPrefixSkip( + event: EventNode, + constantDiscriminators: { name: string; offset: number }[], +): string | null { + if (!isNode(event.data, 'hiddenPrefixTypeNode')) { + return '0'; + } + let hasNonFixedSize = false; + const prefixSize = event.data.prefix.reduce((sum, p) => { + if (!isNode(p.type, 'fixedSizeTypeNode')) { + logWarn( + `[Rust] Event [${event.name}] has a non-fixed-size hidden prefix entry; ` + + `from_bytes will not be generated.`, + ); + hasNonFixedSize = true; + return sum; + } + return sum + p.type.size; + }, 0); + if (hasNonFixedSize) { + return null; + } + const firstDisc = constantDiscriminators.find(d => d.offset === 0); + if (event.data.prefix.length === 1 && firstDisc) { + return `${firstDisc.name}.len()`; + } + return String(prefixSize); +} + +function buildProgramEventsRender( + events: EventNode[], + programNode: ProgramNode, + getImportFrom: GetImportFromFunction, + typeManifestVisitor: ReturnType, + dependencyMap: Record, +): { content: string; imports: ImportMap } | null { + if (events.length === 0) { + return null; + } + + const imports = new ImportMap(); + + const eventsWithDiscriminators = events + .filter(event => (event.discriminators ?? []).length > 0) + .flatMap(event => { + const innerType = resolveNestedTypeNode(event.data); + const fields = isNode(innerType, 'structTypeNode') ? innerType.fields : []; + const { conditions, imports: condImports } = getDiscriminatorConditions({ + discriminatorNodes: event.discriminators ?? [], + fields, + getImportFrom, + importPrefix: 'generatedEvents', + prefix: event.name, + typeManifestVisitor, + }); + const discriminators = event.discriminators ?? []; + const eventConstantDiscs = discriminators.filter(isNodeFilter('constantDiscriminatorNode')).map(d => ({ + name: snakeCase(constantDiscriminatorName(event.name, d, discriminators)).toUpperCase(), + offset: d.offset, + })); + const hiddenPrefixSkipResult = isNode(event.data, 'hiddenPrefixTypeNode') + ? getHiddenPrefixSkip(event, eventConstantDiscs) + : '0'; + + if (hiddenPrefixSkipResult === null || conditions.length === 0) { + return []; + } + + imports.mergeWith(condImports); + return [{ ...event, conditions, hiddenPrefixSkip: hiddenPrefixSkipResult }]; + }); + + if (eventsWithDiscriminators.length === 0) { + return null; + } + + imports.add('borsh::BorshDeserialize'); + eventsWithDiscriminators.forEach(event => { + imports.add(`generatedEvents::${pascalCase(event.name)}`); + }); + + return { + content: render('programEventsPage.njk', { + eventsWithDiscriminators, + imports: imports.toString(dependencyMap), + program: programNode, + }), + imports, + }; +} + function getConflictsForInstructionAccountsAndArgs(instruction: InstructionNode): string[] { const allNames = [ ...instruction.accounts.map(account => account.name), diff --git a/src/utils/discriminatorConstant.ts b/src/utils/discriminatorConstant.ts index 8e88e34..943abb2 100644 --- a/src/utils/discriminatorConstant.ts +++ b/src/utils/discriminatorConstant.ts @@ -6,6 +6,7 @@ import { InstructionArgumentNode, isNode, isNodeFilter, + SizeDiscriminatorNode, snakeCase, StructFieldTypeNode, VALUE_NODES, @@ -70,10 +71,7 @@ function getConstantDiscriminatorConstant( ): Fragment { const { discriminatorNodes, getImportFrom, prefix, typeManifestVisitor } = scope; - const index = discriminatorNodes.filter(isNodeFilter('constantDiscriminatorNode')).indexOf(discriminatorNode); - const suffix = index <= 0 ? '' : `_${index + 1}`; - - const name = camelCase(`${prefix}_discriminator${suffix}`); + const name = constantDiscriminatorName(prefix, discriminatorNode, discriminatorNodes); const typeManifest = visit(discriminatorNode.constant.type, typeManifestVisitor); const value = renderValueNode(discriminatorNode.constant.value, getImportFrom); return getConstant(name, typeManifest, value); @@ -95,13 +93,144 @@ function getFieldDiscriminatorConstant( return null; } - const name = camelCase(`${prefix}_${discriminatorNode.name}`); const typeManifest = visit(field.type, typeManifestVisitor); const value = renderValueNode(field.defaultValue, getImportFrom); - return getConstant(name, typeManifest, value); + return getConstant(fieldDiscriminatorName(prefix, discriminatorNode.name), typeManifest, value); } function getConstant(name: string, typeManifest: TypeManifest, value: Fragment): Fragment { const type: Fragment = { imports: typeManifest.imports, render: typeManifest.type }; return mergeFragments([type, value], ([t, v]) => `pub const ${snakeCase(name).toUpperCase()}: ${t} = ${v};`); } + +export function constantDiscriminatorName( + prefix: string, + discriminatorNode: ConstantDiscriminatorNode, + discriminatorNodes: DiscriminatorNode[], +): string { + const index = discriminatorNodes.filter(isNodeFilter('constantDiscriminatorNode')).indexOf(discriminatorNode); + const suffix = index <= 0 ? '' : `_${index + 1}`; + return camelCase(`${prefix}_discriminator${suffix}`); +} + +function fieldDiscriminatorName(prefix: string, fieldName: string): string { + return camelCase(`${prefix}_${fieldName}`); +} + +export function getDiscriminatorConditions(scope: { + discriminatorNodes: DiscriminatorNode[]; + fields: InstructionArgumentNode[] | StructFieldTypeNode[]; + getImportFrom: GetImportFromFunction; + importPrefix: string; + prefix: string; + typeManifestVisitor: ReturnType; +}): { conditions: string[]; imports: ImportMap } { + const imports = new ImportMap(); + const conditions = scope.discriminatorNodes + .map(node => getDiscriminatorCondition(node, scope, imports)) + .filter(Boolean) as string[]; + return { conditions, imports }; +} + +function getDiscriminatorCondition( + discriminatorNode: DiscriminatorNode, + scope: { + discriminatorNodes: DiscriminatorNode[]; + fields: InstructionArgumentNode[] | StructFieldTypeNode[]; + getImportFrom: GetImportFromFunction; + importPrefix: string; + prefix: string; + typeManifestVisitor: ReturnType; + }, + imports: ImportMap, +): string | null { + switch (discriminatorNode.kind) { + case 'sizeDiscriminatorNode': + return getSizeCondition(discriminatorNode); + case 'constantDiscriminatorNode': + return getConstantCondition(discriminatorNode, scope, imports); + case 'fieldDiscriminatorNode': + return getFieldCondition(discriminatorNode, scope, imports); + default: + return null; + } +} + +function getSizeCondition(discriminatorNode: SizeDiscriminatorNode): string { + return `data.len() == ${discriminatorNode.size}`; +} + +function getConstantCondition( + discriminatorNode: ConstantDiscriminatorNode, + scope: { + discriminatorNodes: DiscriminatorNode[]; + importPrefix: string; + prefix: string; + }, + imports: ImportMap, +): string { + const { discriminatorNodes, importPrefix, prefix } = scope; + const constName = snakeCase(constantDiscriminatorName(prefix, discriminatorNode, discriminatorNodes)).toUpperCase(); + imports.add(`${importPrefix}::${constName}`); + + const offset = discriminatorNode.offset; + if (offset === 0) { + return `data.get(..${constName}.len()) == Some(&${constName}[..])`; + } + return `data.get(${offset}..${offset} + ${constName}.len()) == Some(&${constName}[..])`; +} + +function getFieldCondition( + discriminatorNode: FieldDiscriminatorNode, + scope: { + fields: InstructionArgumentNode[] | StructFieldTypeNode[]; + importPrefix: string; + prefix: string; + }, + imports: ImportMap, +): string | null { + const { fields, importPrefix, prefix } = scope; + const field = fields.find(f => f.name === discriminatorNode.name); + if (!field || !field.defaultValue || !isNode(field.defaultValue, VALUE_NODES)) { + return null; + } + + const constName = snakeCase(fieldDiscriminatorName(prefix, discriminatorNode.name)).toUpperCase(); + imports.add(`${importPrefix}::${constName}`); + const offset = discriminatorNode.offset; + + if (isNode(field.type, 'numberTypeNode')) { + const byteSize = getNumberByteSize(field.type.format); + const bytesFn = field.type.endian === 'le' ? 'to_le_bytes' : 'to_be_bytes'; + const range = offset === 0 ? `..${byteSize}` : `${offset}..${offset + byteSize}`; + return `data.get(${range}) == Some(&${constName}.${bytesFn}())`; + } + + if (offset === 0) { + return `data.get(..${constName}.len()) == Some(&${constName}[..])`; + } + return `data.get(${offset}..${offset} + ${constName}.len()) == Some(&${constName}[..])`; +} + +const NUMBER_BYTE_SIZES: Record = { + f32: 4, + f64: 8, + i128: 16, + i16: 2, + i32: 4, + i64: 8, + i8: 1, + u128: 16, + u16: 2, + u32: 4, + u64: 8, + u8: 1, +}; + +function getNumberByteSize(format: string): number { + const size = NUMBER_BYTE_SIZES[format]; + if (size === undefined) { + throw new Error(`Unknown number format: ${format}`); + } + return size; +} diff --git a/test/eventsPage.test.ts b/test/eventsPage.test.ts new file mode 100644 index 0000000..0de92bd --- /dev/null +++ b/test/eventsPage.test.ts @@ -0,0 +1,802 @@ +import { + arrayTypeNode, + arrayValueNode, + bytesTypeNode, + bytesValueNode, + constantDiscriminatorNode, + constantValueNode, + eventNode, + fieldDiscriminatorNode, + fixedCountNode, + fixedSizeTypeNode, + hiddenPrefixTypeNode, + numberTypeNode, + numberValueNode, + programNode, + rootNode, + sizeDiscriminatorNode, + structFieldTypeNode, + structTypeNode, +} from '@codama/nodes'; +import { getFromRenderMap } from '@codama/renderers-core'; +import { visit } from '@codama/visitors-core'; +import { expect, test } from 'vitest'; + +import { getRenderMapVisitor } from '../src'; +import { codeContains, codeDoesNotContains } from './_setup'; + +test('it renders an event with discriminator as a struct with from_bytes', () => { + const node = programNode({ + events: [ + eventNode({ + data: hiddenPrefixTypeNode( + structTypeNode([ + structFieldTypeNode({ name: 'amount', type: numberTypeNode('u64') }), + structFieldTypeNode({ name: 'price', type: numberTypeNode('u64') }), + ]), + [ + constantValueNode( + fixedSizeTypeNode(bytesTypeNode(), 8), + bytesValueNode('base16', 'bddB7fd34ee661ee'), + ), + ], + ), + discriminators: [ + constantDiscriminatorNode( + constantValueNode( + fixedSizeTypeNode(bytesTypeNode(), 8), + bytesValueNode('base16', 'bddB7fd34ee661ee'), + ), + ), + ], + name: 'tradeEvent', + }), + ], + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }); + + const renderMap = visit(node, getRenderMapVisitor()); + + codeContains(getFromRenderMap(renderMap, 'events/trade_event.rs').content, [ + '#[derive(', + 'BorshSerialize', + 'BorshDeserialize', + 'pub struct TradeEvent', + 'pub amount: u64,', + 'pub price: u64,', + 'TRADE_EVENT_DISCRIMINATOR', + 'pub fn from_bytes', + '"invalid event discriminator"', + 'Self::deserialize(&mut data)', + ]); +}); + +test('it renders an event without discriminator as a plain struct', () => { + const node = programNode({ + events: [ + eventNode({ + data: structTypeNode([structFieldTypeNode({ name: 'value', type: numberTypeNode('u32') })]), + name: 'simpleEvent', + }), + ], + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }); + + const renderMap = visit(node, getRenderMapVisitor()); + codeContains(getFromRenderMap(renderMap, 'events/simple_event.rs').content, [ + 'pub struct SimpleEvent', + 'pub value: u32,', + ]); + codeDoesNotContains(getFromRenderMap(renderMap, 'events/simple_event.rs').content, ['DISCRIMINATOR', 'from_bytes']); +}); + +test('it does not render events module for programs without events', () => { + const node = rootNode( + programNode({ + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }), + ); + + const renderMap = visit(node, getRenderMapVisitor()); + + codeDoesNotContains(getFromRenderMap(renderMap, 'mod.rs').content, 'pub mod events;'); +}); + +test('it renders events in the events module', () => { + const node = rootNode( + programNode({ + events: [ + eventNode({ + data: structTypeNode([structFieldTypeNode({ name: 'amount', type: numberTypeNode('u64') })]), + name: 'transferEvent', + }), + eventNode({ + data: structTypeNode([structFieldTypeNode({ name: 'delegate', type: numberTypeNode('u64') })]), + name: 'approveEvent', + }), + ], + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }), + ); + + const renderMap = visit(node, getRenderMapVisitor()); + codeContains(getFromRenderMap(renderMap, 'events/mod.rs').content, [ + 'pub(crate) mod r#approve_event;', + 'pub use self::r#approve_event::*;', + 'pub(crate) mod r#transfer_event;', + 'pub use self::r#transfer_event::*;', + ]); + + codeContains(getFromRenderMap(renderMap, 'mod.rs').content, 'pub mod events;'); +}); + +test('it renders an event with an empty struct', () => { + const node = programNode({ + events: [ + eventNode({ + data: structTypeNode([]), + name: 'emptyEvent', + }), + ], + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }); + + const renderMap = visit(node, getRenderMapVisitor()); + codeContains(getFromRenderMap(renderMap, 'events/empty_event.rs').content, ['pub struct EmptyEvent']); + codeDoesNotContains(getFromRenderMap(renderMap, 'events/empty_event.rs').content, ['from_bytes', 'DISCRIMINATOR']); +}); + +test('it renders event docs', () => { + const node = programNode({ + events: [ + eventNode({ + data: structTypeNode([structFieldTypeNode({ name: 'value', type: numberTypeNode('u32') })]), + docs: ['Some documentation.', 'Second line.'], + name: 'documentedEvent', + }), + ], + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }); + + const renderMap = visit(node, getRenderMapVisitor()); + codeContains(getFromRenderMap(renderMap, 'events/documented_event.rs').content, [ + '/// Some documentation.', + '/// Second line.', + 'pub struct DocumentedEvent', + ]); +}); + +test('it renders an event with a nested struct field', () => { + const node = programNode({ + events: [ + eventNode({ + data: structTypeNode([ + structFieldTypeNode({ name: 'amount', type: numberTypeNode('u64') }), + structFieldTypeNode({ + name: 'metadata', + type: structTypeNode([ + structFieldTypeNode({ name: 'label', type: numberTypeNode('u8') }), + structFieldTypeNode({ name: 'version', type: numberTypeNode('u16') }), + ]), + }), + ]), + name: 'complexEvent', + }), + ], + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }); + + const renderMap = visit(node, getRenderMapVisitor()); + codeContains(getFromRenderMap(renderMap, 'events/complex_event.rs').content, [ + 'pub struct ComplexEvent', + 'pub amount: u64,', + 'pub metadata: ComplexEventMetadata,', + 'pub struct ComplexEventMetadata', + 'pub label: u8,', + 'pub version: u16,', + ]); +}); + +test('it renders field discriminator constants and skips from_bytes without hidden prefix', () => { + const node = programNode({ + events: [ + eventNode({ + data: structTypeNode([ + structFieldTypeNode({ + defaultValue: numberValueNode(7), + name: 'eventType', + type: numberTypeNode('u8'), + }), + structFieldTypeNode({ name: 'value', type: numberTypeNode('u64') }), + ]), + discriminators: [ + fieldDiscriminatorNode('eventType'), + constantDiscriminatorNode( + constantValueNode( + fixedSizeTypeNode(bytesTypeNode(), 8), + bytesValueNode('base16', 'aabbccdd11223344'), + ), + ), + ], + name: 'mixedEvent', + }), + ], + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }); + + const renderMap = visit(node, getRenderMapVisitor()); + const code = getFromRenderMap(renderMap, 'events/mixed_event.rs').content; + + codeContains(code, ['MIXED_EVENT_EVENT_TYPE: u8 = 7']); + codeContains(code, ['MIXED_EVENT_DISCRIMINATOR']); + codeDoesNotContains(code, ['from_bytes']); +}); + +test('it validates all constant discriminators in from_bytes for multi-disc events', () => { + const disc1 = constantValueNode( + fixedSizeTypeNode(bytesTypeNode(), 8), + bytesValueNode('base16', 'aabbccdd11223344'), + ); + const disc2 = constantValueNode(fixedSizeTypeNode(bytesTypeNode(), 4), bytesValueNode('base16', 'eeff0011')); + const node = programNode({ + events: [ + eventNode({ + data: hiddenPrefixTypeNode( + structTypeNode([structFieldTypeNode({ name: 'value', type: numberTypeNode('u64') })]), + [disc1], + ), + discriminators: [constantDiscriminatorNode(disc1, 0), constantDiscriminatorNode(disc2, 12)], + name: 'multiDiscEvent', + }), + ], + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }); + + const renderMap = visit(node, getRenderMapVisitor()); + const code = getFromRenderMap(renderMap, 'events/multi_disc_event.rs').content; + + codeContains(code, [ + 'pub fn from_bytes', + 'MULTI_DISC_EVENT_DISCRIMINATOR.len()) != Some(&MULTI_DISC_EVENT_DISCRIMINATOR[..])', + 'data.get(12..12 + MULTI_DISC_EVENT_DISCRIMINATOR2.len()) != Some(&MULTI_DISC_EVENT_DISCRIMINATOR2[..])', + 'Self::deserialize(&mut data)', + ]); +}); + +test('it uses literal byte count in from_bytes for multi-prefix hidden prefix', () => { + const prefix1 = constantValueNode( + fixedSizeTypeNode(bytesTypeNode(), 8), + bytesValueNode('base16', 'aabbccdd11223344'), + ); + const prefix2 = constantValueNode(fixedSizeTypeNode(bytesTypeNode(), 4), bytesValueNode('base16', 'eeff0011')); + const node = programNode({ + events: [ + eventNode({ + data: hiddenPrefixTypeNode( + structTypeNode([structFieldTypeNode({ name: 'value', type: numberTypeNode('u64') })]), + [prefix1, prefix2], + ), + discriminators: [constantDiscriminatorNode(prefix1, 0)], + name: 'multiPrefixEvent', + }), + ], + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }); + + const renderMap = visit(node, getRenderMapVisitor()); + const code = getFromRenderMap(renderMap, 'events/multi_prefix_event.rs').content; + + codeContains(code, ['let mut data = &data[12..];']); + codeDoesNotContains(code, ['.len()..']); +}); + +test('it uses literal byte count in from_bytes when constant disc is not at offset 0', () => { + const prefix = constantValueNode( + fixedSizeTypeNode(bytesTypeNode(), 8), + bytesValueNode('base16', 'aabbccdd11223344'), + ); + const node = programNode({ + events: [ + eventNode({ + data: hiddenPrefixTypeNode( + structTypeNode([structFieldTypeNode({ name: 'value', type: numberTypeNode('u64') })]), + [prefix], + ), + discriminators: [constantDiscriminatorNode(prefix, 8)], + name: 'offsetPrefixEvent', + }), + ], + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }); + + const renderMap = visit(node, getRenderMapVisitor()); + const code = getFromRenderMap(renderMap, 'events/offset_prefix_event.rs').content; + + codeContains(code, ['let mut data = &data[8..];']); + codeDoesNotContains(code, ['.len()..']); +}); + +test('it does not render from_bytes when hidden prefix has a non-fixed-size entry', () => { + const prefix1 = constantValueNode( + fixedSizeTypeNode(bytesTypeNode(), 8), + bytesValueNode('base16', 'aabbccdd11223344'), + ); + const prefix2 = constantValueNode(numberTypeNode('u32'), numberValueNode(42)); + const node = programNode({ + events: [ + eventNode({ + data: hiddenPrefixTypeNode( + structTypeNode([structFieldTypeNode({ name: 'value', type: numberTypeNode('u64') })]), + [prefix1, prefix2], + ), + discriminators: [constantDiscriminatorNode(prefix1)], + name: 'dynamicPrefixEvent', + }), + ], + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }); + + const renderMap = visit(node, getRenderMapVisitor()); + const code = getFromRenderMap(renderMap, 'events/dynamic_prefix_event.rs').content; + + codeContains(code, ['pub struct DynamicPrefixEvent', 'DYNAMIC_PREFIX_EVENT_DISCRIMINATOR']); + codeDoesNotContains(code, ['from_bytes']); +}); +// --- Program-level event codegen tests --- + +test('it does not render program events file when no events have discriminators', () => { + const node = programNode({ + events: [ + eventNode({ + data: structTypeNode([structFieldTypeNode({ name: 'amount', type: numberTypeNode('u64') })]), + name: 'transferEvent', + }), + eventNode({ + data: structTypeNode([structFieldTypeNode({ name: 'delegate', type: numberTypeNode('u64') })]), + name: 'approveEvent', + }), + ], + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }); + + const renderMap = visit(node, getRenderMapVisitor()); + const keys = [...renderMap.keys()]; + const programEventsFiles = keys.filter(k => k.includes('my_program_events')); + expect(programEventsFiles).toHaveLength(0); +}); + +test('it renders identify and try_parse for events with constant discriminators', () => { + const node = programNode({ + events: [ + eventNode({ + data: hiddenPrefixTypeNode( + structTypeNode([structFieldTypeNode({ name: 'amount', type: numberTypeNode('u64') })]), + [ + constantValueNode( + fixedSizeTypeNode(bytesTypeNode(), 8), + bytesValueNode('base16', 'aabbccdd11223344'), + ), + ], + ), + discriminators: [ + constantDiscriminatorNode( + constantValueNode( + fixedSizeTypeNode(bytesTypeNode(), 8), + bytesValueNode('base16', 'aabbccdd11223344'), + ), + ), + ], + name: 'tradeEvent', + }), + eventNode({ + data: hiddenPrefixTypeNode( + structTypeNode([structFieldTypeNode({ name: 'price', type: numberTypeNode('u64') })]), + [ + constantValueNode( + fixedSizeTypeNode(bytesTypeNode(), 8), + bytesValueNode('base16', '1122334455667788'), + ), + ], + ), + discriminators: [ + constantDiscriminatorNode( + constantValueNode( + fixedSizeTypeNode(bytesTypeNode(), 8), + bytesValueNode('base16', '1122334455667788'), + ), + ), + ], + name: 'settleEvent', + }), + ], + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }); + + const renderMap = visit(node, getRenderMapVisitor()); + const code = getFromRenderMap(renderMap, 'events/my_program_events.rs').content; + + codeContains(code, [ + 'pub fn identify_my_program_event(data: &[u8]) -> Option', + 'SETTLE_EVENT_DISCRIMINATOR', + 'return Some(MyProgramEventKind::SettleEvent)', + 'TRADE_EVENT_DISCRIMINATOR', + 'return Some(MyProgramEventKind::TradeEvent)', + 'pub fn try_parse_my_program_event(data: &[u8]) -> Option>', + 'identify_my_program_event(data)?', + 'MyProgramEventKind::SettleEvent =>', + 'let mut data = &data[SETTLE_EVENT_DISCRIMINATOR.len()..]', + 'SettleEvent::deserialize(&mut data)', + 'MyProgramEventKind::TradeEvent =>', + 'let mut data = &data[TRADE_EVENT_DISCRIMINATOR.len()..]', + 'TradeEvent::deserialize(&mut data)', + ]); + codeDoesNotContains(code, ['from_bytes', 'Err(std::io::Error::new']); +}); + +test('it uses BorshDeserialize for events without from_bytes in try_parse', () => { + const node = programNode({ + events: [ + eventNode({ + data: hiddenPrefixTypeNode( + structTypeNode([structFieldTypeNode({ name: 'amount', type: numberTypeNode('u64') })]), + [ + constantValueNode( + fixedSizeTypeNode(bytesTypeNode(), 8), + bytesValueNode('base16', 'aabbccdd11223344'), + ), + ], + ), + discriminators: [ + constantDiscriminatorNode( + constantValueNode( + fixedSizeTypeNode(bytesTypeNode(), 8), + bytesValueNode('base16', 'aabbccdd11223344'), + ), + ), + ], + name: 'tradeEvent', + }), + eventNode({ + data: structTypeNode([structFieldTypeNode({ name: 'value', type: numberTypeNode('u32') })]), + discriminators: [ + constantDiscriminatorNode( + constantValueNode( + fixedSizeTypeNode(bytesTypeNode(), 8), + bytesValueNode('base16', '5566778899aabbcc'), + ), + ), + ], + name: 'simpleEvent', + }), + ], + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }); + + const renderMap = visit(node, getRenderMapVisitor()); + const code = getFromRenderMap(renderMap, 'events/my_program_events.rs').content; + + codeContains(code, [ + 'let mut data = &data[TRADE_EVENT_DISCRIMINATOR.len()..]', + 'TradeEvent::deserialize(&mut data)', + ]); + codeContains(code, ['SimpleEvent::deserialize(&mut data)']); + codeDoesNotContains(code, ['from_bytes']); +}); + +test('it excludes non-fixed-size prefix events from program-level try_parse', () => { + const fixedPrefix = constantValueNode( + fixedSizeTypeNode(bytesTypeNode(), 8), + bytesValueNode('base16', 'aabbccdd11223344'), + ); + const nonFixedPrefix = constantValueNode(numberTypeNode('u32'), numberValueNode(42)); + const node = programNode({ + events: [ + eventNode({ + data: hiddenPrefixTypeNode( + structTypeNode([structFieldTypeNode({ name: 'value', type: numberTypeNode('u64') })]), + [fixedPrefix], + ), + discriminators: [constantDiscriminatorNode(fixedPrefix)], + name: 'goodEvent', + }), + eventNode({ + data: hiddenPrefixTypeNode( + structTypeNode([structFieldTypeNode({ name: 'value', type: numberTypeNode('u64') })]), + [fixedPrefix, nonFixedPrefix], + ), + discriminators: [constantDiscriminatorNode(fixedPrefix)], + name: 'dynamicEvent', + }), + ], + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }); + + const renderMap = visit(node, getRenderMapVisitor()); + const code = getFromRenderMap(renderMap, 'events/my_program_events.rs').content; + + codeContains(code, ['GoodEvent', 'GoodEvent::deserialize']); + codeDoesNotContains(code, ['DynamicEvent']); +}); + +test('it does not render program events file when program has no events', () => { + const node = programNode({ + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }); + + const renderMap = visit(node, getRenderMapVisitor()); + const keys = [...renderMap.keys()]; + const programEventsFiles = keys.filter(k => k.includes('_events.rs')); + expect(programEventsFiles).toHaveLength(0); +}); + +test('it includes program events module in events mod.rs', () => { + const node = rootNode( + programNode({ + events: [ + eventNode({ + data: hiddenPrefixTypeNode( + structTypeNode([structFieldTypeNode({ name: 'amount', type: numberTypeNode('u64') })]), + [ + constantValueNode( + fixedSizeTypeNode(bytesTypeNode(), 8), + bytesValueNode('base16', 'aabbccdd11223344'), + ), + ], + ), + discriminators: [ + constantDiscriminatorNode( + constantValueNode( + fixedSizeTypeNode(bytesTypeNode(), 8), + bytesValueNode('base16', 'aabbccdd11223344'), + ), + ), + ], + name: 'transferEvent', + }), + ], + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }), + ); + + const renderMap = visit(node, getRenderMapVisitor()); + expect(renderMap.has('events/my_program_events.rs')).toBe(true); + codeContains(getFromRenderMap(renderMap, 'events/mod.rs').content, [ + 'pub(crate) mod r#my_program_events;', + 'pub use self::r#my_program_events::*;', + ]); +}); + +test('it excludes program events module from events mod.rs when no events have discriminators', () => { + const node = rootNode( + programNode({ + events: [ + eventNode({ + data: structTypeNode([structFieldTypeNode({ name: 'amount', type: numberTypeNode('u64') })]), + name: 'transferEvent', + }), + ], + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }), + ); + + const renderMap = visit(node, getRenderMapVisitor()); + codeDoesNotContains(getFromRenderMap(renderMap, 'events/mod.rs').content, ['my_program_events']); +}); + +test('it renders identify and try_parse for events with field discriminators', () => { + const node = programNode({ + events: [ + eventNode({ + data: structTypeNode([ + structFieldTypeNode({ + defaultValue: numberValueNode(7), + name: 'eventType', + type: numberTypeNode('u8'), + }), + structFieldTypeNode({ name: 'value', type: numberTypeNode('u64') }), + ]), + discriminators: [fieldDiscriminatorNode('eventType')], + name: 'typedEvent', + }), + eventNode({ + data: structTypeNode([ + structFieldTypeNode({ + defaultValue: numberValueNode(1.0), + name: 'version', + type: numberTypeNode('f32'), + }), + structFieldTypeNode({ name: 'value', type: numberTypeNode('u64') }), + ]), + discriminators: [fieldDiscriminatorNode('version')], + name: 'floatDiscEvent', + }), + ], + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }); + + const renderMap = visit(node, getRenderMapVisitor()); + const code = getFromRenderMap(renderMap, 'events/my_program_events.rs').content; + + codeContains(code, [ + 'pub fn identify_my_program_event', + 'data.get(..1) == Some(&TYPED_EVENT_EVENT_TYPE.to_le_bytes())', + 'return Some(MyProgramEventKind::TypedEvent)', + 'data.get(..4) == Some(&FLOAT_DISC_EVENT_VERSION.to_le_bytes())', + 'return Some(MyProgramEventKind::FloatDiscEvent)', + 'pub fn try_parse_my_program_event', + ]); +}); + +test('it renders identify and try_parse for events with size discriminators', () => { + const node = programNode({ + events: [ + eventNode({ + data: structTypeNode([structFieldTypeNode({ name: 'value', type: numberTypeNode('u64') })]), + discriminators: [sizeDiscriminatorNode(8)], + name: 'fixedSizeEvent', + }), + ], + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }); + + const renderMap = visit(node, getRenderMapVisitor()); + const code = getFromRenderMap(renderMap, 'events/my_program_events.rs').content; + + codeContains(code, [ + 'pub fn identify_my_program_event', + 'data.len() == 8', + 'return Some(MyProgramEventKind::FixedSizeEvent)', + 'pub fn try_parse_my_program_event', + ]); +}); + +test('it AND-s multiple discriminators for the same event in identify', () => { + const node = programNode({ + events: [ + eventNode({ + data: structTypeNode([ + structFieldTypeNode({ + defaultValue: numberValueNode(3), + name: 'eventType', + type: numberTypeNode('u8'), + }), + structFieldTypeNode({ name: 'value', type: numberTypeNode('u64') }), + ]), + discriminators: [sizeDiscriminatorNode(9), fieldDiscriminatorNode('eventType')], + name: 'mixedDiscEvent', + }), + ], + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }); + + const renderMap = visit(node, getRenderMapVisitor()); + const code = getFromRenderMap(renderMap, 'events/my_program_events.rs').content; + + codeContains(code, [ + 'data.len() == 9 && data.get(..1) == Some(&MIXED_DISC_EVENT_EVENT_TYPE.to_le_bytes())', + 'return Some(MyProgramEventKind::MixedDiscEvent)', + ]); +}); + +test('it renders identify for events with byte-array field discriminators', () => { + const node = programNode({ + events: [ + eventNode({ + data: structTypeNode([ + structFieldTypeNode({ + defaultValue: arrayValueNode([numberValueNode(1), numberValueNode(2), numberValueNode(3)]), + name: 'disc', + type: arrayTypeNode(numberTypeNode('u8'), fixedCountNode(3)), + }), + structFieldTypeNode({ name: 'value', type: numberTypeNode('u64') }), + ]), + discriminators: [fieldDiscriminatorNode('disc')], + name: 'arrayDiscEvent', + }), + ], + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }); + + const renderMap = visit(node, getRenderMapVisitor()); + const code = getFromRenderMap(renderMap, 'events/my_program_events.rs').content; + + codeContains(code, [ + 'pub fn identify_my_program_event', + 'data.get(..ARRAY_DISC_EVENT_DISC.len()) == Some(&ARRAY_DISC_EVENT_DISC[..])', + 'return Some(MyProgramEventKind::ArrayDiscEvent)', + ]); + codeDoesNotContains(code, ['to_le_bytes']); + + const eventCode = getFromRenderMap(renderMap, 'events/array_disc_event.rs').content; + codeContains(eventCode, ['ARRAY_DISC_EVENT_DISC: [u8; 3] = [1, 2, 3]']); +}); + +test('it handles non-zero offset in constant discriminator conditions', () => { + const node = programNode({ + events: [ + eventNode({ + data: structTypeNode([structFieldTypeNode({ name: 'value', type: numberTypeNode('u64') })]), + discriminators: [ + constantDiscriminatorNode( + constantValueNode(fixedSizeTypeNode(bytesTypeNode(), 4), bytesValueNode('base16', 'aabbccdd')), + 8, + ), + ], + name: 'offsetEvent', + }), + ], + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }); + + const renderMap = visit(node, getRenderMapVisitor()); + const code = getFromRenderMap(renderMap, 'events/my_program_events.rs').content; + + codeContains(code, [ + 'pub fn identify_my_program_event', + 'data.get(8..8 + OFFSET_EVENT_DISCRIMINATOR.len()) == Some(&OFFSET_EVENT_DISCRIMINATOR[..])', + 'return Some(MyProgramEventKind::OffsetEvent)', + 'pub fn try_parse_my_program_event', + 'OffsetEvent::deserialize(&mut data)', + ]); +}); + +test('it handles multiple constant discriminators and excludes events with unresolvable field discriminators', () => { + const disc1 = constantValueNode(fixedSizeTypeNode(bytesTypeNode(), 4), bytesValueNode('base16', 'aabbccdd')); + const disc2 = constantValueNode(fixedSizeTypeNode(bytesTypeNode(), 2), bytesValueNode('base16', 'eeff')); + const node = programNode({ + events: [ + // Event with two constant discriminators — tests _2 suffix naming and AND-ing. + eventNode({ + data: structTypeNode([structFieldTypeNode({ name: 'value', type: numberTypeNode('u64') })]), + discriminators: [constantDiscriminatorNode(disc1, 0), constantDiscriminatorNode(disc2, 4)], + name: 'multiDiscEvent', + }), + // Event with field discriminator that has no defaultValue — should be excluded. + eventNode({ + data: structTypeNode([ + structFieldTypeNode({ name: 'eventType', type: numberTypeNode('u8') }), + structFieldTypeNode({ name: 'value', type: numberTypeNode('u64') }), + ]), + discriminators: [fieldDiscriminatorNode('eventType')], + name: 'noDefaultEvent', + }), + ], + name: 'myProgram', + publicKey: '11111111111111111111111111111111', + }); + + const renderMap = visit(node, getRenderMapVisitor()); + const code = getFromRenderMap(renderMap, 'events/my_program_events.rs').content; + + codeContains(code, [ + 'MULTI_DISC_EVENT_DISCRIMINATOR.len()) == Some(&MULTI_DISC_EVENT_DISCRIMINATOR[..])', + 'MULTI_DISC_EVENT_DISCRIMINATOR2.len()) == Some(&MULTI_DISC_EVENT_DISCRIMINATOR2[..])', + 'return Some(MyProgramEventKind::MultiDiscEvent)', + ]); + codeContains(code, ['pub enum MyProgramEventKind']); + codeDoesNotContains(code, ['NoDefaultEvent']); +});