From df491c63df84cd43458397aa06996188373e6fe7 Mon Sep 17 00:00:00 2001 From: crappyrules Date: Mon, 6 Oct 2025 13:31:01 -0400 Subject: [PATCH] Initial commit: Solana lottery program --- .dockerignore | 46 + .gitignore | 7 + Anchor.toml | 25 + Cargo.lock | 2898 ++++++++++++++++++++++++++++ Cargo.toml | 15 + DEPLOYMENT.md | 85 + DEVNET-STATUS.md | 101 + Dockerfile | 38 + app.js | 838 ++++++++ apple-touch-icon.png | Bin 0 -> 47334 bytes burn-all-tokens.sh | 87 + close-all-token-accounts.sh | 111 ++ config.json | 6 + deploy.sh | 35 + devnet-config.json | 7 + docker-compose.yml | 15 + favicon-16.png | Bin 0 -> 1600 bytes favicon-32.png | Bin 0 -> 642 bytes favicon.ico | Bin 0 -> 5430 bytes favicon.svg | 76 + index.html | 198 ++ init-program.js | 95 + lottery_program/.gitignore | 7 + lottery_program/.prettierignore | 7 + lottery_program/Anchor.toml | 19 + mint-keypair.json | 1 + nginx.conf | 38 + package-lock.json | 755 ++++++++ package.json | 16 + programs/lottery-simple/Cargo.toml | 21 + programs/lottery-simple/src/lib.rs | 457 +++++ send-to-burn.sh | 98 + styles.css | 119 ++ switch-to-mainnet.sh | 57 + test-devnet.sh | 86 + test-program.html | 289 +++ ticker-api.php | 79 + ticker-data.json | 17 + 38 files changed, 6749 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 Anchor.toml create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 DEPLOYMENT.md create mode 100644 DEVNET-STATUS.md create mode 100644 Dockerfile create mode 100644 app.js create mode 100644 apple-touch-icon.png create mode 100755 burn-all-tokens.sh create mode 100755 close-all-token-accounts.sh create mode 100644 config.json create mode 100755 deploy.sh create mode 100644 devnet-config.json create mode 100644 docker-compose.yml create mode 100644 favicon-16.png create mode 100644 favicon-32.png create mode 100644 favicon.ico create mode 100644 favicon.svg create mode 100644 index.html create mode 100644 init-program.js create mode 100644 lottery_program/.gitignore create mode 100644 lottery_program/.prettierignore create mode 100644 lottery_program/Anchor.toml create mode 100644 mint-keypair.json create mode 100644 nginx.conf create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 programs/lottery-simple/Cargo.toml create mode 100644 programs/lottery-simple/src/lib.rs create mode 100755 send-to-burn.sh create mode 100644 styles.css create mode 100755 switch-to-mainnet.sh create mode 100755 test-devnet.sh create mode 100644 test-program.html create mode 100644 ticker-api.php create mode 100644 ticker-data.json diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..5e23850 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,46 @@ +# Development files +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Anchor build artifacts +target/ +.anchor/ +Cargo.lock + +# Program files (not needed for frontend) +programs/ +migrations/ +tests/ +client/ + +# Development configs +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# IDE files +.vscode/ +.idea/ +*.swp +*.swo + +# OS files +.DS_Store +Thumbs.db + +# Git +.git/ +.gitignore + +# Build files +*.log +temp/ +tmp/ + +# Test files +test-*.js +*test.js \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2e0446b --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.anchor +.DS_Store +target +**/*.rs.bk +node_modules +test-ledger +.yarn diff --git a/Anchor.toml b/Anchor.toml new file mode 100644 index 0000000..30961e4 --- /dev/null +++ b/Anchor.toml @@ -0,0 +1,25 @@ +[toolchain] +anchor_version = "0.31.0" + +[features] +seeds = false +skip-lint = false + +[programs.localnet] +lottery_simple = "CQKiz5PKgoQjxNDkGowPWWXu8MJCm2HgonfYAtcFZX87" + +[programs.devnet] +lottery_simple = "8hcuEUBcuVBYyD53QPFQrRSWJjQy3UVURiqJpcuvgrTf" + +[programs.mainnet] +lottery_simple = "CQKiz5PKgoQjxNDkGowPWWXu8MJCm2HgonfYAtcFZX87" + +[registry] +url = "https://api.apr.dev" + +[provider] +cluster = "Devnet" +wallet = "~/.config/solana/id.json" + +[scripts] +test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..3e20cab --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2898 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm-siv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "polyval", + "subtle", + "zeroize", +] + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anchor-attribute-access-control" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f70fd141a4d18adf11253026b32504f885447048c7494faf5fa83b01af9c0cf" +dependencies = [ + "anchor-syn", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-account" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "715a261c57c7679581e06f07a74fa2af874ac30f86bd8ea07cca4a7e5388a064" +dependencies = [ + "anchor-syn", + "bs58", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-constant" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "730d6df8ae120321c5c25e0779e61789e4b70dc8297102248902022f286102e4" +dependencies = [ + "anchor-syn", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-error" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27e6e449cc3a37b2880b74dcafb8e5a17b954c0e58e376432d7adc646fb333ef" +dependencies = [ + "anchor-syn", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-event" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7710e4c54adf485affcd9be9adec5ef8846d9c71d7f31e16ba86ff9fc1dd49f" +dependencies = [ + "anchor-syn", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-program" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ecfd49b2aeadeb32f35262230db402abed76ce87e27562b34f61318b2ec83c" +dependencies = [ + "anchor-lang-idl", + "anchor-syn", + "anyhow", + "bs58", + "heck", + "proc-macro2", + "quote", + "serde_json", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-accounts" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be89d160793a88495af462a7010b3978e48e30a630c91de47ce2c1d3cb7a6149" +dependencies = [ + "anchor-syn", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-serde" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abc6ee78acb7bfe0c2dd2abc677aaa4789c0281a0c0ef01dbf6fe85e0fd9e6e4" +dependencies = [ + "anchor-syn", + "borsh-derive-internal", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-space" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134a01c0703f6fd355a0e472c033f6f3e41fac1ef6e370b20c50f4c8d022cea7" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-lang" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6bab117055905e930f762c196e08f861f8dfe7241b92cee46677a3b15561a0a" +dependencies = [ + "anchor-attribute-access-control", + "anchor-attribute-account", + "anchor-attribute-constant", + "anchor-attribute-error", + "anchor-attribute-event", + "anchor-attribute-program", + "anchor-derive-accounts", + "anchor-derive-serde", + "anchor-derive-space", + "anchor-lang-idl", + "base64 0.21.7", + "bincode", + "borsh 0.10.4", + "bytemuck", + "solana-program", + "thiserror 1.0.69", +] + +[[package]] +name = "anchor-lang-idl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e8599d21995f68e296265aa5ab0c3cef582fd58afec014d01bd0bce18a4418" +dependencies = [ + "anchor-lang-idl-spec", + "anyhow", + "heck", + "regex", + "serde", + "serde_json", + "sha2 0.10.9", +] + +[[package]] +name = "anchor-lang-idl-spec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bdf143115440fe621bdac3a29a1f7472e09f6cd82b2aa569429a0c13f103838" +dependencies = [ + "anyhow", + "serde", +] + +[[package]] +name = "anchor-spl" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c08cb5d762c0694f74bd02c9a5b04ea53cefc496e2c27b3234acffca5cd076b" +dependencies = [ + "anchor-lang", + "spl-associated-token-account", + "spl-pod", + "spl-token", + "spl-token-2022", + "spl-token-group-interface", + "spl-token-metadata-interface", +] + +[[package]] +name = "anchor-syn" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dc7a6d90cc643df0ed2744862cdf180587d1e5d28936538c18fc8908489ed67" +dependencies = [ + "anyhow", + "bs58", + "cargo_toml", + "heck", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2 0.10.9", + "syn 1.0.109", + "thiserror 1.0.69", +] + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "2.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" + +[[package]] +name = "blake3" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "digest 0.10.7", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "borsh" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115e54d64eb62cdebad391c19efc9dce4981c690c85a33a12199d99bb9546fee" +dependencies = [ + "borsh-derive 0.10.4", + "hashbrown 0.13.2", +] + +[[package]] +name = "borsh" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" +dependencies = [ + "borsh-derive 1.5.7", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831213f80d9423998dd696e2c5345aba6be7a0bd8cd19e31c5243e13df1cef89" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate 0.1.5", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd1d3c0c2f5833f22386f252fe8ed005c7f59fdcddeef025c01b4c3b9fd9ac3" +dependencies = [ + "once_cell", + "proc-macro-crate 3.4.0", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65d6ba50644c98714aa2a70d13d7df3cd75cd2b523a2b452bf010443800976b3" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276691d96f063427be83e6692b86148e488ebba9f48f77788724ca027ba3b6d4" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "bv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8834bb1d8ee5dc048ee3124f2c7c1afcc6bc9aed03f11e9dfd8c69470a5db340" +dependencies = [ + "feature-probe", + "serde", +] + +[[package]] +name = "bytemuck" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cargo_toml" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a98356df42a2eb1bd8f1793ae4ee4de48e384dd974ce5eac8eee802edb7492be" +dependencies = [ + "serde", + "toml 0.8.23", +] + +[[package]] +name = "cc" +version = "1.2.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d05d92f4b1fd76aad469d46cdd858ca761576082cd37df81416691e50199fb" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "console_log" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89f72f65e8501878b8a004d5a1afb780987e2ce2b4532c562e367a72c57499f" +dependencies = [ + "log", + "web-sys", +] + +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rand_core 0.6.4", + "rustc_version", + "serde", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "derivation-path" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5c37193a1db1d8ed868c03ec7b152175f26160a5b740e5e484143877e0adf0" + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", + "subtle", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "feature-probe" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-msvc-tools" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0399f9d26e5191ce32c498bebd31e7a3ceabc2745f0ac54af3f335126c3f24b3" + +[[package]] +name = "five8" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75b8549488b4715defcb0d8a8a1c1c76a80661b5fa106b4ca0e7fce59d7d875" +dependencies = [ + "five8_core", +] + +[[package]] +name = "five8_const" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26dec3da8bc3ef08f2c04f61eab298c3ab334523e55f076354d6d6f613799a7b" +dependencies = [ + "five8_core", +] + +[[package]] +name = "five8_core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2551bf44bc5f776c15044b9b94153a00198be06743e262afaaa61f11ac7523a5" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[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 = "indexmap" +version = "2.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +dependencies = [ + "equivalent", + "hashbrown 0.16.0", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.176" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" + +[[package]] +name = "libsecp256k1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9d220bc1feda2ac231cb78c3d26f27676b8cf82c96971f7aeef3d0cf2797c73" +dependencies = [ + "arrayref", + "base64 0.12.3", + "digest 0.9.0", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.7.3", + "serde", + "sha2 0.9.9", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f6ab710cec28cef759c5f18671a27dae2a5f952cdaaee1d8e2908cb2478a80" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccab96b584d38fac86a83f07e659f0deafd0253dc096dab5a36d53efe653c5c3" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67abfe149395e3aa1c48a2beb32b068e2334402df8181f818d3aee2b304c4f5d" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +dependencies = [ + "proc-macro-crate 3.4.0", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "lottery-simple" +version = "0.1.0" +dependencies = [ + "anchor-lang", + "anchor-spl", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml 0.5.11", +] + +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit 0.23.6", +] + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +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 = "quote" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "solana-account" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f949fe4edaeaea78c844023bfc1c898e0b1f5a100f8a8d2d0f85d0a7b090258" +dependencies = [ + "solana-account-info", + "solana-clock", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", +] + +[[package]] +name = "solana-account-info" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8f5152a288ef1912300fc6efa6c2d1f9bb55d9398eb6c72326360b8063987da" +dependencies = [ + "bincode", + "serde", + "solana-program-error", + "solana-program-memory", + "solana-pubkey", +] + +[[package]] +name = "solana-address-lookup-table-interface" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1673f67efe870b64a65cb39e6194be5b26527691ce5922909939961a6e6b395" +dependencies = [ + "bincode", + "bytemuck", + "serde", + "serde_derive", + "solana-clock", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-slot-hashes", +] + +[[package]] +name = "solana-atomic-u64" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52e52720efe60465b052b9e7445a01c17550666beec855cce66f44766697bc2" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "solana-big-mod-exp" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75db7f2bbac3e62cfd139065d15bcda9e2428883ba61fc8d27ccb251081e7567" +dependencies = [ + "num-bigint", + "num-traits", + "solana-define-syscall", +] + +[[package]] +name = "solana-bincode" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19a3787b8cf9c9fe3dd360800e8b70982b9e5a8af9e11c354b6665dd4a003adc" +dependencies = [ + "bincode", + "serde", + "solana-instruction", +] + +[[package]] +name = "solana-blake3-hasher" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a0801e25a1b31a14494fc80882a036be0ffd290efc4c2d640bfcca120a4672" +dependencies = [ + "blake3", + "solana-define-syscall", + "solana-hash", + "solana-sanitize", +] + +[[package]] +name = "solana-borsh" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "718333bcd0a1a7aed6655aa66bef8d7fb047944922b2d3a18f49cbc13e73d004" +dependencies = [ + "borsh 0.10.4", + "borsh 1.5.7", +] + +[[package]] +name = "solana-clock" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb482ab70fced82ad3d7d3d87be33d466a3498eb8aa856434ff3c0dfc2e2e31" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-cpi" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dc71126edddc2ba014622fc32d0f5e2e78ec6c5a1e0eb511b85618c09e9ea11" +dependencies = [ + "solana-account-info", + "solana-define-syscall", + "solana-instruction", + "solana-program-error", + "solana-pubkey", + "solana-stable-layout", +] + +[[package]] +name = "solana-curve25519" +version = "2.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa77936de1910002e7ad5817e38c3990402c2d8e92517cdd736df51485c76d88" +dependencies = [ + "bytemuck", + "bytemuck_derive", + "curve25519-dalek", + "solana-define-syscall", + "subtle", + "thiserror 2.0.17", +] + +[[package]] +name = "solana-decode-error" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c781686a18db2f942e70913f7ca15dc120ec38dcab42ff7557db2c70c625a35" +dependencies = [ + "num-traits", +] + +[[package]] +name = "solana-define-syscall" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ae3e2abcf541c8122eafe9a625d4d194b4023c20adde1e251f94e056bb1aee2" + +[[package]] +name = "solana-derivation-path" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "939756d798b25c5ec3cca10e06212bdca3b1443cb9bb740a38124f58b258737b" +dependencies = [ + "derivation-path", + "qstring", + "uriparse", +] + +[[package]] +name = "solana-epoch-rewards" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b575d3dd323b9ea10bb6fe89bf6bf93e249b215ba8ed7f68f1a3633f384db7" +dependencies = [ + "serde", + "serde_derive", + "solana-hash", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-epoch-schedule" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fce071fbddecc55d727b1d7ed16a629afe4f6e4c217bc8d00af3b785f6f67ed" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-example-mocks" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84461d56cbb8bb8d539347151e0525b53910102e4bced875d49d5139708e39d3" +dependencies = [ + "serde", + "serde_derive", + "solana-address-lookup-table-interface", + "solana-clock", + "solana-hash", + "solana-instruction", + "solana-keccak-hasher", + "solana-message", + "solana-nonce", + "solana-pubkey", + "solana-sdk-ids", + "solana-system-interface", + "thiserror 2.0.17", +] + +[[package]] +name = "solana-feature-gate-interface" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f5c5382b449e8e4e3016fb05e418c53d57782d8b5c30aa372fc265654b956d" +dependencies = [ + "bincode", + "serde", + "serde_derive", + "solana-account", + "solana-account-info", + "solana-instruction", + "solana-program-error", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-system-interface", +] + +[[package]] +name = "solana-fee-calculator" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89bc408da0fb3812bc3008189d148b4d3e08252c79ad810b245482a3f70cd8d" +dependencies = [ + "log", + "serde", + "serde_derive", +] + +[[package]] +name = "solana-hash" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b96e9f0300fa287b545613f007dfe20043d7812bee255f418c1eb649c93b63" +dependencies = [ + "borsh 1.5.7", + "bytemuck", + "bytemuck_derive", + "five8", + "js-sys", + "serde", + "serde_derive", + "solana-atomic-u64", + "solana-sanitize", + "wasm-bindgen", +] + +[[package]] +name = "solana-instruction" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47298e2ce82876b64f71e9d13a46bc4b9056194e7f9937ad3084385befa50885" +dependencies = [ + "bincode", + "borsh 1.5.7", + "getrandom 0.2.16", + "js-sys", + "num-traits", + "serde", + "serde_derive", + "solana-define-syscall", + "solana-pubkey", + "wasm-bindgen", +] + +[[package]] +name = "solana-instructions-sysvar" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0e85a6fad5c2d0c4f5b91d34b8ca47118fc593af706e523cdbedf846a954f57" +dependencies = [ + "bitflags", + "solana-account-info", + "solana-instruction", + "solana-program-error", + "solana-pubkey", + "solana-sanitize", + "solana-sdk-ids", + "solana-serialize-utils", + "solana-sysvar-id", +] + +[[package]] +name = "solana-keccak-hasher" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7aeb957fbd42a451b99235df4942d96db7ef678e8d5061ef34c9b34cae12f79" +dependencies = [ + "sha3", + "solana-define-syscall", + "solana-hash", + "solana-sanitize", +] + +[[package]] +name = "solana-last-restart-slot" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a6360ac2fdc72e7463565cd256eedcf10d7ef0c28a1249d261ec168c1b55cdd" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-loader-v2-interface" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8ab08006dad78ae7cd30df8eea0539e207d08d91eaefb3e1d49a446e1c49654" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", +] + +[[package]] +name = "solana-loader-v3-interface" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f7162a05b8b0773156b443bccd674ea78bb9aa406325b467ea78c06c99a63a2" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-system-interface", +] + +[[package]] +name = "solana-loader-v4-interface" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "706a777242f1f39a83e2a96a2a6cb034cb41169c6ecbee2cf09cb873d9659e7e" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-system-interface", +] + +[[package]] +name = "solana-message" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1796aabce376ff74bf89b78d268fa5e683d7d7a96a0a4e4813ec34de49d5314b" +dependencies = [ + "bincode", + "blake3", + "lazy_static", + "serde", + "serde_derive", + "solana-bincode", + "solana-hash", + "solana-instruction", + "solana-pubkey", + "solana-sanitize", + "solana-sdk-ids", + "solana-short-vec", + "solana-system-interface", + "solana-transaction-error", + "wasm-bindgen", +] + +[[package]] +name = "solana-msg" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36a1a14399afaabc2781a1db09cb14ee4cc4ee5c7a5a3cfcc601811379a8092" +dependencies = [ + "solana-define-syscall", +] + +[[package]] +name = "solana-native-token" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61515b880c36974053dd499c0510066783f0cc6ac17def0c7ef2a244874cf4a9" + +[[package]] +name = "solana-nonce" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703e22eb185537e06204a5bd9d509b948f0066f2d1d814a6f475dafb3ddf1325" +dependencies = [ + "serde", + "serde_derive", + "solana-fee-calculator", + "solana-hash", + "solana-pubkey", + "solana-sha256-hasher", +] + +[[package]] +name = "solana-program" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98eca145bd3545e2fbb07166e895370576e47a00a7d824e325390d33bf467210" +dependencies = [ + "bincode", + "blake3", + "borsh 0.10.4", + "borsh 1.5.7", + "bs58", + "bytemuck", + "console_error_panic_hook", + "console_log", + "getrandom 0.2.16", + "lazy_static", + "log", + "memoffset", + "num-bigint", + "num-derive", + "num-traits", + "rand 0.8.5", + "serde", + "serde_bytes", + "serde_derive", + "solana-account-info", + "solana-address-lookup-table-interface", + "solana-atomic-u64", + "solana-big-mod-exp", + "solana-bincode", + "solana-blake3-hasher", + "solana-borsh", + "solana-clock", + "solana-cpi", + "solana-decode-error", + "solana-define-syscall", + "solana-epoch-rewards", + "solana-epoch-schedule", + "solana-example-mocks", + "solana-feature-gate-interface", + "solana-fee-calculator", + "solana-hash", + "solana-instruction", + "solana-instructions-sysvar", + "solana-keccak-hasher", + "solana-last-restart-slot", + "solana-loader-v2-interface", + "solana-loader-v3-interface", + "solana-loader-v4-interface", + "solana-message", + "solana-msg", + "solana-native-token", + "solana-nonce", + "solana-program-entrypoint", + "solana-program-error", + "solana-program-memory", + "solana-program-option", + "solana-program-pack", + "solana-pubkey", + "solana-rent", + "solana-sanitize", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-secp256k1-recover", + "solana-serde-varint", + "solana-serialize-utils", + "solana-sha256-hasher", + "solana-short-vec", + "solana-slot-hashes", + "solana-slot-history", + "solana-stable-layout", + "solana-stake-interface", + "solana-system-interface", + "solana-sysvar", + "solana-sysvar-id", + "solana-vote-interface", + "thiserror 2.0.17", + "wasm-bindgen", +] + +[[package]] +name = "solana-program-entrypoint" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32ce041b1a0ed275290a5008ee1a4a6c48f5054c8a3d78d313c08958a06aedbd" +dependencies = [ + "solana-account-info", + "solana-msg", + "solana-program-error", + "solana-pubkey", +] + +[[package]] +name = "solana-program-error" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ee2e0217d642e2ea4bee237f37bd61bb02aec60da3647c48ff88f6556ade775" +dependencies = [ + "borsh 1.5.7", + "num-traits", + "serde", + "serde_derive", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-pubkey", +] + +[[package]] +name = "solana-program-memory" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a5426090c6f3fd6cfdc10685322fede9ca8e5af43cd6a59e98bfe4e91671712" +dependencies = [ + "solana-define-syscall", +] + +[[package]] +name = "solana-program-option" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc677a2e9bc616eda6dbdab834d463372b92848b2bfe4a1ed4e4b4adba3397d0" + +[[package]] +name = "solana-program-pack" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "319f0ef15e6e12dc37c597faccb7d62525a509fec5f6975ecb9419efddeb277b" +dependencies = [ + "solana-program-error", +] + +[[package]] +name = "solana-pubkey" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b62adb9c3261a052ca1f999398c388f1daf558a1b492f60a6d9e64857db4ff1" +dependencies = [ + "borsh 0.10.4", + "borsh 1.5.7", + "bytemuck", + "bytemuck_derive", + "curve25519-dalek", + "five8", + "five8_const", + "getrandom 0.2.16", + "js-sys", + "num-traits", + "serde", + "serde_derive", + "solana-atomic-u64", + "solana-decode-error", + "solana-define-syscall", + "solana-sanitize", + "solana-sha256-hasher", + "wasm-bindgen", +] + +[[package]] +name = "solana-rent" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1aea8fdea9de98ca6e8c2da5827707fb3842833521b528a713810ca685d2480" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-sanitize" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61f1bc1357b8188d9c4a3af3fc55276e56987265eb7ad073ae6f8180ee54cecf" + +[[package]] +name = "solana-sdk-ids" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5d8b9cc68d5c88b062a33e23a6466722467dde0035152d8fb1afbcdf350a5f" +dependencies = [ + "solana-pubkey", +] + +[[package]] +name = "solana-sdk-macro" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86280da8b99d03560f6ab5aca9de2e38805681df34e0bb8f238e69b29433b9df" +dependencies = [ + "bs58", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "solana-secp256k1-recover" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baa3120b6cdaa270f39444f5093a90a7b03d296d362878f7a6991d6de3bbe496" +dependencies = [ + "libsecp256k1", + "solana-define-syscall", + "thiserror 2.0.17", +] + +[[package]] +name = "solana-security-txt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "468aa43b7edb1f9b7b7b686d5c3aeb6630dc1708e86e31343499dd5c4d775183" + +[[package]] +name = "solana-seed-derivable" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beb82b5adb266c6ea90e5cf3967235644848eac476c5a1f2f9283a143b7c97f" +dependencies = [ + "solana-derivation-path", +] + +[[package]] +name = "solana-seed-phrase" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36187af2324f079f65a675ec22b31c24919cb4ac22c79472e85d819db9bbbc15" +dependencies = [ + "hmac", + "pbkdf2", + "sha2 0.10.9", +] + +[[package]] +name = "solana-serde-varint" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a7e155eba458ecfb0107b98236088c3764a09ddf0201ec29e52a0be40857113" +dependencies = [ + "serde", +] + +[[package]] +name = "solana-serialize-utils" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "817a284b63197d2b27afdba829c5ab34231da4a9b4e763466a003c40ca4f535e" +dependencies = [ + "solana-instruction", + "solana-pubkey", + "solana-sanitize", +] + +[[package]] +name = "solana-sha256-hasher" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa3feb32c28765f6aa1ce8f3feac30936f16c5c3f7eb73d63a5b8f6f8ecdc44" +dependencies = [ + "sha2 0.10.9", + "solana-define-syscall", + "solana-hash", +] + +[[package]] +name = "solana-short-vec" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c54c66f19b9766a56fa0057d060de8378676cb64987533fa088861858fc5a69" +dependencies = [ + "serde", +] + +[[package]] +name = "solana-signature" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c8ec8e657aecfc187522fc67495142c12f35e55ddeca8698edbb738b8dbd8c" +dependencies = [ + "five8", + "solana-sanitize", +] + +[[package]] +name = "solana-signer" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c41991508a4b02f021c1342ba00bcfa098630b213726ceadc7cb032e051975b" +dependencies = [ + "solana-pubkey", + "solana-signature", + "solana-transaction-error", +] + +[[package]] +name = "solana-slot-hashes" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8691982114513763e88d04094c9caa0376b867a29577939011331134c301ce" +dependencies = [ + "serde", + "serde_derive", + "solana-hash", + "solana-sdk-ids", + "solana-sysvar-id", +] + +[[package]] +name = "solana-slot-history" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97ccc1b2067ca22754d5283afb2b0126d61eae734fc616d23871b0943b0d935e" +dependencies = [ + "bv", + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sysvar-id", +] + +[[package]] +name = "solana-stable-layout" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f14f7d02af8f2bc1b5efeeae71bc1c2b7f0f65cd75bcc7d8180f2c762a57f54" +dependencies = [ + "solana-instruction", + "solana-pubkey", +] + +[[package]] +name = "solana-stake-interface" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5269e89fde216b4d7e1d1739cf5303f8398a1ff372a81232abbee80e554a838c" +dependencies = [ + "borsh 0.10.4", + "borsh 1.5.7", + "num-traits", + "serde", + "serde_derive", + "solana-clock", + "solana-cpi", + "solana-decode-error", + "solana-instruction", + "solana-program-error", + "solana-pubkey", + "solana-system-interface", + "solana-sysvar-id", +] + +[[package]] +name = "solana-system-interface" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d7c18cb1a91c6be5f5a8ac9276a1d7c737e39a21beba9ea710ab4b9c63bc90" +dependencies = [ + "js-sys", + "num-traits", + "serde", + "serde_derive", + "solana-decode-error", + "solana-instruction", + "solana-pubkey", + "wasm-bindgen", +] + +[[package]] +name = "solana-sysvar" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8c3595f95069f3d90f275bb9bd235a1973c4d059028b0a7f81baca2703815db" +dependencies = [ + "base64 0.22.1", + "bincode", + "bytemuck", + "bytemuck_derive", + "lazy_static", + "serde", + "serde_derive", + "solana-account-info", + "solana-clock", + "solana-define-syscall", + "solana-epoch-rewards", + "solana-epoch-schedule", + "solana-fee-calculator", + "solana-hash", + "solana-instruction", + "solana-instructions-sysvar", + "solana-last-restart-slot", + "solana-program-entrypoint", + "solana-program-error", + "solana-program-memory", + "solana-pubkey", + "solana-rent", + "solana-sanitize", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-slot-hashes", + "solana-slot-history", + "solana-stake-interface", + "solana-sysvar-id", +] + +[[package]] +name = "solana-sysvar-id" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5762b273d3325b047cfda250787f8d796d781746860d5d0a746ee29f3e8812c1" +dependencies = [ + "solana-pubkey", + "solana-sdk-ids", +] + +[[package]] +name = "solana-transaction-error" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222a9dc8fdb61c6088baab34fc3a8b8473a03a7a5fd404ed8dd502fa79b67cb1" +dependencies = [ + "solana-instruction", + "solana-sanitize", +] + +[[package]] +name = "solana-vote-interface" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b80d57478d6599d30acc31cc5ae7f93ec2361a06aefe8ea79bc81739a08af4c3" +dependencies = [ + "bincode", + "num-derive", + "num-traits", + "serde", + "serde_derive", + "solana-clock", + "solana-decode-error", + "solana-hash", + "solana-instruction", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-serde-varint", + "solana-serialize-utils", + "solana-short-vec", + "solana-system-interface", +] + +[[package]] +name = "solana-zk-sdk" +version = "2.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffc4ca8e3e26a8f80eb0026adf8af1732863f42739cd2201c40c568ccae360c" +dependencies = [ + "aes-gcm-siv", + "base64 0.22.1", + "bincode", + "bytemuck", + "bytemuck_derive", + "curve25519-dalek", + "itertools", + "js-sys", + "merlin", + "num-derive", + "num-traits", + "rand 0.8.5", + "serde", + "serde_derive", + "serde_json", + "sha3", + "solana-derivation-path", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-seed-derivable", + "solana-seed-phrase", + "solana-signature", + "solana-signer", + "subtle", + "thiserror 2.0.17", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "spl-associated-token-account" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76fee7d65013667032d499adc3c895e286197a35a0d3a4643c80e7fd3e9969e3" +dependencies = [ + "borsh 1.5.7", + "num-derive", + "num-traits", + "solana-program", + "spl-associated-token-account-client", + "spl-token", + "spl-token-2022", + "thiserror 1.0.69", +] + +[[package]] +name = "spl-associated-token-account-client" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f8349dbcbe575f354f9a533a21f272f3eb3808a49e2fdc1c34393b88ba76cb" +dependencies = [ + "solana-instruction", + "solana-pubkey", +] + +[[package]] +name = "spl-discriminator" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7398da23554a31660f17718164e31d31900956054f54f52d5ec1be51cb4f4b3" +dependencies = [ + "bytemuck", + "solana-program-error", + "solana-sha256-hasher", + "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.106", +] + +[[package]] +name = "spl-discriminator-syn" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d1dbc82ab91422345b6df40a79e2b78c7bce1ebb366da323572dd60b7076b67" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.9", + "syn 2.0.106", + "thiserror 1.0.69", +] + +[[package]] +name = "spl-elgamal-registry" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce0f668975d2b0536e8a8fd60e56a05c467f06021dae037f1d0cfed0de2e231d" +dependencies = [ + "bytemuck", + "solana-program", + "solana-zk-sdk", + "spl-pod", + "spl-token-confidential-transfer-proof-extraction", +] + +[[package]] +name = "spl-memo" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f09647c0974e33366efeb83b8e2daebb329f0420149e74d3a4bd2c08cf9f7cb" +dependencies = [ + "solana-account-info", + "solana-instruction", + "solana-msg", + "solana-program-entrypoint", + "solana-program-error", + "solana-pubkey", +] + +[[package]] +name = "spl-pod" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d994afaf86b779104b4a95ba9ca75b8ced3fdb17ee934e38cb69e72afbe17799" +dependencies = [ + "borsh 1.5.7", + "bytemuck", + "bytemuck_derive", + "num-derive", + "num-traits", + "solana-decode-error", + "solana-msg", + "solana-program-error", + "solana-program-option", + "solana-pubkey", + "solana-zk-sdk", + "thiserror 2.0.17", +] + +[[package]] +name = "spl-program-error" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d39b5186f42b2b50168029d81e58e800b690877ef0b30580d107659250da1d1" +dependencies = [ + "num-derive", + "num-traits", + "solana-program", + "spl-program-error-derive", + "thiserror 1.0.69", +] + +[[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.9", + "syn 2.0.106", +] + +[[package]] +name = "spl-tlv-account-resolution" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd99ff1e9ed2ab86e3fd582850d47a739fec1be9f4661cba1782d3a0f26805f3" +dependencies = [ + "bytemuck", + "num-derive", + "num-traits", + "solana-account-info", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-pubkey", + "spl-discriminator", + "spl-pod", + "spl-program-error", + "spl-type-length-value", + "thiserror 1.0.69", +] + +[[package]] +name = "spl-token" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed320a6c934128d4f7e54fe00e16b8aeaecf215799d060ae14f93378da6dc834" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "num_enum", + "solana-program", + "thiserror 1.0.69", +] + +[[package]] +name = "spl-token-2022" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b27f7405010ef816587c944536b0eafbcc35206ab6ba0f2ca79f1d28e488f4f" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "num_enum", + "solana-program", + "solana-security-txt", + "solana-zk-sdk", + "spl-elgamal-registry", + "spl-memo", + "spl-pod", + "spl-token", + "spl-token-confidential-transfer-ciphertext-arithmetic", + "spl-token-confidential-transfer-proof-extraction", + "spl-token-confidential-transfer-proof-generation", + "spl-token-group-interface", + "spl-token-metadata-interface", + "spl-transfer-hook-interface", + "spl-type-length-value", + "thiserror 1.0.69", +] + +[[package]] +name = "spl-token-confidential-transfer-ciphertext-arithmetic" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170378693c5516090f6d37ae9bad2b9b6125069be68d9acd4865bbe9fc8499fd" +dependencies = [ + "base64 0.22.1", + "bytemuck", + "solana-curve25519", + "solana-zk-sdk", +] + +[[package]] +name = "spl-token-confidential-transfer-proof-extraction" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff2d6a445a147c9d6dd77b8301b1e116c8299601794b558eafa409b342faf96" +dependencies = [ + "bytemuck", + "solana-curve25519", + "solana-program", + "solana-zk-sdk", + "spl-pod", + "thiserror 2.0.17", +] + +[[package]] +name = "spl-token-confidential-transfer-proof-generation" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8627184782eec1894de8ea26129c61303f1f0adeed65c20e0b10bc584f09356d" +dependencies = [ + "curve25519-dalek", + "solana-zk-sdk", + "thiserror 1.0.69", +] + +[[package]] +name = "spl-token-group-interface" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d595667ed72dbfed8c251708f406d7c2814a3fa6879893b323d56a10bedfc799" +dependencies = [ + "bytemuck", + "num-derive", + "num-traits", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-pubkey", + "spl-discriminator", + "spl-pod", + "thiserror 1.0.69", +] + +[[package]] +name = "spl-token-metadata-interface" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb9c89dbc877abd735f05547dcf9e6e12c00c11d6d74d8817506cab4c99fdbb" +dependencies = [ + "borsh 1.5.7", + "num-derive", + "num-traits", + "solana-borsh", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-pubkey", + "spl-discriminator", + "spl-pod", + "spl-type-length-value", + "thiserror 1.0.69", +] + +[[package]] +name = "spl-transfer-hook-interface" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa7503d52107c33c88e845e1351565050362c2314036ddf19a36cd25137c043" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "solana-account-info", + "solana-cpi", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-pubkey", + "spl-discriminator", + "spl-pod", + "spl-program-error", + "spl-tlv-account-resolution", + "spl-type-length-value", + "thiserror 1.0.69", +] + +[[package]] +name = "spl-type-length-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba70ef09b13af616a4c987797870122863cba03acc4284f226a4473b043923f9" +dependencies = [ + "bytemuck", + "num-derive", + "num-traits", + "solana-account-info", + "solana-decode-error", + "solana-msg", + "solana-program-error", + "spl-discriminator", + "spl-pod", + "thiserror 1.0.69", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl 2.0.17", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime 0.6.11", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3effe7c0e86fdff4f69cdd2ccc1b96f933e24811c5441d44904e8683e27184b" +dependencies = [ + "indexmap", + "toml_datetime 0.7.2", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.106", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + +[[package]] +name = "winnow" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +dependencies = [ + "memchr", +] + +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..0da7e2c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[workspace] +members = [ + "programs/*" +] +resolver = "2" + +[profile.release] +overflow-checks = true +lto = "fat" +codegen-units = 1 + +[profile.release.build-override] +opt-level = 3 +incremental = false +codegen-units = 1 \ No newline at end of file diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..1c8618d --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,85 @@ +# Lottery Simple DApp - Deployment Guide + +## Quick Deployment + +### Local Testing +```bash +# Build and run with Docker +./deploy.sh + +# Or manually: +docker-compose up --build -d + +# Access at: http://localhost:14888 +``` + +### Remote Server Deployment + +1. **Sync files to server:** + ```bash + # Only sync the necessary frontend files + rsync -av --exclude='.git' --exclude='node_modules' --exclude='target' \ + --exclude='programs' --exclude='client' --exclude='tests' \ + /home/crappy/lottery/lottery-simple/ user@your-server:/opt/lottery-simple/ + ``` + +2. **On the remote server:** + ```bash + cd /opt/lottery-simple + ./deploy.sh + ``` + +3. **Nginx reverse proxy (optional):** + ```nginx + location /lottery/ { + proxy_pass http://localhost:14888/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + ``` + +## Configuration + +### Environment Variables +- Edit `config.json` for different environments +- Update `programId`, `mint`, and `devWallet` as needed + +### Port Configuration +- Default: `14888` (configured in docker-compose.yml) +- Health check: `/health` + +## File Structure (Clean) +``` +lottery-simple/ +โ”œโ”€โ”€ index.html # Frontend HTML +โ”œโ”€โ”€ styles.css # CSS styles +โ”œโ”€โ”€ app.js # Frontend JavaScript +โ”œโ”€โ”€ config.json # Configuration +โ”œโ”€โ”€ Dockerfile # Docker build +โ”œโ”€โ”€ docker-compose.yml # Docker orchestration +โ”œโ”€โ”€ nginx.conf # Web server config +โ”œโ”€โ”€ deploy.sh # Deployment script +โ””โ”€โ”€ .dockerignore # Build exclusions +``` + +## Troubleshooting + +### Check container status: +```bash +docker-compose ps +docker-compose logs +``` + +### Manual container management: +```bash +docker-compose stop +docker-compose start +docker-compose restart +``` + +### Clean rebuild: +```bash +docker-compose down +docker-compose build --no-cache +docker-compose up -d +``` \ No newline at end of file diff --git a/DEVNET-STATUS.md b/DEVNET-STATUS.md new file mode 100644 index 0000000..db4d325 --- /dev/null +++ b/DEVNET-STATUS.md @@ -0,0 +1,101 @@ +# ๐Ÿงช Devnet Deployment Status + +## โœ… Successfully Completed + +### 1. Program Deployment +- **Program ID**: `8hcuEUBcuVBYyD53QPFQrRSWJjQy3UVURiqJpcuvgrTf` +- **Status**: โœ… Successfully deployed to Devnet +- **IDL**: โœ… Successfully created and uploaded +- **Cluster**: Devnet (safe testing environment) + +### 2. Token Mint +- **Mint Address**: `GrCHRSRHdZtiz2fiv1iUgigXCfB5Ta3j8gXepZUUTPP2` +- **Status**: โœ… Created and ready for use +- **Decimals**: 9 (standard SPL token format) + +### 3. Program Features +- โœ… Working Anchor v0.31.0 program +- โœ… All previous fixes implemented: + - Direct lamport manipulation for vault transfers + - Realistic bonding curve math (price increases with supply) + - Dividend distribution system + - Dev fee collection (3%) + - Proper rent exemption handling + - Buy/sell functionality with safety checks + +### 4. Configuration +- โœ… `devnet-config.json` contains all deployment details +- โœ… Program ID matches deployed program +- โœ… Ready for frontend integration + +## ๐Ÿš€ Next Steps + +### Option 1: Test with Browser Interface +1. Open `test-program.html` in your browser +2. Connect Phantom wallet (set to Devnet) +3. Initialize the program +4. Test buy/sell functionality + +### Option 2: Update Your Frontend +1. Update your frontend's config to use `devnet-config.json` +2. Test all functionality with devnet SOL (free!) +3. Verify buy/sell transactions work correctly +4. Test dividend withdrawal + +### Option 3: CLI Testing (if needed) +```bash +# Check program account +solana account 8hcuEUBcuVBYyD53QPFQrRSWJjQy3UVURiqJpcuvgrTf --url devnet + +# Check state PDA (after initialization) +solana account [STATE_PDA] --url devnet + +# Check vault PDA balance +solana account [VAULT_PDA] --url devnet +``` + +## ๐Ÿ“‹ Configuration Details + +```json +{ + "programId": "8hcuEUBcuVBYyD53QPFQrRSWJjQy3UVURiqJpcuvgrTf", + "mintAddress": "GrCHRSRHdZtiz2fiv1iUgigXCfB5Ta3j8gXepZUUTPP2", + "devWallet": "GPFYcM3svcM4Srko6ExLYeSW4Gjwf6XoV4k1eSXoGHoK", + "cluster": "devnet", + "rpcUrl": "https://api.devnet.solana.com" +} +``` + +## โš ๏ธ Important Notes + +1. **This is DEVNET** - No real money is involved +2. **Test thoroughly** before considering mainnet deployment +3. **Devnet SOL is free** - get more from faucets if needed +4. **Program must be initialized** before trading can begin + +## ๐ŸŽฏ Testing Checklist + +Before moving to mainnet, verify: + +- [ ] Program initializes successfully +- [ ] Buy transactions work correctly +- [ ] Sell transactions work correctly +- [ ] Dev fees are collected properly +- [ ] Dividend distribution works +- [ ] Vault PDA maintains proper balance +- [ ] Frontend UI functions correctly +- [ ] Error handling works as expected + +## ๐Ÿšจ Mainnet Deployment + +**Only proceed to mainnet AFTER:** +1. All devnet testing is complete +2. All functionality verified working +3. UI/UX is polished and tested +4. You're confident in the system + +The same program code will work on mainnet - just need to change the cluster configuration and deploy with real SOL. + +--- + +**Current Status**: โœ… DEVNET DEPLOYMENT COMPLETE - READY FOR TESTING \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d008203 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,38 @@ +FROM php:8.1-fpm-alpine + +# Install nginx +RUN apk add --no-cache nginx supervisor + +# Copy site files +COPY index.html /var/www/html/ +COPY styles.css /var/www/html/ +COPY app.js /var/www/html/ +COPY config.json /var/www/html/ +COPY ticker-api.php /var/www/html/ +COPY ticker-data.json /var/www/html/ + +# Copy favicon files +COPY favicon.ico /var/www/html/ +COPY favicon.svg /var/www/html/ +COPY favicon-16.png /var/www/html/ +COPY favicon-32.png /var/www/html/ +COPY apple-touch-icon.png /var/www/html/ + +# Copy nginx config +COPY nginx.conf /etc/nginx/http.d/default.conf + +# Create basic robots.txt and sitemap.xml +RUN echo -e "User-agent: *\nAllow: /\n\nSitemap: /sitemap.xml" > /var/www/html/robots.txt && \ + echo -e '\n\n \n https://your-domain.com/\n 2025-10-06\n weekly\n 0.8\n \n' > /var/www/html/sitemap.xml + +# Set permissions +RUN chown -R www-data:www-data /var/www/html && \ + chmod -R 644 /var/www/html/* && \ + chmod 755 /var/www/html/ && \ + chmod 666 /var/www/html/ticker-data.json + +# Create supervisor config +RUN echo -e '[supervisord]\nnodaemon=true\n\n[program:php-fpm]\ncommand=php-fpm\nautostart=true\nautorestart=true\n\n[program:nginx]\ncommand=nginx -g "daemon off;"\nautostart=true\nautorestart=true' > /etc/supervisord.conf + +EXPOSE 80 +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"] diff --git a/app.js b/app.js new file mode 100644 index 0000000..66f891e --- /dev/null +++ b/app.js @@ -0,0 +1,838 @@ +/* global solanaWeb3 */ +(() => { + const { Connection, PublicKey, SystemProgram, Transaction, TransactionInstruction } = solanaWeb3; + + // DOM helpers + const $ = (id) => document.getElementById(id); + const DEBUG = new URLSearchParams(location.search).has('debug'); +const log = (m, cls='') => { + if (cls === 'err') console.error(m); + else if (cls === 'warn') console.warn(m); + if (DEBUG) console.debug(m); +}; + + // SNS (Solana Name Service) resolution + const snsCache = new Map(); + const NAME_PROGRAM_ID = new PublicKey('namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX'); + + async function findOwnedNameAccountsForUser(connection, userAccount) { + try { + const filters = [ + { + memcmp: { + offset: 32, + bytes: userAccount.toBase58(), + }, + }, + ]; + const accounts = await connection.getProgramAccounts(NAME_PROGRAM_ID, { + filters, + }); + return accounts.map((a) => a.publicKey); + } catch (e) { + return []; + } + } + + async function resolveSNSAddress(address) { + try { + if (snsCache.has(address)) { + return snsCache.get(address); + } + + const { cluster } = getCfg(); + const conn = new Connection(cluster, 'confirmed'); + const publicKey = new PublicKey(address); + + // Find owned name accounts + const nameAccounts = await findOwnedNameAccountsForUser(conn, publicKey); + + if (nameAccounts.length > 0) { + // Get the first domain name account data + const accountInfo = await conn.getAccountInfo(nameAccounts[0]); + if (accountInfo && accountInfo.data) { + // Parse the domain name from account data + // SNS account structure: [96 bytes header] + [4 bytes name length] + [name data] + const data = accountInfo.data; + if (data.length > 100) { + const nameLength = data.readUInt32LE(96); + if (nameLength > 0 && nameLength < 64) { + const domainBytes = data.slice(100, 100 + nameLength); + const domain = new TextDecoder().decode(domainBytes); + if (domain && domain.includes('.')) { + snsCache.set(address, domain); + return domain; + } + } + } + } + } + + // If no SNS name found, cache the negative result + snsCache.set(address, null); + return null; + + } catch (e) { + // Silently fail and return null + snsCache.set(address, null); + return null; + } + } + + async function formatAddressWithSNS(address) { + const snsName = await resolveSNSAddress(address); + if (snsName) { + return snsName; + } + return `${address.slice(0,4)}โ€ฆ${address.slice(-4)}`; + } + + // Providers + function standardToLegacyProvider(stdWallet) { + const features = stdWallet?.features || {}; + const connectFeature = features['standard:connect']; + const eventsFeature = features['standard:events']; + const disconnectFeature = features['standard:disconnect']; + const signTxFeature = features['solana:signTransactions']; + const signAndSendFeature = features['solana:signAndSendTransaction']; + const signMsgFeature = features['solana:signMessage']; + + let currentAccount = null; + const listeners = { connect: [], disconnect: [] }; + const emit = (evt, ...args) => (listeners[evt] || []).forEach((fn) => { + try { fn(...args); } catch {} + }); + + // Forward standard events into legacy-style events + if (eventsFeature?.on) { + try { + eventsFeature.on('change', ({ accounts }) => { + if (accounts && accounts[0]) { + currentAccount = accounts[0]; + provider.publicKey = { + toString: () => accounts[0].address, + toBase58: () => accounts[0].address + }; + emit('connect'); + } else { + currentAccount = null; + provider.publicKey = null; + emit('disconnect'); + } + }); + } catch {} + } + + const provider = { + isStandard: true, + publicKey: null, + on: (evt, fn) => { + if (!listeners[evt]) listeners[evt] = []; + listeners[evt].push(fn); + }, + off: (evt, fn) => { + listeners[evt] = (listeners[evt] || []).filter((f) => f !== fn); + }, + connect: async () => { + if (!connectFeature?.connect) throw new Error('Wallet Standard connect not available'); + const { accounts } = await connectFeature.connect(); + if (!accounts?.length) throw new Error('No account returned'); + currentAccount = accounts[0]; + provider.publicKey = { + toString: () => accounts[0].address, + toBase58: () => accounts[0].address + }; + emit('connect'); + return { publicKey: provider.publicKey }; + }, + disconnect: async () => { + try { + if (disconnectFeature?.disconnect) await disconnectFeature.disconnect(); + } finally { + currentAccount = null; + provider.publicKey = null; + emit('disconnect'); + } + }, + signTransaction: async (tx) => { + if (signTxFeature?.signTransactions) { + const signed = await signTxFeature.signTransactions({ transactions: [tx] }); + // Some implementations return { signedTransactions }, others return array directly + const arr = Array.isArray(signed) ? signed : (signed?.signedTransactions || []); + if (!arr[0]) throw new Error('signTransactions returned no result'); + return arr[0]; + } + throw new Error('Wallet Standard wallet does not support signTransactions'); + }, + _standard: { + signTransactions: signTxFeature?.signTransactions, + signAndSendTransaction: signAndSendFeature?.signAndSendTransaction, + signMessage: signMsgFeature?.signMessage + } + }; + + return provider; + } + + function detectProviders() { + const map = new Map(); + const add = (id, name, provider) => { if (provider && !map.has(id)) map.set(id, { id, name, provider }); }; + // Injected + add('phantom','Phantom', window.solana?.isPhantom ? window.solana : null); + add('solflare','Solflare', window.solflare?.isSolflare ? window.solflare : null); + add('backpack','Backpack', window.backpack?.isBackpack ? window.backpack : null); + add('glow','Glow', window.glow?.solana || null); + add('exodus','Exodus', window.exodus?.solana || null); + // Wallet Standard (if present) + try { + const std = window.navigator?.wallets?.get?.(); + if (Array.isArray(std)) { + for (const w of std) { + const id = w.name?.toLowerCase?.().replace(/\s+/g,'-') || 'standard'; + add(id, w.name || id, standardToLegacyProvider(w)); + } + } + } catch {} + return Array.from(map.values()); + } + + function populateWalletSelect(){ + const select = $('walletSelect'); if(!select) return; + const detected = detectProviders(); + // Keep first option as Auto-detect, remove the rest, then append detected + select.innerHTML = '' + detected.map(d=>``).join(''); + // If none detected, keep auto only + } + + // Centralized ticker functions + async function loadTickerFromServer() { + try { + const response = await fetch('ticker-api.php', { + method: 'GET', + cache: 'no-cache' + }); + if (!response.ok) return; + const tickerData = await response.json(); + + const track = document.getElementById('ticker'); + if (!track) return; + + // Clear existing ticker items + track.innerHTML = ''; + + // Add items from server (reverse order since server stores newest first) + for (const item of tickerData.reverse()) { + const span = document.createElement('span'); + span.className = 'ticker-item ' + (item.type || ''); + + // Try to resolve SNS names in existing ticker text + let displayText = item.text; + if (item.snsName) { + // If SNS name is already stored, use it + displayText = displayText.replace(/[A-Za-z0-9]{4}โ€ฆ[A-Za-z0-9]{4}/, item.snsName); + } else { + // Try to extract address and resolve SNS + const addressMatch = displayText.match(/([A-Za-z0-9]{4})โ€ฆ([A-Za-z0-9]{4})/); + if (addressMatch && item.fullAddress) { + const snsName = await resolveSNSAddress(item.fullAddress); + if (snsName) { + displayText = displayText.replace(addressMatch[0], snsName); + } + } + } + + span.textContent = displayText; + track.appendChild(span); + } + } catch (e) { + log('Failed to load ticker data from server', 'warn'); + } + } + + async function saveTickerToServer(text, type, fullAddress = null, snsName = null) { + try { + const payload = { text, type }; + if (fullAddress) payload.fullAddress = fullAddress; + if (snsName) payload.snsName = snsName; + + await fetch('ticker-api.php', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(payload) + }); + } catch (e) { + log('Failed to save ticker item to server', 'warn'); + } + } + + async function pushTickerItem(text, cls, address = null) { + const track = document.getElementById('ticker'); + if (!track) return; + + let displayText = text; + let snsName = null; + + // Try to resolve SNS name if address is provided + if (address) { + snsName = await resolveSNSAddress(address); + if (snsName) { + displayText = text.replace(/[A-Za-z0-9]{4}โ€ฆ[A-Za-z0-9]{4}/, snsName); + } + } + + // Add to local DOM immediately for responsiveness + const span = document.createElement('span'); + span.className = 'ticker-item ' + (cls || ''); + span.textContent = displayText; + track.appendChild(span); + + // Limit items to prevent DOM bloat (keep more than server since DOM updates faster) + while (track.childNodes.length > 50) { + track.removeChild(track.firstChild); + } + + // Save to server for other users + saveTickerToServer(displayText, cls || '', address, snsName); + } + + // Updated discriminator functions for our new program + async function getDiscriminators(){ + const buyTokens = await anchorDisc('buy_tokens'); + const sellTokens = await anchorDisc('sell_tokens'); + return { buy: buyTokens, sell: sellTokens }; + } + + function b64ToBytes(b64){ const bin = atob(b64); const arr = new Uint8Array(bin.length); for(let i=0;ibuf.length) return false; for(let i=0;ik.toString()); + const feePayer = keys[0] || ''; + const ixList = msg.instructions || msg.compiledInstructions || []; + for(const ix of ixList){ + const progIdx = ix.programIdIndex ?? undefined; + const prog = progIdx !== undefined ? keys[progIdx] : ix.programId?.toString(); + if(prog !== programId.toString()) continue; + const dataB64 = ix.data || ''; + const data = typeof dataB64 === 'string' ? b64ToBytes(dataB64) : new Uint8Array(); + if(startsWith(data, discs.buy)){ + const lamports = Number(readLeU64(data, 8)); + const sol = lamports / 1_000_000_000; + await pushTickerItem(`BUY ${sol.toFixed(3)} SOL โ€ข ${feePayer.slice(0,4)}โ€ฆ${feePayer.slice(-4)}`, 'buy', feePayer); + } else if(startsWith(data, discs.sell)){ + const tokensRaw = Number(readLeU64(data, 8)); + const t = tokensRaw / 1_000_000_000; + await pushTickerItem(`SELL ${t.toFixed(3)} tk โ€ข ${feePayer.slice(0,4)}โ€ฆ${feePayer.slice(-4)}`, 'sell', feePayer); + } + } + } + }catch(e){ /*silent*/ } + } + + function pickProvider() { + const sel = $('walletSelect').value; + const detected = detectProviders(); + if (sel !== 'auto') return detected.find(p => p.id === sel)?.provider; + return detected[0]?.provider; + } + + // Buffer helpers + function le64(n) { const b=new ArrayBuffer(8); new DataView(b).setBigUint64(0, BigInt(n), true); return new Uint8Array(b); } + async function anchorDisc(name){ const data=new TextEncoder().encode(`global:${name}`); const h=await crypto.subtle.digest('SHA-256', data); return new Uint8Array(h).slice(0,8); } + async function buildIx(name, args){ const disc=await anchorDisc(name); const out=new Uint8Array(disc.length+args.length); out.set(disc,0); out.set(args,disc.length); return out; } + + // ATAs + const TOKEN_PROGRAM_ID = new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'); + const ATA_PROGRAM_ID = new PublicKey('ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL'); + async function findAta(mint, owner){ + const [addr] = await PublicKey.findProgramAddress([ + owner.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer() + ], ATA_PROGRAM_ID); + return addr; + } + function createAtaIx(payer, ata, owner, mint){ + const keys=[ + { pubkey:payer, isSigner:true, isWritable:true }, + { pubkey:ata, isSigner:false, isWritable:true }, + { pubkey:owner, isSigner:false, isWritable:false }, + { pubkey:mint, isSigner:false, isWritable:false }, + { pubkey:SystemProgram.programId, isSigner:false, isWritable:false }, + { pubkey:TOKEN_PROGRAM_ID, isSigner:false, isWritable:false }, + { pubkey:solanaWeb3.SYSVAR_RENT_PUBKEY, isSigner:false, isWritable:false }, + ]; + return new TransactionInstruction({ keys, programId: ATA_PROGRAM_ID, data: new Uint8Array([]) }); + } + + // Config via hidden meta tag + function getCfg(){ + const meta = document.getElementById('app-config'); + const cluster = meta?.dataset.cluster || 'https://api.devnet.solana.com'; + const programIdStr = meta?.dataset.programId || ''; + const mintStr = meta?.dataset.mint || ''; + const devWalletStr = meta?.dataset.devWallet || ''; + const programId = programIdStr ? new PublicKey(programIdStr) : null; + const mint = mintStr ? new PublicKey(mintStr) : null; + const devWallet = devWalletStr ? new PublicKey(devWalletStr) : null; + return { programId, mint, cluster, devWallet }; + } + + async function applyConfig(){ + try{ + const res = await fetch('config.json', { cache:'no-store' }); + if(!res.ok) return; + const cfg = await res.json(); + const meta = document.getElementById('app-config'); if(!meta) return; + if(cfg.cluster) meta.dataset.cluster = cfg.cluster; + if(cfg.programId) meta.dataset.programId = cfg.programId; + if(cfg.mint) meta.dataset.mint = cfg.mint; + if(cfg.devWallet) meta.dataset.devWallet = cfg.devWallet; + }catch(e){ /*silent*/ } + } + + let pendingReferrer = ''; + async function readRefFromUrl(){ + const params = new URLSearchParams(location.search); + const ref = params.get('ref'); + if(ref){ + const el = $('referrer'); + if (el) el.value = ref; else pendingReferrer = ref; + } + } + + let walletProvider = null; let walletPubkey = null; + function shortAddr(pk){ const s=pk.toBase58(); return s.slice(0,4)+'โ€ฆ'+s.slice(-4); } + + function updateUIDisconnected(){ + walletPubkey = null; + const chip = $('walletChip'); if(chip){ chip.textContent = 'Not connected'; chip.style.display='none'; } + const wa = $('walletAddr'); if(wa) wa.value = ''; + const ref = $('reflinkRow'); if(ref) ref.style.display = 'none'; + const ap = $('actionsPanel'); if(ap) ap.style.display = 'none'; + const d = $('disconnectBtn'); if(d) d.style.display = 'none'; + const c = $('connectBtn'); if(c) c.style.display = ''; + const st = $('statusValue'); if(st) st.value = 'Connect wallet to participate'; + } + + function updateUIConnected(){ + if(!walletPubkey) return; + const chip = $('walletChip'); if(chip){ chip.textContent = shortAddr(walletPubkey); chip.style.display=''; } + const ref = $('reflinkRow'); if(ref) ref.style.display = ''; + const ap = $('actionsPanel'); if(ap) ap.style.display = ''; + const d = $('disconnectBtn'); if(d) d.style.display = ''; + const c = $('connectBtn'); if(c) c.style.display = 'none'; + const st = $('statusValue'); if(st) st.value = 'Ready'; + } + + async function disconnectWallet(){ + try{ + if(walletProvider && typeof walletProvider.disconnect === 'function'){ + try { await walletProvider.disconnect(); } catch { /* some wallets throw if not connected */ } + } + } finally { + walletProvider = null; + updateUIDisconnected(); + } + } + + async function connectWallet(){ + try{ + walletProvider = pickProvider(); + if(!walletProvider){ log('No compatible wallet detected. Install Phantom/Solflare/Backpack/Glow/Exodus.', 'err'); return; } + // Attach events if provided by wallet + try{ + if (walletProvider.on) { + walletProvider.on('connect', () => { if(walletProvider?.publicKey){ walletPubkey = new PublicKey(walletProvider.publicKey.toString()); $('walletAddr').value = walletPubkey.toBase58(); updateUIConnected(); updateUserBalance(); updateReflink(); } }); + walletProvider.on('disconnect', () => { updateUIDisconnected(); }); + } + }catch{} + + const res = await walletProvider.connect(); + const pubkey = new PublicKey((res?.publicKey ?? walletProvider.publicKey).toString()); + walletPubkey = pubkey; $('walletAddr').value = pubkey.toBase58(); + await readRefFromUrl(); + log(`Connected ${pubkey.toBase58()}`, 'ok'); + updateUIConnected(); + updateUserBalance(); + updateReflink(); + + // Check if user account exists and toggle Prepare button visibility + const { programId } = getCfg(); + await withConn(async (conn)=>{ + const exists = await userExists(conn, programId, walletPubkey); + const prep = document.getElementById('registerBtn'); + if (prep) prep.style.display = exists ? 'none' : ''; + const st = $('statusValue'); if(st) st.value = exists ? 'Ready' : 'Ready โ€” account will be prepared on your first purchase'; + }); + }catch(e){ log(`Connect failed: ${e.message}`,'err'); console.error(e); } + } + + async function withConn(fn){ const {cluster}=getCfg(); const conn=new Connection(cluster,'confirmed'); return fn(conn); } + + async function userExists(conn, programId, owner){ + try { + const [userPda] = await PublicKey.findProgramAddress([new TextEncoder().encode('user'), owner.toBuffer()], programId); + const info = await conn.getAccountInfo(userPda); + return !!info; + } catch { return false; } + } + + function logSendError(e){ + try{ + if (e && typeof e.getLogs === 'function') { + e.getLogs().then((logs)=>console.error('Tx logs:', logs)).catch(()=>{}); + } else if (e?.logs) { + console.error('Tx logs:', e.logs); + } + }catch{} + } + + async function ensureAta(conn, owner, mint){ + const ata = await findAta(mint, owner); + const acc = await conn.getAccountInfo(ata); + if(!acc) return { ata, createIx: createAtaIx(owner, ata, owner, mint) }; + return { ata }; + } + + // Actions - Updated for our new program +let __lastPot = null; +async function updatePot(){ + try{ + const { programId, cluster } = getCfg(); + if(!programId){ const s=$('statusValue'); if(s) s.value='Not configured'; return; } + const conn = new Connection(cluster,'confirmed'); + // Use our new vault PDA + const [vaultPda] = await PublicKey.findProgramAddress([new TextEncoder().encode('vault')], programId); + const lamports = await conn.getBalance(vaultPda, 'confirmed'); + const solNum = lamports/1_000_000_000; + const sol = solNum.toFixed(3); + const potEl = $('potValue'); if(potEl) potEl.value = `${sol} SOL`; + const potEl2 = $('potValue2'); if(potEl2) potEl2.value = `${sol} SOL`; + const potBig = document.getElementById('potBig'); if(potBig) potBig.textContent = sol; + // delta animation + const deltaEl = document.getElementById('potDelta'); + if(deltaEl !== null){ + if(__lastPot !== null){ + const d = solNum - __lastPot; + if(Math.abs(d) >= 0.001){ + deltaEl.textContent = (d>0?'+':'')+d.toFixed(3); + deltaEl.classList.remove('pos','neg','show'); + deltaEl.classList.add(d>0?'pos':'neg'); + // force reflow for animation restart + void deltaEl.offsetWidth; + deltaEl.classList.add('show'); + setTimeout(()=>deltaEl.classList.remove('show'), 1200); + } + } + __lastPot = solNum; + } + }catch(e){ /*silent*/ } + } + + async function updateUserBalance(){ + try{ + if(!walletPubkey) return; + const { mint, cluster } = getCfg(); + const conn = new Connection(cluster,'confirmed'); + const ata = await findAta(mint, walletPubkey); + const bal = await conn.getTokenAccountBalance(ata).catch(()=>null); + const v = bal?.value?.uiAmountString ?? '0'; + const el = $('userBalance'); if(el) el.value = v; + + // Auto-populate sell field with user's raw token balance (9 decimals) + const rawAmount = bal?.value?.amount ?? '0'; + const sellEl = $('sellAmount'); if(sellEl) sellEl.value = rawAmount; + + // Update vault fields + const keysEl = $('userKeys'); if(keysEl) keysEl.value = v; + const keys2El = $('userKeys2'); if(keys2El) keys2El.value = v; // if there's a second field + const earningsEl = $('userEarnings'); if(earningsEl) earningsEl.value = '0.000 SOL'; // placeholder + }catch(e){ /*silent*/ } + } + + function updateReflink(){ + if(!walletPubkey) return; + const url = new URL(location.href); url.searchParams.set('ref', walletPubkey.toBase58()); + const el = $('reflink'); if(el) el.value = url.toString(); + const el2 = $('reflink2'); if(el2) el2.value = url.toString(); + } + + async function register(){ + try{ + if(!walletPubkey) return log('Connect wallet first','warn'); + const { programId, mint } = getCfg(); if(!programId||!mint){ const s=$('statusValue'); if(s) s.value='Not configured'; return; } const owner=walletPubkey; + await withConn(async (conn)=>{ + // Use our new PDA seeds + const [statePda] = await PublicKey.findProgramAddress([new TextEncoder().encode('state')], programId); + const [userPda] = await PublicKey.findProgramAddress([new TextEncoder().encode('user'), owner.toBuffer()], programId); + const ata = await findAta(mint, owner); + + // Our new program uses register_user without referrer args + const data = await buildIx('register_user', new Uint8Array([])); + const keys=[ + { pubkey:statePda, isSigner:false, isWritable:false }, + { pubkey:userPda, isSigner:false, isWritable:true }, + { pubkey:ata, isSigner:false, isWritable:true }, + { pubkey:mint, isSigner:false, isWritable:false }, + { pubkey:owner, isSigner:true, isWritable:true }, + { pubkey:SystemProgram.programId, isSigner:false, isWritable:false }, + { pubkey:TOKEN_PROGRAM_ID, isSigner:false, isWritable:false }, + { pubkey:ATA_PROGRAM_ID, isSigner:false, isWritable:false }, + ]; + const ix = new TransactionInstruction({ keys, programId, data }); + const tx = new Transaction().add(ix); // No manual ATA creation needed + const {blockhash,lastValidBlockHeight}=await conn.getLatestBlockhash('confirmed'); + tx.recentBlockhash=blockhash; tx.feePayer=owner; + const signed = await walletProvider.signTransaction(tx); + const sig = await conn.sendRawTransaction(signed.serialize()); + await conn.confirmTransaction({signature:sig,blockhash,lastValidBlockHeight},'confirmed'); + log(`โœ… Registered: ${sig}`,'ok'); + }); + }catch(e){ log(`Register failed: ${e.message}`,'err'); logSendError(e); } + } + + async function buy(){ + try{ + if(!walletPubkey) return log('Connect wallet first','warn'); + const { programId, mint, devWallet } = getCfg(); if(!programId||!mint){ const s=$('statusValue'); if(s) s.value='Not configured'; return; } const owner=walletPubkey; + const solAmount = parseFloat($('buyAmount').value||'0'); const lamports = Math.floor(solAmount*1_000_000_000); + if(lamports<=0) return log('Invalid amount','warn'); + await withConn(async (conn)=>{ + // Ensure user exists; auto-register if needed + const exists = await userExists(conn, programId, owner); + if(!exists){ + log('User not found; registering...','warn'); + await register(); + } + + // Use our new PDA seeds and account structure + const [statePda] = await PublicKey.findProgramAddress([new TextEncoder().encode('state')], programId); + const [vaultPda] = await PublicKey.findProgramAddress([new TextEncoder().encode('vault')], programId); + const ata = await findAta(mint, owner); + + // Check if ATA exists, create if needed + const ataAccount = await conn.getAccountInfo(ata); + if (!ataAccount) { + log('Creating token account first...', 'warn'); + const createAtaTx = new Transaction().add(createAtaIx(owner, ata, owner, mint)); + const {blockhash: ataBlockhash, lastValidBlockHeight: ataLastValid} = await conn.getLatestBlockhash('confirmed'); + createAtaTx.recentBlockhash = ataBlockhash; + createAtaTx.feePayer = owner; + const signedAtaTx = await walletProvider.signTransaction(createAtaTx); + const ataSig = await conn.sendRawTransaction(signedAtaTx.serialize()); + await conn.confirmTransaction({signature: ataSig, blockhash: ataBlockhash, lastValidBlockHeight: ataLastValid}, 'confirmed'); + log(`ATA created: ${ataSig}`, 'ok'); + } + + const data = await buildIx('buy_tokens', le64(lamports)); + const devWalletPk = devWallet ?? owner; + + // Build keys for our new buy_tokens instruction + const keys = [ + { pubkey: statePda, isSigner:false, isWritable:true }, // state + { pubkey: vaultPda, isSigner:false, isWritable:true }, // vault + { pubkey: mint, isSigner:false, isWritable:true }, // mint + { pubkey: ata, isSigner:false, isWritable:true }, // buyer_token_account + { pubkey: devWalletPk, isSigner:false, isWritable:true }, // dev_wallet + { pubkey: owner, isSigner:true, isWritable:true }, // buyer + { pubkey: SystemProgram.programId, isSigner:false, isWritable:false }, // system_program + { pubkey: TOKEN_PROGRAM_ID, isSigner:false, isWritable:false } // token_program + ]; + const ix = new TransactionInstruction({ keys, programId, data }); + const tx = new Transaction().add(ix); + const {blockhash,lastValidBlockHeight}=await conn.getLatestBlockhash('confirmed'); + tx.recentBlockhash=blockhash; tx.feePayer=owner; + const signed = await walletProvider.signTransaction(tx); + const sig = await conn.sendRawTransaction(signed.serialize()); + await conn.confirmTransaction({signature:sig,blockhash,lastValidBlockHeight},'confirmed'); + await pushTickerItem(`BUY ${solAmount.toFixed(3)} SOL โ€ข ${shortAddr(owner)}`,'buy', owner.toBase58()); + log(`โœ… Buy: ${sig}`,'ok'); + // Refresh balances after successful buy + setTimeout(() => { updateUserBalance(); updatePot(); }, 2000); + }); + }catch(e){ log(`Buy failed: ${e.message}`,'err'); logSendError(e); } + } + + async function sell(){ + try{ + if(!walletPubkey) return log('Connect wallet first','warn'); + const { programId, mint } = getCfg(); const owner=walletPubkey; + const tokensRaw = BigInt($('sellAmount').value||'0'); if(tokensRaw<=0n) return log('Invalid amount','warn'); + await withConn(async (conn)=>{ + // Use our new PDA seeds + const [statePda] = await PublicKey.findProgramAddress([new TextEncoder().encode('state')], programId); + const [vaultPda] = await PublicKey.findProgramAddress([new TextEncoder().encode('vault')], programId); + const ata = await findAta(mint, owner); + const data = await buildIx('sell_tokens', le64(tokensRaw)); + const keys=[ + { pubkey: statePda, isSigner:false, isWritable:true }, + { pubkey: vaultPda, isSigner:false, isWritable:true }, + { pubkey: mint, isSigner:false, isWritable:true }, + { pubkey: ata, isSigner:false, isWritable:true }, + { pubkey: owner, isSigner:true, isWritable:true }, + { pubkey: SystemProgram.programId, isSigner:false, isWritable:false }, + { pubkey: TOKEN_PROGRAM_ID, isSigner:false, isWritable:false }, + ]; + const ix = new TransactionInstruction({ keys, programId, data }); + const tx = new Transaction().add(ix); + const {blockhash,lastValidBlockHeight}=await conn.getLatestBlockhash('confirmed'); + tx.recentBlockhash=blockhash; tx.feePayer=owner; + const signed = await walletProvider.signTransaction(tx); + const sig = await conn.sendRawTransaction(signed.serialize()); + await conn.confirmTransaction({signature:sig,blockhash,lastValidBlockHeight},'confirmed'); + const tk = Number(tokensRaw)/1_000_000_000; await pushTickerItem(`SELL ${tk.toFixed(3)} tk โ€ข ${shortAddr(owner)}`,'sell', owner.toBase58()); + log(`โœ… Sell: ${sig}`,'ok'); + // Refresh balances after successful sell + setTimeout(() => { updateUserBalance(); updatePot(); }, 2000); + }); + }catch(e){ log(`Sell failed: ${e.message}`,'err'); logSendError(e); } + } + + async function withdraw(){ + try{ + if(!walletPubkey) return log('Connect wallet first','warn'); + const { programId, mint } = getCfg(); const owner=walletPubkey; + await withConn(async (conn)=>{ + // Use our new PDA seeds and withdraw_dividends instruction + const [statePda] = await PublicKey.findProgramAddress([new TextEncoder().encode('state')], programId); + const [vaultPda] = await PublicKey.findProgramAddress([new TextEncoder().encode('vault')], programId); + const [userPda] = await PublicKey.findProgramAddress([new TextEncoder().encode('user'), owner.toBuffer()], programId); + const ata = await findAta(mint, owner); + const data = await buildIx('withdraw_dividends', new Uint8Array([])); + const keys=[ + { pubkey: statePda, isSigner:false, isWritable:false }, + { pubkey: vaultPda, isSigner:false, isWritable:true }, + { pubkey: userPda, isSigner:false, isWritable:true }, + { pubkey: ata, isSigner:false, isWritable:false }, + { pubkey: mint, isSigner:false, isWritable:false }, + { pubkey: owner, isSigner:true, isWritable:true }, + { pubkey: SystemProgram.programId, isSigner:false, isWritable:false }, + ]; + const ix = new TransactionInstruction({ keys, programId, data }); + const tx = new Transaction().add(ix); + const {blockhash,lastValidBlockHeight}=await conn.getLatestBlockhash('confirmed'); + tx.recentBlockhash=blockhash; tx.feePayer=owner; + const signed = await walletProvider.signTransaction(tx); + const sig = await conn.sendRawTransaction(signed.serialize()); + await conn.confirmTransaction({signature:sig,blockhash,lastValidBlockHeight},'confirmed'); + log(`โœ… Withdraw: ${sig}`,'ok'); + // Refresh balances after successful withdraw + setTimeout(() => { updateUserBalance(); updatePot(); }, 2000); + }); + }catch(e){ log(`Withdraw failed: ${e.message}`,'err'); logSendError(e); } + } + + // Reflink copy + function copyRef(){ + const el = $('reflink'); if(!el || !el.value) return log('Nothing to copy','warn'); + navigator.clipboard.writeText(el.value); + log('Reflink copied to clipboard','ok'); + } + + // Tabs & countdown + function initTabs(){ + const tabs = document.querySelectorAll('.tab'); + const contents = { + purchase: document.getElementById('tab-purchase'), + vault: document.getElementById('tab-vault'), + referrals: document.getElementById('tab-referrals') + }; + tabs.forEach((tab) => { + tab.addEventListener('click', () => { + const target = tab.dataset.tab; + tabs.forEach((t) => t.classList.remove('active')); + Object.values(contents).forEach((c) => c && c.classList.remove('active')); + tab.classList.add('active'); + if (contents[target]) { + contents[target].classList.add('active'); + contents[target].style.display = ''; + } + }); + }); + } + + function addEventListeners(){ + const connectBtn = $('connectBtn'); if(connectBtn) connectBtn.addEventListener('click', connectWallet); + const disconnectBtn = $('disconnectBtn'); if(disconnectBtn) disconnectBtn.addEventListener('click', disconnectWallet); + const registerBtn = $('registerBtn'); if(registerBtn) registerBtn.addEventListener('click', register); + const buyBtn = $('buyBtn'); if(buyBtn) buyBtn.addEventListener('click', buy); + const sellBtn = $('sellBtn'); if(sellBtn) sellBtn.addEventListener('click', sell); + const withdrawBtn = $('withdrawBtn'); if(withdrawBtn) withdrawBtn.addEventListener('click', withdraw); + const withdrawBtn2 = $('withdrawBtn2'); if(withdrawBtn2) withdrawBtn2.addEventListener('click', withdraw); + const copyRefBtn = $('copyRef'); if(copyRefBtn) copyRefBtn.addEventListener('click', copyRef); + const copyRefBtn2 = $('copyRef2'); if(copyRefBtn2) copyRefBtn2.addEventListener('click', copyRef); + } + + // Money rain animation (working version from website-solana) + function initMoneyRain(count=72){ + const wrap = document.getElementById('moneyRain'); if(!wrap) return; + wrap.innerHTML = ''; + const vw = Math.max(window.innerWidth, document.documentElement.clientWidth); + for(let i=0;i{ clearTimeout(to); to=setTimeout(()=>initMoneyRain(count), 200); }, { passive:true }); + } + + // Initialize + async function init(){ + log('๐Ÿš€ PoWH3d Lottery initializing...'); + populateWalletSelect(); + await applyConfig(); + initTabs(); + addEventListeners(); + updateUIDisconnected(); + + // Load existing ticker data from server + await loadTickerFromServer(); + + // Set up periodic updates + setInterval(updatePot, 5000); + setInterval(pollRecentActivity, 10000); + + // Periodically refresh ticker from server to get updates from other users + setInterval(loadTickerFromServer, 15000); // Every 15 seconds + + initMoneyRain(); // Start the money rain! + updatePot(); // initial load + log('โœ… Ready!'); + } + + // Start when DOM is ready + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } +})(); \ No newline at end of file diff --git a/apple-touch-icon.png b/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..36324023f9c96259685d02fa0a2516f7d5066230 GIT binary patch literal 47334 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 z z{}c-EB>Jw0i;^S-lvw=#2tmgki2E&4jyB@D4R9cr(;8G__ELnpwrKR2Tv>@CbiST0 z!Ts*5uCjd^m@gP(!sm@M%}4rH@f|E^EwC$?+AdMkT4<-!PSQlu7O!OCW%Wk_cfIE% zPPZ8uxd#nQZo`KhIk8Yx%hY>F?WFUj;*_h7Xl6#Me;+TGHF>O2++M5s>~;$_>JH_s z;m+nd7ovTGJy$o?(}ZoH!c`0xsO77Ux)+Hq)Q*D(f3nNEdeWeJZTCOaX`HMB=PFRZ zP39=s-YX-1{bY&*Ixf8Ux<%$tbXhG!3K)AOzzt4RsC(1mxTngU?UNWGU>_GPGWB)# z*1;ohT0q!b2X%0pY52c_pQqju)Av^n;FrK>urn=%j{|s;#`?Z_yan?^-buXci}G(& zT34tdPnV|*A}91Gk)r)gD%8<`8?}v6U`}^1>|SAI`szfj$7wnlciKfQE%O&pdi%|q zW@IF2M85ro1}YT#xP0hnk8Fqek?(MNIFW_ll+u zJk7A9)I`z@7PIh+xaQ3Cd$&r^;Y|J)q<#NZtjx{+Fw3Ld;5(AM(#@%l2L{b#)Kza; zZYz$=tR8##PU?cZNC2c1*;fBD(`Zyo9A(E~**?4;}*fkQ$Q%Lq+qeWq$G-&PXM{I(CG+6N+B6Hun0XMK`YRkj=o>#8JC8B*7T-8Hzw~pG5 zXgxkTDMYJxgYL<@)x##>|C=^^OI?N#m$OudCr&a<=oUHVssu+V*j%{;)0$p*F8f)l zo=}f2rtmDg-XF{owVt&#Zm~CDL9180o?M6o&9^;YK;HTh`I-!E`mx=0CwE_TI`ad~ z*ElvD6Jr*a2O9&}oo-ik{~`)usbucQvjc@M4HBm7)YevD7X1vF z*TT{&=d-!)XB}a%h&yRoewv8=O~Fds4>0;PF9LR?G***f;Z zWcmXxI->WoRx;jY@m*wX^*d+V- z!y@-MxB%W&KjC`wbx)8XDFDE28p@O~YVd=g|58YD|HH-7KBxEPoO8zS3l!i7U9sJx zqm<&m*`fK1ui1jIYZ%`I4GQ?~VM{^l5bH^m;T-=W4UI_l886{JBdtffBT>i~H8&CIFj}j|#~l!o z{LeoCpo+YV-pGIcQz6oXwd5}5O_gn-P!k#LaOzN%kYF?{*Cx>N143G9o6=YyDv>39 zL$XVeHsl5`nBR~kw07{0UEAIYvi~Mkoot}wrgD)kolzSS32BeU%DSHjdOh!HK7$em z%vUULZUdwDCJ%}aY>wR4c|dH$UiOWHup%YZ)=g7V$}|fui^$fA1=OHzLJG%6S@;ec zr}S2;bA`Sy|9JLqu2`H2`!wu46XLkI;wJ+cgzD3T-VWDe_s?Te?+h+5w3Z$n_&Sfi z{6!jlImCAr}xvK zfrEI237P&A|7+e%AN*9vyQ11h9a_jJjhH)vgnH~w--TCu1N!w@LjXQ_u&@uHo-9phj_Vv55S4v@$17FrFPzaB(%oopU*$ss~!LH z^PaWv1oF#b-cpT4t<%UurRMobHp(Z8+?>nGxlwIS(3Qh#rRi(C%;wD4o#z4M+*q&^vE@ zp0&R9eLvpy{mRZc+2^c1CwtD!T-VH0K=VE{Dv&iXQ6%4Tt@tKv4t~)X6+5^7j=p$H zR;mbmuAGie2UY5rJ3wU>Dq;t59}b==Y8s%V{@7oc1eIQ#Ul_;3R)Ac(IxE-abNWOd z(TADnuVPbf%4Yn#l5n) zr{!sqY1HTGiE?x6+A-1!o}_7_FJ9WbIUh#(+%D}yjHJdg@Xk3i&luoa#!n3YDts=! zDEye2%AJV5_u*G}dCyO1hgY8&yw0K?kvw^7lT+DMc3OWb6n(LGp*^~mo?idm95tqW ze>Z(qunq_yhv5UI?TS-1!+O#M{W`Z#W8Ul%mG44+$&;U9CiUsy;#7lK%g^?q&K7?H z$*GVNzi(l?&&81R93oaN?3}9spV(WeG9w`MmjhQ#lWlvJlnRdUBn@|`Rr!AZb@Bx0 zA-_ms`}-ewiTiq>+xMya`LS<7UiS@9YAfrRwCR7UggNFMcz^QmkIWn>uO)Dp+kxJU zDfCnn2bsO3I)vY)oEt0X9|zsnJtV>Mqn$iH4&!^EvKR!`7vW&OmY3Ql@@VnST$^93 zX~Mb6L{E*1U@d{V@IUH_&RUsR$~0Q0FQ)HH52y1-%G#jWW%|reymoM^t@fOzaN+H} zWzGHHA=+#9D>z{tPJ$Lb0=J9|&{@_opz_rK#&alS89eiR=dM$7Ks(>Fx&MG@c?8E< zU-!7nJRW8^7yQV|7)%kAS_55nEHZa!2fvhGr1+{8q|q^8dwY4CWTU;|_m~;g&oqXr zg_D7+((pN6l5P0$*h*zl1$iUcJHuazoS%r)r3F8O1FsSsA zIr_{ql({C5qV@)>mOXX64jC#_WB`5PVD{j>zCANARQYT)_SP~575zF(%p}Klp{9I} z?YO$YCptY&N#Qv!(y8H7MUYsPp5mxUzQRHubg5P%^GRBOjhb6;a9#^S8dn`YYDOvA z^#W|}fDzVQ-(kAXDZrzd-mJj_LU~B(Azf-XnvyH~bH*bskR7MP5m3AOC@*B_6jz3~ zQAKWxFNo~V~U6{^%-Q2MSR&eynfR@{6PzX+6lx?GY$9@2c`R?Sbzy@xA73d}O3=6?J9SkI?1I)dIw`Tw83%=T zXC+AO1A&_s3jgqe9=aw1^B-_yvR1$ISHV~TT!J70!fQMNsx*a;OKWj}&Jau(ZtSrO@XPOq>TCP)0;dc?DAVJE54+Mp zhCqm6(?U9+Q?zjL-MW|mo*G%vx(1{S^Gh;+dA5?ItGPC0{IXCws-7vBnJk&~Fo7Tq z%G=5a%#bMANtnC?#+aBu*ijdy3B}LxW@RoQ%usD@o|J>b(rz0!@m@^>qM=ih z39Gi$^_u1*-gBd!3He^T^^3L~ls~18XB(ofF_R=`Tv;?g5?L`hJQnQ47yg(kT|3C|E| z6h6!Y{jq6XA_g1Zv8TV(Uk^a@J^@4&=-ac$cvcoPRRlcQ1gvvJt&jqJv;$EtAAySp z_Mtr7*oxr?`&}KwDfG<7%Y0c354xyT1BzsCB~+=FNA+FM1YmsP6%CB_lv#O9_QLa% z7qUZf2E5m|iLj|fz7c&AzJP6`4v&BuLsuNj7-Vn0@!PS9Yx{JHP`}S(623kv1otf2B8-kALHn|!kM7FaPZ2F zgC&b1KJ@VeFTez6ZOHItGE^*bTh@#4sN}6ZK3Gb2-XmDSw%~HYMjfda)4;?Q@C<*| zROjG}Yqmw-+e^^~unWExAVPoD1b}3nNg731hKWNTK1xDJ!Q!*%ZcHR!N3aTh6KAI> zoF0^BhUGdPIE_N`>>clY`X<&c-wu~}LNg+5v;ftWeatU6-+a#oI^4K!>zyT&dfRH{ zA}i*)|8(xJu*EJ9R0>QhVuY$fw&I;LyB_{ltd+%=)dpGq6QNVIV!r%o3ERD;5kGTk#%p5U~|&E7oZ73)nXR(tyWfDW03wukW84U9jjC-ce;P)%o~-^i*nX~ zYSTEwLD2elT1ciERKgQahPVuE=9EycP+Zc{F^lS!c!j7wdo#30AM*6jN|%vJjS>9$ z&+E|VoVi8$B$t_bKhhQKy7ei%X+aW=i0zu~j~J;& zdTvr`tKlk6SL-c3VFer;LYh`{Rh$M-D<*|*0bdRVG&)v4No0I6%!P(^OkAFCD8S)HLV z|0>R=NgPDBg1W=?FEEVoyGRwTxVGBp2Xh?6D^x^I<-58Zl$g}R`<69ZQqd`3J=xm8 zKrAzo%u345;{8Q(i9*pu>_tb6hTddCU>?4wPz#wU=f5faArNUF8HEyY(WU&xYsa;cwxL2=CjeBKBQ8_3O>BxT(G0VYz3@{$t${MH(s{uOkp2BbwaO1=uIcVyf7-(u>J zf;9x8&Q^#gqefrKae`np*&gVKtJ0uTKsnU*C{KsCdWzm#}evX0oz^d7A1ksCxfNaK5gshX)a zi$HL-rCjc@@^~tEce`7B*qUnakMFN+##x#;IpccHg3U% zBOfc0O5Qnu>_bjt-;581fal|?*Xx_Np;8i*suR+3J8G-!=|p$;zd9XM{%x#(6%C5@{l8Z>rzj(_RAm7Mu*H-%vsI%U4=7>|tbEDfhjc1-I;QE`Ap=Hxz9cBfhO1M}G zRn6P{L_Ob$I;1#kjlM~+Zcz?;z93}BL_vhx7~?gWQ0TvEUaf`_hd?N7zi4x`PV?fT zNEjR$cGM}7TMAtYhc%G7Cku~P124^x%G6Am?Hnu$SL2nfG3GpTqkde~IV0e$y|<*h zb%$=#pE^+6<}PusT>H#pe17R@IugrY3UMik$w~FY%rtnZ_zR1+l>Gs1z<7)eXoOz< zD08br;@#+of=spD7=_Th_NeK8g%aM7mg+}1k!6Vcmw}FRM7S4i# zDUG%O3a&SAdm!502@G|pxQO`qtKOhU?w{sKUfTaE{_MK`vMe}(bX-vQ$`sf)_DbFI z+u_ByxTck^SMC87DnvtA)Q$A?_x%N3O(b2Row(;s94iZ3Et`St)45co_~b=(dslh3 z&LPv-o%I#yS{{3u0PU6eUoa8%>rAp^ivWqoNGJ{ZzXad5c`tcfR0OdhWa9*p{ZcT5-r5l{ntb()QD?09D3oo*~)|zypA9!SXdZ;sKHucG!Qg?xI z82u-g!wwlS6eIc&`5m!mLhL)ymktkbG#ewLC&q1V^YI4L$6ny46gg+Tpa&^vdL39; zZty1<5Syz`2>-=GeWgx#U)ZiM7Z1zaIIH7S5*!~SSr+iM%-#w=aGKShg&+0Qb4k{8 z7)GD(kVK`8bEO%-5ANDafk>Xf`ji!Ge zE=aC1fe@!v$;@WfX{sM>g))!|n%7zDSvujeQ&tP0j*3t0vTJ*5>xyg)<{RF;PABHf zc78d#>yh-Ae7e5j(VU`g@hqE2ut$OZ;YP_YKPSGCN1O_ig^rppp6-n-9iN6ESt<3g zDV|%fz!XHP(#SW;?yF-NRb=~I33-j*W1i9=bPUH3qH>KFaLu|Zl%#vuN8n^zJlaJpjRs{Bzm%DJ` zf#ZQQ4=0el)vS(dO=8V76GZA%&VS6Hy6=v!w;1X82caa?3RxT8xM&mYWr*4{;ku+W z@LNL ztoWF;!J{M#(qc9Z3%wK($e1$;O#Kh>cNF`m>TwW~e}oGeLs_FVjf3F{MB*eu=(A>q z8&pyFO<3o?r&d2^4VyyY?cgs(j6IFj3U!AlOt|}L2e_04$m@Ypr1K#9+V0M1Xglnp z6EXUhWlzB&GZ?(5Lh6EbQ+0NjRY%DhPcP&{I8H6RM3`M%M-nb>)nFGL&1IkEbQuRZ zZzf{^9G)7XuG2i8FRotUmzF;LKBi;KXk$l3SW@N&%AlT|-Ygi*hw52w?sq<`OwYi= zzDPgS`z%TK2E6~6?j;);1{^IT|Jmk+B-)SkBQTUg|MP#vzlSz@EykJHm@JeYWL_;c z-rq1dh^0X>X_NRziDen)h!&Cbc4XCqmFhy&vfzW6gK?FX6y0ADQfbN@K^Tv9JblrZ zfB#5V`UunvZu~eIq_$~3+IS9iEu}HNGWcC${ROw!dLS9A&iG#Ufx>WI@|fsQ9D$Oy z$Di2grf~~Bbq)|!cSH^37M$&l9S0H+bn%}PuMb<_JM4 z+^SuAycP^}Xr5G(t-8xmI^2b)g0JXzq+wy=y0=ESBmdS?q?#!hzs@sOT!hiKb} zE-~))1Y z7Q{|hmT(0dpv|13l7*ye*@`j9Ka!(LFz#kK7@zLbN(kVHyQ=OzWU-vs)Y^=1ZUY#d zSKN@KmV~c(AltHQqpOXTVdgZ}rq)6isqESOdD-SV&osEUzuF%txFBlB##VGZvQM9T z^!4_%d}hwA1V+QZP@B+d+$j}f``W`RJ7v$i3N?@UP6zy&RHqQtRYu{KMn*!0EOCRl z)C)449LjGlwgmM+JK(iX5Jz^Du+4HXF^R%qnta<td8)D(d7ZkvHugx$?=PS`Hm`k6FDU$z0$vRk7BP^Wy5bUUa0M(`F*HZy7ndQ^ccIF(kfm0)|dAAB=7^$eA}yng^`Vx?xz z42_dC2(or@a-dz=rYpT8+GHLt?;>dWK2(+^EiA$w^x0_Y*i%mY$n4P$FE*+ZObu(7?~(Kf?e40F!tyTms#_P8dxC`3lZicDC2#VzFtTyWW) zy=+VwPVYJV@cexx!9YuVwysos)}dRh&7|aMTu-i3?!2t;MnaH|6=gqRf0EoLIN4S$ zz*h~NiW}zs(m$Q^l9)H3v3jE2?NFzp(V+bx(L%?@;!A-BGwX43l*Irj8v5yUsPmmP z#azX2LulMWcmBG=N5H)V?#YLTv^D>T-R4vOGNv)PAst}iA=^)X9Jb|?W*#b(POifO z->)uA*0&nam1;F~vEAn%{5kQ04s8;IE~h=GQdn9u{QlwJkb^%KT->swp!G#6oxG1u z2g(ojyR~Z0kQY`ZqzBT#)X06El}%{rKu^2dd#wg-R^N2(ChG#O@*-aXni05qUpUnL zd~DaaNMSP!#U7UXH`wG3T}Sl#+E>sJG|w-1Y|AVBG>68 zlY8wW`@A*I?7`cIYIv(F!|H1iYh9tGu(4SYk}8K~saT?4(7S z>Fv^O`L?t#dLH@h1B*rlE21f$Td}sTM4l1}_ilMNXFfmn9r(}gV_b5b&?t&Ad^8Q0 zdwTS$@}f*-LYiksO}I$rK?u>{qP(1S6wi(e8>9Dqo@$saTExHsd&Yf^rrRJ&N8Qgt_2hA;IfzUGH{nBF1CJYo)zH}=x+?tkoS}? zv`k=~+E66(=DIUor|8eYWk!L=dJsOd9(6~yN0-%hG_VfA$mZ&X;hoM7zApcgq`@c( zAmwE=NwAqKhJxUqce`CeFqC|4Wx>D}bOhfl*pCt8vt_VP6{ezu#xUIBHuP3S{fo>J zGVkvxj#CsGVgOb~iIqZ0eYHS^uw5Q4B2^^kAX@{^w%TeUXOjx-_7e^0TPERTR*Dko zzE&~;4hf!h z43c!Td3*TR6wq0_)=V3YjKflleG%OkEjY`F zriu_?++8%hy=>{AHnIfi1BC;JkpFVlYQ?&ugDd^ta1rZR@-!nciSXS`bNeWt)a$Jz zPAO9=ltifyOJebcIp5kun1$AI7C>g#aLSF1qFCI;kn;iHx1DckSwTa2A7W5vff=DftCh{cFzqc$6@1N zYCGA3V@(e=0^VkPl#mV0$$9yJ#PZ#GhGIhF(8D-W(&s_N=525kBre(-wEBtxr% zd&Doyn>sJ$rL1qo-qM7o&YJ!ht?6nK@6QcD-kpP>tjL?(lQ)1Xg(=)_$1+u)DDCo1 zwT^qFp+vj30MpK12Dk|=x#wts#l;7WLZ)Bo=4Pm8tWHZjO3B_12~O7c<19QWNF2_% zawdP$^K!<6<@V6rJU$UAT9ptv)VVA80;*+)VJ=RLJa|!9bOvUzsdQ4O-U!RHySDxU zRLA=i=r)di+mX9gY%~&AvShNnn3|x^rQ8b$&Za%0$Ix$E=;|Aw9(lTc+avkx=`Q zf|v*%@61_Etcfm@=#K63s#3F77T8Vl09P+UG#8=OzY1xOxUIO;1X?Voh;3?rNw9S7 z;b#vXul~tPh`C%o*fLxqJEl3}v`x@+c+!m^R7uD8D=c=<%s#DDw*YvR&pYEL=GPu*gK67#c0 zv6srL`P-#X7>>pIhwgoaK>T>(F>U1b+A1@XF!tVDE4*`@1{8u zqq8ow9i2P&+1E;~_?m+#%aM2r)9a(eJ0hc|J0=SJ9C1IIz9`a;QB!~dE{7hwci&h^ z9%fFc7pG$sD5N7*95IzS7=AbOr}f^OtL)1FNg(>yUip($aJnCS!bkbu9lk4yTHcIz zqLv{q&Hx`llej>sydT9-O8g+cS3(MuH8@fU==ph&KVY=0&S`wVAb97-?Akt*J&FN; zd@{R+4+!l3Ir$)$;WO)t6k&m=aCAi^9gaRAznhNn4;*i69{T7+{S;%|c9p+pk?&)p zu|npBNpUPDETb5uc{f!r*q@f}omUT-_!Yg5u9WpF8Rk37qj$%?1Gl#S*onfAHin=3 z4VOfi+HCzkKa(+jKNUu^#A~)AL0ucRd3Y{?**kkw^9x9W{N|~i+8BJpyk9zXFTS8{ zZ=YpF<8HvIIPXc#03aZRhoYF={?t`5SG1r~bhFqNYBPq9Q*Hw2p5Aw{=qr?=?U?s~Vv>O$o11FS)WmZA5S}eCZfBe;0k6z8 zUgsWe=DI+T+VM!vRxqihkxbNVGV62F#%Hoay;Zb7#2{7TqAenC4?0%-5;0?edG_x; zD1*l)1!u?>BnCcRQlce2k_DsPH!)b7$)c|y@=k3+YVYubQ(6V{{n|n-|GDr-qv?@M zZan$k#L3_%`!CLM56ck!id}_wu~PsuJGn&)>REj^Z(y6)X$oj@NjkQ7Exn!swcIm% zKm)QWhpvy7fwG%NUG`LJv#ibnaW=IcHbGvvployn3GD(R@Ub8rTrvCLVTF0By8Z=| zAQnEKeL!Lvz}G5GNU!xU3vyoKtKG2+1}@px1{=BMw#a%WriLo6&`dX{*&^zncJuFL zTQdDj*B_BpPqE8@{d~}+Hm_4=pX3||1o~n?fQ2pCkKiBxNdjrpzy`J9a<0)>WU0Q1 zUqLCSsZGg-u4zE2tjQRvRNvIEtQ2T!Q?_AkdR!_C80#+8H}k70EikpI*w|89%pLr) zFQF_4YO=zaBX;!dOcaBjoT1PTcgTMUHP9UK_EFD8?{COedrg*jd6RSK7hj8P##Gdl zEjdd7OoV6C7gE!nu-M3RS48s5`VRzfZJW<3*xjRhjMnZXSogH5X8iljWJkcrHfzg4 z-1p(!NHK!RqOV}4yp8XQ>}OBnRaLZY)ieAkfW02Ghrh{nAZ0MC&gkYSn{km`TxQs<*3KV``T7I}W?vo5j;<1x(dKd09wSnHp@>b*i9?AN&wky-U=~ z{$Kn~joP)s@_;6a2EFOOHBj>`?VOj~3R#s{2Tb=b>26%rld9w^b%(bROZQe7juP`? zzzJcxwgWu~Vw<1@QS+A!;?8~h*jrJ+_NF-?$5Ka=aW&Uh^Gnew%dQx|{jPz@r*hmo z%FPYBUAcR6Bh1K7xw~sB-uhLXew^D2!<@wW&pNrN3K8z>o_%DDzZN*ZDR8QrcJ*l! z>b@u)spz@Vp5W=)EgK_!xz8J)vEV(X?io zJ!zj2mL&ppl`VNrKud1gYYD=ujfrTOVQ&5a=V@>4yvc^t8?i$JC%Wf$8vamuKwy<` z1O7AA#xizv0`l4 zm|G;9og#Q$k>M;nb$-Wv+OTY*;rSOiIv9)`+o#xj7C6sSLQ{ZSy($!!oD;76%B(#; zUfNPgXn(v8u66Kf>e8Tx--T1G^jc7taOUV6$B~DDS9C^+1$=z&^d*eWb3-=t+VM-Cos`n!3?)4MNXSiS88Y9mGOO{J7?hj#C z>EV(DiRo60s!z=2WF_>uD3Gd@!{mRrR4O`Sj3Rnr`2~M>V^?I*>7A?BAPsvbuv;Ni zZNqf-6AYy-tS5)SQv-Vmy#+-JMdzS}rwd`0{1g*(<2%y{9fJ<2D4Sm}A1nKs9z|-9 zc;Z@a(m28NjI-(Uy4aEtxcl{zPBLQ*;MDd_#^H)((x9mI;NRYRbb)BoIo&U|{ z`q`+-erce#HPueS38*bq)Z%G?!{BQ!)XTT`iHKZJlm(gk`i9Yx@M?k}#7+Xh7Wz2E z%PlyO;Y}Z#Ic}xYf?=#%DM|+*S?N zCyv>3Z3UZ@D|T451e=tYOH+KpVqq=J5^fNCTbJ7}RbThxQe$nNf}eBubATl{HNU*( z`a>AsP>Ex?WXsw!PLc6*I0Muj^}?QFMAVMFh6M6@G4&*)7<`fcRd{N^#;){j`c@ok z|KaTzuFP{<{TFD!z!b8Oyt^3`6NnCpxOmVNBRHtRiT~FFD^33UKISwbeR#tH zI6U&2EYFomu;$F?MED7x__}G|YD3rOv_y4zP~{{mmSIC1C*!5pn`HNCRbXEC-0*Z1 zZ$)I!=78>@T=rn~a;`GX2V2OpVmTpO@FZ;O;#<2{n7ro;a`cw?B55zOYEqjH`!7=k z-qXX~)jCdxBEdHXbx_3uGSw+e(d?(}JuZ|m$e1MXu{8gdtMkuWj%+zT5XYZzUx)No zK7Ptuq6B{Q~7-E{Zr z>d;reb^zt&;zTg(z(ZbKuE-cs(-5>{q6Ax?fxcMufFj%U*;|Kj)cv-?Cz_N~?f7&e{Qy3ke1Hpi3c(qHW1M5hBVLKfNnsMlZStM?6OU-a0Y{6F*r zuT7d$$*$8wHW!kAVO#G5DQd9Jl4Z=lnh_aZqPHq?Gz*5ED<&Vj$&xOpF2l6vxW$y=J$(XbIw$zWnID8dmTpE7{&P*B^_6@$OksbbNJ-`BBk$GWZk)v5w#p)Yg@zI0pOT5Gus$I)^_E{WD6x?_cYH`-vH{*P(&_W;XiyA@==19dw;}VoK$JKZn?j%9Qk*V} z*EQj#1JG1I7a|ad9?h920wQdQhLXe4!*IYJpmRhA;rKT5brYmb^&?zQ-D@b`7Uh)f zb^(L=r-fekdYpxsYE3F^47E!6@C=2h zI|Md{7fnt3s}}~KXbcjrAFu2ig4vyg+c8f_o9w+CMYuv7W3iC1Gm%U3qBr7~@LD9@ zh*_1lq`-}pihtL&|=^8$gmC9Pc zU=aP`c1nWGz!#eUUm8z3yTnAYOV$#56mS zL+OJ4+T=^Ymv(iWrW}3v)#_iW%ZH8G0y4#)3qB0^)TKtyE&T8LnSJwL!WlB<&Q_Ie0aga{;>+v1_tbtFV%f9h~(uq2Xd+4}GXSdKsAJbfw%2@%eyh*SKn~rt`IBlmx=m<)4 zdU8~;M-72onoiw=65?gN10mW?NwpR;o~TPrYOCgSq3?f|Hb6GzX&ZXV&%xf30ah%i zz-j0~Y>j~KXrrk)pQ+;WE9$2!vLpUZKlnZ(dv^RieT;{A05Me+sPXl7DSNefzO zHIa3gj^eJ|?On^KJ5L5`waGaKB<Uw zY0g_E=H~D^y2me2$SUh%+{9^>MA}bEZvlUSU}l!C`+g2QSr!e3{`3&lx#cPj$zSz} z6R*I^3V+fsYHC^W@wgp4h&ZvOx8INr~w7 zjdW3ZKhavVsY5oTY(!-W=xlrt)k7W>n{S9j>luLmos;+#5jYkowM?^&qi{B1KI?jH zkb-gFFVW9K8}aZR!7RElRq_!P^Sq_?MPo@SDWt4BEMFk!nYzRmx; zL(tSMl>)1B)dSq?=;|rG!=}=C#B^I-d#Sl*0vlcTfkiiJr~aT(U%VmKT)mGrpoX+nNugk`7#J{j~D=qA}8(ZoJR zZ9-H)IVOn{`@YNujB5cU9#^J}&0Ev3Pd#i)A`|jiifO6Ex7%1MZ(1l=s)&&ogYaja@$fJ-o&>1~C zwvAX11QUy-xn>Q#{2V?dLBwQ-)Ydj!6WD}7+*en*?9NNO=Gd#_zRB{%ZECe5y z1m5fC{Y8vldpM~tW~)_uGJB{qR$ak8>P((-petKLlG^cNE$}Nq=u4e2J-VfRiRQN= zQx@|h7CyEp#ysAxFFhX$2yFNX;u^xd0&Q5{l`?3ZzKg?W#xD9z+10HVNT|86zJ88w z+W3=+H>RDe{^2*8$1CHy*ETT2WeGumG0E z5zh<1bxOsV(_Ef)nHYUc95E4RO!}FCUN<*krZ_h}aMwgFYRde!a@nbdgprru+dXE2 znH(z_Q)pb7(rO$=_@O5?{jUd_%fvV8_p&+k4w?@n)r%_RBj|9>M?dK0-rh9EtteiN ze}~3xQz$E$Tfz=PFJx?DZ$iSyqHXb!0P_B-%A35!inme|%YuN#QSzly@r=*iMvE~) za@$H}p?xUr;?gali8Nc0D=S+DqQjMAR(Zh$t2+AgsIV}T?@b>CoPIB7RTM}76JOo+ z(t~`e=mTeg@Iru1Shn~o-`K%#lSX9|&f5z>=L=$D^oDM8hSVH@;ftF_nh+dzH&XlJ zYoh~l?xZzMWNiClQ(>0WK!WCJK=N6=ZHU!`EK^3XdHYoZ_SoRW zJd$s0T*MRJ^J5qM%_Qbd`Yt*AOM@chJqk{DySX5}Q8220auSf;O3ox$_Mqu(`r$z| ze_xzXR3`Nu-_Rn|tD*|mJ|dkWnhPD{m%#YEO{GYv*5qK5?c<|TSz}0groDs7VNY?Y zqkEiQ!o;(#1b8yROHowS#Y~;9In$;{sE~ohx-gi>uC27}T19B6uj@556W8I^_tLHI)!!#b;3Q_}{dcf(eH*2Y1pf?icf4`q1{YKf z0Clv7I>`ckoQNMc#6-lN3yO*eib$G>K9_wiEh{0)FCro /dev/null + +echo "๐Ÿ’ฐ Current wallet: $(solana address)" +echo "๐ŸŒ Cluster: devnet" +echo "" + +# Function to burn tokens for a specific mint +burn_token() { + local mint=$1 + echo "๐Ÿ”ฅ Processing mint: $mint" + + # Get the token account balance + local balance=$(spl-token balance $mint --url devnet 2>/dev/null || echo "0") + + if [ "$balance" == "0" ] || [ -z "$balance" ]; then + echo " โ„น๏ธ No balance or account doesn't exist - skipping" + return + fi + + echo " ๐Ÿ’ฐ Current balance: $balance" + + # Try to burn all tokens + echo " ๐Ÿ”ฅ Burning $balance tokens..." + local result=$(spl-token burn $mint $balance --url devnet 2>&1) + + if [ $? -eq 0 ]; then + echo " โœ… Successfully burned $balance tokens" + + # Try to close the token account to reclaim SOL + echo " ๐Ÿ—‘๏ธ Attempting to close token account..." + spl-token close $mint --url devnet 2>/dev/null + if [ $? -eq 0 ]; then + echo " โœ… Token account closed and SOL reclaimed" + else + echo " โš ๏ธ Token account could not be closed (may still have remaining dust)" + fi + else + echo " โŒ Failed to burn tokens: $result" + fi + + echo "" +} + +# Burn tokens for each mint +for mint in "${TOKENS[@]}"; do + burn_token $mint +done + +echo "๐ŸŽฏ Token burning process complete!" +echo "" +echo "๐Ÿ“Š Final token account status:" +spl-token accounts --url devnet + +echo "" +echo "๐Ÿ’ฐ Final SOL balance:" +solana balance --url devnet + +echo "" +echo "โœ… Ready for clean testing!" +echo "๐Ÿงช You can now test the PoWH program from a clean state" \ No newline at end of file diff --git a/close-all-token-accounts.sh b/close-all-token-accounts.sh new file mode 100755 index 0000000..e0d585d --- /dev/null +++ b/close-all-token-accounts.sh @@ -0,0 +1,111 @@ +#!/bin/bash + +echo "๐Ÿ—‘๏ธ Closing All Token Accounts" +echo "==============================" +echo "โš ๏ธ This will close ALL token accounts and burn any remaining tokens!" +echo "๐Ÿ’ฐ You will reclaim the SOL used for rent" +echo "๐Ÿ“ Cluster: devnet (safe for testing)" +echo "" + +# Confirm before proceeding +read -p "Are you sure you want to close ALL token accounts? (type 'yes' to confirm): " confirm +if [ "$confirm" != "yes" ]; then + echo "โŒ Aborted - no accounts closed" + exit 1 +fi + +echo "๐Ÿ—‘๏ธ Starting token account closure process..." +echo "" + +# Set to devnet +solana config set --url devnet > /dev/null + +echo "๐Ÿ’ฐ Current wallet: $(solana address)" +echo "๐ŸŒ Cluster: devnet" +echo "" +echo "๐Ÿ’ฐ SOL balance before: $(solana balance --url devnet)" +echo "" + +# Get all token accounts and close them +echo "๐Ÿ“Š Current token accounts:" +spl-token accounts --url devnet +echo "" + +# List of specific token account addresses to close (from verbose output) +TOKEN_ACCOUNTS=( + "8zr6uF84gTzY5V1MP9EeEfy6m2LNULkH4VEFgxY1fKwh" + "679vTxSpUw55buSgPpUsEp66eiUCQs3Y7acpoeTt6W97" + "4t7iZeFsLXA2Ro1EyxoR88Aqnq62Da4txb74eiC2WovQ" + "AmNmTDXjESr1PZTjEC6QiWqLbeZxdxdQkM2TasSUL88QK" +) + +# Function to close a token account +close_account() { + local account=$1 + echo "๐Ÿ—‘๏ธ Processing token account: $account" + + # Try to close the account directly + local result=$(spl-token close-account $account --url devnet 2>&1) + + if [ $? -eq 0 ]; then + echo " โœ… Successfully closed account and reclaimed SOL" + else + echo " โŒ Failed to close account: $result" + + # If direct closure failed, try to get the mint and burn first + echo " ๐Ÿ”„ Trying alternative approach..." + + # Get account info to find the mint + local account_info=$(solana account $account --url devnet --output json 2>/dev/null) + if [ $? -eq 0 ]; then + echo " โ„น๏ธ Account exists, attempting force closure..." + spl-token close-account $account --force --url devnet 2>/dev/null + if [ $? -eq 0 ]; then + echo " โœ… Force closure successful" + else + echo " โš ๏ธ Account could not be closed (may have non-zero balance)" + fi + else + echo " โ„น๏ธ Account may already be closed or doesn't exist" + fi + fi + echo "" +} + +# Close all token accounts +for account in "${TOKEN_ACCOUNTS[@]}"; do + close_account $account +done + +# Also try to close by mint address (alternative approach) +echo "๐Ÿ”„ Attempting closure by mint address as backup..." +MINTS=( + "4auc435LJ9AfprLeMziHTJbP8wQmbSi7o4ZpeXdR66jQ" + "5xEQF57sR2L3tJBfdiiqaVtepTMa7nQR9uMybMojEY8n" + "8cJ8APx5TwwmpeRk4Zhz1Qbyi1T3HHdHUEw4GUJqxAGN" + "AK9fXWyDNWurYjZSDhbqBo6fczqLmCLbCZ3vq9Vftsqb" +) + +for mint in "${MINTS[@]}"; do + echo "๐Ÿ—‘๏ธ Attempting to close account for mint: $mint" + spl-token close $mint --url devnet 2>/dev/null + if [ $? -eq 0 ]; then + echo " โœ… Closed successfully" + else + echo " โš ๏ธ Could not close (may not exist or have balance)" + fi +done + +echo "" +echo "๐ŸŽฏ Token account closure process complete!" +echo "" +echo "๐Ÿ“Š Final token account status:" +spl-token accounts --url devnet + +echo "" +echo "๐Ÿ’ฐ Final SOL balance:" +solana balance --url devnet + +echo "" +echo "โœ… Token accounts processed!" +echo "๐Ÿงช Ready for clean testing with your PoWH program" \ No newline at end of file diff --git a/config.json b/config.json new file mode 100644 index 0000000..93284d5 --- /dev/null +++ b/config.json @@ -0,0 +1,6 @@ +{ + "cluster": "https://api.devnet.solana.com", + "programId": "CQKiz5PKgoQjxNDkGowPWWXu8MJCm2HgonfYAtcFZX87", + "mint": "8cJ8APx5TwwmpeRk4Zhz1Qbyi1T3HHdHUEw4GUJqxAGN", + "devWallet": "GPFYcM3svcM4Srko6ExLYeSW4Gjwf6XoV4k1eSXoGHoK" +} diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..5c74f80 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Deployment script for Lottery Simple DApp +set -e + +echo "๐Ÿš€ Building and deploying Lottery Simple DApp..." + +# Build the Docker image +echo "๐Ÿ“ฆ Building Docker image..." +docker-compose build + +# Stop existing container if running +echo "๐Ÿ›‘ Stopping existing container..." +docker-compose down + +# Start the new container +echo "โ–ถ๏ธ Starting new container..." +docker-compose up -d + +# Wait a moment for startup +sleep 3 + +# Check if it's running +if curl -s http://localhost:14888/health > /dev/null; then + echo "โœ… Deployment successful!" + echo "๐ŸŒ Frontend available at: http://localhost:14888" + echo "๐Ÿฅ Health check: http://localhost:14888/health" +else + echo "โŒ Deployment failed - health check failed" + echo "๐Ÿ“‹ Container logs:" + docker-compose logs + exit 1 +fi + +echo "๐ŸŽ‰ Deployment complete!" \ No newline at end of file diff --git a/devnet-config.json b/devnet-config.json new file mode 100644 index 0000000..f1d9686 --- /dev/null +++ b/devnet-config.json @@ -0,0 +1,7 @@ +{ + "programId": "8hcuEUBcuVBYyD53QPFQrRSWJjQy3UVURiqJpcuvgrTf", + "mintAddress": "GrCHRSRHdZtiz2fiv1iUgigXCfB5Ta3j8gXepZUUTPP2", + "devWallet": "GPFYcM3svcM4Srko6ExLYeSW4Gjwf6XoV4k1eSXoGHoK", + "cluster": "devnet", + "rpcUrl": "https://api.devnet.solana.com" +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..0bd4929 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,15 @@ +version: '3.8' + +services: + lottery-simple-dapp: + build: . + container_name: lottery-simple-dapp + ports: + - "14888:80" + restart: unless-stopped + networks: + - lottery-dapp-net + +networks: + lottery-dapp-net: + driver: bridge \ No newline at end of file diff --git a/favicon-16.png b/favicon-16.png new file mode 100644 index 0000000000000000000000000000000000000000..8f9d2dee1dfeb86c405c9ac4377c2dee33d4ad4b GIT binary patch literal 1600 zcmZ{kdpOg39LIm5_T(O-<79TWrU{ufVH}U;IuW6GR%B*sF`Hf7a#^X#F}Exox7-z> zC$|dI5^|T^Rc^UuwCa#c;jHt=dCvKx&+~l0@9+2h`Mf@#|2}cfr)=e<)ujOdki*$o zxrkNq$5E6J--2D$EU`d{b}o(p5UvFP?CSuqDYn=P0Kh^5z@j$*VEzOEHCk5PX)|$R zkIzY4tDS#hugBV3u>)e_9IZhgr1vObl#LalFBNBx? zGyT}}o-OIt8l16^_SaP_G|ur5?#r$lL);GzByY!TT*?NzDXnfK3;s(n0y^_T%E2bB zs5|58Iy=rx{g14Y_L%Sx!Mm-foNdAJk+Pw0qTX>4f8yJcb@7bAs$Md42A^{C{`fJx zvWhxLXY-In@)NnEjodpMXfB+Kls*T<5L7+1TCE@+^b>2%up3dae=Eiyo@6I3Or}k%9}mQ{E~6=(Qg7tM)spvzd{QLwuARI?k-#TD#+MYx4>`? zir-faGiYI=g??LJ)Tz;SwZkiyU*2kw>`Im(*kH=%QY+-=&6W#=j~M0=G)NlPt`Lbz zFFRY$f2e76X+J)Xr=F!)*{WVg89Y^wlu`pZxk!f_os6~7uF2@$rg^p<&sy5PD zmLu?%#Ui$>1{bh8>P1GJ3i%|%w$txiJ63zGXB}&`$$eKYn6>q5$_edh=Mpvb!zbe( z_}6{jolvyQo0xERQHgoJxz-zC0bBe~>V|;d)T+YVckcEN8t*!zP^~haRbCqXM57WP zz4o&9PPM;AKK~8Pf37+dHFWrj>g`Xuy{x3+s!FZzbhSvBz4dIB7M%_j6{E5}HA|kf z_J-wI7LMgx`t%9TmLC8VLHfh_62H!Pd*)}bcE=PP*vPJ*_hl|4=V#n=v&>I={VAtX#ED!A}{rua^h5j2ugm;SE79IM+YS z55p3nV7~j}%XX*tJ%N4c&q&iR z!)O}n9c^sMsQn@Pbx{rdKu^4C3df$As{!Pi=|I?Y#GS1=|2o(3rYMAohF$vH>fC5+ zgh;QyZ3O<9D-&z@dv)3%%PT$LWXW6jxUXsU3HfZL5^$Ya*{qrDQMw1zB2x#`eL$ zaaa{tR#VgRcljKl<&C$t=44(_id%VbaHFV1>sV3$B@2%14E=YfHFRpxy_!XSV%SEk zNPlUZRS?(6i?lzwyU%?Z_Y52UG`?y*3wqZ5lP!^8k*{dZOFiZ zb&F-D&*1gp&-ZGAznfO)I%P3 zGcd#$8egkO=R;;f7|gH$62zAp5I~?q803qA;*8aQ zel&6@DS#n1EyW$^gaAk|iOwVw#g71TAnAusr3aGe5CV}XE?_{?=j5^C*#OS^lvTwE H?`wYpGr7%= literal 0 HcmV?d00001 diff --git a/favicon-32.png b/favicon-32.png new file mode 100644 index 0000000000000000000000000000000000000000..3a06d414dfed6de36ac817c66a889971775629f8 GIT binary patch literal 642 zcmeAS@N?(olHy`uVBq!ia0vp^3Lq@N1SHpV7(M`Tl0AZa85pY67#JE_7#My5g&JNk zFq9fFFuY1&V6d9Oz#v{QXIG#NP=YPV+uh|q7;r{>zXKFuFY)wsWq--V#&0ET!C%h{ z6nf_A;usQfIQhvlF@b=91c^-znG0kcLis=3JC_>VE0Ll8ka>n)ip(p|j#jZYF|n-7 zxwhRpm+sfUowdrnc3MXF2ei*%+qvd zTTY%hQA{nlY2x&j56q7i*G;HuD9e-;m+*C(!Wd)db~vabYUP|ON8C*L15PJpH8T1+ zO*#HNLvF#g%bu*xcKkd)s}fFBCz$Z_m`GH5c1Q?_w%$`-94p@LR>`n8*nfB3p$Yy@ zi{&IT8jOtCC3pN@WA*d;TT$k>25%THw>sBl6rNC5WRN_ZF7Mg5M*-+#)e_f;l9a@f zRIB8oR3OD*WME{fYiOWrU>0I%YGrC+WoW5wU|?lnaH`OP4@E<6eoAIqC2kG7bs9DS zHE6(XD9OxCEiOsSEx@hkjpf2zpq>Jdp5n4}tK|IL+|;}h1_h_Y1ug{( z-K5MCh2oORoK%IB{G6P`B8B42^gN)93lgs&vm!O87%1orG@&RlN1-gWs3bEP$jHge kO9hGN7v-fEDI_K*0~Hi2%zVb@2(+8Q)78&qol`;+0M*vlIsgCw literal 0 HcmV?d00001 diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..9d398da7a16bc0874c48e14081c239a313310ec7 GIT binary patch literal 5430 zcmd^DJxo+V5MGUiaz7lz`{B6b-o57?7>&_@U@Qz47z+v$?Ws)ALZeclXJaCF4S{HB z(87X9Vq;};q7aN4DthKlckj^flF9DQ&V1j@?9S#uh%I8P z2!#ZVhUncUL{(hriZg>*C}izPh`LXh75rR#k#BYqem-!e(m?m$zhW|^LC z*~jtC8iBuTQnbIPz79F-2u?LND>2wd#hVED5ck)hwJwVJ_CDmTV~F{-BjNYi(iB`C zC&!bjCC)}aa=l>``Xab_K8=MFXHo3w!u7r|1};WXxD>_7qjgYWuz#Bsz|>e0vyTmw zifK%bwcyQzU6`9NusChv=bU9@a4hb6vH!kg0K=DKxKn7r?K}yMBQ%Z<#j)@*jqfE3 z0~aE89x8w4pS+huX*`9u#XrLFe12u($BczM<*{ad;#jL)mVrB z-4nyE3+L6-UB6=eIRByksLeStuv7zw|NW$a`6?Ws`xn{lX{8I@L(o@@@n56Jz)_lR>z z*ZR@6Gh+7<-&fYL@IL!H{p0kYjOom|cexjxwLCig>wXV(*Zj^HB%LJu9@!s1Jg!gX z-puo^T!ZpFyr@YfxV{{|0!O@vaWzbbo5$5`z1ZI@{9j+2P>GzsYG2lsQ-2v}z4QN> zUpIDDjdU?&dljCpPR7%n>vE~=bvb2w6`rn6#`Aivime<@6`roH+Wh74xqWqWRoS~S zy + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..ef6ea7c --- /dev/null +++ b/index.html @@ -0,0 +1,198 @@ + + + + + + PoWH3d Lottery + + + + + + + + + + + + + + + + +
+ + + + + +
+
+
+
+
+

