From 78b7a564dede626ccacf710b05e9865978a59b0c Mon Sep 17 00:00:00 2001 From: Swenschaeferjohann Date: Thu, 29 Jan 2026 17:39:05 +0000 Subject: [PATCH 1/4] sync to latest main --- Cargo.lock | 477 ++++++++++++------ Cargo.toml | 7 + programs/cp-swap/Cargo.toml | 10 +- programs/cp-swap/src/instructions/deposit.rs | 3 +- .../cp-swap/src/instructions/initialize.rs | 29 +- programs/cp-swap/src/instructions/withdraw.rs | 2 + programs/cp-swap/tests/functional_test.rs | 6 +- programs/cp-swap/tests/helpers.rs | 41 +- programs/cp-swap/tests/program.rs | 179 ++++++- programs/cp-swap/tests/program_test.rs | 6 +- 10 files changed, 551 insertions(+), 209 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 785203f..854f4ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,6 +12,29 @@ dependencies = [ "regex", ] +[[package]] +name = "account-compression" +version = "2.0.0" +dependencies = [ + "aligned-sized", + "anchor-lang", + "bytemuck", + "light-account-checks", + "light-batched-merkle-tree", + "light-bounded-vec", + "light-compressed-account", + "light-concurrent-merkle-tree", + "light-hash-set", + "light-hasher", + "light-indexed-merkle-tree", + "light-merkle-tree-metadata", + "light-zero-copy", + "num-bigint 0.4.6", + "solana-sdk", + "solana-security-txt", + "zerocopy", +] + [[package]] name = "adler2" version = "2.0.1" @@ -126,8 +149,6 @@ dependencies = [ [[package]] name = "aligned-sized" version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48a526ec4434d531d488af59fe866f36b310fe8906691c75dffa664450a3800a" dependencies = [ "proc-macro2", "quote", @@ -231,6 +252,27 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "anchor-compressed-token" +version = "2.0.0" +dependencies = [ + "account-compression", + "anchor-lang", + "anchor-spl", + "light-compressed-account", + "light-hasher", + "light-heap", + "light-system-program-anchor", + "light-token-interface", + "light-zero-copy", + "pinocchio-pubkey", + "solana-sdk", + "solana-security-txt", + "spl-token 7.0.0", + "spl-token-2022 7.0.0", + "zerocopy", +] + [[package]] name = "anchor-derive-accounts" version = "0.31.1" @@ -315,6 +357,21 @@ dependencies = [ "serde", ] +[[package]] +name = "anchor-spl" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c08cb5d762c0694f74bd02c9a5b04ea53cefc496e2c27b3234acffca5cd076b" +dependencies = [ + "anchor-lang", + "spl-associated-token-account 6.0.0", + "spl-pod", + "spl-token 7.0.0", + "spl-token-2022 6.0.0", + "spl-token-group-interface 0.5.0", + "spl-token-metadata-interface 0.6.0", +] + [[package]] name = "anchor-syn" version = "0.31.1" @@ -935,9 +992,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.53" +version = "1.2.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932" +checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583" dependencies = [ "find-msvc-tools", "jobserver", @@ -2085,7 +2142,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.1", + "socket2 0.6.2", "system-configuration 0.6.1", "tokio", "tower-service", @@ -2095,9 +2152,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.64" +version = "0.1.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2444,10 +2501,9 @@ dependencies = [ [[package]] name = "light-account-checks" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0785da22cd4a7667583141ca56c790a5c8afa2b22ad2a08204d78881035524e8" +version = "0.7.0" dependencies = [ + "pinocchio", "solana-account-info", "solana-msg", "solana-program-error", @@ -2474,29 +2530,25 @@ dependencies = [ [[package]] name = "light-array-map" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859dc5b406a8bf0b114f686e6f2e36d0e939bad6f579492a520d309b52fde1f8" +version = "0.2.0" dependencies = [ "tinyvec", ] [[package]] name = "light-batched-merkle-tree" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13cb8bc778065ee71d1990fdc94112e35dc63a5e387a323284a49f40d123d8e0" +version = "0.9.0" dependencies = [ "aligned-sized", "borsh 0.10.4", "light-account-checks", "light-bloom-filter", - "light-compressed-account 0.8.0", + "light-compressed-account", "light-hasher", "light-macros", "light-merkle-tree-metadata", "light-verifier", - "light-zero-copy 0.6.0", + "light-zero-copy", "solana-account-info", "solana-msg", "solana-program-error", @@ -2508,9 +2560,7 @@ dependencies = [ [[package]] name = "light-bloom-filter" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a609e3c9179f0ae8488cc70c5413c86dfd97dad7ad85fee2ad8da2d0a11e61" +version = "0.6.0" dependencies = [ "bitvec", "num-bigint 0.4.6", @@ -2533,9 +2583,7 @@ dependencies = [ [[package]] name = "light-client" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b1f3cd013364dbe5c45a9e9a8faee1af30dccb600cd56a41e296ed8d5684768" +version = "0.19.0" dependencies = [ "anchor-lang", "async-trait", @@ -2544,7 +2592,8 @@ dependencies = [ "bs58", "futures", "lazy_static", - "light-compressed-account 0.8.0", + "light-compressed-account", + "light-compressed-token-sdk", "light-compressible", "light-concurrent-merkle-tree", "light-event", @@ -2588,61 +2637,101 @@ dependencies = [ [[package]] name = "light-compressed-account" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "058df2733fa6a3e4bda6f162a6c5d41f10fc8c6f6ddb992af1de76b60214e4a6" +version = "0.9.0" dependencies = [ + "anchor-lang", "borsh 0.10.4", + "bytemuck", "light-hasher", "light-macros", + "light-poseidon 0.3.0", "light-program-profiler", - "light-zero-copy 0.5.0", + "light-zero-copy", + "pinocchio", + "solana-msg", + "solana-program-error", + "solana-pubkey", "thiserror 2.0.18", "tinyvec", "zerocopy", ] [[package]] -name = "light-compressed-account" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "768ae5a56d8c9cf315d132b3faa5b067f95b3d6a294c579e82f8f0e0bf29c7cc" +name = "light-compressed-token" +version = "2.1.0" dependencies = [ + "account-compression", + "anchor-compressed-token", "anchor-lang", + "arrayvec", + "bitvec", "borsh 0.10.4", - "bytemuck", + "light-account-checks", + "light-array-map", + "light-compressed-account", + "light-compressible", "light-hasher", + "light-heap", "light-macros", - "light-poseidon 0.3.0", "light-program-profiler", - "light-zero-copy 0.6.0", + "light-system-program-anchor", + "light-token-interface", + "light-zero-copy", + "pinocchio", + "pinocchio-pubkey", + "pinocchio-system", + "pinocchio-token-program", + "solana-pubkey", + "solana-security-txt", + "spl-pod", + "spl-token 7.0.0", + "spl-token-2022 7.0.0", + "tinyvec", + "zerocopy", +] + +[[package]] +name = "light-compressed-token-sdk" +version = "0.1.0" +dependencies = [ + "anchor-lang", + "arrayvec", + "borsh 0.10.4", + "light-account-checks", + "light-compressed-account", + "light-program-profiler", + "light-sdk", + "light-sdk-types", + "light-token-interface", + "light-token-types", + "light-zero-copy", + "solana-account-info", + "solana-cpi", + "solana-instruction", "solana-msg", "solana-program-error", "solana-pubkey", "thiserror 2.0.18", - "tinyvec", - "zerocopy", ] [[package]] name = "light-compressible" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff0f0065beb8d16df587b3ea17082e11dea3f67c98813b4bcc061eecd94561f" +version = "0.4.0" dependencies = [ "aligned-sized", "anchor-lang", "borsh 0.10.4", "bytemuck", "light-account-checks", - "light-compressed-account 0.8.0", + "light-compressed-account", "light-hasher", "light-macros", "light-program-profiler", - "light-sdk-types", - "light-zero-copy 0.6.0", + "light-zero-copy", + "pinocchio", "pinocchio-pubkey", "solana-pubkey", + "solana-rent", "thiserror 2.0.18", "zerocopy", ] @@ -2650,8 +2739,6 @@ dependencies = [ [[package]] name = "light-concurrent-merkle-tree" version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db96f47253a0907aaa46dac15cecb27b5510130e48da0b36690dcd2e99a6d558" dependencies = [ "borsh 0.10.4", "light-bounded-vec", @@ -2663,22 +2750,29 @@ dependencies = [ [[package]] name = "light-event" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1674c9d85b32a9e8abb90cccdee18e35ae29daa1126fdb81a8a28c0a54802096" +version = "0.4.0" dependencies = [ "borsh 0.10.4", - "light-compressed-account 0.8.0", + "light-compressed-account", + "light-hasher", + "light-zero-copy", + "thiserror 2.0.18", +] + +[[package]] +name = "light-hash-set" +version = "4.0.0" +dependencies = [ "light-hasher", - "light-zero-copy 0.6.0", + "num-bigint 0.4.6", + "num-traits", + "solana-program-error", "thiserror 2.0.18", ] [[package]] name = "light-hasher" version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c822662e6e109bac0e132a43fd52a4ef684811245a794e048cf9cda001e934c8" dependencies = [ "ark-bn254 0.5.0", "ark-ff 0.5.0", @@ -2692,11 +2786,16 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "light-heap" +version = "2.0.0" +dependencies = [ + "anchor-lang", +] + [[package]] name = "light-indexed-array" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f14f984030d86b6f07bd8f5ae04e2c40fcd0c3bdfcc7a291fff1ed59c9e6554" dependencies = [ "light-hasher", "num-bigint 0.4.6", @@ -2707,8 +2806,6 @@ dependencies = [ [[package]] name = "light-indexed-merkle-tree" version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0824755289075f28de2820fc7d4ec4e6b9e99d404e033c07338b91cce8c71fb8" dependencies = [ "light-bounded-vec", "light-concurrent-merkle-tree", @@ -2720,11 +2817,39 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "light-instruction-decoder" +version = "0.2.0" +dependencies = [ + "borsh 0.10.4", + "bs58", + "light-compressed-account", + "light-instruction-decoder-derive", + "light-sdk-types", + "light-token-interface", + "serde", + "solana-instruction", + "solana-pubkey", + "solana-signature", + "tabled", +] + +[[package]] +name = "light-instruction-decoder-derive" +version = "0.2.0" +dependencies = [ + "bs58", + "darling", + "heck 0.5.0", + "proc-macro2", + "quote", + "sha2 0.10.9", + "syn 2.0.114", +] + [[package]] name = "light-macros" version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "179ac51cadc1d0ca047b4d6265a7cc245ca3affc16a20a2749585aa6464d39c2" dependencies = [ "bs58", "proc-macro2", @@ -2735,14 +2860,12 @@ dependencies = [ [[package]] name = "light-merkle-tree-metadata" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17d08edcc194eef61b0f499934ce398122d54ac57505d44480e5f079a4220566" +version = "0.9.0" dependencies = [ "anchor-lang", "borsh 0.10.4", "bytemuck", - "light-compressed-account 0.8.0", + "light-compressed-account", "solana-msg", "solana-program-error", "solana-sysvar", @@ -2753,8 +2876,6 @@ dependencies = [ [[package]] name = "light-merkle-tree-reference" version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8d480f62ca32b38a6231bbc5310d693f91d6b5bdcc18bb13c2d9aab7a1c90e8" dependencies = [ "light-hasher", "light-indexed-array", @@ -2809,10 +2930,9 @@ dependencies = [ [[package]] name = "light-program-test" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a981dfbc19c529543ab1dd8d100319b89aac053b81415a681d1474c986218307" +version = "0.19.0" dependencies = [ + "account-compression", "anchor-lang", "async-trait", "base64 0.13.1", @@ -2820,21 +2940,27 @@ dependencies = [ "bs58", "bytemuck", "chrono", + "light-batched-merkle-tree", "light-client", - "light-compressed-account 0.8.0", + "light-compressed-account", + "light-compressed-token", + "light-compressed-token-sdk", "light-compressible", + "light-concurrent-merkle-tree", "light-event", "light-hasher", "light-indexed-array", "light-indexed-merkle-tree", + "light-instruction-decoder", "light-merkle-tree-metadata", "light-merkle-tree-reference", "light-prover-client", + "light-registry", "light-sdk", "light-sdk-types", "light-token", "light-token-interface", - "light-zero-copy 0.6.0", + "light-zero-copy", "litesvm", "log", "num-bigint 0.4.6", @@ -2861,14 +2987,12 @@ dependencies = [ [[package]] name = "light-prover-client" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75d8c9b8b6e9d445b9ef27467da592ee231e614282c3c0bd2f30f567eb904845" +version = "6.0.0" dependencies = [ "ark-bn254 0.5.0", "ark-serialize 0.5.0", "ark-std 0.5.0", - "light-compressed-account 0.7.0", + "light-compressed-account", "light-hasher", "light-indexed-array", "light-sparse-merkle-tree", @@ -2883,23 +3007,46 @@ dependencies = [ "tracing", ] +[[package]] +name = "light-registry" +version = "2.1.0" +dependencies = [ + "account-compression", + "aligned-sized", + "anchor-lang", + "borsh 0.10.4", + "light-account-checks", + "light-batched-merkle-tree", + "light-compressible", + "light-macros", + "light-merkle-tree-metadata", + "light-program-profiler", + "light-system-program-anchor", + "light-token-interface", + "light-zero-copy", + "solana-account-info", + "solana-instruction", + "solana-pubkey", + "solana-sdk", + "solana-security-txt", + "spl-pod", +] + [[package]] name = "light-sdk" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dece106ebd0897bd23a12bad040e0999d93b54447d0473739f91b1f83b1d331" +version = "0.19.0" dependencies = [ "anchor-lang", "bincode", "borsh 0.10.4", "light-account-checks", - "light-compressed-account 0.8.0", + "light-compressed-account", "light-compressible", "light-hasher", "light-macros", "light-sdk-macros", "light-sdk-types", - "light-zero-copy 0.6.0", + "light-zero-copy", "num-bigint 0.4.6", "solana-account-info", "solana-clock", @@ -2917,9 +3064,7 @@ dependencies = [ [[package]] name = "light-sdk-macros" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45d91992fa08093b1a274b3baed1d8368de794cc2645f9942718e5fe47a27dc2" +version = "0.19.0" dependencies = [ "darling", "light-hasher", @@ -2932,14 +3077,12 @@ dependencies = [ [[package]] name = "light-sdk-types" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b765f0a39428a137b8d449fa60ba147194cdbff08aa0add598c6047fff2cb7d2" +version = "0.19.0" dependencies = [ "anchor-lang", "borsh 0.10.4", "light-account-checks", - "light-compressed-account 0.8.0", + "light-compressed-account", "light-hasher", "light-macros", "solana-msg", @@ -2949,8 +3092,6 @@ dependencies = [ [[package]] name = "light-sparse-merkle-tree" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4251e79b6c63f4946572dcfd7623680ad0f9e0efe1a761a944733333c5645063" dependencies = [ "light-hasher", "light-indexed-array", @@ -2959,18 +3100,29 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "light-system-program-anchor" +version = "2.0.0" +dependencies = [ + "account-compression", + "aligned-sized", + "anchor-lang", + "light-compressed-account", + "light-zero-copy", + "zerocopy", +] + [[package]] name = "light-token" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62907a12a9801200e5f4c03bb7f2dbdd9aa679223a959167c456a06005291d79" +version = "0.4.0" dependencies = [ "anchor-lang", "arrayvec", "borsh 0.10.4", "light-account-checks", "light-batched-merkle-tree", - "light-compressed-account 0.8.0", + "light-compressed-account", + "light-compressed-token-sdk", "light-compressible", "light-macros", "light-program-profiler", @@ -2979,7 +3131,7 @@ dependencies = [ "light-sdk-types", "light-token-interface", "light-token-types", - "light-zero-copy 0.6.0", + "light-zero-copy", "solana-account-info", "solana-cpi", "solana-instruction", @@ -2992,21 +3144,19 @@ dependencies = [ [[package]] name = "light-token-interface" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fb19b8e268a0154a8e13b3a8f6f43fa4928643e2de102d98a90b2af21f482ba" +version = "0.3.0" dependencies = [ "aligned-sized", "anchor-lang", "borsh 0.10.4", "bytemuck", "light-array-map", - "light-compressed-account 0.8.0", + "light-compressed-account", "light-compressible", "light-hasher", "light-macros", "light-program-profiler", - "light-zero-copy 0.6.0", + "light-zero-copy", "pinocchio", "pinocchio-pubkey", "solana-account-info", @@ -3020,14 +3170,12 @@ dependencies = [ [[package]] name = "light-token-types" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "278dddbf18d104f1225c480ca6d7b8710e1f9ff4104f24be70c522ecb6ed1dfc" +version = "0.4.0" dependencies = [ "anchor-lang", "borsh 0.10.4", "light-account-checks", - "light-compressed-account 0.8.0", + "light-compressed-account", "light-macros", "light-sdk-types", "solana-msg", @@ -3036,53 +3184,25 @@ dependencies = [ [[package]] name = "light-verifier" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f35f47736be493b60d8b56ef0c8e94afd6a99efafebb257f62b0b545e9aacab" +version = "8.0.0" dependencies = [ "groth16-solana", - "light-compressed-account 0.8.0", + "light-compressed-account", "thiserror 2.0.18", ] -[[package]] -name = "light-zero-copy" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8862f463792fd60ae8f5dc418150c16213e302e19d54fba0694cf8515be5ff" -dependencies = [ - "light-zero-copy-derive 0.5.0", - "zerocopy", -] - [[package]] name = "light-zero-copy" version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5621fb515e14af46148699c0b65334aabe230a1d2cbd06736ccc7a408c8a4af" dependencies = [ - "light-zero-copy-derive 0.6.0", + "light-zero-copy-derive", "solana-program-error", "zerocopy", ] -[[package]] -name = "light-zero-copy-derive" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8af086d52100b3cab1f2993b146adc7a69fa6aaa878ae4c19514c77c50304379" -dependencies = [ - "lazy_static", - "proc-macro2", - "quote", - "syn 2.0.114", -] - [[package]] name = "light-zero-copy-derive" version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c46425e5c7ab5203ff5c86ae2615b169cca55f9283f5f60f5dd74143be6934" dependencies = [ "lazy_static", "proc-macro2", @@ -3324,9 +3444,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" [[package]] name = "num-derive" @@ -3454,9 +3574,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-src" -version = "300.5.4+3.5.4" +version = "300.5.5+3.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507b3792995dae9b0df8a1c1e3771e8418b7c2d9f0baeba32e6fe8b06c7cb72" +checksum = "3f1787d533e03597a7934fd0a765f0d28e94ecc5fb7789f8053b1e699a56f709" dependencies = [ "cc", ] @@ -3560,8 +3680,6 @@ dependencies = [ [[package]] name = "photon-api" version = "0.54.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e572dba0c255f5b8176f15b9e849330d915a8927804f7f9702d5bbbc70e4a1ad" dependencies = [ "reqwest 0.12.28", "serde", @@ -3610,6 +3728,12 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b971851087bc3699b001954ad02389d50c41405ece3548cbcafc88b3e20017a" +[[package]] +name = "pinocchio-log" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd11022408f312e6179ece321c1f7dc0d1b2aa7765fddd39b2a7378d65a899e8" + [[package]] name = "pinocchio-pubkey" version = "0.3.0" @@ -3621,6 +3745,35 @@ dependencies = [ "sha2-const-stable", ] +[[package]] +name = "pinocchio-system" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "141ed5eafb4ab04568bb0e224e3dc9a9de13c933de4c004e0d1a553498be3a7c" +dependencies = [ + "pinocchio", + "pinocchio-pubkey", +] + +[[package]] +name = "pinocchio-token-interface" +version = "0.0.0" +source = "git+https://github.com/Lightprotocol/token?rev=f7bee9bbc8039c224a88ea76e9ae2edd78e0f9c3#f7bee9bbc8039c224a88ea76e9ae2edd78e0f9c3" +dependencies = [ + "pinocchio", + "pinocchio-pubkey", +] + +[[package]] +name = "pinocchio-token-program" +version = "0.1.0" +source = "git+https://github.com/Lightprotocol/token?rev=f7bee9bbc8039c224a88ea76e9ae2edd78e0f9c3#f7bee9bbc8039c224a88ea76e9ae2edd78e0f9c3" +dependencies = [ + "pinocchio", + "pinocchio-log", + "pinocchio-token-interface", +] + [[package]] name = "pkg-config" version = "0.3.32" @@ -3711,9 +3864,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.105" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -3787,7 +3940,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls 0.23.36", - "socket2 0.6.1", + "socket2 0.6.2", "thiserror 2.0.18", "tokio", "tracing", @@ -3824,16 +3977,16 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.1", + "socket2 0.6.2", "tracing", "windows-sys 0.60.2", ] [[package]] name = "quote" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ "proc-macro2", ] @@ -4626,9 +4779,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" dependencies = [ "libc", "windows-sys 0.60.2", @@ -7871,9 +8024,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.45" +version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd" +checksum = "9da98b7d9b7dad93488a84b8248efc35352b0b2657397d4167e7ad67e5d535e5" dependencies = [ "deranged", "itoa", @@ -7886,15 +8039,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "time-macros" -version = "0.2.25" +version = "0.2.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e552d1249bf61ac2a52db88179fd0673def1e1ad8243a00d9ec9ed71fee3dd" +checksum = "78cc610bac2dcee56805c99642447d4c5dbde4d01f752ffea0199aee1f601dc4" dependencies = [ "num-conv", "time-core", @@ -7937,7 +8090,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.1", + "socket2 0.6.2", "tokio-macros", "windows-sys 0.61.2", ] @@ -8323,9 +8476,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" +checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f" dependencies = [ "getrandom 0.3.4", "js-sys", @@ -8887,18 +9040,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.33" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" +checksum = "fdea86ddd5568519879b8187e1cf04e24fce28f7fe046ceecbce472ff19a2572" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.33" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" +checksum = "0c15e1b46eff7c6c91195752e0eeed8ef040e391cdece7c25376957d5f15df22" dependencies = [ "proc-macro2", "quote", @@ -8981,9 +9134,9 @@ dependencies = [ [[package]] name = "zmij" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65" +checksum = "02aae0f83f69aafc94776e879363e9771d7ecbffe2c7fbb6c14c5e00dfe88439" [[package]] name = "zstd" diff --git a/Cargo.toml b/Cargo.toml index 1a290f6..28220f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,10 @@ codegen-units = 1 opt-level = 3 incremental = false codegen-units = 1 + +[patch.crates-io] +light-client = { path = "../light-protocol/sdk-libs/client" } +light-program-test = { path = "../light-protocol/sdk-libs/program-test" } +light-sdk = { path = "../light-protocol/sdk-libs/sdk" } +light-token = { path = "../light-protocol/sdk-libs/token-sdk" } +light-hasher = { path = "../light-protocol/program-libs/hasher" } diff --git a/programs/cp-swap/Cargo.toml b/programs/cp-swap/Cargo.toml index 7ba71c3..4b63df9 100644 --- a/programs/cp-swap/Cargo.toml +++ b/programs/cp-swap/Cargo.toml @@ -32,9 +32,9 @@ bytemuck = { version = "1.4.0", features = ["derive", "min_const_generics"] } arrayref = { version = "0.3.6" } blake3 = { workspace = true } -light-sdk = { version = "0.18.0", features = ["anchor", "anchor-discriminator", "idl-build", "cpi-context"] } -light-token = { version = "0.3.0", features = ["anchor", "idl-build"] } -light-hasher = "5" +light-sdk = { version = ">=0.18.0", features = ["anchor", "anchor-discriminator", "idl-build", "cpi-context"] } +light-token = { version = ">=0.3.0", features = ["anchor", "idl-build"] } +light-hasher = ">=5" light-anchor-spl = { version = "0.31.1", features = ["idl-build", "memo"] } solana-account-info = "2.3" solana-program = "2.2" @@ -48,8 +48,8 @@ quickcheck = "1.0.3" proptest = "1.0" rand = "0.9.0" -light-program-test = { version = "0.18.0" } -light-client = { version = "0.18.0" } +light-program-test = { version = ">=0.18.0", features = ["devenv"] } +light-client = { version = ">=0.18.0" } tokio = { version = "1", features = ["full"] } spl-token = "7.0.0" solana-keypair = { version = "2.2" } diff --git a/programs/cp-swap/src/instructions/deposit.rs b/programs/cp-swap/src/instructions/deposit.rs index 814038b..e2d983d 100644 --- a/programs/cp-swap/src/instructions/deposit.rs +++ b/programs/cp-swap/src/instructions/deposit.rs @@ -6,8 +6,8 @@ use crate::utils::token::*; use anchor_lang::prelude::*; use light_anchor_spl::token::Token; use light_anchor_spl::token_interface::Token2022; +use light_anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface}; use light_token::instruction::MintToCpi; -use light_anchor_spl::token_interface::{TokenAccount, Mint,TokenInterface}; #[derive(Accounts)] pub struct Deposit<'info> { @@ -207,6 +207,7 @@ pub fn deposit( pool_state.lp_supply = pool_state.lp_supply.checked_add(lp_token_amount).unwrap(); MintToCpi { + fee_payer: Some(ctx.accounts.owner.to_account_info()), mint: ctx.accounts.lp_mint.to_account_info(), destination: ctx.accounts.owner_lp_token.to_account_info(), amount: lp_token_amount, diff --git a/programs/cp-swap/src/instructions/initialize.rs b/programs/cp-swap/src/instructions/initialize.rs index 111bfbc..29baa5e 100644 --- a/programs/cp-swap/src/instructions/initialize.rs +++ b/programs/cp-swap/src/instructions/initialize.rs @@ -83,11 +83,13 @@ pub struct Initialize<'info> { #[account(mut)] #[light_account(init, mint, - mint_signer = lp_mint_signer, - authority = authority, - decimals = 9, - mint_seeds = &[LP_MINT_SIGNER_SEED, self.pool_state.to_account_info().key.as_ref(), &[params.lp_mint_signer_bump]], - authority_seeds = &[crate::AUTH_SEED.as_bytes(), &[params.authority_bump]] + mint::signer = lp_mint_signer, + mint::authority = authority, + mint::decimals = 9, + mint::seeds = &[LP_MINT_SIGNER_SEED, self.pool_state.to_account_info().key.as_ref()], + mint::bump = params.lp_mint_signer_bump, + mint::authority_seeds = &[crate::AUTH_SEED.as_bytes()], + mint::authority_bump = params.authority_bump )] pub lp_mint: UncheckedAccount<'info>, @@ -117,7 +119,7 @@ pub struct Initialize<'info> { ], bump, )] - #[light_account(token, authority = [crate::AUTH_SEED.as_bytes()])] + #[light_account(token, token::authority = [crate::AUTH_SEED.as_bytes()])] pub token_0_vault: UncheckedAccount<'info>, #[account( @@ -129,7 +131,7 @@ pub struct Initialize<'info> { ], bump, )] - #[light_account(token, authority = [crate::AUTH_SEED.as_bytes()])] + #[light_account(token, token::authority = [crate::AUTH_SEED.as_bytes()])] pub token_1_vault: UncheckedAccount<'info>, #[account( @@ -199,7 +201,9 @@ pub fn initialize<'info>( owner: ctx.accounts.authority.key(), } .rent_free( - ctx.accounts.light_token_compressible_config.to_account_info(), + ctx.accounts + .light_token_compressible_config + .to_account_info(), ctx.accounts.light_token_rent_sponsor.to_account_info(), ctx.accounts.system_program.to_account_info(), &crate::ID, @@ -219,7 +223,9 @@ pub fn initialize<'info>( owner: ctx.accounts.authority.key(), } .rent_free( - ctx.accounts.light_token_compressible_config.to_account_info(), + ctx.accounts + .light_token_compressible_config + .to_account_info(), ctx.accounts.light_token_rent_sponsor.to_account_info(), ctx.accounts.system_program.to_account_info(), &crate::ID, @@ -332,7 +338,9 @@ pub fn initialize<'info>( } .idempotent() .rent_free( - ctx.accounts.light_token_compressible_config.to_account_info(), + ctx.accounts + .light_token_compressible_config + .to_account_info(), ctx.accounts.light_token_rent_sponsor.to_account_info(), ctx.accounts.system_program.to_account_info(), ) @@ -340,6 +348,7 @@ pub fn initialize<'info>( // Mint LP tokens to creator MintToCpi { + fee_payer: Some(ctx.accounts.creator.to_account_info()), mint: ctx.accounts.lp_mint.to_account_info(), destination: ctx.accounts.creator_lp_token.to_account_info(), amount: user_lp_amount, diff --git a/programs/cp-swap/src/instructions/withdraw.rs b/programs/cp-swap/src/instructions/withdraw.rs index 484d08b..5cc0b92 100644 --- a/programs/cp-swap/src/instructions/withdraw.rs +++ b/programs/cp-swap/src/instructions/withdraw.rs @@ -183,10 +183,12 @@ pub fn withdraw( } BurnCpi { + fee_payer: Some(ctx.accounts.owner.to_account_info()), source: ctx.accounts.owner_lp_token.to_account_info(), mint: ctx.accounts.lp_mint.to_account_info(), amount: lp_token_amount, authority: ctx.accounts.owner.to_account_info(), + system_program: ctx.accounts.system_program.to_account_info(), max_top_up: None, } .invoke()?; diff --git a/programs/cp-swap/tests/functional_test.rs b/programs/cp-swap/tests/functional_test.rs index 9b6218c..664eaa5 100644 --- a/programs/cp-swap/tests/functional_test.rs +++ b/programs/cp-swap/tests/functional_test.rs @@ -269,13 +269,13 @@ async fn test_sdk_from_keyed_accounts() { assert_eq!(sdk.token_1_mint, Some(setup.tokens.token_1_mint)); // Check account requirements for each instruction type - let swap_accounts = sdk.get_accounts_to_update(&CpSwapInstruction::Swap); + let swap_accounts = sdk.get_accounts_for_instruction(CpSwapInstruction::Swap); assert_eq!(swap_accounts.len(), 6, "Swap needs 6 accounts: pool, observation, vault0, vault1, mint0, mint1"); - let deposit_accounts = sdk.get_accounts_to_update(&CpSwapInstruction::Deposit); + let deposit_accounts = sdk.get_accounts_for_instruction(CpSwapInstruction::Deposit); assert_eq!(deposit_accounts.len(), 7, "Deposit needs 7 accounts: pool, observation, vault0, vault1, lp_mint, mint0, mint1"); - let withdraw_accounts = sdk.get_accounts_to_update(&CpSwapInstruction::Withdraw); + let withdraw_accounts = sdk.get_accounts_for_instruction(CpSwapInstruction::Withdraw); assert_eq!(withdraw_accounts.len(), 7, "Withdraw needs 7 accounts: pool, observation, vault0, vault1, lp_mint, mint0, mint1"); // Verify program_id method diff --git a/programs/cp-swap/tests/helpers.rs b/programs/cp-swap/tests/helpers.rs index b0e4b32..76451fb 100644 --- a/programs/cp-swap/tests/helpers.rs +++ b/programs/cp-swap/tests/helpers.rs @@ -2,25 +2,24 @@ /// Functional integration test for cp-swap program. /// Tests pool initialization with light-program-test framework. - use anchor_lang::{InstructionData, ToAccountMetas}; +use light_anchor_spl::memo::spl_memo; use light_client::interface::{ get_create_accounts_proof, CreateAccountsProofInput, CreateAccountsProofResult, InitializeRentFreeConfig, }; -use solana_pubkey::pubkey; use light_program_test::{ program_test::{setup_mock_program_data, LightProgramTest, TestRpc}, Indexer, ProgramTestConfig, Rpc, }; use light_token::{ constants::CPI_AUTHORITY_PDA, + constants::LIGHT_TOKEN_PROGRAM_ID, instruction::{ find_mint_address, get_associated_token_address_and_bump, CreateAssociatedTokenAccount, CreateMint, CreateMintParams, MintTo, COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR as LIGHT_TOKEN_RENT_SPONSOR, }, - constants::LIGHT_TOKEN_PROGRAM_ID, }; use raydium_cp_swap::{ instructions::initialize::LP_MINT_SIGNER_SEED, @@ -29,14 +28,12 @@ use raydium_cp_swap::{ }; use solana_instruction::Instruction; use solana_keypair::Keypair; +use solana_pubkey::pubkey; use solana_pubkey::Pubkey; -use solana_signer::Signer; use solana_sdk::{program_pack::Pack, signature::SeedDerivable}; -use light_anchor_spl::memo::spl_memo; +use solana_signer::Signer; use spl_token_2022; - - // ============================================================================ // Constants // ============================================================================ @@ -88,8 +85,7 @@ pub struct TokenSetup { /// Initialize the test environment with LightProgramTest and compression config. pub async fn setup_test_environment(program_id: Pubkey) -> TestEnv { - let mut config = - ProgramTestConfig::new_v2(true, Some(vec![("raydium_cp_swap", program_id)])); + let mut config = ProgramTestConfig::new_v2(true, Some(vec![("raydium_cp_swap", program_id)])); config = config.with_light_protocol_events(); let mut rpc = LightProgramTest::new(config).await.unwrap(); @@ -197,6 +193,7 @@ pub async fn setup_create_mint( for (idx, (amount, _)) in recipients.iter().enumerate() { if *amount > 0 { let mint_instruction = MintTo { + fee_payer: Some(payer.pubkey()), mint, destination: ata_pubkeys[idx], amount: *amount, @@ -430,7 +427,7 @@ pub async fn get_pool_create_accounts_proof( vec![ CreateAccountsProofInput::pda(pdas.pool_state), CreateAccountsProofInput::pda(pdas.observation_state), - CreateAccountsProofInput::mint(pdas.lp_mint_signer), + CreateAccountsProofInput::mint_from_signer(pdas.lp_mint_signer), ], ) .await @@ -696,7 +693,11 @@ pub async fn assert_onchain_closed(rpc: &mut LightProgramTest, pda: &Pubkey) { } /// Assert all pool accounts exist on-chain (hot or decompressed state). -pub async fn assert_pool_accounts_exist(rpc: &mut LightProgramTest, pdas: &AmmPdas, tokens: &TokenSetup) { +pub async fn assert_pool_accounts_exist( + rpc: &mut LightProgramTest, + pdas: &AmmPdas, + tokens: &TokenSetup, +) { assert_onchain_exists(rpc, &pdas.pool_state).await; assert_onchain_exists(rpc, &pdas.observation_state).await; assert_onchain_exists(rpc, &pdas.lp_mint).await; @@ -708,7 +709,11 @@ pub async fn assert_pool_accounts_exist(rpc: &mut LightProgramTest, pdas: &AmmPd } /// Assert all pool accounts are compressed (closed on-chain). -pub async fn assert_pool_accounts_compressed(rpc: &mut LightProgramTest, pdas: &AmmPdas, tokens: &TokenSetup) { +pub async fn assert_pool_accounts_compressed( + rpc: &mut LightProgramTest, + pdas: &AmmPdas, + tokens: &TokenSetup, +) { assert_onchain_closed(rpc, &pdas.pool_state).await; assert_onchain_closed(rpc, &pdas.observation_state).await; assert_onchain_closed(rpc, &pdas.lp_mint).await; @@ -834,9 +839,17 @@ pub async fn setup_pool_environment(program_id: Pubkey, amm_config_index: u16) - .unwrap(); let initial_balance = 1_000_000; - let tokens = setup_token_mints(&mut env.rpc, &env.payer, &creator.pubkey(), initial_balance).await; + let tokens = + setup_token_mints(&mut env.rpc, &env.payer, &creator.pubkey(), initial_balance).await; - let amm_config = create_amm_config(&mut env.rpc, &env.payer, &admin, program_id, amm_config_index).await; + let amm_config = create_amm_config( + &mut env.rpc, + &env.payer, + &admin, + program_id, + amm_config_index, + ) + .await; setup_create_pool_fee_account(&mut env.rpc, &env.payer.pubkey()); let pdas = derive_amm_pdas( diff --git a/programs/cp-swap/tests/program.rs b/programs/cp-swap/tests/program.rs index 3af4711..9240405 100644 --- a/programs/cp-swap/tests/program.rs +++ b/programs/cp-swap/tests/program.rs @@ -6,11 +6,10 @@ /// - Parsing pool accounts from AccountInterface /// - Tracking account state (hot/cold) /// - Building AccountSpec for load instructions - use anchor_lang::AnchorDeserialize; use light_client::interface::{ - AccountInterface, AccountSpec, AccountToFetch, ColdContext, LightProgramInterface, PdaSpec, - TokenAccountInterface, + AccountInterface, AccountSpec, AccountToFetch, ColdAccountSpec, ColdContext, + LightProgramInterface, PdaSpec, TokenAccountInterface, }; use light_sdk::LightDiscriminator; use light_token::compat::{CTokenData, TokenData}; @@ -156,7 +155,10 @@ impl CpSwapSdk { } /// Parse observation state from AccountInterface. - fn parse_observation_state(&mut self, interface: AccountInterface) -> Result<(), CpSwapSdkError> { + fn parse_observation_state( + &mut self, + interface: AccountInterface, + ) -> Result<(), CpSwapSdkError> { let pool_state = self .pool_state_pubkey .ok_or(CpSwapSdkError::PoolStateNotParsed)?; @@ -186,7 +188,9 @@ impl CpSwapSdk { /// Vaults are program-owned PDAs, so we convert them to PdaSpec with CTokenData variant. pub fn set_token_vault(&mut self, interface: TokenAccountInterface, is_vault_0: bool) { let key = interface.key; - let pool_state = self.pool_state_pubkey.expect("pool_state must be set before vaults"); + let pool_state = self + .pool_state_pubkey + .expect("pool_state must be set before vaults"); let mint = if is_vault_0 { self.token_0_mint.expect("token_0_mint must be set") } else { @@ -408,7 +412,7 @@ impl CpSwapSdk { impl LightProgramInterface for CpSwapSdk { type Variant = LightAccountVariant; - type Instruction = CpSwapInstruction; + type InstructionKind = CpSwapInstruction; type Error = CpSwapSdkError; fn program_id(&self) -> Pubkey { @@ -448,7 +452,7 @@ impl LightProgramInterface for CpSwapSdk { Ok(sdk) } - fn get_accounts_to_update(&self, ix: &Self::Instruction) -> Vec { + fn get_accounts_for_instruction(&self, kind: Self::InstructionKind) -> Vec { let mut accounts = Vec::new(); // All instructions need pool_state and observation_state @@ -476,7 +480,7 @@ impl LightProgramInterface for CpSwapSdk { } // Deposit and Withdraw also need LP mint - match ix { + match kind { CpSwapInstruction::Deposit | CpSwapInstruction::Withdraw => { if let Some(pubkey) = self.lp_mint { accounts.push(AccountToFetch::mint(pubkey)); @@ -488,8 +492,25 @@ impl LightProgramInterface for CpSwapSdk { accounts } - fn update(&mut self, accounts: &[AccountInterface]) -> Result<(), Self::Error> { + fn update_with_interfaces(&mut self, accounts: &[AccountInterface]) -> Result<(), Self::Error> { for account in accounts { + // Handle decompression: if account was cold but now hot, remove from specs + if account.is_hot() { + if self + .pda_specs + .get(&account.key) + .map_or(false, |s| s.is_cold()) + { + self.pda_specs.remove(&account.key); + } + if self + .mint_specs + .get(&account.key) + .map_or(false, |s| s.is_cold()) + { + self.mint_specs.remove(&account.key); + } + } self.parse_account(account)?; } Ok(()) @@ -511,7 +532,7 @@ impl LightProgramInterface for CpSwapSdk { specs } - fn get_specs_for_instruction(&self, ix: &Self::Instruction) -> Vec> { + fn get_specs_for_instruction(&self, kind: Self::InstructionKind) -> Vec> { let mut specs = Vec::new(); // Pool state and observation state needed for all instructions @@ -551,7 +572,7 @@ impl LightProgramInterface for CpSwapSdk { } // LP mint needed for deposit/withdraw - match ix { + match kind { CpSwapInstruction::Deposit | CpSwapInstruction::Withdraw => { if let Some(pubkey) = self.lp_mint { if let Some(spec) = self.mint_specs.get(&pubkey) { @@ -564,4 +585,140 @@ impl LightProgramInterface for CpSwapSdk { specs } + + fn get_compressible_accounts(&self) -> Vec { + let mut accounts = Vec::new(); + if let Some(pubkey) = self.pool_state_pubkey { + accounts.push(pubkey); + } + if let Some(pubkey) = self.observation_key { + accounts.push(pubkey); + } + if let Some(pubkey) = self.token_0_vault { + accounts.push(pubkey); + } + if let Some(pubkey) = self.token_1_vault { + accounts.push(pubkey); + } + if let Some(pubkey) = self.lp_mint { + accounts.push(pubkey); + } + accounts + } + + fn get_compressible_accounts_for_instruction(&self, kind: Self::InstructionKind) -> Vec { + let mut accounts = Vec::new(); + if let Some(pubkey) = self.pool_state_pubkey { + accounts.push(pubkey); + } + if let Some(pubkey) = self.observation_key { + accounts.push(pubkey); + } + if let Some(pubkey) = self.token_0_vault { + accounts.push(pubkey); + } + if let Some(pubkey) = self.token_1_vault { + accounts.push(pubkey); + } + match kind { + CpSwapInstruction::Deposit | CpSwapInstruction::Withdraw => { + if let Some(pubkey) = self.lp_mint { + accounts.push(pubkey); + } + } + CpSwapInstruction::Swap => {} + } + accounts + } + + fn get_cold_specs_for_instruction( + &self, + kind: Self::InstructionKind, + ) -> Vec> { + let mut cold_specs = Vec::new(); + + // Check pool_state + if let Some(pubkey) = self.pool_state_pubkey { + if let Some(spec) = self.pda_specs.get(&pubkey) { + if spec.is_cold() { + if let Some(compressed) = spec.interface.as_compressed_account() { + cold_specs.push(ColdAccountSpec::Pda { + key: pubkey, + compressed: compressed.clone(), + variant: spec.variant.clone(), + program_id: PROGRAM_ID, + }); + } + } + } + } + + // Check observation_state + if let Some(pubkey) = self.observation_key { + if let Some(spec) = self.pda_specs.get(&pubkey) { + if spec.is_cold() { + if let Some(compressed) = spec.interface.as_compressed_account() { + cold_specs.push(ColdAccountSpec::Pda { + key: pubkey, + compressed: compressed.clone(), + variant: spec.variant.clone(), + program_id: PROGRAM_ID, + }); + } + } + } + } + + // Check vaults (stored as PDAs) + for vault_pubkey in [self.token_0_vault, self.token_1_vault].into_iter().flatten() { + if let Some(spec) = self.pda_specs.get(&vault_pubkey) { + if spec.is_cold() { + if let Some(compressed) = spec.interface.as_compressed_account() { + cold_specs.push(ColdAccountSpec::Pda { + key: vault_pubkey, + compressed: compressed.clone(), + variant: spec.variant.clone(), + program_id: PROGRAM_ID, + }); + } + } + } + } + + // Check mints + for mint_pubkey in [self.token_0_mint, self.token_1_mint].into_iter().flatten() { + if let Some(iface) = self.mint_specs.get(&mint_pubkey) { + if iface.is_cold() { + if let Some(compressed) = iface.as_compressed_account() { + cold_specs.push(ColdAccountSpec::Mint { + key: mint_pubkey, + compressed: compressed.clone(), + }); + } + } + } + } + + // LP mint only for deposit/withdraw + match kind { + CpSwapInstruction::Deposit | CpSwapInstruction::Withdraw => { + if let Some(lp_mint) = self.lp_mint { + if let Some(iface) = self.mint_specs.get(&lp_mint) { + if iface.is_cold() { + if let Some(compressed) = iface.as_compressed_account() { + cold_specs.push(ColdAccountSpec::Mint { + key: lp_mint, + compressed: compressed.clone(), + }); + } + } + } + } + } + CpSwapInstruction::Swap => {} + } + + cold_specs + } + } diff --git a/programs/cp-swap/tests/program_test.rs b/programs/cp-swap/tests/program_test.rs index 78a5fda..e303d72 100644 --- a/programs/cp-swap/tests/program_test.rs +++ b/programs/cp-swap/tests/program_test.rs @@ -81,7 +81,7 @@ async fn test_sdk_lifecycle() { .expect("from_keyed_accounts should succeed"); // ==================== PHASE 6: Fetch & Update SDK ==================== - let accounts_to_fetch = sdk.get_accounts_to_update(&CpSwapInstruction::Deposit); + let accounts_to_fetch = sdk.get_accounts_for_instruction(CpSwapInstruction::Deposit); let keyed_accounts = setup .env .rpc @@ -89,11 +89,11 @@ async fn test_sdk_lifecycle() { .await .expect("get_multiple_account_interfaces should succeed"); - sdk.update(&keyed_accounts) + sdk.update_with_interfaces(&keyed_accounts) .expect("sdk.update should succeed"); // ==================== PHASE 7: Build Specs for Load ==================== - let mut all_specs = sdk.get_specs_for_instruction(&CpSwapInstruction::Deposit); + let mut all_specs = sdk.get_specs_for_instruction(CpSwapInstruction::Deposit); // Fetch creator's ATAs (compressed) and add to specs let creator_lp_ata_interface = setup From 40d7a7e7aeedb50f7fb02f9c93b4c90136175f18 Mon Sep 17 00:00:00 2001 From: Swenschaeferjohann Date: Thu, 29 Jan 2026 19:18:57 +0000 Subject: [PATCH 2/4] sync to branch, add spl, t22, combo test cov --- .../instructions/admin/collect_fund_fee.rs | 14 + .../admin/collect_protocol_fee.rs | 14 + programs/cp-swap/src/instructions/deposit.rs | 14 + .../cp-swap/src/instructions/initialize.rs | 16 + .../src/instructions/swap_base_input.rs | 36 +- .../src/instructions/swap_base_output.rs | 22 + programs/cp-swap/src/instructions/withdraw.rs | 14 + programs/cp-swap/src/lib.rs | 54 +- programs/cp-swap/src/utils/token.rs | 21 +- programs/cp-swap/tests/functional_test.rs | 272 ++++++++- programs/cp-swap/tests/helpers.rs | 523 +++++++++++++++++- programs/cp-swap/tests/program_test.rs | 20 +- 12 files changed, 974 insertions(+), 46 deletions(-) diff --git a/programs/cp-swap/src/instructions/admin/collect_fund_fee.rs b/programs/cp-swap/src/instructions/admin/collect_fund_fee.rs index 068d806..77be19e 100644 --- a/programs/cp-swap/src/instructions/admin/collect_fund_fee.rs +++ b/programs/cp-swap/src/instructions/admin/collect_fund_fee.rs @@ -73,12 +73,22 @@ pub struct CollectFundFee<'info> { /// CHECK: light_token CPI authority. pub light_token_cpi_authority: AccountInfo<'info>, + + /// Optional SPL interface PDA for token_0 (only needed if token_0 is SPL) + #[account(mut)] + pub spl_interface_pda_0: Option>, + + /// Optional SPL interface PDA for token_1 (only needed if token_1 is SPL) + #[account(mut)] + pub spl_interface_pda_1: Option>, } pub fn collect_fund_fee( ctx: Context, amount_0_requested: u64, amount_1_requested: u64, + spl_interface_bump_0: Option, + spl_interface_bump_1: Option, ) -> Result<()> { let amount_0: u64; let amount_1: u64; @@ -109,6 +119,8 @@ pub fn collect_fund_fee( ctx.accounts.owner.to_account_info(), ctx.accounts.light_token_cpi_authority.to_account_info(), ctx.accounts.system_program.to_account_info(), + ctx.accounts.spl_interface_pda_0.clone(), + spl_interface_bump_0, )?; transfer_from_pool_vault_to_user( @@ -126,6 +138,8 @@ pub fn collect_fund_fee( ctx.accounts.owner.to_account_info(), ctx.accounts.light_token_cpi_authority.to_account_info(), ctx.accounts.system_program.to_account_info(), + ctx.accounts.spl_interface_pda_1.clone(), + spl_interface_bump_1, )?; Ok(()) diff --git a/programs/cp-swap/src/instructions/admin/collect_protocol_fee.rs b/programs/cp-swap/src/instructions/admin/collect_protocol_fee.rs index 830be27..aaac2b5 100644 --- a/programs/cp-swap/src/instructions/admin/collect_protocol_fee.rs +++ b/programs/cp-swap/src/instructions/admin/collect_protocol_fee.rs @@ -74,12 +74,22 @@ pub struct CollectProtocolFee<'info> { /// CHECK: light_token CPI authority. pub light_token_cpi_authority: AccountInfo<'info>, + + /// Optional SPL interface PDA for token_0 (only needed if token_0 is SPL) + #[account(mut)] + pub spl_interface_pda_0: Option>, + + /// Optional SPL interface PDA for token_1 (only needed if token_1 is SPL) + #[account(mut)] + pub spl_interface_pda_1: Option>, } pub fn collect_protocol_fee( ctx: Context, amount_0_requested: u64, amount_1_requested: u64, + spl_interface_bump_0: Option, + spl_interface_bump_1: Option, ) -> Result<()> { let amount_0: u64; let amount_1: u64; @@ -118,6 +128,8 @@ pub fn collect_protocol_fee( ctx.accounts.owner.to_account_info(), ctx.accounts.light_token_cpi_authority.to_account_info(), ctx.accounts.system_program.to_account_info(), + ctx.accounts.spl_interface_pda_0.clone(), + spl_interface_bump_0, )?; transfer_from_pool_vault_to_user( @@ -135,6 +147,8 @@ pub fn collect_protocol_fee( ctx.accounts.owner.to_account_info(), ctx.accounts.light_token_cpi_authority.to_account_info(), ctx.accounts.system_program.to_account_info(), + ctx.accounts.spl_interface_pda_1.clone(), + spl_interface_bump_1, )?; Ok(()) diff --git a/programs/cp-swap/src/instructions/deposit.rs b/programs/cp-swap/src/instructions/deposit.rs index e2d983d..a0bb379 100644 --- a/programs/cp-swap/src/instructions/deposit.rs +++ b/programs/cp-swap/src/instructions/deposit.rs @@ -93,6 +93,14 @@ pub struct Deposit<'info> { /// CHECK: light-token CPI authority. pub light_token_cpi_authority: AccountInfo<'info>, + + /// Optional SPL interface PDA for token_0 (only needed if token_0 is SPL) + #[account(mut)] + pub spl_interface_pda_0: Option>, + + /// Optional SPL interface PDA for token_1 (only needed if token_1 is SPL) + #[account(mut)] + pub spl_interface_pda_1: Option>, } pub fn deposit( @@ -100,6 +108,8 @@ pub fn deposit( lp_token_amount: u64, maximum_token_0_amount: u64, maximum_token_1_amount: u64, + spl_interface_bump_0: Option, + spl_interface_bump_1: Option, ) -> Result<()> { require_gt!(lp_token_amount, 0); let pool_id = ctx.accounts.pool_state.key(); @@ -186,6 +196,8 @@ pub fn deposit( ctx.accounts.owner.to_account_info(), ctx.accounts.light_token_cpi_authority.to_account_info(), ctx.accounts.system_program.to_account_info(), + ctx.accounts.spl_interface_pda_0.clone(), + spl_interface_bump_0, )?; transfer_from_user_to_pool_vault( @@ -202,6 +214,8 @@ pub fn deposit( ctx.accounts.owner.to_account_info(), ctx.accounts.light_token_cpi_authority.to_account_info(), ctx.accounts.system_program.to_account_info(), + ctx.accounts.spl_interface_pda_1.clone(), + spl_interface_bump_1, )?; pool_state.lp_supply = pool_state.lp_supply.checked_add(lp_token_amount).unwrap(); diff --git a/programs/cp-swap/src/instructions/initialize.rs b/programs/cp-swap/src/instructions/initialize.rs index 29baa5e..3abd2f5 100644 --- a/programs/cp-swap/src/instructions/initialize.rs +++ b/programs/cp-swap/src/instructions/initialize.rs @@ -34,6 +34,10 @@ pub struct InitializeParams { pub lp_mint_signer_bump: u8, pub creator_lp_token_bump: u8, pub authority_bump: u8, + /// SPL interface PDA bump for token_0 (None for Light tokens) + pub spl_interface_bump_0: Option, + /// SPL interface PDA bump for token_1 (None for Light tokens) + pub spl_interface_bump_1: Option, } #[derive(Accounts, LightAccounts)] @@ -166,6 +170,14 @@ pub struct Initialize<'info> { /// CHECK: light-token CPI authority. pub light_token_cpi_authority: AccountInfo<'info>, + + /// Optional SPL interface PDA for token_0 (only needed if token_0 is SPL) + #[account(mut)] + pub spl_interface_pda_0: Option>, + + /// Optional SPL interface PDA for token_1 (only needed if token_1 is SPL) + #[account(mut)] + pub spl_interface_pda_1: Option>, } pub fn initialize<'info>( @@ -248,6 +260,8 @@ pub fn initialize<'info>( ctx.accounts.creator.to_account_info(), ctx.accounts.light_token_cpi_authority.to_account_info(), ctx.accounts.system_program.to_account_info(), + ctx.accounts.spl_interface_pda_0.clone(), + params.spl_interface_bump_0, )?; transfer_from_user_to_pool_vault( @@ -260,6 +274,8 @@ pub fn initialize<'info>( ctx.accounts.creator.to_account_info(), ctx.accounts.light_token_cpi_authority.to_account_info(), ctx.accounts.system_program.to_account_info(), + ctx.accounts.spl_interface_pda_1.clone(), + params.spl_interface_bump_1, )?; // Get vault balances - supports both light token and spl token accounts diff --git a/programs/cp-swap/src/instructions/swap_base_input.rs b/programs/cp-swap/src/instructions/swap_base_input.rs index 1c686a0..234a90b 100644 --- a/programs/cp-swap/src/instructions/swap_base_input.rs +++ b/programs/cp-swap/src/instructions/swap_base_input.rs @@ -79,9 +79,23 @@ pub struct Swap<'info> { /// CHECK: light_token CPI authority. pub light_token_cpi_authority: AccountInfo<'info>, + + /// Optional SPL interface PDA for token_0 (only needed if token_0 is SPL) + #[account(mut)] + pub spl_interface_pda_0: Option>, + + /// Optional SPL interface PDA for token_1 (only needed if token_1 is SPL) + #[account(mut)] + pub spl_interface_pda_1: Option>, } -pub fn swap_base_input(ctx: Context, amount_in: u64, minimum_amount_out: u64) -> Result<()> { +pub fn swap_base_input( + ctx: Context, + amount_in: u64, + minimum_amount_out: u64, + spl_interface_bump_0: Option, + spl_interface_bump_1: Option, +) -> Result<()> { let block_timestamp = solana_program::clock::Clock::get()?.unix_timestamp as u64; let pool_id = ctx.accounts.pool_state.key(); let pool_state = &mut ctx.accounts.pool_state; @@ -233,6 +247,22 @@ pub fn swap_base_input(ctx: Context, amount_in: u64, minimum_amount_out: u }); require_gte!(constant_after, constant_before); + // Select SPL interface PDAs based on trade direction + let (input_spl_pda, input_spl_bump, output_spl_pda, output_spl_bump) = match trade_direction { + TradeDirection::ZeroForOne => ( + ctx.accounts.spl_interface_pda_0.clone(), + spl_interface_bump_0, + ctx.accounts.spl_interface_pda_1.clone(), + spl_interface_bump_1, + ), + TradeDirection::OneForZero => ( + ctx.accounts.spl_interface_pda_1.clone(), + spl_interface_bump_1, + ctx.accounts.spl_interface_pda_0.clone(), + spl_interface_bump_0, + ), + }; + transfer_from_user_to_pool_vault( ctx.accounts.payer.to_account_info(), ctx.accounts.input_token_account.to_account_info(), @@ -243,6 +273,8 @@ pub fn swap_base_input(ctx: Context, amount_in: u64, minimum_amount_out: u ctx.accounts.payer.to_account_info(), ctx.accounts.light_token_cpi_authority.to_account_info(), ctx.accounts.system_program.to_account_info(), + input_spl_pda, + input_spl_bump, )?; transfer_from_pool_vault_to_user( @@ -256,6 +288,8 @@ pub fn swap_base_input(ctx: Context, amount_in: u64, minimum_amount_out: u ctx.accounts.payer.to_account_info(), ctx.accounts.light_token_cpi_authority.to_account_info(), ctx.accounts.system_program.to_account_info(), + output_spl_pda, + output_spl_bump, )?; // update the previous price to the observation diff --git a/programs/cp-swap/src/instructions/swap_base_output.rs b/programs/cp-swap/src/instructions/swap_base_output.rs index 853e460..209e0d7 100644 --- a/programs/cp-swap/src/instructions/swap_base_output.rs +++ b/programs/cp-swap/src/instructions/swap_base_output.rs @@ -10,6 +10,8 @@ pub fn swap_base_output( ctx: Context, max_amount_in: u64, amount_out_less_fee: u64, + spl_interface_bump_0: Option, + spl_interface_bump_1: Option, ) -> Result<()> { require_gt!(amount_out_less_fee, 0); let block_timestamp = solana_program::clock::Clock::get()?.unix_timestamp as u64; @@ -165,6 +167,22 @@ pub fn swap_base_output( }); require_gte!(constant_after, constant_before); + // Select SPL interface PDAs based on trade direction + let (input_spl_pda, input_spl_bump, output_spl_pda, output_spl_bump) = match trade_direction { + TradeDirection::ZeroForOne => ( + ctx.accounts.spl_interface_pda_0.clone(), + spl_interface_bump_0, + ctx.accounts.spl_interface_pda_1.clone(), + spl_interface_bump_1, + ), + TradeDirection::OneForZero => ( + ctx.accounts.spl_interface_pda_1.clone(), + spl_interface_bump_1, + ctx.accounts.spl_interface_pda_0.clone(), + spl_interface_bump_0, + ), + }; + transfer_from_user_to_pool_vault( ctx.accounts.payer.to_account_info(), ctx.accounts.input_token_account.to_account_info(), @@ -175,6 +193,8 @@ pub fn swap_base_output( ctx.accounts.payer.to_account_info(), ctx.accounts.light_token_cpi_authority.to_account_info(), ctx.accounts.system_program.to_account_info(), + input_spl_pda, + input_spl_bump, )?; transfer_from_pool_vault_to_user( @@ -188,6 +208,8 @@ pub fn swap_base_output( ctx.accounts.payer.to_account_info(), ctx.accounts.light_token_cpi_authority.to_account_info(), ctx.accounts.system_program.to_account_info(), + output_spl_pda, + output_spl_bump, )?; // update the previous price to the observation diff --git a/programs/cp-swap/src/instructions/withdraw.rs b/programs/cp-swap/src/instructions/withdraw.rs index 5cc0b92..b3e35c6 100644 --- a/programs/cp-swap/src/instructions/withdraw.rs +++ b/programs/cp-swap/src/instructions/withdraw.rs @@ -102,6 +102,14 @@ pub struct Withdraw<'info> { /// Light Token program for CPI pub light_token_program: Interface<'info, TokenInterface>, + + /// Optional SPL interface PDA for token_0 (only needed if token_0 is SPL) + #[account(mut)] + pub spl_interface_pda_0: Option>, + + /// Optional SPL interface PDA for token_1 (only needed if token_1 is SPL) + #[account(mut)] + pub spl_interface_pda_1: Option>, } pub fn withdraw( @@ -109,6 +117,8 @@ pub fn withdraw( lp_token_amount: u64, minimum_token_0_amount: u64, minimum_token_1_amount: u64, + spl_interface_bump_0: Option, + spl_interface_bump_1: Option, ) -> Result<()> { require_gt!(lp_token_amount, 0); let pool_id = ctx.accounts.pool_state.key(); @@ -210,6 +220,8 @@ pub fn withdraw( ctx.accounts.owner.to_account_info(), ctx.accounts.light_token_cpi_authority.to_account_info(), ctx.accounts.system_program.to_account_info(), + ctx.accounts.spl_interface_pda_0.clone(), + spl_interface_bump_0, )?; transfer_from_pool_vault_to_user( @@ -227,6 +239,8 @@ pub fn withdraw( ctx.accounts.owner.to_account_info(), ctx.accounts.light_token_cpi_authority.to_account_info(), ctx.accounts.system_program.to_account_info(), + ctx.accounts.spl_interface_pda_1.clone(), + spl_interface_bump_1, )?; pool_state.recent_epoch = Clock::get()?.epoch; diff --git a/programs/cp-swap/src/lib.rs b/programs/cp-swap/src/lib.rs index 2d80571..cb5fc8e 100644 --- a/programs/cp-swap/src/lib.rs +++ b/programs/cp-swap/src/lib.rs @@ -145,8 +145,16 @@ pub mod raydium_cp_swap { ctx: Context, amount_0_requested: u64, amount_1_requested: u64, + spl_interface_bump_0: Option, + spl_interface_bump_1: Option, ) -> Result<()> { - instructions::collect_protocol_fee(ctx, amount_0_requested, amount_1_requested) + instructions::collect_protocol_fee( + ctx, + amount_0_requested, + amount_1_requested, + spl_interface_bump_0, + spl_interface_bump_1, + ) } /// Collect the fund fee accrued to the pool @@ -161,8 +169,16 @@ pub mod raydium_cp_swap { ctx: Context, amount_0_requested: u64, amount_1_requested: u64, + spl_interface_bump_0: Option, + spl_interface_bump_1: Option, ) -> Result<()> { - instructions::collect_fund_fee(ctx, amount_0_requested, amount_1_requested) + instructions::collect_fund_fee( + ctx, + amount_0_requested, + amount_1_requested, + spl_interface_bump_0, + spl_interface_bump_1, + ) } /// Creates a pool for the given token pair and the initial price @@ -193,12 +209,16 @@ pub mod raydium_cp_swap { lp_token_amount: u64, maximum_token_0_amount: u64, maximum_token_1_amount: u64, + spl_interface_bump_0: Option, + spl_interface_bump_1: Option, ) -> Result<()> { instructions::deposit( ctx, lp_token_amount, maximum_token_0_amount, maximum_token_1_amount, + spl_interface_bump_0, + spl_interface_bump_1, ) } @@ -216,12 +236,16 @@ pub mod raydium_cp_swap { lp_token_amount: u64, minimum_token_0_amount: u64, minimum_token_1_amount: u64, + spl_interface_bump_0: Option, + spl_interface_bump_1: Option, ) -> Result<()> { instructions::withdraw( ctx, lp_token_amount, minimum_token_0_amount, minimum_token_1_amount, + spl_interface_bump_0, + spl_interface_bump_1, ) } @@ -237,8 +261,16 @@ pub mod raydium_cp_swap { ctx: Context, amount_in: u64, minimum_amount_out: u64, + spl_interface_bump_0: Option, + spl_interface_bump_1: Option, ) -> Result<()> { - instructions::swap_base_input(ctx, amount_in, minimum_amount_out) + instructions::swap_base_input( + ctx, + amount_in, + minimum_amount_out, + spl_interface_bump_0, + spl_interface_bump_1, + ) } /// Swap the tokens in the pool base output amount @@ -249,7 +281,19 @@ pub mod raydium_cp_swap { /// * `max_amount_in` - input amount prevents excessive slippage /// * `amount_out` - amount of output token /// - pub fn swap_base_output(ctx: Context, max_amount_in: u64, amount_out: u64) -> Result<()> { - instructions::swap_base_output(ctx, max_amount_in, amount_out) + pub fn swap_base_output( + ctx: Context, + max_amount_in: u64, + amount_out: u64, + spl_interface_bump_0: Option, + spl_interface_bump_1: Option, + ) -> Result<()> { + instructions::swap_base_output( + ctx, + max_amount_in, + amount_out, + spl_interface_bump_0, + spl_interface_bump_1, + ) } } diff --git a/programs/cp-swap/src/utils/token.rs b/programs/cp-swap/src/utils/token.rs index 1849b79..5a1d089 100644 --- a/programs/cp-swap/src/utils/token.rs +++ b/programs/cp-swap/src/utils/token.rs @@ -6,6 +6,7 @@ use light_anchor_spl::{ token_interface::{initialize_account3, InitializeAccount3, Mint}, }; use light_sdk::constants::LIGHT_TOKEN_PROGRAM_ID; +pub use light_token::instruction::get_spl_interface_pda_and_bump; use light_token::instruction::TransferInterfaceCpi; use spl_token_2022::{ self, @@ -55,11 +56,13 @@ pub fn transfer_from_user_to_pool_vault<'a>( from: AccountInfo<'a>, to_vault: AccountInfo<'a>, mint: AccountInfo<'a>, - _token_program: AccountInfo<'a>, + token_program: AccountInfo<'a>, amount: u64, payer: AccountInfo<'a>, light_token_cpi_authority: AccountInfo<'a>, system_program: AccountInfo<'a>, + spl_interface_pda: Option>, + spl_interface_pda_bump: Option, ) -> Result<()> { if amount == 0 { return Ok(()); @@ -79,6 +82,12 @@ pub fn transfer_from_user_to_pool_vault<'a>( light_token_cpi_authority, system_program, ) + .spl_interface( + mint.clone(), + token_program, + spl_interface_pda, + spl_interface_pda_bump, + ) .invoke() .map_err(|e| anchor_lang::prelude::ProgramError::from(e))?; @@ -90,12 +99,14 @@ pub fn transfer_from_pool_vault_to_user<'a>( from_vault: AccountInfo<'a>, to: AccountInfo<'a>, mint: AccountInfo<'a>, - _token_program: AccountInfo<'a>, + token_program: AccountInfo<'a>, amount: u64, signer_seeds: &[&[&[u8]]], payer: AccountInfo<'a>, light_token_cpi_authority: AccountInfo<'a>, system_program: AccountInfo<'a>, + spl_interface_pda: Option>, + spl_interface_pda_bump: Option, ) -> Result<()> { if amount == 0 { return Ok(()); @@ -115,6 +126,12 @@ pub fn transfer_from_pool_vault_to_user<'a>( light_token_cpi_authority, system_program, ) + .spl_interface( + mint.clone(), + token_program, + spl_interface_pda, + spl_interface_pda_bump, + ) .invoke_signed(signer_seeds) .map_err(|e| anchor_lang::prelude::ProgramError::from(e))?; diff --git a/programs/cp-swap/tests/functional_test.rs b/programs/cp-swap/tests/functional_test.rs index 664eaa5..ea58a20 100644 --- a/programs/cp-swap/tests/functional_test.rs +++ b/programs/cp-swap/tests/functional_test.rs @@ -1,6 +1,5 @@ /// Functional integration test for cp-swap program. /// Tests pool initialization with light-program-test framework. - use light_client::interface::AccountInterfaceExt; use light_program_test::program_test::TestRpc; use light_program_test::Rpc; @@ -86,7 +85,10 @@ async fn test_full_lifecycle() { // Check initial LP token balance (should have received initial LP tokens from initialize) let lp_balance_after_init = get_token_balance(&mut env.rpc, pdas.creator_lp_token).await; println!("LP balance after init: {}", lp_balance_after_init); - assert!(lp_balance_after_init > 0, "Should have received LP tokens from initialization"); + assert!( + lp_balance_after_init > 0, + "Should have received LP tokens from initialization" + ); // ======================================================================== // Deposit @@ -108,6 +110,7 @@ async fn test_full_lifecycle() { deposit_lp_amount, max_token_0, max_token_1, + SplInterfaceInfo::default(), ); env.rpc @@ -152,6 +155,9 @@ async fn test_full_lifecycle() { true, // is_token_0_input swap_amount_in, min_amount_out, + SplInterfaceInfo::default(), + light_token_program_id(), + light_token_program_id(), ); env.rpc @@ -193,6 +199,7 @@ async fn test_full_lifecycle() { withdraw_lp_amount, 0, // minimum_token_0_amount - accept any 0, // minimum_token_1_amount - accept any + SplInterfaceInfo::default(), ); env.rpc @@ -218,11 +225,241 @@ async fn test_full_lifecycle() { println!("Full lifecycle test completed successfully!"); } +// ============================================================================ +// Token Type Pool Tests - Full Lifecycle +// ============================================================================ + +/// Generic full lifecycle test for any token type combination. +/// Tests: Initialize -> Deposit -> Swap -> Withdraw +async fn test_pool_with_token_types(type_a: TokenType, type_b: TokenType, config_index: u16) { + let test_name = format!("{:?} + {:?}", type_a, type_b); + println!("\n=== {} Full Lifecycle ===", test_name); + + let program_id = raydium_cp_swap::ID; + + // Setup environment + let mut env = setup_test_environment(program_id).await; + + let creator = Keypair::new(); + env.rpc + .airdrop_lamports(&creator.pubkey(), 100_000_000_000) + .await + .unwrap(); + + let admin = get_admin_keypair(); + env.rpc + .airdrop_lamports(&admin.pubkey(), 10_000_000_000) + .await + .unwrap(); + + // Create token pair with specified types + let initial_balance = 1_000_000; + let flex_tokens = setup_token_pair( + &mut env.rpc, + &env.payer, + &creator.pubkey(), + initial_balance, + type_a, + type_b, + ) + .await; + + println!( + "Tokens: 0={:?}, 1={:?}", + flex_tokens.token_0_type, flex_tokens.token_1_type + ); + + // Create SPL interface PDAs for any SPL/Token-2022 mints + create_spl_interface_pdas_for_setup(&mut env.rpc, &env.payer, &flex_tokens).await; + + let tokens = flex_tokens.to_token_setup(); + let spl_interface = flex_tokens.build_spl_interface(); + + // Create AMM config + let amm_config = + create_amm_config(&mut env.rpc, &env.payer, &admin, program_id, config_index).await; + assert_amm_config_created(&mut env.rpc, amm_config).await; + setup_create_pool_fee_account(&mut env.rpc, &env.payer.pubkey()); + + let pdas = derive_amm_pdas( + &program_id, + &amm_config, + &tokens.token_0_mint, + &tokens.token_1_mint, + &creator.pubkey(), + ); + + // ======================================================================== + // Initialize + // ======================================================================== + let proof_result = get_pool_create_accounts_proof(&env.rpc, &program_id, &pdas).await; + + let init_instruction = build_initialize_instruction_with_spl( + program_id, + creator.pubkey(), + amm_config, + &pdas, + &tokens, + env.config_pda, + &proof_result, + 100_000, + 100_000, + 0, + flex_tokens.token_0_type.program_id(), + flex_tokens.token_1_type.program_id(), + spl_interface.clone(), + ); + + env.rpc + .create_and_send_transaction(&[init_instruction], &creator.pubkey(), &[&creator]) + .await + .expect("Initialize should succeed"); + + assert_pool_initialized(&mut env.rpc, &pdas).await; + let lp_after_init = get_token_balance(&mut env.rpc, pdas.creator_lp_token).await; + assert!(lp_after_init > 0, "Should have LP tokens after init"); + println!("[Init] LP: {}", lp_after_init); + + // ======================================================================== + // Deposit + // ======================================================================== + let lp_before_deposit = get_token_balance(&mut env.rpc, pdas.creator_lp_token).await; + + let deposit_instruction = build_deposit_instruction( + program_id, + creator.pubkey(), + &pdas, + &tokens, + tokens.creator_token_0, + tokens.creator_token_1, + 500, // lp_amount + 10_000, // max_token_0 + 10_000, // max_token_1 + spl_interface.clone(), + ); + + env.rpc + .create_and_send_transaction(&[deposit_instruction], &creator.pubkey(), &[&creator]) + .await + .expect("Deposit should succeed"); + + assert_deposit_succeeded(&mut env.rpc, pdas.creator_lp_token, lp_before_deposit, 500).await; + println!( + "[Deposit] LP: {}", + get_token_balance(&mut env.rpc, pdas.creator_lp_token).await + ); + + // ======================================================================== + // Swap (token_0 -> token_1) + // ======================================================================== + env.rpc.warp_to_slot(100).unwrap(); + + let t0_before = get_token_balance(&mut env.rpc, tokens.creator_token_0).await; + let t1_before = get_token_balance(&mut env.rpc, tokens.creator_token_1).await; + + let swap_instruction = build_swap_instruction( + program_id, + creator.pubkey(), + amm_config, + &pdas, + &tokens, + tokens.creator_token_0, + tokens.creator_token_1, + true, // is_token_0_input + 100, // amount_in + 1, // min_amount_out + spl_interface.clone(), + flex_tokens.token_0_type.program_id(), + flex_tokens.token_1_type.program_id(), + ); + + env.rpc + .create_and_send_transaction(&[swap_instruction], &creator.pubkey(), &[&creator]) + .await + .expect("Swap should succeed"); + + assert_swap_succeeded( + &mut env.rpc, + tokens.creator_token_0, + tokens.creator_token_1, + t0_before, + t1_before, + 100, + 1, + ) + .await; + println!( + "[Swap] T0: {}, T1: {}", + get_token_balance(&mut env.rpc, tokens.creator_token_0).await, + get_token_balance(&mut env.rpc, tokens.creator_token_1).await + ); + + // ======================================================================== + // Withdraw + // ======================================================================== + let lp_before_withdraw = get_token_balance(&mut env.rpc, pdas.creator_lp_token).await; + let withdraw_amount = lp_before_withdraw / 2; + + let withdraw_instruction = build_withdraw_instruction( + program_id, + creator.pubkey(), + &pdas, + &tokens, + tokens.creator_token_0, + tokens.creator_token_1, + withdraw_amount, + 0, + 0, + spl_interface, + ); + + env.rpc + .create_and_send_transaction(&[withdraw_instruction], &creator.pubkey(), &[&creator]) + .await + .expect("Withdraw should succeed"); + + assert_withdraw_succeeded( + &mut env.rpc, + pdas.creator_lp_token, + lp_before_withdraw, + withdraw_amount, + ) + .await; + println!( + "[Withdraw] LP: {}, T0: {}, T1: {}", + get_token_balance(&mut env.rpc, pdas.creator_lp_token).await, + get_token_balance(&mut env.rpc, tokens.creator_token_0).await, + get_token_balance(&mut env.rpc, tokens.creator_token_1).await + ); + + println!("=== {} Full Lifecycle PASSED ===\n", test_name); +} + +#[tokio::test] +async fn test_pool_spl_light() { + test_pool_with_token_types(TokenType::Spl, TokenType::Light, 10).await; +} + +#[tokio::test] +async fn test_pool_spl_spl() { + test_pool_with_token_types(TokenType::Spl, TokenType::Spl, 11).await; +} + +#[tokio::test] +async fn test_pool_spl_token2022() { + test_pool_with_token_types(TokenType::Spl, TokenType::Token2022, 12).await; +} + +#[tokio::test] +async fn test_pool_light_token2022() { + test_pool_with_token_types(TokenType::Light, TokenType::Token2022, 13).await; +} + /// Test SDK initialization from fetched accounts and account requirements. #[tokio::test] async fn test_sdk_from_keyed_accounts() { - use program::{CpSwapSdk, CpSwapInstruction}; use light_client::interface::LightProgramInterface; + use program::{CpSwapInstruction, CpSwapSdk}; let program_id = raydium_cp_swap::ID; @@ -230,7 +467,8 @@ async fn test_sdk_from_keyed_accounts() { let mut setup = setup_pool_environment(program_id, 2).await; // Initialize pool first (SDK requires actual account data) - let proof_result = get_pool_create_accounts_proof(&setup.env.rpc, &program_id, &setup.pdas).await; + let proof_result = + get_pool_create_accounts_proof(&setup.env.rpc, &program_id, &setup.pdas).await; let init_ix = build_initialize_instruction( program_id, setup.creator.pubkey(), @@ -243,13 +481,17 @@ async fn test_sdk_from_keyed_accounts() { 100_000, 0, ); - setup.env.rpc + setup + .env + .rpc .create_and_send_transaction(&[init_ix], &setup.creator.pubkey(), &[&setup.creator]) .await .expect("Initialize should succeed"); // Fetch pool state account - let pool_interface = setup.env.rpc + let pool_interface = setup + .env + .rpc .get_account_interface(&setup.pdas.pool_state, &program_id) .await .expect("get_account_interface should succeed"); @@ -270,13 +512,25 @@ async fn test_sdk_from_keyed_accounts() { // Check account requirements for each instruction type let swap_accounts = sdk.get_accounts_for_instruction(CpSwapInstruction::Swap); - assert_eq!(swap_accounts.len(), 6, "Swap needs 6 accounts: pool, observation, vault0, vault1, mint0, mint1"); + assert_eq!( + swap_accounts.len(), + 6, + "Swap needs 6 accounts: pool, observation, vault0, vault1, mint0, mint1" + ); let deposit_accounts = sdk.get_accounts_for_instruction(CpSwapInstruction::Deposit); - assert_eq!(deposit_accounts.len(), 7, "Deposit needs 7 accounts: pool, observation, vault0, vault1, lp_mint, mint0, mint1"); + assert_eq!( + deposit_accounts.len(), + 7, + "Deposit needs 7 accounts: pool, observation, vault0, vault1, lp_mint, mint0, mint1" + ); let withdraw_accounts = sdk.get_accounts_for_instruction(CpSwapInstruction::Withdraw); - assert_eq!(withdraw_accounts.len(), 7, "Withdraw needs 7 accounts: pool, observation, vault0, vault1, lp_mint, mint0, mint1"); + assert_eq!( + withdraw_accounts.len(), + 7, + "Withdraw needs 7 accounts: pool, observation, vault0, vault1, lp_mint, mint0, mint1" + ); // Verify program_id method assert_eq!(sdk.program_id(), program_id); diff --git a/programs/cp-swap/tests/helpers.rs b/programs/cp-swap/tests/helpers.rs index 76451fb..0179205 100644 --- a/programs/cp-swap/tests/helpers.rs +++ b/programs/cp-swap/tests/helpers.rs @@ -16,10 +16,11 @@ use light_token::{ constants::CPI_AUTHORITY_PDA, constants::LIGHT_TOKEN_PROGRAM_ID, instruction::{ - find_mint_address, get_associated_token_address_and_bump, CreateAssociatedTokenAccount, - CreateMint, CreateMintParams, MintTo, COMPRESSIBLE_CONFIG_V1, + find_mint_address, get_associated_token_address_and_bump, get_spl_interface_pda_and_bump, + CreateAssociatedTokenAccount, CreateMint, CreateMintParams, MintTo, COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR as LIGHT_TOKEN_RENT_SPONSOR, }, + spl_interface::CreateSplInterfacePda, }; use raydium_cp_swap::{ instructions::initialize::LP_MINT_SIGNER_SEED, @@ -212,6 +213,60 @@ pub async fn setup_create_mint( (mint, ata_pubkeys, mint_seed) } +/// Create the SPL interface PDA (token pool) for an SPL/Token-2022 mint. +/// This is required before SPL tokens can be transferred to/from Light Token accounts. +pub async fn create_spl_interface_pda( + rpc: &mut LightProgramTest, + payer: &Keypair, + mint: &Pubkey, +) -> Pubkey { + create_spl_interface_pda_with_program(rpc, payer, mint, spl_token::id()).await +} + +/// Create the SPL interface PDA for a mint with specified token program. +pub async fn create_spl_interface_pda_with_program( + rpc: &mut LightProgramTest, + payer: &Keypair, + mint: &Pubkey, + token_program: Pubkey, +) -> Pubkey { + let (spl_interface_pda, _bump) = get_spl_interface_pda_and_bump(mint); + + let ix = CreateSplInterfacePda::new(payer.pubkey(), *mint, token_program, false).instruction(); + + rpc.create_and_send_transaction(&[ix], &payer.pubkey(), &[payer]) + .await + .expect("Create SPL interface PDA should succeed"); + + spl_interface_pda +} + +/// Create SPL interface PDAs for any non-Light tokens in the setup. +pub async fn create_spl_interface_pdas_for_setup( + rpc: &mut LightProgramTest, + payer: &Keypair, + setup: &FlexibleTokenSetup, +) { + if setup.token_0_type.needs_spl_interface() { + create_spl_interface_pda_with_program( + rpc, + payer, + &setup.token_0_mint, + setup.token_0_type.program_id(), + ) + .await; + } + if setup.token_1_type.needs_spl_interface() { + create_spl_interface_pda_with_program( + rpc, + payer, + &setup.token_1_mint, + setup.token_1_type.program_id(), + ) + .await; + } +} + /// Create token mints and fund creator with initial balances. pub async fn setup_token_mints( rpc: &mut LightProgramTest, @@ -259,6 +314,364 @@ pub async fn setup_token_mints( } } +/// Create an SPL token mint (not Light token) and fund creator with initial balance. +pub async fn setup_spl_mint( + rpc: &mut LightProgramTest, + payer: &Keypair, + decimals: u8, + recipients: Vec<(u64, Pubkey)>, +) -> (Pubkey, Keypair, Vec) { + use light_anchor_spl::associated_token::spl_associated_token_account; + use solana_sdk::program_pack::Pack; + + let mint_keypair = Keypair::new(); + let mint_pubkey = mint_keypair.pubkey(); + + // Create mint account + let mint_rent = rpc + .get_minimum_balance_for_rent_exemption(spl_token::state::Mint::LEN) + .await + .unwrap(); + let create_mint_ix = solana_sdk::system_instruction::create_account( + &payer.pubkey(), + &mint_pubkey, + mint_rent, + spl_token::state::Mint::LEN as u64, + &spl_token::id(), + ); + + let init_mint_ix = spl_token::instruction::initialize_mint( + &spl_token::id(), + &mint_pubkey, + &payer.pubkey(), + None, + decimals, + ) + .unwrap(); + + rpc.create_and_send_transaction( + &[create_mint_ix, init_mint_ix], + &payer.pubkey(), + &[payer, &mint_keypair], + ) + .await + .expect("Create SPL mint should succeed"); + + if recipients.is_empty() { + return (mint_pubkey, mint_keypair, vec![]); + } + + let mut ata_pubkeys = Vec::with_capacity(recipients.len()); + + for (amount, owner) in &recipients { + let ata = spl_associated_token_account::get_associated_token_address(owner, &mint_pubkey); + ata_pubkeys.push(ata); + + let create_ata_ix = + spl_associated_token_account::instruction::create_associated_token_account( + &payer.pubkey(), + owner, + &mint_pubkey, + &spl_token::id(), + ); + + rpc.create_and_send_transaction(&[create_ata_ix], &payer.pubkey(), &[payer]) + .await + .expect("Create SPL ATA should succeed"); + + if *amount > 0 { + let mint_to_ix = spl_token::instruction::mint_to( + &spl_token::id(), + &mint_pubkey, + &ata, + &payer.pubkey(), + &[], + *amount, + ) + .unwrap(); + + rpc.create_and_send_transaction(&[mint_to_ix], &payer.pubkey(), &[payer]) + .await + .expect("Mint SPL tokens should succeed"); + } + } + + (mint_pubkey, mint_keypair, ata_pubkeys) +} + +/// Token type for flexible test setup. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum TokenType { + Light, + Spl, + Token2022, +} + +impl TokenType { + pub fn program_id(&self) -> Pubkey { + match self { + TokenType::Light => light_token_program_id(), + TokenType::Spl => spl_token::id(), + TokenType::Token2022 => spl_token_2022::id(), + } + } + + pub fn needs_spl_interface(&self) -> bool { + matches!(self, TokenType::Spl | TokenType::Token2022) + } +} + +/// Flexible token setup that works with any combination of token types. +pub struct FlexibleTokenSetup { + pub token_0_mint: Pubkey, + pub token_1_mint: Pubkey, + pub token_0_mint_signer: Pubkey, + pub token_1_mint_signer: Pubkey, + pub creator_token_0: Pubkey, + pub creator_token_1: Pubkey, + pub token_0_type: TokenType, + pub token_1_type: TokenType, +} + +impl FlexibleTokenSetup { + pub fn to_token_setup(&self) -> TokenSetup { + TokenSetup { + token_0_mint: self.token_0_mint, + token_1_mint: self.token_1_mint, + token_0_mint_signer: self.token_0_mint_signer, + token_1_mint_signer: self.token_1_mint_signer, + creator_token_0: self.creator_token_0, + creator_token_1: self.creator_token_1, + } + } + + pub fn build_spl_interface(&self) -> SplInterfaceInfo { + let (token_0_pda, token_0_bump) = if self.token_0_type.needs_spl_interface() { + let (pda, bump) = get_spl_interface_pda_and_bump(&self.token_0_mint); + (Some(pda), Some(bump)) + } else { + (None, None) + }; + + let (token_1_pda, token_1_bump) = if self.token_1_type.needs_spl_interface() { + let (pda, bump) = get_spl_interface_pda_and_bump(&self.token_1_mint); + (Some(pda), Some(bump)) + } else { + (None, None) + }; + + SplInterfaceInfo { + token_0_pda, + token_0_bump, + token_1_pda, + token_1_bump, + } + } +} + +/// Create a Token-2022 mint (no extensions) and fund creator with initial balance. +pub async fn setup_token2022_mint( + rpc: &mut LightProgramTest, + payer: &Keypair, + decimals: u8, + recipients: Vec<(u64, Pubkey)>, +) -> (Pubkey, Keypair, Vec) { + use light_anchor_spl::associated_token::spl_associated_token_account; + use solana_sdk::program_pack::Pack; + + let mint_keypair = Keypair::new(); + let mint_pubkey = mint_keypair.pubkey(); + + // Create mint account for Token-2022 + let mint_rent = rpc + .get_minimum_balance_for_rent_exemption(spl_token_2022::state::Mint::LEN) + .await + .unwrap(); + let create_mint_ix = solana_sdk::system_instruction::create_account( + &payer.pubkey(), + &mint_pubkey, + mint_rent, + spl_token_2022::state::Mint::LEN as u64, + &spl_token_2022::id(), + ); + + let init_mint_ix = spl_token_2022::instruction::initialize_mint( + &spl_token_2022::id(), + &mint_pubkey, + &payer.pubkey(), + None, + decimals, + ) + .unwrap(); + + rpc.create_and_send_transaction( + &[create_mint_ix, init_mint_ix], + &payer.pubkey(), + &[payer, &mint_keypair], + ) + .await + .expect("Create Token-2022 mint should succeed"); + + if recipients.is_empty() { + return (mint_pubkey, mint_keypair, vec![]); + } + + let mut ata_pubkeys = Vec::with_capacity(recipients.len()); + + for (amount, owner) in &recipients { + let ata = spl_associated_token_account::get_associated_token_address_with_program_id( + owner, + &mint_pubkey, + &spl_token_2022::id(), + ); + ata_pubkeys.push(ata); + + let create_ata_ix = + spl_associated_token_account::instruction::create_associated_token_account( + &payer.pubkey(), + owner, + &mint_pubkey, + &spl_token_2022::id(), + ); + + rpc.create_and_send_transaction(&[create_ata_ix], &payer.pubkey(), &[payer]) + .await + .expect("Create Token-2022 ATA should succeed"); + + if *amount > 0 { + let mint_to_ix = spl_token_2022::instruction::mint_to( + &spl_token_2022::id(), + &mint_pubkey, + &ata, + &payer.pubkey(), + &[], + *amount, + ) + .unwrap(); + + rpc.create_and_send_transaction(&[mint_to_ix], &payer.pubkey(), &[payer]) + .await + .expect("Mint Token-2022 tokens should succeed"); + } + } + + (mint_pubkey, mint_keypair, ata_pubkeys) +} + +/// Create a single token based on type. +async fn create_single_token( + rpc: &mut LightProgramTest, + payer: &Keypair, + creator: &Pubkey, + initial_balance: u64, + token_type: TokenType, +) -> (Pubkey, Pubkey, Pubkey) { + // Returns (mint, mint_signer, creator_ata) + match token_type { + TokenType::Light => { + let (mint, atas, mint_seed) = setup_create_mint( + rpc, + payer, + payer.pubkey(), + 9, + vec![(initial_balance, *creator)], + ) + .await; + (mint, mint_seed.pubkey(), atas[0]) + } + TokenType::Spl => { + let (mint, _keypair, atas) = + setup_spl_mint(rpc, payer, 9, vec![(initial_balance, *creator)]).await; + (mint, Pubkey::default(), atas[0]) + } + TokenType::Token2022 => { + let (mint, _keypair, atas) = + setup_token2022_mint(rpc, payer, 9, vec![(initial_balance, *creator)]).await; + (mint, Pubkey::default(), atas[0]) + } + } +} + +/// Create token pair with specified types. +pub async fn setup_token_pair( + rpc: &mut LightProgramTest, + payer: &Keypair, + creator: &Pubkey, + initial_balance: u64, + type_a: TokenType, + type_b: TokenType, +) -> FlexibleTokenSetup { + let (mint_a, signer_a, ata_a) = + create_single_token(rpc, payer, creator, initial_balance, type_a).await; + let (mint_b, signer_b, ata_b) = + create_single_token(rpc, payer, creator, initial_balance, type_b).await; + + // Ensure proper ordering: token_0_mint < token_1_mint + if mint_a < mint_b { + FlexibleTokenSetup { + token_0_mint: mint_a, + token_1_mint: mint_b, + token_0_mint_signer: signer_a, + token_1_mint_signer: signer_b, + creator_token_0: ata_a, + creator_token_1: ata_b, + token_0_type: type_a, + token_1_type: type_b, + } + } else { + FlexibleTokenSetup { + token_0_mint: mint_b, + token_1_mint: mint_a, + token_0_mint_signer: signer_b, + token_1_mint_signer: signer_a, + creator_token_0: ata_b, + creator_token_1: ata_a, + token_0_type: type_b, + token_1_type: type_a, + } + } +} + +/// Legacy: Token setup for mixed SPL + Light token pair. +pub struct MixedTokenSetup { + pub token_0_mint: Pubkey, + pub token_1_mint: Pubkey, + pub token_0_mint_signer: Pubkey, + pub token_1_mint_signer: Pubkey, + pub creator_token_0: Pubkey, + pub creator_token_1: Pubkey, + pub token_0_is_spl: bool, + pub token_1_is_spl: bool, +} + +/// Legacy: Create token mints where one is SPL and other is Light. +pub async fn setup_mixed_token_mints( + rpc: &mut LightProgramTest, + payer: &Keypair, + creator: &Pubkey, + initial_balance: u64, +) -> MixedTokenSetup { + let flex = setup_token_pair( + rpc, + payer, + creator, + initial_balance, + TokenType::Spl, + TokenType::Light, + ) + .await; + MixedTokenSetup { + token_0_mint: flex.token_0_mint, + token_1_mint: flex.token_1_mint, + token_0_mint_signer: flex.token_0_mint_signer, + token_1_mint_signer: flex.token_1_mint_signer, + creator_token_0: flex.creator_token_0, + creator_token_1: flex.creator_token_1, + token_0_is_spl: flex.token_0_type == TokenType::Spl, + token_1_is_spl: flex.token_1_type == TokenType::Spl, + } +} + // ============================================================================ // AMM Config Functions // ============================================================================ @@ -445,6 +858,7 @@ pub fn build_withdraw_instruction( lp_token_amount: u64, minimum_token_0_amount: u64, minimum_token_1_amount: u64, + spl_interface: SplInterfaceInfo, ) -> Instruction { let accounts = raydium_cp_swap::accounts::Withdraw { owner, @@ -464,12 +878,16 @@ pub fn build_withdraw_instruction( system_program: solana_sdk::system_program::ID, light_token_cpi_authority: CPI_AUTHORITY_PDA, light_token_program: light_token_program_id(), + spl_interface_pda_0: spl_interface.token_0_pda, + spl_interface_pda_1: spl_interface.token_1_pda, }; let instruction_data = raydium_cp_swap::instruction::Withdraw { lp_token_amount, minimum_token_0_amount, minimum_token_1_amount, + spl_interface_bump_0: spl_interface.token_0_bump, + spl_interface_bump_1: spl_interface.token_1_bump, }; Instruction { @@ -491,22 +909,30 @@ pub fn build_swap_instruction( is_token_0_input: bool, // true = swap 0->1, false = swap 1->0 amount_in: u64, minimum_amount_out: u64, + spl_interface: SplInterfaceInfo, + token_0_program: Pubkey, + token_1_program: Pubkey, ) -> Instruction { - let (input_vault, output_vault, input_mint, output_mint) = if is_token_0_input { - ( - pdas.token_0_vault, - pdas.token_1_vault, - tokens.token_0_mint, - tokens.token_1_mint, - ) - } else { - ( - pdas.token_1_vault, - pdas.token_0_vault, - tokens.token_1_mint, - tokens.token_0_mint, - ) - }; + let (input_vault, output_vault, input_mint, output_mint, input_program, output_program) = + if is_token_0_input { + ( + pdas.token_0_vault, + pdas.token_1_vault, + tokens.token_0_mint, + tokens.token_1_mint, + token_0_program, + token_1_program, + ) + } else { + ( + pdas.token_1_vault, + pdas.token_0_vault, + tokens.token_1_mint, + tokens.token_0_mint, + token_1_program, + token_0_program, + ) + }; let accounts = raydium_cp_swap::accounts::Swap { payer, @@ -517,19 +943,23 @@ pub fn build_swap_instruction( output_token_account, input_vault, output_vault, - input_token_program: light_token_program_id(), - output_token_program: light_token_program_id(), + input_token_program: input_program, + output_token_program: output_program, input_token_mint: input_mint, output_token_mint: output_mint, observation_state: pdas.observation_state, light_token_program: light_token_program_id(), system_program: solana_sdk::system_program::ID, light_token_cpi_authority: CPI_AUTHORITY_PDA, + spl_interface_pda_0: spl_interface.token_0_pda, + spl_interface_pda_1: spl_interface.token_1_pda, }; let instruction_data = raydium_cp_swap::instruction::SwapBaseInput { amount_in, minimum_amount_out, + spl_interface_bump_0: spl_interface.token_0_bump, + spl_interface_bump_1: spl_interface.token_1_bump, }; Instruction { @@ -550,6 +980,7 @@ pub fn build_deposit_instruction( lp_token_amount: u64, maximum_token_0_amount: u64, maximum_token_1_amount: u64, + spl_interface: SplInterfaceInfo, ) -> Instruction { let accounts = raydium_cp_swap::accounts::Deposit { owner, @@ -568,12 +999,16 @@ pub fn build_deposit_instruction( lp_mint: pdas.lp_mint, system_program: solana_sdk::system_program::ID, light_token_cpi_authority: CPI_AUTHORITY_PDA, + spl_interface_pda_0: spl_interface.token_0_pda, + spl_interface_pda_1: spl_interface.token_1_pda, }; let instruction_data = raydium_cp_swap::instruction::Deposit { lp_token_amount, maximum_token_0_amount, maximum_token_1_amount, + spl_interface_bump_0: spl_interface.token_0_bump, + spl_interface_bump_1: spl_interface.token_1_bump, }; Instruction { @@ -583,6 +1018,15 @@ pub fn build_deposit_instruction( } } +/// SPL interface info for instructions involving SPL/Token-2022 tokens. +#[derive(Default, Clone)] +pub struct SplInterfaceInfo { + pub token_0_pda: Option, + pub token_0_bump: Option, + pub token_1_pda: Option, + pub token_1_bump: Option, +} + /// Build the Initialize instruction. pub fn build_initialize_instruction( program_id: Pubkey, @@ -595,6 +1039,39 @@ pub fn build_initialize_instruction( init_amount_0: u64, init_amount_1: u64, open_time: u64, +) -> Instruction { + build_initialize_instruction_with_spl( + program_id, + creator, + amm_config, + pdas, + tokens, + config_pda, + proof_result, + init_amount_0, + init_amount_1, + open_time, + light_token_program_id(), + light_token_program_id(), + SplInterfaceInfo::default(), + ) +} + +/// Build the Initialize instruction with SPL interface support. +pub fn build_initialize_instruction_with_spl( + program_id: Pubkey, + creator: Pubkey, + amm_config: Pubkey, + pdas: &AmmPdas, + tokens: &TokenSetup, + config_pda: Pubkey, + proof_result: &CreateAccountsProofResult, + init_amount_0: u64, + init_amount_1: u64, + open_time: u64, + token_0_program: Pubkey, + token_1_program: Pubkey, + spl_interface: SplInterfaceInfo, ) -> Instruction { let init_params = InitializeParams { init_amount_0, @@ -604,6 +1081,8 @@ pub fn build_initialize_instruction( lp_mint_signer_bump: pdas.lp_mint_signer_bump, creator_lp_token_bump: pdas.creator_lp_token_bump, authority_bump: pdas.authority_bump, + spl_interface_bump_0: spl_interface.token_0_bump, + spl_interface_bump_1: spl_interface.token_1_bump, }; let accounts = raydium_cp_swap::accounts::Initialize { @@ -623,8 +1102,8 @@ pub fn build_initialize_instruction( observation_state: pdas.observation_state, create_pool_fee: raydium_cp_swap::create_pool_fee_receiver::ID, token_program: spl_token::id(), - token_0_program: light_token_program_id(), - token_1_program: light_token_program_id(), + token_0_program, + token_1_program, associated_token_program: light_anchor_spl::associated_token::ID, system_program: solana_sdk::system_program::ID, rent: solana_sdk::sysvar::rent::ID, @@ -633,6 +1112,8 @@ pub fn build_initialize_instruction( light_token_rent_sponsor: Pubkey::from(LIGHT_TOKEN_RENT_SPONSOR), light_token_program: light_token_program_id(), light_token_cpi_authority: CPI_AUTHORITY_PDA, + spl_interface_pda_0: spl_interface.token_0_pda, + spl_interface_pda_1: spl_interface.token_1_pda, }; let instruction_data = raydium_cp_swap::instruction::Initialize { diff --git a/programs/cp-swap/tests/program_test.rs b/programs/cp-swap/tests/program_test.rs index e303d72..13818f4 100644 --- a/programs/cp-swap/tests/program_test.rs +++ b/programs/cp-swap/tests/program_test.rs @@ -1,6 +1,5 @@ /// Clean integration test for cp-swap using CpSwapSdk. /// Tests the full lifecycle: Initialize -> Warp -> Compress -> Load -> Execute Operations - use light_client::interface::{ create_load_instructions, AccountInterfaceExt, AccountSpec, LightProgramInterface, }; @@ -19,7 +18,12 @@ use program::{CpSwapInstruction, CpSwapSdk}; fn log_transaction_size(name: &str, ixs: &[Instruction]) { let tx = Transaction::new_with_payer(ixs, None); let serialized = bincode::serialize(&tx).expect("Failed to serialize transaction"); - println!("{}: {} bytes ({} instructions)", name, serialized.len(), ixs.len()); + println!( + "{}: {} bytes ({} instructions)", + name, + serialized.len(), + ixs.len() + ); } #[tokio::test] @@ -55,12 +59,7 @@ async fn test_sdk_lifecycle() { assert_pool_accounts_exist(&mut setup.env.rpc, &setup.pdas, &setup.tokens).await; // ==================== PHASE 3: Warp to Trigger Compression ==================== - setup - .env - .rpc - .warp_epoch_forward(30) - .await - .unwrap(); + setup.env.rpc.warp_epoch_forward(30).await.unwrap(); // ==================== PHASE 4: Assert All Accounts Are Compressed ==================== assert_pool_accounts_compressed(&mut setup.env.rpc, &setup.pdas, &setup.tokens).await; @@ -159,6 +158,7 @@ async fn test_sdk_lifecycle() { 500, 10_000, 10_000, + SplInterfaceInfo::default(), ); log_transaction_size("Deposit transaction", &[deposit_ix.clone()]); @@ -186,6 +186,9 @@ async fn test_sdk_lifecycle() { true, 100, 1, + SplInterfaceInfo::default(), + light_token_program_id(), + light_token_program_id(), ); log_transaction_size("Swap transaction", &[swap_ix.clone()]); @@ -213,6 +216,7 @@ async fn test_sdk_lifecycle() { lp_balance / 2, 0, 0, + SplInterfaceInfo::default(), ); setup .env From bf428dca3e35082d3d3d653ba5f89cfff16b4f09 Mon Sep 17 00:00:00 2001 From: Swenschaeferjohann Date: Mon, 9 Feb 2026 21:50:19 +0000 Subject: [PATCH 3/4] upd --- Cargo.lock | 881 ++++++++++++------ programs/cp-swap/tests/functional_test.rs | 56 +- programs/cp-swap/tests/program.rs | 1002 ++++++++------------- programs/cp-swap/tests/program_test.rs | 72 +- 4 files changed, 1083 insertions(+), 928 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 854f4ea..b9d66fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,19 +16,19 @@ dependencies = [ name = "account-compression" version = "2.0.0" dependencies = [ - "aligned-sized", + "aligned-sized 1.1.0", "anchor-lang", "bytemuck", - "light-account-checks", + "light-account-checks 0.8.0", "light-batched-merkle-tree", "light-bounded-vec", - "light-compressed-account", + "light-compressed-account 0.10.0", "light-concurrent-merkle-tree", "light-hash-set", "light-hasher", "light-indexed-merkle-tree", "light-merkle-tree-metadata", - "light-zero-copy", + "light-zero-copy 0.6.0", "num-bigint 0.4.6", "solana-sdk", "solana-security-txt", @@ -155,6 +155,17 @@ dependencies = [ "syn 2.0.114", ] +[[package]] +name = "aligned-sized" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48a526ec4434d531d488af59fe866f36b310fe8906691c75dffa664450a3800a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + [[package]] name = "alloc-no-stdlib" version = "2.0.4" @@ -259,12 +270,12 @@ dependencies = [ "account-compression", "anchor-lang", "anchor-spl", - "light-compressed-account", + "light-compressed-account 0.10.0", "light-hasher", "light-heap", "light-system-program-anchor", "light-token-interface", - "light-zero-copy", + "light-zero-copy 0.6.0", "pinocchio-pubkey", "solana-sdk", "solana-security-txt", @@ -721,6 +732,28 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "aws-lc-rs" +version = "1.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7b6141e96a8c160799cc2d5adecd5cbbe5054cb8c7c4af53da0f83bb7ad256" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c34dda4df7017c8db52132f0f8a2e0f8161649d15723ed63fc00c82d0f2081a" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + [[package]] name = "base64" version = "0.12.3" @@ -1002,6 +1035,12 @@ dependencies = [ "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" version = "1.0.4" @@ -1034,7 +1073,6 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", - "serde", "wasm-bindgen", "windows-link", ] @@ -1049,6 +1087,15 @@ dependencies = [ "inout", ] +[[package]] +name = "cmake" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +dependencies = [ + "cc", +] + [[package]] name = "combine" version = "3.8.1" @@ -1062,6 +1109,16 @@ dependencies = [ "unreachable", ] +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "compression-codecs" version = "0.4.36" @@ -1129,6 +1186,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -1300,16 +1367,6 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "deranged" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" -dependencies = [ - "powerfmt", - "serde_core", -] - [[package]] name = "derivation-path" version = "0.2.0" @@ -1359,10 +1416,10 @@ dependencies = [ ] [[package]] -name = "dyn-clone" -version = "1.0.20" +name = "dunce" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "eager" @@ -1636,6 +1693,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "funty" version = "2.0.0" @@ -1818,7 +1881,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.13.0", + "indexmap", "slab", "tokio", "tokio-util 0.7.18", @@ -1837,7 +1900,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.4.0", - "indexmap 2.13.0", + "indexmap", "slab", "tokio", "tokio-util 0.7.18", @@ -1853,12 +1916,6 @@ dependencies = [ "byteorder", ] -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "hashbrown" version = "0.13.2" @@ -2282,17 +2339,6 @@ dependencies = [ "icu_properties", ] -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - [[package]] name = "indexmap" version = "2.13.0" @@ -2301,8 +2347,6 @@ checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", "hashbrown 0.16.1", - "serde", - "serde_core", ] [[package]] @@ -2385,6 +2429,28 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine 4.6.7", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + [[package]] name = "jobserver" version = "0.1.34" @@ -2499,15 +2565,49 @@ dependencies = [ "libsecp256k1-core", ] +[[package]] +name = "light-account" +version = "0.20.0" +dependencies = [ + "light-account-checks 0.8.0", + "light-compressed-account 0.10.0", + "light-compressible 0.5.0", + "light-hasher", + "light-macros 2.2.0", + "light-sdk-macros 0.20.0", + "light-sdk-types 0.20.0", + "solana-account-info", + "solana-instruction", + "solana-pubkey", +] + [[package]] name = "light-account-checks" version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f20b8640212dd450e18d452a247e28d8ba6fdce3c713fd8b69f1e95874d1b7ab" +dependencies = [ + "solana-account-info", + "solana-msg", + "solana-program-error", + "solana-pubkey", + "solana-sysvar", + "thiserror 2.0.18", +] + +[[package]] +name = "light-account-checks" +version = "0.8.0" dependencies = [ "pinocchio", + "pinocchio-system", "solana-account-info", + "solana-cpi", + "solana-instruction", "solana-msg", "solana-program-error", "solana-pubkey", + "solana-system-interface", "solana-sysvar", "thiserror 2.0.18", ] @@ -2537,18 +2637,18 @@ dependencies = [ [[package]] name = "light-batched-merkle-tree" -version = "0.9.0" +version = "0.10.0" dependencies = [ - "aligned-sized", + "aligned-sized 1.1.0", "borsh 0.10.4", - "light-account-checks", + "light-account-checks 0.8.0", "light-bloom-filter", - "light-compressed-account", + "light-compressed-account 0.10.0", "light-hasher", - "light-macros", + "light-macros 2.2.0", "light-merkle-tree-metadata", "light-verifier", - "light-zero-copy", + "light-zero-copy 0.6.0", "solana-account-info", "solana-msg", "solana-program-error", @@ -2583,7 +2683,7 @@ dependencies = [ [[package]] name = "light-client" -version = "0.19.0" +version = "0.20.0" dependencies = [ "anchor-lang", "async-trait", @@ -2592,22 +2692,26 @@ dependencies = [ "bs58", "futures", "lazy_static", - "light-compressed-account", + "light-account", + "light-compressed-account 0.10.0", "light-compressed-token-sdk", - "light-compressible", + "light-compressible 0.5.0", "light-concurrent-merkle-tree", "light-event", "light-hasher", "light-indexed-merkle-tree", "light-merkle-tree-metadata", "light-prover-client", - "light-sdk", + "light-sdk 0.20.0", + "light-sdk-types 0.20.0", "light-token", "light-token-interface", "litesvm", "num-bigint 0.4.6", "photon-api", "rand 0.8.5", + "reqwest 0.12.28", + "serde_json", "smallvec", "solana-account", "solana-account-decoder-client-types", @@ -2638,15 +2742,32 @@ dependencies = [ [[package]] name = "light-compressed-account" version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c3537bbad9df133ca97fc191b164bba93dba1de4da01a92d979ef63f8ce106" +dependencies = [ + "anchor-lang", + "borsh 0.10.4", + "light-hasher", + "light-macros 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "light-program-profiler", + "light-zero-copy 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror 2.0.18", + "tinyvec", + "zerocopy", +] + +[[package]] +name = "light-compressed-account" +version = "0.10.0" dependencies = [ "anchor-lang", "borsh 0.10.4", "bytemuck", "light-hasher", - "light-macros", + "light-macros 2.2.0", "light-poseidon 0.3.0", "light-program-profiler", - "light-zero-copy", + "light-zero-copy 0.6.0", "pinocchio", "solana-msg", "solana-program-error", @@ -2666,17 +2787,17 @@ dependencies = [ "arrayvec", "bitvec", "borsh 0.10.4", - "light-account-checks", + "light-account-checks 0.8.0", "light-array-map", - "light-compressed-account", - "light-compressible", + "light-compressed-account 0.10.0", + "light-compressible 0.5.0", "light-hasher", "light-heap", - "light-macros", + "light-macros 2.2.0", "light-program-profiler", "light-system-program-anchor", "light-token-interface", - "light-zero-copy", + "light-zero-copy 0.6.0", "pinocchio", "pinocchio-pubkey", "pinocchio-system", @@ -2692,19 +2813,20 @@ dependencies = [ [[package]] name = "light-compressed-token-sdk" -version = "0.1.0" +version = "0.20.0" dependencies = [ "anchor-lang", "arrayvec", "borsh 0.10.4", - "light-account-checks", - "light-compressed-account", + "light-account", + "light-account-checks 0.8.0", + "light-compressed-account 0.10.0", "light-program-profiler", - "light-sdk", - "light-sdk-types", + "light-sdk 0.20.0", + "light-sdk-types 0.20.0", "light-token-interface", "light-token-types", - "light-zero-copy", + "light-zero-copy 0.6.0", "solana-account-info", "solana-cpi", "solana-instruction", @@ -2717,17 +2839,40 @@ dependencies = [ [[package]] name = "light-compressible" version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00d1dc14271907009f4ca23ae99c49279f7d2acc5a83f0f8d7362834b37e2e16" dependencies = [ - "aligned-sized", + "aligned-sized 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "anchor-lang", "borsh 0.10.4", "bytemuck", - "light-account-checks", - "light-compressed-account", + "light-account-checks 0.7.0", + "light-compressed-account 0.9.0", "light-hasher", - "light-macros", + "light-macros 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "light-program-profiler", - "light-zero-copy", + "light-zero-copy 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pinocchio-pubkey", + "solana-pubkey", + "solana-rent", + "thiserror 2.0.18", + "zerocopy", +] + +[[package]] +name = "light-compressible" +version = "0.5.0" +dependencies = [ + "aligned-sized 1.1.0", + "anchor-lang", + "borsh 0.10.4", + "bytemuck", + "light-account-checks 0.8.0", + "light-compressed-account 0.10.0", + "light-hasher", + "light-macros 2.2.0", + "light-program-profiler", + "light-zero-copy 0.6.0", "pinocchio", "pinocchio-pubkey", "solana-pubkey", @@ -2750,12 +2895,13 @@ dependencies = [ [[package]] name = "light-event" -version = "0.4.0" +version = "0.20.0" dependencies = [ "borsh 0.10.4", - "light-compressed-account", + "light-compressed-account 0.10.0", "light-hasher", - "light-zero-copy", + "light-token-interface", + "light-zero-copy 0.6.0", "thiserror 2.0.18", ] @@ -2819,13 +2965,13 @@ dependencies = [ [[package]] name = "light-instruction-decoder" -version = "0.2.0" +version = "0.20.0" dependencies = [ "borsh 0.10.4", "bs58", - "light-compressed-account", + "light-compressed-account 0.10.0", "light-instruction-decoder-derive", - "light-sdk-types", + "light-sdk-types 0.20.0", "light-token-interface", "serde", "solana-instruction", @@ -2858,14 +3004,27 @@ dependencies = [ "syn 2.0.114", ] +[[package]] +name = "light-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "179ac51cadc1d0ca047b4d6265a7cc245ca3affc16a20a2749585aa6464d39c2" +dependencies = [ + "bs58", + "proc-macro2", + "quote", + "solana-pubkey", + "syn 2.0.114", +] + [[package]] name = "light-merkle-tree-metadata" -version = "0.9.0" +version = "0.10.0" dependencies = [ "anchor-lang", "borsh 0.10.4", "bytemuck", - "light-compressed-account", + "light-compressed-account 0.10.0", "solana-msg", "solana-program-error", "solana-sysvar", @@ -2930,7 +3089,7 @@ dependencies = [ [[package]] name = "light-program-test" -version = "0.19.0" +version = "0.20.0" dependencies = [ "account-compression", "anchor-lang", @@ -2940,12 +3099,14 @@ dependencies = [ "bs58", "bytemuck", "chrono", + "light-account", + "light-account-checks 0.8.0", "light-batched-merkle-tree", "light-client", - "light-compressed-account", + "light-compressed-account 0.10.0", "light-compressed-token", "light-compressed-token-sdk", - "light-compressible", + "light-compressible 0.5.0", "light-concurrent-merkle-tree", "light-event", "light-hasher", @@ -2956,11 +3117,11 @@ dependencies = [ "light-merkle-tree-reference", "light-prover-client", "light-registry", - "light-sdk", - "light-sdk-types", + "light-sdk 0.20.0", + "light-sdk-types 0.20.0", "light-token", "light-token-interface", - "light-zero-copy", + "light-zero-copy 0.6.0", "litesvm", "log", "num-bigint 0.4.6", @@ -2987,12 +3148,12 @@ dependencies = [ [[package]] name = "light-prover-client" -version = "6.0.0" +version = "7.0.0" dependencies = [ "ark-bn254 0.5.0", "ark-serialize 0.5.0", "ark-std 0.5.0", - "light-compressed-account", + "light-compressed-account 0.10.0", "light-hasher", "light-indexed-array", "light-sparse-merkle-tree", @@ -3012,18 +3173,18 @@ name = "light-registry" version = "2.1.0" dependencies = [ "account-compression", - "aligned-sized", + "aligned-sized 1.1.0", "anchor-lang", "borsh 0.10.4", - "light-account-checks", + "light-account-checks 0.8.0", "light-batched-merkle-tree", - "light-compressible", - "light-macros", + "light-compressible 0.5.0", + "light-macros 2.2.0", "light-merkle-tree-metadata", "light-program-profiler", "light-system-program-anchor", "light-token-interface", - "light-zero-copy", + "light-zero-copy 0.6.0", "solana-account-info", "solana-instruction", "solana-pubkey", @@ -3035,18 +3196,52 @@ dependencies = [ [[package]] name = "light-sdk" version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f523c64a6fcdafe6df81d647560770529896f2707be8ffcf048d909b7e2b30" dependencies = [ "anchor-lang", "bincode", "borsh 0.10.4", - "light-account-checks", - "light-compressed-account", - "light-compressible", + "light-account-checks 0.7.0", + "light-compressed-account 0.9.0", + "light-compressible 0.4.0", "light-hasher", - "light-macros", - "light-sdk-macros", - "light-sdk-types", - "light-zero-copy", + "light-macros 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "light-sdk-macros 0.19.0", + "light-sdk-types 0.19.0", + "light-zero-copy 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint 0.4.6", + "solana-account-info", + "solana-clock", + "solana-cpi", + "solana-instruction", + "solana-loader-v3-interface", + "solana-msg", + "solana-program", + "solana-program-error", + "solana-pubkey", + "solana-system-interface", + "solana-sysvar", + "thiserror 2.0.18", +] + +[[package]] +name = "light-sdk" +version = "0.20.0" +dependencies = [ + "anchor-lang", + "bincode", + "borsh 0.10.4", + "bytemuck", + "light-account-checks 0.8.0", + "light-compressed-account 0.10.0", + "light-hasher", + "light-macros 2.2.0", + "light-program-profiler", + "light-sdk-macros 0.20.0", + "light-sdk-types 0.20.0", + "light-token-interface", + "light-zero-copy 0.6.0", "num-bigint 0.4.6", "solana-account-info", "solana-clock", @@ -3065,10 +3260,25 @@ dependencies = [ [[package]] name = "light-sdk-macros" version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3d12bb13b84df21a4871dc0837aafc0b48464d3e9b17e2773c0719b6cc724c5" dependencies = [ "darling", "light-hasher", - "light-sdk-types", + "light-sdk-types 0.19.0", + "proc-macro2", + "quote", + "solana-pubkey", + "syn 2.0.114", +] + +[[package]] +name = "light-sdk-macros" +version = "0.20.0" +dependencies = [ + "darling", + "light-hasher", + "light-sdk-types 0.20.0", "proc-macro2", "quote", "solana-pubkey", @@ -3078,17 +3288,37 @@ dependencies = [ [[package]] name = "light-sdk-types" version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d8153b27d057ded7b2b6bbd78c643e3f20bd9a9718ca1a8d4183169ccd812c3" dependencies = [ "anchor-lang", "borsh 0.10.4", - "light-account-checks", - "light-compressed-account", + "light-account-checks 0.7.0", + "light-compressed-account 0.9.0", "light-hasher", - "light-macros", + "light-macros 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "solana-msg", "thiserror 2.0.18", ] +[[package]] +name = "light-sdk-types" +version = "0.20.0" +dependencies = [ + "anchor-lang", + "borsh 0.10.4", + "bytemuck", + "light-account-checks 0.8.0", + "light-compressed-account 0.10.0", + "light-compressible 0.5.0", + "light-hasher", + "light-macros 2.2.0", + "light-token-interface", + "solana-msg", + "solana-program-error", + "thiserror 2.0.18", +] + [[package]] name = "light-sparse-merkle-tree" version = "0.3.0" @@ -3105,33 +3335,34 @@ name = "light-system-program-anchor" version = "2.0.0" dependencies = [ "account-compression", - "aligned-sized", + "aligned-sized 1.1.0", "anchor-lang", - "light-compressed-account", - "light-zero-copy", + "light-compressed-account 0.10.0", + "light-zero-copy 0.6.0", "zerocopy", ] [[package]] name = "light-token" -version = "0.4.0" +version = "0.20.0" dependencies = [ "anchor-lang", "arrayvec", "borsh 0.10.4", - "light-account-checks", + "light-account", + "light-account-checks 0.8.0", "light-batched-merkle-tree", - "light-compressed-account", + "light-compressed-account 0.10.0", "light-compressed-token-sdk", - "light-compressible", - "light-macros", + "light-compressible 0.5.0", + "light-macros 2.2.0", "light-program-profiler", - "light-sdk", - "light-sdk-macros", - "light-sdk-types", + "light-sdk 0.20.0", + "light-sdk-macros 0.20.0", + "light-sdk-types 0.20.0", "light-token-interface", "light-token-types", - "light-zero-copy", + "light-zero-copy 0.6.0", "solana-account-info", "solana-cpi", "solana-instruction", @@ -3144,19 +3375,19 @@ dependencies = [ [[package]] name = "light-token-interface" -version = "0.3.0" +version = "0.4.0" dependencies = [ - "aligned-sized", + "aligned-sized 1.1.0", "anchor-lang", "borsh 0.10.4", "bytemuck", "light-array-map", - "light-compressed-account", - "light-compressible", + "light-compressed-account 0.10.0", + "light-compressible 0.5.0", "light-hasher", - "light-macros", + "light-macros 2.2.0", "light-program-profiler", - "light-zero-copy", + "light-zero-copy 0.6.0", "pinocchio", "pinocchio-pubkey", "solana-account-info", @@ -3170,24 +3401,24 @@ dependencies = [ [[package]] name = "light-token-types" -version = "0.4.0" +version = "0.20.0" dependencies = [ "anchor-lang", "borsh 0.10.4", - "light-account-checks", - "light-compressed-account", - "light-macros", - "light-sdk-types", + "light-account-checks 0.8.0", + "light-compressed-account 0.10.0", + "light-macros 2.2.0", + "light-sdk-types 0.20.0", "solana-msg", "thiserror 2.0.18", ] [[package]] name = "light-verifier" -version = "8.0.0" +version = "9.0.0" dependencies = [ "groth16-solana", - "light-compressed-account", + "light-compressed-account 0.10.0", "thiserror 2.0.18", ] @@ -3195,11 +3426,21 @@ dependencies = [ name = "light-zero-copy" version = "0.6.0" dependencies = [ - "light-zero-copy-derive", + "light-zero-copy-derive 0.6.0", "solana-program-error", "zerocopy", ] +[[package]] +name = "light-zero-copy" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5621fb515e14af46148699c0b65334aabe230a1d2cbd06736ccc7a408c8a4af" +dependencies = [ + "light-zero-copy-derive 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zerocopy", +] + [[package]] name = "light-zero-copy-derive" version = "0.6.0" @@ -3210,6 +3451,18 @@ dependencies = [ "syn 2.0.114", ] +[[package]] +name = "light-zero-copy-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41c46425e5c7ab5203ff5c86ae2615b169cca55f9283f5f60f5dd74143be6934" +dependencies = [ + "lazy_static", + "proc-macro2", + "quote", + "syn 2.0.114", +] + [[package]] name = "linux-raw-sys" version = "0.11.0" @@ -3233,7 +3486,7 @@ dependencies = [ "agave-reserved-account-keys", "ansi_term", "bincode", - "indexmap 2.13.0", + "indexmap", "itertools 0.14.0", "log", "solana-account", @@ -3348,16 +3601,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - [[package]] name = "miniz_oxide" version = "0.8.9" @@ -3388,10 +3631,10 @@ dependencies = [ "libc", "log", "openssl", - "openssl-probe", + "openssl-probe 0.1.6", "openssl-sys", "schannel", - "security-framework", + "security-framework 2.11.1", "security-framework-sys", "tempfile", ] @@ -3442,12 +3685,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-conv" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" - [[package]] name = "num-derive" version = "0.4.2" @@ -3572,6 +3809,12 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + [[package]] name = "openssl-src" version = "300.5.5+3.5.5" @@ -3681,13 +3924,10 @@ dependencies = [ name = "photon-api" version = "0.54.0" dependencies = [ - "reqwest 0.12.28", + "progenitor-client", + "reqwest 0.13.2", "serde", - "serde_derive", "serde_json", - "serde_with", - "url", - "uuid", ] [[package]] @@ -3807,12 +4047,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -3871,6 +4105,21 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "progenitor-client" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffab7b358944dba033a7b324e7558e66e6bcb1fb4705cf57f26fd5092bcae630" +dependencies = [ + "bytes", + "futures-core", + "percent-encoding", + "reqwest 0.13.2", + "serde", + "serde_json", + "serde_urlencoded", +] + [[package]] name = "proptest" version = "1.9.0" @@ -3953,6 +4202,7 @@ version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ + "aws-lc-rs", "bytes", "getrandom 0.3.4", "lru-slab", @@ -4125,7 +4375,7 @@ dependencies = [ "light-client", "light-hasher", "light-program-test", - "light-sdk", + "light-sdk 0.19.0", "light-token", "proptest", "quickcheck", @@ -4177,26 +4427,6 @@ dependencies = [ "bitflags 2.10.0", ] -[[package]] -name = "ref-cast" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - [[package]] name = "regex" version = "1.12.2" @@ -4293,7 +4523,6 @@ dependencies = [ "js-sys", "log", "mime", - "mime_guess", "native-tls", "percent-encoding", "pin-project-lite", @@ -4317,6 +4546,47 @@ dependencies = [ "webpki-roots 1.0.5", ] +[[package]] +name = "reqwest" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.8.1", + "hyper-rustls 0.27.7", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls 0.23.36", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.2", + "tokio", + "tokio-rustls 0.26.4", + "tokio-util 0.7.18", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + [[package]] name = "reqwest-middleware" version = "0.4.2" @@ -4398,6 +4668,7 @@ version = "0.23.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" dependencies = [ + "aws-lc-rs", "once_cell", "ring", "rustls-pki-types", @@ -4406,6 +4677,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe 0.2.1", + "rustls-pki-types", + "schannel", + "security-framework 3.5.1", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -4425,6 +4708,33 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls 0.23.36", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki 0.103.9", + "security-framework 3.5.1", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -4441,6 +4751,7 @@ version = "0.103.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -4471,36 +4782,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" [[package]] -name = "schannel" -version = "0.1.28" +name = "same-file" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "schemars" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" -dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", + "winapi-util", ] [[package]] -name = "schemars" -version = "1.2.0" +name = "schannel" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", + "windows-sys 0.61.2", ] [[package]] @@ -4526,7 +4822,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ "bitflags 2.10.0", - "core-foundation", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +dependencies = [ + "bitflags 2.10.0", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -4637,17 +4946,8 @@ version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" dependencies = [ - "base64 0.22.1", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.13.0", - "schemars 0.9.0", - "schemars 1.2.0", "serde_core", - "serde_json", "serde_with_macros", - "time", ] [[package]] @@ -6228,7 +6528,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "474a2d95dc819898ded08d24f29642d02189d3e1497bbb442a92a3997b7eb55f" dependencies = [ "byteorder", - "combine", + "combine 3.8.1", "hash32", "libc", "log", @@ -7842,7 +8142,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "system-configuration-sys 0.5.0", ] @@ -7853,7 +8153,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ "bitflags 2.10.0", - "core-foundation", + "core-foundation 0.9.4", "system-configuration-sys 0.6.0", ] @@ -8022,37 +8322,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "time" -version = "0.3.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9da98b7d9b7dad93488a84b8248efc35352b0b2657397d4167e7ad67e5d535e5" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" - -[[package]] -name = "time-macros" -version = "0.2.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc610bac2dcee56805c99642447d4c5dbde4d01f752ffea0199aee1f601dc4" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "tinystr" version = "0.8.2" @@ -8225,7 +8494,7 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.13.0", + "indexmap", "serde", "serde_spanned", "toml_datetime 0.6.11", @@ -8239,7 +8508,7 @@ version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ - "indexmap 2.13.0", + "indexmap", "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "winnow", @@ -8397,12 +8666,6 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" -[[package]] -name = "unicase" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" - [[package]] name = "unicode-ident" version = "1.0.22" @@ -8474,18 +8737,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" -[[package]] -name = "uuid" -version = "1.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f" -dependencies = [ - "getrandom 0.3.4", - "js-sys", - "serde_core", - "wasm-bindgen", -] - [[package]] name = "valuable" version = "0.1.1" @@ -8519,6 +8770,16 @@ dependencies = [ "libc", ] +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -8608,6 +8869,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "web-sys" version = "0.3.85" @@ -8628,6 +8902,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "webpki-roots" version = "0.25.4" @@ -8744,6 +9027,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -8789,6 +9081,21 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -8837,6 +9144,12 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -8855,6 +9168,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -8873,6 +9192,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -8903,6 +9228,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -8921,6 +9252,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -8939,6 +9276,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -8957,6 +9300,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" diff --git a/programs/cp-swap/tests/functional_test.rs b/programs/cp-swap/tests/functional_test.rs index ea58a20..ed17eb2 100644 --- a/programs/cp-swap/tests/functional_test.rs +++ b/programs/cp-swap/tests/functional_test.rs @@ -1,6 +1,5 @@ /// Functional integration test for cp-swap program. /// Tests pool initialization with light-program-test framework. -use light_client::interface::AccountInterfaceExt; use light_program_test::program_test::TestRpc; use light_program_test::Rpc; use solana_keypair::Keypair; @@ -455,10 +454,10 @@ async fn test_pool_light_token2022() { test_pool_with_token_types(TokenType::Light, TokenType::Token2022, 13).await; } -/// Test SDK initialization from fetched accounts and account requirements. +/// Test SDK initialization from pool data and instruction accounts. #[tokio::test] -async fn test_sdk_from_keyed_accounts() { - use light_client::interface::LightProgramInterface; +async fn test_sdk_from_pool_data() { + use light_client::interface::LightProgram; use program::{CpSwapInstruction, CpSwapSdk}; let program_id = raydium_cp_swap::ID; @@ -466,7 +465,6 @@ async fn test_sdk_from_keyed_accounts() { // Setup environment and initialize pool let mut setup = setup_pool_environment(program_id, 2).await; - // Initialize pool first (SDK requires actual account data) let proof_result = get_pool_create_accounts_proof(&setup.env.rpc, &program_id, &setup.pdas).await; let init_ix = build_initialize_instruction( @@ -492,48 +490,48 @@ async fn test_sdk_from_keyed_accounts() { let pool_interface = setup .env .rpc - .get_account_interface(&setup.pdas.pool_state, &program_id) + .get_account_interface(&setup.pdas.pool_state, None) .await - .expect("get_account_interface should succeed"); + .expect("get_account_interface should succeed") + .value + .expect("pool_state should exist"); - // Create SDK from fetched account - let sdk = CpSwapSdk::from_keyed_accounts(&[pool_interface]) - .expect("from_keyed_accounts should succeed"); + // Create SDK from pool data + let sdk = CpSwapSdk::from_pool_data(setup.pdas.pool_state, pool_interface.data()) + .expect("from_pool_data should succeed"); // Verify SDK parsed addresses match expected - assert_eq!(sdk.pool_state_pubkey, Some(setup.pdas.pool_state)); - assert_eq!(sdk.observation_key, Some(setup.pdas.observation_state)); - assert_eq!(sdk.token_0_vault, Some(setup.pdas.token_0_vault)); - assert_eq!(sdk.token_1_vault, Some(setup.pdas.token_1_vault)); - assert_eq!(sdk.lp_mint, Some(setup.pdas.lp_mint)); - assert_eq!(sdk.amm_config, Some(setup.amm_config)); - assert_eq!(sdk.token_0_mint, Some(setup.tokens.token_0_mint)); - assert_eq!(sdk.token_1_mint, Some(setup.tokens.token_1_mint)); - - // Check account requirements for each instruction type - let swap_accounts = sdk.get_accounts_for_instruction(CpSwapInstruction::Swap); + assert_eq!(sdk.pool_state_pubkey, setup.pdas.pool_state); + assert_eq!(sdk.observation_key, setup.pdas.observation_state); + assert_eq!(sdk.token_0_vault, setup.pdas.token_0_vault); + assert_eq!(sdk.token_1_vault, setup.pdas.token_1_vault); + assert_eq!(sdk.lp_mint, setup.pdas.lp_mint); + assert_eq!(sdk.amm_config, setup.amm_config); + assert_eq!(sdk.token_0_mint, setup.tokens.token_0_mint); + assert_eq!(sdk.token_1_mint, setup.tokens.token_1_mint); + + // Check instruction_accounts for each instruction type + let swap_accounts = sdk.instruction_accounts(&CpSwapInstruction::Swap); assert_eq!( swap_accounts.len(), 6, "Swap needs 6 accounts: pool, observation, vault0, vault1, mint0, mint1" ); - let deposit_accounts = sdk.get_accounts_for_instruction(CpSwapInstruction::Deposit); + let deposit_accounts = sdk.instruction_accounts(&CpSwapInstruction::Deposit); assert_eq!( deposit_accounts.len(), 7, - "Deposit needs 7 accounts: pool, observation, vault0, vault1, lp_mint, mint0, mint1" + "Deposit needs 7 accounts: pool, observation, vault0, vault1, mint0, mint1, lp_mint" ); - let withdraw_accounts = sdk.get_accounts_for_instruction(CpSwapInstruction::Withdraw); + let withdraw_accounts = sdk.instruction_accounts(&CpSwapInstruction::Withdraw); assert_eq!( withdraw_accounts.len(), 7, - "Withdraw needs 7 accounts: pool, observation, vault0, vault1, lp_mint, mint0, mint1" + "Withdraw needs 7 accounts: pool, observation, vault0, vault1, mint0, mint1, lp_mint" ); - // Verify program_id method - assert_eq!(sdk.program_id(), program_id); - - println!("SDK initialization test completed successfully!"); + // Verify program_id + assert_eq!(CpSwapSdk::program_id(), program_id); } diff --git a/programs/cp-swap/tests/program.rs b/programs/cp-swap/tests/program.rs index 9240405..2f1e47f 100644 --- a/programs/cp-swap/tests/program.rs +++ b/programs/cp-swap/tests/program.rs @@ -1,30 +1,35 @@ #![allow(dead_code)] -/// CpSwap SDK implementing LightProgramInterface trait. +/// CpSwap SDK implementing LightProgram and Jupiter Amm traits. /// /// Provides: -/// - Parsing pool accounts from AccountInterface -/// - Tracking account state (hot/cold) -/// - Building AccountSpec for load instructions +/// - Flat struct populated from pool state at construction +/// - LightProgram: instruction_accounts + load_specs for cold account handling +/// - Jupiter AMM: quotes and swap instruction building use anchor_lang::AnchorDeserialize; +use jupiter_amm_interface::{ + AccountMap, Amm, AmmContext, KeyedAccount, Quote, QuoteParams, Swap, SwapAndAccountMetas, + SwapMode, SwapParams, +}; use light_client::interface::{ - AccountInterface, AccountSpec, AccountToFetch, ColdAccountSpec, ColdContext, - LightProgramInterface, PdaSpec, TokenAccountInterface, + AccountInterface, AccountSpec, ColdContext, LightProgram, PdaSpec, }; -use light_sdk::LightDiscriminator; use light_token::compat::{CTokenData, TokenData}; +use raydium_cp_swap::curve::calculator::CurveCalculator; +use raydium_cp_swap::curve::fees::FEE_RATE_DENOMINATOR_VALUE; use raydium_cp_swap::instructions::initialize::LP_MINT_SIGNER_SEED; +use raydium_cp_swap::states::config::AmmConfig; use raydium_cp_swap::{ raydium_cp_swap::{LightAccountVariant, TokenAccountVariant}, - states::{ObservationState, PoolState}, + states::{ObservationState, PoolState, PoolStatusBitIndex}, AUTH_SEED, }; +use rust_decimal::Decimal; +use solana_instruction::AccountMeta; use solana_pubkey::Pubkey; -use std::collections::HashMap; pub const PROGRAM_ID: Pubkey = raydium_cp_swap::ID; -/// Instructions supported by the cp-swap program. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CpSwapInstruction { Swap, @@ -32,693 +37,472 @@ pub enum CpSwapInstruction { Withdraw, } -/// Error type for SDK operations. #[derive(Debug, Clone)] pub enum CpSwapSdkError { ParseError(String), - UnknownDiscriminator([u8; 8]), - MissingField(&'static str), - PoolStateNotParsed, - AccountNotFound(Pubkey), } impl std::fmt::Display for CpSwapSdkError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::ParseError(msg) => write!(f, "Parse error: {}", msg), - Self::UnknownDiscriminator(disc) => write!(f, "Unknown discriminator: {:?}", disc), - Self::MissingField(field) => write!(f, "Missing field: {}", field), - Self::PoolStateNotParsed => write!(f, "Pool state must be parsed first"), - Self::AccountNotFound(key) => write!(f, "Account not found: {}", key), } } } impl std::error::Error for CpSwapSdkError {} -/// SDK for managing cp-swap pool accounts and building decompression instructions. +/// Flat SDK struct. All pubkey fields populated at construction from pool state. +/// No Options, no HashMaps. Variants built on the fly in `load_specs`. #[derive(Debug, Clone)] pub struct CpSwapSdk { - /// Pool state pubkey - pub pool_state_pubkey: Option, - /// AMM config pubkey - pub amm_config: Option, - /// Token 0 mint pubkey - pub token_0_mint: Option, - /// Token 1 mint pubkey - pub token_1_mint: Option, - /// Token 0 vault pubkey - pub token_0_vault: Option, - /// Token 1 vault pubkey - pub token_1_vault: Option, - /// LP mint pubkey - pub lp_mint: Option, - /// LP mint signer pubkey - pub lp_mint_signer: Option, - /// Observation state pubkey - pub observation_key: Option, - /// Authority pubkey - pub authority: Option, - /// Cached PDA specs keyed by pubkey (includes pool_state, observation, and vaults) - pda_specs: HashMap>, - /// Cached mint interfaces keyed by pubkey - mint_specs: HashMap, -} - -impl Default for CpSwapSdk { - fn default() -> Self { - Self::new() - } + pub pool_state_pubkey: Pubkey, + pub amm_config: Pubkey, + pub token_0_mint: Pubkey, + pub token_1_mint: Pubkey, + pub token_0_vault: Pubkey, + pub token_1_vault: Pubkey, + pub lp_mint: Pubkey, + pub lp_mint_signer: Pubkey, + pub observation_key: Pubkey, + pub authority: Pubkey, + pub token_0_program: Pubkey, + pub token_1_program: Pubkey, + // Jupiter AMM mutable state (populated via Amm::update) + pub token_0_amount: u64, + pub token_1_amount: u64, + pub protocol_fees_token_0: u64, + pub protocol_fees_token_1: u64, + pub fund_fees_token_0: u64, + pub fund_fees_token_1: u64, + pub trade_fee_rate: u64, + pub protocol_fee_rate: u64, + pub fund_fee_rate: u64, + pub pool_status: u8, } impl CpSwapSdk { - /// Create a new empty SDK instance. - pub fn new() -> Self { - Self { - pool_state_pubkey: None, - amm_config: None, - token_0_mint: None, - token_1_mint: None, - token_0_vault: None, - token_1_vault: None, - lp_mint: None, - lp_mint_signer: None, - observation_key: None, - authority: None, - pda_specs: HashMap::new(), - mint_specs: HashMap::new(), - } - } - - /// Parse pool state from AccountInterface and populate SDK fields. - fn parse_pool_state(&mut self, interface: AccountInterface) -> Result<(), CpSwapSdkError> { - let data = interface.data(); - if data.len() < 8 { - return Err(CpSwapSdkError::ParseError( - "Account data too short".to_string(), - )); - } - - // Skip 8-byte discriminator - let pool_state = PoolState::deserialize(&mut &data[8..]) + /// Construct from pool state pubkey and its account data. + pub fn from_pool_data( + pool_state_pubkey: Pubkey, + pool_data: &[u8], + ) -> Result { + let pool = PoolState::deserialize(&mut &pool_data[8..]) .map_err(|e| CpSwapSdkError::ParseError(e.to_string()))?; - let pool_pubkey = interface.key; - self.pool_state_pubkey = Some(pool_pubkey); - self.amm_config = Some(pool_state.amm_config); - self.token_0_mint = Some(pool_state.token_0_mint); - self.token_1_mint = Some(pool_state.token_1_mint); - self.token_0_vault = Some(pool_state.token_0_vault); - self.token_1_vault = Some(pool_state.token_1_vault); - self.lp_mint = Some(pool_state.lp_mint); - self.observation_key = Some(pool_state.observation_key); - - // Derive lp_mint_signer and authority PDAs - let (lp_mint_signer, _) = - Pubkey::find_program_address(&[LP_MINT_SIGNER_SEED, pool_pubkey.as_ref()], &PROGRAM_ID); - self.lp_mint_signer = Some(lp_mint_signer); - let (authority, _) = Pubkey::find_program_address(&[AUTH_SEED.as_bytes()], &PROGRAM_ID); - self.authority = Some(authority); - - // Create PdaSpec with variant - let variant = LightAccountVariant::PoolState { - data: pool_state.clone(), - amm_config: pool_state.amm_config, - token_0_mint: pool_state.token_0_mint, - token_1_mint: pool_state.token_1_mint, - }; - let spec = PdaSpec::new(interface, variant, PROGRAM_ID); - self.pda_specs.insert(pool_pubkey, spec); - - Ok(()) - } - - /// Parse observation state from AccountInterface. - fn parse_observation_state( - &mut self, - interface: AccountInterface, - ) -> Result<(), CpSwapSdkError> { - let pool_state = self - .pool_state_pubkey - .ok_or(CpSwapSdkError::PoolStateNotParsed)?; - - let data = interface.data(); - if data.len() < 8 { - return Err(CpSwapSdkError::ParseError( - "Account data too short".to_string(), - )); - } - - let obs_pubkey = interface.key; - let obs_state = ObservationState::deserialize(&mut &data[8..]) - .map_err(|e| CpSwapSdkError::ParseError(e.to_string()))?; - - let variant = LightAccountVariant::ObservationState { - data: obs_state, - pool_state, - }; - let spec = PdaSpec::new(interface, variant, PROGRAM_ID); - self.pda_specs.insert(obs_pubkey, spec); - - Ok(()) + let (lp_mint_signer, _) = Pubkey::find_program_address( + &[LP_MINT_SIGNER_SEED, pool_state_pubkey.as_ref()], + &PROGRAM_ID, + ); + + Ok(Self { + pool_state_pubkey, + amm_config: pool.amm_config, + token_0_mint: pool.token_0_mint, + token_1_mint: pool.token_1_mint, + token_0_vault: pool.token_0_vault, + token_1_vault: pool.token_1_vault, + lp_mint: pool.lp_mint, + lp_mint_signer, + observation_key: pool.observation_key, + authority, + token_0_program: pool.token_0_program, + token_1_program: pool.token_1_program, + token_0_amount: 0, + token_1_amount: 0, + protocol_fees_token_0: pool.protocol_fees_token_0, + protocol_fees_token_1: pool.protocol_fees_token_1, + fund_fees_token_0: pool.fund_fees_token_0, + fund_fees_token_1: pool.fund_fees_token_1, + trade_fee_rate: 0, + protocol_fee_rate: 0, + fund_fee_rate: 0, + pool_status: pool.status, + }) } - /// Store token vault interface. - /// Vaults are program-owned PDAs, so we convert them to PdaSpec with CTokenData variant. - pub fn set_token_vault(&mut self, interface: TokenAccountInterface, is_vault_0: bool) { - let key = interface.key; - let pool_state = self - .pool_state_pubkey - .expect("pool_state must be set before vaults"); - let mint = if is_vault_0 { - self.token_0_mint.expect("token_0_mint must be set") - } else { - self.token_1_mint.expect("token_1_mint must be set") - }; - - // Build TokenData from TokenAccountInterface - let token_data = TokenData { - mint: interface.mint(), - owner: interface.owner(), - amount: interface.amount(), - delegate: if interface.parsed.delegate.option == [1, 0, 0, 0] { - Some(Pubkey::from(interface.parsed.delegate.value)) - } else { - None - }, - state: light_token::compat::AccountState::Initialized, - tlv: None, - }; - - // Build variant based on which vault this is - let variant = if is_vault_0 { - LightAccountVariant::CTokenData(CTokenData { - variant: TokenAccountVariant::Token0Vault { - pool_state, - token_0_mint: mint, - }, - token_data, - }) - } else { - LightAccountVariant::CTokenData(CTokenData { - variant: TokenAccountVariant::Token1Vault { - pool_state, - token_1_mint: mint, - }, - token_data, - }) - }; - - // Convert TokenAccountInterface to AccountInterface for PdaSpec - // For cold vaults, we need to convert ColdContext::Token to ColdContext::Account - let cold = if let Some(ColdContext::Token(ct)) = &interface.cold { - Some(ColdContext::Account(ct.account.clone())) - } else { - None - }; - - let account_interface = AccountInterface { - key, - account: interface.account.clone(), - cold, - }; - - let spec = PdaSpec::new(account_interface, variant, PROGRAM_ID); - - self.pda_specs.insert(key, spec); - if is_vault_0 { - self.token_0_vault = Some(key); - } else { - self.token_1_vault = Some(key); - } - } - - /// Store LP mint interface. - pub fn set_lp_mint(&mut self, interface: AccountInterface) { - let key = interface.key; - self.lp_mint = Some(key); - self.mint_specs.insert(key, interface); - } - - /// Parse token vault from AccountInterface and store as PdaSpec. - fn parse_token_vault( - &mut self, + /// Convert token vault ColdContext::Token -> ColdContext::Account. + fn convert_vault_interface( account: &AccountInterface, - is_vault_0: bool, - ) -> Result<(), CpSwapSdkError> { - let pool_state = self - .pool_state_pubkey - .ok_or(CpSwapSdkError::PoolStateNotParsed)?; - - // Deserialize token data properly - let token_data = TokenData::deserialize(&mut &account.data()[..]) - .map_err(|e| CpSwapSdkError::ParseError(e.to_string()))?; - - // Build variant based on which vault this is - let variant = if is_vault_0 { - let token_0_mint = self - .token_0_mint - .ok_or(CpSwapSdkError::MissingField("token_0_mint"))?; - LightAccountVariant::CTokenData(CTokenData { - variant: TokenAccountVariant::Token0Vault { - pool_state, - token_0_mint, - }, - token_data, - }) - } else { - let token_1_mint = self - .token_1_mint - .ok_or(CpSwapSdkError::MissingField("token_1_mint"))?; - LightAccountVariant::CTokenData(CTokenData { - variant: TokenAccountVariant::Token1Vault { - pool_state, - token_1_mint, - }, - token_data, - }) - }; - - // For token vaults, convert ColdContext::Token to ColdContext::Account - // because they're decompressed as PDAs, not as token accounts - let interface = if account.is_cold() { + ) -> Result { + if account.is_cold() { let compressed_account = match &account.cold { Some(ColdContext::Token(ct)) => ct.account.clone(), Some(ColdContext::Account(ca)) => ca.clone(), - None => return Err(CpSwapSdkError::MissingField("cold_context")), + _ => { + return Err(CpSwapSdkError::ParseError( + "unexpected cold context for vault".to_string(), + )) + } }; - AccountInterface { + Ok(AccountInterface { key: account.key, account: account.account.clone(), cold: Some(ColdContext::Account(compressed_account)), - } + }) } else { - account.clone() - }; - - let spec = PdaSpec::new(interface, variant, PROGRAM_ID); - self.pda_specs.insert(account.key, spec); - - Ok(()) - } - - /// Parse LP mint from AccountInterface. - fn parse_mint(&mut self, account: &AccountInterface) -> Result<(), CpSwapSdkError> { - self.mint_specs.insert(account.key, account.clone()); - Ok(()) - } - - /// Parse any account and route to appropriate parser. - fn parse_account(&mut self, account: &AccountInterface) -> Result<(), CpSwapSdkError> { - // Check if this is a known vault by pubkey - if Some(account.key) == self.token_0_vault { - return self.parse_token_vault(account, true); + Ok(account.clone()) } - if Some(account.key) == self.token_1_vault { - return self.parse_token_vault(account, false); - } - - // Check discriminator for pool/observation state - let data = account.data(); - if data.len() >= 8 { - let discriminator: [u8; 8] = data[..8].try_into().unwrap_or_default(); - - if discriminator == PoolState::LIGHT_DISCRIMINATOR { - return self.parse_pool_state(account.clone()); - } - if discriminator == ObservationState::LIGHT_DISCRIMINATOR { - return self.parse_observation_state(account.clone()); - } - } - - // Check if this is an LP mint by matching the signer - if let Some(lp_mint_signer) = self.lp_mint_signer { - if let Some(mint_signer) = account.mint_signer() { - if Pubkey::new_from_array(mint_signer) == lp_mint_signer { - return self.parse_mint(account); - } - } - } - - // Check if this is a vault mint (token_0_mint or token_1_mint) - if Some(account.key) == self.token_0_mint || Some(account.key) == self.token_1_mint { - return self.parse_mint(account); - } - - Ok(()) } - /// Check if pool state is cold. - pub fn is_pool_state_cold(&self) -> bool { - self.pool_state_pubkey - .and_then(|k| self.pda_specs.get(&k)) - .map_or(false, |s| s.is_cold()) + // Jupiter AMM helpers + + fn vault_amounts_without_fees(&self) -> (u64, u64) { + let token_0 = self + .token_0_amount + .saturating_sub(self.protocol_fees_token_0) + .saturating_sub(self.fund_fees_token_0); + let token_1 = self + .token_1_amount + .saturating_sub(self.protocol_fees_token_1) + .saturating_sub(self.fund_fees_token_1); + (token_0, token_1) } - /// Check if observation state is cold. - pub fn is_observation_cold(&self) -> bool { - self.observation_key - .and_then(|k| self.pda_specs.get(&k)) - .map_or(false, |s| s.is_cold()) + fn is_swap_enabled(&self) -> bool { + (self.pool_status & (1 << (PoolStatusBitIndex::Swap as u8))) == 0 } - /// Check if token 0 vault is cold. - pub fn is_vault_0_cold(&self) -> bool { - self.token_0_vault - .and_then(|k| self.pda_specs.get(&k)) - .map_or(false, |s| s.is_cold()) - } + fn calculate_quote( + &self, + input_mint: Pubkey, + output_mint: Pubkey, + amount: u64, + swap_mode: SwapMode, + ) -> Result { + let (vault_0, vault_1) = self.vault_amounts_without_fees(); + + let (source_amount, dest_amount, fee_mint) = + if input_mint == self.token_0_mint && output_mint == self.token_1_mint { + (vault_0 as u128, vault_1 as u128, input_mint) + } else if input_mint == self.token_1_mint && output_mint == self.token_0_mint { + (vault_1 as u128, vault_0 as u128, input_mint) + } else { + return Err(anyhow::anyhow!("Invalid mint pair")); + }; - /// Check if token 1 vault is cold. - pub fn is_vault_1_cold(&self) -> bool { - self.token_1_vault - .and_then(|k| self.pda_specs.get(&k)) - .map_or(false, |s| s.is_cold()) - } + let result = match swap_mode { + SwapMode::ExactIn => CurveCalculator::swap_base_input( + amount as u128, + source_amount, + dest_amount, + self.trade_fee_rate, + self.protocol_fee_rate, + self.fund_fee_rate, + ), + SwapMode::ExactOut => CurveCalculator::swap_base_output( + amount as u128, + source_amount, + dest_amount, + self.trade_fee_rate, + self.protocol_fee_rate, + self.fund_fee_rate, + ), + } + .ok_or_else(|| anyhow::anyhow!("Swap calculation failed"))?; + + let (in_amount, out_amount) = match swap_mode { + SwapMode::ExactIn => (amount, result.destination_amount_swapped as u64), + SwapMode::ExactOut => (result.source_amount_swapped as u64, amount), + }; - /// Check if LP mint is cold. - pub fn is_lp_mint_cold(&self) -> bool { - self.lp_mint - .and_then(|k| self.mint_specs.get(&k)) - .map_or(false, |s| s.is_cold()) - } + let fee_pct = + Decimal::from(self.trade_fee_rate) / Decimal::from(FEE_RATE_DENOMINATOR_VALUE); - /// Get pool state pubkey. - pub fn pool_state(&self) -> Option { - self.pool_state_pubkey + Ok(Quote { + in_amount, + out_amount, + fee_amount: result.trade_fee as u64, + fee_mint, + fee_pct, + }) } } -impl LightProgramInterface for CpSwapSdk { +// ============================================================================ +// LightProgram Trait Implementation +// ============================================================================ + +impl LightProgram for CpSwapSdk { type Variant = LightAccountVariant; - type InstructionKind = CpSwapInstruction; - type Error = CpSwapSdkError; + type Instruction = CpSwapInstruction; - fn program_id(&self) -> Pubkey { + fn program_id() -> Pubkey { PROGRAM_ID } - fn from_keyed_accounts(accounts: &[AccountInterface]) -> Result { - let mut sdk = Self::new(); - - // First pass: find and parse pool state - for account in accounts { - let data = account.data(); - if data.len() >= 8 { - let discriminator: [u8; 8] = data[..8].try_into().unwrap_or_default(); - if discriminator == PoolState::LIGHT_DISCRIMINATOR { - sdk.parse_pool_state(account.clone())?; - break; - } - } - } - - if sdk.pool_state_pubkey.is_none() { - return Err(CpSwapSdkError::MissingField("pool_state")); + fn instruction_accounts(&self, ix: &Self::Instruction) -> Vec { + match ix { + CpSwapInstruction::Swap => vec![ + self.pool_state_pubkey, + self.observation_key, + self.token_0_vault, + self.token_1_vault, + self.token_0_mint, + self.token_1_mint, + ], + CpSwapInstruction::Deposit | CpSwapInstruction::Withdraw => vec![ + self.pool_state_pubkey, + self.observation_key, + self.token_0_vault, + self.token_1_vault, + self.token_0_mint, + self.token_1_mint, + self.lp_mint, + ], } - - // Second pass: parse other accounts - for account in accounts { - let data = account.data(); - if data.len() >= 8 { - let discriminator: [u8; 8] = data[..8].try_into().unwrap_or_default(); - if discriminator == ObservationState::LIGHT_DISCRIMINATOR { - sdk.parse_observation_state(account.clone())?; - } - } - } - - Ok(sdk) } - fn get_accounts_for_instruction(&self, kind: Self::InstructionKind) -> Vec { - let mut accounts = Vec::new(); - - // All instructions need pool_state and observation_state - if let Some(pubkey) = self.pool_state_pubkey { - accounts.push(AccountToFetch::pda(pubkey, PROGRAM_ID)); - } - if let Some(pubkey) = self.observation_key { - accounts.push(AccountToFetch::pda(pubkey, PROGRAM_ID)); - } - - // All instructions need token vaults - if let Some(pubkey) = self.token_0_vault { - accounts.push(AccountToFetch::token(pubkey)); - } - if let Some(pubkey) = self.token_1_vault { - accounts.push(AccountToFetch::token(pubkey)); - } - - // All instructions need vault mints (token_0_mint and token_1_mint) - if let Some(pubkey) = self.token_0_mint { - accounts.push(AccountToFetch::mint(pubkey)); - } - if let Some(pubkey) = self.token_1_mint { - accounts.push(AccountToFetch::mint(pubkey)); - } - - // Deposit and Withdraw also need LP mint - match kind { - CpSwapInstruction::Deposit | CpSwapInstruction::Withdraw => { - if let Some(pubkey) = self.lp_mint { - accounts.push(AccountToFetch::mint(pubkey)); - } + fn load_specs( + &self, + cold_accounts: &[AccountInterface], + ) -> Result>, Box> { + let mut specs = Vec::new(); + for account in cold_accounts { + if account.key == self.pool_state_pubkey { + let pool = PoolState::deserialize(&mut &account.data()[8..]) + .map_err(|e| CpSwapSdkError::ParseError(e.to_string()))?; + let variant = LightAccountVariant::PoolState { + data: pool, + amm_config: self.amm_config, + token_0_mint: self.token_0_mint, + token_1_mint: self.token_1_mint, + }; + specs.push(AccountSpec::Pda(PdaSpec::new( + account.clone(), + variant, + PROGRAM_ID, + ))); + } else if account.key == self.observation_key { + let obs = ObservationState::deserialize(&mut &account.data()[8..]) + .map_err(|e| CpSwapSdkError::ParseError(e.to_string()))?; + let variant = LightAccountVariant::ObservationState { + data: obs, + pool_state: self.pool_state_pubkey, + }; + specs.push(AccountSpec::Pda(PdaSpec::new( + account.clone(), + variant, + PROGRAM_ID, + ))); + } else if account.key == self.token_0_vault { + let token_data = TokenData::deserialize(&mut &account.data()[..]) + .map_err(|e| CpSwapSdkError::ParseError(e.to_string()))?; + let variant = LightAccountVariant::CTokenData(CTokenData { + variant: TokenAccountVariant::Token0Vault { + pool_state: self.pool_state_pubkey, + token_0_mint: self.token_0_mint, + }, + token_data, + }); + let interface = Self::convert_vault_interface(account)?; + specs.push(AccountSpec::Pda(PdaSpec::new(interface, variant, PROGRAM_ID))); + } else if account.key == self.token_1_vault { + let token_data = TokenData::deserialize(&mut &account.data()[..]) + .map_err(|e| CpSwapSdkError::ParseError(e.to_string()))?; + let variant = LightAccountVariant::CTokenData(CTokenData { + variant: TokenAccountVariant::Token1Vault { + pool_state: self.pool_state_pubkey, + token_1_mint: self.token_1_mint, + }, + token_data, + }); + let interface = Self::convert_vault_interface(account)?; + specs.push(AccountSpec::Pda(PdaSpec::new(interface, variant, PROGRAM_ID))); + } else if account.key == self.token_0_mint + || account.key == self.token_1_mint + || account.key == self.lp_mint + { + specs.push(AccountSpec::Mint(account.clone())); } - CpSwapInstruction::Swap => {} } - - accounts + Ok(specs) } +} - fn update_with_interfaces(&mut self, accounts: &[AccountInterface]) -> Result<(), Self::Error> { - for account in accounts { - // Handle decompression: if account was cold but now hot, remove from specs - if account.is_hot() { - if self - .pda_specs - .get(&account.key) - .map_or(false, |s| s.is_cold()) - { - self.pda_specs.remove(&account.key); - } - if self - .mint_specs - .get(&account.key) - .map_or(false, |s| s.is_cold()) - { - self.mint_specs.remove(&account.key); - } - } - self.parse_account(account)?; - } - Ok(()) +// ============================================================================ +// Jupiter AMM Trait Implementation +// ============================================================================ + +impl Amm for CpSwapSdk { + fn from_keyed_account( + keyed_account: &KeyedAccount, + _amm_context: &AmmContext, + ) -> Result + where + Self: Sized, + { + let data = &keyed_account.account.data; + let pool = PoolState::deserialize(&mut &data[8..]) + .map_err(|e| anyhow::anyhow!("Failed to parse pool state: {}", e))?; + + let pool_pubkey = keyed_account.key; + let (authority, _) = Pubkey::find_program_address(&[AUTH_SEED.as_bytes()], &PROGRAM_ID); + let (lp_mint_signer, _) = Pubkey::find_program_address( + &[LP_MINT_SIGNER_SEED, pool_pubkey.as_ref()], + &PROGRAM_ID, + ); + + Ok(Self { + pool_state_pubkey: pool_pubkey, + amm_config: pool.amm_config, + token_0_mint: pool.token_0_mint, + token_1_mint: pool.token_1_mint, + token_0_vault: pool.token_0_vault, + token_1_vault: pool.token_1_vault, + lp_mint: pool.lp_mint, + lp_mint_signer, + observation_key: pool.observation_key, + authority, + token_0_program: pool.token_0_program, + token_1_program: pool.token_1_program, + token_0_amount: 0, + token_1_amount: 0, + protocol_fees_token_0: pool.protocol_fees_token_0, + protocol_fees_token_1: pool.protocol_fees_token_1, + fund_fees_token_0: pool.fund_fees_token_0, + fund_fees_token_1: pool.fund_fees_token_1, + trade_fee_rate: 0, + protocol_fee_rate: 0, + fund_fee_rate: 0, + pool_status: pool.status, + }) } - fn get_all_specs(&self) -> Vec> { - let mut specs = Vec::new(); + fn label(&self) -> String { + "Raydium CP Swap".to_string() + } - // Add PDA specs (includes pool_state, observation, and vaults) - for spec in self.pda_specs.values() { - specs.push(AccountSpec::Pda(spec.clone())); - } + fn program_id(&self) -> Pubkey { + PROGRAM_ID + } - // Add mint specs - for spec in self.mint_specs.values() { - specs.push(AccountSpec::Mint(spec.clone())); - } + fn key(&self) -> Pubkey { + self.pool_state_pubkey + } - specs + fn get_reserve_mints(&self) -> Vec { + vec![self.token_0_mint, self.token_1_mint] } - fn get_specs_for_instruction(&self, kind: Self::InstructionKind) -> Vec> { - let mut specs = Vec::new(); + fn get_accounts_to_update(&self) -> Vec { + vec![ + self.pool_state_pubkey, + self.token_0_vault, + self.token_1_vault, + self.amm_config, + ] + } - // Pool state and observation state needed for all instructions - if let Some(pubkey) = self.pool_state_pubkey { - if let Some(spec) = self.pda_specs.get(&pubkey) { - specs.push(AccountSpec::Pda(spec.clone())); - } - } - if let Some(pubkey) = self.observation_key { - if let Some(spec) = self.pda_specs.get(&pubkey) { - specs.push(AccountSpec::Pda(spec.clone())); + fn update(&mut self, account_map: &AccountMap) -> Result<(), anyhow::Error> { + if let Some(account) = account_map.get(&self.pool_state_pubkey) { + if account.data.len() >= 8 { + let pool = PoolState::deserialize(&mut &account.data[8..]) + .map_err(|e| anyhow::anyhow!("Failed to parse pool state: {}", e))?; + self.protocol_fees_token_0 = pool.protocol_fees_token_0; + self.protocol_fees_token_1 = pool.protocol_fees_token_1; + self.fund_fees_token_0 = pool.fund_fees_token_0; + self.fund_fees_token_1 = pool.fund_fees_token_1; + self.pool_status = pool.status; } } - // Token vaults needed for all instructions (stored as PDA specs with CTokenData variant) - if let Some(pubkey) = self.token_0_vault { - if let Some(spec) = self.pda_specs.get(&pubkey) { - specs.push(AccountSpec::Pda(spec.clone())); - } - } - if let Some(pubkey) = self.token_1_vault { - if let Some(spec) = self.pda_specs.get(&pubkey) { - specs.push(AccountSpec::Pda(spec.clone())); + if let Some(account) = account_map.get(&self.token_0_vault) { + if account.data.len() >= 72 { + self.token_0_amount = + u64::from_le_bytes(account.data[64..72].try_into().unwrap_or_default()); } } - // Vault mints (token_0_mint and token_1_mint) needed for all instructions - if let Some(pubkey) = self.token_0_mint { - if let Some(spec) = self.mint_specs.get(&pubkey) { - specs.push(AccountSpec::Mint(spec.clone())); - } - } - if let Some(pubkey) = self.token_1_mint { - if let Some(spec) = self.mint_specs.get(&pubkey) { - specs.push(AccountSpec::Mint(spec.clone())); + if let Some(account) = account_map.get(&self.token_1_vault) { + if account.data.len() >= 72 { + self.token_1_amount = + u64::from_le_bytes(account.data[64..72].try_into().unwrap_or_default()); } } - // LP mint needed for deposit/withdraw - match kind { - CpSwapInstruction::Deposit | CpSwapInstruction::Withdraw => { - if let Some(pubkey) = self.lp_mint { - if let Some(spec) = self.mint_specs.get(&pubkey) { - specs.push(AccountSpec::Mint(spec.clone())); - } - } + if let Some(account) = account_map.get(&self.amm_config) { + if account.data.len() >= 8 { + let config = AmmConfig::deserialize(&mut &account.data[8..]) + .map_err(|e| anyhow::anyhow!("Failed to parse amm config: {}", e))?; + self.trade_fee_rate = config.trade_fee_rate; + self.protocol_fee_rate = config.protocol_fee_rate; + self.fund_fee_rate = config.fund_fee_rate; } - CpSwapInstruction::Swap => {} } - specs - } - - fn get_compressible_accounts(&self) -> Vec { - let mut accounts = Vec::new(); - if let Some(pubkey) = self.pool_state_pubkey { - accounts.push(pubkey); - } - if let Some(pubkey) = self.observation_key { - accounts.push(pubkey); - } - if let Some(pubkey) = self.token_0_vault { - accounts.push(pubkey); - } - if let Some(pubkey) = self.token_1_vault { - accounts.push(pubkey); - } - if let Some(pubkey) = self.lp_mint { - accounts.push(pubkey); - } - accounts + Ok(()) } - fn get_compressible_accounts_for_instruction(&self, kind: Self::InstructionKind) -> Vec { - let mut accounts = Vec::new(); - if let Some(pubkey) = self.pool_state_pubkey { - accounts.push(pubkey); - } - if let Some(pubkey) = self.observation_key { - accounts.push(pubkey); - } - if let Some(pubkey) = self.token_0_vault { - accounts.push(pubkey); + fn quote(&self, quote_params: &QuoteParams) -> Result { + if !self.is_swap_enabled() { + return Err(anyhow::anyhow!("Swap is disabled for this pool")); } - if let Some(pubkey) = self.token_1_vault { - accounts.push(pubkey); - } - match kind { - CpSwapInstruction::Deposit | CpSwapInstruction::Withdraw => { - if let Some(pubkey) = self.lp_mint { - accounts.push(pubkey); - } - } - CpSwapInstruction::Swap => {} - } - accounts + self.calculate_quote( + quote_params.input_mint, + quote_params.output_mint, + quote_params.amount, + quote_params.swap_mode, + ) } - fn get_cold_specs_for_instruction( + fn get_swap_and_account_metas( &self, - kind: Self::InstructionKind, - ) -> Vec> { - let mut cold_specs = Vec::new(); - - // Check pool_state - if let Some(pubkey) = self.pool_state_pubkey { - if let Some(spec) = self.pda_specs.get(&pubkey) { - if spec.is_cold() { - if let Some(compressed) = spec.interface.as_compressed_account() { - cold_specs.push(ColdAccountSpec::Pda { - key: pubkey, - compressed: compressed.clone(), - variant: spec.variant.clone(), - program_id: PROGRAM_ID, - }); - } - } - } - } - - // Check observation_state - if let Some(pubkey) = self.observation_key { - if let Some(spec) = self.pda_specs.get(&pubkey) { - if spec.is_cold() { - if let Some(compressed) = spec.interface.as_compressed_account() { - cold_specs.push(ColdAccountSpec::Pda { - key: pubkey, - compressed: compressed.clone(), - variant: spec.variant.clone(), - program_id: PROGRAM_ID, - }); - } - } - } - } - - // Check vaults (stored as PDAs) - for vault_pubkey in [self.token_0_vault, self.token_1_vault].into_iter().flatten() { - if let Some(spec) = self.pda_specs.get(&vault_pubkey) { - if spec.is_cold() { - if let Some(compressed) = spec.interface.as_compressed_account() { - cold_specs.push(ColdAccountSpec::Pda { - key: vault_pubkey, - compressed: compressed.clone(), - variant: spec.variant.clone(), - program_id: PROGRAM_ID, - }); - } - } - } - } + swap_params: &SwapParams<'_, '_>, + ) -> Result { + let (input_vault, output_vault, input_mint, output_mint, input_program, output_program) = + if swap_params.source_mint == self.token_0_mint { + ( + self.token_0_vault, + self.token_1_vault, + self.token_0_mint, + self.token_1_mint, + self.token_0_program, + self.token_1_program, + ) + } else { + ( + self.token_1_vault, + self.token_0_vault, + self.token_1_mint, + self.token_0_mint, + self.token_1_program, + self.token_0_program, + ) + }; - // Check mints - for mint_pubkey in [self.token_0_mint, self.token_1_mint].into_iter().flatten() { - if let Some(iface) = self.mint_specs.get(&mint_pubkey) { - if iface.is_cold() { - if let Some(compressed) = iface.as_compressed_account() { - cold_specs.push(ColdAccountSpec::Mint { - key: mint_pubkey, - compressed: compressed.clone(), - }); - } - } - } - } + let account_metas = vec![ + AccountMeta::new_readonly(swap_params.token_transfer_authority, true), + AccountMeta::new_readonly(self.authority, false), + AccountMeta::new_readonly(self.amm_config, false), + AccountMeta::new(self.pool_state_pubkey, false), + AccountMeta::new(swap_params.source_token_account, false), + AccountMeta::new(swap_params.destination_token_account, false), + AccountMeta::new(input_vault, false), + AccountMeta::new(output_vault, false), + AccountMeta::new_readonly(input_program, false), + AccountMeta::new_readonly(output_program, false), + AccountMeta::new_readonly(input_mint, false), + AccountMeta::new_readonly(output_mint, false), + AccountMeta::new(self.observation_key, false), + ]; + + Ok(SwapAndAccountMetas { + swap: Swap::RaydiumCP, + account_metas, + }) + } - // LP mint only for deposit/withdraw - match kind { - CpSwapInstruction::Deposit | CpSwapInstruction::Withdraw => { - if let Some(lp_mint) = self.lp_mint { - if let Some(iface) = self.mint_specs.get(&lp_mint) { - if iface.is_cold() { - if let Some(compressed) = iface.as_compressed_account() { - cold_specs.push(ColdAccountSpec::Mint { - key: lp_mint, - compressed: compressed.clone(), - }); - } - } - } - } - } - CpSwapInstruction::Swap => {} - } + fn clone_amm(&self) -> Box { + Box::new(self.clone()) + } - cold_specs + fn supports_exact_out(&self) -> bool { + true } + fn is_active(&self) -> bool { + self.is_swap_enabled() + } } diff --git a/programs/cp-swap/tests/program_test.rs b/programs/cp-swap/tests/program_test.rs index 13818f4..d80f38e 100644 --- a/programs/cp-swap/tests/program_test.rs +++ b/programs/cp-swap/tests/program_test.rs @@ -1,8 +1,6 @@ /// Clean integration test for cp-swap using CpSwapSdk. /// Tests the full lifecycle: Initialize -> Warp -> Compress -> Load -> Execute Operations -use light_client::interface::{ - create_load_instructions, AccountInterfaceExt, AccountSpec, LightProgramInterface, -}; +use light_client::interface::{create_load_instructions, AccountSpec, LightProgram}; use light_program_test::program_test::TestRpc; use light_program_test::Rpc; use solana_instruction::Instruction; @@ -68,55 +66,84 @@ async fn test_sdk_lifecycle() { let pool_interface = setup .env .rpc - .get_account_interface(&setup.pdas.pool_state, &program_id) + .get_account_interface(&setup.pdas.pool_state, None) .await + .expect("failed to get pool_state") + .value .expect("pool should be compressed"); assert!( pool_interface.is_cold(), "pool_state should be cold after warp" ); - let mut sdk = CpSwapSdk::from_keyed_accounts(&[pool_interface]) - .expect("from_keyed_accounts should succeed"); + let sdk = CpSwapSdk::from_pool_data(setup.pdas.pool_state, pool_interface.data()) + .expect("from_pool_data should succeed"); - // ==================== PHASE 6: Fetch & Update SDK ==================== - let accounts_to_fetch = sdk.get_accounts_for_instruction(CpSwapInstruction::Deposit); - let keyed_accounts = setup + // ==================== PHASE 6: Fetch Cold Accounts ==================== + let pubkeys = sdk.instruction_accounts(&CpSwapInstruction::Deposit); + let account_interfaces = setup .env .rpc - .get_multiple_account_interfaces(&accounts_to_fetch) + .fetch_accounts(&pubkeys, None) .await - .expect("get_multiple_account_interfaces should succeed"); - - sdk.update_with_interfaces(&keyed_accounts) - .expect("sdk.update should succeed"); + .expect("fetch_accounts should succeed"); + let cold_accounts: Vec<_> = account_interfaces + .into_iter() + .filter(|a| a.is_cold()) + .collect(); // ==================== PHASE 7: Build Specs for Load ==================== - let mut all_specs = sdk.get_specs_for_instruction(CpSwapInstruction::Deposit); + let mut all_specs = sdk + .load_specs(&cold_accounts) + .expect("load_specs should succeed"); // Fetch creator's ATAs (compressed) and add to specs let creator_lp_ata_interface = setup .env .rpc - .get_ata_interface(&setup.creator.pubkey(), &setup.pdas.lp_mint) + .get_associated_token_account_interface( + &setup.creator.pubkey(), + &setup.pdas.lp_mint, + None, + ) .await - .expect("get_ata_interface for creator_lp_token should succeed"); + .expect("get ata interface for creator_lp_token should succeed") + .value + .expect("creator_lp_token should exist") + .try_into() + .expect("should convert to TokenAccountInterface"); all_specs.push(AccountSpec::Ata(creator_lp_ata_interface)); let creator_token_0_interface = setup .env .rpc - .get_ata_interface(&setup.creator.pubkey(), &setup.tokens.token_0_mint) + .get_associated_token_account_interface( + &setup.creator.pubkey(), + &setup.tokens.token_0_mint, + None, + ) .await - .expect("get_ata_interface for creator_token_0 should succeed"); + .expect("get ata interface for creator_token_0 should succeed") + .value + .expect("creator_token_0 should exist") + .try_into() + .expect("should convert to TokenAccountInterface"); all_specs.push(AccountSpec::Ata(creator_token_0_interface)); let creator_token_1_interface = setup .env .rpc - .get_ata_interface(&setup.creator.pubkey(), &setup.tokens.token_1_mint) + .get_associated_token_account_interface( + &setup.creator.pubkey(), + &setup.tokens.token_1_mint, + None, + ) .await - .expect("get_ata_interface for creator_token_1 should succeed"); + .expect("get ata interface for creator_token_1 should succeed") + .value + .expect("creator_token_1 should exist") + .try_into() + .expect("should convert to TokenAccountInterface"); all_specs.push(AccountSpec::Ata(creator_token_1_interface)); // ==================== PHASE 8: Create Load Instructions ==================== @@ -124,7 +151,6 @@ async fn test_sdk_lifecycle() { &all_specs, setup.env.payer.pubkey(), setup.env.config_pda, - setup.env.payer.pubkey(), &setup.env.rpc, ) .await @@ -162,7 +188,6 @@ async fn test_sdk_lifecycle() { ); log_transaction_size("Deposit transaction", &[deposit_ix.clone()]); - // Log combined Load + Deposit let mut load_plus_deposit = all_load_ixs.clone(); load_plus_deposit.push(deposit_ix.clone()); log_transaction_size("Load + Deposit transaction", &load_plus_deposit); @@ -192,7 +217,6 @@ async fn test_sdk_lifecycle() { ); log_transaction_size("Swap transaction", &[swap_ix.clone()]); - // Log combined Load + Swap let mut load_plus_swap = all_load_ixs.clone(); load_plus_swap.push(swap_ix.clone()); log_transaction_size("Load + Swap transaction", &load_plus_swap); From 95a28d2572cbd15ede36790a60ec353abb166f8f Mon Sep 17 00:00:00 2001 From: Swenschaeferjohann Date: Mon, 9 Feb 2026 22:02:28 +0000 Subject: [PATCH 4/4] rm fetch_accounts --- programs/cp-swap/tests/program_test.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/programs/cp-swap/tests/program_test.rs b/programs/cp-swap/tests/program_test.rs index d80f38e..4255c77 100644 --- a/programs/cp-swap/tests/program_test.rs +++ b/programs/cp-swap/tests/program_test.rs @@ -84,11 +84,13 @@ async fn test_sdk_lifecycle() { let account_interfaces = setup .env .rpc - .fetch_accounts(&pubkeys, None) + .get_multiple_account_interfaces(pubkeys.iter().collect(), None) .await - .expect("fetch_accounts should succeed"); + .expect("get_multiple_account_interfaces should succeed"); let cold_accounts: Vec<_> = account_interfaces + .value .into_iter() + .flatten() .filter(|a| a.is_cold()) .collect();