token support

This commit is contained in:
Sewmina Dilshan 2025-05-16 14:32:46 +05:30
parent 8243e7c9de
commit 079cf49de7
9 changed files with 1437 additions and 100 deletions

751
Cargo.lock generated
View File

@ -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"

View File

@ -18,3 +18,4 @@ idl-build = ["anchor-lang/idl-build"]
[dependencies]
anchor-lang = "0.30.1"
anchor-spl = "0.30.1"

View File

@ -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<CloseBet>, winner:Pubkey, userid:String)->Result<()>{
pub fn close(ctx: Context<CloseBet>, 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<CloseBet>, 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<AccountInfo<'info>>,
pub system_program: Program<'info, System>
pub system_program: Program<'info, System>,
// SPL Token accounts
#[account(mut)]
pub bet_vault_token_account: Option<AccountInfo<'info>>,
#[account(mut)]
pub fee_wallet_token_account: Option<AccountInfo<'info>>,
#[account(mut)]
pub owner_referrer_token_account: Option<AccountInfo<'info>>,
#[account(mut)]
pub joiner_referrer_token_account: Option<AccountInfo<'info>>,
pub token_program: Program<'info, Token>,
}

View File

@ -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<CreateBet>, wager: u64, user_id:String, game_id:String, _nonce:u64) -> Result<()> {
pub fn create(
ctx: Context<CreateBet>,
wager: u64,
user_id: String,
game_id: String,
_nonce: u64,
is_native_sol: bool,
token_mint: Option<Pubkey>,
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<CreateBet>, 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<Pubkey>,
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<AccountInfo<'info>>,
#[account(mut)]
pub bet_vault_token_account: Option<AccountInfo<'info>>,
pub token_program: Program<'info, Token>,
}

View File

@ -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<JoinBet>,user_id:String, _game_id:String) ->Result<()>{
pub fn join(ctx: Context<JoinBet>, 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<AccountInfo<'info>>,
#[account(mut)]
pub bet_vault_token_account: Option<AccountInfo<'info>>,
pub token_program: Program<'info, Token>,
}

View File

@ -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<RefundBet>, owner: Pubkey) -> Result<()> {
@ -9,44 +10,71 @@ pub fn refund(ctx: Context<RefundBet>, 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<AccountInfo<'info>>,
#[account(mut)]
pub joiner_token_account: Option<AccountInfo<'info>>,
pub token_program: Program<'info, Token>,
}

View File

@ -19,19 +19,37 @@ pub mod bets {
initialize_bets_list::init(ctx)
}
pub fn create_bet(ctx: Context<CreateBet>, 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<CreateBet>,
wager: u64,
user_id: String,
game_id: String,
nonce: u64,
is_native_sol: bool,
token_mint: Option<Pubkey>,
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<JoinBet>,user_id:String, game_id:String) -> Result<()>{
join_bet::join(ctx,user_id, game_id)
pub fn join_bet(ctx: Context<JoinBet>, user_id: String, game_id: String) -> Result<()> {
join_bet::join(ctx, user_id, game_id)
}
pub fn close_bet(ctx:Context<CloseBet>, winner:Pubkey, userid:String)->Result<()> {
pub fn close_bet(ctx: Context<CloseBet>, winner: Pubkey, userid: String) -> Result<()> {
close_bet::close(ctx, winner, userid)
}
pub fn refund_bet(ctx:Context<RefundBet>, owner:Pubkey)->Result<()>{
pub fn refund_bet(ctx: Context<RefundBet>, owner: Pubkey) -> Result<()> {
refund_bet::refund(ctx, owner)
}
}

View File

@ -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<Pubkey>,
pub token_decimals: u8
}

View File

@ -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<Bets>;
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)));
});
});
});