PoWH3d Lottery

+
+
0.000 SOL
+
+

8โ€‘bit bondingโ€‘curve lottery on Solana โ€ข ultraโ€‘low fees โ€ข instant settlements

+
+
+ +
+
+
+ + + +
+
+
+
+

Connect

+
+ + + + +
+ +
+ + + + +
+ + + +
+
+

Pot

+
+ + + + +
+
+
+
+ + + +
+ + + + + +
+

How it Works

+

Buy with SOL to mint lottery tokens on a bonding curve. A small protocol fee and referral bonus are taken on each trade; the rest boosts the community pot and holder rewards. Sell anytime; proceeds are net of fees. As activity grows, holders earn more.

+
+ + +
+ +
+ ยฉ PoWH3d Lottery ยท Built for Solana + Privacy +
+ + diff --git a/init-program.js b/init-program.js new file mode 100644 index 0000000..47b92dc --- /dev/null +++ b/init-program.js @@ -0,0 +1,95 @@ +const { Connection, PublicKey, Keypair } = require('@solana/web3.js'); +const { Program, AnchorProvider, web3, utils } = require('@coral-xyz/anchor'); +const fs = require('fs'); + +// Load configuration +const config = JSON.parse(fs.readFileSync('./devnet-config.json', 'utf8')); +const idl = JSON.parse(fs.readFileSync('./target/idl/lottery_simple.json', 'utf8')); + +async function initializeProgram() { + console.log('๐Ÿš€ Initializing PoWH Program...'); + console.log('==============================='); + + // Set up connection and provider + const connection = new Connection(config.rpcUrl, 'confirmed'); + + // Load wallet + const walletKeypair = Keypair.fromSecretKey( + Uint8Array.from(JSON.parse(fs.readFileSync(process.env.HOME + '/.config/solana/id.json', 'utf8'))) + ); + + const wallet = { + publicKey: walletKeypair.publicKey, + signTransaction: async (tx) => { + tx.sign(walletKeypair); + return tx; + }, + signAllTransactions: async (txs) => { + return txs.map(tx => { + tx.sign(walletKeypair); + return tx; + }); + } + }; + + const provider = new AnchorProvider(connection, wallet, { commitment: 'confirmed' }); + const program = new Program(idl, new PublicKey(config.programId), provider); + + console.log('๐Ÿ“‹ Program ID:', config.programId); + console.log('๐Ÿ’ฐ Authority:', walletKeypair.publicKey.toString()); + console.log('๐Ÿช™ Mint:', config.mintAddress); + console.log('๐ŸŒ Cluster:', config.cluster); + + try { + // Derive PDAs + const [statePda] = PublicKey.findProgramAddressSync( + [Buffer.from("state")], + program.programId + ); + + const [vaultPda] = PublicKey.findProgramAddressSync( + [Buffer.from("vault")], + program.programId + ); + + console.log('๐Ÿ›๏ธ State PDA:', statePda.toString()); + console.log('๐Ÿฆ Vault PDA:', vaultPda.toString()); + + // Initialize the program + console.log('โณ Sending initialize transaction...'); + + const tx = await program.methods + .initialize() + .accounts({ + state: statePda, + vault: vaultPda, + mint: new PublicKey(config.mintAddress), + authority: walletKeypair.publicKey, + systemProgram: web3.SystemProgram.programId, + tokenProgram: utils.token.TOKEN_PROGRAM_ID, + }) + .rpc(); + + console.log('โœ… Program initialized successfully!'); + console.log('๐Ÿ“ Transaction:', tx); + + // Update config with PDAs + const updatedConfig = { + ...config, + statePda: statePda.toString(), + vaultPda: vaultPda.toString(), + initialized: true, + initTx: tx + }; + + fs.writeFileSync('./devnet-config.json', JSON.stringify(updatedConfig, null, 2)); + console.log('๐Ÿ’พ Configuration updated with PDA addresses'); + + } catch (error) { + console.error('โŒ Initialization failed:', error); + process.exit(1); + } +} + +// Run the initialization +initializeProgram().catch(console.error); \ No newline at end of file diff --git a/lottery_program/.gitignore b/lottery_program/.gitignore new file mode 100644 index 0000000..2e0446b --- /dev/null +++ b/lottery_program/.gitignore @@ -0,0 +1,7 @@ +.anchor +.DS_Store +target +**/*.rs.bk +node_modules +test-ledger +.yarn diff --git a/lottery_program/.prettierignore b/lottery_program/.prettierignore new file mode 100644 index 0000000..4142583 --- /dev/null +++ b/lottery_program/.prettierignore @@ -0,0 +1,7 @@ +.anchor +.DS_Store +target +node_modules +dist +build +test-ledger diff --git a/lottery_program/Anchor.toml b/lottery_program/Anchor.toml new file mode 100644 index 0000000..2861f1f --- /dev/null +++ b/lottery_program/Anchor.toml @@ -0,0 +1,19 @@ +[toolchain] +package_manager = "yarn" + +[features] +resolution = true +skip-lint = false + +[programs.localnet] +lottery_program = "9Un1evsbgmJa9XH7baEeTSwpQjAPz8pmDFVccVp8HBY4" + +[registry] +url = "https://api.apr.dev" + +[provider] +cluster = "localnet" +wallet = "~/.config/solana/id.json" + +[scripts] +test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" diff --git a/mint-keypair.json b/mint-keypair.json new file mode 100644 index 0000000..ac987e5 --- /dev/null +++ b/mint-keypair.json @@ -0,0 +1 @@ +[228,210,161,242,26,178,81,232,150,232,162,61,163,178,12,246,125,252,229,194,95,188,161,46,214,173,205,225,0,193,115,79,235,121,133,123,65,221,159,48,80,179,37,118,124,100,188,234,93,73,20,79,101,38,82,36,1,80,66,19,145,90,81,53] \ No newline at end of file diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..24bd7bb --- /dev/null +++ b/nginx.conf @@ -0,0 +1,38 @@ +server { + listen 80; + server_name _; + root /var/www/html; + index index.html index.php; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + + # Enable gzip compression + gzip on; + gzip_types text/plain text/css application/javascript application/json image/svg+xml; + + # PHP location + location ~ \.php$ { + try_files $uri =404; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass 127.0.0.1:9000; + fastcgi_index index.php; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + } + + # Main location + location / { + try_files $uri $uri/ /index.html; + } + + # Health check endpoint + location /health { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; + } +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..07e7cba --- /dev/null +++ b/package-lock.json @@ -0,0 +1,755 @@ +{ + "name": "lottery-simple", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "lottery-simple", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@coral-xyz/anchor": "^0.31.1", + "@solana/web3.js": "^1.98.4" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@coral-xyz/anchor": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@coral-xyz/anchor/-/anchor-0.31.1.tgz", + "integrity": "sha512-QUqpoEK+gi2S6nlYc2atgT2r41TT3caWr/cPUEL8n8Md9437trZ68STknq897b82p5mW0XrTBNOzRbmIRJtfsA==", + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "@coral-xyz/anchor-errors": "^0.31.1", + "@coral-xyz/borsh": "^0.31.1", + "@noble/hashes": "^1.3.1", + "@solana/web3.js": "^1.69.0", + "bn.js": "^5.1.2", + "bs58": "^4.0.1", + "buffer-layout": "^1.2.2", + "camelcase": "^6.3.0", + "cross-fetch": "^3.1.5", + "eventemitter3": "^4.0.7", + "pako": "^2.0.3", + "superstruct": "^0.15.4", + "toml": "^3.0.0" + }, + "engines": { + "node": ">=17" + } + }, + "node_modules/@coral-xyz/anchor-errors": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@coral-xyz/anchor-errors/-/anchor-errors-0.31.1.tgz", + "integrity": "sha512-NhNEku4F3zzUSBtrYz84FzYWm48+9OvmT1Hhnwr6GnPQry2dsEqH/ti/7ASjjpoFTWRnPXrjAIT1qM6Isop+LQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=10" + } + }, + "node_modules/@coral-xyz/borsh": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.31.1.tgz", + "integrity": "sha512-9N8AU9F0ubriKfNE3g1WF0/4dtlGXoBN/hd1PvbNBamBNwRgHxH4P+o3Zt7rSEloW1HUs6LfZEchlx9fW7POYw==", + "license": "Apache-2.0", + "dependencies": { + "bn.js": "^5.1.2", + "buffer-layout": "^1.2.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@solana/web3.js": "^1.69.0" + } + }, + "node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@solana/buffer-layout": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz", + "integrity": "sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==", + "license": "MIT", + "dependencies": { + "buffer": "~6.0.3" + }, + "engines": { + "node": ">=5.10" + } + }, + "node_modules/@solana/codecs-core": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@solana/codecs-core/-/codecs-core-2.3.0.tgz", + "integrity": "sha512-oG+VZzN6YhBHIoSKgS5ESM9VIGzhWjEHEGNPSibiDTxFhsFWxNaz8LbMDPjBUE69r9wmdGLkrQ+wVPbnJcZPvw==", + "license": "MIT", + "dependencies": { + "@solana/errors": "2.3.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": ">=5.3.3" + } + }, + "node_modules/@solana/codecs-numbers": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@solana/codecs-numbers/-/codecs-numbers-2.3.0.tgz", + "integrity": "sha512-jFvvwKJKffvG7Iz9dmN51OGB7JBcy2CJ6Xf3NqD/VP90xak66m/Lg48T01u5IQ/hc15mChVHiBm+HHuOFDUrQg==", + "license": "MIT", + "dependencies": { + "@solana/codecs-core": "2.3.0", + "@solana/errors": "2.3.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": ">=5.3.3" + } + }, + "node_modules/@solana/errors": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@solana/errors/-/errors-2.3.0.tgz", + "integrity": "sha512-66RI9MAbwYV0UtP7kGcTBVLxJgUxoZGm8Fbc0ah+lGiAw17Gugco6+9GrJCV83VyF2mDWyYnYM9qdI3yjgpnaQ==", + "license": "MIT", + "dependencies": { + "chalk": "^5.4.1", + "commander": "^14.0.0" + }, + "bin": { + "errors": "bin/cli.mjs" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": ">=5.3.3" + } + }, + "node_modules/@solana/web3.js": { + "version": "1.98.4", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.98.4.tgz", + "integrity": "sha512-vv9lfnvjUsRiq//+j5pBdXig0IQdtzA0BRZ3bXEP4KaIyF1CcaydWqgyzQgfZMNIsWNWmG+AUHwPy4AHOD6gpw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.0", + "@noble/curves": "^1.4.2", + "@noble/hashes": "^1.4.0", + "@solana/buffer-layout": "^4.0.1", + "@solana/codecs-numbers": "^2.1.0", + "agentkeepalive": "^4.5.0", + "bn.js": "^5.2.1", + "borsh": "^0.7.0", + "bs58": "^4.0.1", + "buffer": "6.0.3", + "fast-stable-stringify": "^1.0.0", + "jayson": "^4.1.1", + "node-fetch": "^2.7.0", + "rpc-websockets": "^9.0.2", + "superstruct": "^2.0.2" + } + }, + "node_modules/@solana/web3.js/node_modules/superstruct": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-2.0.2.tgz", + "integrity": "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", + "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "license": "MIT" + }, + "node_modules/@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "7.4.7", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", + "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/base-x": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.11.tgz", + "integrity": "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bn.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz", + "integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==", + "license": "MIT" + }, + "node_modules/borsh": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/borsh/-/borsh-0.7.0.tgz", + "integrity": "sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==", + "license": "Apache-2.0", + "dependencies": { + "bn.js": "^5.2.0", + "bs58": "^4.0.0", + "text-encoding-utf-8": "^1.0.2" + } + }, + "node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "license": "MIT", + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-layout": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/buffer-layout/-/buffer-layout-1.2.2.tgz", + "integrity": "sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==", + "license": "MIT", + "engines": { + "node": ">=4.5" + } + }, + "node_modules/bufferutil": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.9.tgz", + "integrity": "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/commander": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.1.tgz", + "integrity": "sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/cross-fetch": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", + "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.7.0" + } + }, + "node_modules/delay": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", + "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "license": "MIT" + }, + "node_modules/es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", + "license": "MIT", + "dependencies": { + "es6-promise": "^4.0.3" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==", + "engines": { + "node": "> 0.1.90" + } + }, + "node_modules/fast-stable-stringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz", + "integrity": "sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==", + "license": "MIT" + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/isomorphic-ws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", + "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/jayson": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/jayson/-/jayson-4.2.0.tgz", + "integrity": "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg==", + "license": "MIT", + "dependencies": { + "@types/connect": "^3.4.33", + "@types/node": "^12.12.54", + "@types/ws": "^7.4.4", + "commander": "^2.20.3", + "delay": "^5.0.0", + "es6-promisify": "^5.0.0", + "eyes": "^0.1.8", + "isomorphic-ws": "^4.0.1", + "json-stringify-safe": "^5.0.1", + "stream-json": "^1.9.1", + "uuid": "^8.3.2", + "ws": "^7.5.10" + }, + "bin": { + "jayson": "bin/jayson.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jayson/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "license": "ISC" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "license": "(MIT AND Zlib)" + }, + "node_modules/rpc-websockets": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-9.2.0.tgz", + "integrity": "sha512-DS/XHdPxplQTtNRKiBCRWGBJfjOk56W7fyFUpiYi9fSTWTzoEMbUkn3J4gB0IMniIEVeAGR1/rzFQogzD5MxvQ==", + "license": "LGPL-3.0-only", + "dependencies": { + "@swc/helpers": "^0.5.11", + "@types/uuid": "^8.3.4", + "@types/ws": "^8.2.2", + "buffer": "^6.0.3", + "eventemitter3": "^5.0.1", + "uuid": "^8.3.2", + "ws": "^8.5.0" + }, + "funding": { + "type": "paypal", + "url": "https://paypal.me/kozjak" + }, + "optionalDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + } + }, + "node_modules/rpc-websockets/node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/rpc-websockets/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/rpc-websockets/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/stream-chain": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.5.tgz", + "integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==", + "license": "BSD-3-Clause" + }, + "node_modules/stream-json": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.9.1.tgz", + "integrity": "sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==", + "license": "BSD-3-Clause", + "dependencies": { + "stream-chain": "^2.2.5" + } + }, + "node_modules/superstruct": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.15.5.tgz", + "integrity": "sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==", + "license": "MIT" + }, + "node_modules/text-encoding-utf-8": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz", + "integrity": "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==" + }, + "node_modules/toml": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==", + "license": "MIT" + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..f960edb --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "lottery-simple", + "version": "1.0.0", + "description": "", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@coral-xyz/anchor": "^0.31.1", + "@solana/web3.js": "^1.98.4" + } +} diff --git a/programs/lottery-simple/Cargo.toml b/programs/lottery-simple/Cargo.toml new file mode 100644 index 0000000..cec5aea --- /dev/null +++ b/programs/lottery-simple/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "lottery-simple" +version = "0.1.0" +description = "A PoWH (Proof of Weak Hands) token with bonding curve" +edition = "2021" + +[lib] +crate-type = ["cdylib", "lib"] +name = "lottery_simple" + +[features] +no-entrypoint = [] +no-idl = [] +no-log-ix-name = [] +cpi = ["no-entrypoint"] +idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build"] +default = [] + +[dependencies] +anchor-lang = { version = "0.31.0", features = ["init-if-needed"] } +anchor-spl = "0.31.0" diff --git a/programs/lottery-simple/src/lib.rs b/programs/lottery-simple/src/lib.rs new file mode 100644 index 0000000..0184e58 --- /dev/null +++ b/programs/lottery-simple/src/lib.rs @@ -0,0 +1,457 @@ +use anchor_lang::prelude::*; +use anchor_spl::token::{self, Burn, Mint, MintTo, Token, TokenAccount}; +use anchor_spl::associated_token::AssociatedToken; + +declare_id!("8hcuEUBcuVBYyD53QPFQrRSWJjQy3UVURiqJpcuvgrTf"); + +#[program] +pub mod lottery_simple { + use super::*; + + pub fn initialize(ctx: Context) -> Result<()> { + let state = &mut ctx.accounts.state; + state.bump = ctx.bumps.state; + state.vault_bump = ctx.bumps.vault; + state.authority = ctx.accounts.authority.key(); + state.mint = ctx.accounts.mint.key(); + state.vault = ctx.accounts.vault.key(); + state.total_supply = 0; + state.dev_fee_bps = 300; // 3% + state.dividend_fee_bps = 200; // 2% + state.token_price_initial = 100_000; // 0.0001 SOL + state.token_price_increment = 10_000; // 0.00001 SOL per token + state.profit_per_share = 0; + msg!("PoWH initialized with mint: {}", ctx.accounts.mint.key()); + Ok(()) + } + + pub fn register_user(ctx: Context) -> Result<()> { + let user = &mut ctx.accounts.user; + user.owner = ctx.accounts.owner.key(); + user.token_account = ctx.accounts.token_account.key(); + user.dividends_withdrawn = 0; + msg!("User registered: {}", ctx.accounts.owner.key()); + Ok(()) + } + + pub fn buy_tokens(ctx: Context, sol_amount: u64) -> Result<()> { + // Capture needed values before mutable borrow + let state_bump = ctx.accounts.state.bump; + let buyer_key = ctx.accounts.buyer.key(); + let state_account_info = ctx.accounts.state.to_account_info(); + + let state = &mut ctx.accounts.state; + + // Calculate fees + let dev_fee = (sol_amount as u128 * state.dev_fee_bps as u128) / 10_000; + let dividend_fee = (sol_amount as u128 * state.dividend_fee_bps as u128) / 10_000; + let net_purchase = sol_amount as u128 - dev_fee - dividend_fee; + + // Transfer dev fee from buyer to dev wallet + if dev_fee > 0 { + let transfer_ix = anchor_lang::solana_program::system_instruction::transfer( + &ctx.accounts.buyer.key(), + &ctx.accounts.dev_wallet.key(), + dev_fee as u64, + ); + + anchor_lang::solana_program::program::invoke( + &transfer_ix, + &[ + ctx.accounts.buyer.to_account_info(), + ctx.accounts.dev_wallet.to_account_info(), + ctx.accounts.system_program.to_account_info(), + ], + )?; + } + + // Transfer net purchase + dividend fee to vault (vault holds everything except dev fee) + let vault_amount = net_purchase + dividend_fee; + if vault_amount > 0 { + let transfer_ix = anchor_lang::solana_program::system_instruction::transfer( + &ctx.accounts.buyer.key(), + &ctx.accounts.vault.key(), + vault_amount as u64, + ); + + anchor_lang::solana_program::program::invoke( + &transfer_ix, + &[ + ctx.accounts.buyer.to_account_info(), + ctx.accounts.vault.to_account_info(), + ctx.accounts.system_program.to_account_info(), + ], + )?; + } + + // Distribute dividends to existing holders + if state.total_supply > 0 && dividend_fee > 0 { + let dividend_per_token = (dividend_fee * PRECISION) / state.total_supply as u128; + state.profit_per_share += dividend_per_token; + } + + // Calculate tokens to mint using bonding curve + let tokens_to_mint = calculate_tokens_for_sol(net_purchase as u64, state.total_supply, state)?; + + // Mint tokens to buyer + let seeds = &[b"state".as_ref(), &[state_bump]]; + let signer_seeds = &[&seeds[..]]; + + let cpi_ctx = CpiContext::new_with_signer( + ctx.accounts.token_program.to_account_info(), + MintTo { + mint: ctx.accounts.mint.to_account_info(), + to: ctx.accounts.buyer_token_account.to_account_info(), + authority: state_account_info, + }, + signer_seeds, + ); + token::mint_to(cpi_ctx, tokens_to_mint)?; + + state.total_supply += tokens_to_mint; + + emit!(TradeEvent { + user: buyer_key, + tokens: tokens_to_mint, + sol_amount, + is_buy: true, + }); + + msg!("Bought {} tokens for {} SOL", tokens_to_mint, sol_amount); + Ok(()) + } + + pub fn sell_tokens(ctx: Context, tokens_to_sell: u64) -> Result<()> { + let state = &mut ctx.accounts.state; + require!(tokens_to_sell > 0, ErrorCode::InvalidAmount); + require!(ctx.accounts.seller_token_account.amount >= tokens_to_sell, ErrorCode::InsufficientFunds); + + // Calculate SOL to return using bonding curve + let calculated_sol = calculate_sol_for_tokens(tokens_to_sell, state.total_supply, state)?; + + // Safety check: limit to vault balance to prevent errors + let vault_balance = ctx.accounts.vault.to_account_info().lamports(); + let sol_to_return = if calculated_sol > vault_balance { + msg!("Calculated return {} exceeds vault balance {}, limiting to vault balance", calculated_sol, vault_balance); + // Leave enough for rent exemption - be more conservative + vault_balance.saturating_sub(5000) // Leave 5000 lamports for rent exemption + } else { + calculated_sol + }; + + // Burn tokens from seller + let cpi_ctx = CpiContext::new( + ctx.accounts.token_program.to_account_info(), + Burn { + mint: ctx.accounts.mint.to_account_info(), + from: ctx.accounts.seller_token_account.to_account_info(), + authority: ctx.accounts.seller.to_account_info(), + }, + ); + token::burn(cpi_ctx, tokens_to_sell)?; + + state.total_supply -= tokens_to_sell; + + // Transfer SOL from vault to seller (direct lamport manipulation) + // This is the correct approach for program-owned PDAs as per Solana documentation + if sol_to_return > 0 { + // Direct lamport manipulation - standard practice for program-owned accounts + **ctx.accounts.vault.to_account_info().try_borrow_mut_lamports()? -= sol_to_return; + **ctx.accounts.seller.to_account_info().try_borrow_mut_lamports()? += sol_to_return; + + msg!("Transferred {} lamports from vault to seller", sol_to_return); + } + + emit!(TradeEvent { + user: ctx.accounts.seller.key(), + tokens: tokens_to_sell, + sol_amount: sol_to_return, + is_buy: false, + }); + + msg!("Sold {} tokens for {} SOL", tokens_to_sell, sol_to_return); + Ok(()) + } + + pub fn withdraw_dividends(ctx: Context) -> Result<()> { + let state = &ctx.accounts.state; + let user = &mut ctx.accounts.user; + let token_balance = ctx.accounts.token_account.amount as u128; + + // Calculate available dividends + let total_dividends = (state.profit_per_share * token_balance) / PRECISION; + let available_dividends = total_dividends - user.dividends_withdrawn; + + require!(available_dividends > 0, ErrorCode::NoDividends); + + user.dividends_withdrawn = total_dividends; + + // Transfer dividends from vault to user (direct lamport manipulation) + // This is the correct approach for program-owned PDAs + **ctx.accounts.vault.to_account_info().try_borrow_mut_lamports()? -= available_dividends as u64; + **ctx.accounts.user_authority.to_account_info().try_borrow_mut_lamports()? += available_dividends as u64; + + emit!(WithdrawalEvent { + user: ctx.accounts.user_authority.key(), + amount: available_dividends as u64, + }); + + msg!("Withdrew {} lamports in dividends", available_dividends); + Ok(()) + } +} + +// Realistic bonding curve based on actual SOL in vault +fn calculate_tokens_for_sol(sol: u64, supply: u64, _state: &PowhState) -> Result { + // Linear bonding curve: price increases with supply + // Base price: 0.0001 SOL per token (100,000 lamports per token) + // Price increases by 0.00001 SOL per billion tokens in supply + let base_price_lamports = 100_000u128; // 0.0001 SOL in lamports + let price_increment = 10_000u128; // 0.00001 SOL in lamports + let supply_factor = (supply as u128) / 1_000_000_000; // Per billion tokens + + let current_price = base_price_lamports + (price_increment * supply_factor); + let tokens = (sol as u128 * 1_000_000_000) / current_price; // Convert lamports to tokens + + msg!("Buy: {} lamports -> {} tokens at price {} lamports per token", sol, tokens, current_price); + Ok(tokens as u64) +} + +fn calculate_sol_for_tokens(tokens: u64, supply: u64, _state: &PowhState) -> Result { + // Sell at 90% of current buy price to create spread + let base_price_lamports = 100_000u128; + let price_increment = 10_000u128; + let supply_factor = (supply as u128) / 1_000_000_000; + + let current_price = base_price_lamports + (price_increment * supply_factor); + let sell_price = (current_price * 90) / 100; // 90% of buy price + let sol_lamports = (tokens as u128 * sell_price) / 1_000_000_000; + + msg!("Sell: {} tokens -> {} lamports at price {} lamports per token", tokens, sol_lamports, sell_price); + Ok(sol_lamports as u64) +} + +const PRECISION: u128 = 1_000_000_000_000_000_000; // 18 decimal places for dividend calculations + +#[derive(Accounts)] +pub struct Initialize<'info> { + #[account( + init, + payer = authority, + space = 8 + PowhState::INIT_SPACE, + seeds = [b"state"], + bump + )] + pub state: Account<'info, PowhState>, + + /// CHECK: This is the vault PDA for holding SOL + #[account( + init, + payer = authority, + space = 0, + seeds = [b"vault"], + bump + )] + pub vault: UncheckedAccount<'info>, + + #[account(mut)] + pub mint: Account<'info, Mint>, + + #[account(mut)] + pub authority: Signer<'info>, + + pub system_program: Program<'info, System>, + pub token_program: Program<'info, Token>, +} + +#[derive(Accounts)] +pub struct RegisterUser<'info> { + #[account( + seeds = [b"state"], + bump = state.bump + )] + pub state: Account<'info, PowhState>, + + #[account( + init, + payer = owner, + space = 8 + User::INIT_SPACE, + seeds = [b"user", owner.key().as_ref()], + bump + )] + pub user: Account<'info, User>, + + #[account( + init_if_needed, + payer = owner, + associated_token::mint = mint, + associated_token::authority = owner, + )] + pub token_account: Account<'info, TokenAccount>, + + pub mint: Account<'info, Mint>, + + #[account(mut)] + pub owner: Signer<'info>, + + pub system_program: Program<'info, System>, + pub token_program: Program<'info, Token>, + pub associated_token_program: Program<'info, AssociatedToken>, +} + +#[derive(Accounts)] +pub struct BuyTokens<'info> { + #[account( + mut, + seeds = [b"state"], + bump = state.bump + )] + pub state: Account<'info, PowhState>, + + /// CHECK: This is the vault PDA + #[account( + mut, + seeds = [b"vault"], + bump = state.vault_bump + )] + pub vault: UncheckedAccount<'info>, + + #[account(mut)] + pub mint: Account<'info, Mint>, + + #[account( + mut, + token::mint = mint, + token::authority = buyer, + )] + pub buyer_token_account: Account<'info, TokenAccount>, + + /// CHECK: Dev wallet for fees + #[account(mut)] + pub dev_wallet: UncheckedAccount<'info>, + + #[account(mut)] + pub buyer: Signer<'info>, + + pub system_program: Program<'info, System>, + pub token_program: Program<'info, Token>, +} + +#[derive(Accounts)] +pub struct SellTokens<'info> { + #[account( + mut, + seeds = [b"state"], + bump = state.bump + )] + pub state: Account<'info, PowhState>, + + /// CHECK: This is the vault PDA + #[account( + mut, + seeds = [b"vault"], + bump = state.vault_bump + )] + pub vault: UncheckedAccount<'info>, + + #[account(mut)] + pub mint: Account<'info, Mint>, + + #[account( + mut, + token::mint = mint, + token::authority = seller, + )] + pub seller_token_account: Account<'info, TokenAccount>, + + #[account(mut)] + pub seller: Signer<'info>, + + pub system_program: Program<'info, System>, + pub token_program: Program<'info, Token>, +} + +#[derive(Accounts)] +pub struct WithdrawDividends<'info> { + #[account( + seeds = [b"state"], + bump = state.bump + )] + pub state: Account<'info, PowhState>, + + /// CHECK: This is the vault PDA + #[account( + mut, + seeds = [b"vault"], + bump = state.vault_bump + )] + pub vault: UncheckedAccount<'info>, + + #[account( + mut, + seeds = [b"user", user_authority.key().as_ref()], + bump + )] + pub user: Account<'info, User>, + + #[account( + token::mint = mint, + token::authority = user_authority, + )] + pub token_account: Account<'info, TokenAccount>, + + pub mint: Account<'info, Mint>, + + #[account(mut)] + pub user_authority: Signer<'info>, + + pub system_program: Program<'info, System>, +} + +#[account] +#[derive(InitSpace)] +pub struct PowhState { + pub bump: u8, + pub vault_bump: u8, + pub authority: Pubkey, + pub mint: Pubkey, + pub vault: Pubkey, + pub total_supply: u64, + pub dev_fee_bps: u16, + pub dividend_fee_bps: u16, + pub token_price_initial: u64, + pub token_price_increment: u64, + pub profit_per_share: u128, +} + +#[account] +#[derive(InitSpace)] +pub struct User { + pub owner: Pubkey, + pub token_account: Pubkey, + pub dividends_withdrawn: u128, +} + +#[event] +pub struct TradeEvent { + pub user: Pubkey, + pub tokens: u64, + pub sol_amount: u64, + pub is_buy: bool, +} + +#[event] +pub struct WithdrawalEvent { + pub user: Pubkey, + pub amount: u64, +} + +#[error_code] +pub enum ErrorCode { + #[msg("Insufficient funds")] + InsufficientFunds, + #[msg("Invalid amount")] + InvalidAmount, + #[msg("No dividends available")] + NoDividends, +} \ No newline at end of file diff --git a/send-to-burn.sh b/send-to-burn.sh new file mode 100755 index 0000000..16edd50 --- /dev/null +++ b/send-to-burn.sh @@ -0,0 +1,98 @@ +#!/bin/bash + +echo "๐Ÿ”ฅ Sending All Tokens to Burn Address" +echo "=====================================" +echo "๐ŸŽฏ Burn Address: 1nc1nerator11111111111111111111111111111111" +echo "๐Ÿ“ Cluster: devnet (safe for testing)" +echo "" + +# Confirm before proceeding +read -p "Send ALL tokens to burn address? (type 'yes' to confirm): " confirm +if [ "$confirm" != "yes" ]; then + echo "โŒ Aborted" + exit 1 +fi + +echo "๐Ÿ”ฅ Starting burn process..." +echo "" + +# Set to devnet +solana config set --url devnet > /dev/null + +BURN_ADDRESS="1nc1nerator11111111111111111111111111111111" + +echo "๐Ÿ’ฐ Wallet: $(solana address)" +echo "๐Ÿ”ฅ Burn Address: $BURN_ADDRESS" +echo "" + +# Get all token accounts +echo "๐Ÿ“Š Current token accounts:" +spl-token accounts --url devnet +echo "" + +# List of token mints to burn +MINTS=( + "4auc435LJ9AfprLeMziHTJbP8wQmbSi7o4ZpeXdR66jQ" + "5xEQF57sR2L3tJBfdiiqaVtepTMa7nQR9uMybMojEY8n" + "8cJ8APx5TwwmpeRk4Zhz1Qbyi1T3HHdHUEw4GUJqxAGN" + "AK9fXWyDNWurYjZSDhbqBo6fczqLmCLbCZ3vq9Vftsqb" +) + +# Function to send tokens to burn address +burn_tokens() { + local mint=$1 + echo "๐Ÿ”ฅ Processing mint: $mint" + + # Get balance + local balance=$(spl-token balance $mint --url devnet 2>/dev/null || echo "0") + + if [ "$balance" == "0" ] || [ -z "$balance" ]; then + echo " โ„น๏ธ No balance - skipping" + return + fi + + echo " ๐Ÿ’ฐ Current balance: $balance" + + # Create associated token account for burn address if needed + echo " ๐Ÿญ Creating burn address token account..." + spl-token create-account $mint --owner $BURN_ADDRESS --url devnet 2>/dev/null || true + + # Transfer all tokens to burn address + echo " ๐Ÿ”ฅ Sending $balance tokens to burn address..." + local result=$(spl-token transfer $mint $balance $BURN_ADDRESS --url devnet --allow-unfunded-recipient 2>&1) + + if [ $? -eq 0 ]; then + echo " โœ… Successfully sent $balance tokens to burn address" + + # Close the now-empty account to reclaim rent + echo " ๐Ÿ—‘๏ธ Closing empty token account..." + spl-token close $mint --url devnet 2>/dev/null + if [ $? -eq 0 ]; then + echo " โœ… Token account closed, rent reclaimed" + else + echo " โš ๏ธ Could not close account" + fi + else + echo " โŒ Transfer failed: $result" + fi + + echo "" +} + +# Process each mint +for mint in "${MINTS[@]}"; do + burn_tokens $mint +done + +echo "๐ŸŽฏ Burn process complete!" +echo "" +echo "๐Ÿ“Š Final token account status:" +spl-token accounts --url devnet + +echo "" +echo "๐Ÿ’ฐ Final SOL balance:" +solana balance --url devnet + +echo "" +echo "๐Ÿ”ฅ All tokens have been sent to the incinerator!" +echo "โœ… Ready for clean testing!" \ No newline at end of file diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..41331e0 --- /dev/null +++ b/styles.css @@ -0,0 +1,119 @@ +/* Ticker */ +.ticker-wrap{ position:fixed; top:0; left:0; width:100%; height:36px; background: rgba(11,16,32,0.92); z-index:9999; overflow:hidden; box-shadow:0 2px 10px rgba(0,0,0,0.4); } +.ticker{ display:inline-block; white-space:nowrap; will-change:transform; animation: ticker 28s linear infinite; padding-left:100%; } +.ticker:hover{ animation-play-state: paused; } +.ticker-item{ display:inline-block; padding:8px 18px; font-family:'Press Start 2P',cursive; font-size:11px; color:#cbd5ff; opacity:0.95; } +.ticker-item.buy{ color:#22c55e; } +.ticker-item.sell{ color:#ef4444; } +@keyframes ticker{ 0%{ transform:translateX(0);} 100%{ transform:translateX(-100%);} } + +/* offset hero a bit since ticker is fixed */ +.hero{ padding-top: 64px; } + +/* Chip and delta styles */ +.chip{ background:#0e1430; color:#cbd5ff; border:1px solid rgba(34,211,238,0.35); padding:6px 10px; border-radius:999px; font-size:11px; font-family:'Press Start 2P',cursive; } +.delta{ margin-left:8px; font-size:10px; color:#22d3ee; opacity:0; transition:opacity .3s, transform .3s; font-family: 'Press Start 2P', cursive; text-shadow: 1px 1px 0px rgba(0,0,0,0.8); } +.delta.show{ opacity:1; transform:translateY(-2px) } +.delta.pos{ color:#22d3ee; text-shadow: 0 0 6px #22d3ee; } +.delta.neg{ color:#ef4444; text-shadow: 0 0 6px #ef4444; } + +/* Money rain (from nancymoneysite) */ +.money-rain{ position:fixed; top:0; left:0; width:100%; height:100%; pointer-events:none; z-index:-1; } +.money{ position:absolute; width:30px; height:15px; background:url('data:image/svg+xml;utf8,$') no-repeat center/contain; opacity:.4; animation:rain linear infinite; } +@keyframes rain{ 0%{ transform: translateY(-100vh) rotate(0deg);} 100%{ transform: translateY(100vh) rotate(360deg);} } + +/* Background animation from nancymoneysite */ +.animated-background { position: fixed; top:0; left:0; width:100%; height:100%; z-index:-2; background: var(--bg); overflow:hidden; } +.animated-wave { width:100%; height:100%; } +.wave { fill:none; stroke:#22d3ee; stroke-width:1; stroke-opacity:0.18; animation: chartMove 6s ease-in-out infinite; filter: drop-shadow(0 0 5px rgba(34,211,238,0.25)); } +.wave2 { stroke-opacity:0.12; animation-delay:-2s; } +.wave3 { stroke-opacity:0.08; animation-delay:-1s; } +@keyframes chartMove { + 0% { d: path("M0,90 L20,85 L40,80 L60,85 L80,70 L100,75"); } + 50% { d: path("M0,85 L20,70 L40,50 L60,30 L80,20 L100,10"); } + 100% { d: path("M0,90 L20,85 L40,80 L60,85 L80,70 L100,75"); } +} + + +/* Hero pot */ +.hero-pot { display:flex; align-items:center; justify-content:center; gap:16px; margin: 12px 0 8px; } +.hero-pot .amount { font-family:'Press Start 2P',cursive; font-size:34px; color:#fff; text-shadow:0 0 12px rgba(124,58,237,0.8); } +.hero-pot .timer { color: var(--muted); font-size:10px; font-family: 'Press Start 2P', cursive; } + +/* Tabs */ +.tabs .tabs-nav { display:flex; gap:8px; margin-bottom:8px; } +.tabs .tab { background:#0e1430; color:#cbd5ff; border:1px solid rgba(124,58,237,0.25); padding:8px 12px; border-radius:8px; cursor:pointer; font-family:'Press Start 2P',cursive; font-size:11px; } +.tabs .tab.active { background: linear-gradient(135deg, rgba(124,58,237,0.5), rgba(34,211,238,0.35)); color:#0b1020; } +.tab-content { margin-top:8px; } +.tab-content.active { display:block; } + +/* Numbers */ +.big { font-size: 26px; font-family:'Press Start 2P',cursive; } + +/* 8-bit headings */ +h2, h3, h4 { font-family: 'Press Start 2P', cursive; color: var(--accent); text-shadow: 2px 2px 0px rgba(0,0,0,0.5); } +h2 { font-size: 14px; margin: 0 0 12px 0; } +h3 { font-size: 12px; margin: 0 0 8px 0; } +p { font-family: 'Press Start 2P', cursive; font-size: 10px; line-height: 1.6; } + +/* Select styling */ +select { + background: #0e1430; + color: var(--text); + border: 1px solid rgba(124,58,237,0.25); + border-radius: 8px; + padding: 10px; + font-family: 'Press Start 2P', cursive; + font-size: 10px; +} + +/* Link styling */ +a { + color: var(--accent); + text-decoration: none; + font-family: 'Press Start 2P', cursive; + font-size: 10px; +} +a:hover { + color: #fff; + text-shadow: 0 0 8px var(--accent); +} + +/* 8-bit Dark Theme */ +:root { + --bg: #0b1020; + --panel: #151b2e; + --text: #dbe4ff; + --muted: #8ea0c7; + --primary: #7c3aed; + --accent: #22d3ee; +} +*{box-sizing:border-box} +body { margin:0; background:var(--bg); color:var(--text); font-family: 'Press Start 2P', cursive; font-size: 11px; line-height: 1.6; } +.hero { padding: 48px 0 28px; background: radial-gradient(1200px 600px at 50% -10%, rgba(124,58,237,0.35), transparent 60%), #0b1020; text-align:center; } +.hero h1 { font-family:'Press Start 2P', cursive; margin:0 0 8px; font-size: 28px; letter-spacing: 1px; } +.hero .sub { color: var(--muted); margin:0; font-family: 'Press Start 2P', cursive; font-size: 10px; } +.wrap { max-width: 1100px; margin: 0 auto; padding: 0 16px; } +.panel { background: var(--panel); border:1px solid rgba(124,58,237,0.15); border-radius: 12px; padding: 16px; margin: 16px 0; box-shadow: 0 8px 30px rgba(124,58,237,0.10); } +.row { display:flex; gap:16px; flex-wrap:wrap; } +.col { flex:1 1 420px; } +.grid2 { display:grid; grid-template-columns: 180px 1fr; gap:8px 12px; align-items:center; } +.label { color: var(--muted); font-size: 10px; font-family: 'Press Start 2P', cursive; text-transform: uppercase; } +.input { background:#0e1430; color:var(--text); border:1px solid rgba(124,58,237,0.25); border-radius:8px; padding:10px; width:100%; font-family: 'Press Start 2P', cursive; font-size: 10px; } +.wallet-row { display:flex; gap:8px; margin: 8px 0 12px; } +.btn { background: var(--primary); color:#fff; border:0; border-radius:8px; padding:10px 14px; font-family:'Press Start 2P', cursive; box-shadow:0 0 0 3px #000, 0 8px 0 0 #000; cursor:pointer; } +.btn:active { position:relative; top:2px; box-shadow:0 0 0 3px #000, 0 6px 0 0 #000; } +.btn.alt { background: var(--accent); color:#0b1020; } +.btn.danger { background:#ef4444; } +.muted { color: var(--muted); font-family: 'Press Start 2P', cursive; font-size: 10px; } +.log { background:#060912; border:1px solid rgba(124,58,237,0.15); border-radius:8px; padding:12px; min-height:160px; font-family: ui-monospace, Menlo, Monaco, Consolas, 'Courier New', monospace; overflow:auto; } +.foot { display:flex; justify-content:space-between; align-items:center; padding: 16px 0 48px; color: var(--muted); font-family: 'Press Start 2P', cursive; font-size: 10px; } +@media (max-width: 720px){ + .grid2{grid-template-columns:1fr; gap:12px;} + .label{font-size:9px; margin-bottom:4px;} + h2{font-size:12px;} + .hero h1{font-size:20px;} + .hero-pot .amount{font-size:24px;} + .btn{font-size:9px; padding:8px 10px;} + .input{font-size:9px;} +} diff --git a/switch-to-mainnet.sh b/switch-to-mainnet.sh new file mode 100755 index 0000000..d81c345 --- /dev/null +++ b/switch-to-mainnet.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +# Simple script to switch from devnet to mainnet +set -e + +echo "๐Ÿ”„ Switching to Mainnet Configuration..." +echo "โš ๏ธ WARNING: You'll need to deploy the program to mainnet first!" +echo + +# Get current config +if [[ ! -f "config.json" ]]; then + echo "โŒ config.json not found" + exit 1 +fi + +# Show current config +echo "๐Ÿ“‹ Current config:" +cat config.json +echo + +# Get user inputs +read -p "๐Ÿ”‘ Enter MAINNET program ID: " PROGRAM_ID +read -p "๐Ÿช™ Enter MAINNET mint address: " MINT_ADDRESS +read -p "๐Ÿ’ฐ Enter MAINNET dev wallet (or press enter for current): " DEV_WALLET + +# Use current dev wallet if none provided +if [[ -z "$DEV_WALLET" ]]; then + DEV_WALLET=$(cat config.json | grep devWallet | cut -d'"' -f4) +fi + +# Create mainnet config +cat > config-mainnet.json << EOF +{ + "cluster": "https://api.mainnet-beta.solana.com", + "programId": "$PROGRAM_ID", + "mint": "$MINT_ADDRESS", + "devWallet": "$DEV_WALLET" +} +EOF + +# Backup current config +cp config.json config-devnet-backup.json + +# Switch to mainnet +cp config-mainnet.json config.json + +echo "โœ… Switched to mainnet!" +echo "๐Ÿ“ Devnet config backed up to: config-devnet-backup.json" +echo "๐Ÿ“ Mainnet config saved to: config-mainnet.json" +echo + +echo "๐Ÿ“ To deploy frontend with mainnet config:" +echo " ./deploy.sh" +echo + +echo "๐Ÿ“ To switch back to devnet:" +echo " cp config-devnet-backup.json config.json" \ No newline at end of file diff --git a/test-devnet.sh b/test-devnet.sh new file mode 100755 index 0000000..05033fe --- /dev/null +++ b/test-devnet.sh @@ -0,0 +1,86 @@ +#!/bin/bash + +echo "๐Ÿงช Lottery Devnet Testing Script" +echo "==============================" + +# Set to devnet +solana config set --url devnet +echo "โœ… Set Solana config to devnet" + +# Check wallet balance +echo "๐Ÿ’ฐ Checking wallet balance..." +BALANCE=$(solana balance) +echo "Current balance: $BALANCE" + +# Ensure we have enough SOL (at least 5 SOL for testing) +MIN_BALANCE="5.0" +BALANCE_NUM=$(echo $BALANCE | cut -d' ' -f1) + +if (( $(echo "$BALANCE_NUM < $MIN_BALANCE" | bc -l) )); then + echo "โŒ Insufficient balance for testing. Need at least 5 SOL." + echo "๐Ÿ’ธ Requesting airdrop..." + solana airdrop 5 + echo "โณ Waiting 10 seconds for airdrop confirmation..." + sleep 10 +fi + +# Build the program +echo "๐Ÿ”จ Building Anchor program..." +anchor build +if [ $? -ne 0 ]; then + echo "โŒ Build failed!" + exit 1 +fi + +# Deploy the program +echo "๐Ÿš€ Deploying to devnet..." +anchor deploy --provider.cluster devnet +if [ $? -ne 0 ]; then + echo "โŒ Deploy failed!" + exit 1 +fi + +# Generate the IDL +echo "๐Ÿ“ Generating IDL..." +anchor idl init -f target/idl/lottery_simple.json $(solana-keygen pubkey target/deploy/lottery_simple-keypair.json) + +echo "โœ… Program deployed successfully!" + +# Show deployment info +PROGRAM_ID=$(solana-keygen pubkey target/deploy/lottery_simple-keypair.json) +echo "๐Ÿ“ Program ID: $PROGRAM_ID" + +# Create a simple test to verify the deployment +echo "๐Ÿงช Creating simple test..." + +# Generate a new keypair for the mint +echo "๐Ÿ”‘ Creating mint keypair..." +solana-keygen new --outfile mint-keypair.json --no-bip39-passphrase + +# Create the mint +echo "๐Ÿช™ Creating token mint..." +MINT_ADDRESS=$(solana-keygen pubkey mint-keypair.json) +spl-token create-token mint-keypair.json +echo "โœ… Token mint created: $MINT_ADDRESS" + +# Generate devnet config +echo "๐Ÿ“„ Generating devnet configuration..." +cat > devnet-config.json << EOF +{ + "programId": "$PROGRAM_ID", + "mintAddress": "$MINT_ADDRESS", + "devWallet": "$(solana address)", + "cluster": "devnet", + "rpcUrl": "https://api.devnet.solana.com" +} +EOF + +echo "โœ… Devnet configuration saved to devnet-config.json" +echo "" +echo "๐ŸŽ‰ Deployment complete!" +echo "๐Ÿ“‹ Next steps:" +echo " 1. Update your frontend to use devnet-config.json" +echo " 2. Initialize the program state using the frontend or CLI" +echo " 3. Test buy/sell functionality" +echo "" +echo "โš ๏ธ Remember: This is devnet SOL - not real money!" \ No newline at end of file diff --git a/test-program.html b/test-program.html new file mode 100644 index 0000000..a9a2ed5 --- /dev/null +++ b/test-program.html @@ -0,0 +1,289 @@ + + + + + + PoWH Program Test - Devnet + + + + + +

