diff --git a/Cargo.lock b/Cargo.lock index 4e5d1a1..b408576 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,9 +40,9 @@ dependencies = [ [[package]] name = "agave-feature-set" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd05b6a1f0867ccce373385f007b9683a116228bad9c3a0965316209617788a8" +checksum = "4a36f13a213d45f45f8ff87ea9fc6b0a792a7997c76b7c5d6d4a2ebe741d19d0" dependencies = [ "ahash", "solana-epoch-schedule", @@ -54,9 +54,9 @@ dependencies = [ [[package]] name = "agave-reserved-account-keys" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c1bcebf73c3adb9fe92474a03e6192ee6c5041a6be6eeeb28166fc7e678809d" +checksum = "6e4dbcc14290a572b94f8793561f74f87e8dd1efb6c28c883d79a84242ee9633" dependencies = [ "agave-feature-set", "solana-pubkey 3.0.0", @@ -65,9 +65,9 @@ dependencies = [ [[package]] name = "agave-syscalls" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ae5f415f57e60b9dc1c17a314d595b32308f2470c2969beacaec9d0461d089d" +checksum = "108ce050e29fa68a49928213a74f12fc314f8f6964cceeec908be700f1ae5a80" dependencies = [ "bincode", "libsecp256k1", @@ -195,7 +195,7 @@ dependencies = [ "ark-std 0.5.0", "educe", "fnv", - "hashbrown 0.15.5", + "hashbrown 0.15.2", "itertools 0.13.0", "num-bigint 0.4.6", "num-integer", @@ -314,7 +314,7 @@ dependencies = [ "ark-std 0.5.0", "educe", "fnv", - "hashbrown 0.15.5", + "hashbrown 0.15.2", ] [[package]] @@ -887,8 +887,17 @@ dependencies = [ "pinocchio-pubkey", "pinocchio-system", "pinocchio-token", + "solana-instruction", + "solana-keypair", + "solana-message", + "solana-native-token", + "solana-pubkey 4.1.0", "solana-sdk", + "solana-sdk-ids", + "solana-signer", + "solana-transaction", "spl-associated-token-account", + "spl-token", "wincode", ] @@ -1089,9 +1098,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.5" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ "allocator-api2", ] @@ -2285,9 +2294,9 @@ dependencies = [ [[package]] name = "solana-bpf-loader-program" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cc6de8801030c776e7f2875621f19f806330bb8c0e273a97d5c1112663c0d63" +checksum = "36128e7525889b05f13a4dbfb86f1ff3aef2063ab63b899591d348226e20def8" dependencies = [ "agave-syscalls", "bincode", @@ -2314,9 +2323,9 @@ dependencies = [ [[package]] name = "solana-builtins" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f76574e46c312cd1970d5d53e30f0abe91d38fa5a9e0b3403d71bca0f780fbc" +checksum = "784cd398c18e3d565d08382d1f83c94d4603647169239f37b32ba25439873b07" dependencies = [ "agave-feature-set", "solana-bpf-loader-program", @@ -2334,9 +2343,9 @@ dependencies = [ [[package]] name = "solana-builtins-default-costs" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1bda64d71859131227ba12b47e64c313c9f0c998bd0bca29a7ee245ab52464" +checksum = "98f0361a1c69c869ed9c03cd7fbbacbda5e51b80d41aab5e3b2b2a21f2bca5d9" dependencies = [ "agave-feature-set", "ahash", @@ -2365,9 +2374,9 @@ dependencies = [ [[package]] name = "solana-compute-budget" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e87b9f9e3014226c130c630b8a982212f8f562763e0245955cc3698512b08cc" +checksum = "c346408840a596e128b1b5214fd72a2c1cf8484cc96965e10333f6a18fcb897b" dependencies = [ "solana-fee-structure", "solana-program-runtime", @@ -2375,9 +2384,9 @@ dependencies = [ [[package]] name = "solana-compute-budget-instruction" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ba0080c3b4ea2f7775546e027d617f37f65b514c82a4a4e8aee15aa7db1ad93" +checksum = "e5247f6c5a646681ed98f8f92b01d18ae75a22311ee00415ed2970d60dfcd610" dependencies = [ "agave-feature-set", "log", @@ -2407,9 +2416,9 @@ dependencies = [ [[package]] name = "solana-compute-budget-program" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "230f049bd1f84c2009bb0d31d2a5551c30cd5f32a9fa1f23068ade298d2cf6e8" +checksum = "bb474148341578c7aebd3a6d42e2f5b3be0c49ed28eb325460966d63073c9498" dependencies = [ "solana-program-runtime", ] @@ -2430,9 +2439,9 @@ dependencies = [ [[package]] name = "solana-curve25519" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "720e1d774f0404957bf112365b2720ca9af9b0d1bdf0a653b22cfb52dbaa9c9e" +checksum = "f3d7e1177e6006823b91e0a930d94992ed74f8a6327d54ee50a9457ff72e625a" dependencies = [ "bytemuck", "bytemuck_derive", @@ -2552,9 +2561,9 @@ dependencies = [ [[package]] name = "solana-fee" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed09f24debf8af4738c54fb41d73142571c91a04e93f7db406408c0ef7405606" +checksum = "56a1e5ee3af013f0851107fbf873c4949aeb7bfaeaea5444fa7ed0adb209696f" dependencies = [ "agave-feature-set", "solana-fee-structure", @@ -2626,15 +2635,15 @@ dependencies = [ [[package]] name = "solana-instruction" -version = "3.2.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6a6d22d0a6fdf345be294bb9afdcd40c296cdc095e64e7ceaa3bb3c2f608c1c" +checksum = "ee1b699a2c1518028a9982e255e0eca10c44d90006542d9d7f9f40dbce3f7c78" dependencies = [ "bincode", "borsh", "serde", "serde_derive", - "solana-define-syscall 5.0.0", + "solana-define-syscall 4.0.1", "solana-instruction-error", "solana-pubkey 4.1.0", ] @@ -2694,15 +2703,14 @@ dependencies = [ [[package]] name = "solana-keypair" -version = "3.1.2" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "263d614c12aa267a3278703175fd6440552ca61bc960b5a02a4482720c53438b" +checksum = "5ac8be597c9e231b0cab2928ce3bc3e4ee77d9c0ad92977b9d901f3879f25a7a" dependencies = [ "ed25519-dalek", "ed25519-dalek-bip32", "five8", - "five8_core 1.0.0", - "rand 0.9.2", + "rand 0.8.5", "solana-address 2.2.0", "solana-derivation-path", "solana-seed-derivable", @@ -2755,9 +2763,9 @@ dependencies = [ [[package]] name = "solana-loader-v4-program" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "431f88b97c0d89abb272dbafa8ee6d930d0979a811a3283b8cca42715f1b2b9b" +checksum = "3f21fc8ee1d0f22ff002542f352eb5c38128dfc2dbc700be1db23251ecc3e78c" dependencies = [ "log", "solana-account", @@ -2779,17 +2787,17 @@ dependencies = [ [[package]] name = "solana-message" -version = "3.1.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0448b1fd891c5f46491e5dc7d9986385ba3c852c340db2911dd29faa01d2b08d" +checksum = "85666605c9fd727f865ed381665db0a8fc29f984a030ecc1e40f43bfb2541623" dependencies = [ "bincode", "blake3", "lazy_static", "serde", "serde_derive", - "solana-address 2.2.0", - "solana-hash 4.2.0", + "solana-address 1.1.0", + "solana-hash 3.1.0", "solana-instruction", "solana-sanitize", "solana-sdk-ids", @@ -2814,15 +2822,15 @@ checksum = "ae8dd4c280dca9d046139eb5b7a5ac9ad10403fbd64964c7d7571214950d758f" [[package]] name = "solana-nonce" -version = "3.1.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc469152a63284ef959b80c59cda015262a021da55d3b8fe42171d89c4b64f8" +checksum = "abbdc6c8caf1c08db9f36a50967539d0f72b9f1d4aea04fec5430f532e5afadc" dependencies = [ "serde", "serde_derive", "solana-fee-calculator", - "solana-hash 4.2.0", - "solana-pubkey 4.1.0", + "solana-hash 3.1.0", + "solana-pubkey 3.0.0", "solana-sha256-hasher", ] @@ -2865,9 +2873,9 @@ dependencies = [ [[package]] name = "solana-poseidon" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f78b376ada87092ac1ce2aeda1b90e16a3c07b881497bce9fec4fa7762ce19" +checksum = "25a2c8f53a80947785b58e55bda1b619bac03f266c9055b7b0208775f0522057" dependencies = [ "ark-bn254 0.4.0", "ark-bn254 0.5.0", @@ -2984,18 +2992,18 @@ checksum = "362279f6e8020e4cf11313233789bf619420ad8835ebc91963ee5cec91bb05da" [[package]] name = "solana-program-pack" -version = "3.1.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7701cb15b90667ae1c89ef4ac35a59c61e66ce58ddee13d729472af7f41d59" +checksum = "c169359de21f6034a63ebf96d6b380980307df17a8d371344ff04a883ec4e9d0" dependencies = [ "solana-program-error", ] [[package]] name = "solana-program-runtime" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad04a3aa8e5597c2d8e4933be8c50126f9911ed7bf97eb17a2516d93a19266" +checksum = "b9cefa43d3f60a2dd25e0fcc3a5a46f50de021c42981029b1e8f375e1d954853" dependencies = [ "base64 0.22.1", "bincode", @@ -3296,12 +3304,12 @@ dependencies = [ [[package]] name = "solana-stable-layout" -version = "3.0.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9f6a291ba063a37780af29e7db14bdd3dc447584d8ba5b3fc4b88e2bbc982fa" +checksum = "1da74507795b6e8fb60b7c7306c0c36e2c315805d16eaaf479452661234685ac" dependencies = [ "solana-instruction", - "solana-pubkey 4.1.0", + "solana-pubkey 3.0.0", ] [[package]] @@ -3325,9 +3333,9 @@ dependencies = [ [[package]] name = "solana-svm-callback" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce7750f8e00d1116629b47ab0a9dba78b332d4e6583ee4e15df1b791dc834099" +checksum = "b9114cc1391e57d0d6902c7c347964cb8e41e3c141ebb4be3ec5440be1c994fa" dependencies = [ "solana-account", "solana-clock", @@ -3337,30 +3345,30 @@ dependencies = [ [[package]] name = "solana-svm-feature-set" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b991162ad8f2fa5e35f15c7d2c90bf8ad3fdfc269cc1eaac199749d60420e4" +checksum = "62a20c4fc8d409780c4592c17ac3e01b6f3dc949e6ffd3acbda6d2a21e67b53a" [[package]] name = "solana-svm-log-collector" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d92fe6bc5e6376b28cba96cbb31c3158605a51d4b66bb5ed1a25233ef4c276" +checksum = "f92f14f80cf719d5bc2c14bbee50075920637d1f9afe2fce990e5394fa579293" dependencies = [ "log", ] [[package]] name = "solana-svm-measure" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a1dd1d72be70c87927454988359177107925e3c328226358ed82f1b023061aa" +checksum = "f4c68915dfa801dd03b3f7c3e0a510fe233754463e77f5afe6a30a51ac9fa326" [[package]] name = "solana-svm-timings" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12493e27f0fdcd34dcb1e7c0d41c88571dcec3ed704aaec405557bad7287f834" +checksum = "89a98d45197b57ae84bbf97647840adb153e444ad147638e742929d83343f48e" dependencies = [ "eager", "enum-iterator", @@ -3369,9 +3377,9 @@ dependencies = [ [[package]] name = "solana-svm-transaction" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae5b541edec0d134ff094d9f9394475ac242df424ecb98665c83849bebb0dde" +checksum = "1a6074e3b4bdd7ade15db51a4f309eb9e22239d33b01f4ea5ecf6aff8f81d0ba" dependencies = [ "solana-hash 3.1.0", "solana-message", @@ -3383,9 +3391,9 @@ dependencies = [ [[package]] name = "solana-svm-type-overrides" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c9f8e9939fd6ae4d9b1072de19500380f3dc7b990439b304b504219016cc3d" +checksum = "51b2ea7c2f849cd6d190e2607c2af779d3dad2792784cc13c0e9342e429c87a1" dependencies = [ "rand 0.8.5", ] @@ -3407,9 +3415,9 @@ dependencies = [ [[package]] name = "solana-system-program" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad3cdd09f7d9e13f41c7e107ef96445a2249085be5da217b1e7ef1deb126b543" +checksum = "14d88343aac5ad97d240eca299c8dfcde833e861b6a40bff6fda3ac215efc211" dependencies = [ "bincode", "log", @@ -3483,9 +3491,9 @@ checksum = "0ced92c60aa76ec4780a9d93f3bd64dfa916e1b998eacc6f1c110f3f444f02c9" [[package]] name = "solana-transaction" -version = "3.1.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96697cff5075a028265324255efed226099f6d761ca67342b230d09f72cc48d2" +checksum = "2ceb2efbf427a91b884709ffac4dac29117752ce1e37e9ae04977e450aa0bb76" dependencies = [ "bincode", "serde", @@ -3505,9 +3513,9 @@ dependencies = [ [[package]] name = "solana-transaction-context" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5378750bacbb3e3c19203588a240e725712e2e92336aa345ece0efb67258215" +checksum = "be7c191d89fb883fef0b4bb4225121f7ad14eb5664d5dc9707b4af661e21924c" dependencies = [ "bincode", "serde", @@ -3522,9 +3530,9 @@ dependencies = [ [[package]] name = "solana-transaction-error" -version = "3.1.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8396904805b0b385b9de115a652fe80fd01e5b98ce0513f4fcd8184ada9bb792" +checksum = "4222065402340d7e6aec9dc3e54d22992ddcf923d91edcd815443c2bfca3144a" dependencies = [ "serde", "serde_derive", @@ -3560,9 +3568,9 @@ dependencies = [ [[package]] name = "solana-vote-program" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc892d65dd35e791b495e118ab33b8ac823846b4fb733ebede592a50bc133cff" +checksum = "be3d7ddb2f7585dcc2225dec9991b15a76c2b80c3edad8f4d5b563fd6b7a2f25" dependencies = [ "agave-feature-set", "bincode", @@ -3592,9 +3600,9 @@ dependencies = [ [[package]] name = "solana-zk-elgamal-proof-program" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7f5958bf5724955730ee67ee02200eede18c2400f9c614f491c21bb83448f34" +checksum = "f83f4e40a373aee876998df8f2ffb2a7a6a49667b732a0e6de6d8e5655edc392" dependencies = [ "agave-feature-set", "bytemuck", @@ -3646,9 +3654,9 @@ dependencies = [ [[package]] name = "solana-zk-token-proof-program" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1969df9bfd95199f5248969dd7a46e465982205bf836a12b9c4c6d969c7c95b6" +checksum = "6a51feb27c5d776ca053a9b4dd6d7635015fae86fd07b0e74f0cf38e78941d3c" dependencies = [ "agave-feature-set", "bytemuck", @@ -3663,9 +3671,9 @@ dependencies = [ [[package]] name = "solana-zk-token-sdk" -version = "3.1.9" +version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8171bc921005f8f37c551c5d03a76fc9f919bcd64e987752be48c0b00a1aeeba" +checksum = "f2cd24e74dc810c1270c4c8237e4c1fb9f5b80ba3eba96da125070e17187b838" dependencies = [ "aes-gcm-siv", "base64 0.22.1", @@ -3797,6 +3805,34 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "spl-token" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "878b0183d51fcd8a53e1604f4c13321894cf53227e6773c529b0d03d499a8dfd" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "num_enum", + "solana-account-info", + "solana-cpi", + "solana-instruction", + "solana-msg", + "solana-program-entrypoint", + "solana-program-error", + "solana-program-memory", + "solana-program-option", + "solana-program-pack", + "solana-pubkey 3.0.0", + "solana-rent", + "solana-sdk-ids", + "solana-sysvar", + "spl-token-interface", + "thiserror 2.0.18", +] + [[package]] name = "spl-token-2022-interface" version = "2.1.0" diff --git a/Cargo.toml b/Cargo.toml index 9c1245a..f9d76de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,13 @@ litesvm = "0.10.0" solana-sdk = "3.0.0" litesvm-token = "0.10.0" spl-associated-token-account = "8.0.0" + +solana-instruction = "3.1.0" +solana-keypair = "3.1.0" +solana-native-token = "3.0.0" +solana-pubkey = "4.1.0" +solana-signer = "3.0.0" +solana-transaction = "3.0.2" +solana-message = "3.0.1" +solana-sdk-ids = "3.1.0" +spl-token = { version = "9.0.0", features = ["no-entrypoint"]} diff --git a/src/lib.rs b/src/lib.rs index 36f9408..9b4f3a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,4 +4,6 @@ pub mod constants; pub mod instructions; pub mod math; pub mod state; +#[cfg(test)] +pub mod tests; pub mod utils; diff --git a/src/tests/deposit.rs b/src/tests/deposit.rs new file mode 100644 index 0000000..290f0e4 --- /dev/null +++ b/src/tests/deposit.rs @@ -0,0 +1,124 @@ +use super::helpers::program_id; +use crate::{ + instructions::DepositArgs, + tests::helpers::{MarketSetup, setup_market}, +}; +use litesvm_token::{MintTo, TOKEN_ID as TOKEN_PROGRAM_ID}; +use solana_instruction::{AccountMeta, Instruction}; +use solana_keypair::Keypair; +use solana_message::Message; +use solana_signer::Signer; +use solana_transaction::Transaction; +use spl_token::solana_program::program_pack::Pack; + +#[test] +fn test_deposit() { + let MarketSetup { + mut svm, + admin, + config_pda, + mint, + vault, + maker_ata, + .. + } = setup_market(1_000, 100, 400, 200); + + let deposit_amount = 500_000_000u64; + MintTo::new(&mut svm, &admin, &mint, &maker_ata, 1_000_000_000) + .send() + .unwrap(); + + let deposit_args = DepositArgs { + amount: deposit_amount, + }; + + let mut deposit_data = vec![3u8]; + deposit_data.extend_from_slice(unsafe { + core::slice::from_raw_parts( + &deposit_args as *const _ as *const u8, + core::mem::size_of::(), + ) + }); + + let deposit_ix = Instruction { + program_id: program_id(), + accounts: vec![ + AccountMeta::new(admin.pubkey(), true), + AccountMeta::new(config_pda, false), + AccountMeta::new(maker_ata, false), + AccountMeta::new(vault, false), + AccountMeta::new(TOKEN_PROGRAM_ID, false), + ], + data: deposit_data, + }; + + let msg = Message::new(&[deposit_ix], Some(&admin.pubkey())); + + let tx = svm + .send_transaction(Transaction::new(&[&admin], msg, svm.latest_blockhash())) + .unwrap(); + + println!("{:<12} | {:>6} CUs", "deposit", tx.compute_units_consumed); + + let vault_acc = svm.get_account(&vault).unwrap(); + let vault_data = spl_token::state::Account::unpack(&vault_acc.data).unwrap(); + assert_eq!(vault_data.amount, deposit_amount); +} + +#[test] +#[should_panic] +fn test_deposit_unauthorized() { + let MarketSetup { + mut svm, + config_pda, + mint, + vault, + admin, + .. + } = setup_market(1_000, 100, 400, 200); + + let unauthorized_user = Keypair::new(); + svm.airdrop(&unauthorized_user.pubkey(), 1_000_000_000) + .unwrap(); + + let unauthorized_ata = + litesvm_token::CreateAssociatedTokenAccount::new(&mut svm, &unauthorized_user, &mint) + .owner(&unauthorized_user.pubkey()) + .send() + .unwrap(); + + MintTo::new(&mut svm, &admin, &mint, &unauthorized_ata, 1_000_000_000) + .send() + .unwrap(); + + let deposit_args = DepositArgs { + amount: 100_000_000, + }; + + let mut deposit_data = vec![3u8]; + deposit_data.extend_from_slice(unsafe { + core::slice::from_raw_parts( + &deposit_args as *const _ as *const u8, + core::mem::size_of::(), + ) + }); + + let deposit_ix = Instruction { + program_id: program_id(), + accounts: vec![ + AccountMeta::new(unauthorized_user.pubkey(), true), + AccountMeta::new(config_pda, false), + AccountMeta::new(unauthorized_ata, false), + AccountMeta::new(vault, false), + AccountMeta::new(TOKEN_PROGRAM_ID, false), + ], + data: deposit_data, + }; + + svm.send_transaction(Transaction::new( + &[&unauthorized_user], + Message::new(&[deposit_ix], Some(&unauthorized_user.pubkey())), + svm.latest_blockhash(), + )) + .unwrap(); +} diff --git a/src/tests/helpers.rs b/src/tests/helpers.rs new file mode 100644 index 0000000..fc8e628 --- /dev/null +++ b/src/tests/helpers.rs @@ -0,0 +1,201 @@ +use std::path::PathBuf; + +use litesvm::LiteSVM; +use litesvm_token::{ + CreateAssociatedTokenAccount, CreateMint, MintTo, TOKEN_ID as TOKEN_PROGRAM_ID, +}; +use solana_keypair::Keypair; +use solana_message::{AccountMeta, Instruction, Message}; +use solana_native_token::LAMPORTS_PER_SOL; +use solana_pubkey::Pubkey; +use solana_signer::Signer; +use solana_transaction::Transaction; + +use crate::instructions::{DepositArgs, InitializeMarketArgs}; + +pub fn program_id() -> Pubkey { + Pubkey::from_str_const("9FmdmGsrkXuhiLZYPD25BctczFmT5DQkpvKekA6v9tPh") +} + +pub fn load_svm() -> (LiteSVM, Keypair) { + let mut svm = LiteSVM::new(); + let payer = Keypair::new(); + + svm.airdrop(&payer.pubkey(), 10 * LAMPORTS_PER_SOL).unwrap(); + + let so_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("target/sbpf-solana-solana/release/enclave.so"); + + let program_data = std::fs::read(so_path).expect("Failed to read program SO file"); + svm.add_program(program_id(), &program_data).unwrap(); + + (svm, payer) +} + +pub struct MarketSetup { + pub svm: LiteSVM, + pub admin: Keypair, + pub config_pda: Pubkey, + pub vault: Pubkey, + pub mint: Pubkey, + pub maker_ata: Pubkey, + pub init_cu: u64, +} + +pub fn setup_market( + target_inventory: u64, + risk_aversion: u64, + variance: u64, + spread_factor: u64, +) -> MarketSetup { + let (mut svm, admin) = load_svm(); + let (config_pda, bump) = Pubkey::find_program_address(&[b"maker_config"], &program_id()); + + let mint = CreateMint::new(&mut svm, &admin) + .decimals(6) + .authority(&admin.pubkey()) + .send() + .unwrap(); + + let maker_ata = CreateAssociatedTokenAccount::new(&mut svm, &admin, &mint) + .owner(&admin.pubkey()) + .send() + .unwrap(); + + let vault = CreateAssociatedTokenAccount::new(&mut svm, &admin, &mint) + .owner(&config_pda) + .send() + .unwrap(); + + let args = InitializeMarketArgs { + target_inventory, + risk_aversion, + variance, + spread_factor, + bump, + }; + + let mut data = vec![1u8]; + data.extend_from_slice(unsafe { + core::slice::from_raw_parts( + &args as *const _ as *const u8, + core::mem::size_of::(), + ) + }); + + let ix = Instruction { + program_id: program_id(), + accounts: vec![ + AccountMeta::new(admin.pubkey(), true), + AccountMeta::new(config_pda, false), + AccountMeta::new(solana_sdk_ids::system_program::ID, false), + ], + data, + }; + + let msg = Message::new(&[ix], Some(&admin.pubkey())); + let blockhash = svm.latest_blockhash(); + let tx = svm + .send_transaction(Transaction::new(&[&admin], msg, blockhash)) + .unwrap(); + + MarketSetup { + svm, + admin, + config_pda, + vault, + mint, + maker_ata, + init_cu: tx.compute_units_consumed, + } +} + +pub fn setup_market_and_deposit(amount: u64) -> MarketSetup { + let (mut svm, admin) = load_svm(); + let admin_pubkey = admin.pubkey(); + let (config_pda, bump) = Pubkey::find_program_address(&[b"maker_config"], &program_id()); + + let mint = CreateMint::new(&mut svm, &admin) + .decimals(6) + .authority(&admin_pubkey) + .send() + .unwrap(); + let maker_ata = CreateAssociatedTokenAccount::new(&mut svm, &admin, &mint) + .owner(&admin_pubkey) + .send() + .unwrap(); + let vault = CreateAssociatedTokenAccount::new(&mut svm, &admin, &mint) + .owner(&config_pda) + .send() + .unwrap(); + + MintTo::new(&mut svm, &admin, &mint, &maker_ata, amount) + .send() + .unwrap(); + + let init_args = InitializeMarketArgs { + target_inventory: amount, + risk_aversion: 100, + variance: 400, + spread_factor: 200, + bump, + }; + + let mut init_data = vec![1u8]; + init_data.extend_from_slice(unsafe { + core::slice::from_raw_parts( + &init_args as *const _ as *const u8, + core::mem::size_of::(), + ) + }); + + let deposit_args = DepositArgs { amount }; + let mut deposit_data = vec![3u8]; + deposit_data.extend_from_slice(unsafe { + core::slice::from_raw_parts( + &deposit_args as *const _ as *const u8, + core::mem::size_of::(), + ) + }); + + let ixs = [ + Instruction { + program_id: program_id(), + accounts: vec![ + AccountMeta::new(admin_pubkey, true), + AccountMeta::new(config_pda, false), + AccountMeta::new(solana_sdk_ids::system_program::ID, false), + ], + data: init_data, + }, + Instruction { + program_id: program_id(), + accounts: vec![ + AccountMeta::new(admin_pubkey, true), + AccountMeta::new(config_pda, false), + AccountMeta::new(maker_ata, false), + AccountMeta::new(vault, false), + AccountMeta::new(TOKEN_PROGRAM_ID, false), + ], + data: deposit_data, + }, + ]; + + let tx = svm + .send_transaction(Transaction::new( + &[&admin], + Message::new(&ixs, Some(&admin_pubkey)), + svm.latest_blockhash(), + )) + .unwrap(); + + MarketSetup { + svm, + admin, + config_pda, + vault, + mint, + maker_ata, + init_cu: tx.compute_units_consumed, + } +} diff --git a/src/tests/initialize_market.rs b/src/tests/initialize_market.rs new file mode 100644 index 0000000..5192bc1 --- /dev/null +++ b/src/tests/initialize_market.rs @@ -0,0 +1,63 @@ +use solana_instruction::{AccountMeta, Instruction}; +use solana_message::Message; +use solana_pubkey::Pubkey; +use solana_signer::Signer; +use solana_transaction::Transaction; + +use super::helpers::{load_svm, program_id}; +use crate::{instructions::InitializeMarketArgs, state::MakerConfig}; + +#[test] +fn test_initialize_market() { + let (mut svm, admin) = load_svm(); + + let (config_pda, bump) = Pubkey::find_program_address(&[b"maker_config"], &program_id()); + + let args = InitializeMarketArgs { + target_inventory: 1_000, + risk_aversion: 100, + variance: 400, + spread_factor: 200, + bump, + }; + + let mut data = vec![1u8]; + data.extend_from_slice(unsafe { + core::slice::from_raw_parts( + &args as *const _ as *const u8, + core::mem::size_of::(), + ) + }); + + let ix = Instruction { + program_id: program_id(), + accounts: vec![ + AccountMeta::new(admin.pubkey(), true), + AccountMeta::new(config_pda, false), + AccountMeta::new(solana_sdk_ids::system_program::ID, false), + ], + data, + }; + + let msg = Message::new(&[ix], Some(&admin.pubkey())); + let blockhash = svm.latest_blockhash(); + let tx = svm + .send_transaction(Transaction::new(&[&admin], msg, blockhash)) + .unwrap(); + + println!( + "{:<12} | {:>6} CUs", + "init_market", tx.compute_units_consumed + ); + + let account = svm.get_account(&config_pda).expect("Account should exist"); + let config = MakerConfig::load(&account.data).unwrap(); + + assert_eq!( + u64::from_le_bytes(config.target_inventory), + args.target_inventory + ); + assert_eq!(u64::from_le_bytes(config.risk_aversion), args.risk_aversion); + assert_eq!(config.market_maker, admin.pubkey().to_bytes()); + assert_eq!(config.bump, bump); +} diff --git a/src/tests/mod.rs b/src/tests/mod.rs new file mode 100644 index 0000000..4cae44f --- /dev/null +++ b/src/tests/mod.rs @@ -0,0 +1,14 @@ +#[cfg(test)] +pub mod helpers; + +#[cfg(test)] +pub mod initialize_market; + +#[cfg(test)] +pub mod update_config; + +#[cfg(test)] +pub mod deposit; + +#[cfg(test)] +pub mod withdraw; diff --git a/src/tests/update_config.rs b/src/tests/update_config.rs new file mode 100644 index 0000000..78d6ce0 --- /dev/null +++ b/src/tests/update_config.rs @@ -0,0 +1,109 @@ +use solana_instruction::{AccountMeta, Instruction}; +use solana_keypair::Keypair; +use solana_message::Message; +use solana_signer::Signer; +use solana_transaction::Transaction; + +use super::helpers::program_id; +use crate::{ + instructions::UpdateConfigArgs, + state::MakerConfig, + tests::helpers::{MarketSetup, setup_market}, +}; + +#[test] +fn test_update_config() { + let MarketSetup { + mut svm, + admin, + config_pda, + .. + } = setup_market(1_000, 100, 400, 200); + + let update_args = UpdateConfigArgs { + target_inventory: 5_000, + risk_aversion: 150, + variance: 350, + spread_factor: 250, + }; + + let mut update_data = vec![2u8]; + update_data.extend_from_slice(unsafe { + core::slice::from_raw_parts( + &update_args as *const _ as *const u8, + core::mem::size_of::(), + ) + }); + + let update_ix = Instruction { + program_id: program_id(), + accounts: vec![ + AccountMeta::new(admin.pubkey(), true), + AccountMeta::new(config_pda, false), + ], + data: update_data, + }; + + let msg = Message::new(&[update_ix], Some(&admin.pubkey())); + let tx = svm + .send_transaction(Transaction::new(&[&admin], msg, svm.latest_blockhash())) + .unwrap(); + + println!( + "{:<12} | {:>6} CUs", + "update_config", tx.compute_units_consumed + ); + + let account = svm.get_account(&config_pda).expect("Account should exist"); + let config = MakerConfig::load(&account.data).expect("Should load config bytes"); + + assert_eq!(u64::from_le_bytes(config.target_inventory), 5_000); + assert_eq!(u64::from_le_bytes(config.risk_aversion), 150); + assert_eq!(u64::from_le_bytes(config.variance), 350); + assert_eq!(u64::from_le_bytes(config.spread_factor), 250); +} + +#[test] +#[should_panic] +fn test_update_config_unauthorized() { + let MarketSetup { + mut svm, + config_pda, + .. + } = setup_market(1_000, 100, 400, 200); + + let unauthorized_user = Keypair::new(); + svm.airdrop(&unauthorized_user.pubkey(), 1_000_000_000) + .unwrap(); + + let update_args = UpdateConfigArgs { + target_inventory: 5_000, + risk_aversion: 150, + variance: 350, + spread_factor: 250, + }; + + let mut update_data = vec![2u8]; + update_data.extend_from_slice(unsafe { + core::slice::from_raw_parts( + &update_args as *const _ as *const u8, + core::mem::size_of::(), + ) + }); + + let update_unauthorized_ix = Instruction { + program_id: program_id(), + accounts: vec![ + AccountMeta::new(unauthorized_user.pubkey(), true), + AccountMeta::new(config_pda, false), + ], + data: update_data, + }; + + svm.send_transaction(Transaction::new( + &[&unauthorized_user], + Message::new(&[update_unauthorized_ix], Some(&unauthorized_user.pubkey())), + svm.latest_blockhash(), + )) + .unwrap(); +} diff --git a/src/tests/withdraw.rs b/src/tests/withdraw.rs new file mode 100644 index 0000000..a562f64 --- /dev/null +++ b/src/tests/withdraw.rs @@ -0,0 +1,124 @@ +use super::helpers::program_id; +use crate::{ + instructions::WithdrawArgs, + tests::helpers::{MarketSetup, setup_market_and_deposit}, +}; +use litesvm_token::{CreateAssociatedTokenAccount, TOKEN_ID as TOKEN_PROGRAM_ID}; +use solana_instruction::{AccountMeta, Instruction}; +use solana_keypair::Keypair; +use solana_message::Message; +use solana_signer::Signer; +use solana_transaction::Transaction; +use spl_token::solana_program::program_pack::Pack; + +#[test] +fn test_withdraw() { + let initial_deposit = 1_000_000_000; + let withdraw_amount = 400_000_000; + + let MarketSetup { + mut svm, + admin, + config_pda, + vault, + maker_ata, + .. + } = setup_market_and_deposit(initial_deposit); + + let args = WithdrawArgs { + amount: withdraw_amount, + }; + + let mut data = vec![4u8]; + data.extend_from_slice(unsafe { + core::slice::from_raw_parts( + &args as *const _ as *const u8, + core::mem::size_of::(), + ) + }); + + let ix = Instruction { + program_id: program_id(), + accounts: vec![ + AccountMeta::new(admin.pubkey(), true), + AccountMeta::new(config_pda, false), + AccountMeta::new(vault, false), + AccountMeta::new(maker_ata, false), + AccountMeta::new(TOKEN_PROGRAM_ID, false), + ], + data, + }; + + let tx = svm + .send_transaction(Transaction::new( + &[&admin], + Message::new(&[ix], Some(&admin.pubkey())), + svm.latest_blockhash(), + )) + .unwrap(); + + println!("{:<12} | {:>6} CUs", "withdraw", tx.compute_units_consumed); + + let vault_acc = svm.get_account(&vault).unwrap(); + let vault_data = spl_token::state::Account::unpack(&vault_acc.data).unwrap(); + assert_eq!(vault_data.amount, initial_deposit - withdraw_amount); + + let maker_acc = svm.get_account(&maker_ata).unwrap(); + let maker_data = spl_token::state::Account::unpack(&maker_acc.data).unwrap(); + assert_eq!(maker_data.amount, withdraw_amount); +} + +#[test] +#[should_panic] +fn test_withdraw_unauthorized() { + let initial_deposit = 1_000_000_000; + let withdraw_amount = 400_000_000; + + let MarketSetup { + mut svm, + config_pda, + vault, + mint, + .. + } = setup_market_and_deposit(initial_deposit); + + let unauthorized_user = Keypair::new(); + svm.airdrop(&unauthorized_user.pubkey(), 1_000_000_000) + .unwrap(); + + let unauthorized_ata = CreateAssociatedTokenAccount::new(&mut svm, &unauthorized_user, &mint) + .owner(&unauthorized_user.pubkey()) + .send() + .unwrap(); + + let args = WithdrawArgs { + amount: withdraw_amount, + }; + + let mut data = vec![4u8]; + data.extend_from_slice(unsafe { + core::slice::from_raw_parts( + &args as *const _ as *const u8, + core::mem::size_of::(), + ) + }); + + let ix = Instruction { + program_id: program_id(), + accounts: vec![ + AccountMeta::new(unauthorized_user.pubkey(), true), + AccountMeta::new(config_pda, false), + AccountMeta::new(vault, false), + AccountMeta::new(unauthorized_ata, false), + AccountMeta::new(TOKEN_PROGRAM_ID, false), + ], + data, + }; + + svm.send_transaction(Transaction::new( + &[&unauthorized_user], + Message::new(&[ix], Some(&unauthorized_user.pubkey())), + svm.latest_blockhash(), + )) + .unwrap(); +}