diff --git a/Cargo.lock b/Cargo.lock index 8a4dd28..42b7e56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,42 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aead" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +dependencies = [ + "generic-array", +] + +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", + "opaque-debug", +] + +[[package]] +name = "aes-gcm-siv" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589c637f0e68c877bbd59a4599bbe849cac8e5f3e4b5a3ebae8f528cd218dcdc" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "polyval", + "subtle", + "zeroize", +] + [[package]] name = "ahash" version = "0.7.8" @@ -196,6 +232,21 @@ dependencies = [ "serde", ] +[[package]] +name = "anchor-spl" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04bd077c34449319a1e4e0bc21cea572960c9ae0d0fefda0dd7c52fcc3c647a3" +dependencies = [ + "anchor-lang", + "spl-associated-token-account", + "spl-pod", + "spl-token", + "spl-token-2022", + "spl-token-group-interface", + "spl-token-metadata-interface", +] + [[package]] name = "anchor-syn" version = "0.30.1" @@ -350,6 +401,23 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -373,6 +441,7 @@ name = "bets" version = "0.1.0" dependencies = [ "anchor-lang", + "anchor-spl", ] [[package]] @@ -422,6 +491,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ + "block-padding", "generic-array", ] @@ -434,6 +504,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + [[package]] name = "borsh" version = "0.9.3" @@ -637,6 +713,24 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "num-traits", +] + +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array", +] + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -723,6 +817,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "ctr" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +dependencies = [ + "cipher", +] + [[package]] name = "curve25519-dalek" version = "3.2.1" @@ -737,6 +840,47 @@ dependencies = [ "zeroize", ] +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.100", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "derivation-path" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5c37193a1db1d8ed868c03ec7b152175f26160a5b740e5e484143877e0adf0" + [[package]] name = "derivative" version = "2.2.0" @@ -768,12 +912,60 @@ dependencies = [ "subtle", ] +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "ed25519-dalek-bip32" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d2be62a4061b872c8c0873ee4fc6f101ce7b889d039f019c5fa2af471a59908" +dependencies = [ + "derivation-path", + "ed25519-dalek", + "hmac 0.12.1", + "sha2 0.10.8", +] + [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -786,6 +978,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "generic-array" version = "0.14.7" @@ -856,6 +1054,15 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hmac" version = "0.8.1" @@ -866,6 +1073,15 @@ dependencies = [ "digest 0.9.0", ] +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "hmac-drbg" version = "0.3.0" @@ -874,9 +1090,21 @@ checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" dependencies = [ "digest 0.9.0", "generic-array", - "hmac", + "hmac 0.8.1", ] +[[package]] +name = "humantime" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "im" version = "15.1.0" @@ -1058,6 +1286,18 @@ dependencies = [ "autocfg", ] +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.6.4", + "zeroize", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -1097,6 +1337,27 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro-crate 3.3.0", + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -1147,6 +1408,33 @@ dependencies = [ "crypto-mac", ] +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "polyval" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -1183,6 +1471,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "qstring" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "qualifier_attr" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e2e25ee72f5b24d773cae88422baddefff7714f97aab68d96fe2b6fc4a28fb2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "quote" version = "1.0.40" @@ -1419,6 +1727,28 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" +dependencies = [ + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "sha2" version = "0.9.9" @@ -1443,6 +1773,18 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha3" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "keccak", + "opaque-debug", +] + [[package]] name = "sha3" version = "0.10.8" @@ -1459,6 +1801,18 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "sized-chunks" version = "0.6.5" @@ -1512,6 +1866,17 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "solana-logger" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121d36ffb3c6b958763312cbc697fbccba46ee837d3a0aa4fc0e90fcb3b884f3" +dependencies = [ + "env_logger", + "lazy_static", + "log", +] + [[package]] name = "solana-program" version = "1.18.26" @@ -1557,7 +1922,7 @@ dependencies = [ "serde_derive", "serde_json", "sha2 0.10.8", - "sha3", + "sha3 0.10.8", "solana-frozen-abi", "solana-frozen-abi-macro", "solana-sdk-macro", @@ -1567,6 +1932,61 @@ dependencies = [ "zeroize", ] +[[package]] +name = "solana-sdk" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "580ad66c2f7a4c3cb3244fe21440546bd500f5ecb955ad9826e92a78dded8009" +dependencies = [ + "assert_matches", + "base64 0.21.7", + "bincode", + "bitflags", + "borsh 1.5.7", + "bs58 0.4.0", + "bytemuck", + "byteorder", + "chrono", + "derivation-path", + "digest 0.10.7", + "ed25519-dalek", + "ed25519-dalek-bip32", + "generic-array", + "hmac 0.12.1", + "itertools", + "js-sys", + "lazy_static", + "libsecp256k1", + "log", + "memmap2", + "num-derive", + "num-traits", + "num_enum", + "pbkdf2 0.11.0", + "qstring", + "qualifier_attr", + "rand 0.7.3", + "rand 0.8.5", + "rustc_version", + "rustversion", + "serde", + "serde_bytes", + "serde_derive", + "serde_json", + "serde_with", + "sha2 0.10.8", + "sha3 0.10.8", + "siphasher", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-logger", + "solana-program", + "solana-sdk-macro", + "thiserror", + "uriparse", + "wasm-bindgen", +] + [[package]] name = "solana-sdk-macro" version = "1.18.26" @@ -1581,10 +2001,258 @@ dependencies = [ ] [[package]] -name = "subtle" -version = "2.6.1" +name = "solana-security-txt" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +checksum = "468aa43b7edb1f9b7b7b686d5c3aeb6630dc1708e86e31343499dd5c4d775183" + +[[package]] +name = "solana-zk-token-sdk" +version = "1.18.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cbdf4249b6dfcbba7d84e2b53313698043f60f8e22ce48286e6fbe8a17c8d16" +dependencies = [ + "aes-gcm-siv", + "base64 0.21.7", + "bincode", + "bytemuck", + "byteorder", + "curve25519-dalek", + "getrandom 0.1.16", + "itertools", + "lazy_static", + "merlin", + "num-derive", + "num-traits", + "rand 0.7.3", + "serde", + "serde_json", + "sha3 0.9.1", + "solana-program", + "solana-sdk", + "subtle", + "thiserror", + "zeroize", +] + +[[package]] +name = "spl-associated-token-account" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "143109d789171379e6143ef23191786dfaac54289ad6e7917cfb26b36c432b10" +dependencies = [ + "assert_matches", + "borsh 1.5.7", + "num-derive", + "num-traits", + "solana-program", + "spl-token", + "spl-token-2022", + "thiserror", +] + +[[package]] +name = "spl-discriminator" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "210101376962bb22bb13be6daea34656ea1cbc248fce2164b146e39203b55e03" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator-derive", +] + +[[package]] +name = "spl-discriminator-derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9e8418ea6269dcfb01c712f0444d2c75542c04448b480e87de59d2865edc750" +dependencies = [ + "quote", + "spl-discriminator-syn", + "syn 2.0.100", +] + +[[package]] +name = "spl-discriminator-syn" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f05593b7ca9eac7caca309720f2eafb96355e037e6d373b909a80fe7b69b9" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.8", + "syn 2.0.100", + "thiserror", +] + +[[package]] +name = "spl-memo" +version = "4.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a49f49f95f2d02111ded31696ab38a081fab623d4c76bd4cb074286db4560836" +dependencies = [ + "solana-program", +] + +[[package]] +name = "spl-pod" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c52d84c55efeef8edcc226743dc089d7e3888b8e3474569aa3eff152b37b9996" +dependencies = [ + "borsh 1.5.7", + "bytemuck", + "solana-program", + "solana-zk-token-sdk", + "spl-program-error", +] + +[[package]] +name = "spl-program-error" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e45a49acb925db68aa501b926096b2164adbdcade7a0c24152af9f0742d0a602" +dependencies = [ + "num-derive", + "num-traits", + "solana-program", + "spl-program-error-derive", + "thiserror", +] + +[[package]] +name = "spl-program-error-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d375dd76c517836353e093c2dbb490938ff72821ab568b545fd30ab3256b3e" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.8", + "syn 2.0.100", +] + +[[package]] +name = "spl-tlv-account-resolution" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fab8edfd37be5fa17c9e42c1bff86abbbaf0494b031b37957f2728ad2ff842ba" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator", + "spl-pod", + "spl-program-error", + "spl-type-length-value", +] + +[[package]] +name = "spl-token" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9eb465e4bf5ce1d498f05204c8089378c1ba34ef2777ea95852fc53a1fd4fb2" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "num_enum", + "solana-program", + "thiserror", +] + +[[package]] +name = "spl-token-2022" +version = "3.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c39e416aeb1ea0b22f3b2bbecaf7e38a92a1aa8f4a0c5785c94179694e846a0" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "num_enum", + "solana-program", + "solana-security-txt", + "solana-zk-token-sdk", + "spl-memo", + "spl-pod", + "spl-token", + "spl-token-group-interface", + "spl-token-metadata-interface", + "spl-transfer-hook-interface", + "spl-type-length-value", + "thiserror", +] + +[[package]] +name = "spl-token-group-interface" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "014817d6324b1e20c4bbc883e8ee30a5faa13e59d91d1b2b95df98b920150c17" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator", + "spl-pod", + "spl-program-error", +] + +[[package]] +name = "spl-token-metadata-interface" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3da00495b602ebcf5d8ba8b3ecff1ee454ce4c125c9077747be49c2d62335ba" +dependencies = [ + "borsh 1.5.7", + "solana-program", + "spl-discriminator", + "spl-pod", + "spl-program-error", + "spl-type-length-value", +] + +[[package]] +name = "spl-transfer-hook-interface" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9b5c08a89838e5a2931f79b17f611857f281a14a2100968a3ccef352cb7414b" +dependencies = [ + "arrayref", + "bytemuck", + "solana-program", + "spl-discriminator", + "spl-pod", + "spl-program-error", + "spl-tlv-account-resolution", + "spl-type-length-value", +] + +[[package]] +name = "spl-type-length-value" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c872f93d0600e743116501eba2d53460e73a12c9a496875a42a7d70e034fe06d" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator", + "spl-pod", + "spl-program-error", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" @@ -1608,6 +2276,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -1635,9 +2312,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffc59cb9dfc85bb312c3a78fd6aa8a8582e310b0fa885d5bb877f6dcc601839d" dependencies = [ "anyhow", - "hmac", + "hmac 0.8.1", "once_cell", - "pbkdf2", + "pbkdf2 0.4.0", "rand 0.7.3", "rustc-hash", "sha2 0.9.9", @@ -1732,6 +2409,26 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "uriparse" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0200d0fc04d809396c2ad43f3c95da3582a2556eba8d453c1087f4120ee352ff" +dependencies = [ + "fnv", + "lazy_static", +] + [[package]] name = "version_check" version = "0.9.5" @@ -1818,6 +2515,46 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.52.6" diff --git a/programs/bets/Cargo.toml b/programs/bets/Cargo.toml index 2fc3579..33f22b2 100644 --- a/programs/bets/Cargo.toml +++ b/programs/bets/Cargo.toml @@ -18,3 +18,4 @@ idl-build = ["anchor-lang/idl-build"] [dependencies] anchor-lang = "0.30.1" +anchor-spl = "0.30.1" diff --git a/programs/bets/src/instructions/close_bet.rs b/programs/bets/src/instructions/close_bet.rs index e783aa9..03a58a2 100644 --- a/programs/bets/src/instructions/close_bet.rs +++ b/programs/bets/src/instructions/close_bet.rs @@ -1,9 +1,10 @@ use std::str::FromStr; use anchor_lang::prelude::*; +use anchor_spl::token::{self, Token, TokenAccount, Transfer}; use crate::{error::BettingError, *}; -pub fn close(ctx: Context, winner:Pubkey, userid:String)->Result<()>{ +pub fn close(ctx: Context, winner: Pubkey, userid: String) -> Result<()> { let bet_vault = &mut ctx.accounts.bet_vault; require!( bet_vault.owner == winner || bet_vault.joiner == winner || bet_vault.owner_id == userid || bet_vault.joiner_id == userid, @@ -17,44 +18,96 @@ pub fn close(ctx: Context, winner:Pubkey, userid:String)->Result<()>{ BettingError::InvalidFeeCollector ); - // Calculate the 5% fee - let total_lamports = **bet_vault.to_account_info().lamports.borrow(); - let mut fee = total_lamports / 20; // 5% - let referrer_fee = fee / 4; //50% for each referrer + let is_native_sol = bet_vault.is_native_sol; + let bet_vault_key = bet_vault.key(); - // In the close function: - if let Some(owner_ref) = &ctx.accounts.owner_referrer { - fee -= referrer_fee; - **bet_vault.to_account_info().try_borrow_mut_lamports()? -= referrer_fee; - **owner_ref.try_borrow_mut_lamports()? += referrer_fee; - } + if is_native_sol { + // Calculate the 5% fee + let total_lamports = **bet_vault.to_account_info().lamports.borrow(); + let mut fee = total_lamports / 20; // 5% + let referrer_fee = fee / 4; //50% for each referrer - if let Some(joiner_ref) = &ctx.accounts.joiner_referrer { - fee -= referrer_fee; - **bet_vault.to_account_info().try_borrow_mut_lamports()? -= referrer_fee; - **joiner_ref.try_borrow_mut_lamports()? += referrer_fee; + // Handle referrer fees + if let Some(owner_ref) = &ctx.accounts.owner_referrer { + fee -= referrer_fee; + **bet_vault.to_account_info().try_borrow_mut_lamports()? -= referrer_fee; + **owner_ref.try_borrow_mut_lamports()? += referrer_fee; + } + + if let Some(joiner_ref) = &ctx.accounts.joiner_referrer { + fee -= referrer_fee; + **bet_vault.to_account_info().try_borrow_mut_lamports()? -= referrer_fee; + **joiner_ref.try_borrow_mut_lamports()? += referrer_fee; + } + + **bet_vault.to_account_info().try_borrow_mut_lamports()? -= fee; + **ctx.accounts.fee_wallet.to_account_info().try_borrow_mut_lamports()? += fee; + } else { + // Handle SPL token fees + let bet_vault_token_account = ctx.accounts.bet_vault_token_account.as_ref().ok_or(BettingError::BetNotFilled)?; + let fee_wallet_token_account = ctx.accounts.fee_wallet_token_account.as_ref().ok_or(BettingError::BetNotFilled)?; + + let token_account = TokenAccount::try_deserialize(&mut &bet_vault_token_account.data.borrow()[..])?; + let total_tokens = token_account.amount; + let mut fee = total_tokens / 20; // 5% + let referrer_fee = fee / 4; // 50% for each referrer + + // Handle referrer fees + if let Some(owner_ref) = &ctx.accounts.owner_referrer_token_account { + fee -= referrer_fee; + let cpi_accounts = Transfer { + from: bet_vault_token_account.to_account_info(), + to: owner_ref.to_account_info(), + authority: ctx.accounts.bet_vault.to_account_info(), + }; + let cpi_ctx = CpiContext::new( + ctx.accounts.token_program.to_account_info(), + cpi_accounts, + ); + token::transfer(cpi_ctx, referrer_fee)?; + } + + if let Some(joiner_ref) = &ctx.accounts.joiner_referrer_token_account { + fee -= referrer_fee; + let cpi_accounts = Transfer { + from: bet_vault_token_account.to_account_info(), + to: joiner_ref.to_account_info(), + authority: ctx.accounts.bet_vault.to_account_info(), + }; + let cpi_ctx = CpiContext::new( + ctx.accounts.token_program.to_account_info(), + cpi_accounts, + ); + token::transfer(cpi_ctx, referrer_fee)?; + } + + // Transfer fee to fee wallet + let cpi_accounts = Transfer { + from: bet_vault_token_account.to_account_info(), + to: fee_wallet_token_account.to_account_info(), + authority: ctx.accounts.bet_vault.to_account_info(), + }; + let cpi_ctx = CpiContext::new( + ctx.accounts.token_program.to_account_info(), + cpi_accounts, + ); + token::transfer(cpi_ctx, fee)?; } - - **bet_vault.to_account_info().try_borrow_mut_lamports()? -= fee; - **ctx.accounts.fee_wallet.to_account_info().try_borrow_mut_lamports()? += fee; let bets_list = &mut ctx.accounts.bets_list; // Remove the bet_vault public key from the list - bets_list.bets.retain(|&bet| bet != bet_vault.key()); + bets_list.bets.retain(|&bet| bet != bet_vault_key); - msg!("Bet {} closed by {}", bet_vault.key(), winner); - - + msg!("Bet {} closed by {}", bet_vault_key, winner); Ok(()) } - #[derive(Accounts)] -pub struct CloseBet<'info>{ +pub struct CloseBet<'info> { #[account(mut)] pub bets_list: Account<'info, BetsList>, - #[account(mut, close=winner)] + #[account(mut, close = winner)] pub bet_vault: Account<'info, BetVault>, /// CHECK: This is validated against the FEE_COLLECTOR constant. @@ -73,5 +126,20 @@ pub struct CloseBet<'info>{ #[account(mut)] pub joiner_referrer: Option>, - pub system_program: Program<'info, System> + pub system_program: Program<'info, System>, + + // SPL Token accounts + #[account(mut)] + pub bet_vault_token_account: Option>, + + #[account(mut)] + pub fee_wallet_token_account: Option>, + + #[account(mut)] + pub owner_referrer_token_account: Option>, + + #[account(mut)] + pub joiner_referrer_token_account: Option>, + + pub token_program: Program<'info, Token>, } \ No newline at end of file diff --git a/programs/bets/src/instructions/create_bet.rs b/programs/bets/src/instructions/create_bet.rs index 86e4462..98f2292 100644 --- a/programs/bets/src/instructions/create_bet.rs +++ b/programs/bets/src/instructions/create_bet.rs @@ -1,7 +1,17 @@ use anchor_lang::prelude::*; -use crate::*; +use anchor_spl::token::{self, Token, TokenAccount, Transfer}; +use crate::{error::BettingError, *}; -pub fn create(ctx: Context, wager: u64, user_id:String, game_id:String, _nonce:u64) -> Result<()> { +pub fn create( + ctx: Context, + wager: u64, + user_id: String, + game_id: String, + _nonce: u64, + is_native_sol: bool, + token_mint: Option, + token_decimals: u8, +) -> Result<()> { let bets_list = &mut ctx.accounts.bets_list; let bet_vault = &mut ctx.accounts.bet_vault; let payer = &ctx.accounts.payer; @@ -10,26 +20,54 @@ pub fn create(ctx: Context, wager: u64, user_id:String, game_id:Strin // Store bet details bet_vault.game_id = game_id; bet_vault.owner = payer.key(); - bet_vault.owner_id= user_id; + bet_vault.owner_id = user_id; bet_vault.wager = wager; + bet_vault.is_native_sol = is_native_sol; + bet_vault.token_mint = token_mint; + bet_vault.token_decimals = token_decimals; - // Transfer SOL from the payer to the bet vault - let cpi_accounts = anchor_lang::system_program::Transfer { - from: payer.to_account_info(), - to: bet_vault.to_account_info(), - }; - let cpi_ctx = CpiContext::new(system_program.to_account_info(), cpi_accounts); - anchor_lang::system_program::transfer(cpi_ctx, wager)?; + if is_native_sol { + // Transfer SOL from the payer to the bet vault + let cpi_accounts = anchor_lang::system_program::Transfer { + from: payer.to_account_info(), + to: bet_vault.to_account_info(), + }; + let cpi_ctx = CpiContext::new(system_program.to_account_info(), cpi_accounts); + anchor_lang::system_program::transfer(cpi_ctx, wager)?; + } else { + // Transfer SPL tokens + let payer_token_account = ctx.accounts.payer_token_account.as_ref().ok_or(BettingError::BetNotFilled)?; + let bet_vault_token_account = ctx.accounts.bet_vault_token_account.as_ref().ok_or(BettingError::BetNotFilled)?; + + let cpi_accounts = Transfer { + from: payer_token_account.to_account_info(), + to: bet_vault_token_account.to_account_info(), + authority: payer.to_account_info(), + }; + let cpi_ctx = CpiContext::new( + ctx.accounts.token_program.to_account_info(), + cpi_accounts, + ); + token::transfer(cpi_ctx, wager)?; + } // Add this bet to the global list bets_list.bets.push(bet_vault.key()); - msg!("New bet {} created with {} lamports!", bet_vault.key(), wager); + msg!("New bet {} created with {} tokens!", bet_vault.key(), wager); Ok(()) } #[derive(Accounts)] -#[instruction(wager: u64,user_id:String, game_id: String, _nonce:u64)] +#[instruction( + wager: u64, + user_id: String, + game_id: String, + _nonce: u64, + is_native_sol: bool, + token_mint: Option, + token_decimals: u8 +)] pub struct CreateBet<'info> { #[account(mut)] pub payer: Signer<'info>, @@ -40,11 +78,20 @@ pub struct CreateBet<'info> { #[account( init, payer = payer, - space = 8 + BetVault::INIT_SPACE, // Owner (Pubkey) + Wager (u64) + space = 8 + BetVault::INIT_SPACE, seeds = [b"bet_vault", payer.key().as_ref(), &game_id.as_bytes(), &_nonce.to_le_bytes()], bump )] pub bet_vault: Account<'info, BetVault>, pub system_program: Program<'info, System>, + + // SPL Token accounts + #[account(mut)] + pub payer_token_account: Option>, + + #[account(mut)] + pub bet_vault_token_account: Option>, + + pub token_program: Program<'info, Token>, } diff --git a/programs/bets/src/instructions/join_bet.rs b/programs/bets/src/instructions/join_bet.rs index ecae4a0..c8a5906 100644 --- a/programs/bets/src/instructions/join_bet.rs +++ b/programs/bets/src/instructions/join_bet.rs @@ -1,45 +1,70 @@ use anchor_lang::prelude::*; +use anchor_spl::token::{self, Token, TokenAccount, Transfer}; use crate::{error::BettingError, *}; -pub fn join(ctx: Context,user_id:String, _game_id:String) ->Result<()>{ +pub fn join(ctx: Context, user_id: String, _game_id: String) -> Result<()> { let bet_vault = &mut ctx.accounts.bet_vault; require!( bet_vault.joiner == Pubkey::default(), BettingError::BetAlreadyFilled ); - let payer= &ctx.accounts.payer; + let payer = &ctx.accounts.payer; - let ix = anchor_lang::solana_program::system_instruction::transfer( - &payer.key(), - &bet_vault.key(), - bet_vault.wager, - ); + if bet_vault.is_native_sol { + let ix = anchor_lang::solana_program::system_instruction::transfer( + &payer.key(), + &bet_vault.key(), + bet_vault.wager, + ); - anchor_lang::solana_program::program::invoke( - &ix, - &[ - payer.to_account_info(), - bet_vault.to_account_info(), - ], - )?; + anchor_lang::solana_program::program::invoke( + &ix, + &[ + payer.to_account_info(), + bet_vault.to_account_info(), + ], + )?; + } else { + // Transfer SPL tokens + let payer_token_account = ctx.accounts.payer_token_account.as_ref().ok_or(BettingError::BetNotFilled)?; + let bet_vault_token_account = ctx.accounts.bet_vault_token_account.as_ref().ok_or(BettingError::BetNotFilled)?; + + let cpi_accounts = Transfer { + from: payer_token_account.to_account_info(), + to: bet_vault_token_account.to_account_info(), + authority: payer.to_account_info(), + }; + let cpi_ctx = CpiContext::new( + ctx.accounts.token_program.to_account_info(), + cpi_accounts, + ); + token::transfer(cpi_ctx, bet_vault.wager)?; + } bet_vault.joiner = payer.key(); bet_vault.joiner_id = user_id; msg!("Joined bet {}!", bet_vault.key()); Ok(()) - } #[derive(Accounts)] -#[instruction(_game_id:String)] -pub struct JoinBet<'info>{ - #[account( - mut - )] +#[instruction(_game_id: String)] +pub struct JoinBet<'info> { + #[account(mut)] pub bet_vault: Account<'info, BetVault>, #[account(mut)] pub payer: Signer<'info>, + pub system_program: Program<'info, System>, + + // SPL Token accounts + #[account(mut)] + pub payer_token_account: Option>, + + #[account(mut)] + pub bet_vault_token_account: Option>, + + pub token_program: Program<'info, Token>, } \ No newline at end of file diff --git a/programs/bets/src/instructions/refund_bet.rs b/programs/bets/src/instructions/refund_bet.rs index ab1d039..54028ad 100644 --- a/programs/bets/src/instructions/refund_bet.rs +++ b/programs/bets/src/instructions/refund_bet.rs @@ -1,4 +1,5 @@ use anchor_lang::prelude::*; +use anchor_spl::token::{self, Token, TokenAccount, Transfer}; use crate::{error::BettingError, *}; pub fn refund(ctx: Context, owner: Pubkey) -> Result<()> { @@ -9,44 +10,71 @@ pub fn refund(ctx: Context, owner: Pubkey) -> Result<()> { BettingError::InvalidWinner ); + let is_native_sol = bet_vault.is_native_sol; + let joiner = bet_vault.joiner; + let system_program_key = ctx.accounts.system_program.key(); + let bet_vault_key = bet_vault.key(); + // Check if there's a joiner and it's a valid account (not the system program or default) - if bet_vault.joiner != Pubkey::default() && bet_vault.joiner != ctx.accounts.system_program.key() { - // Calculate the 50% that goes to the joiner - let total_lamports = **bet_vault.to_account_info().lamports.borrow(); - let joiner_share = total_lamports / 2; - - // Transfer joiner's share - **bet_vault.to_account_info().try_borrow_mut_lamports()? -= joiner_share; - - // We need to find the joiner account in the remaining accounts - let joiner_account = ctx.remaining_accounts - .iter() - .find(|account| account.key() == bet_vault.joiner) - .ok_or(BettingError::JoinerAccountNotProvided)?; + if joiner != Pubkey::default() && joiner != system_program_key { + if is_native_sol { + // Calculate the 50% that goes to the joiner + let total_lamports = **bet_vault.to_account_info().lamports.borrow(); + let joiner_share = total_lamports / 2; - **joiner_account.try_borrow_mut_lamports()? += joiner_share; - - msg!("Sent {} lamports to joiner {}", joiner_share, bet_vault.joiner); + // Transfer joiner's share + **bet_vault.to_account_info().try_borrow_mut_lamports()? -= joiner_share; + + // We need to find the joiner account in the remaining accounts + let joiner_account = ctx.remaining_accounts + .iter() + .find(|account| account.key() == joiner) + .ok_or(BettingError::JoinerAccountNotProvided)?; + + **joiner_account.try_borrow_mut_lamports()? += joiner_share; + + msg!("Sent {} lamports to joiner {}", joiner_share, joiner); + } else { + // Handle SPL token refund + let bet_vault_token_account = ctx.accounts.bet_vault_token_account.as_ref().ok_or(BettingError::BetNotFilled)?; + let joiner_token_account = ctx.accounts.joiner_token_account.as_ref().ok_or(BettingError::JoinerAccountNotProvided)?; + + let token_account = TokenAccount::try_deserialize(&mut &bet_vault_token_account.data.borrow()[..])?; + let total_tokens = token_account.amount; + let joiner_share = total_tokens / 2; + + let cpi_accounts = Transfer { + from: bet_vault_token_account.to_account_info(), + to: joiner_token_account.to_account_info(), + authority: ctx.accounts.bet_vault.to_account_info(), + }; + let cpi_ctx = CpiContext::new( + ctx.accounts.token_program.to_account_info(), + cpi_accounts, + ); + token::transfer(cpi_ctx, joiner_share)?; + + msg!("Sent {} tokens to joiner {}", joiner_share, joiner); + } } // The remaining balance will go to the owner through the close=owner directive let bets_list = &mut ctx.accounts.bets_list; // Remove the bet_vault public key from the list - bets_list.bets.retain(|&bet| bet != bet_vault.key()); + bets_list.bets.retain(|&bet| bet != bet_vault_key); - msg!("Bet {} Refunded by {}", bet_vault.key(), owner); + msg!("Bet {} Refunded by {}", bet_vault_key, owner); Ok(()) } - #[derive(Accounts)] pub struct RefundBet<'info> { #[account(mut)] pub bets_list: Account<'info, BetsList>, - #[account(mut, close=owner)] + #[account(mut, close = owner)] pub bet_vault: Account<'info, BetVault>, #[account(mut)] @@ -54,5 +82,15 @@ pub struct RefundBet<'info> { #[account(mut)] pub payer: Signer<'info>, - pub system_program: Program<'info, System> + + pub system_program: Program<'info, System>, + + // SPL Token accounts + #[account(mut)] + pub bet_vault_token_account: Option>, + + #[account(mut)] + pub joiner_token_account: Option>, + + pub token_program: Program<'info, Token>, } diff --git a/programs/bets/src/lib.rs b/programs/bets/src/lib.rs index d73d463..0ade9f7 100644 --- a/programs/bets/src/lib.rs +++ b/programs/bets/src/lib.rs @@ -19,19 +19,37 @@ pub mod bets { initialize_bets_list::init(ctx) } - pub fn create_bet(ctx: Context, wager:u64,user_id:String, game_id:String, nonce:u64)-> Result<()>{ - create_bet::create(ctx, wager, user_id, game_id,nonce) + pub fn create_bet( + ctx: Context, + wager: u64, + user_id: String, + game_id: String, + nonce: u64, + is_native_sol: bool, + token_mint: Option, + token_decimals: u8, + ) -> Result<()> { + create_bet::create( + ctx, + wager, + user_id, + game_id, + nonce, + is_native_sol, + token_mint, + token_decimals + ) } - pub fn join_bet(ctx: Context,user_id:String, game_id:String) -> Result<()>{ - join_bet::join(ctx,user_id, game_id) + pub fn join_bet(ctx: Context, user_id: String, game_id: String) -> Result<()> { + join_bet::join(ctx, user_id, game_id) } - pub fn close_bet(ctx:Context, winner:Pubkey, userid:String)->Result<()> { + pub fn close_bet(ctx: Context, winner: Pubkey, userid: String) -> Result<()> { close_bet::close(ctx, winner, userid) } - pub fn refund_bet(ctx:Context, owner:Pubkey)->Result<()>{ + pub fn refund_bet(ctx: Context, owner: Pubkey) -> Result<()> { refund_bet::refund(ctx, owner) } } diff --git a/programs/bets/src/state/bet_vault.rs b/programs/bets/src/state/bet_vault.rs index 8fa2db7..fd2607f 100644 --- a/programs/bets/src/state/bet_vault.rs +++ b/programs/bets/src/state/bet_vault.rs @@ -12,5 +12,8 @@ pub struct BetVault { pub joiner: Pubkey, #[max_len(40)] pub joiner_id: String, - pub wager:u64 + pub wager: u64, + pub is_native_sol: bool, + pub token_mint: Option, + pub token_decimals: u8 } \ No newline at end of file diff --git a/tests/bets.ts b/tests/bets.ts index edb5c99..31ffb46 100644 --- a/tests/bets.ts +++ b/tests/bets.ts @@ -1,16 +1,416 @@ import * as anchor from "@coral-xyz/anchor"; import { Program } from "@coral-xyz/anchor"; import { Bets } from "../target/types/bets"; +import { PublicKey, SystemProgram, Keypair, LAMPORTS_PER_SOL } from "@solana/web3.js"; +import { TOKEN_PROGRAM_ID, createMint, createAccount, mintTo, getAccount } from "@solana/spl-token"; +import { assert } from "chai"; describe("bets", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.env()); + // Configure the client to use the local cluster + const provider = anchor.AnchorProvider.env(); + anchor.setProvider(provider); const program = anchor.workspace.Bets as Program; - it("Is initialized!", async () => { - // Add your test here. - const tx = await program.methods.initialize().rpc(); - console.log("Your transaction signature", tx); + // Test accounts + const owner = Keypair.generate(); + const joiner = Keypair.generate(); + const feeWallet = Keypair.generate(); + const ownerReferrer = Keypair.generate(); + const joinerReferrer = Keypair.generate(); + + // SPL Token accounts + let tokenMint: PublicKey; + let ownerTokenAccount: PublicKey; + let joinerTokenAccount: PublicKey; + let betVaultTokenAccount: PublicKey; + let feeWalletTokenAccount: PublicKey; + let ownerReferrerTokenAccount: PublicKey; + let joinerReferrerTokenAccount: PublicKey; + + // Program state accounts + let betsList: PublicKey; + let betVault: PublicKey; + + const gameId = "test-game-1"; + const ownerId = "owner123"; + const joinerId = "joiner456"; + const wager = new anchor.BN(LAMPORTS_PER_SOL); // 1 SOL or 1 token + const tokenDecimals = 9; + + before(async () => { + // Airdrop SOL to test accounts + await provider.connection.requestAirdrop(owner.publicKey, 10 * LAMPORTS_PER_SOL); + await provider.connection.requestAirdrop(joiner.publicKey, 10 * LAMPORTS_PER_SOL); + await provider.connection.requestAirdrop(feeWallet.publicKey, 10 * LAMPORTS_PER_SOL); + await provider.connection.requestAirdrop(ownerReferrer.publicKey, 10 * LAMPORTS_PER_SOL); + await provider.connection.requestAirdrop(joinerReferrer.publicKey, 10 * LAMPORTS_PER_SOL); + + // Create SPL token mint + tokenMint = await createMint( + provider.connection, + owner, + owner.publicKey, + null, + tokenDecimals + ); + + // Create token accounts + ownerTokenAccount = await createAccount( + provider.connection, + owner, + tokenMint, + owner.publicKey + ); + + joinerTokenAccount = await createAccount( + provider.connection, + joiner, + tokenMint, + joiner.publicKey + ); + + betVaultTokenAccount = await createAccount( + provider.connection, + owner, + tokenMint, + owner.publicKey + ); + + feeWalletTokenAccount = await createAccount( + provider.connection, + feeWallet, + tokenMint, + feeWallet.publicKey + ); + + ownerReferrerTokenAccount = await createAccount( + provider.connection, + ownerReferrer, + tokenMint, + ownerReferrer.publicKey + ); + + joinerReferrerTokenAccount = await createAccount( + provider.connection, + joinerReferrer, + tokenMint, + joinerReferrer.publicKey + ); + + // Mint tokens to owner and joiner + await mintTo( + provider.connection, + owner, + tokenMint, + ownerTokenAccount, + owner, + 100 * LAMPORTS_PER_SOL + ); + + await mintTo( + provider.connection, + owner, + tokenMint, + joinerTokenAccount, + owner, + 100 * LAMPORTS_PER_SOL + ); + }); + + it("Initializes the bets list", async () => { + [betsList] = PublicKey.findProgramAddressSync( + [Buffer.from("bets_list")], + program.programId + ); + + await program.methods + .initialize() + .accounts({ + betsList, + payer: provider.wallet.publicKey, + systemProgram: SystemProgram.programId, + }) + .rpc(); + + const betsListAccount = await program.account.betsList.fetch(betsList); + assert.ok(betsListAccount.bets.length === 0); + }); + + describe("Native SOL bets", () => { + it("Creates a bet with native SOL", async () => { + const nonce = new anchor.BN(Date.now()); + [betVault] = PublicKey.findProgramAddressSync( + [ + Buffer.from("bet_vault"), + owner.publicKey.toBuffer(), + Buffer.from(gameId), + nonce.toArrayLike(Buffer, "le", 8), + ], + program.programId + ); + + await program.methods + .createBet( + wager, + ownerId, + gameId, + nonce, + true, // isNativeSol + null, // tokenMint + 0 // tokenDecimals + ) + .accounts({ + payer: owner.publicKey, + betsList, + betVault, + systemProgram: SystemProgram.programId, + }) + .signers([owner]) + .rpc(); + + const betVaultAccount = await program.account.betVault.fetch(betVault); + assert.ok(betVaultAccount.owner.equals(owner.publicKey)); + assert.ok(betVaultAccount.ownerId === ownerId); + assert.ok(betVaultAccount.gameId === gameId); + assert.ok(betVaultAccount.wager.eq(wager)); + assert.ok(betVaultAccount.isNativeSol === true); + }); + + it("Joins a bet with native SOL", async () => { + await program.methods + .joinBet(joinerId, gameId) + .accounts({ + betVault, + payer: joiner.publicKey, + systemProgram: SystemProgram.programId, + }) + .signers([joiner]) + .rpc(); + + const betVaultAccount = await program.account.betVault.fetch(betVault); + assert.ok(betVaultAccount.joiner.equals(joiner.publicKey)); + assert.ok(betVaultAccount.joinerId === joinerId); + }); + + it("Closes a bet with native SOL", async () => { + await program.methods + .closeBet(owner.publicKey, ownerId) + .accounts({ + betsList, + betVault, + feeWallet: feeWallet.publicKey, + winner: owner.publicKey, + payer: owner.publicKey, + ownerReferrer: ownerReferrer.publicKey, + joinerReferrer: joinerReferrer.publicKey, + systemProgram: SystemProgram.programId, + }) + .signers([owner]) + .rpc(); + + // Verify the bet was removed from the list + const betsListAccount = await program.account.betsList.fetch(betsList); + assert.ok(!betsListAccount.bets.some(bet => bet.equals(betVault))); + }); + }); + + describe("SPL Token bets", () => { + it("Creates a bet with SPL tokens", async () => { + const nonce = new anchor.BN(Date.now()); + [betVault] = PublicKey.findProgramAddressSync( + [ + Buffer.from("bet_vault"), + owner.publicKey.toBuffer(), + Buffer.from(gameId), + nonce.toArrayLike(Buffer, "le", 8), + ], + program.programId + ); + + await program.methods + .createBet( + wager, + ownerId, + gameId, + nonce, + false, // isNativeSol + tokenMint, + tokenDecimals + ) + .accounts({ + payer: owner.publicKey, + betsList, + betVault, + systemProgram: SystemProgram.programId, + payerTokenAccount: ownerTokenAccount, + betVaultTokenAccount, + tokenProgram: TOKEN_PROGRAM_ID, + }) + .signers([owner]) + .rpc(); + + const betVaultAccount = await program.account.betVault.fetch(betVault); + assert.ok(betVaultAccount.owner.equals(owner.publicKey)); + assert.ok(betVaultAccount.ownerId === ownerId); + assert.ok(betVaultAccount.gameId === gameId); + assert.ok(betVaultAccount.wager.eq(wager)); + assert.ok(betVaultAccount.isNativeSol === false); + assert.ok(betVaultAccount.tokenMint.equals(tokenMint)); + assert.ok(betVaultAccount.tokenDecimals === tokenDecimals); + }); + + it("Joins a bet with SPL tokens", async () => { + await program.methods + .joinBet(joinerId, gameId) + .accounts({ + betVault, + payer: joiner.publicKey, + systemProgram: SystemProgram.programId, + payerTokenAccount: joinerTokenAccount, + betVaultTokenAccount, + tokenProgram: TOKEN_PROGRAM_ID, + }) + .signers([joiner]) + .rpc(); + + const betVaultAccount = await program.account.betVault.fetch(betVault); + assert.ok(betVaultAccount.joiner.equals(joiner.publicKey)); + assert.ok(betVaultAccount.joinerId === joinerId); + }); + + it("Closes a bet with SPL tokens", async () => { + await program.methods + .closeBet(owner.publicKey, ownerId) + .accounts({ + betsList, + betVault, + feeWallet: feeWallet.publicKey, + winner: owner.publicKey, + payer: owner.publicKey, + ownerReferrer: ownerReferrer.publicKey, + joinerReferrer: joinerReferrer.publicKey, + systemProgram: SystemProgram.programId, + betVaultTokenAccount, + feeWalletTokenAccount, + ownerReferrerTokenAccount, + joinerReferrerTokenAccount, + tokenProgram: TOKEN_PROGRAM_ID, + }) + .signers([owner]) + .rpc(); + + // Verify the bet was removed from the list + const betsListAccount = await program.account.betsList.fetch(betsList); + assert.ok(!betsListAccount.bets.some(bet => bet.equals(betVault))); + }); + }); + + describe("Refund functionality", () => { + it("Refunds a native SOL bet", async () => { + // Create a new bet + const nonce = new anchor.BN(Date.now()); + [betVault] = PublicKey.findProgramAddressSync( + [ + Buffer.from("bet_vault"), + owner.publicKey.toBuffer(), + Buffer.from(gameId), + nonce.toArrayLike(Buffer, "le", 8), + ], + program.programId + ); + + await program.methods + .createBet( + wager, + ownerId, + gameId, + nonce, + true, // isNativeSol + null, // tokenMint + 0 // tokenDecimals + ) + .accounts({ + payer: owner.publicKey, + betsList, + betVault, + systemProgram: SystemProgram.programId, + }) + .signers([owner]) + .rpc(); + + // Refund the bet + await program.methods + .refundBet(owner.publicKey) + .accounts({ + betsList, + betVault, + owner: owner.publicKey, + payer: owner.publicKey, + systemProgram: SystemProgram.programId, + }) + .signers([owner]) + .rpc(); + + // Verify the bet was removed from the list + const betsListAccount = await program.account.betsList.fetch(betsList); + assert.ok(!betsListAccount.bets.some(bet => bet.equals(betVault))); + }); + + it("Refunds an SPL token bet", async () => { + // Create a new bet + const nonce = new anchor.BN(Date.now()); + [betVault] = PublicKey.findProgramAddressSync( + [ + Buffer.from("bet_vault"), + owner.publicKey.toBuffer(), + Buffer.from(gameId), + nonce.toArrayLike(Buffer, "le", 8), + ], + program.programId + ); + + await program.methods + .createBet( + wager, + ownerId, + gameId, + nonce, + false, // isNativeSol + tokenMint, + tokenDecimals + ) + .accounts({ + payer: owner.publicKey, + betsList, + betVault, + systemProgram: SystemProgram.programId, + payerTokenAccount: ownerTokenAccount, + betVaultTokenAccount, + tokenProgram: TOKEN_PROGRAM_ID, + }) + .signers([owner]) + .rpc(); + + // Refund the bet + await program.methods + .refundBet(owner.publicKey) + .accounts({ + betsList, + betVault, + owner: owner.publicKey, + payer: owner.publicKey, + systemProgram: SystemProgram.programId, + betVaultTokenAccount, + joinerTokenAccount, + tokenProgram: TOKEN_PROGRAM_ID, + }) + .signers([owner]) + .rpc(); + + // Verify the bet was removed from the list + const betsListAccount = await program.account.betsList.fetch(betsList); + assert.ok(!betsListAccount.bets.some(bet => bet.equals(betVault))); + }); }); });