๐Ÿงช PoWH Program Test - Devnet

+ +
+

Connection Status

+
Not connected
+ +
+ +
+

Program Information

+
+
Program ID: Loading...
+
Mint Address: Loading...
+
Cluster: devnet
+
State PDA: Not calculated
+
Vault PDA: Not calculated
+
+ +
+ +
+

Program Initialization

+
Ready to initialize
+ +
+ +
+

Test Trading

+
+ + +
+
+ + +
+
Ready to trade
+
+ + + + \ No newline at end of file diff --git a/ticker-api.php b/ticker-api.php new file mode 100644 index 0000000..033f1f2 --- /dev/null +++ b/ticker-api.php @@ -0,0 +1,79 @@ + 'Invalid input. Required: text, type']); + exit; + } + + $tickerData = readTickerData($tickerFile); + + // Add new item with timestamp + $newItem = [ + 'text' => $input['text'], + 'type' => $input['type'], // 'buy' or 'sell' + 'timestamp' => time() + ]; + + // Add optional SNS fields if provided + if (isset($input['fullAddress'])) { + $newItem['fullAddress'] = $input['fullAddress']; + } + if (isset($input['snsName'])) { + $newItem['snsName'] = $input['snsName']; + } + + // Add to beginning of array (most recent first) + array_unshift($tickerData, $newItem); + + // Keep only the last N items + if (count($tickerData) > $maxItems) { + $tickerData = array_slice($tickerData, 0, $maxItems); + } + + // Save to file + if (writeTickerData($tickerFile, $tickerData)) { + echo json_encode(['success' => true, 'itemsCount' => count($tickerData)]); + } else { + http_response_code(500); + echo json_encode(['error' => 'Failed to save ticker data']); + } + +} else { + http_response_code(405); + echo json_encode(['error' => 'Method not allowed']); +} +?> \ No newline at end of file diff --git a/ticker-data.json b/ticker-data.json new file mode 100644 index 0000000..69a46e2 --- /dev/null +++ b/ticker-data.json @@ -0,0 +1,17 @@ +[ + { + "text": "BUY 0.250 SOL โ€ข 4Zp2โ€ฆ8Tv3", + "type": "buy", + "timestamp": 1728219420 + }, + { + "text": "SELL 15.000 tk โ€ข 8Rv1โ€ฆ2Mn4", + "type": "sell", + "timestamp": 1728219380 + }, + { + "text": "BUY 0.100 SOL โ€ข 2Qm9โ€ฆ5Lp6", + "type": "buy", + "timestamp": 1728219340 + } +]